Abstand und Kollisionsdetektion in Ursina für 3D-Spiele
In Ursina ermöglichen die Funktionen distance(), distance_2d() und distance_xz() die präzise Berechnung von Abständen zwischen Objekten im 3D-Raum. Die Methode intersects() erkennt Kollisionen mithilfe von Collidern. Diese Werkzeuge sind essenziell für Mechaniken wie Erkennung, Gegenstandsaufnahme und künstliche Intelligenz.
Abstandsberechnungsfunktionen
Die Funktion distance() berechnet den euklidischen Abstand im 3D-Raum über alle X-, Y- und Z-Achsen zwischen den Zentren zweier Entity-Objekte:
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"Abstand vom Helden zur Kugel: {dist:.2f}")
app.run()
Formel: √((x₂-x₁)² + (y₂-y₁)² + (z₂-z₁)²). Ideal für umfassende räumliche Analyse.
distance_2d() ignoriert die Z-Achse und arbeitet in der X-Y-Ebene:
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-Abstand vom Helden zur Kugel: {dist:.2f}")
app.run()
Nützlich für UI-Elemente oder Objekte auf gleicher Höhe.
distance_xz() konzentriert sich auf die horizontale X-Z-Ebene und ignoriert die Y-Achse:
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"Horizontaler Abstand vom Helden zur Kugel: {dist:.2f}")
app.run()
Perfekt für AI-Systeme auf dem Boden, bei denen die vertikale Position keine Rolle spielt.
Das intersects()-Kollisionsystem
Die Methode intersects() prüft Kollisionen zwischen Collidern (Box, Sphere, Mesh) über Panda3D’s CollisionTraverser. Beide Objekte müssen einen collider und collision=True besitzen.
Gibt ein HitInfo-Objekt mit folgenden Feldern zurück:
- hit: True/False
- entity: das getroffene Objekt
- point: Koordinaten des Aufpralls
- distance: Abstand zum Aufprallpunkt
Beispiel 1: Wanderkennung
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("Kollision!")
print(f"Mit: {hit_info.entity}")
print(f"Aufprallpunkt: {hit_info.point}")
print(f"Abstand zum Aufprall: {hit_info.distance:.2f}")
app.run()
Beispiel 2: Gegenstand aufheben
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('Gegenstand aufgehoben!')
destroy(pickup)
app.run()
Genauer als distance() bei komplexen Formen.
Beispiel 3: Projektilesystem
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"Geschoss traf {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()
Empfohlen wird raycast() für präzise Treffer anstelle physischer Geschosse.
Praktische Anwendungsfälle
Aufheben per Abstand:
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('Gegenstand aufgehoben!')
player.has_pickup = True
pickup.animate_scale(0, duration=.1)
destroy(pickup, delay=.1)
app.run()
Feind-AI mit Erkennungszon:
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()
Wichtige Erkenntnisse
- Verwende
distance()für vollständige 3D-Analyse,distance_2d()für X-Y-Ebene,distance_xz()für horizontale X-Z-Ebene. intersects()erfordert Collidern auf beiden Objekten und liefert detaillierteHitInfo-Daten.- Für Geschosse bevorzuge
raycast()statt physischer Projektilen. - Kombiniere
distance()mitintersects()für Interaktionszonen und präzise Kollisionsprüfungen. - Verwende immer
ignore=(self,)inintersects(), um Selbstkollisionen zu vermeiden.
— Editorial Team
Noch keine Kommentare.