Tutorial: Creating a 3D Game with PyOpenGL
Step 1: Install Required Libraries
Before you begin, make sure you have PyGame and PyOpenGL installed. These libraries will help with window management and rendering 3D objects.
In your terminal or command prompt, run:
bash CopyEdit pip install pygame PyOpenGL
Step 2: Basic Folder Structure
Here is the folder structure we'll use:
bash CopyEdit 3d_game/ │ ├── assets/ # For storing any textures, models, sounds ├── src/ # Source code for the game │ ├── main.py # Main game loop │ ├── engine.py # Contains camera and movement logic │ ├── player.py # Handles user input (WASD, mouse) │ └── physics.py # Handles physics, e.g., preventing falling └── README.md # Game info (optional)
Step 3: engine.py
- Setting Up OpenGL and Camera
In this file, we’ll set up OpenGL, initialize the camera, and handle basic camera movement.
python CopyEdit import pygame from pygame.locals import * from OpenGL.GL import * from OpenGL.GLU import * import math # Window settings WIDTH, HEIGHT = 800, 600 screen = pygame.display.set_mode((WIDTH, HEIGHT), DOUBLEBUF | OPENGL) # Camera settings camera_pos = [0, 0, -5] # Camera's position in 3D space (x, y, z) camera_angle = [0, 0] # Camera's angle (yaw, pitch) # Initialize OpenGL and set up the camera def init_camera(): glMatrixMode(GL_PROJECTION) glLoadIdentity() gluPerspective(45, (WIDTH / HEIGHT), 0.1, 50.0) # Perspective projection glTranslatef(camera_pos[0], camera_pos[1], camera_pos[2]) # Position the camera def update_camera(): glRotatef(camera_angle[0], 1, 0, 0) # Rotate around X-axis (pitch) glRotatef(camera_angle[1], 0, 1, 0) # Rotate around Y-axis (yaw) # Handle keyboard input for movement (WASD keys) def handle_keys(): global camera_pos keys = pygame.key.get_pressed() # Move forward, backward, left, right if keys[pygame.K_w]: camera_pos[2] += 0.1 # Move forward if keys[pygame.K_s]: camera_pos[2] -= 0.1 # Move backward if keys[pygame.K_a]: camera_pos[0] += 0.1 # Move left if keys[pygame.K_d]: camera_pos[0] -= 0.1 # Move right
Step 4: player.py
- Handling Mouse Look
The player file will handle mouse movement for looking around.
python CopyEdit import pygame from engine import camera_pos, camera_angle # Mouse sensitivity for camera rotation MOUSE_SENSITIVITY = 0.1 def handle_mouse(): global camera_angle mouse_x, mouse_y = pygame.mouse.get_pos() # Calculate the movement of the mouse from the center delta_x = mouse_x - (WIDTH // 2) delta_y = mouse_y - (HEIGHT // 2) # Update camera angles (yaw and pitch) camera_angle[1] += delta_x * MOUSE_SENSITIVITY # Rotate left/right (yaw) camera_angle[0] -= delta_y * MOUSE_SENSITIVITY # Rotate up/down (pitch) # Restrict pitch angle (up/down) to prevent flipping if camera_angle[0] > 90: camera_angle[0] = 90 if camera_angle[0] < -90: camera_angle[0] = -90 # Center the mouse in the window pygame.mouse.set_pos(WIDTH // 2, HEIGHT // 2) def update(): handle_mouse() # Control camera with mouse handle_keys() # Control movement with WASD
Step 5: physics.py
- Basic Physics (Collision)
This file handles simple physics, like making sure the player doesn’t fall through the ground.
python CopyEdit # Simple collision check for the ground def check_collision(player_pos, ground_level=0): if player_pos[1] < ground_level: player_pos[1] = ground_level # Prevent player from falling below ground
Step 6: main.py
- The Main Game Loop
This is where everything comes together: initializing the game, running the game loop, and rendering objects.
python CopyEdit import pygame from engine import init_camera, update_camera from player import update from physics import check_collision # Window settings WIDTH, HEIGHT = 800, 600 screen = pygame.display.set_mode((WIDTH, HEIGHT), DOUBLEBUF | OPENGL) # Game loop def game_loop(): clock = pygame.time.Clock() running = True while running: for event in pygame.event.get(): if event.type == pygame.QUIT: running = False # Clear the screen before drawing glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) # Initialize the camera init_camera() # Update the player input (movement, mouse look) update() # Basic collision (prevent falling through the ground) check_collision(camera_pos) # Draw a rotating cube (just an example) draw_cube() # Update the display pygame.display.flip() # Control the frame rate clock.tick(60) pygame.quit() # Function to draw a simple cube def draw_cube(): vertices = [ (1, -1, -1), (1, 1, -1), (-1, 1, -1), (-1, -1, -1), (1, -1, 1), (1, 1, 1), (-1, -1, 1), (-1, 1, 1) ] edges = [ (0, 1), (1, 2), (2, 3), (3, 0), (4, 5), (5, 6), (6, 7), (7, 4), (0, 4), (1, 5), (2, 6), (3, 7) ] glBegin(GL_LINES) for edge in edges: for vertex in edge: glVertex3fv(vertices[vertex]) glEnd() # Run the game if __name__ == "__main__": pygame.mouse.set_visible(False) # Hide the mouse cursor pygame.mouse.set_pos(WIDTH // 2, HEIGHT // 2) # Center the mouse game_loop()
Step 7: Run the Game
- Open the folder containing your game files in your terminal.
- Run the game by typing:
bash CopyEdit python src/main.py
This will open a window with a rotating cube in 3D space. You can use the WASD keys to move the camera around and the mouse to look around.
What’s Happening in the Code?
engine.py
: Initializes OpenGL, sets up the camera, and handles basic movement (WASD).player.py
: Uses the mouse to look around (pitch and yaw).physics.py
: Ensures the player doesn’t fall below the ground (very basic physics).main.py
: Contains the main game loop and rendering logic (including drawing a rotating cube).
Next Steps
Once you have the basic setup running, you can:
- Add 3D models: Use
glBindTexture()
to add textures to objects or load external models like.obj
files. - Advanced Physics: Integrate more realistic physics (e.g., using PyBullet or Pymunk).
- Lighting: Use
glEnable(GL_LIGHTING)
andglLightfv()
to add lighting effects to your scene.