Help understanding how to push and pull object

Godot Version

4.2

Question

Help understanding how to push and pull object when button is held down and only when in a area2d.

Player node: setup using the default script so not much is different there.

Box node:
Rigidbody2d
–collision2d
-sprite2d
-area2d(leftsideedge)
–collision2d(small box area)
-area2d(rightsideedge)
–collision2d(small box area)

Player doesnt collide with the box and can walk through it or past it as this is a platformer.
Trying to achieve: When the player is next to the left or right edge of the box and holds down the action key for a set peroid of time, the player will move the box depending on which directional key is also pressed.

Current:
area2d(leftsideedge) collision box setup from the edge and extrudes out left and is connected to a signal on_body_entered and exited. This has a print statement stating entered and exited. (working)
area2d(rightsideedge) collision box setup from the edge and extrudes out right and is connected to a signal on_body_entered and exited. This has a print statement stating entered and exited. (working)

I have a input action timer also which is as follows,

elif Input.is_action_pressed(“action_key”):
action_hold_time += _delta
if action_hold_time >= HOLD_ACTION_THRESHOLD and action_can_move:
action_is_moving = true
else:
action_is_moving = false
(working)
I currently have this in the box node script, should this be in the player or should it not matter?

As the player needs to walk past the box the collision on the rigidbody2d is not set to interact with the player by default so this will need to be changed when the action key is held down. Perhaps changing the layer and mask on action then revert back to default setting? Is this logical?

I have seen a number of videos on pushing objects but i cant seem to connect the while holding button move the direction your movement keys are going.

Would i be better using a KinematicBody2D vs RigidBody2D?

Thanks in advance.

