# Your First AeThex Game - Multiplayer Pong

Build a simple multiplayer Pong game in 30 minutes. Learn core AeThex features: multiplayer, cloud saves, and the Studio IDE.

**What you'll build:** Real-time multiplayer Pong where 2 players compete, with automatic cloud save of high scores.

**Time required:** 30 minutes  
**Difficulty:** Beginner

---

## Prerequisites

- AeThex Engine installed
- Basic programming knowledge (see [GDScript Basics](../GDSCRIPT_BASICS.md) if needed)
- Studio IDE running at http://localhost:9002/ide

---

## Part 1: Project Setup (5 min)

### 1. Create New Project

In Studio IDE:
1. Click **File → New Project**
2. Name: `MultiplayerPong`
3. Location: Choose your projects folder
4. Click **Create & Open**

### 2. Create Scene Structure

In the Scene Tree:
1. Create **Node2D** (root) - name it `Game`
2. Add children:
   - **ColorRect** (background) - name it `Background`
   - **RigidBody2D** - name it `Ball`
   - **StaticBody2D** - name it `Paddle1`
   - **StaticBody2D** - name it `Paddle2`
   - **CanvasLayer** - name it `UI`

Your scene tree should look like:
```
Game (Node2D)
├── Background (ColorRect)
├── Ball (RigidBody2D)
├── Paddle1 (StaticBody2D)
├── Paddle2 (StaticBody2D)
└── UI (CanvasLayer)
```

---

## Part 2: Create the Ball (5 min)

### 1. Add Ball Components

Select **Ball** node, add children:
- **Sprite2D** - name it `Sprite`
- **CollisionShape2D** - name it `Collision`

### 2. Configure Ball

**Sprite2D:**
- In Inspector: Texture → Create new `CircleTexture2D`
- Set Radius: 16

**CollisionShape2D:**
- Shape → Create new `CircleShape2D`
- Radius: 16

**Ball (RigidBody2D):**
- Gravity Scale: 0 (no gravity)
- Lock Rotation: ON

### 3. Ball Script

Right-click **Ball** → Attach Script:

```gdscript
extends RigidBody2D

const INITIAL_SPEED = 400

func _ready():
    # Start ball moving in random direction
    var direction = Vector2(randf_range(-1, 1), randf_range(-0.5, 0.5)).normalized()
    linear_velocity = direction * INITIAL_SPEED

func _on_body_entered(body):
    # Increase speed slightly on each hit
    linear_velocity *= 1.05
    
    # Keep vertical speed reasonable
    if abs(linear_velocity.y) < 100:
        linear_velocity.y = sign(linear_velocity.y) * 100
```

Connect the `body_entered` signal:
- Inspector → Node → Signals
- Double-click `body_entered`
- Connect to `_on_body_entered`

---

## Part 3: Create Paddles (7 min)

### 1. Setup Paddle1

Select **Paddle1**, add children:
- **ColorRect** - name it `Visual`
- **CollisionShape2D** - name it `Collision`

**ColorRect:**
- Size: 20 x 100
- Position: (50, 300)
- Color: White

**CollisionShape2D:**
- Create new `RectangleShape2D`
- Size: 20 x 100

**Paddle1 position:** (50, 300)

### 2. Paddle1 Script

Right-click **Paddle1** → Attach Script:

```gdscript
extends StaticBody2D

const SPEED = 400

func _physics_process(delta):
    var input = Input.get_axis("ui_up", "ui_down")
    position.y += input * SPEED * delta
    
    # Keep paddle on screen
    position.y = clamp(position.y, 50, 550)
```

### 3. Setup Paddle2

Select **Paddle2**, add same components as Paddle1:
- **ColorRect** - name it `Visual`
- **CollisionShape2D** - name it `Collision`

**ColorRect:**
- Size: 20 x 100
- Position: (1130, 300)
- Color: White

**CollisionShape2D:**
- RectangleShape2D
- Size: 20 x 100

**Paddle2 position:** (1130, 300)

### 4. Paddle2 Script

Right-click **Paddle2** → Attach Script:

```gdscript
extends StaticBody2D

const SPEED = 400

func _physics_process(delta):
    # Control with W/S keys
    var input = 0
    if Input.is_action_pressed("ui_up"):  # We'll add this
        input -= 1
    if Input.is_action_pressed("ui_down"):
        input += 1
    
    position.y += input * SPEED * delta
    position.y = clamp(position.y, 50, 550)
```

---

## Part 4: Add Walls & UI (5 min)

### 1. Create Walls

Create 2 **StaticBody2D** nodes as children of `Game`:
- **TopWall**
- **BottomWall**

**TopWall:**
- Position: (600, 0)
- Add **CollisionShape2D** → RectangleShape2D
- Size: 1200 x 20

**BottomWall:**
- Position: (600, 600)
- Add **CollisionShape2D** → RectangleShape2D
- Size: 1200 x 20

