Added pushable boxes.

Added click event handler
main
Yan Wittmann 2023-03-27 18:37:30 +02:00
parent 6147541a9e
commit 85a34b01d8
19 changed files with 123 additions and 37 deletions

View File

@ -20,7 +20,7 @@
,,,,,,,,,,,,,,,,#,#,#,#,#,#,#,#,#,#,#,#,,,,,,,,,,,,,,,,,,#,#,#,#,#,#,#,#,#,,,,,,,
,,,,,,,,,,,,,,,,#,#,#,#,#,#,#,#,#,,,,,,,,,,,,,,,+,+,+,+,+,+,#,#,#,#,#,#,#,#,#,,,,,,,
,,,,,,,,,,,,,,,,#,#,#,#,#,#,#,#,#,,,,,,,,,,,,,,,,,,#,#,#,#,#,#,#,#,#,#,#,#,,,,,,,
,,,,,,,,,,,,,,,,#,#,#,#,#,#,#,#,#,,,,,,,,,,,,,,,,,,,,,#,#,#,#,#,#,#,#,#,,,,,,,
,,,,,M,,,,,,,,,,,#,#,#,#,#,#,#,#,#,,,,,,,,,,,,,,,,,,,,,#,#,#,#,#,#,#,#,#,,,,,,,
,,,,,,,,,,,,,,,,#,#,#,#,#,#,#,#,#,,,,,,,,,,,,,,,,,,,,,#,#,#,#,#,#,#,#,#,,,,,,,
,,,,,,,,,,,,,,,,#,#,#,#,#,#,#,#,#,,,,,,,,+,+,+,+,+,+,,,,,,,,#,#,#,#,#,#,#,#,#,,,,,,,
,,,,,,,,,,,,,,,,#,#,#,#,#,#,#,#,#,,,,,,,,,,,,,,,,,,,,,#,#,#,#,#,#,#,#,#,,,,,,,
@ -31,14 +31,16 @@
,,,,,,,,,,,,,,,,,,,,,,,#,#,,,,,,,,,,,+,+,+,+,+,+,,,,,#,#,#,#,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,G,,,,,,,,,,,D,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,L,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,P,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,P,,,,,,P,,,,,,,,,M,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,#,#,#,#,#,#,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,#,#,#,#,#,+,+,+,+,+,+,+,+,+,+
#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#
#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#
#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#
#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#
#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
AQ,18,requires_xor=lever-1;button-1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
AU,32,requires_and=lever-1;button-1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
AJ,33,id=lever-1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
Q,34,id=button-1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
F,23,size=7,,weight=0.5,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
1
20 # # # # # # # # # # # # # # # # # # # # #
21 # # # # # # # # # + + + + + + # # # # # # # # #
22 # # # # # # # # # # # # # # # # # # # # #
23 M # # # # # # # # # # # # # # # # # #
24 # # # # # # # # # # # # # # # # # #
25 # # # # # # # # # + + + + + + # # # # # # # # #
26 # # # # # # # # # # # # # # # # # #
31 # # + + + + + + # # # #
32 G D
33 L
34 P P M
35 + + + + + + + + + + + + + + + + + + # # # # # # + + + + + + + + + + + + + + + + + + + + + + # # # # # + + + + + + + + + +
36 # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
37 # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
38 # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
39 # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
40 # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
41
42 AQ 18 requires_xor=lever-1;button-1
43 AU 32 requires_and=lever-1;button-1
44 AJ 33 id=lever-1
45 Q 34 id=button-1
46 F 23 size=7 weight=0.5

Binary file not shown.

After

Width:  |  Height:  |  Size: 536 B

View File

