Zpět na domů

Vzdálenosti a kolize v Ursina 3D

Průvodce funkcemi distance(), distance_2d(), distance_xz() a intersects() v Ursina. Příklady kódu pro výpočet vzdáleností, detekci kolizí, AI a sběr předmětů v 3D hrách.

Ursina: distance a intersects pro hry
Advertisement 728x90

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.

Google AdInline article slot

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:

Google AdInline article slot
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:

Google AdInline article slot
  • 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,) v intersects() pro správné fungování.

— Editorial Team

Advertisement 728x90

Číst dál