### 2. Create Score UI

Select **UI** (CanvasLayer), add children:
- **Label** - name it `Player1Score`
- **Label** - name it `Player2Score`
- **Label** - name it `RoomCode`

**Player1Score:**
- Text: "0"
- Position: (400, 50)
- Font Size: 48

**Player2Score:**
- Text: "0"
- Position: (800, 50)
- Font Size: 48

**RoomCode:**
- Text: "Room: ----"
- Position: (500, 10)
- Font Size: 24

---

## Part 5: Add Multiplayer (THE MAGIC - 3 Lines!)

### 1. Update Game Script

Select **Game** root node → Attach Script:

```gdscript
extends Node2D

var score_p1 = 0
var score_p2 = 0

func _ready():
    # MULTIPLAYER IN 3 LINES! 🎮
    AeThexMultiplayer.create_room("pong-" + str(randi() % 10000))
    AeThexMultiplayer.connect("player_joined", _on_player_joined)
    
    # Update UI with room code
    var code = AeThexMultiplayer.get_room_code()
    $UI/RoomCode.text = "Room: " + code
    print("Share this code with friend: ", code)

func _on_player_joined(player_id):
    print("Player ", player_id, " joined!")
    
    # Assign paddle control
    if AeThexMultiplayer.get_players().size() == 1:
        $Paddle1.set_multiplayer_authority(player_id)
    else:
        $Paddle2.set_multiplayer_authority(player_id)

func _on_ball_out_left():
    score_p2 += 1
    $UI/Player2Score.text = str(score_p2)
    reset_ball()

func _on_ball_out_right():
    score_p1 += 1
    $UI/Player1Score.text = str(score_p1)
    reset_ball()

func reset_ball():
    $Ball.position = Vector2(600, 300)
    $Ball.linear_velocity = Vector2.ZERO
    await get_tree().create_timer(1.0).timeout
    
    var direction = Vector2(randf_range(-1, 1), randf_range(-0.5, 0.5)).normalized()
    $Ball.linear_velocity = direction * 400
```

### 2. Create Goal Areas

Add 2 **Area2D** nodes as children of `Game`:
- **GoalLeft**  
- **GoalRight**

**GoalLeft:**
- Position: (-20, 300)
- Add **CollisionShape2D** → RectangleShape2D
- Size: 40 x 600

**GoalRight:**
- Position: (1220, 300)
- Add **CollisionShape2D** → RectangleShape2D
- Size: 40 x 600

Connect `body_entered` signals:
- **GoalLeft** → `Game._on_ball_out_left`
- **GoalRight** → `Game._on_ball_out_right`

---

## Part 6: Add Cloud Saves (5 min)

Save high scores to cloud automatically!

### Update Game Script

Add to `game.gd`:

```gdscript
var high_score_p1 = 0
var high_score_p2 = 0

func _ready():
    # ... existing multiplayer code ...
    
    # Load saved high scores
    load_high_scores()

func load_high_scores():
    var save_data = await AeThexSaves.load_game("high_scores")
    if save_data:
        high_score_p1 = save_data.get("p1", 0)
        high_score_p2 = save_data.get("p2", 0)
        print("Loaded high scores: P1=", high_score_p1, " P2=", high_score_p2)

func save_high_scores():
    var save_data = {
        "p1": max(score_p1, high_score_p1),
        "p2": max(score_p2, high_score_p2),
        "last_played": Time.get_unix_time_from_system()
    }
    
    if AeThexSaves.save_game("high_scores", save_data):
        print("High scores saved to cloud!")

func _on_ball_out_left():
    score_p2 += 1
    $UI/Player2Score.text = str(score_p2)
    
    # Update high score
    if score_p2 > high_score_p2:
        high_score_p2 = score_p2
        save_high_scores()
    
    reset_ball()

func _on_ball_out_right():
    score_p1 += 1
    $UI/Player1Score.text = str(score_p1)
    
    if score_p1 > high_score_p1:
        high_score_p1 = score_p1
        save_high_scores()
    
    reset_ball()
```

---

## Part 7: Test Your Game! (5 min)

### 1. Run the Game

Click **Play** button in Studio (or press F5)

### 2. Get Room Code

Look in the console output for:
```
Share this code with friend: ABCD-1234
```

### 3. Test Multiplayer

**Option A: Two Instances**
1. Run game twice (different terminals)
2. Second instance joins with room code

**Option B: Share with Friend**
1. Give friend the room code
2. They run AeThex and join your room

### 4. Play!

- **Player 1:** Arrow keys
- **Player 2:** W/S keys
- Score goals to test cloud saves!

---

## What You Just Built

✅ **Real-time multiplayer** - 3 lines of code!  
✅ **Cloud saves** - High scores sync automatically  
✅ **Room codes** - Easy friend invites  
✅ **Cross-platform** - Works on all devices  

