1
\$\begingroup\$

Context

So I'm making a clone of Crossy Roads where the camera follows the player. It does some linear interpolation (Lerp) after moving, and the camera starts moving away from the player in the positive direction (x-axis until camera reaches to a certain range where player is not visible enough). Things I have tried is by flagging it, but I think I'm doing it wrong.

Problem

I have done my camera movements accordingly, but I am having an issue where the conditions are not properly met. I'm get the offset camera after not moving, but it does not the Lerp, and vice-versa. I want both to happen after a certain condition after the game starts. When the player moves, the camera follows it in Lerp. However, once the player is "Idle", its still Lerping. I want the camera to continue by itself and at the same time focus at the player's object.

Example

Camera with Lerp, but not moving away from the player

Camera Lerps after moving, but it doesn't move away from the player

Camera moving away, but not following player with lerp

enter image description here

Code

CameraController.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/*
** Camera following player (Smoothing and angle): https://youtu.be/4HpC--2iowE
** Maximo: https://www.youtube.com/watch?v=bXNFxQpp2qk&ab_channel=iHeartGameDev
** Moving character relative to camera: https://forum.unity.com/threads/moving-character-relative-to-camera.383086/
** Camera follow v2: https://youtu.be/Jpqt2gRHXtc?list=PLq_nO-RwB516fNlRBce0GbtJSfysAjOgU
*/

public class CameraController : MonoBehaviour
{
    public GameObject player;
    public PlayerControl playerControlScript;
    private Vector3 newCameraPos;
    public bool stillIdle;


    void Start()
    {
        stillIdle = false; 
        PlayerControl playerControlScript = GetComponent<PlayerControl>();
    }

    void LateUpdate()
    {
        player = GameObject.FindGameObjectWithTag("Player");
        if (playerControlScript.GetfirstInput()) //True
        {
            stillIdle = true;
            newCameraPos = Vector3.Lerp(transform.position, playerControlScript.transform.position, Time.deltaTime);
            transform.position = new Vector3(newCameraPos.x, 1, newCameraPos.z);
        }
        if (stillIdle)
            transform.position = new Vector3(transform.position.x + 0.69f * Time.deltaTime, transform.position.y, transform.position.z); //Moving camera away effect
    }
}

PlayerControl.cs

public class PlayerControl : MonoBehaviour
{
    bool firstInput;
    Vector3 startPos;
    Vector3 endPos;
    public bool GetfirstInput() //I was learning how to have a Get function while my member was private from another script file
    {
        return firstInput;
    }
    void Update()
    {
         if (Input.GetButtonDown("up") || Input.GetButtonDown("left") || Input.GetButtonDown("right") || Input.GetButtonDown("down"))
        {
            //if game starts
            {
                //Other variables being initialized here
                firstInput = true;
            }
        }
        
    }
}

Hierarchy/Inspector

Main Camera

enter image description here

Player Object

enter image description here

Some help would be appreciate it. I feel I have been staring at this problem and I bet it is something minimal and small from just thinking it.

Let me know if you need clarifications. I'm happy to edit and answer them for everyone

\$\endgroup\$
0

2 Answers 2

0
\$\begingroup\$

Maybe try Cinemachine? It's a package that pretty much gives the facilities to easily produce camera follow, camera bounds, etc.

I was thinking maybe just find out if the player is above the centre of the screen, and if so, lerp the camera towards them until they are at centre screen. Then, apply a constant movement every frame:

void FixedUpdate()
{
   if(<Camera camera>.WorldToScreenPoint(<Transform player>.position).y >= Screen.height/2){<CinemachineVirtualCamera cameraController>.Follow = <Transform player>;}
   else{<CinemachineVirtualCamera cameraController>.Follow = null;}
   camera.transform.Translate(<float cameraSpeed> * <Vector3 cameraDirection> * Time.deltaTime);
}

Make sure to insert using Cinemachine; at the top of your script, and replace <anything in these brackets> with their respective values. Hope it works, I haven't had time to test. (If it doesn't, feel free to send the error code and I will try and fix it for you)

\$\endgroup\$
7
  • \$\begingroup\$ Is your example with Cinemachine? If so, I don't have Cinemachine purchased. Otherwise, I would give it a try. I currently have it somewhat working as intended. It just needs more tweaking. \$\endgroup\$ Commented Nov 15, 2022 at 9:36
  • \$\begingroup\$ @ZeidTisnes Cinemachine is free, you can install it in the Unity editor \$\endgroup\$ Commented Nov 15, 2022 at 16:15
  • \$\begingroup\$ Actually? It shows its 25$. What is the link exactly? \$\endgroup\$ Commented Nov 15, 2022 at 20:10
  • \$\begingroup\$ Agree, Cinemachine is built for camera movements and it performs well even on mobile devices. \$\endgroup\$ Commented Nov 16, 2022 at 2:16
  • \$\begingroup\$ @ZeidTisnes you can install it in the editor just go to Window > Package manager > Packages: Unity Registry > Cinemachine > Install \$\endgroup\$ Commented Nov 16, 2022 at 18:25
0
\$\begingroup\$

Create a two-part system

If I'm reading the question correctly, you have three rules:

  • The camera should catch up quickly if player when the player moves ahead of it
  • The camera should slowly move forward at all times
  • The camera's movement should be smooth (for example, by lerp)

I suggest creating a two-part system, where an invisible "Camera Target" game object enforces the first two rules, and the camera is only responsible for the third.

The Camera Target

The camera target should be a non-rendering game object which "teleports" to wherever the camera should be moving toward. It can enforce the first two rules by the following:

  • Every FixedUpdate, move the target some distance forward
  • When the player moves, if the target is behind them, snap it to their current position.

This separate target can be useful if you want to change the rules for camera following. For example, maybe after playtesting you decide that you only want to force the camera forward if the player gets into the top 20% of the screen, instead of just the top 50%. If you need to make changes, it will be easier to implement if the target logic is decoupled from the smoothness logic.

The camera itself

Once you know where you want the camera to be, all the camera needs to do is move towards the target during FixedUpdate. I recommend using FixedUpdate rather than Update for both of these objects to make sure your camera movement is not framerate-dependent.

You also might want to consider using something besides lerp for your smoothness. Using lerp in this way can doesn't strictly guarantee that the player will always be on screen - though in practice, it basically does for most reasonable speeds.

\$\endgroup\$
1
  • \$\begingroup\$ Thank you for the approach. William Peng helped me out (link below) in SO with the problem and I'm looking forward to improve it too. The idea of the 20% of the screen seems to be approachable. Currently, what we have was LateUpdate so the camera doesn't stutter when movement happens. If I get hands on Cinemachine, maybe I can play more with different kind of smoothness, but it was something I had in mind at first. stackoverflow.com/a/74426159/6017248 (Stackoverflow post) \$\endgroup\$ Commented Nov 16, 2022 at 8:00

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .