Gegner Bewegung ohne Runterfallen

This commit is contained in:
Benjamin 2026-06-12 16:43:28 +02:00
parent 8e465ba27b
commit 86312b2106
5 changed files with 151 additions and 76 deletions

View file

@ -14,7 +14,9 @@ class Editor:
pygame.display.set_caption("Editor")
self.screen: pygame.Surface = pygame.display.set_mode((640, 480))
self.display: pygame.Surface = pygame.Surface((self.screen.get_width()//2, self.screen.get_height()//2))
self.display: pygame.Surface = pygame.Surface(
(self.screen.get_width() // 2, self.screen.get_height() // 2)
)
self.clock: pygame.time.Clock = pygame.time.Clock()
@ -62,9 +64,9 @@ class Editor:
self.tilemap.render(surface=self.display, offset=render_scroll)
# Aktuelle Kachel (Kopie)
current_tile_img: pygame.Surface = self.assets[self.tile_list[self.tile_group]][
self.tile_variant
].copy()
current_tile_img: pygame.Surface = self.assets[
self.tile_list[self.tile_group]
][self.tile_variant].copy()
# Halbtransparent
current_tile_img.set_alpha(100)
@ -243,4 +245,5 @@ class Editor:
pygame.quit()
sys.exit()
Editor().run()

26
game.py
View file

@ -1,4 +1,4 @@
import pygame # ty: ignore[unresolved-import]
import pygame # ty: ignore[unresolved-import]
import sys
import random
import math
@ -52,10 +52,12 @@ class Game:
"player/wall_slide": Animation(
load_images(path="entities/player/wall_slide")
),
"enemy/idle": Animation(load_images(path="entities/enemy/idle"), image_duration=6),
"enemy/run": Animation(load_images(path="entities/enemy/run"), image_duration=4),
"enemy/idle": Animation(
load_images(path="entities/enemy/idle"), image_duration=6
),
"enemy/run": Animation(
load_images(path="entities/enemy/run"), image_duration=4
),
"particles/leaf": Animation(
load_images(path="particles/leaf"), image_duration=20, loop=False
),
@ -64,7 +66,7 @@ class Game:
),
"particles/particle": Animation(
load_images(path="particles/particle"), image_duration=6, loop=False
)
),
}
self.player: Player = Player(game=self, pos=(50, 50), size=(8, 15))
@ -128,8 +130,6 @@ class Game:
)
)
self.clouds.update()
self.clouds.render(surface=self.display, offset=render_scroll)
self.tilemap.render(surface=self.display, offset=render_scroll)
@ -147,13 +147,11 @@ class Game:
for particle in self.particles.copy():
kill: bool = particle.update()
particle.render(self.display, offset=render_scroll)
if particle.type == 'leaf':
if particle.type == "leaf":
particle.pos[0] += math.sin(particle.animation.frame * 0.035) * 0.3
if kill:
self.particles.remove(particle)
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.running = False
@ -164,7 +162,9 @@ class Game:
self.movement[0] = True
if event.key == pygame.K_d:
self.movement[1] = True
if event.key == pygame.K_SPACE or event.key == pygame.K_w: # and not self.isJumping
if (
event.key == pygame.K_SPACE or event.key == pygame.K_w
): # and not self.isJumping
self.player.jump()
if event.key == pygame.K_m:
@ -195,7 +195,5 @@ class Game:
sys.exit()
game = Game()
game.run()

View file

