Hello am looking for help with smothing out 2D movement. Am using Cinemachine for the camera system though it seems to jitter even when using a basic follow camera. I’ve notice this jitter in both my parallax background and on my player character. Not both at once however the background jitter only happens when my cininmachine brain has been set to smart update. Anyother setting also makes the player jitter. I’ve link a video showing this.
So what could be causing this?
Things I’ve tried
- Setting the player’s rigidbody interpolate mode to interpolate
- I’ve tried serveral parllax scripts all result in the same result
- Using different update methods on the Cinimachine Brain and in my Parallax script
Current Code for Parllax and Movement
Player Controller
using System.Collections;
using Gameplay_Scripts;
using Gameplay_Scripts.General_Components;
using Status_Effect_System;
using UnityEngine;
using UnityEngine.InputSystem;
namespace Player_Scripts
{
/// <summary>
/// Handles all player movement and player input
/// </summary>
public class PlayerController : MonoBehaviour
{
[Header("Movement Settings")]
[Tooltip("Amount of force added when the player jumps")]
public float jumpForce = 400f;
[Tooltip("How fast the player can run")]
public float runSpeed = 35f;
[Tooltip("How fast the player falls")]
public float fallspeed = 69;
[Tooltip("How much speed the player has in air")]
public float inAirSpeed = 69;
[Tooltip("How much to smooth out the movement")]
[Range(0, .3f)] [SerializeField] private float movementSmoothing = 0.05f;
[Tooltip("Whether or not a player can steer while jumping")]
[SerializeField] private bool hasAirControl = false;
[Tooltip("How fast the player accelerates")]
[SerializeField] private float playerAcceleration = 10f;
[SerializeField] private PlayerLegs myLegs;
#region Local Vars
private Rigidbody2D myRigidbody2D = null;
private bool isFacingRight = true; // For determining which way the player is currently facing.
private Health hpComp;
private Vector3 currentVelocity = Vector3.zero;
private float defaultRunspeed = 0f;
#endregion
#region Player Controls
private PlayerControls controls = null;
private float horizontalMove = 0f;
#endregion
#region Player Components
//private Animator myAnimator = null;
private Health healthComponent = null;
private Rigidbody2D myRigidBody2D = null;
#endregion
private void Awake()
{
myRigidbody2D = GetComponent<Rigidbody2D>();
hpComp = GetComponent<Health>();
myLegs = transform.GetComponentInChildren<PlayerLegs>();
defaultRunspeed = runSpeed;
controls = new PlayerControls();
healthComponent = GetComponent<Health>();
myRigidBody2D = GetComponent<Rigidbody2D>();
controls.Player.Jump.started += OnJumpPressed;
controls.Player.DebugButton.started += OnDebugPress;
}
private void OnEnable()
{
controls.Player.Enable();
}
private void OnDisable()
{
controls.Player.Disable();
}
#region Input Actions
/// <summary>
/// Called when jump button is pressed
/// </summary>
/// <param name="context"></param>
private void OnJumpPressed(InputAction.CallbackContext context)
{
if (CanPlayerMove())
{
Jump();
}
}
/// <summary>
/// Debug button
/// </summary>
/// <param name="context"></param>
private void OnDebugPress(InputAction.CallbackContext context)
{
Debug.Log(PlayerState.CurrentMiteCount);
}
#endregion
#region Movement Functions
/// <summary>
/// Check for player movement input and gun input
/// </summary>
private void Update()
{
if (CanPlayerMove())
{
CheckForMoveInput();
}
}
/// <summary>
/// Check for jump input if true set movement state to jumping
/// </summary>
private void FixedUpdate()
{
if (CanPlayerMove())
{
Move(horizontalMove * Time.fixedDeltaTime, true);
}
}
/// <summary>
/// Check to see if there is any input from the player
/// </summary>
private void CheckForMoveInput()
{
if (healthComponent.IsCurrentlyDead) return;
horizontalMove = controls.Player.Movement.ReadValue<Vector2>().x;
/*switch (transform.localEulerAngles.y >= 180)
{
case true:
myAnimator.SetFloat("Speed", -horizontalMove);
break;
case false:
myAnimator.SetFloat("Speed", horizontalMove);
break;
}*/
if (horizontalMove == 0)
{
//myAnimator.SetBool("Idle", true);
}
else
{
//myAnimator.SetBool("Idle", false);
}
}
/// <summary>
/// Actually move the player based the move direction
/// </summary>
/// <param name="move"></param>
/// <param name="forceFlip"></param>
private void Move(float move, bool forceFlip)
{
if (hpComp.IsCurrentlyDead) return;
//Check to see if player is in air and applies in air speed if the player is in the air and is jumping
if (myRigidbody2D.velocity.y < 0 || myRigidbody2D.velocity.y > 0 && !myLegs.IsGrounded)
{
runSpeed = inAirSpeed;
}
else
{
runSpeed = defaultRunspeed;
}
//only control the player if grounded or airControl is turned on
if (!myLegs.IsGrounded && !hasAirControl) return;
// Move the character by finding the target velocity
var velocity = myRigidbody2D.velocity;
Vector3 targetVelocity = new Vector2(move * runSpeed * playerAcceleration, velocity.y);
// And then smoothing it out and applying it to the character
myRigidbody2D.velocity = Vector3.SmoothDamp(velocity, targetVelocity, ref currentVelocity, movementSmoothing);
// If the input is moving the player right and the player is facing left...
if (move > 0 && !isFacingRight && forceFlip)
{
// ... flip the player.
// Switch the way the player is labeled as facing.
isFacingRight = !isFacingRight;
var transform1 = transform;
var rotation = transform1.rotation;
rotation = new Quaternion(rotation.x, 180f, rotation.z, 0f);
transform1.rotation = rotation;
//transform.SetPositionAndRotation();
//GeneralFunctions.FlipObject(gameObject);
}
// Otherwise if the input is moving the player left and the player is facing right...
else if (move < 0 && isFacingRight && forceFlip)
{
// Switch the way the player is labeled as facing.
isFacingRight = !isFacingRight;
var transform1 = transform;
var rotation = transform1.rotation;
rotation = new Quaternion(rotation.x, 0f, rotation.z, 0f);
transform1.rotation = rotation;
//GeneralFunctions.FlipObject(gameObject);
}
}
/// <summary>
/// Add Upward Velocity to make the player jump
/// </summary>
private void Jump()
{
if (!myLegs.IsGrounded) return;
// Remove all force towards player
myRigidbody2D.angularVelocity = 0f;
myRigidbody2D.velocity = Vector2.zero;
// Apply the actual jump force
myRigidbody2D.AddForce(new Vector2(0f, jumpForce));
}
/// <summary>
/// Called when player touches ground
/// </summary>
private void OnLanding()
{
//myAnimator.SetBool("IsJumping", false);
}
/// <summary>
/// Completely stop all current movement on player will also completely freeze all incoming movement then sleeps the player rigidbody
/// </summary>
public void StopMovement()
{
myRigidbody2D.angularVelocity = 0;
myRigidbody2D.velocity = Vector2.zero;
currentVelocity = Vector3.zero;
myRigidbody2D.constraints = RigidbodyConstraints2D.FreezeAll;
myRigidbody2D.Sleep();
}
/// <summary>
/// Will reset player movement and wake up the players rigidbody
/// </summary>
public void ResetMovement()
{
myRigidbody2D.WakeUp();
myRigidbody2D.constraints = RigidbodyConstraints2D.None;
myRigidbody2D.freezeRotation = true;
}
/// <summary>
/// Checks to see if player can currently move
/// </summary>
/// <returns></returns>
private bool CanPlayerMove()
{
return !ControlDisabled && !healthComponent.IsCurrentlyDead && !GeneralFunctions.IsConsoleOpen();
}
#endregion
#region Properties
/// <summary>
/// Checks to see if the player is stunned
/// </summary>
public bool ControlDisabled { get; private set; } = false;
/// <summary>
/// Reference to the players box collider
/// </summary>
public BoxCollider2D MyBoxCollider { get; private set; } = null;
#endregion
}
}
Parllax Code - Used this script Parallax scrolling using orthographic camera - Questions & Answers - Unity Discussions
Parallax Camera
using UnityEngine;
namespace Map_Control_Scripts
{
[ExecuteInEditMode]
public class ParallaxCamera : MonoBehaviour
{
public delegate void ParallaxCameraDelegate(float deltaMovement);
public ParallaxCameraDelegate ONCameraTranslate;
private float _oldPosition;
private void Start()
{
_oldPosition = transform.position.x;
}
public void LateUpdate()
{
if (transform.position.x == _oldPosition) return;
var position = transform.position;
var delta = _oldPosition - position.x;
ONCameraTranslate?.Invoke(delta);
_oldPosition = position.x;
}
}
}
Parallax Layer
using UnityEngine;
namespace Map_Control_Scripts
{
[ExecuteInEditMode]
public class ParallaxLayer : MonoBehaviour
{
public float parallaxFactor;
public void Move(float delta)
{
var transform1 = transform;
var newPos = transform1.localPosition;
newPos.x -= delta * parallaxFactor;
transform1.localPosition = newPos;
}
}
}
Parallax Background
using System.Collections.Generic;
using UnityEngine;
namespace Map_Control_Scripts
{
[ExecuteInEditMode]
public class ParallaxBackground : MonoBehaviour
{
public ParallaxCamera parallaxCamera;
private readonly List<ParallaxLayer> _parallaxLayers = new List<ParallaxLayer>();
private void Start()
{
if (parallaxCamera == null)
if (Camera.main is { })
parallaxCamera = Camera.main.GetComponent<ParallaxCamera>();
if (parallaxCamera != null)
parallaxCamera.ONCameraTranslate += Move;
SetLayers();
}
private void SetLayers()
{
_parallaxLayers.Clear();
for (var i = 0; i < transform.childCount; i++)
{
var layer = transform.GetChild(i).GetComponent<ParallaxLayer>();
if (layer == null) continue;
layer.name = "Layer-" + i;
_parallaxLayers.Add(layer);
}
}
private void Move(float delta)
{
foreach (var layer in _parallaxLayers)
{
layer.Move(delta);
}
}
}
}