Car Physics with many cars
From GameStudio Wiki
I modified the original scripts supplied in A7 demo project Car Level. The original source code contains three key-files: physics_car.c, physics_events.c and physics_init.c. Such scripts have two missing features:
- They can manage only a physic car
- You can create only a vehicle profile (sport-car, off-road, etc...)
- I made a general code clean-up (even if I left original commented code; I will eliminate it only in the final release).
I modified them in order to add such features, maintaining an high usability.
So I created the following scripts (modified version of the original ones). Filenames are the same, but its usage is quite different.
Here follows the source code. You can find how to use them at the end of the page.
NOTE: this is a beta-test code. What's missing? What should be completed?
- Lift was eliminated (sorry, but I really don't like it!)
- Particle system is used to create "smoke": currently, only a car at the same time can show the smoke. This is a bug: I'm working on it.
Contents |
SOURCE CODE
physics_car.c
//////////////////////////////////////////////////////////////
// Program modified by Alessandro Manotti
// Original code taken from A7 buggy demo.
// Last-update: 24-Jan-2008
// History:
// 24-Jan-2008
// * Original code
//////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////
// These defines are needed to manage the vehicles and
// physics engine.
//////////////////////////////////////////////////////////////
#define MAX_CARS_COUNT 9 // Max number of cars available at the same time.
#define MAX_CARS_DETAILS_COUNT 9 // Max number of different car types (off-road/sport/etc...)
//////////////////////////////////////////////////////////////
// Car constants
// This struct maintain an array of different vehicle
// characteristics. Every vehicle characteristic, stored here,
// will be associated to a material vehicle using its
// "vehicle.properties" pointer.
//////////////////////////////////////////////////////////////
typedef struct {
var maxTorque; // maximum torque applied to rear wheels
var maxTorqueBrake; // this will be applied to rear wheels when hitting brakes or handbrake
var maxAngSpeed; // max. angular speed of wheels; max linear speed depends on wheel radius
var massWheel; // mass per wheel
var massChassis; // mass for chassis lighter than wheels so it doesn't tilt
var fricWheel; // friction of wheels -god for "in the water"
var fricChassis; // friction of chassis (low to avoid getting stuck on ramps
var dampChassis; // damping lin/ang for chassis
var dampWheel; // damping ang for wheels
var suspensionERP; // how quickly shocks are reduced to correct position (100,000 max, 0: never)
var suspensionCFM; // how far shocks can overshoot (0: never, 100,000 max)
var jumpRechargeDelay; // time before user is allowed to jump again in secs
var jumpForce; // force multiplier for jumps
var bDentCar; // when set to 1 deform car on impact
} TVehicleProperties;
TVehicleProperties vehicleProperties[MAX_CARS_DETAILS_COUNT];
//////////////////////////////////////////////////////////////
// Steering constants
//////////////////////////////////////////////////////////////
var accelKeyboard = 2; // acceleration multiplier for key control
var accelSteering = 3; // acceleration multiplier for steering key control
VECTOR* joyNullzone = { x=20; y=20; } // 0..255 x and y direction center range for joystick
//////////////////////////////////////////////////////////////
// these values depend on MDL and WMP where car is located
//////////////////////////////////////////////////////////////
var vecUp[3] = {0, 0, 1}; // up axis, usually +Z
var vecRight[3] = {0, -1, 0}; // chassis right axis
var vecDriverOffset[3] = {-35, 10, 34}; // where camera will be located relative to chassis center (cockpit)
var vecChaseOffset[3] = {-150, 0, 60}; // where camera will be located relative to chassis center (chase)
ANGLE vecChaseAngOff; // angle tweak of the chase camera
var gravity = 1800; //2000; // gravity along -Z axis
#define NUM_TRACKS 1 //400; // number of tire tracks
#define CRASH_SPEED 100 // head-on impact above this speed causes wheels to come off
//////////////////////////////////////////////////////////////
// Controller setup
//////////////////////////////////////////////////////////////
#define KEY_FWD key_w // Forward
#define KEY_BWD key_s // Backward
#define KEY_LEFT key_a // Left
#define KEY_RIGHT key_d // Right
#define KEY_HORN key_h // Push horn
#define KEY_JUMP key_e // Turbo Boost ;-)
#define KEY_BRAKE key_space // Pull Handbrake
var USE_GAMEPAD= 0; // Treat joystick as absolute device
#define JOY_JUMP joy_1 // Turbo Boost ;-)
#define JOY_BRAKE joy_2 // Pull Handbrake
#define JOY_HORN joy_5 // Push horn
#define JOYRAW_Y joy_raw.y // Forward/Backward axis on joystick
#define JOYRAW_X joy_raw.x // Left/right axis on joystick
//////////////////////////////////////////////////////////////
// physics global vars
var DoShutdown=0; // set to 1 and wait a while before reloading level
var DoSteering=1; // set to 1 to enable all controls 0 for pause
//////////////////////////////////////////////////////////////
// This structure defines every data related to a vehicle.
//////////////////////////////////////////////////////////////
typedef struct {
ENTITY* pChassis; // Chassis handle. Camera points this object.
ENTITY* pFL; // Tires handles.
ENTITY* pFR;
ENTITY* pRL;
ENTITY* pRR;
TVehicleProperties *properties; // Pointer to an array element containing
// specific vehicle characteristics.
var wheelFL; // constraint handles.
var wheelFR;
var wheelRL;
var wheelRR;
var jumping; // time left in jump
var hornPlaying; // on or off
var brakePlaying; // on or off
var lastPos[3]; // position of chassis in last frame
var arrpTracks[NUM_TRACKS]; // cyclic buffer of track entities (handles)
var trackLen; // length of treadmark in quants
var currTrack; // current track number
var timerSeconds; // a simple stopwatch, can be removed
var linSpeed; // car speed in XY
var downSpeed; // car speed in Z direction
var angSpeed; // current wheel angular speed along y axis of wheel
var targetSpeed; // desired angular speed (if torque permits)
var targetSteer; // desired steering angle
var commonWheelHeight;
} TVehicle;
// This variable store every detail regarding vehicle physics.
TVehicle vehicle[MAX_CARS_COUNT];
//////////////////////////////////////////////////////////////////////////////////////////
// Resources
//////////////////////////////////////////////////////////////////////////////////////////
SOUND* engine_wav = "engine.wav"; // player's engine sound
SOUND* horn_wav = "horn.wav"; // player's horn
SOUND* crash_wav = "crash.wav"; // player's crashing sound
SOUND* tires_wav = "tires.wav"; // tires squealing
SOUND* flap_wav = "lp_flap.wav"; // tires squealing
var hTireSnd_n; // handle to sound playing
BMAP* bmpSmoke= "smoke.tga";
//////////////////////////////////////////////////////////////////////////////////////////
// Includes
#include "physics_init.c" // all car init functions
#include "physics_events.c" // all car event & particle functions
//////////////////////////////////////////////////////////////////////////////////////////
// Some status data
var noTorque=0;
//////////////////////////////////////////////////////////////////////////////////////////
// Startup initialization.
//////////////////////////////////////////////////////////////////////////////////////////
#define VEHICLE_PROPS_BUGGY 0
function game_startup() {
int i;
//_/Init dati della struct contnenti le info dei veicoli\__________________
//_________________________________________________________________________
for(i=0; i<MAX_CARS_COUNT; i++) {
//_/Vehicle characteristics\_______________________________________________
//_________________________________________________________________________
//
// BUGGY
//
vehicleProperties[i].maxTorque = 800; //1200 // maximum torque applied to rear wheels
vehicleProperties[i].maxTorqueBrake = 3000; // this will be applied to rear wheels when hitting brakes or handbrake
vehicleProperties[i].maxAngSpeed = 80; //70 // max. angular speed of wheels; max linear speed depends on wheel radius
vehicleProperties[i].massWheel = 20; // mass per wheel
vehicleProperties[i].massChassis = 70; // mass for chassis lighter than wheels so it doesn't tilt
vehicleProperties[i].fricWheel = 80; //100 // friction of wheels -god for "in the water"
vehicleProperties[i].fricChassis = 50; // friction of chassis (low to avoid getting stuck on ramps
vehicleProperties[i].dampChassis = 50; //5; // damping lin/ang for chassis
vehicleProperties[i].dampWheel = 50; //15; //15 // damping ang for wheels
vehicleProperties[i].suspensionERP = 20000; //90000; // how quickly shocks are reduced to correct position (100,000 max, 0: never)
vehicleProperties[i].suspensionCFM = 150; //500 // how far shocks can overshoot (0: never, 100,000 max)
vehicleProperties[i].jumpRechargeDelay = 1.0; //1.5 // time before user is allowed to jump again in secs
vehicleProperties[i].jumpForce = 3000; //3000; // force multiplier for jumps
vehicleProperties[i].bDentCar = 1; // when set to 1 deform car on impact
vehicle[i].jumping = 0; // time left in jump
vehicle[i].hornPlaying = 0; // on or off
vehicle[i].brakePlaying = 0; // on or off
vehicle[i].trackLen = 20; // length of treadmark in quants
vehicle[i].currTrack = 0; // current track number
vehicle[i].targetSpeed=0; // desired angular speed (if torque permits)
vehicle[i].targetSteer=0; // desired steering angle
vehicle[i].commonWheelHeight = -100000;
vehicle[i].properties = &vehicleProperties[i];
}
}
//////////////////////////////////////////////////////////////////////////////////////////
// SpeedControl
//////////////////////////////////////////////////////////////////////////////////////////
function SpeedControl()
{
ENTITY* myEntity = my;
int myID = (int)my.skill1;
var locDirection;
var locUseJoy = 0;
while (1)
{
if (DoShutdown) { return; } // quitting?
if (!DoSteering) // currently pausing?
{
wait(1); continue;
}
if (JOYRAW_Y<-joyNullzone.y || JOYRAW_Y>joyNullzone.y)
{
// treat joystick as absolute device which is more
// like a keyboard than a joystick
if (USE_GAMEPAD) {
locUseJoy=0; locDirection= -(JOYRAW_Y / 255);
} else {
locUseJoy=1; locDirection= -(JOYRAW_Y-joyNullzone.y)/(255-joyNullzone.y);
}
} else {
locUseJoy=0; locDirection= (KEY_FWD-KEY_BWD) + joy_force.y;
}
// if we drive in one locDirection and want to change locDirection, apply brakes
// (angSpeed>0 checks if rear wheel is turning forward
// locDirection<0 checks if user hits reverse key
if ((vehicle[myID].angSpeed>5 && locDirection<0) || (vehicle[myID].angSpeed<-5 && locDirection>0))
{
vehicle[myID].targetSpeed=0; // brake
// press brakes quarter down when rolling at high speed
if (abs(vehicle[myID].angSpeed)>0.25 * vehicle[myID].properties.maxAngSpeed)
{
phcon_setmotor(vehicle[myID].wheelRL, nullvector, vector(vehicle[myID].angSpeed*0.5, 0.5 * vehicle[myID].properties.maxTorqueBrake,0), nullvector);
phcon_setmotor(vehicle[myID].wheelRR, nullvector, vector(vehicle[myID].angSpeed*0.5, 0.5 * vehicle[myID].properties.maxTorqueBrake,0), nullvector);
if (abs(vehicle[myID].angSpeed)> (0.50 * vehicle[myID].properties.maxAngSpeed) )
{
EventBrake(myID); // braking sound
}
} else {
// we're already slow- apply max. units brake torque
phcon_setmotor(vehicle[myID].wheelRL, nullvector, vector(0, vehicle[myID].properties.maxTorqueBrake,0), nullvector);
phcon_setmotor(vehicle[myID].wheelRR, nullvector, vector(0, vehicle[myID].properties.maxTorqueBrake,0), nullvector);
}
} else {
var oldSpeed; oldSpeed= vehicle[myID].targetSpeed;
// regular driving
if (locUseJoy) {
vehicle[myID].targetSpeed = vehicle[myID].properties.maxAngSpeed * locDirection; //absolute value for stick
} else {
// speed up over time for keyboard (relative acceleration)
vehicle[myID].targetSpeed += locDirection*accelKeyboard* time_step;
}
var locTorque = vehicle[myID].properties.maxTorque;
// damp down speed over time if no key pressed
if (!locUseJoy && locDirection==0) {
vehicle[myID].targetSpeed *= 1-(time_step * 0.02);
locTorque = vehicle[myID].properties.maxTorque / 2;
// switch off opposing locTorque so we can roll down hills
if (abs(vehicle[myID].linSpeed) < 20) {
locTorque=0;
vehicle[myID].targetSpeed *= 0.5;
}
} else {
// use less locTorque to slow down or else we'll spin
if (locUseJoy && abs(vehicle[myID].targetSpeed) < abs(vehicle[myID].angSpeed)) {
locTorque = vehicle[myID].properties.maxTorque / 2;
//targetSpeed *=0.5;
}
}
// max. reverse is 1/4 of forward speed
vehicle[myID].targetSpeed= clamp(vehicle[myID].targetSpeed, -vehicle[myID].properties.maxAngSpeed*0.25, vehicle[myID].properties.maxAngSpeed);
phcon_setmotor(vehicle[myID].wheelRL, nullvector, vector(vehicle[myID].targetSpeed, locTorque,0), nullvector);
phcon_setmotor(vehicle[myID].wheelRR, nullvector, vector(vehicle[myID].targetSpeed, locTorque,0), nullvector);
}
// handbrake
if (KEY_BRAKE|| JOY_BRAKE) {
phcon_setmotor(vehicle[myID].wheelRL, nullvector, vector(0, vehicle[myID].properties.maxTorqueBrake,0), nullvector);
phcon_setmotor(vehicle[myID].wheelRR, nullvector, vector(0, vehicle[myID].properties.maxTorqueBrake,0), nullvector);
if (abs(vehicle[myID].linSpeed) > 20) {
EventBrake(myID); // squeaky brakes
}
}
EventJump(myEntity); // check for jump
wait(1);
}
}
//////////////////////////////////////////////////////////////////////////////////////////
// Continuously calculate wheel rotational speed and store in global var
//////////////////////////////////////////////////////////////////////////////////////////
function UpdateSpeed()
{
int myID = (int)my.skill1;
var locAngRL; var locAngRR;
VECTOR locVecLin;
var locRotSign;
var locPlayers_engine; //handle for soundloop
while( (vehicle[myID].pRL == NULL) || (vehicle[myID].pRR == NULL) ) { wait(1); }
locPlayers_engine = snd_loop(engine_wav, 100, 0); // play the engine sound in a loop
snd_tune(locPlayers_engine, 80, 80, 0); //80% of real frequency
// check the rotation of the rear tires
if(vehicle[myID].pRL.pan != vehicle[myID].pRR.pan) {
// assume that the wheels are twisted 180 deg from each other
locRotSign = -1;
} else {
locRotSign = 1;
}
while(1) {
if (DoShutdown) { // quitting?
snd_stop (locPlayers_engine);
return;
}
// get linear speed and tune engine sound
phent_getvelocity(vehicle[myID].pChassis, locVecLin, nullvector); //linear speed
vehicle[myID].downSpeed = locVecLin.z * 0.091;
locVecLin.z = 0; // store Z separately
vehicle[myID].linSpeed= vec_length(locVecLin) * 0.091; // convert from quant/sec to kmh
if (vehicle[myID].properties.maxAngSpeed > 0)
{
var locFreq = 300 * abs(vehicle[myID].angSpeed) / vehicle[myID].properties.maxAngSpeed;
snd_tune(locPlayers_engine, 5, locFreq + 80, 0); //80% of real frequency
}
// grab angular velocity vector from rear wheels
phent_getangvelocity(vehicle[myID].pRL, temp); locAngRL = temp[1];
phent_getangvelocity(vehicle[myID].pRR, temp); locAngRR = temp[1];
// store largest one
if (abs(locAngRL) > abs(locAngRR)) {
vehicle[myID].angSpeed = locAngRL;
} else {
vehicle[myID].angSpeed = locRotSign * locAngRR;
}
wait(1);
}
}
//////////////////////////////////////////////////////////////////////
// the faster we go the more sensitive the car gets to steering. to make it easier for user
// scale down his steering inputs the faster we go
//////////////////////////////////////////////////////////////////////
function CalcSteeringScale(int myID)
{
return (1 - (abs(vehicle[myID].angSpeed) / (vehicle[myID].properties.maxAngSpeed+60)));
}
function SteerControl()
{
ENTITY* myEntity = my;
int myID = (int)my.skill1;
var locDirection = 0; //delta change
var locUseJoy; //using joystick for input?
while (1) {
if (DoShutdown) { return; } // quitting?
if (!DoSteering) // currently pausing?
{
wait(1); continue;
}
if (JOYRAW_X<-joyNullzone.x || JOYRAW_X>joyNullzone.x)
{
// treat gamepad as absolute device which is more
// like a keyboard than a joystick
if (USE_GAMEPAD) {
locUseJoy = 0;
locDirection = (JOYRAW_X / 255);
locDirection *= CalcSteeringScale(myID);
} else {
// using joystick
locUseJoy = 1;
locDirection = (JOYRAW_X - joyNullzone.x) / (255 - joyNullzone.x);
locDirection *= CalcSteeringScale(myID);
}
} else {
locUseJoy = 0;
locDirection = (KEY_RIGHT-KEY_LEFT) + joy_force.x;
locDirection *= CalcSteeringScale(myID);
}
// steering control
if (locUseJoy == 0) {
vehicle[myID].targetSteer += locDirection * accelSteering * time_step;
} else {
vehicle[myID].targetSteer = 30 * locDirection;
}
vehicle[myID].targetSteer = clamp(vehicle[myID].targetSteer, -30, 30);
if ((vehicle[myID].angSpeed > vehicle[myID].properties.maxAngSpeed * 0.95) && abs(vehicle[myID].targetSteer) > 7) {
EventBrake(myID); // play squeaky tires
}
if (locDirection != 0) {
phcon_setparams2(vehicle[myID].wheelFL, vector(vehicle[myID].targetSteer, vehicle[myID].targetSteer, 0), nullvector, vector(vehicle[myID].properties.suspensionERP, vehicle[myID].properties.suspensionCFM, 0));
phcon_setparams2(vehicle[myID].wheelFR, vector(vehicle[myID].targetSteer, vehicle[myID].targetSteer ,0), nullvector, vector(vehicle[myID].properties.suspensionERP, vehicle[myID].properties.suspensionCFM, 0));
} else {
vehicle[myID].targetSteer *= 1 - (time_step * 0.25);
if (abs(vehicle[myID].targetSteer) < 1) {
vehicle[myID].targetSteer = 0;
} // force to 0
phcon_setparams2(vehicle[myID].wheelFL, vector(vehicle[myID].targetSteer, vehicle[myID].targetSteer, 0), nullvector, vector(vehicle[myID].properties.suspensionERP, vehicle[myID].properties.suspensionCFM, 0));
phcon_setparams2(vehicle[myID].wheelFR, vector(vehicle[myID].targetSteer, vehicle[myID].targetSteer, 0), nullvector, vector(vehicle[myID].properties.suspensionERP, vehicle[myID].properties.suspensionCFM, 0));
}
if (KEY_HORN || JOY_HORN)
{
EventHorn(); //sound horn
}
wait(1);
}
}
///////////////////////////////////////////////////////////////////////////////
// CarControl: Assigning this to your car's chassis is all you have to do
///////////////////////////////////////////////////////////////////////////////
// skill1: Vehicle_ID 1
// skill2: Vehicle_type 1
action CarInit()
{
int myID = my.skill1;
// build a car and wait til all wheels are ready
ChassisInit();
my.material = mat_metal;
while( (vehicle[myID].pRL == NULL) || (vehicle[myID].pRR == NULL) ) { wait(1); }
while( (vehicle[myID].pFL == NULL) || (vehicle[myID].pFR == NULL) ) { wait(1); }
// Setup vehicle characteristics.
UpdateSpeed(); // update rear wheel speed (continuous)
SpeedControl();// change speed (continuous)
SteerControl();// change heading (continuous)
}
physics_events.c
//////////////////////////////////////////////////////////////
// Program modified by Alessandro Manotti
// Original code taken from A7 buggy demo.
// Last-update: 24-Jan-2008
// History:
// 24-Jan-2008
// * Original code
//////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
// Particle and event function
///////////////////////////////////////////////////////////////////
// helper function: sets the vector to random direction and length
function vec_randomize (var* vec, range) {
vec[0] = random(1) - 0.5;
vec[1] = random(1) - 0.5;
vec[2] = random(1) - 0.5;
vec_normalize(vec,random(range));
}
// helper function: fades out a particle
function part_alphafade(PARTICLE* p) {
p.alpha -= time_step;
p.size += time_step;
if (p.alpha <= 0) {
p.lifespan = 0;
} else {
p.lifespan=5;
}
}
// particle function: generates a fading explosion into vel direction
function smokeAction(PARTICLE* p) {
var locSpeed;
vec_randomize(temp, 1.5);
// extract passed values
locSpeed = p.vel_x;
p.vel_x = 0;
vec_add (p.vel_x, temp);
p.bmap = bmpSmoke;
set(p, MOVE);
p.event = part_alphafade; // change to a shorter, faster function
if (random(2) > 1) {
// every other particle is white
set(p, BRIGHT);
p.alpha = 10 + random(10);
p.size = 1 + locSpeed * random(10);
} else {
p.alpha = 5 + random(25);
p.size = 5 + locSpeed * random(30);
}
}
//////////////////////////////////////////////////////////////////////////////////////////
//
function CreateTrack(ENTITY* myEntity) {
return(-1); // only for WMB blocks.
// int myID; myID = (int)myEntity.skill1;
//
// // only create track if we have moved a bit
// vec_set(temp, vehicle[myID].pChassis.x);
// vec_sub(temp, vehicle[myID].lastPos);
// if (vec_length(temp)<=(trackLen*0.2))
// {
// return ;
// } else {
// vec_set(lastPos, pChassis[whoami].x);
// }
//
// VECTOR pos;
// vec_set(temp, vector(trackLen*0.5,0,0));
// vec_rotate(temp, pChassis[whoami].pan);
// vec_set(pos, temp);
// vec_add(pos, pRL[whoami].x);
// c_trace(pos, vector(pos.x, pos.y, -5),
// IGNORE_PASSABLE+IGNORE_PASSENTS+IGNORE_MODELS+IGNORE_SPRITES
// );
// if (trace_hit)
// {
// target.z+=1; //raise above track
// you=ptr_for_handle(arrpTracks[currTrack]);
// vec_set(you.x, target); reset(you,INVISIBLE);
// vec_set(you.pan, pChassis[whoami].pan); you.tilt+=90;
// currTrack+=1; if (currTrack>=NUM_TRACKS) { currTrack=0; }
// }
// vec_set(pos, temp);
// vec_add(pos, pRR[whoami].x);
// c_trace(pos, vector(pos.x, pos.y, -5),
// IGNORE_PASSABLE+IGNORE_PASSENTS+IGNORE_MODELS+IGNORE_SPRITES
// );
// if (trace_hit)
// {
// target.z+=1; //raise above track
// you=ptr_for_handle(arrpTracks[currTrack]);
// vec_set(you.x, target); reset(you,INVISIBLE);
// vec_set(you.pan, pChassis[whoami].pan); you.tilt+=90;
// currTrack+=1; if (currTrack>=NUM_TRACKS) { currTrack=0; }
// }
}
//////////////////////////////////////////////////////////////////////////////////////////
//
function EventBrake(int myID) {
var locWheelDir[3];
var locSpeed;
if (!snd_playing(hTireSnd_n)) {
hTireSnd_n = snd_play (tires_wav, 55, 0);
while (snd_playing(hTireSnd_n)) {
vec_set(locWheelDir, vector(-15, random(16)-8, -15));
locSpeed = (abs(vehicle[myID].angSpeed) / (vehicle[myID].properties.maxAngSpeed));
vec_rotate(locWheelDir, vehicle[myID].pChassis.pan);
vec_set(temp, vehicle[myID].pRL.x);
vec_add(temp, locWheelDir);
effect(smokeAction, 5, temp, vector(locSpeed, 0, 0));
vec_set(temp, vehicle[myID].pRR.x);
vec_add(temp, locWheelDir);
effect(smokeAction, 5, temp, vector(locSpeed, 0, 0));
//CreateTrack(myID);
wait(random(4));
}
}
}
//////////////////////////////////////////////////////////////////////////////////////////
//
function EventJump(ENTITY* myEntity) {
int myID = (int)myEntity.skill1;
// jump!
if ((KEY_JUMP || JOY_JUMP) && !vehicle[myID].jumping)
{
var locJumpTimer = 1;
vehicle[myID].jumping = 1;
snd_play (crash_wav, 70, 0);
while (locJumpTimer > 0)
{
locJumpTimer -= time_step;
vec_set(temp, vector(0, 0, (vehicle[myID].properties.massWheel * 4 + vehicle[myID].properties.massChassis) * vehicle[myID].properties.jumpForce));
vec_rotate(temp, vehicle[myID].pChassis.pan);
phent_addcentralforce(vehicle[myID].pFL, temp);
phent_addcentralforce(vehicle[myID].pFR, temp);
phent_addcentralforce(vehicle[myID].pRL, temp);
phent_addcentralforce(vehicle[myID].pRR, temp);
wait(1);
if (DoShutdown) { return; } // quitting?
}
wait(-vehicle[myID].properties.jumpRechargeDelay); // wait a while before jumping again
vehicle[myID].jumping = 0;
}
}
//////////////////////////////////////////////////////////////////////////////////////////
//
//////////////////////////////////////////////////////////////////////////////////////////
var Horn_handle; //cg
function EventHorn() {
// if (!hornPlaying) { cg
if (!snd_playing (Horn_handle)) {
//hornPlaying=1; cg
Horn_handle = snd_play (horn_wav, 100, 0);
//sleep(0.5); cg
//hornPlaying=0; cg
}
}
//////////////////////////////////////////////////////////////////////////////////////////
// check if we crashed at high speed, then disassemble car
//////////////////////////////////////////////////////////////////////////////////////////
var falp_handle; //cg
function EventImpact() {
int myID = (int)my.skill1;
if (!snd_playing (falp_handle)) {
falp_handle=snd_play (flap_wav, 60, 0);
} //cg
if (!DoSteering || DoShutdown) {
EventHorn();
return;
}
if (vehicle[myID].properties.bDentCar!=0 && vehicle[myID].linSpeed>(CRASH_SPEED*0.7) ) {
//_/find closest vertex and dent the car\__________________
//_________________________________________________________
var i = 0;
var k = 0;
var temp99[3];
var min_val=500;
var min_i;
while (i < ent_vertices(my)) {
vec_for_vertex(temp99, my, i);
//k=vec_dist(temp99, you.x);
k=vec_dist(temp99, target.x);
if(k<min_val) {
min_val = k;
min_i = i;
}
i += 1;
}
vec_for_mesh(temp, my, min_i);
vec_scale(temp, 0.9);
vec_to_mesh(temp, my, min_i);
ent_fixnormals(my, 0);
////////////////////////////
}
if(vehicle[myID].linSpeed>CRASH_SPEED && abs(normal.z)<0.1) {// heads on into wall
DoSteering = 0;
snd_play(crash_wav, 100, 0);
wait(1);
phcon_remove(vehicle[myID].wheelFL);
phcon_remove(vehicle[myID].wheelFR);
phcon_remove(vehicle[myID].wheelRL);
phcon_remove(vehicle[myID].wheelRR);
phent_setdamping(vehicle[myID].pFL, 80, 50);
phent_setdamping(vehicle[myID].pFR, 80, 50);
phent_setdamping(vehicle[myID].pRL, 80, 50);
phent_setdamping(vehicle[myID].pRR, 80, 50);
phent_setdamping(vehicle[myID].pChassis, 80, 50);
var locTimer = 80;
while (locTimer > 0) {
locTimer -= 1;
vec_set(temp, vehicle[myID].pChassis.x);
//vec_add(temp, wheelDir);
effect(smokeAction, 10, temp, vector(1, 0, 0));
wait(-0.1);
}
}
}
physics_init.c
//////////////////////////////////////////////////////////////
// Program modified by Alessandro Manotti
// Original code taken from A7 buggy demo.
// Last-update: 24-Jan-2008
// History:
// 24-Jan-2008
// * Original code
//////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////
// INIT
STRING* dummy_pcx = "dummy.pcx";
//////////////////////////////////////////////////////////////
// setup all 4 wheels
function InitWheel() {
int myID = (int)my.skill1;
// set(my, SHADOW);
if (vehicle[myID].commonWheelHeight <= -100000) {
vehicle[myID].commonWheelHeight = my.z; // store z for other wheels to come
} else {
my.z = vehicle[myID].commonWheelHeight; // Set all wheels to same height
}
vec_set(my.skill51, my.x); // store pos/orientation for reset
vec_set(my.skill54, my.pan);
phent_settype(my, PH_RIGID, PH_SPHERE);
phent_setmass(my, vehicle[myID].properties.massWheel, PH_SPHERE);
phent_setgroup(my, 2);
phent_setfriction(my, vehicle[myID].properties.fricWheel);
phent_setdamping(my, 15, vehicle[myID].properties.dampWheel);
phent_setelasticity(my, 0, 100); // bounciness
}
// skill1: Vehicle_ID 1
action FLInit() {
int myID = my.skill1;
set(my,PASSABLE);
while(!vehicle[myID].pChassis) { wait(1); }
vehicle[myID].pFL = my;
InitWheel();
// make wheel constraint pointing up and towards center
vehicle[myID].wheelFL = phcon_add(PH_WHEEL, vehicle[myID].pChassis, my);
phcon_setparams1(vehicle[myID].wheelFL, my.x, vecUp, vecRight);
phcon_setparams2(vehicle[myID].wheelFL, vector(0, 0, 0), nullvector, vector(vehicle[myID].properties.suspensionERP, vehicle[myID].properties.suspensionCFM, 0));
reset(my,PASSABLE);
}
// skill1: Vehicle_ID 1
action FRInit() {
int myID = my.skill1;
set(my,PASSABLE);
while (!vehicle[myID].pChassis) { wait(1); }
vehicle[myID].pFR = my;
InitWheel();
// make wheel constraint pointing up and towards center
vehicle[myID].wheelFR = phcon_add(PH_WHEEL, vehicle[myID].pChassis, my);
phcon_setparams1(vehicle[myID].wheelFR, my.x, vecUp, vecRight);
phcon_setparams2(vehicle[myID].wheelFR, vector(0,0,0), nullvector, vector(vehicle[myID].properties.suspensionERP, vehicle[myID].properties.suspensionCFM,0));
reset(my,PASSABLE);
}
// skill1: Vehicle_ID 1
action RLInit() {
int myID = my.skill1;
set(my,PASSABLE);
while (!vehicle[myID].pChassis) { wait(1); }
vehicle[myID].pRL = my;
InitWheel();
// make wheel constraint pointing up and towards center
vehicle[myID].wheelRL = phcon_add(PH_WHEEL, vehicle[myID].pChassis, my);
phcon_setparams1(vehicle[myID].wheelRL, my.x, vecUp, vecRight);
phcon_setparams2(vehicle[myID].wheelRL, vector(0,0,0), nullvector, vector(vehicle[myID].properties.suspensionERP, vehicle[myID].properties.suspensionCFM,0));
reset(my,PASSABLE);
}
// skill1: Vehicle_ID 1
action RRInit() {
int myID = my.skill1;
set(my,PASSABLE);
while (!vehicle[myID].pChassis) { wait(1); }
vehicle[myID].pRR = my;
InitWheel();
// make wheel constraint pointing up
vehicle[myID].wheelRR = phcon_add(PH_WHEEL, vehicle[myID].pChassis, my);
phcon_setparams1(vehicle[myID].wheelRR, my.x, vecUp, vecRight);
phcon_setparams2(vehicle[myID].wheelRR, vector(0,0,0), nullvector, vector(vehicle[myID].properties.suspensionERP, vehicle[myID].properties.suspensionCFM,0));
reset(my,PASSABLE);
}
//////////////////////////////////////////////////////////////
// create a whole bunch of track marks at startup
function InitMarks(ENTITY* myEntity) {
int myID = (int)myEntity.skill1;
while (!vehicle[myID].pChassis) { wait(1); }
var locCounter = NUM_TRACKS - 1;
while (locCounter >= 0) {
you = ent_createlocal(dummy_pcx, vector(-5000, -900, 20), 0);
vehicle[myID].arrpTracks[locCounter] = handle(you);
locCounter -= 1 ; //you.overlay= OFF;
you.scale_y = vehicle[myID].trackLen;
you.scale_x = 10;
set(you, INVISIBLE|PASSABLE|TRANSLUCENT|UNLIT);
you.pan = 0.1;
you.alpha = 90;
}
}
//////////////////////////////////////////////////////////////
// construct car
function EventImpact();
function ChassisInit() {
int myID = (int)my.skill1;
DoShutdown = 0;
DoSteering = 1;
vec_set(my.skill51, my.x); // store pos/orientation for reset
vec_set(my.skill54, my.pan);
pFocus = my;
set(my, SHADOW|PASSABLE);
vec_set(vecRight, vector(0, -1, 0)); // reset right vector (in case of level restart)
vec_rotate(vecRight, my.pan); // rotate right vector to correspond to car orientation
phent_settype(my, PH_RIGID, PH_BOX);
phent_setmass(my, vehicle[myID].properties.massChassis, PH_BOX);
phent_setgroup(my, 2); // all car parts in group2 ->no Collisions
phent_setfriction(my, vehicle[myID].properties.fricChassis);
phent_setdamping(my, vehicle[myID].properties.dampChassis, vehicle[myID].properties.dampChassis);
phent_setelasticity(my, 10, 100); // little bouncing
vehicle[myID].pChassis = my;
// set event handler if we hit something
vehicle[myID].pChassis.event = EventImpact;
vehicle[myID].pChassis.emask = ENABLE_FRICTION;
while( (vehicle[myID].pRL == NULL) || (vehicle[myID].pRR == NULL) ) { wait(1); }
while( (vehicle[myID].pFL == NULL) || (vehicle[myID].pFR == NULL) ) { wait(1); }
reset(my,PASSABLE);
ph_setgravity(vector(0, 0, -gravity));
InitMarks(my);
// wait for shutdown and remove everything
while (1) {
if (DoShutdown) {
var i;
vehicle[myID].targetSpeed = 0;
vehicle[myID].angSpeed = 0;
ph_setgravity(vector(0, 0, 0));
phcon_remove(vehicle[myID].wheelFL);
phcon_remove(vehicle[myID].wheelFR);
phcon_remove(vehicle[myID].wheelRL);
phcon_remove(vehicle[myID].wheelRR);
phent_settype(vehicle[myID].pChassis, 0, 0);
phent_settype(vehicle[myID].pFL, 0, 0);
phent_settype(vehicle[myID].pFR, 0, 0);
phent_settype(vehicle[myID].pRL, 0, 0);
phent_settype(vehicle[myID].pRR, 0, 0);
return;
}
wait(-0.5);
}
}
HOW TO USE IT?
Its usage is very easy.
STEP 1
Decide how many cars at the same time, maximum, you will have in your application. You should not indicate the maximum number of cars in total in your program, but only the ones that will work at the same time. Modify the #define in the source code physics_car.c:
#define MAX_CARS_COUNT 12 // Max number of cars available at the same time. #define MAX_CARS_DETAILS_COUNT 3 // Max number of different car types (off-road/sport/etc...)
In this case you will be able to use 12 cars at the same time, and 3 different cars setup (e.g.: a sport-car, a jeep, a big-foot).
STEP 2
Pleas check two key-structs, called TVehicle and TVehicleProperties. The first struct contains all info regarding a vehicle (you will not need to "interact" with it; this struct contains internal runtime vehicle parameters). The second struct is very important for you, in-fact you need to fill-in it in order to define physic information of a specific car type. Fill the data regarding the vehicle details using the code contained in function game_startup() (physics_car.c).
STEP 3
Now you can simply create the vehicles in WED, and assigning the actions the the chassis and tires. Finally assign a number to skill1 - Vehicle ID, starting from 0 (zero). NOTE: The same id will identify the same vehicle. E.g.: a car could have a chassis with code "1", then four tires with the same code "1".
Traction: Rear or 4WD!
Next step is creating a car with a rear traction, front traction, or 4WD. Well, it is very easy. In the source code "physics_car.c" look for "// max. reverse is 1/4 of forward speed" (without apex!). You will find the following code:
// max. reverse is 1/4 of forward speed vehicle[myID].targetSpeed= clamp(vehicle[myID].targetSpeed, -vehicle[myID].properties.maxAngSpeed*0.25, vehicle[myID].properties.maxAngSpeed); phcon_setmotor(vehicle[myID].wheelRL, nullvector, vector(vehicle[myID].targetSpeed, locTorque,0), nullvector); phcon_setmotor(vehicle[myID].wheelRR, nullvector, vector(vehicle[myID].targetSpeed, locTorque,0), nullvector);
If you want to add 4WD system, add the following lines:
if(activate4Tires == 1) {
//FRONT
phcon_setmotor(vehicle[myID].wheelFL, nullvector, vector(vehicle[myID].targetSpeed, locTorque,0), nullvector);
phcon_setmotor(vehicle[myID].wheelFR, nullvector, vector(vehicle[myID].targetSpeed, locTorque,0), nullvector);
}
In this way you can use the variable "activate4Tires" to insert/deactivate 4WD system! It's great!
Thank you.
Author: Alessandro Manotti
Original post: [1]
