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.
|
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],
|
||||||
[],
|
[],
|
||||||
[],
|
[],
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in a new issue