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

24
game.py
View file

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

View file

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

View file

@ -4,12 +4,19 @@ from scripts.animation import Animation
class Particle: 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.game = game
self.type: str = p_type self.type: str = p_type
self.pos: list[float] = list(pos) self.pos: list[float] = list(pos)
self.velocity: list[float] = list(velocity) 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 self.animation.frame = frame
def update(self) -> bool: def update(self) -> bool:
@ -24,7 +31,9 @@ class Particle:
return kill 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() img = self.animation.img()
surface.blit( surface.blit(
img, 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, 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)] NEIGHBOR_OFFSETS: list[tuple[int, int]] = [
PHYSICS_TILES: set[str] = {'grass', 'stone'} (-1, 0),
AUTOTILE_TYPES: set[str] = {'grass', 'stone'} (-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: class Tilemap:
def __init__(self, game, tile_size: int = 16) -> None: def __init__(self, game, tile_size: int = 16) -> None:
@ -25,7 +36,9 @@ class Tilemap:
self.tilemap: dict[str, Tile] = {} self.tilemap: dict[str, Tile] = {}
self.offgrid_tiles: list[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] = [] matches: list[dict] = []
for tile in self.offgrid_tiles.copy(): for tile in self.offgrid_tiles.copy():
if (tile.type, tile.variant) in id_pairs: if (tile.type, tile.variant) in id_pairs:
@ -37,7 +50,10 @@ class Tilemap:
tilemap_tile = self.tilemap[loc] tilemap_tile = self.tilemap[loc]
if (tilemap_tile.type, tilemap_tile.variant) in id_pairs: if (tilemap_tile.type, tilemap_tile.variant) in id_pairs:
end_match = tilemap_tile.to_dict() 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) matches.append(end_match)
@ -47,48 +63,67 @@ class Tilemap:
def tiles_around(self, pos: tuple[float, float]) -> list[Tile]: def tiles_around(self, pos: tuple[float, float]) -> list[Tile]:
tiles: 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: 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: if check_location in self.tilemap:
tiles.append(self.tilemap[check_location]) tiles.append(self.tilemap[check_location])
return tiles return tiles
def save(self, path: str) -> None: def save(self, path: str) -> None:
data: dict = { data: dict = {
'tilemap': { "tilemap": {k: tile.to_dict() for k, tile in self.tilemap.items()},
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],
'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) json.dump(data, f)
def load(self, path: str) -> None: def load(self, path: str) -> None:
with open(path, 'r') as f: with open(path, "r") as f:
map_data = json.load(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 # Grid-Tiles: Dict -> Tile-Objekte
self.tilemap: dict[str, Tile] = {} 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) self.tilemap[key] = Tile.from_dict(tile_data)
# Offgrid-Tiles: Dict -> Tile-Objekte # Offgrid-Tiles: Dict -> Tile-Objekte
self.offgrid_tiles: list[Tile] = [ 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]: def physics_rects_around(self, pos: tuple[float, float]) -> list[pygame.Rect]:
rects: list[pygame.Rect] = [] rects: list[pygame.Rect] = []
for tile in self.tiles_around(pos): for tile in self.tiles_around(pos):
if tile.type in PHYSICS_TILES: 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 return rects
def autotile(self): def autotile(self):
@ -96,7 +131,9 @@ class Tilemap:
tile: Tile = self.tilemap[loc] tile: Tile = self.tilemap[loc]
neighbors: set[tuple[int, int]] = set() neighbors: set[tuple[int, int]] = set()
for shift in [(1, 0), (-1, 0), (0, -1), (0, 1)]: 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 check_loc in self.tilemap:
if self.tilemap[check_loc].type == tile.type: if self.tilemap[check_loc].type == tile.type:
neighbors.add(shift) neighbors.add(shift)
@ -104,23 +141,30 @@ class Tilemap:
if (tile.type in AUTOTILE_TYPES) and (neighbors in AUTOTILE_MAP): if (tile.type in AUTOTILE_TYPES) and (neighbors in AUTOTILE_MAP):
tile.variant = AUTOTILE_MAP[neighbors] 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: for tile in self.offgrid_tiles:
surface.blit( surface.blit(
self.game.assets[tile.type][tile.variant], 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, for x in range(
(offset[0] + surface.get_width()) // self.tile_size + 1): offset[0] // self.tile_size,
for y in range(offset[1] // self.tile_size, (offset[0] + surface.get_width()) // self.tile_size + 1,
(offset[1] + surface.get_height()) // self.tile_size + 1): ):
location_key: str = str(x) + ';' + str(y) 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: if location_key in self.tilemap:
tile: Tile = self.tilemap[location_key] tile: Tile = self.tilemap[location_key]
surface.blit( surface.blit(
self.game.assets[tile.type][tile.variant], self.game.assets[tile.type][tile.variant],
( (
tile.pos[0] * self.tile_size - offset[0], tile.pos[0] * self.tile_size - offset[0],
tile.pos[1] * self.tile_size - offset[1]) tile.pos[1] * self.tile_size - offset[1],
),
) )