0
\$\begingroup\$

I am attempting to make a 2D racing game. So far I have a player car (sprite) and a main camera.

I would like to know how to make the main camera follow the player car while still showing signs of movement (I don't want the positions to match exactly in the middle of the screen).

My Player Car has a Rigidbody2D and a MoveCar script

using UnityEngine;
using System.Collections;

public class MoveCar : MonoBehaviour {

    public float speed = 10f;
    public float gravity = 0f;

    void CarMove () 
    {
        if(Input.GetKey(KeyCode.RightArrow))
        {
            transform.Translate(Vector2.right * speed * Time.deltaTime);
        }
    }

    // Update is called once per frame
    void Update () 
    {
        CarMove();
    }
}

Then there is my Main Camera, its projection is switched to Orthographic and has default settings as well as a FollowCar script:

using UnityEngine;
using System.Collections;

public class FollowCar : MonoBehaviour {

    public Transform player;

    public GameObject other;
    public Rigidbody2D rb = other.GetComponent<Rigidbody2D>;


    void Update()
    {
        transform.Translate(Vector2.right * .5f * Time.deltaTime);
        Vector2.MoveTowards( Vector2 , Vector2 player,
    }
}

I would like to know where to go next or if I'm even going in the right direction. I have no idea how to utilize the MoveTowards() function.

\$\endgroup\$
1
\$\begingroup\$

There are lots of ways to do this, though MoveTowards seems reasonable for your purposes.

Essentially, you'll want to determine some speed you want the camera to follow the car. Let's say that's float cameraFollowSpeed;

Then you need to adjust that to be framerate independent with Time.deltaTime

So now FollowCar's Update() your code is something like

transform.position = Vector2.MoveTowards(
         transform.position, car.transform.position,
         Time.deltaTime*cameraFollowSpeed);

I don't think you need to use transform.Translate().

As written, with the appropriate (experimentally determined) value of cameraFollowSpeed, your camera should drift lazily behind the car.

This might be enough to feel fun, or you might want to calculate the cameraFollowSpeed each frame based on the distance between the camera and car.

\$\endgroup\$
1
\$\begingroup\$

Sounds to me like what you need is to define a box in the middle of the screen, and only if the car moves outside of that box then the camera will move. That way the car has a bit of freedom to move without the camera moving, but as soon as it gets too far away from the center of the screen the camera does follow it.

This is similar to the technique used in older platform games like Super Mario, where the camera would only move once mario gets near the right edge of the screen.

I've actually written a script like this in the last game jam so I can share it:

using UnityEngine;
using System.Collections;

public class CameraTracking : MonoBehaviour {

    public Vector2 cameraSize;

    public Vector2 horizontalWorldBounds;
    public Vector2 verticalWorldBounds;

    public GameObject player;

    // Use this for initialization
    private void Start() 
    {
        transform.position = new Vector3(player.transform.position.x, player.transform.position.y, transform.position.z);
        player = PlayerController.Instance.gameObject;
    }

    // Update is called once per frame
    void Update () 
    {
        float deltaX = player.transform.position.x - transform.position.x;
        deltaX = (Mathf.Abs(deltaX) > cameraSize.x) ? (Mathf.Abs(deltaX) - cameraSize.x) * Mathf.Abs(deltaX) / deltaX : 0;

        float deltaY = player.transform.position.y - transform.position.y;
        deltaY = (Mathf.Abs(deltaY) > cameraSize.y) ? (Mathf.Abs(deltaY) - cameraSize.y) * Mathf.Abs(deltaY) / deltaY : 0;

        float width = CalculateGameWorldWidth();
        float newX = Mathf.Clamp(transform.position.x + deltaX, horizontalWorldBounds.x + 0.5f * width, horizontalWorldBounds.y - 0.5f * width);
        float newY = Mathf.Clamp(transform.position.y + deltaY, verticalWorldBounds.x + 0.5f * width, verticalWorldBounds.y - 0.5f * camera.orthographicSize);
        transform.position = new Vector3(newX, newY, transform.position.z);
    }

    private float CalculateGameWorldWidth()
    {
        return (camera.pixelWidth / camera.pixelHeight) * camera.orthographicSize;
    }
}

Attach this script to your camera. Assign the player car in "player" in the inspector.

cameraSize defines the size of the box in the middle of the screen where the player can move freely and the camera will stay put.

horizontalWorldBounds and verticalWorldBounds define the final bounds of your world, so the camera will never display anything outside these bounds even if the player moves there. It's the edges of the map. They are vector2, so x is the lower bound and y is the upper bound. These are in world coordinates.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.