Hello Unity Answers, I’m working on a 2D Platformer/RPG hybrid, and there’s an issue I’ve been struggling with for a while. When moving my character, there’s a very slight jitter whenever it reaches the edge of the camera’s Dead Zone (using a 2D Cinemachine Camera).
I’ve created my character using the [2D Game Creation tutorials][1], so I’m using a Kinematic rigidbody that moves the player using the following code, called PhysicsObject:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PhysicsObject : MonoBehaviour {
//public values for easy tweaking
[Header("Generic Physics Settings")]
public float minGroundNormalY = .65f;
public float groundedGravity = 1f;
public float gravityModifier = 1f;
//values used in this script
[HideInInspector]
public Vector2 velocity;
protected Vector2 targetVelocity;
//[HideInInspector]
public bool grounded;
protected Vector2 groundNormal;
protected Rigidbody2D rb2d;
protected ContactFilter2D contactFilter;
protected RaycastHit2D[] hitBuffer = new RaycastHit2D[16];
protected List<RaycastHit2D> hitBufferList = new List<RaycastHit2D> (16);
protected const float minMoveDistance = 0.0001f;
protected const float shellRadius = 0.01f;
public bool rightWallTouch = false;
public bool leftWallTouch = false;
//activated when the game starts
void OnEnable()
{
contactFilter.useTriggers = false;
contactFilter.SetLayerMask (Physics2D.GetLayerCollisionMask (gameObject.layer));
contactFilter.useLayerMask = true;
rb2d = GetComponent<Rigidbody2D> ();
}
void Update ()
{
targetVelocity = Vector2.zero;
ComputeVelocity ();
}
//computevelocity is used in the playercontrol script
protected virtual void ComputeVelocity()
{
}
protected void FixedUpdate()
{
if(!grounded)
{
velocity += gravityModifier * Physics2D.gravity * Time.deltaTime;
}
else if (grounded)
{
velocity += groundedGravity * Physics2D.gravity * Time.deltaTime;
}
velocity.x = targetVelocity.x;
grounded = false;
Vector2 deltaPosition = velocity * Time.deltaTime;
Vector2 moveAlongGround = new Vector2 (groundNormal.y, -groundNormal.x);
Vector2 move = moveAlongGround * deltaPosition.x;
Movement (move, false);
move = Vector2.up * deltaPosition.y;
Movement (move, true);
}
void Movement(Vector2 move, bool yMovement)
{
float distance = move.magnitude;
if (distance > minMoveDistance)
{
int count = rb2d.Cast (move, contactFilter, hitBuffer, distance + shellRadius);
hitBufferList.Clear ();
for (int i = 0; i < count; i++) {
hitBufferList.Add (hitBuffer *);*
-
}*
-
for (int i = 0; i < hitBufferList.Count; i++)*
-
{*
_ Vector2 currentNormal = hitBufferList .normal;_
* if (currentNormal.y > minGroundNormalY)*
* {*
* grounded = true;*
* if (yMovement)*
* {*
* groundNormal = currentNormal;*
* currentNormal.x = 0;*
* }*
* }*
* float projection = Vector2.Dot (velocity, currentNormal);*
* if (projection < 0)*
* {*
_ velocity = velocity - projection * currentNormal;_
* }*
_ float modifiedDistance = hitBufferList .distance - shellRadius;
* distance = modifiedDistance < distance ? modifiedDistance : distance;
}
}*_
_ rb2d.position = rb2d.position + move.normalized * distance;
* }
ComputeVelocity is done in another script: PlayerControl, which inherits the code from PhysicsObject. This is the relevant code from it:
protected override void ComputeVelocity()
{*
Vector2 move = Vector2.zero;_
* if (moveControlOn) {*
* //get the input from axises, allowing the game to recognize multiple input options*
* move.x = Input.GetAxis (“Horizontal”);*
* }*
* if (velocity.y < 0) {*
* wallJumping = false;*
* }*
* //press jump and if grounded and control on, change the vertical velocity*
if (Input.GetButtonDown (“Jump”) && grounded && jumpControlOn && Time.timeScale != 0) {
animator.SetTrigger(“jumpStart”);
//different heights when having landed once or twice
if(landedTwice)
{
velocity.y = tripleJumpHeight;
//set triple jump bool to true later, and others false
animator.SetBool(“jumpOne”, true);
}
else if(landedOnce)
{
velocity.y = highJumpHeight;
//set high jump bool to true later, and others false
animator.SetBool(“jumpOne”, true);
}
else if(!landedOnce && !landedTwice)
{
velocity.y = jumpHeight;
animator.SetBool(“jumpOne”, true);
}
//landcheck, used for checking when land booleans should be turned on and off
landCheck = false;
//play audio and particles
sfx.PlayOneShot (jumpSound, 1);
Instantiate(jumpParticleSpawner, new Vector3(transform.position.x, transform.position.y, -0.1f), Quaternion.identity);
* }*
//release jump, cut the velocity short
else if (Input.GetButtonUp (“Jump”))
* {*
* if (velocity.y > 0) {*
_ velocity.y = velocity.y * 0.5f;
* wallJumping = false;
}
}
if (grounded)
{
targetVelocity = move * maxSpeed;
wallJumping = false;
} else if (wallJumping)
{
targetVelocity = wallJumpMove * maxAirSpeed;
}
else*
* {
targetVelocity = move * maxAirSpeed;
}
}
My camera settings are like this:
[121989-0-21-2.png*|121989]*
And my Cinemachine Brain settings are like this:
[121990-0-21-3.png|121990]
Setting the Update Method to Smart or Fixed Update makes the stutter worse. Moving the player’s movement to LateUpdate instead of FixedUpdate also makes the stutter worse. The problem is only there when the player moves against the Dead Zone (regardless of how much Soft Zone there is).
I’ve created a video showcasing the stutter, which shows just how small the problem is, but nevertheless it drives me up the wall. Watch in 1080p, 60fps and keep your eyes on the tree: [Cinemachine stutter (Watch the tree) - YouTube][4]
Having googled the problem, people often remark that the character movement is jittery. But the movement seems fine in the scene view. Also, since I’ve followed tutorials made by Unity on both the movement and the camera, it seems strange that this sort of thing happens.
I hope someone can help me! Thank you in advance, and I hope I’ve given enough information regarding the issue.
[1]: Recorded Video Session: 2D Platformer Character Controller - Unity Learn
*
*
_*[4]: Cinemachine stutter (Watch the tree) - YouTube