Hi everyone ! I’m implementing a push object mechanic in my game, but i’ve noticed a strange jitter while pushing the object. The fixed fps rate in project settings is set to 60 fps, and my moniter maximum refresh rate is 75 hz.
This is the code that is responsible for pushing the item :
player.gd
func _physics_process(delta: float) -> void:
if push_ray.is_colliding(): ## push_ray is a raycast
_crate = push_ray.get_collider() as Crate
if _crate.is_on_floor() && action_handler.move().x != 0.0:
_crate.slide(data.push_force * orientation * 15.0 * delta) # 15.0 is a speed mutiplier
else:
if _crate:
_crate.velocity.x = 0.0
_crate = null
move_and_slide()
This is a video that shows this issue, at 60fps (Physics Ticks per second) :
And I tried to set Physics Ticks per second to 240 fps, and here is the result :
It’s better, but at the cost of huge performance usage.
I’m currently lost, as i’ve tried many things vainly to make the crate move smoothly.
Can you help me please ?
Is it off the table to make your crate a RigidBody2D? you could even lock rotation
Your script is very jitter-prone, setting velocity to push force (thus forcing it out of the touch zone) when touching, and zero when not. You would do better to leverage the physics system more; even getting slide collisions for it’s depth would be a better “push” value, but I think RigidBody2D would suite best.
@gertkeno
OK. So i’ve tried rigidbody, and the result is better, at 60 physics ticks per seconds.
My new code is this:
player.gd
func _physics_process(delta: float) -> void:
for i in get_slide_collision_count():
var collision = get_slide_collision(i)
var collision_crate = collision.get_collider()
if collision_crate.is_in_group("Crate"):
collision_crate.apply_central_impulse(-collision.get_normal() * data.push_force * delta)
move_and_slide()
I encounter an issue though :
The crate stops randomly against a tile. How to solve this ? (Sorry to bother you)
Probably a floating point rounding issue, maybe when applying the force you can nudge the block up half a pixel? Very cludgy, but it’s going to be hard to tell a microscopic gap from a great wall; if only we had rounded edges.
Obviously if the floor was a solid plane this wouldn’t happen, but that’s no fun to design levels manually placing collisions like that.
Ok so after a morning of numerous tests, i’ve ended up by using a character body 2d for my crate as its physic behaviour was quite unpredictable with rigidbody2D, and it was stopping against some tiles. Moreover, there was still some jittering.
So i’ve changed its type to characterbody2D and i’ve changed the code a little bit to make it work (I’m currently using a finite state machine) :
player_push_state.gd
func _physics_process(delta: float) -> void:
player.velocity.x = action.move().x * data.push_force * delta
apply_push_force()
func apply_push_force():
for i in player.get_slide_collision_count():
var collision = player.get_slide_collision(i)
var collision_crate = collision.get_collider()
if collision_crate.is_in_group("Crate"):
collision_crate.velocity.x = player.velocity.x
As for the crate :
crate.gd
extends CharacterBody2D
class_name Crate
@onready var floor_ray_l: RayCast2D = $FloorRay_L
@onready var floor_ray_r: RayCast2D = $FloorRay_R
@onready var player_ray_r: RayCast2D = $PlayerRay_R
@onready var player_ray_l: RayCast2D = $PlayerRay_L
func _physics_process(delta: float) -> void:
if !_on_floor() or !_contact():
velocity.x = 0.0
if !is_on_floor():
velocity.y += 1500 * delta
move_and_slide()
func _on_floor() -> bool:
return floor_ray_l.is_colliding() or floor_ray_r.is_colliding()
func _contact() -> bool:
return player_ray_r.is_colliding() or player_ray_l.is_colliding()
Physics ticks per second is 75, that is the same as my monitor refresh rate, so i’ve managed to get rid of the jitter, which is barely noticeable.
If Physics ticks per second is set to 120, jitter will disappear.
I hope that, if you encounter some day the same issue as me, this article will help you resolve that issue