Compare to traditional approach:
- **Godot alone:** 500+ lines for networking, server setup, NAT traversal
- **With AeThex:** 3 lines for multiplayer, 5 lines for cloud saves

---

## Next Steps

### Improve Your Game

**Add effects:**
```gdscript
# In ball script
func _on_body_entered(body):
    # Play sound
    $HitSound.play()
    
    # Screen shake
    get_tree().root.get_camera_2d().shake(0.1)
```

**Add power-ups:**
```gdscript
# Random power-up
var powerup_scene = preload("res://powerup.tscn")
var powerup = powerup_scene.instantiate()
powerup.position = Vector2(randf_range(100, 1100), randf_range(100, 500))
add_child(powerup)
```

**Add particle effects:**
- Add `GPUParticles2D` to ball
- Emit trail when moving

### Learn More Features

- **[AI Assistant Tutorial](AI_ASSISTANT_TUTORIAL.md)** - Get coding help in-game
- **[Authentication Tutorial](AUTH_TUTORIAL.md)** - Add login system
- **[Analytics Tutorial](ANALYTICS_TUTORIAL.md)** - Track player behavior
- **[Publishing Guide](../PUBLISHING_GUIDE.md)** - Deploy your game

---

## Troubleshooting

**Ball goes through paddle:**
- Check CollisionShape2D is child of paddle
- Verify RigidBody2D collision layers

**Multiplayer not working:**
- Check internet connection
- Verify `AeThexMultiplayer` is available (check console)
- Try creating new room

**Cloud saves failing:**
- Check `AeThexAuth.is_logged_in()` - may need login
- Use guest mode: `AeThexAuth.login_as_guest()`

**Paddles not moving:**
- Check input actions in Project Settings
- Verify `_physics_process` is called

---

## Complete Code

### game.gd (Final Version)
```gdscript
extends Node2D

var score_p1 = 0
var score_p2 = 0
var high_score_p1 = 0
var high_score_p2 = 0

func _ready():
    # Connect to cloud
    if not AeThexCloud.is_connected():
        AeThexCloud.connect_to_cloud()
    
    # Login as guest for cloud saves
    if not AeThexAuth.is_logged_in():
        await AeThexAuth.login_as_guest()
    
    # Create multiplayer room
    AeThexMultiplayer.create_room("pong-" + str(randi() % 10000))
    AeThexMultiplayer.connect("player_joined", _on_player_joined)
    
    # Show room code
    var code = AeThexMultiplayer.get_room_code()
    $UI/RoomCode.text = "Room: " + code
    print("=================================")
    print(" Share code with friend: ", code)
    print("=================================")
    
    # Load high scores
    load_high_scores()

func _on_player_joined(player_id):
    print("Player ", player_id, " joined!")
    
    var players = AeThexMultiplayer.get_players()
    if players.size() == 1:
        $Paddle1.set_multiplayer_authority(player_id)
    else:
        $Paddle2.set_multiplayer_authority(player_id)

func load_high_scores():
    var save_data = await AeThexSaves.load_game("high_scores")
    if save_data:
        high_score_p1 = save_data.get("p1", 0)
        high_score_p2 = save_data.get("p2", 0)
        print("High scores loaded: P1=", high_score_p1, " P2=", high_score_p2)

func save_high_scores():
    var save_data = {
        "p1": max(score_p1, high_score_p1),
        "p2": max(score_p2, high_score_p2),
        "timestamp": Time.get_unix_time_from_system()
    }
    
    if AeThexSaves.save_game("high_scores", save_data):
        print("✓ High scores saved to cloud!")

func _on_ball_out_left():
    score_p2 += 1
    $UI/Player2Score.text = str(score_p2)
    
    if score_p2 > high_score_p2:
        high_score_p2 = score_p2
        save_high_scores()
    
    reset_ball()

func _on_ball_out_right():
    score_p1 += 1
    $UI/Player1Score.text = str(score_p1)
    
    if score_p1 > high_score_p1:
        high_score_p1 = score_p1
        save_high_scores()
    
    reset_ball()

func reset_ball():
    $Ball.position = Vector2(600, 300)
    $Ball.linear_velocity = Vector2.ZERO
    
    await get_tree().create_timer(1.0).timeout
    
    var direction = Vector2(randf_range(-1, 1), randf_range(-0.5, 0.5)).normalized()
    $Ball.linear_velocity = direction * 400
```

---

## Congratulations! 🎉

You just built your first multiplayer cloud-enabled game with AeThex!

**Share your creation:**
- Tweet [@AeThexEngine](https://twitter.com/AeThexEngine) with your room code
- Join our [Discord](https://discord.gg/aethex) community
- Show off in [r/AeThex](https://reddit.com/r/aethex)

Ready for more? Check out the [Tutorial Index](README.md) for advanced topics!
