I am working on an endless runner game for Android by Unity. I dont want to use a kinematic rigidbody. So physics is involved but the rigidbody is supposed to run along a predefined path by default. (and jumps or changes lanes by user actions). Moving straight is easy. I have done that but I want to have the next stage in the game where there are turns. It seems to work but it sometimes gets jittery and the turning isnt as smooth as I want it to be. And if I increase the speed, the player gets wonky. Could you please help me to optimize the code to get a smoother turns no matter what the speed is.
As far as I searched I couldnt find an answer on internet probably people are using kinematic rigidbodies more often in order not to deal with physics. So I use .AddForce
and .AddTorque
. I now use prefabs with predefined turns (road pieces). So it is spawned as the player moves along. Each road prefab has a spline (a free asset based on Unity 2015 procedural spline generation video I suppose) for the moving path. So the player is picking up a node along the spline and sets it as target and uses its rotation to turn towards using the AddTorque.
Maybe it is easier if I switch to kinematic rigidbody. Maybe that is ideal but I insist on doing this for the sake of learning physics and some people might find it useful for another project as there isnt enough resources on this.
void FixedUpdate()
{
if (!jump)
{
//maxangle = Mathf.Clamp(r.velocity.magnitude * 2f,3,15f);
maxangle = r.velocity.magnitude;
r.constraints = RigidbodyConstraints.None;
r.constraints = RigidbodyConstraints.FreezeRotationZ | RigidbodyConstraints.FreezeRotationX;
TurnToTarget(transform, sample.Rotation,target, maxangle);
r.constraints = RigidbodyConstraints.None;
r.constraints = RigidbodyConstraints.FreezeRotationZ | RigidbodyConstraints.FreezeRotationX | RigidbodyConstraints.FreezeRotationY;
}
//Debug.Log(currentroad.transform.name + maxangle);
if (!GameManager.gameManager.dead && running)
{
r.isKinematic = false;
//Debug.Log(transform.position.y);
var speed = r.velocity.magnitude;
Vector3 directionOfTarget = (target - transform.position).normalized;
if (speed < runspeed)
{
//r.velocity += Vector3.forward * 1f;
Debug.Log(r.velocity.z+ " " + r.velocity.magnitude);
Debug.Log(directionOfTarget);
r.AddForce(directionOfTarget* (runspeed-speed), ForceMode.VelocityChange);
}
if (transform.position.y > 2.7f)
{
r.mass = 50000f;
Physics.gravity = new Vector3(0, -100f, 0);
}
if (grounded)
{
r.mass = 10f;
Physics.gravity = new Vector3(0, -10f, 0);
}
private void TurnToTarget(Transform transform, Quaternion targetrot, Vector3 movePoint, float maxTurnAccel)
{
Vector3 directionOfTarget = (movePoint -transform.position).normalized;
Vector3 directionInEulers = targetrot.eulerAngles;
Vector3 offsetInEulers = ClampHeading(directionInEulers) - ClampHeading(transform.eulerAngles);
offsetInEulers = ClampHeading(offsetInEulers);
//optional
Vector3 angularVelocity = r.angularVelocity / Time.fixedDeltaTime;
if (offsetInEulers.sqrMagnitude < Mathf.Pow(maxTurnAccel, 2))
{
if (offsetInEulers.y < 0)
{
if (angularVelocity.y < offsetInEulers.y)
{
offsetInEulers.y = -offsetInEulers.y;
}
}
else
{
if (angularVelocity.y > offsetInEulers.y)
{
offsetInEulers.y = -offsetInEulers.y;
}
}
if (offsetInEulers.x > 0)
{
if (angularVelocity.x < -offsetInEulers.x)
{
offsetInEulers.x = -offsetInEulers.x * 2;
}
}
else
{
if (angularVelocity.x > -offsetInEulers.x)
{
offsetInEulers.x = -offsetInEulers.x * 2;
}
}
if (offsetInEulers.z > 0)
{
if (angularVelocity.z < -offsetInEulers.z)
offsetInEulers.z = -offsetInEulers.z * 2;
}
else
{
if (angularVelocity.z > -offsetInEulers.z)
offsetInEulers.z = -offsetInEulers.z * 2;
}
}
offsetInEulers = ClampVector(offsetInEulers, -maxTurnAccel, maxTurnAccel);
//Debug.Log(currentroad + " " + offsetInEulers + " " + r.angularVelocity + " " + directionOfTarget + " " + ClampHeading(directionInEulers)+" " +transform.eulerAngles);
r.AddRelativeTorque(transform.up * offsetInEulers.y);
//r.AddTorque(offsetInEulers*r.velocity.magnitude);
}