Výpočet vzdáleností a kolizí v Ursině pro 3D hry
Ve Ursině umožňují funkce distance(), distance_2d() a distance_xz() přesný výpočet vzdáleností mezi objekty ve 3D prostoru. Funkce intersects() zjišťuje kolize prostřednictvím kolizních těles (colliders). Tyto nástroje jsou nezbytné pro mechaniky detekce, sbírání předmětů a umělé inteligence.
distance() vypočítává eukleidovskou vzdálenost ve 3D prostoru podle všech os X, Y, Z mezi středy objektů Entity:
from ursina import *
from ursina.prefabs.first_person_controller import FirstPersonController
app = Ursina()
ground = Entity(model='plane', texture='grass', scale=20, collider='box')
player = FirstPersonController(position=(0, 2, 0))
sphere = Entity(position=(3, 4, 0), model='sphere', color=color.blue)
Sky()
def update():
dist = distance(player, sphere)
print(f"Vzdálenost od hráče k síti: {dist:.2f}")
app.run()
Vzorec: √((x₂-x₁)² + (y₂-y₁)² + (z₂-z₁)²). Ideální pro kompletní prostorovou analýzu.
distance_2d() ignoruje osu Z a pracuje pouze v rovině X-Y:
from ursina import *
from ursina.prefabs.first_person_controller import FirstPersonController
app = Ursina()
ground = Entity(
model='plane',
texture='grass',
scale=20,
collider='box'
)
player = FirstPersonController(position=(0, 2, 0))
sphere = Entity(position=(3, 4, 0), model='sphere', color=color.blue)
Sky()
def update():
dist = distance_2d(player, sphere)
print(f"2D vzdálenost od hráče k síti: {dist:.2f}")
app.run()
Používá se například pro UI prvky nebo objekty na stejné výšce.
distance_xz() se zaměřuje na horizontální rovinu X-Z a ignoruje osu Y:
from ursina import *
from ursina.prefabs.first_person_controller import FirstPersonController
app = Ursina()
ground = Entity(
model='plane',
texture='grass',
scale=20,
collider='box'
)
player = FirstPersonController(position=(0, 2, 0))
sphere = Entity(position=(3, 4, 0), model='sphere', color=color.blue)
Sky()
def update():
dist = distance_xz(player, sphere)
print(f"Horizontální vzdálenost od hráče k síti: {dist:.2f}")
app.run()
Ideální pro terénní umělou inteligenci, kde výška není kritická.
Mechanismus intersects() pro kolize
Funkce intersects() ověřuje průniky kolizních těles (box, sphere, mesh) prostřednictvím Panda3D CollisionTraverser. Vyžaduje, aby oba objekty měly nastavený collider a collision=True.
Vrací strukturu HitInfo s následujícími poli:
- hit: True/False
- entity: kolizní objekt
- point: souřadnice místa nárazu
- distance: vzdálenost do bodu nárazu
Příklad 1: Detekce stěny
from ursina import *
app = Ursina()
player = Entity(model='cube', color=color.orange, collider='box')
wall = Entity(model='cube', position=(5,0,0), scale=(1,5,5), collider='box', color=color.red)
def update():
player.x += 0.1 * time.dt
hit_info = player.intersects()
if hit_info.hit:
print("Kolize!")
print(f"S kým: {hit_info.entity}")
print(f"Bod nárazu: {hit_info.point}")
print(f"Vzdálenost k bodu nárazu: {hit_info.distance:.2f}")
app.run()
Příklad 2: Sbírání předmětu
from ursina import *
from ursina.prefabs.first_person_controller import FirstPersonController
app = Ursina()
ground = Entity(model='plane', texture='grass', scale=10, collider='box')
player = FirstPersonController(model='cube', origin_y=-.5)
pickup = Entity(model='sphere', position=(1,.5,3), collider='box')
def update():
hit_info = player.intersects()
if hit_info.hit and hit_info.entity == pickup:
print('Předmět byl sebrán!')
destroy(pickup)
app.run()
Přesnější než distance() u složitých tvarů.
Příklad 3: Soustava střel
import random
from ursina import *
app = Ursina()
class Bullet(Entity):
def __init__(self, position, direction):
super().__init__(
model='sphere',
scale=.1,
speed=15,
collider='sphere',
position=position,
color=color.yellow,
name='bullet'
)
self.direction = direction.normalized()
def update(self):
self.position += self.direction * time.dt * self.speed
hit_info = self.intersects(ignore=(self,))
if hit_info.hit:
print(f"Střela zasáhla {hit_info.entity.name}!")
destroy(self)
class Enemy(Entity):
def __init__(self):
super().__init__(
model='cube',
color=color.red,
scale=1,
collider='box',
name='enemy'
)
self.x = random.choice([-4, 4])
self.z = random.choice([-4, 4])
for i in range(5):
Enemy()
player = Entity(model='cube', color=color.blue, position=(0, 0, 0))
shoot_timer = 0
def update():
global shoot_timer
shoot_timer += time.dt
if shoot_timer >= 1:
shoot_timer = 0
random_direction = Vec3(
random.uniform(-1, 1),
random.uniform(-1, 1),
random.uniform(-1, 1)
).normalized()
bullet = Bullet(
position=player.position,
direction=random_direction
)
app.run()
Pro přesné zásahy je vhodnější použít raycast().
Praktické scénáře
Sbírání podle vzdálenosti:
from ursina import *
from ursina.prefabs.first_person_controller import FirstPersonController
app = Ursina()
ground = Entity(model='plane', texture='grass', scale=10, collider='box')
player = FirstPersonController(model='cube', origin_y=-.5, color=color.orange, has_pickup=False)
pickup = Entity(model='sphere', position=(1,.5,3))
def update():
if not player.has_pickup and distance(player, pickup) < pickup.scale_x / 2:
print('Předmět byl sebrán!')
player.has_pickup = True
pickup.animate_scale(0, duration=.1)
destroy(pickup, delay=.1)
app.run()
Umělá inteligence nepřítele s oblastí detekce:
from ursina import *
app = Ursina()
player = Entity(model='cube', position=(0, 0, 0), color=color.blue)
ground = Entity(model='plane', scale=20, color=color.dark_gray, y=-1)
class Enemy(Entity):
def __init__(self, **kwargs):
super().__init__(model='cube', scale_y=2, origin_y=-.5, color=color.light_gray, **kwargs)
self.original_color = self.color
self.detection_radius = 10
def update(self):
dist = distance_xz(player.position, self.position)
if dist > self.detection_radius:
self.color = self.original_color
return
self.color = color.red
self.look_at_2d(player.position, 'y')
if dist > 2:
self.position += self.forward * time.dt * 3
enemies = [Enemy(x=x * 5, z=5) for x in range(-2, 3)]
def update():
player.x += held_keys['d'] * time.dt * 5
app.run()
Důležité informace
- Použijte distance() pro plnou 3D vzdálenost, distance_2d() pro rovinu X-Y, distance_xz() pro horizontální rovinu X-Z.
- intersects() vyžaduje kolizní těleso u obou objektů a vrací detailní informace ve formátu HitInfo.
- Pro střely doporučujeme použít raycast() místo fyzikálních projektilů.
- Kombinujte distance() s intersects() pro zóny interakce a přesné kolize.
- Vždy používejte
ignore=(self,)vintersects()pro správné fungování.
— Editorial Team
Zatím žádné komentáře.