Python library pygame: RPG game basic concepts and coding examples

roguelike RPG


This will be a roguelike RPG with procedural generation, inventory management, combat, and basic AI. It's a substantial amount of code, and I'll break it down into sections with explanations.

Game Concept:

  • Genre: Roguelike RPG (turn-based, procedural generation, permadeath)

  • Setting: A dungeon with multiple levels.

  • Player: A hero with stats (HP, Attack, Defense, etc.) and an inventory.

  • Enemies: Various enemy types with different stats and behaviors.

  • Items: Weapons, armor, potions, scrolls.

  • Procedural Generation: Dungeon layouts are randomly generated.

Code Structure (Conceptual):

  1. Initialization: Pygame setup, screen creation, font loading.

  2. Data Structures: Classes for Player, Enemy, Item, Dungeon, Tile.

  3. Procedural Generation: Dungeon generation algorithm (e.g., recursive backtracker).

  4. Game Loop: Handles input, updates game state, renders the screen.

  5. Combat System: Turn-based combat logic.

  6. Inventory System: Managing items, equipping weapons/armor.

  7. AI: Basic enemy AI (e.g., move towards player, attack).

  8. Rendering: Drawing the dungeon, player, enemies, and UI.

Here's a substantial code example (with explanations). It's long, but necessary to demonstrate complexity. I'll focus on the core mechanics. I'll omit detailed comments for brevity, but the structure should be clear.



import pygame
import random

# --- Constants ---
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
TILE_SIZE = 32
FPS = 30

# Colors
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)

# --- Classes ---

