(Solved)Godot 4: Build 2D Action-Adventure Section 28 - Pushing the block

Hello, I am running in to an issue in the 2D Action Adventure for Godot, following through Udemy.

In Section 28 - “Pushing the Block” I am having an issue where when the player collides with the block it doesnt move while I am colliding with the block. Only when I release my keyboard button being pressed does the block move. It should be moving the entire time the player is colliding with the block. The block does move in the correct direction when the keyboard button is released but no movement at ll when pressed.

Block Scene:
RigidBody2D > CollisionShape2D > Sprite2D

RigidBody2D
Mass = 1kg
Gravity = 0
Lock Rotation = On
Damp = 15

CollisionShape2D
Shape = RectangleShape2D
Size = 16x16

Player Scene

extends CharacterBody2D
class_name Player

@export var move_speed: float = 100
@export var push_strength: float = 10

# Called when the node enters the scene tree for the first time.
func _ready() -> void:
	position = SceneManager.player_spawn_position
	pass

# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
	var move_vector: Vector2 = Input.get_vector("move_left", "move_right", "move_up", "move_down")
	
	velocity = move_vector * move_speed
	
	if velocity.x > 0:
		$AnimatedSprite2D.play("move_right")
	
	elif  velocity.x < 0:
		$AnimatedSprite2D.play("move_left")
		
	elif  velocity.y > 0:
		$AnimatedSprite2D.play("move_down")
	
	elif  velocity.y < 0:
		$AnimatedSprite2D.play("move_up")
		
	else:
		$AnimatedSprite2D.stop()
		
		#get the last collision
		#Check if it is the Block object
		#If it is the Block Object, push it
		
		var collision: KinematicCollision2D = get_last_slide_collision()
		if collision:
			
			var collider_node = collision.get_collider() 
			
			if collider_node is RigidBody2D:
				var collision_normal: Vector2 = collision.get_normal() 
				
				collider_node.apply_central_force(-collision_normal * push_strength) 
	
	#takes velocity to move player
	move_and_slide()

The relevant code seems to be ok, but you’ve placed it in _process(). As this is physics-related code, it should be in _physics_process(). While I wouldn’t expect that to cause the exact problem you’re seeing, it’s certainly a cause of unexpected behaviour, and it might be responsible for what you’re seeing. Try making that change and let us know how it goes =)

Sorry I don’t quite understand. I am a complete newbie here and the instructor has it under the _process() in the video.

are you saying i need to set up another thing like

func _physics_process() -> void:

and put

		var collision: KinematicCollision2D = get_last_slide_collision()
		if collision:
			
			var collider_node = collision.get_collider() 
			
			if collider_node is RigidBody2D:
				var collision_normal: Vector2 = collision.get_normal() 
				
				collider_node.apply_central_force(-collision_normal * push_strength) 
	
	#takes velocity to move player
	move_and_slide()

so?

	func _physics_process() -> void:
		var collision: KinematicCollision2D = get_last_slide_collision()
		if collision:
			
			var collider_node = collision.get_collider() 
			
			if collider_node is RigidBody2D:
				var collision_normal: Vector2 = collision.get_normal() 
				
				collider_node.apply_central_force(-collision_normal * push_strength) 
	
	#takes velocity to move player
	move_and_slide()

when i do that i get an error saying "Error at (36,1): Standalone lambdas cannot be accessed. Consider assigning it to a variable.

edit I also tried changing

# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:

to

# Called every frame. 'delta' is the elapsed time since the previous frame.
func _physics_process(delta: float) -> void:

and although it runs with no errors the same issue is persistent

Ah, yes, you’re right about that! This will be changed later on, and normally you wouldn’t place code like that in _process, so I figured that would be a good first thing to try. _process() executes as often as possible, while _physics_process() attempts to run as close to a constant 60 executions per second as possible (configurable in Project Settings). This is independent of the project’s actual rendered framerate, such that players on faster or slower machines aren’t at an unfair (dis)advantage. There are lots of legitimate things that can be done in _process() though =)

Also, despite using a tool to help me detect differences in your code, I’m now seeing something that I didn’t notice before: your entire collision logic is inside the else-statement! Very easy trap to fall into; even for very experienced devs, that’s a difficult thing to detect. Where other languages use {} braces for example to indicate codeblocks, GDScript unfortunately relies entirely on indentation for this, so an extra tab here and there will literally change how your code works. You need to be super-careful to keep that nice and clean when working with GDScript:

#indents are needed to mark codeblocks. This code will result in an error 
#because the engine will see the if-statement as empty, like an 
#incomplete English sentence.

if condition:
this_won't_work_because_it's_consisdered_outside_the_if_statement

#Each indent represents another codeblock, like folders inside folders
func no_indent() -> void:
	if one_indent:
		do_two_indents

#Indenting more than one tab-press at a time makes no difference though,
#and this is sometimes used to indicate the continuation of a long line
if condition:
				do_inside_if #this example looks weird, but works fine.

In this case, your collision code will only execute when you stop moving because that’s what causes the else statement to execute. That would be consistent with what you described.

To fix it, highlight all of this in your Script Editor, then press Shift + Tab to bring everything back by one level of indentation:

		#get the last collision
		#Check if it is the Block object
		#If it is the Block Object, push it
		
		var collision: KinematicCollision2D = get_last_slide_collision()
		if collision:
			
			var collider_node = collision.get_collider() 
			
			if collider_node is RigidBody2D:
				var collision_normal: Vector2 = collision.get_normal() 
				
				collider_node.apply_central_force(-collision_normal * push_strength) 

Note that later on, the Player-moving code and the block pushing code will be placed in separate functions for easier organization. That’s one way to help reduce this sort of problem from happening, but now that you’ve experienced this, you’ll hopefully have a better shot at catching/preventing this one in the future. Like I said though, this is a trap everyone falls into from time to time - all good =)

Thanks a ton. Was wracking my brain. I’m working these next two days so won’t get to tackle it but glad to know you found a solution and I am looking forward to implementing the change in the coming days and will be more vigilant with the indentations.

1 Like

This topic was automatically closed 20 days after the last reply. New replies are no longer allowed.