Ende Unterricht 24.04.26

This commit is contained in:
Benjamin Hinz 2026-04-24 18:45:31 +02:00
parent 4d8ef1f040
commit 35e22e1fbf
5 changed files with 189 additions and 125 deletions

100
editor.py
View file

@ -1,11 +1,11 @@
import pygame
import sys
from scripts.tilemap import *
from scripts.clouds import Clouds
from scripts.tilemap import Tilemap
from scripts.utils import load_images
RENDER_SCALE = 2.0
class better_list(list):
def __init__(self, iterable):
super().__init__(iterable)
@ -15,20 +15,16 @@ class Editor:
def __init__(self):
pygame.init()
pygame.display.set_caption("Editor")
self.screen = pygame.display.set_mode((640, 480))
self.display = pygame.Surface((320, 240))
self.clock = pygame.time.Clock()
self.running = True
self.collision_area = pygame.Rect(50, 50, 300, 50)
self.movement = [False, False, False, False]
self.assets = {
"decor": load_images("tiles/decor"),
"grass": load_images("tiles/grass"),
@ -39,10 +35,10 @@ class Editor:
self.tilemap = Tilemap(self, 16)
try:
self.tilemap.load('map.json')
self.tilemap.load("map.json")
except FileNotFoundError:
pass
self.isJumping = False
self.scroll = [0, 0]
self.tile_list = better_list(self.assets)
@ -60,55 +56,71 @@ class Editor:
def run(self):
while self.running:
self.display.fill((0, 0, 0))
self.scroll[0] += (self.movement[1] - self.movement[0]) * 3
self.scroll[1] += (self.movement[3] - self.movement[2]) * 3
render_scroll = (int(self.scroll[0]), int(self.scroll[1]))
self.tilemap.render(surface=self.display, offset=render_scroll)
current_tile_img = self.assets[self.tile_list[self.tile_group]][self.tile_variant].copy()
current_tile_img = self.assets[self.tile_list[self.tile_group]][
self.tile_variant
].copy()
current_tile_img.set_alpha(100)
mpos = pygame.mouse.get_pos()
mpos = (mpos[0] / RENDER_SCALE, mpos[1] / RENDER_SCALE)
tile_pos = (int((mpos[0] + self.scroll[0]) // self.tilemap.tile_size),
int((mpos[1] + self.scroll[1]) // self.tilemap.tile_size))
tile_pos = (
int((mpos[0] + self.scroll[0]) // self.tilemap.tile_size),
int((mpos[1] + self.scroll[1]) // self.tilemap.tile_size),
)
# print(tile_pos[0] * self.tilemap.tile_size + self.scroll[0], tile_pos[1]*self.tilemap.tile_size + self.scroll[1])
# print(mpos)
if self.on_grid:
self.display.blit(current_tile_img, (tile_pos[0] * self.tilemap.tile_size - self.scroll[0], tile_pos[1]*self.tilemap.tile_size - self.scroll[1]))
self.display.blit(
current_tile_img,
(
tile_pos[0] * self.tilemap.tile_size - self.scroll[0],
tile_pos[1] * self.tilemap.tile_size - self.scroll[1],
),
)
else:
self.display.blit(current_tile_img, (mpos[0], mpos[1]))
if self.clicking:
if self.on_grid:
self.tilemap.tilemap[str(tile_pos[0]) + ';' + str(tile_pos[1])] = {
'type': self.tile_list[self.tile_group],
'variant': self.tile_variant,
'pos': tile_pos
self.tilemap.tilemap[str(tile_pos[0]) + ";" + str(tile_pos[1])] = {
"type": self.tile_list[self.tile_group],
"variant": self.tile_variant,
"pos": tile_pos,
}
if not self.on_grid:
self.tilemap.offgrid_tiles.append({"type": self.tile_list[self.tile_group], "variant": self.tile_variant, "pos": (mpos[0]+self.scroll[0], mpos[1]+self.scroll[1])})
self.tilemap.offgrid_tiles.append(
{
"type": self.tile_list[self.tile_group],
"variant": self.tile_variant,
"pos": (mpos[0] + self.scroll[0], mpos[1] + self.scroll[1]),
}
)
if self.right_clicking:
tile_loc = str(tile_pos[0]) + ';' + str(tile_pos[1])
tile_loc = str(tile_pos[0]) + ";" + str(tile_pos[1])
if tile_loc in self.tilemap.tilemap:
del self.tilemap.tilemap[tile_loc]
for offi_tile in self.tilemap.offgrid_tiles.copy():
offi_tile_img = self.assets[offi_tile['type']][offi_tile['variant']]
offi_tile_r = pygame.Rect(offi_tile["pos"][0]-self.scroll[0], offi_tile["pos"][1]-self.scroll[1], offi_tile_img.get_width(), offi_tile_img.get_height())
offi_tile_img = self.assets[offi_tile["type"]][offi_tile["variant"]]
offi_tile_r = pygame.Rect(
offi_tile["pos"][0] - self.scroll[0],
offi_tile["pos"][1] - self.scroll[1],
offi_tile_img.get_width(),
offi_tile_img.get_height(),
)
if offi_tile_r.collidepoint(mpos):
self.tilemap.offgrid_tiles.remove(offi_tile)
self.display.blit(current_tile_img, (5, 5))
@ -116,14 +128,15 @@ class Editor:
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.running = False
if event.type == pygame.MOUSEBUTTONUP:
if event.button == 1: self.clicking = False
if event.button == 3: self.right_clicking = False
if event.type == pygame.MOUSEBUTTONUP:
if event.button == 1:
self.clicking = False
if event.button == 3:
self.right_clicking = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
self.running = False
@ -144,17 +157,15 @@ class Editor:
if event.key == pygame.K_g:
self.on_grid = not self.on_grid
if event.key == pygame.K_o:
self.tilemap.save('map.json')
self.tilemap.save("map.json")
print("Map gespeichert")
if event.key == pygame.K_t:
self.tilemap.autotile()
if event.type == pygame.KEYUP:
if event.key == pygame.K_a:
self.movement[0] = False
@ -178,13 +189,16 @@ class Editor:
if event.button == 4:
if self.shifting:
if self.tile_variant >= len(self.assets[self.tile_list[self.tile_group]])-1:
if (
self.tile_variant
>= len(self.assets[self.tile_list[self.tile_group]]) - 1
):
self.tile_variant = 0
else:
self.tile_variant += 1
else:
if self.tile_group >= len(self.tile_list)-1:
if self.tile_group >= len(self.tile_list) - 1:
self.tile_group = 0
self.tile_variant = 0
else:
@ -193,32 +207,34 @@ class Editor:
if event.button == 5:
if self.shifting:
if self.tile_variant <= 0:
self.tile_variant = len(self.assets[self.tile_list[self.tile_group]])-1 # Lösung vom Problem musste das von oben nehmen: len(self.assets[self.tile_list[self.tile_group]])-1
self.tile_variant = (
len(self.assets[self.tile_list[self.tile_group]])
- 1
) # Lösung vom Problem musste das von oben nehmen: len(self.assets[self.tile_list[self.tile_group]])-1
else:
self.tile_variant -= 1
else:
if self.tile_group <= 0:
self.tile_group = len(self.tile_list)-1
self.tile_group = len(self.tile_list) - 1
self.tile_variant = 0
else:
self.tile_group -= 1
self.tile_variant = 0
self.zaehler += 1
if self.zaehler >= self.sekunde:
self.zaehler = 0
# print(f"{self.tile_group} + {self.tile_variant}")
self.screen.blit(pygame.transform.scale(self.display, self.screen.get_size()), (0,0))
self.screen.blit(
pygame.transform.scale(self.display, self.screen.get_size()), (0, 0)
)
pygame.display.update()
self.clock.tick(60)
pygame.quit()
sys.exit()

View file

@ -110,8 +110,6 @@ class Game:
game = Game()
if __name__ == "__main__":
# game.testolino()
pass
game.run()

View file

@ -1,24 +1,29 @@
import pygame
class PhysicsEntity:
from scripts.tilemap import Tilemap
def __init__(self, game, e_type, pos:tuple, size):
class PhysicsEntity:
def __init__(self, game, e_type, pos: tuple, size):
self.game = game
self.e_type = e_type
self.pos = list(pos)
self.size = size
self.velocity = [0,0]
self.collisions = {"top":False, "bottom":False, "left":False, "right":False}
self.velocity = [0, 0]
self.collisions = {"top": False, "bottom": False, "left": False, "right": False}
self.speed = 3
# Animationen
self.action = ""
self.flip = False
self.animation_offset = (-3, -3)
self.set_action('idle')
self.set_action("idle")
def update(self, tilemap, movement:tuple=(0,0)):
self.collisions = {"top":False, "bottom":False, "left":False, "right":False}
frame_movement = ((movement[0] + self.velocity[0])*self.speed, movement[1] + self.velocity[1])
def update(self, tilemap: Tilemap, movement: tuple = (0, 0)):
self.collisions = {"top": False, "bottom": False, "left": False, "right": False}
frame_movement = (
(movement[0] + self.velocity[0]) * self.speed,
movement[1] + self.velocity[1],
)
self.pos[0] += frame_movement[0]
entity_rect = self.rect()
@ -29,21 +34,18 @@ class PhysicsEntity:
self.collisions["right"] = True
self.game.isJumping = False
if frame_movement[0] < 0:
entity_rect.left = recto.right
self.collisions["left"] = True
self.game.isJumping = False
self.pos[0] = entity_rect.x
self.pos[1] += frame_movement[1]
entity_rect = self.rect()
for rectolino in tilemap.physics_rects_around(self.pos):
if entity_rect.colliderect(rectolino):
if frame_movement[1] > 0:
entity_rect.bottom = rectolino.top
self.collisions["bottom"] = True
@ -54,26 +56,31 @@ class PhysicsEntity:
self.collisions["top"] = True
self.pos[1] = entity_rect.y
if movement[0] > 0:
self.flip = False
if movement[0] < 0:
self.flip = True
if self.collisions['bottom'] or self.collisions['top']:
self.velocity[1] = 0
if self.collisions["bottom"] or self.collisions["top"]:
self.velocity[1] = 0
self.velocity[1] = min(5, self.velocity[1] + 0.1)
self.animation.update()
def render(self, surface:pygame.Surface, offset:tuple=(0,0)):
surface.blit(pygame.transform.flip(self.animation.img(), self.flip, False), (self.pos[0] - offset[0] + self.animation_offset[0],self.pos[1] - offset[1] + self.animation_offset[1]))
def render(self, surface: pygame.Surface, offset: tuple = (0, 0)):
surface.blit(
pygame.transform.flip(self.animation.img(), self.flip, False),
(
self.pos[0] - offset[0] + self.animation_offset[0],
self.pos[1] - offset[1] + self.animation_offset[1],
),
)
def rect(self):
return pygame.Rect(self.pos[0], self.pos[1], self.size[0], self.size[1])
def set_action(self, action):
if action != self.action:
self.action = action
@ -86,7 +93,7 @@ class Player(PhysicsEntity):
super().__init__(game, "player", pos, size)
self.air_time = 0 # Neu! Zählt, wie lange wir fallen/springe
def update(self, tilemap, movement = (0, 0)):
def update(self, tilemap, movement=(0, 0)):
super().update(tilemap, movement)
self.air_time += 1
@ -95,13 +102,12 @@ class Player(PhysicsEntity):
self.air_time = 0
# Wenn wir länger als 4 Frames in der Luft sind -> Springen!
if self.air_time > 4:
self.set_action('jump')
self.set_action("jump")
# Wenn wir nicht in der Luft sind, aber uns bewegen -> Rennen!
elif movement[0] != 0:
self.set_action('run')
self.set_action("run")
# Sonst -> Rumstehen!
else:
self.set_action('idle')
self.set_action("idle")

View file

@ -9,15 +9,21 @@ class Tile:
def grid_key(self) -> str:
"""Erzeugt den String-Key für die Tilemap."""
return f"{self.pos[0]};{self.pos[1]}"
return f"{self.pos[0]};{self.post[1]}"
def to_dict(self) -> dict:
"""Konvertiert zurück in ein Dict (für JSON-Speicherung)."""
return {"type": self.type, "variant": self.variant, "pos": list(self.pos)}
return {
'type': self.type,
'variant': self.variant,
'pos': list(self.post)
}
@staticmethod
def from_dict(data: dict) -> "Tile":
def from_dict(data: dict) -> 'Tile':
"""Erstellt ein Tile aus einem Dictionary."""
return Tile(type=data["type"], variant=data["variant"], pos=data["pos"])
return Tile(
type=data['type'],
variant=data['variant'],
pos=list(data['pos']),
)

View file

@ -1,9 +1,20 @@
import pygame as pg
import json
from scripts.models import Tile
NEIGHBOR_OFFSETS = [(0, 0), (-1, 0), (-1, -1), (-1, 1), (0, 1), (0, -1), (1, 1), (1, 0), (1, -1)]
NEIGHBOR_OFFSETS = [
(0, 0),
(-1, 0),
(-1, -1),
(-1, 1),
(0, 1),
(0, -1),
(1, 1),
(1, 0),
(1, -1),
]
PHYSICS_TILES = {"grass", "stone"}
AUTOTILE_TYPES = {'grass', 'stone'}
AUTOTILE_TYPES = {"grass", "stone"}
AUTOTILE_MAP = {
tuple(sorted([(1, 0), (0, 1)])): 0,
tuple(sorted([(1, 0), (0, 1), (-1, 0)])): 1,
@ -16,50 +27,69 @@ AUTOTILE_MAP = {
tuple(sorted([(1, 0), (-1, 0), (0, 1), (0, -1)])): 8,
}
class Tilemap:
def __init__(self, game, tile_size=16):
"""Eine Map aus Tiles."""
def __init__(self, game, tile_size: int = 16):
self.game = game
self.tile_size = tile_size
self.tilemap = {}
self.offgrid_tiles = []
for i in range(10):
self.tilemap[str(3 + i) + ';10'] = {'type': 'grass', 'variant': 1, 'pos': (3 + i, 10)}
self.tilemap['10;' + str(5 + i)] = {'type': 'stone', 'variant': 1, 'pos': (10, i + 5)}
self.tilemap[str(3 + i) + ";10"] = Tile("grass", 1, (3 + i, 10))
self.tilemap["10;" + str(5 + i)] = Tile("stone", 1, (10, i + 5))
def render(self, surface:pg.Surface, offset:tuple=(0,0)):
def render(self, surface: pg.Surface, offset: tuple = (0, 0)):
for tile in self.offgrid_tiles:
surface.blit(self.game.assets[tile['type']][tile['variant']], (tile['pos'][0] - offset[0], tile['pos'][1] - offset[1]))
for x in range(offset[0] // self.tile_size, (offset[0] + surface.get_width()) // self.tile_size + 1):
for y in range(offset[1] // self.tile_size, (offset[1] + surface.get_height()) // self.tile_size + 1):
surface.blit(
self.game.assets[tile["type"]][tile["variant"]],
(tile["pos"][0] - offset[0], tile["pos"][1] - offset[1]),
)
for x in range(
offset[0] // self.tile_size,
(offset[0] + surface.get_width()) // self.tile_size + 1,
):
for y in range(
offset[1] // self.tile_size,
(offset[1] + surface.get_height()) // self.tile_size + 1,
):
location_key = f"{x};{y}"
if location_key in self.tilemap:
tile = self.tilemap[location_key]
surface.blit(self.game.assets[tile['type']][tile['variant']], (tile['pos'][0] * self.tile_size - offset[0],
tile['pos'][1] * self.tile_size - offset[1]))
surface.blit(
self.game.assets[tile["type"]][tile["variant"]],
(
tile["pos"][0] * self.tile_size - offset[0],
tile["pos"][1] * self.tile_size - offset[1],
),
)
def tiles_around(self, pos):
def tiles_around(self, pos: list):
tiles = []
tile_location = (int(pos[0] // self.tile_size), int(pos[1] // self.tile_size))
for offset in NEIGHBOR_OFFSETS:
check_location = str(tile_location[0] + offset[0]) + ';' + str(tile_location[1] + offset[1])
check_location = (
str(tile_location[0] + offset[0])
+ ";"
+ str(tile_location[1] + offset[1])
)
if check_location in self.tilemap:
tiles.append(self.tilemap[check_location])
return tiles
def physics_rects_around(self, pos) -> list:
def physics_rects_around(self, pos: list) -> list:
# Erzeuge eine leere Liste für die Rechtecke
rects = []
# Durchlaufe alle Tiles aus der Umgebung (tiles_around)
for tile in self.tiles_around(pos):
# Prüfe, ob der Tile-Typ in PHYSICS_TILES enthalten ist
# (nur diese Tiles sollen kollidieren)
if tile["type"] in PHYSICS_TILES:
# Rechne die Tile-Position in Pixel-Koordinaten um
x_pixel = tile['pos'][0] * self.tile_size
y_pixel = tile['pos'][1] * self.tile_size
x_pixel = tile["pos"][0] * self.tile_size
y_pixel = tile["pos"][1] * self.tile_size
# Erzeuge ein pygame.Rect mit:
# - x_pixel
@ -72,44 +102,52 @@ class Tilemap:
# Gib die Liste der Rechtecke zurück
return rects
def autotile(self):
for loc in self.tilemap:
for loc in self.tilemap:
tile = self.tilemap[loc]
tile = self.tilemap[loc]
neighbors = set()
neighbors = set()
for shift in [(1, 0), (-1, 0), (0, -1), (0, 1)]:
check_loc = (
str(tile["pos"][0] + shift[0])
+ ";"
+ str(tile["pos"][1] + shift[1])
)
for shift in [(1, 0), (-1, 0), (0, -1), (0, 1)]:
if check_loc in self.tilemap:
if self.tilemap[check_loc]["type"] == tile["type"]:
neighbors.add(shift)
check_loc = str(tile['pos'][0] + shift[0]) + ';' + str(tile['pos'][1] + shift[1])
neighbors = tuple(sorted(neighbors))
if check_loc in self.tilemap:
if (tile["type"] in AUTOTILE_TYPES) and (neighbors in AUTOTILE_MAP):
tile["variant"] = AUTOTILE_MAP[neighbors]
if self.tilemap[check_loc]['type'] == tile['type']:
def save(self, path: str) -> None:
neighbors.add(shift)
data = {
"tilemap": {k: tile.to_dict() for k, tile in self.tilemap.items()},
"tile_size": self.tile_size,
"offgrid": [tile.to_dict() for tile in self.offgrid_tiles],
}
neighbors = tuple(sorted(neighbors))
if (tile['type'] in AUTOTILE_TYPES) and (neighbors in AUTOTILE_MAP):
tile['variant'] = AUTOTILE_MAP[neighbors]
def save(self, path):
with open(path, "w") as f:
json.dump(data, f)
json.dump({'tilemap': self.tilemap, 'tile_size': self.tile_size, 'offgrid': self.offgrid_tiles}, f)
def load(self, path: str):
"""Lädt die gespeicherte Tilemap."""
def load(self, path):
with open(path, "r") as f:
map_data = json.load(f)
map_data: dict = json.load(f)
self.tilemap = map_data['tilemap']
self.tile_size = map_data['tile_size']
self.offgrid_tiles = map_data['offgrid']
self.tilemap: dict[str:Tile] = {}
self.offgrid_tiles: list[Tile] = []
for key, data in map_data.items():
self.tilemap[key] = Tile.from_dict(data)
for tile in map_data["offgrid"]:
self.offgrid_tiles.append(Tile.from_dict(tile))