On rend la partie équitable en mettant le bot a la même vitesse que le joueur #10

Merged
CoCo_Sol merged 3 commits from fair-game into main 2024-01-07 09:45:57 +00:00
Showing only changes of commit dc4dc2516e - Show all commits

View file

@ -1,204 +1,204 @@
""" """
Plugin implémentant une physique exacte pour des collisions AABB. Plugin implémentant une physique exacte pour des collisions AABB.
""" """
from typing import Callable from typing import Callable
from engine import GlobalPlugin from engine import GlobalPlugin
from engine.ecs import Entity, World from engine.ecs import Entity, World
from engine.math import Vec2 from engine.math import Vec2
from plugins.render import Origin, Position, Scale from plugins.render import Origin, Position, Scale
from plugins.timing import Delta from plugins.timing import Delta
class Solid: class Solid:
""" """
Composant représentant un objet (de préférence imobille pour que la simulation soit prédictible) qui ne laisse pas passer les objets dynamiques. Composant représentant un objet (de préférence imobille pour que la simulation soit prédictible) qui ne laisse pas passer les objets dynamiques.
""" """
class Velocity(Vec2): class Velocity(Vec2):
""" """
Composant donnant la vélocité d'un objet. Composant donnant la vélocité d'un objet.
""" """
class CollisionHandler: class CollisionHandler:
""" """
Composant permettant de traiter les collisions. Composant permettant de traiter les collisions.
""" """
def __init__(self, callback: Callable[[Entity, Entity], bool]): def __init__(self, callback: Callable[[Entity, Entity], bool]):
self.callback = callback self.callback = callback
class AABB: class AABB:
""" """
Définit une boite. Définit une boite.
""" """
def __init__(self, min: Vec2, max: Vec2, entity: Entity): def __init__(self, min: Vec2, max: Vec2, entity: Entity):
self.min = min self.min = min
self.max = max self.max = max
self.entity = entity self.entity = entity
@staticmethod @staticmethod
def from_entity(entity: Entity): def from_entity(entity: Entity):
min = entity[Position] - entity[Origin] * entity[Scale] min = entity[Position] - entity[Origin] * entity[Scale]
return AABB(min, min + entity[Scale], entity) return AABB(min, min + entity[Scale], entity)
def entity_position(self, entity: Entity): def entity_position(self, entity: Entity):
scale = self.max - self.min scale = self.max - self.min
entity[Position] = self.min + entity[Origin] * scale entity[Position] = self.min + entity[Origin] * scale
entity[Scale] = scale entity[Scale] = scale
def __contains__(self, point: Vec2): def __contains__(self, point: Vec2):
return ( return (
self.min.x <= point.x <= self.max.x and self.min.y <= point.y <= self.max.y self.min.x <= point.x <= self.max.x and self.min.y <= point.y <= self.max.y
) )
def move(self, movement: Vec2): def move(self, movement: Vec2):
self.min += movement self.min += movement
self.max += movement self.max += movement
def collide(self, other: "AABB"): def collide(self, other: "AABB"):
return ( return (
self.min.x < other.max.x self.min.x < other.max.x
and self.max.x > other.min.x and self.max.x > other.min.x
and self.min.y < other.max.y and self.min.y < other.max.y
and self.max.y > other.min.y and self.max.y > other.min.y
) )
def line_to_line(sa: Vec2, ea: Vec2, sb: Vec2, eb: Vec2): def line_to_line(sa: Vec2, ea: Vec2, sb: Vec2, eb: Vec2):
""" """
Renvoie la collision entre deux lignes. Renvoie la collision entre deux lignes.
""" """
if sa.x == ea.x: if sa.x == ea.x:
sa.x += 0.0001 sa.x += 0.0001
if sb.x == eb.x: if sb.x == eb.x:
sb.x += 0.0001 sb.x += 0.0001
if sa.y == ea.y: if sa.y == ea.y:
sa.y += 0.0001 sa.y += 0.0001
if sb.y == eb.y: if sb.y == eb.y:
sb.y += 0.0001 sb.y += 0.0001
divisor = (eb.y - sb.y) * (ea.x - sa.x) - (eb.x - sb.x) * (ea.y - sa.y) divisor = (eb.y - sb.y) * (ea.x - sa.x) - (eb.x - sb.x) * (ea.y - sa.y)
if divisor == 0: if divisor == 0:
uA = 0 uA = 0
else: else:
uA = ((eb.x - sb.x) * (sa.y - sb.y) - (eb.y - sb.y) * (sa.x - sb.x)) / (divisor) uA = ((eb.x - sb.x) * (sa.y - sb.y) - (eb.y - sb.y) * (sa.x - sb.x)) / (divisor)
divisor = (eb.y - sb.y) * (ea.x - sa.x) - (eb.x - sb.x) * (ea.y - sa.y) divisor = (eb.y - sb.y) * (ea.x - sa.x) - (eb.x - sb.x) * (ea.y - sa.y)
if divisor == 0: if divisor == 0:
uB = 0 uB = 0
else: else:
uB = ((ea.x - sa.x) * (sa.y - sb.y) - (ea.y - sa.y) * (sa.x - sb.x)) / (divisor) uB = ((ea.x - sa.x) * (sa.y - sb.y) - (ea.y - sa.y) * (sa.x - sb.x)) / (divisor)
if uA >= 0 and uA <= 1 and uB >= 0 and uB <= 1: if uA >= 0 and uA <= 1 and uB >= 0 and uB <= 1:
return ( return (
Vec2((uA * (ea.x - sa.x)), (uA * (ea.y - sa.y))).length / (ea - sa).length Vec2((uA * (ea.x - sa.x)), (uA * (ea.y - sa.y))).length / (ea - sa).length
) )
return 1.0 return 1.0
def line_to_aabb(start: Vec2, end: Vec2, aabb: AABB): def line_to_aabb(start: Vec2, end: Vec2, aabb: AABB):
""" """
Renvoie la collision entre une ligne et une AABB. Renvoie la collision entre une ligne et une AABB.
""" """
left = line_to_line(start, end, aabb.min, Vec2(aabb.min.x, aabb.max.y)) left = line_to_line(start, end, aabb.min, Vec2(aabb.min.x, aabb.max.y))
right = line_to_line(start, end, Vec2(aabb.max.x, aabb.min.y), aabb.max) right = line_to_line(start, end, Vec2(aabb.max.x, aabb.min.y), aabb.max)
bottom = line_to_line(start, end, aabb.min, Vec2(aabb.max.x, aabb.min.y)) bottom = line_to_line(start, end, aabb.min, Vec2(aabb.max.x, aabb.min.y))
top = line_to_line(start, end, Vec2(aabb.min.x, aabb.max.y), aabb.max) top = line_to_line(start, end, Vec2(aabb.min.x, aabb.max.y), aabb.max)
t = min([left, right, bottom, top]) t = min([left, right, bottom, top])
if t == left: if t == left:
normal = Vec2(-1, 0) normal = Vec2(-1, 0)
elif t == right: elif t == right:
normal = Vec2(1, 0) normal = Vec2(1, 0)
elif t == bottom: elif t == bottom:
normal = Vec2(0, -1) normal = Vec2(0, -1)
elif t == top: elif t == top:
normal = Vec2(0, 1) normal = Vec2(0, 1)
else: else:
normal = Vec2(0, 0) normal = Vec2(0, 0)
return t, normal return t, normal
def aabb_to_aabb(moving: AABB, static: AABB, movement: Vec2): def aabb_to_aabb(moving: AABB, static: AABB, movement: Vec2):
""" """
Renvoie la collision entre deux AABB. Renvoie la collision entre deux AABB.
""" """
if moving.collide(static): if moving.collide(static):
return 1.0, Vec2(0, 0) return 1.0, Vec2(0, 0)
size = (moving.max - moving.min) / 2 size = (moving.max - moving.min) / 2
static = AABB(static.min - size, static.max + size, static.entity) static = AABB(static.min - size, static.max + size, static.entity)
start_pos = moving.min + size start_pos = moving.min + size
return line_to_aabb(start_pos, start_pos + movement, static) return line_to_aabb(start_pos, start_pos + movement, static)
def aabb_to_aabbs(moving: AABB, statics: list[AABB], movement: Vec2): def aabb_to_aabbs(moving: AABB, statics: list[AABB], movement: Vec2):
""" """
Renvoie la collision entre deux AABB. Renvoie la collision entre deux AABB.
""" """
t = 1.0 t = 1.0
normal = Vec2(0, 0) normal = Vec2(0, 0)
entity = None entity = None
for static in statics: for static in statics:
if static.entity == moving.entity: if static.entity == moving.entity:
continue continue
result = aabb_to_aabb(moving, static, movement) result = aabb_to_aabb(moving, static, movement)
if result[0] < t: if result[0] < t:
t = result[0] t = result[0]
normal = result[1] normal = result[1]
entity = static.entity entity = static.entity
return t, normal, entity return t, normal, entity
def move_entity(entity: Entity, movement: Vec2, disable_callback: bool = False): def move_entity(entity: Entity, movement: Vec2, disable_callback: bool = False):
world = entity.world world = entity.world
aabb = AABB.from_entity(entity) aabb = AABB.from_entity(entity)
others = [ others = [
AABB.from_entity(other) for other in world.query(Solid, Position, Scale, Origin) AABB.from_entity(other) for other in world.query(Solid, Position, Scale, Origin)
] ]
counter = 0 counter = 0
while movement.length > 0.0001 and counter < 50: while movement.length > 0.0001 and counter < 50:
t, normal, obstacle = aabb_to_aabbs(aabb, others, movement) t, normal, obstacle = aabb_to_aabbs(aabb, others, movement)
if t == 1.0: if t == 1.0:
step = movement step = movement
else: else:
step = movement * max(t - 0.000001, 0) step = movement * max(t - 0.000001, 0)
aabb.move(step) aabb.move(step)
aabb.entity_position(entity) aabb.entity_position(entity)
movement -= step movement -= step
if normal.x != 0: if normal.x != 0:
movement.x *= -1 movement.x *= -1
entity[Velocity].x *= -1 entity[Velocity].x *= -1
if normal.y != 0: if normal.y != 0:
movement.y *= -1 movement.y *= -1
entity[Velocity].y *= -1 entity[Velocity].y *= -1
movement /= entity[Velocity] movement /= entity[Velocity]
if obstacle is not None and not disable_callback: if obstacle is not None and not disable_callback:
if not entity.get( if not entity.get(
CollisionHandler, CollisionHandler(lambda e, o: True) CollisionHandler, CollisionHandler(lambda e, o: True)
).callback(entity, obstacle): ).callback(entity, obstacle):
break break
if not obstacle.get( if not obstacle.get(
CollisionHandler, CollisionHandler(lambda e, o: True) CollisionHandler, CollisionHandler(lambda e, o: True)
).callback(obstacle, entity): ).callback(obstacle, entity):
break break
movement *= entity[Velocity] movement *= entity[Velocity]
counter += 1 counter += 1
def __apply_velocity(world: World): def __apply_velocity(world: World):
""" """
Applique la vélocité a toutes les entitées. Applique la vélocité a toutes les entitées.
""" """
delta = world[Delta] delta = world[Delta]
for entity in world.query(Velocity, Position, Scale, Origin): for entity in world.query(Velocity, Position, Scale, Origin):
move_entity(entity, entity[Velocity] * delta) move_entity(entity, entity[Velocity] * delta)
PLUGIN = GlobalPlugin( PLUGIN = GlobalPlugin(
[], [],
[__apply_velocity], [__apply_velocity],
[], [],
[], [],
) )