104 lines
3.6 KiB
Python
104 lines
3.6 KiB
Python
import math
|
|
import pygame
|
|
from spiel.game import Game
|
|
from spiel.compute import rot_3D, project_perspective
|
|
|
|
|
|
class CubeGame(Game):
|
|
"""Ein interaktives 3D-Würfel-Spiel basierend auf der MatrixMania-Basisklasse."""
|
|
|
|
def __init__(self) -> None:
|
|
super().__init__(
|
|
title="MatrixMania - Rotierender Würfel",
|
|
fps=60,
|
|
size=(800, 600)
|
|
)
|
|
|
|
# 8 Eckpunkte eines Würfels im 3D-Raum (x, y, z)
|
|
self.vertices = [
|
|
[-50, -50, -50],
|
|
[50, -50, -50],
|
|
[50, 50, -50],
|
|
[-50, 50, -50],
|
|
[-50, -50, 50],
|
|
[50, -50, 50],
|
|
[50, 50, 50],
|
|
[-50, 50, 50]
|
|
]
|
|
|
|
# Die 12 Kanten des Würfels (verbinden jeweils zwei Indizes)
|
|
self.edges = [
|
|
(0, 1), (1, 2), (2, 3), (3, 0), # Hinteres Quadrat
|
|
(4, 5), (5, 6), (6, 7), (7, 4), # Vorderes Quadrat
|
|
(0, 4), (1, 5), (2, 6), (3, 7) # Verbindungskanten
|
|
]
|
|
|
|
self.angle_x = 0.0
|
|
self.angle_y = 0.0
|
|
|
|
def handle_event(self, event: pygame.event.Event) -> bool:
|
|
"""Verarbeitet Events (z. B. Schließen des Fensters oder ESC-Taste)."""
|
|
if event.type == pygame.QUIT:
|
|
return False
|
|
if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
|
|
return False
|
|
return True
|
|
|
|
def update_game(self) -> bool:
|
|
"""Aktualisiert die Rotationswinkel bei jedem Frame."""
|
|
self.angle_x += 1.0
|
|
self.angle_y += 1.5
|
|
return True
|
|
|
|
def draw_game(self) -> None:
|
|
"""Zeichnet den 3D-Würfel auf den Bildschirm."""
|
|
if self.screen is None:
|
|
return
|
|
|
|
# Hintergrund füllen (dunkles Blau/Grau)
|
|
self.screen.fill((20, 20, 30))
|
|
|
|
center_x = self.size[0] // 2
|
|
center_y = self.size[1] // 2
|
|
|
|
projected_points = []
|
|
|
|
for vertex in self.vertices:
|
|
# 1. Rotation um die X-Achse
|
|
rx_mat = rot_3D(self.angle_x, 'x')
|
|
v_rx = [
|
|
rx_mat[0][0] * vertex[0] + rx_mat[0][1] * vertex[1] + rx_mat[0][2] * vertex[2],
|
|
rx_mat[1][0] * vertex[0] + rx_mat[1][1] * vertex[1] + rx_mat[1][2] * vertex[2],
|
|
rx_mat[2][0] * vertex[0] + rx_mat[2][1] * vertex[1] + rx_mat[2][2] * vertex[2]
|
|
]
|
|
|
|
# 2. Rotation um die Y-Achse
|
|
ry_mat = rot_3D(self.angle_y, 'y')
|
|
v_rot = [
|
|
ry_mat[0][0] * v_rx[0] + ry_mat[0][1] * v_rx[1] + ry_mat[0][2] * v_rx[2],
|
|
ry_mat[1][0] * v_rx[0] + ry_mat[1][1] * v_rx[1] + ry_mat[1][2] * v_rx[2],
|
|
ry_mat[2][0] * v_rx[0] + ry_mat[2][1] * v_rx[1] + ry_mat[2][2] * v_rx[2]
|
|
]
|
|
|
|
# 3. Perspektivische Projektion (z + 300, damit der Würfel vor der Kamera liegt)
|
|
proj_x, proj_y = project_perspective(
|
|
(v_rot[0], v_rot[1], v_rot[2] + 300),
|
|
distance=300.0,
|
|
scale=1.5
|
|
)
|
|
|
|
# In Bildschirmkoordinaten umrechnen (Zentrum als Ursprung)
|
|
projected_points.append((center_x + proj_x, center_y + proj_y))
|
|
|
|
# Kanten des Würfels zeichnen (Neon-Türkis)
|
|
for edge in self.edges:
|
|
p1 = projected_points[edge[0]]
|
|
p2 = projected_points[edge[1]]
|
|
pygame.draw.line(self.screen, (0, 255, 180), p1, p2, 2)
|
|
|
|
# Eckpunkte als weiße Kreise zeichnen
|
|
for p in projected_points:
|
|
pygame.draw.circle(self.screen, (255, 255, 255), (int(p[0]), int(p[1])), 4)
|
|
|
|
# Aufruf der Basisklasse, um das Display zu aktualisieren (flip)
|
|
super().draw_game() |