- Home /
Top Down car/vehicle movement
Hey.
I have been looking on google for tutorials/scripts, but couldn't find anything. Anyway i am having a problem. I have a top down controller which is very basic right now. You just get the horizontal and vertical axis and AddForce in that direction. So like i said very very basic. It can also rotate to the current moving direction.
However, right now it is suitable for a humanoid-like movement. But i would like a more vehicle style approach. So that the player can not make sharp turns, but instead would turn slowly which is determined by a turnSpeed variable.
Well i followed a tutorial: https://www.youtube.com/watch?v=amsUjRLMuDc <- this one! Which is great for a 3rd person view camera. But i have a Top-Down view, and i would like that the forward direction is always pointed up (if that makes sense) and that the right direction is always pointed right, not the right of the vehicle's transform.
So eventually you would have the vehicle moving towards the joystick angle.
Does anyone know how to do this? Or has a link of a tutorial that is covering this?
My script(for the moment) is:
using UnityEngine;
using System.Collections;
using UnityStandardAssets.CrossPlatformInput;
public class Player : MonoBehaviour {
public float speed = 5;
public float turnSpeed = 5;
private Rigidbody rb;
private Vector3 v;
private Transform myTransform;
Vector3 lookPos;
void Start()
{
rb = gameObject.GetComponent<Rigidbody>();
myTransform = transform;
}
void Update()
{
}
void FixedUpdate()
{
float horizontal = CrossPlatformInputManager.GetAxis("Horizontal");
float vertical = CrossPlatformInputManager.GetAxis("Vertical");
float r_horizontal = CrossPlatformInputManager.GetAxis("r_horizontal");
float r_vertical = CrossPlatformInputManager.GetAxis("r_vertical");
Moving(horizontal, vertical);
//Rotation(r_horizontal, r_vertical);
}
void Moving(float horizontal, float vertical)
{
Vector3 movement = new Vector3(horizontal, 0, vertical);
float angle = Mathf.Atan2(horizontal, vertical) * Mathf.Rad2Deg;
rb.AddForce(movement * speed / Time.deltaTime);
myTransform.rotation = Quaternion.Euler(new Vector3(0, angle, 0));
}
void Rotation(float horizontal, float vertical)
{
float angle = Mathf.Atan2(horizontal, vertical) * Mathf.Rad2Deg;
myTransform.rotation = Quaternion.Euler(new Vector3(0, angle, 0));
}
}
Anyway thanks in advance, i am really stuck here. So any help is appreciated!
EDIT!!!
Here is an example picture of what i am trying to achieve. The blue dot is the player, the green dot is the joystick(the black arrow is the movement direction) The light blue line with the greyish arrow in it is the actual movement. The red arrow is the player's current facing direction.
1 is what i am getting right now and 2 is what i want.
So if the player is facing up, and the player presses right, it will turn according to the turningSpeed to the right. While making a turn, the player transform/rigidbody also rotates to the "targetRotation".
Cheers, Darryl
Answer by ijidau · Nov 09, 2015 at 05:50 AM
Ok, this is a little bit complicated but I experimented and figured out a solution.
It works by finding a Quaternion from the combination of horizontal and vertical input. This is the target rotation. The rotation is then smoothly transitioned from it's current rotation to the target rotation over a period of time (turnRate). A relative force is added to get the rigidbody to move forward in the direction it is facing. This works, with some help from a conditional to prevent it resetting target rotation to up (2D top down forwards) if the input is zero.
However, If you apply force to a rigidbody and the rotation doesn't match this 'direction', it will appear to drift like a spaceship without friction. Disable lines 37 onwards to see what I mean. So, you need to calculate the sideways drift occurring, then apply some form of traction force to counter this. Basically you need to simulate that the tyres should not be allowed to slide sideways. This is the most complicated part of the script, but hopefully the included comments clarify how this works.
N.B. the RigidBody2D in my test has gravity scale set to zero (it's a top down game, so doesn't need gravity) and it has a linear drag of 1 (simulates rolling friction), angular drag of 0.05.
using UnityEngine;
using System.Collections;
public class CarDriveInDirection : MonoBehaviour {
private Rigidbody2D rb;
private float hInput = 0.0f;
private float vInput = 0.0f;
public float speed = 200.0f;
public float turnRate = 3.0f;
Quaternion targetRotation;
void Start()
{
rb = GetComponent<Rigidbody2D>();
targetRotation = Quaternion.identity;
}
void Update()
{
hInput = Input.GetAxis("Horizontal");
vInput = Input.GetAxis("Vertical");
}
void FixedUpdate()
{
Vector3 move = new Vector3(hInput, vInput, 0);
if(move.x != 0.0f || move.y != 0.0f)
{
targetRotation = Quaternion.LookRotation(Vector3.forward, move);
transform.rotation = Quaternion.RotateTowards(transform.rotation, targetRotation, turnRate);
rb.AddRelativeForce(Vector3.up * speed * Time.fixedDeltaTime);
}
// Get a left or right 90 degrees angle based on the rigidbody current rotation velocity
float steeringRightAngle;
if(rb.angularVelocity > 0) {
steeringRightAngle = -90;
} else {
steeringRightAngle = 90;
}
// Find a Vector2 that is 90 degrees relative to the local forward direction (2D top down)
Vector2 rightAngleFromForward = Quaternion.AngleAxis(steeringRightAngle, Vector3.forward) * Vector2.up;
// Calculate the 'drift' sideways velocity from comparing rigidbody forward movement and the right angle to this.
float driftForce = Vector2.Dot(rb.velocity, rb.GetRelativeVector(rightAngleFromForward.normalized));
// Calculate an opposite force to the drift and apply this to generate sideways traction (aka tyre grip)
Vector2 relativeForce = (rightAngleFromForward.normalized * -1.0f) * (driftForce * 10.0f);
rb.AddForce(rb.GetRelativeVector(relativeForce));
}
}
I tried to implement this, but this doesn't change anything. I still move directly to the angle that i am pointing to. I edited my post with a picture of what i am trying to achieve.
Cheers, Darryl
I've updated this solution based on a previous script I've written. This is somewhat more complicated, but it takes into account a very important aspect; drift.
Also note that you can replace Quaternion.RotateTowards with Quaternion.Slerp for a smoother and less 'arcade' turning style:
transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, turnRate * Time.fixedDeltaTime);
Wow thats exactly what i meant, however. Since i have never used Rigidbody2D, is there an equivalent version to this script for a normal Rigidbody? I tried editing it, but there is no GetRelativeVector for a normal Rigidbody.
The game that i am currently working on is set in a 3D environment, so i dont know if a Rigidbody2D would be the best solution.
Cheers, Darryl
Glad you like it. I've not tried this in 3D yet, not sure when I'll next have time to experiment unfortunately. The concepts will be the same though, so you should be able to hack away and figure it out.
Oh $$anonymous$$an Really Thank you. it is working like charm. Again Thanks.