On rend la partie équitable en mettant le bot a la même vitesse que le joueur #10
|
@ -1,204 +1,204 @@
|
|||
"""
|
||||
Plugin implémentant une physique exacte pour des collisions AABB.
|
||||
"""
|
||||
|
||||
from typing import Callable
|
||||
from engine import GlobalPlugin
|
||||
from engine.ecs import Entity, World
|
||||
from engine.math import Vec2
|
||||
from plugins.render import Origin, Position, Scale
|
||||
from plugins.timing import Delta
|
||||
|
||||
|
||||
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.
|
||||
"""
|
||||
|
||||
|
||||
class Velocity(Vec2):
|
||||
"""
|
||||
Composant donnant la vélocité d'un objet.
|
||||
"""
|
||||
|
||||
|
||||
class CollisionHandler:
|
||||
"""
|
||||
Composant permettant de traiter les collisions.
|
||||
"""
|
||||
|
||||
def __init__(self, callback: Callable[[Entity, Entity], bool]):
|
||||
self.callback = callback
|
||||
|
||||
|
||||
class AABB:
|
||||
"""
|
||||
Définit une boite.
|
||||
"""
|
||||
|
||||
def __init__(self, min: Vec2, max: Vec2, entity: Entity):
|
||||
self.min = min
|
||||
self.max = max
|
||||
self.entity = entity
|
||||
|
||||
@staticmethod
|
||||
def from_entity(entity: Entity):
|
||||
min = entity[Position] - entity[Origin] * entity[Scale]
|
||||
return AABB(min, min + entity[Scale], entity)
|
||||
|
||||
def entity_position(self, entity: Entity):
|
||||
scale = self.max - self.min
|
||||
entity[Position] = self.min + entity[Origin] * scale
|
||||
entity[Scale] = scale
|
||||
|
||||
def __contains__(self, point: Vec2):
|
||||
return (
|
||||
self.min.x <= point.x <= self.max.x and self.min.y <= point.y <= self.max.y
|
||||
)
|
||||
|
||||
def move(self, movement: Vec2):
|
||||
self.min += movement
|
||||
self.max += movement
|
||||
|
||||
def collide(self, other: "AABB"):
|
||||
return (
|
||||
self.min.x < other.max.x
|
||||
and self.max.x > other.min.x
|
||||
and self.min.y < other.max.y
|
||||
and self.max.y > other.min.y
|
||||
)
|
||||
|
||||
|
||||
def line_to_line(sa: Vec2, ea: Vec2, sb: Vec2, eb: Vec2):
|
||||
"""
|
||||
Renvoie la collision entre deux lignes.
|
||||
"""
|
||||
if sa.x == ea.x:
|
||||
sa.x += 0.0001
|
||||
if sb.x == eb.x:
|
||||
sb.x += 0.0001
|
||||
if sa.y == ea.y:
|
||||
sa.y += 0.0001
|
||||
if sb.y == eb.y:
|
||||
sb.y += 0.0001
|
||||
|
||||
divisor = (eb.y - sb.y) * (ea.x - sa.x) - (eb.x - sb.x) * (ea.y - sa.y)
|
||||
if divisor == 0:
|
||||
uA = 0
|
||||
else:
|
||||
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)
|
||||
if divisor == 0:
|
||||
uB = 0
|
||||
else:
|
||||
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:
|
||||
return (
|
||||
Vec2((uA * (ea.x - sa.x)), (uA * (ea.y - sa.y))).length / (ea - sa).length
|
||||
)
|
||||
return 1.0
|
||||
|
||||
|
||||
def line_to_aabb(start: Vec2, end: Vec2, aabb: 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))
|
||||
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))
|
||||
top = line_to_line(start, end, Vec2(aabb.min.x, aabb.max.y), aabb.max)
|
||||
t = min([left, right, bottom, top])
|
||||
if t == left:
|
||||
normal = Vec2(-1, 0)
|
||||
elif t == right:
|
||||
normal = Vec2(1, 0)
|
||||
elif t == bottom:
|
||||
normal = Vec2(0, -1)
|
||||
elif t == top:
|
||||
normal = Vec2(0, 1)
|
||||
else:
|
||||
normal = Vec2(0, 0)
|
||||
return t, normal
|
||||
|
||||
|
||||
def aabb_to_aabb(moving: AABB, static: AABB, movement: Vec2):
|
||||
"""
|
||||
Renvoie la collision entre deux AABB.
|
||||
"""
|
||||
if moving.collide(static):
|
||||
return 1.0, Vec2(0, 0)
|
||||
size = (moving.max - moving.min) / 2
|
||||
static = AABB(static.min - size, static.max + size, static.entity)
|
||||
start_pos = moving.min + size
|
||||
return line_to_aabb(start_pos, start_pos + movement, static)
|
||||
|
||||
|
||||
def aabb_to_aabbs(moving: AABB, statics: list[AABB], movement: Vec2):
|
||||
"""
|
||||
Renvoie la collision entre deux AABB.
|
||||
"""
|
||||
t = 1.0
|
||||
normal = Vec2(0, 0)
|
||||
entity = None
|
||||
for static in statics:
|
||||
if static.entity == moving.entity:
|
||||
continue
|
||||
result = aabb_to_aabb(moving, static, movement)
|
||||
if result[0] < t:
|
||||
t = result[0]
|
||||
normal = result[1]
|
||||
entity = static.entity
|
||||
return t, normal, entity
|
||||
|
||||
|
||||
def move_entity(entity: Entity, movement: Vec2, disable_callback: bool = False):
|
||||
world = entity.world
|
||||
aabb = AABB.from_entity(entity)
|
||||
others = [
|
||||
AABB.from_entity(other) for other in world.query(Solid, Position, Scale, Origin)
|
||||
]
|
||||
counter = 0
|
||||
while movement.length > 0.0001 and counter < 50:
|
||||
t, normal, obstacle = aabb_to_aabbs(aabb, others, movement)
|
||||
if t == 1.0:
|
||||
step = movement
|
||||
else:
|
||||
step = movement * max(t - 0.000001, 0)
|
||||
aabb.move(step)
|
||||
aabb.entity_position(entity)
|
||||
movement -= step
|
||||
if normal.x != 0:
|
||||
movement.x *= -1
|
||||
entity[Velocity].x *= -1
|
||||
if normal.y != 0:
|
||||
movement.y *= -1
|
||||
entity[Velocity].y *= -1
|
||||
movement /= entity[Velocity]
|
||||
if obstacle is not None and not disable_callback:
|
||||
if not entity.get(
|
||||
CollisionHandler, CollisionHandler(lambda e, o: True)
|
||||
).callback(entity, obstacle):
|
||||
break
|
||||
if not obstacle.get(
|
||||
CollisionHandler, CollisionHandler(lambda e, o: True)
|
||||
).callback(obstacle, entity):
|
||||
break
|
||||
movement *= entity[Velocity]
|
||||
counter += 1
|
||||
|
||||
|
||||
def __apply_velocity(world: World):
|
||||
"""
|
||||
Applique la vélocité a toutes les entitées.
|
||||
"""
|
||||
delta = world[Delta]
|
||||
for entity in world.query(Velocity, Position, Scale, Origin):
|
||||
move_entity(entity, entity[Velocity] * delta)
|
||||
|
||||
|
||||
PLUGIN = GlobalPlugin(
|
||||
[],
|
||||
[__apply_velocity],
|
||||
[],
|
||||
[],
|
||||
)
|
||||
"""
|
||||
Plugin implémentant une physique exacte pour des collisions AABB.
|
||||
"""
|
||||
|
||||
from typing import Callable
|
||||
from engine import GlobalPlugin
|
||||
from engine.ecs import Entity, World
|
||||
from engine.math import Vec2
|
||||
from plugins.render import Origin, Position, Scale
|
||||
from plugins.timing import Delta
|
||||
|
||||
|
||||
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.
|
||||
"""
|
||||
|
||||
|
||||
class Velocity(Vec2):
|
||||
"""
|
||||
Composant donnant la vélocité d'un objet.
|
||||
"""
|
||||
|
||||
|
||||
class CollisionHandler:
|
||||
"""
|
||||
Composant permettant de traiter les collisions.
|
||||
"""
|
||||
|
||||
def __init__(self, callback: Callable[[Entity, Entity], bool]):
|
||||
self.callback = callback
|
||||
|
||||
|
||||
class AABB:
|
||||
"""
|
||||
Définit une boite.
|
||||
"""
|
||||
|
||||
def __init__(self, min: Vec2, max: Vec2, entity: Entity):
|
||||
self.min = min
|
||||
self.max = max
|
||||
self.entity = entity
|
||||
|
||||
@staticmethod
|
||||
def from_entity(entity: Entity):
|
||||
min = entity[Position] - entity[Origin] * entity[Scale]
|
||||
return AABB(min, min + entity[Scale], entity)
|
||||
|
||||
def entity_position(self, entity: Entity):
|
||||
scale = self.max - self.min
|
||||
entity[Position] = self.min + entity[Origin] * scale
|
||||
entity[Scale] = scale
|
||||
|
||||
def __contains__(self, point: Vec2):
|
||||
return (
|
||||
self.min.x <= point.x <= self.max.x and self.min.y <= point.y <= self.max.y
|
||||
)
|
||||
|
||||
def move(self, movement: Vec2):
|
||||
self.min += movement
|
||||
self.max += movement
|
||||
|
||||
def collide(self, other: "AABB"):
|
||||
return (
|
||||
self.min.x < other.max.x
|
||||
and self.max.x > other.min.x
|
||||
and self.min.y < other.max.y
|
||||
and self.max.y > other.min.y
|
||||
)
|
||||
|
||||
|
||||
def line_to_line(sa: Vec2, ea: Vec2, sb: Vec2, eb: Vec2):
|
||||
"""
|
||||
Renvoie la collision entre deux lignes.
|
||||
"""
|
||||
if sa.x == ea.x:
|
||||
sa.x += 0.0001
|
||||
if sb.x == eb.x:
|
||||
sb.x += 0.0001
|
||||
if sa.y == ea.y:
|
||||
sa.y += 0.0001
|
||||
if sb.y == eb.y:
|
||||
sb.y += 0.0001
|
||||
|
||||
divisor = (eb.y - sb.y) * (ea.x - sa.x) - (eb.x - sb.x) * (ea.y - sa.y)
|
||||
if divisor == 0:
|
||||
uA = 0
|
||||
else:
|
||||
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)
|
||||
if divisor == 0:
|
||||
uB = 0
|
||||
else:
|
||||
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:
|
||||
return (
|
||||
Vec2((uA * (ea.x - sa.x)), (uA * (ea.y - sa.y))).length / (ea - sa).length
|
||||
)
|
||||
return 1.0
|
||||
|
||||
|
||||
def line_to_aabb(start: Vec2, end: Vec2, aabb: 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))
|
||||
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))
|
||||
top = line_to_line(start, end, Vec2(aabb.min.x, aabb.max.y), aabb.max)
|
||||
t = min([left, right, bottom, top])
|
||||
if t == left:
|
||||
normal = Vec2(-1, 0)
|
||||
elif t == right:
|
||||
normal = Vec2(1, 0)
|
||||
elif t == bottom:
|
||||
normal = Vec2(0, -1)
|
||||
elif t == top:
|
||||
normal = Vec2(0, 1)
|
||||
else:
|
||||
normal = Vec2(0, 0)
|
||||
return t, normal
|
||||
|
||||
|
||||
def aabb_to_aabb(moving: AABB, static: AABB, movement: Vec2):
|
||||
"""
|
||||
Renvoie la collision entre deux AABB.
|
||||
"""
|
||||
if moving.collide(static):
|
||||
return 1.0, Vec2(0, 0)
|
||||
size = (moving.max - moving.min) / 2
|
||||
static = AABB(static.min - size, static.max + size, static.entity)
|
||||
start_pos = moving.min + size
|
||||
return line_to_aabb(start_pos, start_pos + movement, static)
|
||||
|
||||
|
||||
def aabb_to_aabbs(moving: AABB, statics: list[AABB], movement: Vec2):
|
||||
"""
|
||||
Renvoie la collision entre deux AABB.
|
||||
"""
|
||||
t = 1.0
|
||||
normal = Vec2(0, 0)
|
||||
entity = None
|
||||
for static in statics:
|
||||
if static.entity == moving.entity:
|
||||
continue
|
||||
result = aabb_to_aabb(moving, static, movement)
|
||||
if result[0] < t:
|
||||
t = result[0]
|
||||
normal = result[1]
|
||||
entity = static.entity
|
||||
return t, normal, entity
|
||||
|
||||
|
||||
def move_entity(entity: Entity, movement: Vec2, disable_callback: bool = False):
|
||||
world = entity.world
|
||||
aabb = AABB.from_entity(entity)
|
||||
others = [
|
||||
AABB.from_entity(other) for other in world.query(Solid, Position, Scale, Origin)
|
||||
]
|
||||
counter = 0
|
||||
while movement.length > 0.0001 and counter < 50:
|
||||
t, normal, obstacle = aabb_to_aabbs(aabb, others, movement)
|
||||
if t == 1.0:
|
||||
step = movement
|
||||
else:
|
||||
step = movement * max(t - 0.000001, 0)
|
||||
aabb.move(step)
|
||||
aabb.entity_position(entity)
|
||||
movement -= step
|
||||
if normal.x != 0:
|
||||
movement.x *= -1
|
||||
entity[Velocity].x *= -1
|
||||
if normal.y != 0:
|
||||
movement.y *= -1
|
||||
entity[Velocity].y *= -1
|
||||
movement /= entity[Velocity]
|
||||
if obstacle is not None and not disable_callback:
|
||||
if not entity.get(
|
||||
CollisionHandler, CollisionHandler(lambda e, o: True)
|
||||
).callback(entity, obstacle):
|
||||
break
|
||||
if not obstacle.get(
|
||||
CollisionHandler, CollisionHandler(lambda e, o: True)
|
||||
).callback(obstacle, entity):
|
||||
break
|
||||
movement *= entity[Velocity]
|
||||
counter += 1
|
||||
|
||||
|
||||
def __apply_velocity(world: World):
|
||||
"""
|
||||
Applique la vélocité a toutes les entitées.
|
||||
"""
|
||||
delta = world[Delta]
|
||||
for entity in world.query(Velocity, Position, Scale, Origin):
|
||||
move_entity(entity, entity[Velocity] * delta)
|
||||
|
||||
|
||||
PLUGIN = GlobalPlugin(
|
||||
[],
|
||||
[__apply_velocity],
|
||||
[],
|
||||
[],
|
||||
)
|
||||
|
|
Loading…
Reference in a new issue