In this technical blog, I'll walk you through creating "Echoes of Code," a 2D top-down puzzle game where you solve puzzles by collaborating with echoes of your past actions. I'll focus on the key components and concepts to keep this a quick 5-minute read.
Setting Up the Development Environment
Installing WSL on Windows
- Enable WSL: Open PowerShell as Administrator and run:([DEV Community][1])
wsl --install
- Restart your computer and complete the Linux distribution setup.
Setting Up Python and PyGame
# Install Python and dependencies
sudo apt install python3 python3-pip python3-dev -y
sudo apt install python3-pygame -y
# Install PyGame
pip3 install pygame
Installing Amazon Q CLI
# Install AWS CLI first
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install
# Configure AWS
aws configure
# Install Amazon Q CLI
curl -Lo "/tmp/q-cli.deb" "https://d3l58w4xvhud7h.cloudfront.net/linux/amd64/latest/q-cli.deb"
sudo dpkg -i /tmp/q-cli.deb
# Login
q login
Game Design and Concept
"Echoes of Code" features a software engineer trapped in a glitched digital world. Every 10 seconds, time resets, and your past actions are replayed by ghost clones called "echoes." You must use these echoes strategically to solve puzzles.
Implementation Highlights
Time Loop System
The core of the game is the TimerManager
class that handles the time loop mechanics:
class TimerManager:
def __init__(self, loop_duration: float, max_loops: int):
self.loop_duration = loop_duration
self.max_loops = max_loops
self.start_time = time.time()
self.current_loop = 1
def get_loop_time(self) -> float:
"""Get time elapsed in the current loop"""
return self.get_elapsed_time() % self.loop_duration
def should_reset_loop(self) -> bool:
return self.get_loop_time() >= self.loop_duration
def reset_loop(self):
self.current_loop += 1
Action Recording and Replay
The Player
class records actions, while the Echo
class replays them:
# In Player class
def record_action(self, action_type: str, time_stamp: float):
self.actions.append(Action(action_type, (self.x, self.y), time_stamp, self.current_direction))
# In Echo class
def update(self, current_time: float, game_objects: List):
if self.current_action_index < len(self.actions):
next_action = self.actions[self.current_action_index]
if current_time >= next_action.time_stamp:
if next_action.action_type == "move":
self.x, self.y = next_action.position
self.current_direction = next_action.direction
elif next_action.action_type == "interact":
self.interact_with_objects(game_objects)
self.current_action_index += 1
Interactive Game Objects
The game includes various interactive elements:
# Example of the PressurePlate class
class PressurePlate:
def update(self, players: List[Player]):
self.is_active = False
plate_rect = pygame.Rect(self.x, self.y, self.width, self.height)
for player in players:
player_rect = pygame.Rect(player.x, player.y, player.width, player.height)
if plate_rect.colliderect(player_rect):
self.is_active = True
break
Loop Reset Logic
When a loop resets, we create a new echo and reset the player:
def reset_loop(self):
# Create a new echo from the current player
echo_colors = [PURPLE, YELLOW, CYAN, RED, GREEN]
echo_color = echo_colors[min(len(self.echoes), len(echo_colors) - 1)]
self.echoes.append(Echo(self.player, echo_color, self.timer_manager.current_loop))
# Reset player position
current_level = self.level_manager.get_current_level()
self.player.x, self.player.y = current_level.player_start
# Clear player actions for the new loop
self.player.actions = []
# Increment loop counter
self.timer_manager.reset_loop()
Level Design
Levels are created using the Level
class, which manages all game objects:
def create_levels(self):
# Level 1: Simple pressure plate and gate
level1 = Level(1, (100, 300), 3) # Player starts at (100, 300), 3 loops max
# Add walls (border)
level1.add_wall(0, 0, SCREEN_WIDTH, 20) # Top
level1.add_wall(0, SCREEN_HEIGHT - 20, SCREEN_WIDTH, 20) # Bottom
# Add pressure plate
level1.add_pressure_plate(300, 200, 1) # Controls gate with ID 1
# Add gate
level1.add_gate(400, 400, 20, 100, 1) # Gate with ID 1
# Add exit
level1.set_exit(700, 300)
Main Game Loop
The Game
class ties everything together:
def run(self):
running = True
while running:
running = self.handle_events()
self.update()
self.draw()
self.clock.tick(FPS)
Development Challenges
Echo Replay Timing: Ensuring echoes replayed actions at the exact same time they were recorded.
Collision Detection: Making sure players and echoes interacted correctly with game objects.
Loop Reset Logic: Managing the state transition when a loop resets, especially ensuring all objects reset properly.
Future Enhancements
The game can be expanded with:
Additional Levels: More complex puzzles requiring multiple echoes to solve.
Mini Terminal: Implementing a system where players can write simple logic commands.
Enemies: Adding entities that track and interact with echoes.
Visual Timeline: Creating a replay system to show all actions at the end of each level.
Running the Game
To run the game:
cd ~/echoes_of_code
python3 echoes_of_code.py
Conclusion
Building "Echoes of Code" demonstrates how a unique game mechanic like time loops can create engaging puzzle gameplay. The modular code structure makes it easy to expand with new levels and features. By leveraging WSL for development and Amazon Q CLI for assistance, I was able to streamline the development process and focus on implementing the core game mechanics.
If you're interested in time-based puzzle games or want to explore PyGame development, I encourage you to try out "Echoes of Code" and even extend it with your own ideas!
Top comments (0)