@ -340,6 +340,19 @@
}
]
},
{
"id": "movable_box",
"subsheets": [
{
"id": "1",
"delays": [
1
],
"width": 12,
"height": 12
}
]
},
{
"id": "test_1",
"subsheets": [

View File

@ -34,7 +34,7 @@ class Level:
continue
split_item = line[i].split('=')
if split_item[0] in ['id', 'emitter_state', 'debug', 'active_state']:
if split_item[0] in ['id', 'emitter_state', 'debug', 'active_state', 'size', 'weight']:
tile[split_item[0]] = split_item[1]
elif split_item[0] in ['requires', 'requires_and', 'requires_or', 'requires_xor']:
tile[split_item[0]] = split_item[1].split(';')

View File

@ -10,7 +10,6 @@ class LevelElement:
def __init__(self, tile: dict, loaded_level):
self.tile = tile
self.loaded_level = loaded_level
self.sprites = []
if 'id' in tile:
self.id = tile['id']
@ -20,7 +19,3 @@ class LevelElement:
@abstractmethod
def load(self, sprite_manager: SpriteManager, spritesheet_manager: SpritesheetManager, level: Level):
pass
def destroy(self, sprite_manager: SpriteManager):
for sprite in self.sprites:
sprite_manager.remove_ui_element(sprite)

View File

@ -1,12 +1,13 @@
from typing import Optional
from level.Level import Level
from level.elements.ButtonInputLevelElement import ButtonInputLevelElement
from level.elements.GateReceiverLevelElement import GateReceiverLevelElement
from level.elements.dynamic.PushableBoxLevelElement import PushableBoxLevelElement
from level.elements.static.ButtonInputLevelElement import ButtonInputLevelElement
from level.elements.static.GateReceiverLevelElement import GateReceiverLevelElement
from level.elements.LevelElement import LevelElement
from level.elements.LeverInputLevelElement import LeverInputLevelElement
from level.elements.SimpleBlockLevelElement import SimpleBlockLevelElement
from level.elements.UnknownTileLevelElement import UnknownTileLevelElement
from level.elements.static.LeverInputLevelElement import LeverInputLevelElement
from level.elements.static.SimpleBlockLevelElement import SimpleBlockLevelElement
from level.elements.static.UnknownTileLevelElement import UnknownTileLevelElement
from physics import ConstantsParser
from physics.SpriteManager import SpriteManager, DrawLayers
from sprite.SpritesheetManager import SpritesheetManager
@ -24,6 +25,8 @@ TILES = {
'L': LeverInputLevelElement,
'G': GateReceiverLevelElement,
'M': PushableBoxLevelElement,
}
@ -48,8 +51,6 @@ class LoadedLevel:
if name in TILES:
element = TILES[name](tile, self)
if isinstance(element, LeverInputLevelElement):
print(f"Found lever at {position}")
elif not name == '':
print(f"Unknown tile at {position}: {name}")
element = UnknownTileLevelElement(tile, self)

View File

@ -0,0 +1,10 @@
from abc import ABC
from level.elements.LevelElement import LevelElement
from sprite.DynamicSprite import DynamicSprite
class DynamicLevelElement(DynamicSprite, LevelElement, ABC):
def __init__(self, tile: dict, loaded_level):
DynamicSprite.__init__(self)
LevelElement.__init__(self, tile, loaded_level)

View File

@ -0,0 +1,18 @@
from level.Level import Level
from level.elements.dynamic.PushableLevelElement import PushableLevelElement
from physics.SpriteManager import SpriteManager
from sprite.SpritesheetManager import SpritesheetManager
class PushableBoxLevelElement(PushableLevelElement):
def __init__(self, tile: dict, loaded_level):
super().__init__(tile, loaded_level)
if 'size' in self.tile:
self.position_scale.scale = (float(self.tile['size']),
float(self.tile['size']))
def load(self, sprite_manager: SpriteManager, spritesheet_manager: SpritesheetManager, level: Level):
self.spritesheet = spritesheet_manager.get_sheet('movable_box')
self.set_animation_state('1')
self.position_scale.position = self.tile['position']

View File

@ -0,0 +1,28 @@
from abc import ABC
from level.elements.dynamic.DynamicLevelElement import DynamicLevelElement
from physics.CollisionDirection import CollisionDirection
from physics.TickData import TickData
class PushableLevelElement(DynamicLevelElement, ABC):
def __init__(self, tile: dict, loaded_level):
super().__init__(tile, loaded_level)
if 'weight' in self.tile:
self.weight = float(self.tile['weight'])
else:
self.weight = 1.0
def tick(self, tick_data: TickData):
super().tick(tick_data)
collides_element = self.get_collides_with_direction(CollisionDirection.LEFT)
if collides_element:
self.motion = (self.motion[0] + self.weight, self.motion[1])
collides_element = self.get_collides_with_direction(CollisionDirection.RIGHT)
if collides_element:
self.motion = (self.motion[0] - self.weight, self.motion[1])

View File

@ -1,5 +1,5 @@
from level.Level import Level
from level.elements.InputLevelElement import InputLevelElement
from level.elements.static.InputLevelElement import InputLevelElement
from physics.CollisionDirection import CollisionDirection
from physics.SpriteManager import SpriteManager
from physics.TickData import TickData

View File

@ -1,5 +1,5 @@
from level.Level import Level
from level.elements.ReceiverLevelElement import ReceiverLevelElement
from level.elements.static.ReceiverLevelElement import ReceiverLevelElement
from physics.SpriteManager import SpriteManager
from sprite.SpritesheetManager import SpritesheetManager
@ -13,7 +13,6 @@ class GateReceiverLevelElement(ReceiverLevelElement):
self.set_animation_state('open' if self.active_state else 'close')
self.position_scale.position = self.tile['position']
def set_active(self, active: bool):
super().set_active(active)
self.set_animation_state('open' if self.active_state else 'close')

View File

@ -1,7 +1,7 @@
from abc import ABC
from level.elements.StaticLevelElement import StaticLevelElement
from level.elements.static.StaticLevelElement import StaticLevelElement
class InputLevelElement(StaticLevelElement, ABC):

View File

@ -1,5 +1,5 @@
from level.Level import Level
from level.elements.InputLevelElement import InputLevelElement
from level.elements.static.InputLevelElement import InputLevelElement
from physics.CollisionDirection import CollisionDirection
from physics.SpriteManager import SpriteManager
from physics.TickData import TickData

View File

@ -1,6 +1,6 @@
from abc import ABC
from level.elements.StaticLevelElement import StaticLevelElement
from level.elements.static.StaticLevelElement import StaticLevelElement
from physics.TickData import TickData
@ -18,13 +18,17 @@ class ReceiverLevelElement(StaticLevelElement, ABC):
self.requires['xor'] = tile['requires_xor']
self.active_state = False
if 'active_state' in tile:
self.set_active(tile['active_state'] == 'True')
self.active_state_invert = tile['active_state'] == 'True'
else:
self.set_active(False)
self.active_state_invert = False
def set_active(self, active: bool):
self.active_state = active
if self.active_state_invert:
self.active_state = not active
else:
self.active_state = active
def tick(self, tick_data: TickData):
super().tick(tick_data)
@ -48,7 +52,6 @@ class ReceiverLevelElement(StaticLevelElement, ABC):
break
if 'xor' in self.requires:
print(self.requires['xor'])
for requirement in self.requires['xor']:
element = self.loaded_level.get_element_by_id(requirement)
if element and element.emitter_state:

View File

@ -1,7 +1,7 @@
import random
from level.Level import Level
from level.elements.StaticLevelElement import StaticLevelElement
from level.elements.static.StaticLevelElement import StaticLevelElement
from physics.SpriteManager import SpriteManager
from sprite.SpritesheetManager import SpritesheetManager

View File

@ -1,4 +1,3 @@
import uuid
from abc import ABC
from level.elements.LevelElement import LevelElement

View File

@ -1,5 +1,5 @@
from level.Level import Level
from level.elements.StaticLevelElement import StaticLevelElement
from level.elements.static.StaticLevelElement import StaticLevelElement
from physics.SpriteManager import SpriteManager
from sprite.SpritesheetManager import SpritesheetManager

View File

@ -12,6 +12,7 @@ from sprite.PositionScale import PositionScale
from sprite.SpritesheetManager import SpritesheetManager
from sprite.Sprite import Sprite
from sprite.StaticSprite import StaticSprite
from ui_elements import CoordinateTransform
from ui_elements.ClickEvent import ClickEvent
from ui_elements.KeyManager import KeyManager
from ui_elements.LevelMenu import LevelMenu
@ -66,6 +67,12 @@ if what_to_run == 'menu':
key_manager.update_key_events(pygame_events)
click_events: list[ClickEvent] = ClickEvent.create_events(pygame_events, screen_transform)
for event in click_events:
for layer in sprite_manager.layers:
for sprite in sprite_manager.layers[layer]:
if sprite.get_bounding_box().contains_point(event.screen_position):
sprite.click(event)
for event in pygame_events:
if event.type == pygame.QUIT:
pygame.quit()
@ -97,7 +104,7 @@ elif what_to_run == 'level':
parsed_levels_manager.load_from_config('data/levels/levels.json')
generated_level = LoadedLevel(sprite_manager, spritesheet_manager)
generated_level.load_level(parsed_levels_manager.levels[1])
generated_level.load_level(parsed_levels_manager.levels[0])
ghost_character = PlayerSprite(spritesheet_manager.get_sheet("ghost_character"))
ghost_character.position_scale = PositionScale((90, 50), (1, 1))
sprite_manager.add_ui_element(DrawLayers.OBJECTS, ghost_character)

View File

@ -4,6 +4,7 @@ from typing import Optional
from physics.CollisionDirection import CollisionDirection
from physics.TickData import TickData
from sprite.DynamicSprite import DynamicSprite
from sprite.PositionScale import PositionScale
from sprite.Sprite import Sprite
from sprite.StaticSprite import StaticSprite
from ui_elements.UiElement import UiElement
@ -26,6 +27,8 @@ class PhysicsElementsHandler:
sprites = []
for layer in layers:
for sprite in layers[layer]:
# if str(type(sprite)) == "<class 'level.elements.dynamic.PushableBoxLevelElement.PushableBoxLevelElement'>":
# print(f"Found pushable box")
sprite.tick(tick_data)
if isinstance(sprite, Sprite):
sprites.append(sprite)
@ -51,15 +54,15 @@ class PhysicsElementsHandler:
sorted_dynamic_sprites = sorted(dynamic_sprites, key=lambda spr: spr.position_scale.position[1])
for sprite in sorted_dynamic_sprites:
collides_with = self.attempt_move(tick_data.dt, sprite, colliders, MOTION_STEPS)
collides_with = self.attempt_move(tick_data, sprite, colliders, MOTION_STEPS)
if collides_with is not None:
for callback in self.collision_callbacks:
callback(sprite, collides_with)
def attempt_move(self, dt: float, sprite: DynamicSprite, colliders: list[StaticSprite],
def attempt_move(self, tick_data: TickData, sprite: DynamicSprite, colliders: list[StaticSprite],
motion_steps: int) -> Optional[StaticSprite]:
total_motion = sprite.motion
motion_step = ((total_motion[0] * dt) / motion_steps, (total_motion[1] * dt) / motion_steps)
motion_step = ((total_motion[0] * tick_data.dt) / motion_steps, (total_motion[1] * tick_data.dt) / motion_steps)
collides_with_last = None
collided = [False, False]
@ -73,7 +76,7 @@ class PhysicsElementsHandler:
# print('Elements: ', list(filter(lambda spr: str(type(spr)) == "<class 'level.elements.LeverInputLevelElement.LeverInputLevelElement'>", colliders)))
collides_with = self.check_collides(sprite, colliders)
collides_with = self.check_collides(sprite, colliders, tick_data.screen_transform)
for collider in collides_with:
if collider is not None:
if sprite.is_collider and collider.is_collider:
@ -117,7 +120,7 @@ class PhysicsElementsHandler:
sprite.position_scale.position[1] + motion_step[1]
)
collides_with = self.check_collides(sprite, colliders)
collides_with = self.check_collides(sprite, colliders, tick_data.screen_transform)
for collider in collides_with:
if collider is not None:
if sprite.is_collider and collider.is_collider:
@ -157,13 +160,14 @@ class PhysicsElementsHandler:
return collides_with_last
def check_collides(self, sprite: StaticSprite, colliders: list[StaticSprite]) -> list[StaticSprite]:
def check_collides(self, sprite: StaticSprite, colliders: list[StaticSprite], screen_transform: PositionScale) -> list[StaticSprite]:
collides_with = []
for collider in colliders:
if sprite is not collider:
distance = self.calculate_basic_distance(sprite, collider)
if distance > 50:
if distance > max(self.get_sprite_size_for_distance(sprite, screen_transform),
self.get_sprite_size_for_distance(collider, screen_transform)):
continue
if sprite.collides_with(collider, TOLERANCE):
collides_with.append(collider)
@ -175,3 +179,10 @@ class PhysicsElementsHandler:
def calculate_basic_distance(self, sprite1: StaticSprite, sprite2: StaticSprite) -> float:
return math.sqrt((sprite1.position_scale.position[0] - sprite2.position_scale.position[0]) ** 2 +
(sprite1.position_scale.position[1] - sprite2.position_scale.position[1]) ** 2)
def get_sprite_size_for_distance(self, ui_element: UiElement, screen_transform: PositionScale) -> int:
image = ui_element.render_sprite_image()
if image is None:
return 0
return max(image.get_width() * ui_element.position_scale.scale[0] * screen_transform.scale[0],
image.get_height() * ui_element.position_scale.scale[1] * screen_transform.scale[1])