I don’t know what you meant by “push and pull object”, did you mean applying forces to a `RigidBody2D? I have several questions.

  1. Did you successfully connect the area_entered signal on your Area2D?
  2. Do your input map working well?
  3. Does the code successfully handle keyboard inputs?

Don’t worry about these if you get lost, you can always use the print() function to test whether it works or not. Lastly, we can keep working on this issue together.

About the question:

We usually use KinematicBody2D for character controls, and RigidBody2D for others, like your boxes.

Push and Pull: The idea is to press the E key (player_action) and hold it down to activate the next stages of input. So as an example my player is facing the left edge of the box within the area2d collision box and holding down the player_action key.
Once this is achieved move the box by pulling it or pushing it with the directional keys.

End Goal
player_action held down and also holding the right movement button to push the box right.

Answers:

  1. I have placed print signals with in the enter and exit and the collision mask is set to detect the player. These are successful.
  2. directional input does work with movement going left or right and jump. These are successful.
  3. I have code that places items in and removes them from the box. When holding down the player_action key it will hit the 1.0 sec limit and print true when past it. also prints when i have let go. These are successful.

I did try using a characterbody2d but changed back to the rigidbody2d as you have also suggested.

Hope this is enough to go on and cheers for the help.

It shouldn’t have any problems anymore, as your code is all good. So, I’ll try posting the code that I’ll write:

# As always, I explicitly write the types of variables, parameters, and return values.

const BOX_MOVE_FORCE: float = 100
var is_player_inside: bool = false

func _on_area2d_entered(area: Area2D) -> void:
    if area is Player: # I assume your player is of the "Player" class
        is_player_inside = true

func _on_area2d_exited(area: Area2D) -> void:
    if area is Player:
        is_player_inside = false

func _physics_process(delta: float) -> void:
    if is_player_inside:
        # I assume "move_left" is your left movement action, and the same as "move_right"
        var direction := Input.get_axis("move_left", "move_right")
        if direction != 0:
            # I don't know how to get the box, but this should be your RigidBody2D box
            box.apply_force(direction * BOX_MOVE_FORCE * delta)

I can provide more precise assistance with the code if you can provide your scene tree and the code of the boxes and the areas within it.

Here is my code.

const HOLD_ACTION_THRESHOLD: float = 1.0
const IMPULSE_STRENGTH: float = 200.0
func _physics_process(_delta):
if Input.is_action_just_pressed("player_action"):
		place_item_in_container()
	elif Input.is_action_pressed("player_action"):
		action_hold_time += _delta
		if action_hold_time >= HOLD_ACTION_THRESHOLD and action_push_ready:
				action_pushing = true
				var direction := Input.get_axis("move_left", "move_right")
				if direction != 0:
					var force = Vector2(direction * IMPULSE_STRENGTH * _delta,0)
					box.apply_force(force)
		else:
			action_pushing = false
	else:
		action_hold_time = 0.0
		action_is_pulling = false

This is the handles on right side of the box.

func _on_handle_right_body_entered(body):
	if body.is_in_group("Player"):
		action_push_ready = true


func _on_handle_right_body_exited(body):
	if body.is_in_group("Player"):
		action_push_ready = false

Layer and mask set to Player = 8. This is set on the box.

Error when holding button down and moving an direction:

Invalid type in function ‘apply_force’ in base ‘RigidBody2D (Box)’. Cannot convert argument 1 from float to Vector2. — Error fixed with var force.

However new issue, the box doesnt move at all…

Will research also, thanks for the input.

1 Like

You’re using the delta parameter, I think the underscore before the delta would be the problem since Godot will assume that won’t be used.

I have now removed that but the box still doesnt move. Also facepalm momement on the delta lol

Do you think this is a config issue on the node?

You can set all properties of the box RigidBody2D back to default, print out the force variable which is passed to apply_force(), and test if these if statements could be reached.

Put some print statements in just to get things going.

elif Input.is_action_pressed("player_action"):
		action_hold_time += delta
		print("hold down key: ", action_hold_time)
		if action_hold_time >= HOLD_ACTION_THRESHOLD and action_push_ready:
				#action_pushing = true
				print("action push ready: ", action_push_ready)
				var direction := Input.get_axis("move_left", "move_right")
				print("var direction: ",direction)
				if direction != 0:
					var force = Vector2(direction * IMPULSE_STRENGTH * delta,0)
					print("force: ", force)
					box.apply_force(force)
					print("apply.force: ", box.apply_force(force))

print("hold down key: ", action_hold_time)
works gives me float value.

print("action push ready: ", action_push_ready)
shows true

print("var direction: ",direction)
works shows -1 , 0 or 1 in float values

print("force: ", force)
works shows -3 or 3 in float values

print("apply.force: ", box.apply_force(force))

Error: Trying to get a return value of a method that returns “void”
Edit: Ah i think this is me being stupid. if i remove the print statement it stops the error. However i am wondering why its value is void?

Feeling the water rise to my neck now… :grimacing:

This method doesn’t return anything as it said, void means it doesn’t return anything, calm down :sweat_smile:

Shouldn’t this be a Vector2? this looks suspicious. You might have to use strong type to handle this:

# Ensure this is a Vector2 instead of anything else.
var force: Vector2 = Vector2(direction * IMPULSE_STRENGTH * delta,0)

# Or use the type of its initial value, for we explicitly created a Vector2.
var force := Vector2(direction * IMPULSE_STRENGTH * delta,0)

Changed

var force := Vector2(direction * IMPULSE_STRENGTH * delta,0)

Here is the output for holding move_left

action push ready: true
var direction: -1
force: (-3.333333, 0)

Still no change to the box movement. I must have done something somewhere which is causing this issue.

here is the box
image

image

L and R are the handles

C is a collision for item storing

The main collision for the rigidbody is behind C

1 Like

It seems like you did a pretty good job on your game. You might have some older code that affects the box, such as your player script, but you haven’t noticed it.

I usually create a temporary statement in the _ready() method to test something. For example, you can test inside the box with this code:

func _ready() -> void:
    apply_force(Vector2(0, -1000))

I’m also wondering where the code you previously posted is attached.

This did nothing (-1000), however, if i apply -10000 it moves it up. I have also tested with -50000 and this makes it move.

apply_force(Vector2(0, -1000)) - does nothing
apply_force(Vector2(0, -10000)) - moves up
apply_force(Vector2(0, -50000)) - moves up a lot

Tested changing the IMPULSE_STRENGTH = 10000, didnt move the box. This is with the player layer and mask set to 8 on the box.
output

action push ready: true
var direction: -1
force: (-166.6667, 0)

So i have scripted all the code within the box. Do you think this logic should be within the player script and just looks for the on enter body signals of the box?

I have also just tried something with the layers and mask.

Current settings:
box collision set to,
player layer and mask = 8
world mask = 1

if i remove the player layer and only have the box on the follwoing:

player mask = 8
world mask = 1

I can push the box and with enough force pass through it. issue is thats just me moving with no keys pressed.

If i add the box back to the player layer = 8 and have the CURRENT settings it does not move. The player shouldnt be moving th box full stop with out holding down the action key, so im not sure whats going on there now… sigh…

Really appreciate the feeback and help!

The layer property defines the layer of this object, while the mask property determines the layers that this object should collide with. The issue may be stemming from this conflicting configuration.

Also, check out your box’s mass and any other properties, and try to reset them all to their default values.

Let’s pause for a moment right now to consider the desired effect rather than just the actions being taken.

1 Like

New box all defaults.

player is using the default movement script.
Current settings
player
layer:8
mask:1, 8

Box
layer: 8
mask:1,8

Zero movement.

class_name Box extends RigidBody2D

const HOLD_ACTION_THRESHOLD: float = 1.0
const IMPULSE_STRENGTH: float = 100.00 

var action_push_ready: bool = false
var action_hold_time: float = 0.0


func _physics_process(delta):
	
	if Input.is_action_just_pressed("player_action"):
		#place_item_in_container()
		pass
	elif Input.is_action_pressed("player_action"):
		action_hold_time += delta
		#print("hold down key: ", action_hold_time)
		if action_hold_time >= HOLD_ACTION_THRESHOLD and action_push_ready:
				#action_pushing = true
				print("action push ready: ", action_push_ready)
				var direction := Input.get_axis("move_left", "move_right")
				print("var direction: ",direction)
				if direction != 0:
					var force := Vector2(direction * IMPULSE_STRENGTH * delta,0)
					print("force: ", force)
					apply_force(force)
	else:
		action_hold_time = 0.0
		#action_is_pulling = false
		#print("player action button: ",Input.is_action_just_pressed("player_action"))


func _on_right_handle_body_entered(body):
	if body.is_in_group("Player"):
		action_push_ready = true
		print("ready to push: ", action_push_ready)
	pass # Replace with function body.

image

Mass 1kg gravity 1 all defaults.

The only time the box moves is when,
Box
layer: (removed)
mask:1,8
This doesnt require a button press either, just moves.

It seems to me that you cant move a object thats on the same layer and mask. I find this hard to believe…

Again, layer are the layers of this object, and mask are the layers this object will collide with.

So if the ground layer is 1, the player layer is 2, and the box layer is 8; then the player mask should be 1 and 8 to collide with the ground and the box, and the box mask should be 1 and 2 to collide with the ground and the player. Then we’ll have a configuration like:

  • Ground
    • Layer
      • 1
  • Player
    • Layer
      • 2
    • Mask
      • 1
      • 8
  • Box
    • Layer
      • 8
    • Mask
      • 1
      • 2

I have set the layers and mask as you have put and the box moves when the player walks into it.

This does not require the code above or any action key presses.

Back to the drawing board for me.

It seems to be a physics issue, if it’s not, then does the code listening to the action key work as expected?

When i press the keys they give me the print outs as expected, however, the box moves without the need to press the action key.

So makes the code useless…

How does it behave? Is it pushed by the player or sliding across a slope? It’s not affected by the box code since we know the action key checking is fine, can you post every code related to the box?

Also, let’s look at the apply_force() method. The force we applied is too small indeed, I’ve tested it, and it proved this, setting it to 1000 should take effect.