2d movement solution
- by Phil
Hi!
I'm making a simple top-down tank game on the ipad where the user controls the movement of the tank with the left "joystick" and the rotation of the turret with the right one. I've spent several hours just trying to get it to work decently but now I turn to the pros :)
I have two referencial objects, one for the movement and one for the rotation. The referencial objects always stay max two units away from the tank and I use them to tell the tank in what direction to move. I chose this approach to decouple movement and rotational behaviour from the raw input of the joysticks, I believe this will make it simpler to implement whatever behaviour I want for the tank.
My problem is 1; the turret rotates the long way to the target. With this I mean that the target can be -5 degrees away in rotation and still it rotates 355 degrees instead of -5 degrees. I can't figure out why. The other problem is with the movement. It just doesn't feel right to have the tank turn while moving. I'd like to have a solution that would work as well for the AI as for the player. A blackbox function for the movement where the player only specifies in what direction it should move and it moves there under the constraints that are imposed on it.
I am using the standard joystick class found in the Unity iPhone package.
This is the code I'm using for the movement:
public class TankFollow : MonoBehaviour {
//Check angle difference and turn accordingly
public GameObject followPoint;
public float speed;
public float turningSpeed;
void Update()
{
transform.position = Vector3.Slerp(transform.position, followPoint.transform.position, speed * Time.deltaTime);
//Calculate angle
var forwardA = transform.forward;
var forwardB = (followPoint.transform.position - transform.position);
var angleA = Mathf.Atan2(forwardA.x, forwardA.z) * Mathf.Rad2Deg;
var angleB = Mathf.Atan2(forwardB.x, forwardB.z) * Mathf.Rad2Deg;
var angleDiff = Mathf.DeltaAngle(angleA, angleB);
//print(angleDiff.ToString());
if (angleDiff > 5) {
//Rotate to
transform.Rotate(new Vector3(0, (-turningSpeed * Time.deltaTime),0));
//transform.rotation = new Quaternion(transform.rotation.x, transform.rotation.y + adjustment, transform.rotation.z, transform.rotation.w);
}
else if (angleDiff < 5) {
transform.Rotate(new Vector3(0, (turningSpeed * Time.deltaTime),0));
//transform.rotation = new Quaternion(transform.rotation.x, transform.rotation.y + adjustment, transform.rotation.z, transform.rotation.w);
}
else {
}
transform.position = new Vector3(transform.position.x, 0, transform.position.z);
}
}
And this is the code I'm using to rotate the turret:
void LookAt()
{
var forwardA = -transform.right;
var forwardB = (toLookAt.transform.position - transform.position);
var angleA = Mathf.Atan2(forwardA.x, forwardA.z) * Mathf.Rad2Deg;
var angleB = Mathf.Atan2(forwardB.x, forwardB.z) * Mathf.Rad2Deg;
var angleDiff = Mathf.DeltaAngle(angleA, angleB);
//print(angleDiff.ToString());
if (angleDiff - 180 > 1) {
//Rotate to
transform.Rotate(new Vector3(0, (turretSpeed * Time.deltaTime),0));
//transform.rotation = new Quaternion(transform.rotation.x, transform.rotation.y + adjustment, transform.rotation.z, transform.rotation.w);
}
else if (angleDiff - 180 < -1) {
transform.Rotate(new Vector3(0, (-turretSpeed * Time.deltaTime),0));
//transform.rotation = new Quaternion(transform.rotation.x, transform.rotation.y + adjustment, transform.rotation.z, transform.rotation.w);
print((angleDiff - 180).ToString());
}
else {
}
}
Since I want the turret reference point to turn in relation to the tank (when you rotate the body, the turret should follow and not stay locked on since it makes it impossible to control when you've got two thumbs to work with), I've made the TurretFollowPoint a child of the Turret object, which in turn is a child of the body. I'm thinking that I'm making it too difficult for myself with the reference points but I'm imagining that it's a good idea. Please be honest about this point.
So I'll be grateful for any help I can get!
I'm using Unity3d iPhone. Thanks!