Part 6 — Launch and reset the ball
In this part, we will connect the ball to the paddle's launch signal.
Before launch, the ball will sit just above the paddle. When the player presses the Launch input action, the paddle will dispatch its signal and the ball will apply an impulse.
Add the Ball Script
Create a PHP script under Assets/Scripts named Ball.php.
<?php
declare(strict_types=1);
namespace BreakOut\Game\Scripts;
use Lenga\Engine\Attributes\AddComponentMenu;
use Lenga\Engine\Attributes\Header;
use Lenga\Engine\Attributes\RequireComponent;
use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\Rigidbody2D;
use Lenga\Engine\Core\Vector2;
use Lenga\Engine\Core\Vector3;
use Lenga\Engine\Enumerations\ForceMode2D;
use Random\Randomizer;
#[AddComponentMenu('Gameplay/Ball')]
#[RequireComponent(Rigidbody2D::class)]
final class Ball extends Behaviour
{
#[Header('References')]
public ?Paddle $paddle = null;
#[Header('Launch')]
public float $speed = 20.0;
public float $xVariance = 10.0;
public float $yVariance = 5.0;
public ?Vector2 $spawnOffset = null;
private ?Rigidbody2D $body = null;
private bool $launched = false;
public function start(): void
{
$this->body = $this->gameObject->getComponent(Rigidbody2D::class);
$this->spawnOffset ??= new Vector2(x: 4.0, y: -2.0);
$this->paddle?->onLaunch->add(fn (Paddle $paddle): bool => $this->launch($paddle));
$this->resetBall();
}
}
Attach this script to Ball, then assign the Paddle reference in the Inspector.
Follow the Paddle Before Launch
Add this method pair:
public function update(): void
{
if (!$this->launched) {
$this->followPaddle();
}
}
private function followPaddle(): void
{
if (!$this->paddle instanceof Paddle || !$this->spawnOffset instanceof Vector2) {
return;
}
$paddlePosition = $this->paddle->transform->position;
$this->transform->position = new Vector3(
x: $paddlePosition->x + $this->spawnOffset->x,
y: $paddlePosition->y + $this->spawnOffset->y,
z: $paddlePosition->z,
);
}
Now the ball stays attached to the paddle until it is launched.
Launch with an Impulse
Add the launch method:
private function launch(Paddle $paddle): bool
{
if ($this->launched || !$this->body instanceof Rigidbody2D) {
return false;
}
$this->followPaddle();
$randomizer = new Randomizer();
$horizontal = $randomizer->getFloat(-$this->xVariance, $this->xVariance);
$vertical = -($this->speed + $randomizer->getFloat(0.0, $this->yVariance));
$this->body->velocity = Vector3::zero();
$this->body->addForce(
new Vector2(x: $horizontal, y: $vertical),
ForceMode2D::Impulse,
);
$this->launched = true;
return true;
}
In this 2D setup, negative Y launches the ball upward. The small random X variance keeps each launch from feeling identical.
Add a Reset Helper
Add this helper now. We will call it from the out-of-bounds event in the next part.
private function resetBall(): bool
{
$this->launched = false;
if ($this->body instanceof Rigidbody2D) {
$this->body->velocity = Vector3::zero();
}
$this->followPaddle();
return true;
}
Checkpoint
At the end of this part:
- the ball follows the paddle before launch
- pressing
Launchfires the paddle signal - the ball launches with an impulse
- the ball has a reset helper ready for the next mechanic
Next, we will handle out-of-bounds and lives.