@ -105,6 +105,7 @@ class PhysicsEntity:
image = self.e_type + "/" + self.action
self.animation = self.game.assets[image].copy()
class Enemy(PhysicsEntity):
def __init__(self, game, pos: tuple[float, float], size: tuple[int, int]):
super().__init__(game, "enemy", pos, size)
@ -113,13 +114,26 @@ class Enemy(PhysicsEntity):
def update(self, tilemap, movement: tuple[float, float] = (0, 0)):
if self.walking:
movement = (movement[0] - 0.5 if self.flip else 0.5, movement[1])
if tilemap.solid_check(
(self.rect().centerx + (-7 if self.flip else 7), self.pos[1] + 23)
):
if self.collisions.right or self.collisions.left:
self.flip = not self.flip
else:
movement = (movement[0] - 0.5 if self.flip else 0.5, movement[1])
else:
self.flip = not self.flip
self.walking = max(0, self.walking - 1)
elif random.random() < 0.01:
self.walking = random.randint(30, 120)
super().update(tilemap, movement)
if movement[0] != 0:
self.set_action("run")
else:
self.set_action("idle")
class Player(PhysicsEntity):
def __init__(self, game, pos: tuple[float, float], size: tuple[int, int]):
@ -199,7 +213,15 @@ class Player(PhysicsEntity):
0,
]
self.game.particles.append(Particle(self.game, "particle", self.rect().center, particle_velocity, random.randint(0, 7)))
self.game.particles.append(
Particle(
self.game,
"particle",
self.rect().center,
particle_velocity,
random.randint(0, 7),
)
)
if self.velocity[0] > 0:
self.velocity[0] = max(self.velocity[0] - 0.1, 0)
@ -220,7 +242,7 @@ class Player(PhysicsEntity):
self.air_time = 5
self.jumps = max(0, self.jumps - 1)
elif self.jumps > 0:
elif self.jumps > 0: # and not self.dashing
self.velocity[1] = -3
self.jumps -= 1
self.air_time = 5
@ -246,7 +268,6 @@ class Player(PhysicsEntity):
else:
self.dashing = 60
def render(self, surface, offset=(0, 0)):
if abs(self.dashing) <= 50:
super().render(surface, offset)

View file

