Search code examples
unity-game-enginegame-physicsgame-developmenttopdown

Is there a better way to write this top down car mdoel


I Am writing a top down car drift game, where i want to make the car move in a circle and user controls the radius of the circle with the steering, basically straight steering is a straight line and turned left is a small circle to the left of the car which it will follow. as the car turns more (either side) i try to make the cars front turn more and more towards the centre, as if its drifting.

Am using unity and this is how i though i should design the model.

  1. This is for a mobile game. so i have written a steering controller for the car, to control its left and right, the speed is constant.

  2. There are two rigid bodies.

car_fig_0

  1. The front white coloured box and the big grey coloured box both are rigid bodies. connected with a hinge joint.

  2. The front box on every frame rotate a certain amount of degrees on it own z axis and moves a little forward. Thus follows a circle shape. how much is rotates is determined my the steering.

  3. I Can make both the bodies kinematic and make sure they follow a circle shape and move that way, but i also want appropriate behaviour during collisions, In the final plan there can be all kinds of collisions. So i made them kinematic and added velocities using add force methods. On it's own the front box is perfect, It rotates in circle as i change the steering the circle gets bigger everything is fine, But when i add the Other chassis box, the forces are all messed up. centrifugal forces, and the inertia messes everything.

  4. Basically i need the car to drift is perfect circles and user should be able to control the radius of the circle. What is the best way to model this.

Here is the code for both the rigid bodies.

  1. the front box code. This is just the fixed update method. Please ignore the controls related code. It'e unnecessary so i left it out.

    {
    //-------------------------
    // I N I T.
    //-------------------------
    
    current_steering_angle = controls_i.ToSignedRotation(controls_i.getSteeringAngle());
    current_steering_angle = (current_steering_angle < 0) ? (current_steering_angle + 90): current_steering_angle;
    current_steering_angle -= 45;
    
    //-------------------------
    // S P E E D.
    //-------------------------
    
    current_speed = Utils.curve_map_linear(0, 45, min_speed, max_speed, Mathf.Abs(current_steering_angle), -1);
    _rigid_body.AddRelativeForce(acceleration * Vector3.up);
    velocity = transform.InverseTransformVector(_rigid_body.velocity);
    if(velocity.y > current_speed){
        velocity.y = current_speed;
    }
    // to prevent reverse movement due to centrifugal force.
    velocity.x = 0;
    
    //-------------------------
    // R O T A T I O N.
    //-------------------------
    
    current_radius = Mathf.Sign(current_steering_angle) * Utils.exp_range_3(0, 45, min_radius, max_radius, Mathf.Abs(current_steering_angle), steering_tension, -1);
    current_rot_rate = (velocity.y / (2 * Mathf.PI * current_radius)) * 360;
    Vector3 angle = transform.localEulerAngles;
    angle.z += (current_rot_rate * Time.fixedDeltaTime);
    transform.localEulerAngles = angle;
    
    //-------------------------
    // A P P L Y.
    //-------------------------
    _rigid_body.velocity = transform.TransformVector(velocity); 
    
    }
    

The Utils.exp_linear and exp_range functions are just range to range mappers with some tension, to control the sensitivity of the steering on the controls.

  1. Code for the chassis.

    {
    
    //-------------------------
    // I N I T.
    //-------------------------
    
    current_steering_angle = controls_i.ToSignedRotation(controls_i.getSteeringAngle());
    current_steering_angle = (current_steering_angle < 0) ? (current_steering_angle + 90): current_steering_angle;
    current_steering_angle = current_steering_angle - 45;
    current_steering_angle = ((float)((int)(current_steering_angle * Mathf.Pow(10, 5))))/Mathf.Pow(10,5);
    
    //-------------------------
    // R O T A T I O N.
    //-------------------------
    
    current_angle = front_transform.eulerAngles.z - _rigid_body.transform.eulerAngles.z;
    
    current_angle = (Mathf.Abs(current_angle) > 180) ? ((Mathf.Abs(current_angle) - 360) * Mathf.Sign(current_angle)) : current_angle;
    
    current_angle += Mathf.Sign(current_steering_angle) * Utils.curve_map_linear(0, 45, min_angle, max_angle, Mathf.Abs(current_steering_angle), 1);
    
    _rigid_body.transform.RotateAround(front_transform.position, Vector3.forward, current_angle);
    
    //-------------------------
    // S P E E D.
    //-------------------------
    
    Vector3 velocity = _rigid_body.transform.InverseTransformVector(_rigid_body.velocity);
    if (velocity.y < 0){
        velocity.y = 0;
    }
    _rigid_body.velocity = _rigid_body.transform.TransformVector(velocity);
    
    }
    

I would like to know how to actually implement something like this in unity. I am new to this unity stuff. I looked all over and there was a lot of stuff for 3d cars but not much on top down ones. Appreciate any kind of feedback.


Solution

  • Make it non kinematic, remove the rigidbody of the front tires and make the front tires a child of the chassis.

    Handling position:

    Make a method that looks at the car's current position, how much the player is turning, and returns the tangential velocity the car needs to go to drive in the appropriate circle:

    Vector2 GetTangentialVelocity(...) { }
    

    Then, find the difference between that velocity and the current velocity of the chassis:

    Vector2 velDiff = GetTangentialVelocity(...) - chassis_rigidbody.velocity;
    

    Then, apply that difference using AddForce with ForceType.VelocityChange:

    chassis_rigidbody.AddForce(velDiff, ForceType.VelocityChange);
    

    Handling rotation:

    Move the center of mass forward. You can do this by making the rigidbody long and put the collider at the rear of the object, or by setting RigidBody.centerOfMass:

    chassis_rigidbody.centerOfMass = Vector2.up * 0.5;
    

    Make a function similar to GetTangentialVelocity that returns the desired angular velocity, find the difference from chassis_rigidbody.angularVelocity, then use AddTorque with ForceType.VelocityChange:

    Vector2 angVelDiff = GetAngularVelocity(...) - chassis_rigidbody.angularVelocity;
    chassis_rigidbody.AddTorque(angVelDiff , ForceType.VelocityChange);