0 votes

I'm taking my first steps with Godot and trying a simple scene with a player ship and a meteor to knock around. I just set up a couple of RigidBody2D objects and gave them each a sprite and one or more CollisionDetection2D objects. All I was expecting was to be able to move the player ship, and for it to 'kick' the other object when they collide.

Everything works as I expected it to until either object gets wrapped to the other side of the screen, after that they don't collide. However, if both objects get wrapped on the same side of the screen, the collision detection works as expected. If I take the ship through multiple screen wraps, it doesn't collide with the meteor until I've reversed the same number of screen wraps.

I assume something funky is happening with the CollisionDetection2D objects, but I don't know what steps to take to debug (I tried Debug > Visible Collision Shapes and everything looked OK).

Any hints on debugging, or even outright solutions, would be most appreciated.

The project is available at https://drive.google.com/file/d/1Es3N-ScafuEtVPTM5E7BwAztXaA5addJ/view?usp=sharing (the sprites are from the Kenny pack)

But for convenience here is the script which is attached to the player, followed by the one attached to the meteor.

extends RigidBody2D

var screensize
var thrust_power = 5
var brake_power = 40
var torque_power = 200

func _ready():
    screensize = get_viewport().size

func _integrate_forces(state):
    var thrust = Vector2( (Input.get_action_strength("thrust") * thrust_power), 0)
    var torque = (Input.get_action_strength("clockwise") * torque_power) - (Input.get_action_strength('anticlockwise') * torque_power)
    thrust = thrust.rotated(rotation)

    if Input.is_action_pressed("brake"):
        thrust.x = 0 - (state.linear_velocity.x / brake_power)
        thrust.y = 0 - (state.linear_velocity.y / brake_power)

    state.apply_impulse(Vector2(), thrust)
    state.apply_torque_impulse(torque)

    position.x = wrapf(position.x, 0, screensize.x)
    position.y = wrapf(position.y, 0, screensize.y)

The meteor script:

extends RigidBody2D

var screensize

func _ready():
    screensize = get_viewport().size

# Prefixed 'state' with underscore based on message from debugger, to indicate
# that it's knowingly not used
func _integrate_forces(_state):
    position.x = wrapf(position.x, 0, screensize.x)
    position.y = wrapf(position.y, 0, screensize.y)
in Engine by (15 points)

Have you tried changing the meteor position in the _state.transform passed to_integrate_forces() instead of modifying position directly?

Yes. I replaced the code affecting position, in both the Meteor and Player scripts, with this:

var xform = state.transform
xform.origin.x = wrapf(xform.origin.x, 0, screensize.x)
xform.origin.y = wrapf(xform.origin.y, 0, screensize.y)
state.transform = xform

1 Answer

+1 vote
Best answer

You should never set the position of a RigidBody2D directly (as you're doing in _integrate_forces) - I'd assume those assignments for screen wrapping are breaking the physics system...

Rather than try to explain the issue here, KidsCanCode has a perfect article for this case. See specifically The Position Problem here...

https://kidscancode.org/godot_recipes/physics/godot3_kyn_rigidbody1/

by (12,475 points)
selected by

Thankyou, that's enabled me to code the screen wrap correctly. KidsCanCode looks like a really useful resource as well.

Welcome to Godot Engine Q&A, where you can ask questions and receive answers from other members of the community.

Please make sure to read How to use this Q&A? before posting your first questions.
Social login is currently unavailable. If you've previously logged in with a Facebook or GitHub account, use the I forgot my password link in the login box to set a password for your account. If you still can't access your account, send an email to webmaster@godotengine.org with your username.