class Player(pygame.sprite.Sprite):
    def __init__(self, x, y):
        super().__init__()
        self.image = pygame.Surface([TILE_SIZE, TILE_SIZE])
        self.image.fill(GREEN)
        self.rect = self.image.get_rect()
        self.rect.x = x
        self.rect.y = y
        self.hp = 100
        self.attack = 10
        self.inventory = []

    def move(self, dx, dy, dungeon):
        new_x = self.rect.x + dx
        new_y = self.rect.y + dy
        if 0 <= new_x < SCREEN_WIDTH and 0 <= new_y < SCREEN_HEIGHT and dungeon.grid[new_y // TILE_SIZE][new_x // TILE_SIZE] != 1: # 1 represents a wall
            self.rect.x = new_x
            self.rect.y = new_y

class Enemy(pygame.sprite.Sprite):
    def __init__(self, x, y):
        super().__init__()
        self.image = pygame.Surface([TILE_SIZE, TILE_SIZE])
        self.image.fill(RED)
        self.rect = self.image.get_rect()
        self.rect.x = x
        self.rect.y = y
        self.hp = 50
        self.attack = 5

    def move_towards_player(self, player, dungeon):
        dx = 0
        dy = 0
        if player.rect.x > self.rect.x:
            dx = TILE_SIZE
        elif player.rect.x < self.rect.x:
            dx = -TILE_SIZE
        if player.rect.y > self.rect.y:
            dy = TILE_SIZE
        elif player.rect.y < self.rect.y:
            dy = -TILE_SIZE

        new_x = self.rect.x + dx
        new_y = self.rect.y + dy

        if 0 <= new_x < SCREEN_WIDTH and 0 <= new_y < SCREEN_HEIGHT and dungeon.grid[new_y // TILE_SIZE][new_x // TILE_SIZE] != 1:
            self.rect.x = new_x
            self.rect.y = new_y

class Item:
    def __init__(self, name, type, stats):
        self.name = name
        self.type = type  # "weapon", "armor", "potion"
        self.stats = stats  # e.g., {"attack": 5, "defense": 2}

class Dungeon:
    def __init__(self, width, height):
        self.width = width
        self.height = height
        self.grid = [[0 for _ in range(width)] for _ in range(height)]  # 0: floor, 1: wall
        self.generate()

    def generate(self):
        # Recursive Backtracker Algorithm
        def carve(x, y):
            self.grid[y][x] = 0
            directions = [(0, 2), (2, 0), (0, -2), (-2, 0)]
            random.shuffle(directions)
            for dx, dy in directions:
                nx, ny = x + dx, y + dy
                if 0 < nx < self.width - 1 and 0 < ny < self.height - 1 and self.grid[ny][nx] == 1:
                    self.grid[y + dy // 2][x + dx // 2] = 0
                    carve(nx, ny)

        start_x = random.randint(1, self.width - 2)
        start_y = random.randint(1, self.height - 2)
        carve(start_x, start_y)

        # Add walls around the edges
        for x in range(self.width):
            self.grid[0][x] = 1
            self.grid[self.height - 1][x] = 1
        for y in range(self.height):
            self.grid[y][0] = 1
            self.grid[y][self.width - 1] = 1

# --- Initialization ---
pygame.init()
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("Roguelike RPG")
clock = pygame.time.Clock()
font = pygame.font.Font(None, 24)

# --- Game Objects ---
dungeon = Dungeon(SCREEN_WIDTH // TILE_SIZE, SCREEN_HEIGHT // TILE_SIZE)
player = Player(TILE_SIZE, TILE_SIZE)
enemies = pygame.sprite.Group()
for _ in range(5):
    x = random.randint(TILE_SIZE, SCREEN_WIDTH - TILE_SIZE)
    y = random.randint(TILE_SIZE, SCREEN_HEIGHT - TILE_SIZE)
    enemy = Enemy(x, y)
    enemies.add(enemy)

all_sprites = pygame.sprite.Group()
all_sprites.add(player)
all_sprites.add(enemies)

# --- Game Loop ---
running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    # --- Input ---
    keys = pygame.key.get_pressed()
    if keys[pygame.K_LEFT]:
        player.move(-TILE_SIZE, 0, dungeon)
    if keys[pygame.K_RIGHT]:
        player.move(TILE_SIZE, 0, dungeon)
    if keys[pygame.K_UP]:
        player.move(0, -TILE_SIZE, dungeon)
    if keys[pygame.K_DOWN]:
        player.move(0, TILE_SIZE, dungeon)

    # --- Update ---
    for enemy in enemies:
        enemy.move_towards_player(player, dungeon)

    # --- Rendering ---
    screen.fill(BLACK)

    # Draw Dungeon
    for y in range(dungeon.height):
        for x in range(dungeon.width):
            if dungeon.grid[y][x] == 1:
                pygame.draw.rect(screen, WHITE, (x * TILE_SIZE, y * TILE_SIZE, TILE_SIZE, TILE_SIZE))

    all_sprites.draw(screen)

    # Display HP
    hp_text = font.render(f"HP: {player.hp}", True, WHITE)
    screen.blit(hp_text, (10, 10))

    pygame.display.flip()
    clock.tick(FPS)

pygame.quit()

@

Key Improvements & Next Steps (Beyond this example):

  • Collision Detection: More robust collision handling (e.g., preventing enemies from walking through walls).

  • Combat System: Implement attack logic, damage calculation, and enemy death.

  • Inventory System: Add item pickup, equipping, and usage.

  • More Complex AI: Implement pathfinding (A* algorithm), different enemy behaviors.

  • Level Generation: Add more variety to dungeon generation (rooms, corridors, special features).

  • UI: Create a user interface for inventory, stats, and other information.

  • Sound Effects & Music: Enhance the game with audio.

  • Game States: Implement different game states (e.g., main menu, game over screen).

  • Data Persistence: Save and load game progress.

  • Animation: Add animations for player and enemies.

Important Considerations:

  • Complexity: This is a very complex project. Start small and build incrementally.

  • Organization: Use classes and functions to keep your code organized.

  • Testing: Test your code frequently to catch bugs early.

  • Optimization: Optimize your code for performance, especially if you're dealing with large dungeons or many enemies.

Comments