0
\$\begingroup\$

I am trying to create camera behavior where camera moving slightly forward the player along his movement direction to extend the visible area.

enter image description here

For camera movement, I use an empty object which moves as the player object by Input.GetAxis() and clamps in some borders around the player for the later to stay in camera area.

But I don't want to simply snap camera to the target object. I want the camera to smoothly lerp forward in player's movement direction and if there is no movement anymore - smoothly lerp back to the player object.

Problem is - I can't detect presence or absence of movement of the empty target object. I thought of using this method https://answers.unity.com/questions/907078/how-to-detect-movement.html if(Mathf.Abs(rigidbody2D.velocity.y) < 0.001)

It didn't work at first because I moved my target point with transform.Translate(). When I tried rigidbody2D.Addforce() and it didn't work neither. I noticed that applied force actualy isn't reducing per time it stays the same. Then I tried rigidbody2D.MovePosition() and it also isn't working also velocity on every axis stays 0 and clamp isn't working with this method. Here is the code:

public class TargetPointBehaviour : MonoBehaviour{

[Tooltip("Used as origin point for camera movement.")]
[SerializeField]
private GameObject playerObject; //Used as origin point for camera movement

private Transform playerTransform; //players transform
private Vector2 playerPosition; // x, y player position

private Rigidbody2D targetPointRB2D; //rigidbody2D of target point object

//========================================================================================================

[Tooltip("Value to offset the camera from the player")]
[SerializeField]
private float offsetFromThePlayer; //value to determine how far target to interpolation will be

void Start()
{
    playerTransform = playerObject.GetComponent<Transform>();
    targetPointRB2D = GetComponent<Rigidbody2D>(); // assigning RigidBody2D component to variable
}

void FixedUpdate()
{
    MoveTargetPoint();
    ClampMovement();
    MoveTargetPointToPlayerPosition();
}

private void MoveTargetPoint() // moves targetPoint object by player controls
{
    GetComponent<Rigidbody2D>().MovePosition((new Vector2(Input.GetAxis("Horizontal") * 100,
        Input.GetAxis("Vertical") * 100))); // moves object by player controls
}

private void ClampMovement() //moves target point in defined borders GetComponent<Rigidbody2D>().AddForce
{
    transform.position = new Vector2(Mathf.Clamp(transform.position.x, playerTransform.position.x - offsetFromThePlayer, playerTransform.position.x + offsetFromThePlayer),
    Mathf.Clamp(transform.position.y, playerTransform.position.y - offsetFromThePlayer,
    playerTransform.position.y + offsetFromThePlayer)); //constraints movement by offsetFromThePlayer value 


}

//checks if movement of targetPoint stops and when returns it to player object coordinates
private void MoveTargetPointToPlayerPosition()
{
    Debug.Log("Velocity Y: " + targetPointRB2D.velocity.y + ". Velocity X: " + targetPointRB2D.velocity.x);

    if (Mathf.Abs(targetPointRB2D.velocity.y) < 0.01f && Mathf.Abs(targetPointRB2D.velocity.x) < 0.01f) 
    {
        StartCoroutine(DelayBeforereturningToPlayerObject());
        transform.Translate(playerTransform.position.x, playerTransform.position.y, 0);
    }
}

private IEnumerator DelayBeforereturningToPlayerObject() //delay for some time befor target returns to player object
{
    yield return new WaitForSeconds(0.1f);
}}

I didn't implemented actual camera movement yet cause don't see the point while the target for the camera isn't working. But maybe there is a more efficient way to implement what I want?

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

If you want to use the object's velocity, you need to move it with velocity / forces / impulses / accelerations. The physics engine won't infer a velocity for you from other types of position setting, and it's generally best to avoid mixing physical and non-physical movements on a single object.

If you want that velocity to go to zero when you stop providing input, you need to tell it to do so. (Newton's first law: an object in motion tends to stay in motion unless acted upon).

You could for instance do this:

[RequireComponent(typeof(Rigidbody2D))]
public class SimpleVelocityControl : MonoBehaviour {

    [Tooltip("How fast the object can move, in world units per second")]
    public float maxSpeed = 5f;
    [Tooltip("How long it takes to reach max speed, in seconds")]
    public float accelerationDuration = 0.2f;

    // Cache our physics body for later use
    Rigidbody2D _body;        
    void Start() {
        _body = GetComponent<Rigidbody2D>();
    }

    void FixedUpdate() {
        Vector2 input = new Vector2( Input.GetAxis("Horizontal"), 
                                     Input.GetAxis("Vertical")    );

        // Clamp the input to a circle (so we don't go faster diagonally)
        if(input.sqrMagnitude > 1f)
            input.Normalize();

        // Apply physics nudges to approach the desired speed
        AccelerateToward(input * maxSpeed, maxSpeed/accelerationDuration);
    }

    // Approach a desired speed without exceeding the allowed acceleration
    void AccelerateToward(Vector2 targetVelocity, float maxAcceleration) {
        // Calculate change in velocity needed.
        Vector2 deltaV = targetVelocity - _body.velocity;
        float magnitude = deltaV.magnitude;

        // Calculate a limit for what we can perform in this frame
        float limit = maxAcceleration * Time.deltaTime;

        // Cap the change at our limit
        if(magnitude > limit)
            deltaV *= limit/magnitude;

        // Apply the velocity change
        _body.velocity += deltaV;
    }
}

(There are lots of ways to improve on this basic movement, like amping up the acceleration when turning sharply or stopping, but this gets the foundation)

Now your camera follow has a legitimate velocity value to use:

public class LeadBody : MonoBehaviour {

    public Rigidbody2D target;

    [Tooltip("How far to look ahead of the object's movement, in seconds")]
    public float leadSeconds = 0.8f;

    [Tooltip("How gradual / elastic the camera move should be")]
    [Range(0.000001f, 1f)]
    public float smoothness = 0.95f;

    void LateUpdate() {
        Vector3 leadPosition = target.transform.position + (Vector3)target.velocity * leadSeconds;
        leadPosition.z = transform.position.z;

        // Correct exponential ease for framerate.
        float blend = 1f - Mathf.Pow(smoothness, Time.deltaTime * 30f);
        transform.position += (leadPosition - transform.position) * blend;
    }
}
\$\endgroup\$
  • \$\begingroup\$ Didn't realy catche this clamp thing if(input.sqrMagnitude > 1f) input.Normalize(); I applied it at my scripts instead of my clamp method but it looks like it doesn't spot anywhere while I keep any input key pressed. \$\endgroup\$ – Random generalist Mar 11 '18 at 16:27
  • \$\begingroup\$ If can happen when you're pressing the horizontal and vertical keys at once (eg. 1.0 right plus 1.0 up is greater than 1.0 diagonally in total). You might not see it hit if you're using an analog stick that's already limited to a circle. \$\endgroup\$ – DMGregory Mar 11 '18 at 16:31

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.