New collision system
parent
bf0688408e
commit
f2fab23b75
|
@ -12,21 +12,21 @@
|
|||
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,x,x,x,x,x,x,x,x,x,,,,,,,,,,,,,,,,,D,,,
|
||||
#,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
#,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
#,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
#,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
#,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
#,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
#,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,#
|
||||
#,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,#
|
||||
#,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,#
|
||||
#,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,#
|
||||
#,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,#
|
||||
#,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,#
|
||||
#,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,#
|
||||
#,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,#
|
||||
#,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,x,x,x,x,x,x,x,x,x,,,,,,,,,,,,,,,,,D,,,#
|
||||
#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,,,,,,,,,,,,,,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#
|
||||
#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,,,,,,,,,,,,,,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#
|
||||
#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,,,,,,,,,,,,,,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#
|
||||
|
@ -39,5 +39,3 @@
|
|||
#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,,,,,,,,,,,,,,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#
|
||||
#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,,,,,,,,,,,,,,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#
|
||||
#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,,,,,,,,,,,,,,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#
|
||||
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
3,3,id=HEBEL,requires=HEBEL;HEBEL-2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
|
|
|
|
@ -6,7 +6,7 @@ from level.LevelManager import LevelManager
|
|||
from level.elements.LoadedLevel import LoadedLevel
|
||||
from physics.SpriteManager import SpriteManager, DrawLayers
|
||||
from physics.TickData import TickData
|
||||
from physics.controllers.PlayerSprite import PlayerSprite
|
||||
from physics.sprites.PlayerSprite import PlayerSprite
|
||||
from sprite.PositionScale import PositionScale
|
||||
from sprite.SpritesheetManager import SpritesheetManager
|
||||
from sprite.Sprite import Sprite
|
||||
|
@ -47,6 +47,16 @@ if what_to_run == 'level':
|
|||
generated_level = LoadedLevel(sprite_manager, spritesheet_manager)
|
||||
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)
|
||||
|
||||
text_1 = TextLabel("Frame: 0", 2, 110, 50, alignment="left")
|
||||
text_1.position_scale.scale = (0.3, 0.3)
|
||||
sprite_manager.add_ui_element(DrawLayers.UI, text_1)
|
||||
|
||||
ghost_character.debug_label = text_1
|
||||
|
||||
while True:
|
||||
clock.tick(frame_rate)
|
||||
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
class CollisionDirection:
|
||||
LEFT = 0
|
||||
RIGHT = 1
|
||||
TOP = 2
|
||||
BOTTOM = 3
|
||||
DIRECTION_NAMES = ['LEFT', 'RIGHT', 'TOP', 'BOTTOM']
|
||||
|
||||
def __init__(self, direction: int, primary_sprite, secondary_sprite):
|
||||
self.direction = direction
|
||||
self.primary_sprite = primary_sprite
|
||||
self.secondary_sprite = secondary_sprite
|
||||
|
||||
def to_string(self):
|
||||
return 'CollisionDirection.' + CollisionDirection.DIRECTION_NAMES[self.direction]
|
|
@ -1,5 +1,6 @@
|
|||
from typing import Optional
|
||||
|
||||
from physics.CollisionDirection import CollisionDirection
|
||||
from physics.TickData import TickData
|
||||
from sprite.DynamicSprite import DynamicSprite
|
||||
from sprite.Sprite import Sprite
|
||||
|
@ -40,6 +41,8 @@ class PhysicsElementsHandler:
|
|||
# 4.2.3. If it doesn't, move the sprite to the new position
|
||||
|
||||
colliders = [sprite for sprite in sprites if isinstance(sprite, StaticSprite) and sprite.is_collider]
|
||||
for collider in colliders:
|
||||
collider.reset_collides_with()
|
||||
|
||||
dynamic_sprites = [sprite for sprite in sprites if isinstance(sprite, DynamicSprite)]
|
||||
sorted_dynamic_sprites = sorted(dynamic_sprites, key=lambda spr: spr.position_scale.position[1])
|
||||
|
@ -55,9 +58,12 @@ class PhysicsElementsHandler:
|
|||
total_motion = sprite.motion
|
||||
motion_step = ((total_motion[0] * dt) / motion_steps, (total_motion[1] * dt) / motion_steps)
|
||||
|
||||
for i in range(motion_steps):
|
||||
sprite.reset_touches()
|
||||
collides_with_last = None
|
||||
collided = [False, False]
|
||||
sprite.reset_collides_with()
|
||||
|
||||
for i in range(motion_steps):
|
||||
if not collided[0]:
|
||||
sprite.position_scale.position = (
|
||||
sprite.position_scale.position[0] + motion_step[0],
|
||||
sprite.position_scale.position[1]
|
||||
|
@ -71,14 +77,17 @@ class PhysicsElementsHandler:
|
|||
)
|
||||
|
||||
if sprite.motion[0] > 0:
|
||||
sprite.set_touches_right(True)
|
||||
sprite.add_collides_with(CollisionDirection(CollisionDirection.RIGHT, sprite, collides_with))
|
||||
|
||||
if sprite.motion[0] < 0:
|
||||
sprite.set_touches_left(True)
|
||||
sprite.add_collides_with(CollisionDirection(CollisionDirection.LEFT, sprite, collides_with))
|
||||
|
||||
sprite.motion = (0, sprite.motion[1])
|
||||
return collides_with
|
||||
|
||||
collides_with_last = collides_with
|
||||
collided[0] = True
|
||||
|
||||
if not collided[1]:
|
||||
sprite.position_scale.position = (
|
||||
sprite.position_scale.position[0],
|
||||
sprite.position_scale.position[1] + motion_step[1]
|
||||
|
@ -92,19 +101,22 @@ class PhysicsElementsHandler:
|
|||
)
|
||||
|
||||
if sprite.motion[1] > 0:
|
||||
sprite.set_touches_bottom(True)
|
||||
sprite.add_collides_with(CollisionDirection(CollisionDirection.BOTTOM, sprite, collides_with))
|
||||
|
||||
if sprite.motion[1] < 0:
|
||||
sprite.set_touches_top(True)
|
||||
sprite.add_collides_with(CollisionDirection(CollisionDirection.TOP, sprite, collides_with))
|
||||
|
||||
sprite.motion = (sprite.motion[0], 0)
|
||||
return collides_with
|
||||
|
||||
return None
|
||||
collides_with_last = collides_with
|
||||
collided[1] = True
|
||||
|
||||
return collides_with_last
|
||||
|
||||
def check_collides(self, sprite: StaticSprite, colliders: list[StaticSprite]) -> Optional[StaticSprite]:
|
||||
for collider in colliders:
|
||||
if sprite is not collider and sprite.collides_with(collider, TOLERANCE):
|
||||
if sprite is not collider:
|
||||
if sprite.collides_with(collider, TOLERANCE):
|
||||
return collider
|
||||
|
||||
return None
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
from typing import Optional
|
||||
|
||||
import pygame
|
||||
from pygame import Surface
|
||||
|
||||
from physics.PhysicsElementsHandler import PhysicsElementsHandler
|
||||
|
@ -57,5 +56,11 @@ class SpriteManager:
|
|||
return elements
|
||||
|
||||
def collision_detected(self, sprite_a: Sprite, sprite_b: Sprite):
|
||||
# print(f"Collision detected between {sprite_a} and {sprite_b}")
|
||||
pass
|
||||
|
||||
def find_sprite_by_uuid(self, uuid: str) -> Optional[UiElement]:
|
||||
for layer in DrawLayers.DRAW_ORDER:
|
||||
for sprite in self.layers[layer]:
|
||||
if sprite.uuid == uuid:
|
||||
return sprite
|
||||
return None
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
from physics.TickData import TickData
|
||||
from sprite.DynamicSprite import DynamicSprite
|
||||
from sprite.Spritesheet import Spritesheet
|
||||
from ui_elements.KeyManager import KeyManager
|
||||
from ui_elements.TextLabel import TextLabel
|
||||
|
||||
|
||||
class PlayerSprite(DynamicSprite):
|
||||
def __init__(self, spritesheet: Spritesheet):
|
||||
super().__init__(spritesheet)
|
||||
self.jump_time = -1
|
||||
self.allowed_jump_time = 20
|
||||
self.debug_label = TextLabel('', -1, -1)
|
||||
|
||||
def tick(self, tick_data: TickData):
|
||||
super().tick(tick_data)
|
||||
|
||||
if tick_data.key_manager.is_keymap_down(KeyManager.KEY_RIGHT):
|
||||
self.motion = (self.motion[0] + 2, self.motion[1])
|
||||
|
||||
if tick_data.key_manager.is_keymap_down(KeyManager.KEY_LEFT):
|
||||
self.motion = (self.motion[0] - 2, self.motion[1])
|
||||
|
||||
self.debug_label.set_text(f'jump: {self.jump_time}, x: {round(self.motion[0], 2)}, y: {round(self.motion[1], 2)}, touches: {self.touches_bounding}')
|
|
@ -0,0 +1,11 @@
|
|||
from physics.TickData import TickData
|
||||
from sprite.Spritesheet import Spritesheet
|
||||
from sprite.StaticSprite import StaticSprite
|
||||
|
||||
|
||||
class DeathBox(StaticSprite):
|
||||
def __init__(self, spritesheet: Spritesheet):
|
||||
super().__init__(spritesheet)
|
||||
|
||||
def tick(self, tick_data: TickData):
|
||||
super().tick(tick_data)
|
|
@ -0,0 +1,45 @@
|
|||
from physics.CollisionDirection import CollisionDirection
|
||||
from physics.TickData import TickData
|
||||
from sprite.DynamicSprite import DynamicSprite
|
||||
from sprite.Spritesheet import Spritesheet
|
||||
from ui_elements.KeyManager import KeyManager
|
||||
from ui_elements.TextLabel import TextLabel
|
||||
|
||||
|
||||
class PlayerSprite(DynamicSprite):
|
||||
def __init__(self, spritesheet: Spritesheet):
|
||||
super().__init__(spritesheet)
|
||||
self.debug_label = TextLabel('', -1, -1)
|
||||
|
||||
self.jump_time = -1
|
||||
self.allowed_jump_time = 12
|
||||
|
||||
self.acceleration_horizontal = 2
|
||||
|
||||
self.deceleration_horizontal_air = 0.02
|
||||
self.deceleration_horizontal_ground = 0.3
|
||||
self.gravity = 9.81 / 10
|
||||
|
||||
self.max_motion_horizontal_via_input = 5
|
||||
|
||||
def tick(self, tick_data: TickData):
|
||||
super().tick(tick_data)
|
||||
|
||||
if tick_data.key_manager.is_keymap_down(KeyManager.KEY_RIGHT):
|
||||
if self.motion[0] < self.max_motion_horizontal_via_input:
|
||||
self.motion = (self.motion[0] + self.acceleration_horizontal, self.motion[1])
|
||||
|
||||
if tick_data.key_manager.is_keymap_down(KeyManager.KEY_LEFT):
|
||||
if self.motion[0] > -self.max_motion_horizontal_via_input:
|
||||
self.motion = (self.motion[0] - self.acceleration_horizontal, self.motion[1])
|
||||
|
||||
if tick_data.key_manager.is_keymap_down(KeyManager.KEY_UP):
|
||||
if self.jump_time < 0 and self.get_collides_with_direction(CollisionDirection.BOTTOM):
|
||||
self.jump_time = self.allowed_jump_time
|
||||
self.motion = (self.motion[0], self.motion[1] - 7)
|
||||
if self.jump_time >= 0:
|
||||
self.motion = (self.motion[0], self.motion[1] - 0.5)
|
||||
if self.jump_time >= 0:
|
||||
self.jump_time -= 1
|
||||
|
||||
self.debug_label.set_text(f'jump: {self.jump_time}, x: {round(self.motion[0], 2)}, y: {round(self.motion[1], 2)}, touches: {list(map(lambda x: x.to_string(), self.get_collides_with()))}')
|
|
@ -1,4 +1,4 @@
|
|||
from sprite.BoundingBox import BoundingBox
|
||||
from physics.CollisionDirection import CollisionDirection
|
||||
from sprite.Spritesheet import Spritesheet
|
||||
from sprite.StaticSprite import StaticSprite
|
||||
from physics.TickData import TickData
|
||||
|
@ -10,19 +10,18 @@ class DynamicSprite(StaticSprite):
|
|||
|
||||
self.motion = (0, 0)
|
||||
|
||||
self.apply_base_deceleration = True
|
||||
self.deceleration_horizontal_air = 0.02
|
||||
self.deceleration_horizontal_ground = 0.3
|
||||
self.gravity = 9.81 / 10
|
||||
|
||||
# up, right, down, left
|
||||
self.touches_bounding = (False, False, False, False)
|
||||
|
||||
def tick(self, tick_data: TickData):
|
||||
super().tick(tick_data)
|
||||
|
||||
if self.apply_base_deceleration:
|
||||
deceleration_horizontal = 0
|
||||
if abs(self.motion[0]) > 0:
|
||||
if self.touches_bounding[2]:
|
||||
if self.get_collides_with_direction(CollisionDirection.BOTTOM):
|
||||
deceleration_horizontal = self.deceleration_horizontal_ground
|
||||
else:
|
||||
deceleration_horizontal = self.deceleration_horizontal_air
|
||||
|
@ -31,18 +30,3 @@ class DynamicSprite(StaticSprite):
|
|||
self.motion[0] - deceleration_horizontal * self.motion[0] * tick_data.dt,
|
||||
self.motion[1] + self.gravity * tick_data.dt
|
||||
)
|
||||
|
||||
def set_touches_bottom(self, value: bool):
|
||||
self.touches_bounding = (self.touches_bounding[0], self.touches_bounding[1], value, self.touches_bounding[3])
|
||||
|
||||
def set_touches_right(self, value: bool):
|
||||
self.touches_bounding = (self.touches_bounding[0], value, self.touches_bounding[2], self.touches_bounding[3])
|
||||
|
||||
def set_touches_left(self, value: bool):
|
||||
self.touches_bounding = (self.touches_bounding[0], self.touches_bounding[1], self.touches_bounding[2], value)
|
||||
|
||||
def set_touches_top(self, value: bool):
|
||||
self.touches_bounding = (value, self.touches_bounding[1], self.touches_bounding[2], self.touches_bounding[3])
|
||||
|
||||
def reset_touches(self):
|
||||
self.touches_bounding = (False, False, False, False)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
class PositionScale:
|
||||
def __init__(self, position: tuple[float, float] = (0, 0), scale: tuple[float, float] = (1, 1)):
|
||||
self.position = position
|
||||
self.scale = scale
|
||||
self.position: tuple[float, float] = position
|
||||
self.scale: tuple[float, float] = scale
|
||||
|
||||
def apply_scale_to_position(self):
|
||||
return self.position[0] * self.scale[0], self.position[1] * self.scale[1]
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
from typing import Optional
|
||||
|
||||
import pygame
|
||||
|
||||
from physics.CollisionDirection import CollisionDirection
|
||||
from physics.TickData import TickData
|
||||
from sprite.BoundingBox import BoundingBox
|
||||
from sprite.PositionScale import PositionScale
|
||||
|
@ -22,6 +25,22 @@ class Sprite(UiElement):
|
|||
self.image = None
|
||||
|
||||
self.is_collider = True
|
||||
self.collides_with_elements: list[CollisionDirection] = []
|
||||
|
||||
def add_collides_with(self, collision_direction: CollisionDirection):
|
||||
self.collides_with_elements.append(collision_direction)
|
||||
|
||||
def reset_collides_with(self):
|
||||
self.collides_with_elements = []
|
||||
|
||||
def get_collides_with(self) -> list[CollisionDirection]:
|
||||
return self.collides_with_elements
|
||||
|
||||
def get_collides_with_direction(self, direction: int) -> Optional[CollisionDirection]:
|
||||
for collision_direction in self.collides_with_elements:
|
||||
if collision_direction.direction == direction:
|
||||
return collision_direction
|
||||
return None
|
||||
|
||||
def tick(self, tick_data: TickData):
|
||||
animation = self.spritesheet.animations[self.animation_state]
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import abc
|
||||
import uuid
|
||||
from typing import Optional
|
||||
|
||||
import pygame
|
||||
|
@ -16,6 +17,8 @@ class UiElement:
|
|||
self.visible = True
|
||||
self.click_listeners = []
|
||||
|
||||
self.uuid = uuid.uuid4()
|
||||
|
||||
def add_click_listener(self, listener):
|
||||
self.click_listeners.append(listener)
|
||||
|
||||
|
|
Loading…
Reference in New Issue