Help with paddle and ball interactions

Im making a pong game and while Ive mostly succeeded at getting the ball to work correctly It has problems with the paddle. Specifically, when the paddle hits the ball it acts more like a moving wall and it usually ends up looking more like its pushing the ball rather than launching it. In my game the paddles can freely move and rotate. What can I do to improve this? The code for the ball is this:

extends CharacterBody2D


@onready var speed_timer: Timer = $speed_timer
var screen_size: Vector2
var dir = get_ran_dir()
var speed = 100
var out_bounds: bool = false
var middle = Vector2()
var high: bool
var low: bool

func _ready() -> void:
	screen_size = get_viewport_rect().size
	position = Vector2(screen_size.x / 2, screen_size.y / 2)
	set_multiplayer_authority(multiplayer.get_unique_id())

func _physics_process(delta: float) -> void:
	var collision = move_and_collide(dir * speed * delta)
	if collision:
		speed *= 1.2
		dir = dir.bounce(collision.get_normal())
	position_tracking()
	
func get_ran_dir() -> Vector2:
	var new_dir = Vector2()
	new_dir.x = [1,-1].pick_random()
	new_dir.y = [1,-1].pick_random()
	return new_dir.normalized()

func position_tracking() -> void:
	high = position.x >= screen_size.x / 1.01 or position.y >= screen_size.y 
	low = position.x <= -screen_size.x / 1.01 or position.y <= -screen_size.y 
	middle = Vector2(screen_size.x / 2, screen_size.y / 2)
	screen_size = get_viewport_rect().size
	if high or low:
		position = middle
		speed = 100
		print("return")

and for the paddle its this:

extends CharacterBody2D

@onready var charge_time: Timer = $charge_time
@onready var ball
const SPEED = 300.0
@export var rotation_speed = 3.0 # Speed of rotation
@export var speed = 400.0 # Movement speed

func _ready() -> void:
	ball = get_parent().get_node("Ball")

func _physics_process(delta: float) -> void:
	if !is_multiplayer_authority(): return
	var direction := Input.get_axis("Left", "Right")
	var direction_hor := Input.get_axis("up", "down")
	velocity.x = direction * SPEED if direction else move_toward(velocity.x, 0, SPEED)
	velocity.y = direction_hor * SPEED if direction_hor else move_toward(velocity.y, 0, SPEED)
	move_and_slide()
	
	if Input.is_action_pressed("Rotate clockwise"):
		rotation -= rotation_speed * delta
	elif Input.is_action_pressed("Rotate counter clockwise"):
		rotation += rotation_speed * delta
	
	var collision = move_and_collide(velocity * delta)
	if collision:
		var colider = collision.get_collider()
		if colider == ball:
			ball.speed += 1000

Another problem is that for some reason upon colliding the ball sometimes drags the paddle with it after bouncing.

I’m not 100% sure what kind of behaviour you’re going for, but there are two quick fixes that resolve your issues.

The first problem you’re describing (the paddle ends up looking like it’s pushing the ball) happens because of the order in which colissions are triggered. When you “push” the ball with the paddle, the paddle will be the initiator of the collision in such cases - it moves into the ball, then the paddle collision logic is immediately triggered via move_and_slide(), which moves it back out of the ball. The ball itself doesn’t “notice” anything in this case and thus isn’t sped up.

This is likely why you have added the second collision check in there - it checks whether a collision with the ball has happened and then speeds up the ball. However, you will likely have observed that this only works inconsistently - basically, you’re telling the physics engine to apply the velocity and move the paddle back out again (via move_and_slide), then do it again (via move_and_collide) and only this time check for the ball. The second application will have slightly different results (because you don’t necessarily end up in the exact same situation you started in after “sliding” out), and thus the speedup may or may not be triggered.

This is easily resolved by only performing one such collision check and then performing the speedup if necessary. I would use move_and_slide() for simplicity.

Paddle:

func _physics_process(delta: float) -> void:
	if !is_multiplayer_authority(): return
	var direction := Input.get_axis("Left", "Right")
	var direction_hor := Input.get_axis("up", "down")
	velocity.x = direction * SPEED if direction else move_toward(velocity.x, 0, SPEED)
	velocity.y = direction_hor * SPEED if direction_hor else move_toward(velocity.y, 0, SPEED)
	
	if Input.is_action_pressed("Rotate clockwise"):
		rotation -= rotation_speed * delta
	elif Input.is_action_pressed("Rotate counter clockwise"):
		rotation += rotation_speed * delta
	
	move_and_slide()
	var collision = get_last_slide_collision()
	if collision:
		var colider = collision.get_collider()
		if colider == ball:
			ball.speed += 1000

The second problem you’re describing is that colliding the ball sometimes “drags” the paddle with it after bouncing. I’ve observed the contrary - the ball pushes the paddle sometimes and you feel like you hit a wall briefly when the ball hits the paddle. This is because both objects are affected by the collision within a frame if they move into each other at the same time, and move_and_slide() has the side effect of “stopping” objects (in this case, the paddle) when there is a direct collision.

Luckily, this is an easy fix - the ball shouldn’t be a “CharacterBody2D” as those are primarily meant for objects that are user-controlled. Instead, a “StaticBody2D” would be more suitable, which does not affect other physics bodies when it is moved (but still collide normally).

Simply change the first line of your ball code to extends StaticBody2Dand also change the Node type to StaticBody2D.

Hope this helps!

Thanks a lot. Staticbody2d worked so much better than characterbody2d.

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