@ -4,12 +4,19 @@ from scripts.animation import Animation
class Particle:
def __init__(self, game, p_type: str, pos: tuple[float, float], velocity: list[float] = [0, 0], frame: int = 0):
def __init__(
self,
game,
p_type: str,
pos: tuple[float, float],
velocity: list[float] = [0, 0],
frame: int = 0,
):
self.game = game
self.type: str = p_type
self.pos: list[float] = list(pos)
self.velocity: list[float] = list(velocity)
self.animation: Animation = self.game.assets['particles/' + p_type].copy()
self.animation: Animation = self.game.assets["particles/" + p_type].copy()
self.animation.frame = frame
def update(self) -> bool:
@ -24,7 +31,9 @@ class Particle:
return kill
def render(self, surface: pygame.Surface, offset: tuple[float, float] = (0, 0)) -> None:
def render(
self, surface: pygame.Surface, offset: tuple[float, float] = (0, 0)
) -> None:
img = self.animation.img()
surface.blit(
img,

View file

@ -14,9 +14,20 @@ AUTOTILE_MAP: dict[tuple[tuple[int, int], ...], int] = {
tuple(sorted([(1, 0), (-1, 0), (0, 1), (0, -1)])): 8,
}
NEIGHBOR_OFFSETS: list[tuple[int, int]] = [(-1, 0), (-1, -1), (0, -1), (1, -1), (1, 0), (0, 0), (-1, 1), (0, 1), (1, 1)]
PHYSICS_TILES: set[str] = {'grass', 'stone'}
AUTOTILE_TYPES: set[str] = {'grass', 'stone'}
NEIGHBOR_OFFSETS: list[tuple[int, int]] = [
(-1, 0),
(-1, -1),
(0, -1),
(1, -1),
(1, 0),
(0, 0),
(-1, 1),
(0, 1),
(1, 1),
]
PHYSICS_TILES: set[str] = {"grass", "stone"}
AUTOTILE_TYPES: set[str] = {"grass", "stone"}
class Tilemap:
def __init__(self, game, tile_size: int = 16) -> None:
@ -25,7 +36,9 @@ class Tilemap:
self.tilemap: dict[str, Tile] = {}
self.offgrid_tiles: list[Tile] = []
def extract(self, id_pairs: list[tuple[str, int]], keep: bool = False) -> list[dict]:
def extract(
self, id_pairs: list[tuple[str, int]], keep: bool = False
) -> list[dict]:
matches: list[dict] = []
for tile in self.offgrid_tiles.copy():
if (tile.type, tile.variant) in id_pairs:
@ -37,7 +50,10 @@ class Tilemap:
tilemap_tile = self.tilemap[loc]
if (tilemap_tile.type, tilemap_tile.variant) in id_pairs:
end_match = tilemap_tile.to_dict()
end_match["pos"] = [end_match['pos'][0] * self.tile_size, end_match['pos'][1] * self.tile_size]
end_match["pos"] = [
end_match["pos"][0] * self.tile_size,
end_match["pos"][1] * self.tile_size,
]
matches.append(end_match)
@ -47,48 +63,67 @@ class Tilemap:
def tiles_around(self, pos: tuple[float, float]) -> list[Tile]:
tiles: list[Tile] = []
tile_location: tuple[int, int] = (int(pos[0] // self.tile_size), int(pos[1] // self.tile_size))
tile_location: tuple[int, int] = (
int(pos[0] // self.tile_size),
int(pos[1] // self.tile_size),
)
for offset in NEIGHBOR_OFFSETS:
check_location: str = str(tile_location[0] + offset[0]) + ';' + str(tile_location[1] + offset[1])
check_location: str = (
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 save(self, path: str) -> None:
data: dict = {
'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
],
"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],
}
with open(path, 'w') as f:
with open(path, "w") as f:
json.dump(data, f)
def load(self, path: str) -> None:
with open(path, 'r') as f:
with open(path, "r") as f:
map_data = json.load(f)
self.tile_size: int = map_data['tile_size']
self.tile_size: int = map_data["tile_size"]
# Grid-Tiles: Dict -> Tile-Objekte
self.tilemap: dict[str, Tile] = {}
for key, tile_data in map_data['tilemap'].items():
for key, tile_data in map_data["tilemap"].items():
self.tilemap[key] = Tile.from_dict(tile_data)
# Offgrid-Tiles: Dict -> Tile-Objekte
self.offgrid_tiles: list[Tile] = [
Tile.from_dict(t) for t in map_data['offgrid']
Tile.from_dict(t) for t in map_data["offgrid"]
]
def solid_check(self, pos: tuple[float, float]):
tile_loc = (
str(int(pos[0] // self.tile_size))
+ ";"
+ str(int(pos[1] // self.tile_size))
)
if tile_loc in self.tilemap:
if self.tilemap[tile_loc].type in PHYSICS_TILES:
return self.tilemap[tile_loc]
def physics_rects_around(self, pos: tuple[float, float]) -> list[pygame.Rect]:
rects: list[pygame.Rect] = []
for tile in self.tiles_around(pos):
if tile.type in PHYSICS_TILES:
rects.append(pygame.Rect(tile.pos[0] * self.tile_size, tile.pos[1] * self.tile_size, self.tile_size, self.tile_size))
rects.append(
pygame.Rect(
tile.pos[0] * self.tile_size,
tile.pos[1] * self.tile_size,
self.tile_size,
self.tile_size,
)
)
return rects
def autotile(self):
@ -96,7 +131,9 @@ class Tilemap:
tile: Tile = self.tilemap[loc]
neighbors: set[tuple[int, int]] = set()
for shift in [(1, 0), (-1, 0), (0, -1), (0, 1)]:
check_loc: str = str(tile.pos[0] + shift[0]) + ';' + str(tile.pos[1] + shift[1])
check_loc: str = (
str(tile.pos[0] + shift[0]) + ";" + str(tile.pos[1] + shift[1])
)
if check_loc in self.tilemap:
if self.tilemap[check_loc].type == tile.type:
neighbors.add(shift)
@ -104,23 +141,30 @@ class Tilemap:
if (tile.type in AUTOTILE_TYPES) and (neighbors in AUTOTILE_MAP):
tile.variant = AUTOTILE_MAP[neighbors]
def render(self, surface: pygame.Surface, offset: tuple[float, float] = (0, 0)) -> None:
def render(
self, surface: pygame.Surface, offset: tuple[float, float] = (0, 0)
) -> None:
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])
(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: str = str(x) + ';' + str(y)
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: str = str(x) + ";" + str(y)
if location_key in self.tilemap:
tile: 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])
)
(
tile.pos[0] * self.tile_size - offset[0],
tile.pos[1] * self.tile_size - offset[1],
),
)