111 lines
4.1 KiB
Python
111 lines
4.1 KiB
Python
import pygame
|
|
from spiel.game import Game
|
|
from spiel.compute import rot_3D, project_ortho
|
|
|
|
|
|
class CubeGame(Game):
|
|
"""Ein interaktiver 3D-Würfel-Spiel basierend auf der MatrixMania-Aufgabenstellung."""
|
|
|
|
def __init__(self) -> None:
|
|
super().__init__(
|
|
title="MatrixMania - Rotierender Würfel",
|
|
fps=60,
|
|
size=(800, 600)
|
|
)
|
|
|
|
# Die 8 Eckpunkte des Würfels laut Aufgabenstellung (Mittelpunkt im Ursprung)
|
|
self.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]
|
|
]
|
|
|
|
# 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
|
|
]
|
|
|
|
# Startwinkel für x- und y-Achse sind 0
|
|
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, ESC oder Mausbewegung)."""
|
|
if event.type == pygame.QUIT:
|
|
return False
|
|
if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
|
|
return False
|
|
|
|
# Maussteuerung: Wenn die linke Maustaste gedrückt gehalten wird, Würfel drehen
|
|
if event.type == pygame.MOUSEMOTION:
|
|
# Prüfen, ob die linke Maustaste (Index 0) gedrückt gehalten wird
|
|
if pygame.mouse.get_pressed()[0]:
|
|
dx, dy = event.rel # Relativer Maus-Versatz seit dem letzten Event
|
|
self.angle_y += dx * 0.01
|
|
self.angle_x += dy * 0.01
|
|
|
|
return True
|
|
|
|
def update_game(self) -> bool:
|
|
"""Aktualisiert das Spiel (hier keine automatische Drehung mehr nötig)."""
|
|
return True
|
|
|
|
def draw_game(self) -> None:
|
|
"""Berechnet die Rotationen, projiziert orthogonal und zeichnet den Würfel."""
|
|
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 = []
|
|
scale_factor = 100.0 # Skalierungsfaktor, damit der Würfel groß genug ist
|
|
|
|
for vertex in self.vertices:
|
|
# 1. Rotationsmatrix für x-Achse generieren und anwenden
|
|
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. Rotationsmatrix für y-Achse generieren und anwenden
|
|
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. Orthogonale Projektion auf die Bildschirmebene
|
|
proj_x, proj_y = project_ortho(v_rot)
|
|
|
|
# 4. Skalieren und in Bildschirmkoordinaten zentrieren
|
|
screen_x = center_x + proj_x * scale_factor
|
|
screen_y = center_y + proj_y * scale_factor
|
|
|
|
projected_points.append((screen_x, screen_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 für das Display-Update (flip)
|
|
super().draw_game() |