Momentum transfers

Im trying to make it so that when the paddle in my pong game hits the ball to the same direction it was already moving towards it gets a speed boost and acts like it was impacted instead just being pushed by the paddle like this:

| → O →

I want my game’s physics to be like the game, “paddle force” on itch.io
My paddle code:

extends CharacterBody2D

@onready var charge_time: Timer = $charge_time
@onready var ball
const SPEED = 300.0
@export var rotation_speed = 3 # Speed of rotation
@export var speed = 100.0 # Movement speed
var speed_up: float = 0
var is_moving: bool = false
var rotation_delta: float = 0
var current_rotation: float = 0
@onready var paddle_image: Sprite2D = $Paddle_image
@onready var size: float = 0


func _ready() -> void:
	ball = get_parent().get_node("offline_ball")
	size = paddle_image.texture.get_size().x
	charge_time.timeout.connect(on_charge_timeout)

func _physics_process(delta: float) -> void:
	if !is_multiplayer_authority(): return
	current_rotation = rotation
	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
		
	is_it_moving()

func is_it_moving() -> void:
	rotation_delta = abs(rotation - current_rotation)
	is_moving = velocity.length() > 0 or rotation_delta > 0.001
	if is_moving and !charge_time.is_stopped():
		pass  # timer already running
	elif is_moving:
		charge_time.start(0.2)  # start 1-second timer
	else:
		speed_up = 0
		charge_time.stop()

func on_charge_timeout() -> void:
	if is_moving:
		speed_up = min(speed_up + 10, 300)
		print(speed_up)
		charge_time.start(0.2)  # restart timer for next second

My ball code:

extends StaticBody2D

@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
var can_collide := true
@onready var paddle
@onready var physics: Node = $Ball_physics


func _ready() -> void:
	paddle = get_parent().get_node("Offlinepaddle")
	screen_size = get_viewport_rect().size
	position = Vector2(screen_size.x / 2, screen_size.y / 2)

func _physics_process(delta: float) -> void:
	var collision = move_and_collide(dir * speed * delta)
	if can_collide and collision:
		var collider = collision.get_collider()
		if collision && collider != paddle:
			speed *= 1.2
			dir = dir.bounce(collision.get_normal())
			temp_pause()
		elif collision && collider == paddle:
			speed += paddle.speed_up
			position += collision.get_normal() * 4
			dir = dir.bounce(collision.get_normal())
			temp_pause()
	position_tracking()

func temp_pause() -> void:
	physics.disabled = true
	can_collide = false
	speed_timer.start()

func _on_speed_timer_timeout() -> void:
	physics.disabled = false
	can_collide = true

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")

To be clear the issue seems to be the fact that when the paddle moves to the ball on the same trajectory the ball doesn’t seem to recognize the collision.

Change your ball from a StaticBody2D to a Rigidbody2D and the physics engine will handle momentum transfers for you.

1 Like

Ive used characterbody2d, rigidbody2d, and staticbody2d, and out of all of them static body 2d is the easiest to control. I just want the code to recognize it as a collision when the paddle hits the ball towards the trajectory it was already going. After that it should speed up to move away from the paddle and avoid the physics wackiness Ive been dealing with. Also this is the only ball-paddld interaction ive been struggling with.

Update: I tried checking the distance to make the ball move instead of checking the collision but the problem is still there. Ball moves perfectly whenever it and the paddle arent moving in the same trajectory. When they do the paddle just pushes it rather than launching it, which it does at every other angle.

1 Like

Just a guess: Because you are using a static body and not a rigid body the physics engine will alter the paddle velocity in move_and_slide().
I have seen static bodies with large velocity getting stopped by character bodies before. This would suggest testing for motion could happen before move_and_slide(), but i would suggest a redesign in this case.
Try simplifying the code until the collision works then adding the timer back in.
Test if the paddle collides, then alter the velocity of the ball, instead of testing whether the ball collides. Maybe add an Area3d larger than the ball and use the on_body_entered() signal.

I think if you use rigid bodies for everything that moves and interacts then you can maintain predictable, and consistent behaviour.

I have tried using rigidbody2d on the ball before but I was never able to get it to move in a way that made sense. staticbody2d was an instant improvement over both rigidbody2d and characterbody2d and the problem ive outlined is pretty much the only problem I have with it. Il go back to rigidbody2d and see if I can make it work though.

If both the paddle and ball are rigid bodies then its a matter of simulation and the system should start to work with the correct masses and forces.

You could run a test that iterates simple experiments of a simple ball and paddle collision, prints the numbers for each then you can visually compare the results. Also perhaps for different physics materials.

Or perhaps you could add keys that change the simulation parameters and another key to repeat the test.