1. Einleitung
Dieses Tutorial behandelt die Implementierung dynamischer Objektformationen in der MiniScript-Umgebung mit XRXplorer. Ziel ist es, die Transformation einer Menge von 3D-Objekten (Würfeln) von einer initialen Gitterstruktur in eine sphärische Anordnung und zurück zu demonstrieren. Dabei werden grundlegende Konzepte der 3D-Grafikprogrammierung, wie Transformationen, Zeitmanagement und mathematische Interpolation, vertieft.
MiniScript ist eine leichtgewichtige Skriptsprache, die für eingebettete Anwendungen konzipiert wurde. Sie bietet eine klare Syntax und grundlegende mathematische Funktionen, die für die Manipulation von Objekten in einer 3D-Umgebung essenziell sind.
2. Grundlagen der 3D-Transformationen
In 3D-Umgebungen werden Objekte durch Transformationen im Raum positioniert, ausgerichtet und skaliert. Jedes Objekt besitzt eine sogenannte “Transformationsmatrix”, die seine Position (Translation), Rotation und Skalierung im Weltraum definiert.
- Position (Translation): Beschreibt den Ort eines Objekts im 3D-Raum (X, Y, Z-Koordinaten).
- Skalierung: Definiert die Größe eines Objekts entlang seiner Achsen.
- Rotation: Beschreibt die Ausrichtung eines Objekts im Raum.
In XRXPlorer werden diese Transformationen durch spezifische Befehle wie moveObject
und scaleObject
direkt angewendet. Das Verständnis, ob diese Befehle im globalen Weltkoordinatensystem oder in einem lokalen, hierarchischen System (Parenting) operieren, ist entscheidend. In dieser Fallstudie gehen wir davon aus, dass moveObject
Objekte direkt im globalen Weltraum positioniert.
3. Das Konzept der Interpolation (Lerp)
Um einen fließenden Übergang zwischen zwei Zuständen (hier: Gitterformation und Kugelformation) zu realisieren, wird die lineare Interpolation (oft als “Lerp” bezeichnet) verwendet. Lerp berechnet einen Zwischenwert zwischen zwei gegebenen Werten basierend auf einem Interpolationsfaktor, der typischerweise zwischen 0 und 1 liegt.
Die Formel für die lineare Interpolation zwischen zwei Werten A und B mit einem Faktor t ist: Value=Acdot(1−t)+Bcdott
- Wenn t=0, ist der Value=A.
- Wenn t=1, ist der Value=B.
- Für 0\<t\<1 liegt der Value proportional zwischen A und B.
Dies kann auf 3D-Vektoren (Positionen) angewendet werden, indem jede Komponente (X, Y, Z) separat interpoliert wird.
4. Mathematische Grundlagen der Kugelverteilung
Um 100 Würfel annähernd gleichmäßig auf der Oberfläche einer Kugel zu verteilen, wird eine Methode verwendet, die auf dem Goldenen Winkel basiert. Dies ist eine effiziente Methode, um Punkte spiralförmig auf einer Kugeloberfläche zu verteilen, ohne komplexe Algorithmen zur exakten Kugelpackung zu benötigen.
Die Formeln für die Position eines Punktes (x, y, z) auf einer Kugel mit Radius R für den i-ten Punkt (von N Punkten) sind:
- Goldener Winkel (phi): phi=picdot(3−sqrt5)
- Y-Koordinate: y=1−(i/(N−1))cdot2 (verteilt y von 1 bis −1)
- Radius im XY-Plane (r_xy): r_xy=sqrt1−y2cdotR
- Winkel um die Y-Achse (alpha): alpha=phicdoti
- X-Koordinate: x=r_xycdotcos(alpha)
- Z-Koordinate: z=r_xycdotsin(alpha)
Diese Berechnungen ermöglichen es, die Würfel in einer ansprechenden sphärischen Anordnung zu platzieren.
5. Analyse des MiniScript-Codes
Betrachten wir nun den MiniScript-Code für die “Würfel-Kugel-Formation-Animation” im Detail:
numCubes = 100
cubeBaseName = "Cube"
cubeScale = 0.25 // Einheitliche Skalierung für alle Würfel
// Gitter-Parameter (Start-/Endposition)
gridSize = 10 // 10x10 Gitter
initialSpacing = 0.5 // Abstand zwischen den Würfeln im Gitter
gridYPos = 0.5 // Feste Y-Position des Gitters
// Kugel-Parameter (Ziel-Formation)
sphereRadius = 4 // Radius der Kugel-Formation
sphereYPos = 2 // Y-Position der Kugel-Formation
// Animations-Parameter
animationDuration = 5 // Dauer einer Animationsphase (Gitter zu Kugel oder Kugel zu Gitter)
animationSpeed = 1 // Geschwindigkeit der gesamten Animation
cubeNames = []
for i in range(1, numCubes)
cubeNames.push(cubeBaseName + i)
end for
// Initiale Skalierung aller Würfel
for i in range(0, numCubes - 1)
currentCubeName = cubeNames[i]
scaleObject currentCubeName, cubeScale, cubeScale, cubeScale
end for
// Hauptanimationsschleife für die Kugel-Formation
while true
currentTime = time
// Phasen-Berechnung: Wechsel zwischen Gitter -> Kugel und Kugel -> Gitter
// Modulo 2 * animationDuration, um die Zeit in Phasen zu unterteilen
phaseTime = currentTime * animationSpeed % (2 * animationDuration)
// Interpolationsfaktor (von 0 bis 1 und zurück)
// Wenn phaseTime < animationDuration: Gitter zu Kugel (factor von 0 nach 1)
// Wenn phaseTime >= animationDuration: Kugel zu Gitter (factor von 1 nach 0)
interpFactor = 0
if phaseTime < animationDuration then
interpFactor = phaseTime / animationDuration
else
interpFactor = 1 - ((phaseTime - animationDuration) / animationDuration)
end if
for i in range(0, numCubes - 1)
currentCubeName = cubeNames[i]
// 1. Gitter-Startposition (unabhängig von Expansion)
row = floor(i / gridSize)
col = i % gridSize
gridPosX = (col - (gridSize - 1) / 2) * initialSpacing
gridPosZ = (row - (gridSize - 1) / 2) * initialSpacing
// 2. Kugel-Zielposition
// Gleichmässige Verteilung der Würfel auf einer Kugeloberfläche
// (Annäherung: Fibonacci-Gitter oder spiralförmige Anordnung auf Kugel)
// Hier eine einfache spiralförmige Anordnung für 100 Punkte auf einer Kugel
goldenAngle = pi * (3 - sqrt(5)) // Goldener Winkel in Radianten
y = 1 - (i / (numCubes - 1)) * 2 // Y von 1 bis -1
radiusAtY = sqrt(1 - y*y) * sphereRadius
angle = goldenAngle * i
spherePosX = radiusAtY * cos(angle)
spherePosY = y * sphereRadius
spherePosZ = radiusAtY * sin(angle)
// Interpolation zwischen Gitter- und Kugelposition
// lerp = linear interpolation
finalPosX = gridPosX * (1 - interpFactor) + spherePosX * interpFactor
finalPosY = gridYPos * (1 - interpFactor) + spherePosY * interpFactor
finalPosZ = gridPosZ * (1 - interpFactor) + spherePosZ * interpFactor
moveObject currentCubeName, finalPosX, finalPosY, finalPosZ
end for
// yield // Dieses Statement bleibt entfernt, wie gewünscht
end while
5.1. Initialisierung und Parameter (Zeilen 1-28)
numCubes
,cubeBaseName
,cubeScale
: Definieren die Anzahl, den Namenspräfix und die Größe der Würfel.gridSize
,initialSpacing
,gridYPos
: Legen die Dimensionen und die Y-Position der initialen Gitterformation fest.gridSize = 10
bedeutet ein 10×10 Gitter für 100 Würfel.initialSpacing
ist der Abstand zwischen den Würfeln im Gitter.
sphereRadius
,sphereYPos
: Bestimmen den Radius und die Y-Position der Ziel-Kugelformation.animationDuration
,animationSpeed
: Steuern die Dauer einer einzelnen Transformationsphase (z.B. Gitter zu Kugel) und die Gesamtgeschwindigkeit der Animation.- Die Schleifen zur Befüllung von
cubeNames
und zur initialenscaleObject
-Anwendung sind Standardprozeduren.
5.2. Hauptanimationsschleife und Phasenberechnung (Zeilen 31-44)
while true
: Eine Endlosschleife, die die Animation kontinuierlich ablaufen lässt.currentTime = time
: Ruft die aktuelle Systemzeit ab.time
ist eine intrinsische MiniScript-Funktion, die die Sekunden seit Programmstart zurückgibt.phaseTime = currentTime * animationSpeed % (2 * animationDuration)
:currentTime * animationSpeed
: Skaliert die Zeit, um die Gesamtgeschwindigkeit der Animation zu steuern.% (2 * animationDuration)
: Der Modulo-Operator sorgt dafür, dassphaseTime
immer im Bereich von0
bis2 * animationDuration
bleibt. Dies teilt die Animation in wiederkehrende Zyklen (z.B. 0-10 Sekunden für einen Zyklus, wennanimationDuration
5 ist).
- Interpolationsfaktor (
interpFactor
):- Dieser Faktor steuert den Übergang zwischen der Gitter- und der Kugelformation.
- Wenn
phaseTime
kleiner alsanimationDuration
ist (erste Hälfte des Zyklus: Gitter zu Kugel), steigtinterpFactor
linear von 0 auf 1. - Wenn
phaseTime
größer oder gleichanimationDuration
ist (zweite Hälfte des Zyklus: Kugel zu Gitter), fälltinterpFactor
linear von 1 auf 0.
5.3. Positionsberechnung für Gitter und Kugel (Zeilen 46-70)
Innerhalb der for
-Schleife für jeden Würfel werden zwei Zielpositionen berechnet:
- Gitter-Position (
gridPosX
,gridPosZ
):row = floor(i / gridSize)
undcol = i % gridSize
: Berechnen die Reihe und Spalte des aktuellen Würfels im Gitter.- Die Formeln
(col - (gridSize - 1) / 2) * initialSpacing
und(row - (gridSize - 1) / 2) * initialSpacing
zentrieren das Gitter um den Ursprung (0,0) der XZ-Ebene.
- Kugel-Position (
spherePosX
,spherePosY
,spherePosZ
):goldenAngle = pi * (3 - sqrt(5))
: Definiert den Goldenen Winkel für die spiralförmige Verteilung.y = 1 - (i / (numCubes - 1)) * 2
: Berechnet die Y-Koordinate des Punktes auf der Kugel, die von 1 bis -1 linear über die Anzahl der Würfel verteilt wird.radiusAtY = sqrt(1 - y*y) * sphereRadius
: Berechnet den Radius des Kreises auf der Y-Ebene, der für die XZ-Position des Punktes auf der Kugel verwendet wird.angle = goldenAngle * i
: Berechnet den Winkel um die Y-Achse für den aktuellen Punkt in der Spirale.spherePosX = radiusAtY * cos(angle)
undspherePosZ = radiusAtY * sin(angle)
: Berechnen die X- und Z-Koordinaten auf dem Kreis bei der jeweiligen Y-Ebene.spherePosY = y * sphereRadius
: Skaliert die Y-Koordinate auf den Kugelradius.
5.4. Interpolation und Anwendung der Transformation (Zeilen 72-78)
finalPosX = gridPosX * (1 - interpFactor) + spherePosX * interpFactor
(und analog für Y und Z):- Dies ist die Anwendung der linearen Interpolation. Die endgültige Position jedes Würfels ist eine Mischung aus seiner Gitter-Position und seiner Kugel-Position, gesteuert durch den
interpFactor
. - Beachten Sie, dass
finalPosY
auch zwischengridYPos
undspherePosY
interpoliert wird, um den Y-Übergang zu steuern.
- Dies ist die Anwendung der linearen Interpolation. Die endgültige Position jedes Würfels ist eine Mischung aus seiner Gitter-Position und seiner Kugel-Position, gesteuert durch den
moveObject currentCubeName, finalPosX, finalPosY, finalPosZ
: Verschiebt den Würfel an die berechnete interpolierte Position.
6. Implementierung und Anpassung
- Vorbereitung in der Galaxie: Stelle sicher, dass 100 Würfel in deiner Szene vorhanden sind und exakt von
Cube1
bisCube100
benannt sind. Ihre initiale Position ist für dieses Skript nicht kritisch, da sie sofort neu positioniert werden. - Skript einfügen: Kopiere den gesamten MiniScript-Code in den dafür vorgesehenen Editor deiner Galaxie.
- Anpassung der Parameter: Experimentiere mit den Werten der Variablen im oberen Bereich des Skripts (
gridSize
,initialSpacing
,sphereRadius
,animationDuration
,animationSpeed
). KleinereanimationDuration
-Werte führen zu schnelleren Übergängen, höhere Werte zu langsameren. - Ausführung: Starte das Skript in deiner Galaxie. Die Würfel sollten sich nun kontinuierlich zwischen der Gitter- und der Kugelformation hin und her bewegen.
7. Fazit
Dieses Tutorial hat demonstriert, wie komplexe dynamische Objektformationen in MiniScript durch die Kombination von mathematischen Prinzipien (Sinus, Kosinus, Goldener Winkel) und dem Konzept der linearen Interpolation realisiert werden können. So kannst du Effekte durch präzise mathematische Berechnungen im globalen Raum erreichen. Das Verständnis der zugrunde liegenden Algorithmen ist entscheidend für die Erstellung überzeugender und dynamischer 3D-Szenen.
8. Download
Beispiel: formation.txt