- Home /
2D Pixel Art Player Blurry/Vibrates When Moving Via Kinematic Platformer Controller
Two example gifs here: https://imgur.com/a/st2ONuD
There are two sprites in the gifs. The above sprite is enlarged and is only moving back and forth. The smaller sprite is the player sprite and is controlled via a slightly modified 2D Kinematic Platformer Controller which I made following the Unity tutorial for making a kinematic platformer controller: https://www.youtube.com/watch?v=wGI2e3Dzk_w&list=PLX2vGYjWbI0SUWwVPCERK88Qw8hpjEGd8
Here are the scripts used for the player:
Physics Object Script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PhysicsObject : MonoBehaviour
{
[Header("Custom Physics Variables")]
//0.65 is about a 50 degree angle on rotated object
public float minGroundAngleY = 0.65f;
public float gravityModifier = 1f;
public Vector2 gravity = new Vector2(0, -9.8f);
[Tooltip("Jump decay when going up")]
public float jumpDecay = 0.2f;
[Tooltip("Jump decay when player reaches apex and starts to fall")]
public float jumpApexDecay = 0.3f;
protected Vector2 targetVelocity;
protected bool grounded;
protected Vector2 groundNormal;
protected Rigidbody2D rb;
protected Vector2 velocity;
protected ContactFilter2D contactFilter;
protected RaycastHit2D[] hitBuffer = new RaycastHit2D[16];
protected List<RaycastHit2D> hitBufferList = new List<RaycastHit2D>(16);
protected const float minCheckColDist = 0.001f;
protected const float shellRadius = 0.01f;
private void OnEnable()
{
rb = GetComponent<Rigidbody2D>();
}
private void Start()
{
contactFilter.useTriggers = false;
contactFilter.SetLayerMask(Physics2D.GetLayerCollisionMask(gameObject.layer));
contactFilter.useLayerMask = true;
}
private void Update()
{
targetVelocity = Vector2.zero;
ComputeVelocity();
}
protected virtual void ComputeVelocity()
{
}
private void FixedUpdate()
{
MovementPrep();
}
private void MovementPrep()
{
velocity += gravityModifier * gravity * Time.deltaTime;
velocity.x = targetVelocity.x;
if (velocity.y > 0)
{
velocity.y -= jumpDecay * Time.deltaTime;
}
else if (velocity.y < 0)
{
velocity.y -= jumpApexDecay * Time.deltaTime;
}
grounded = false;
Vector2 moveAlongGround = new Vector2(groundNormal.y, -groundNormal.x);
Vector2 move = moveAlongGround * velocity.x * Time.deltaTime;
Movement(move, false);
move = Vector2.up * velocity.y * Time.deltaTime;
Movement(move, true);
}
private void Movement(Vector2 move, bool yMovement)
{
float colMoveDist = move.magnitude;
if (colMoveDist > minCheckColDist)
{
int count = rb.Cast(move, contactFilter, hitBuffer, colMoveDist + shellRadius);
hitBufferList.Clear();
for (int i = 0; i < count; i++)
{
hitBufferList.Add(hitBuffer[i]);
}
for (int i = 0; i < hitBufferList.Count; i++)
{
Vector2 currentNormal = hitBufferList[i].normal;
if (currentNormal.y > minGroundAngleY)
{
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[i].distance - shellRadius;
colMoveDist = modifiedDistance < colMoveDist ? modifiedDistance : colMoveDist;
}
}
rb.position = rb.position + move.normalized * colMoveDist;
}
}
PlayerController Script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : PhysicsObject
{
[Header("Player Variables")]
public float speed = 7f;
public float jumpPower = 10f;
private SpriteRenderer spriteRender;
private Animator animator;
private void Awake()
{
spriteRender = transform.GetChild(0).GetComponent<SpriteRenderer>();
animator = transform.GetChild(0).GetComponent<Animator>();
}
protected override void ComputeVelocity()
{
Vector2 move = Vector2.zero;
if (!GameManager.instance.InShop)
{
move.x = Input.GetAxis("Horizontal");
if (Input.GetButtonDown("Jump") && grounded)
{
velocity.y = jumpPower;
}
else if (Input.GetButtonUp("Jump"))
{
if (velocity.y > 0)
velocity.y *= 0.5f;
}
}
targetVelocity = move * speed;
if (Mathf.Abs(targetVelocity.x) > 0)
animator.SetBool("moving", true);
else
animator.SetBool("moving", false);
//If moving right
if (move.x > 0)
{
if (!spriteRender.flipX)
spriteRender.flipX = true;
}
//If moving left
else if (move.x < 0)
{
if (spriteRender.flipX)
spriteRender.flipX = false;
}
}
}
And here is the test script I made for the larger sprite:
Test Script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Test : MonoBehaviour
{
public float speed;
private Rigidbody2D rb;
private bool moveRight;
private void Awake()
{
rb = GetComponent<Rigidbody2D>();
moveRight = true;
}
private void FixedUpdate()
{
MoveRBObject();
}
private void MoveRBObject()
{
if (moveRight)
rb.position = rb.position + (Vector2.right * speed * Time.deltaTime);
else
rb.position = rb.position + (Vector2.left * speed * Time.deltaTime);
if (rb.position.x > 6)
moveRight = false;
else if (rb.position.x < -6)
moveRight = true;
}
}
I've searched for a solution to fix the vibrating and have tried about a half a dozen different solutions but nothing seemed to have changed the vibrating in any significant way. A few things I have tried:
Changed rb.position to rb.MovePosition() and set the Rigidbody interpolation to "Interpolate" and "Extrapolate"
Made a very simple "pixel perfect" movement script which just changed the player sprite position on LateUpdate() to be to the nearest pixel position
Attempted having the movement code be in Update() as opposed to FixedUpdate()
Tried implementing the scripts from this post I found on movement in Unity: http://www.kinematicsoup.com/news/2016/8/9/rrypp5tkubynjwxhxjzd42s3o034o8 (Not sure if I implemented them correctly or not though. They didn't seem to change anything)
Updated my graphics card drivers
I would be very grateful for any tips or suggestions on how to fix this. Please let me know if you need more information to help solve the problem.
Thanks!
Answer by MrRightclick · Jul 12, 2019 at 08:24 AM
With pixel art characters this small you might need to use a pixel perfect camera. Check out this Unity blog post about the new Pixel Perfect 2D asset: https://blogs.unity3d.com/2019/03/13/2d-pixel-perfect-how-to-set-up-your-unity-project-for-retro-8-bits-games/
Your answer
Follow this Question
Related Questions
Move/Rotate Kinematic (2D) Child with MovePosition/MoveRotation Doesn't Work 0 Answers
Rigidbody2D AddForce only working with large numbers 2 Answers
Hinge Joint 2D separating from object 0 Answers
Using Rigidbody.MovePosition With Local Position and Lerp 2 Answers
Moving Object in constant speed in one axis while be able to control the object in all axis 1 Answer