Hey, I’m pretty new to programming and have run into some problems. I’m making a simple sidescroller and right now the player can only move forward and backward and the camera follows the player, like in the Super Mario games. I watched a tutorial on how to do it, so the code looks just like it, and it works as it should, but every time the camera follows the player, the player lags a lot. If the player moves within the margins it moves normally, but as soon as the camera starts following, the player starts shaking.
I looked around in the forum and found a few similar questions with answers, but their code looked so much different than mine, so I have no idea how to apply those changes to my code. But guessing from what their problems were, maybe some stuff in the code should be in the Update function, but is not, or similar. But like I said, I’m pretty new to programming, so be gentle. Thanks in advance!
public float xMargin = 1f; // Distance in the x axis the player can move before the camera follows
public float yMargin = 1f; // Distance in the y axis the player can move before the camera follows
public float xSmooth = 8f; // How smoothly the camera catches up with the target movement in the x axis
public float ySmooth = 8f; // How smoothly the camera catches up with the target movement in the y axis
public Vector2 maxXY; // The maximum X and Y coordinates the camera can have
public Vector2 minXY; // The minimum X and Y coordinates the camera can have
private Transform player; // Reference to the player's transform
void Awake () {
// Setting up the reference
player = GameObject.FindGameObjectWithTag("Player").transform;
}
bool CheckXMargin() {
// Returns true if the distance between the camera and the player in the X asis is greater than the xMargin
return Mathf.Abs(transform.position.x - player.position.x) > xMargin;
} bool CheckYMargin() {
// Returns true if the distance between the camera and the player in the Y asis is greater than the yMargin
return Mathf.Abs (transform.position.y - player.position.y) > yMargin;
}
void FixedUpdate() {
TrackPlayer();
}
void TrackPlayer () {
// default coordinates of camera
float targetX = transform.position.x;
float targetY = transform.position.y;
// If the player has moved beyond the margin, target should be a Lerp between the camera's current pos and the player's current pos.
if(CheckXMargin())
targetX = Mathf.Lerp (transform.position.x, player.position.x, xSmooth * Time.deltaTime);
if(CheckYMargin())
targetY = Mathf.Lerp (transform.position.y, player.position.y, ySmooth * Time.deltaTime);
// The target coordinates should not be larger than the max or smaller than the min
targetX = Mathf.Clamp (targetX, minXY.x, maxXY.x);
targetY = Mathf.Clamp (targetY, minXY.y, maxXY.y);
// Set the camera's position to the target position with the same z component
transform.position = new Vector3(targetX, targetY, transform.position.z);
}
I tried to put everything in Update instead of FixedUpdate and it works much much better now, but it’s still a bit laggy, so I’m guessing it’s the Lerp lagging now.
I tried to make a Vector3.MoveTowards, but I just get errors that the arguments cannot convert float to UnityEngine.Vector3. I’ve never used Vector3.MoveTowards before. I tried looking at forums etc, but I find it difficult to apply it to my code. Do you think you could help me? How should I write the Vector3.MoveTowards?
Lerping would not work well in this situation because the player is constantly moving. If the player were to stand still while the camera is moving, then the lerping would work fine.
I think the best solution is move the camera the same amount as the player has moved, and don’t do lerping.
Try to put your code in LateUpdate to see if it helps. If not, try doing something like this in LateUpdate:
Vector3 lastPlayerPosition; //Keep track of the last player position
void LateUpdate {
Vector3 currentPlayerPosition = player.transform.position; //Get current player position
Vector3 distanceMoved = currentPlayerPosition - lastPlayerPosition; //Figure out how much the player moved since the last frame
lastPlayerPosition = currentPlayerPosition;
camera.transform.position = camera.transform.position + distanceMoved; //Move the camera
}
The bonus to using MovetTowards is it never over shoots its target. If you want smoothing then you can check out Vector3.SmoothDamp to have it slow down as it gets closer.
I’ve now tried this, and this is the code that looks best, as in it doesn’t lag at all. However, there’s no clamping, so it shows things outside of the level etc. Also, it doesn’t have a “move-margin”, like my code does. So with your code, even if I just move one step, the camera will follow. And, when the player runs into walls, normally the player starts lagging, but with your code, everything BUT the player lags. So the entire level starts shaking if the player runs into a wall.
I’ve now tried this code too, but I didn’t quite like the result. The follow-movement was really slow and just floats towards the player, but not quick enough, so the player runs out of view really quickly. And there’s no clamping etc.
I’m confused. There has to be something else wrong with your scene or maybe player position. Because, using my code, if the player runs into a wall, then the player won’t move, therefore the camera shouldn’t move, so nothing should “lag”. I suspect your player transform.position is not where you think it is in your game. Maybe you could post your code for your player and your code for where you get the keyboard input?
Also, could you post a screenshot of your hierarchy in the editor?
Yeah, I know. Something is lagging, but at least when it was just the player it was OK, but when the entire screen shakes, it’s too noticeable. I didn’t know how to clamp my screen, as it’s a sidescroller (when I did it like everyone says you should, it clamps at the original-view, and doesn’t “keep in mind” that the screen is longer than that as the player moves. So I just put colliders on the end and start of the game. And for regular objects, like islands and walls in the middle of the game, which are the most laggy with your code, just have colliders on them. Don’t know what the problem is. I could show you a pic of those if that would help?
Here’s the code for my player:
using UnityEngine;
using System.Collections;
public class PlayerMovement : MonoBehaviour {
public float speed; // walk speed
public Rigidbody2D rigidbody;
public float jumpForce = 700f;
// Ground stuff
public bool grounded = false; // default is that we are NOT grounded
public Transform groundCheck; // using our empty GameObject "groundCheck" to see where the ground is
float groundRadius = 0.2f; // how big our circle will be when we're checking for the ground
public LayerMask whatIsGround; // what is considered ground?? - Everything except the player.
// Use this for initialization
void Start () {
rigidbody = GetComponent<Rigidbody2D>();
}
// Update is called once per frame
void Update () {
// Right and left keys
if(Input.GetKey(KeyCode.RightArrow)){
transform.position += Vector3.right * speed * Time.deltaTime;
} else if(Input.GetKey (KeyCode.LeftArrow)){
transform.position += Vector3.left * speed * Time.deltaTime;
}
grounded = Physics2D.OverlapCircle(groundCheck.position, groundRadius, whatIsGround);
// Jump
if(Input.GetKeyDown (KeyCode.Space) && grounded) {
rigidbody.AddForce(new Vector2(0, jumpForce));
}
}
The problem is most likely you are moving the player. But the player has a rigidBody, so you should move the RigidBody instead of the player. Otherwise the player’s rigidBody and transorm position begin “fighting” eachother.
You should use rigidBody.MovePosition(rigidBody.position + Vector3.right * speed * Time.fixedDeltaTime);