Space fighter combat with gripping missions against an invading alien warrior race. Dodge hazardous asteroids and minefields. Vanquish enemy fighters and massive ships using skill, fortitude, and hi-tech weaponry.

Post tutorial Report RSS The player is the centre of the world

Ever wondered how game programmers are able to create large and seemingly endless worlds, yet when you try all your world objects start to shake their booty? Wonder no more.

Posted by on - Basic Client Side Coding

Note: Before I start it must be stated I am using Unity 3D and C# for this tutorial. I expect you to be familiar with the Editor and a fundamental understanding of C#. If you are using another Game Engine or programming language you will need to adapt this code to your situation, which I will not be able to help you with.

Setting the Scene

With that out of the way, first set up your scene. Add in 4 Game Object cubes and position them around. Here I have the player and 3 other game objects representing some awesome object or other set in the game world. They have also been parented to a game object called GameWorld.

Scene1

Moving the Player

Next create a C# script called MoveMe. This will be used to move the player around the game world. Copy and paste the below script over the existing MoveMe.cs code.

MoveMe.cs

using UnityEngine;
using System.Collections;

public class MoveMe : MonoBehaviour {
    Rigidbody rb;
    int velocityDirection;
    Vector3 rotationDirection;


	// Use this for initialization
    void Start () {
        rb = GetComponent<Rigidbody>();
    }

    // Update is called once per frame
    void Update() {
        if (Input.GetKeyDown(KeyCode.W)) {
            velocityDirection = 1;
        }
        else if (Input.GetKeyDown(KeyCode.S)) {
            velocityDirection = -1;
        }
        else {
            velocityDirection = 0;
        }

        if (Input.GetKeyDown(KeyCode.D)) {
            rotationDirection.y = 1;
        }
        else if (Input.GetKeyDown(KeyCode.A)) {
            rotationDirection.y = -1;
        }
        else {
            rotationDirection.y = 0;
        }

        if (Input.GetKeyDown(KeyCode.UpArrow)) {
            rotationDirection.x = 1;
        }
        else if (Input.GetKeyDown(KeyCode.DownArrow)) {
            rotationDirection.x = -1;
        }
        else {
            rotationDirection.x = 0;
        }

        if (Input.GetKeyDown(KeyCode.RightArrow)) {
            rotationDirection.z = 1;
        }
        else if (Input.GetKeyDown(KeyCode.LeftArrow)) {
            rotationDirection.z = -1;
        }
        else {
            rotationDirection.z = 0;
        }
    }

    void LateUpdate() {
        if (velocityDirection == 1) rb.AddForce(new Vector3(0, 0, 5), 
ForceMode.Impulse);
        else if (velocityDirection == -1) rb.AddForce(new Vector3(0, 0, -5), 
ForceMode.Impulse);

        if (rotationDirection.y == 1) rb.AddTorque(new Vector3(0, 0.1f, 0), 
ForceMode.Impulse);
        else if (rotationDirection.y == -1) rb.AddTorque(new Vector3(0, -0.1f, 0), 
ForceMode.Impulse);

        if (rotationDirection.x == 1) rb.AddTorque(new Vector3(0.1f, 0, 0), 
ForceMode.Impulse);
        else if (rotationDirection.x == -1) rb.AddTorque(new Vector3(-0.1f, 0, 0), 
ForceMode.Impulse);

        if (rotationDirection.z == 1) rb.AddTorque(new Vector3(0, 0, 0.1f), 
ForceMode.Impulse);
        else if (rotationDirection.z == -1) rb.AddTorque(new Vector3(0, 0, -0.1f), 
ForceMode.Impulse);
    }
}

The first run

There is nothing out of the ordinary with this code. It allows the player to control the GameObject the script is attached to. We can move forward, backwards, left, right and rotate along all three axes.

The player position and rotation is updated in the game world as you would expect.

Attach this script to your player GameObject. Go ahead and run the project to get a feel for how the code works. I’ll be waiting... ;)

You are the centre of the Universe.

Create a new GameObject, this will be used for all our GameManagement. Create a new C# script as shown below. Then attach the script on to the GameManagement.

MoveTheWorld.cs

using UnityEngine;
using System.Collections;

public class MoveTheWorld : MonoBehaviour {
    public GameObject GameWorld; //The GameObject containing all game world objects
    public GameObject CenterOfTheWorldObject; //The player or player controlled object
    private Rigidbody CenterOfTheWorldObjectRigidbody; //Rididbody of the player or player controlled object

	// Use this for initialization
    void Start () {
        CenterOfTheWorldObjectRigidbody = CenterOfTheWorldObject.GetComponent<Rigidbody>();     //Find the rigidbody
    }
	
	// Update is called once per frame
    void Update () {
        //Store the position and keep the object centre of the universe
Vector3 positionChangeFromZero = CenterOfTheWorldObject.transform.position;             CenterOfTheWorldObject.transform.position = new Vector3();

        //Store the rotation velocity and keep the object always facing forward
Vector3 rotationChangeFromZero =
CenterOfTheWorldObjectRigidbody.angularVelocity;
        CenterOfTheWorldObject.transform.eulerAngles = new Vector3();

        //For any GameObjects in the GameWorld update their position relative to the centre objects velocity
        foreach (Transform child in GameWorld.transform) {
            if (child.tag != "Player") {
                child.transform.position -= positionChangeFromZero;
            }
        }

        GameWorld.transform.Rotate(-rotationChangeFromZero);
        CenterOfTheWorldObject.transform.Rotate(rotationChangeFromZero);
    }
}

The second run

Run the program. Move around like your did the first time and noticed the different. Pause and step through the editor and check out the variables. The player position always remains at 0, only it’s rotation changes to mirror the GameWorld rotation. Job done.

A simple explanation

In the update, we store the current player position and angular velocity. Then we zero out the players position and eulerAngle (rotation in degrees).

The foreach goes through every GameObject attached to the GameWorld, the children in other words. For each child found we subtract the position we stored from the player, moving the child object instead of the player object.

We then rotate the GameWorld based on the players’ negated angular velocity and apply the angular rotation back to the player.

Conclusion

The player is truly the centre of the world. This will prevent jittery game objects and the canvas (which happens 1000 metres from Zero, Zero, Zero). You can create vast, seemingly endless words for the player to explore without worrying about on screen objects jumping around the player.

Final note: I haven’t tested this against the Physics engine, that’s on my TODO list. If you try before me, be sure to let me know of the results J

Post comment Comments
centaurianmudpig Author
centaurianmudpig - - 26 comments

I came back and realised how the player moves may not be what many developers would be doing, myself included, when looking at my game project code.

I made and very simple change which applies Torque to the player game object on it's local axis, rather than the world axis. (I've also included the project file). See this link for more info: Gracegracemedia.wordpress.com

Reply Good karma+2 votes
Post a comment

Your comment will be anonymous unless you join the community. Or sign in with your social account: