Pong

This article documents rebuilding the classic game Pong using Unity’s Entity Component System (ECS) framework. The game features a ball and two paddles—one player-controlled, one AI-controlled—where players earn points when opponents miss the ball.

Entity Components

The implementation defines four key component structures:

Paddle Prefab

Ball Prefab

[GenerateAuthoringComponent]
public struct PongPaddle : IComponentData { }

[GenerateAuthoringComponent]
public struct Ball : IComponentData { }

[GenerateAuthoringComponent]
public struct AI : IComponentData { }

[GenerateAuthoringComponent]
public struct Speed : IComponentData
{
    public float value;
}

Player Paddle Movement System

The player paddle responds to vertical input (up/down axes) and constrains movement within bounds:

public class PongPaddleMovementSystem : SystemBase
{
    protected override void OnUpdate()
    {
        float movement = Input.GetAxis("Vertical");
        float dt = Time.DeltaTime;

        Entities.WithAll<Player, PongPaddle>()
            .ForEach((ref Translation translation, in Speed speed) => {
                translation.Value.y += dt * movement * speed.value;
                translation.Value.y = math.clamp(translation.Value.y, -4, 4);
            }).Schedule();
    }
}

AI Paddle Movement

The AI paddle tracks the ball’s Y-position with a dead zone to prevent constant jittering:

public class PongAIPaddleMovementSystem : SystemBase
{
    protected override void OnUpdate()
    {
        float deltaTime = Time.DeltaTime;
        float3 ballPos = EntityManager.CreateEntityQuery(
            typeof(Translation), typeof(Ball))
            .GetSingleton<Translation>().Value;

        Entities.WithAll<AI>()
            .ForEach((ref Translation translation, in Speed speed) => {
                float dist = math.abs(ballPos.y - translation.Value.y);
                float dir = math.select(-1, 1, ballPos.y > translation.Value.y);
                dir = math.select(0, dir, dist > 0.3f);
                translation.Value.y += speed.value * deltaTime * dir;
                translation.Value.y = math.clamp(translation.Value.y, -4, 4);
            }).Schedule();
    }
}

Scoring System

The scoring system determines points based on ball position relative to paddles:

public class ScoreCalculationSystem : SystemBase
{
    GameHandler m_handler;

    protected override void OnUpdate()
    {
        // Queries retrieve ball and paddle positions
        // If ball.x <= player.x, AI scores
        // If ball.x >= ai.x, player scores
    }
}

Suggested Enhancements

  • Advanced physics reflection based on paddle contact location and movement
  • Two-player mode with both players controlling dual paddles
  • Power-ups triggered when the ball contacts paddles
  • Improved AI using trajectory calculations for ball prediction

Source Code

Full implementation available on my GitHub repository.