Motion Capturing

Aus toolbox_interaktion
Wechseln zu: Navigation, Suche
Motion Capture.JPG

 

"Unter Motion Capture, wörtlich Bewegungs-Erfassung, versteht man eine Technik, die es ermöglicht, menschliche Bewegungen so aufzuzeichnen und in ein von Computern lesbares Format umzuwandeln, dass diese die Bewegungen zum einen analysieren und zum anderen auf im Computer generierte 3D-Modelle übertragen können."(Wikipedia)

Seit vielen Jahren wird in der Film- sowie Spielindustrie aufwendig und kostspielig Motion Capturing eingesetzt.Die in der Praxis gebräuchlichen Verfahren sind technisch sehr aufwendig und mit hohen Kosten verbunden.Wir haben uns das Ziel gesetzt genau an dieser Stelle anzusetzen und Motion Capturing Consumer tauglich mit einem Bruchteil der Kosten zu realisieren. Die Verwendete Hardware sind einfache Microsoft Kinects die als Massenware überall käuflich zu erwerben sind. Unser Wunsch war es Bewegungen eines Kampfsportlers mit Hilfe der Kinects aufzunehmen und diese von selbst erstellten 3D Charakteren in einer Spielentwicklung wiederzugeben. Die Aufnahme von Kampfbewegungen stellt aufgrund der Schwierigkeit die höchste Form des Motion Capturing dar.

Motion Capturing
Logo NerdPlayer.png
Projektleiter: Christian Siemer
Motion Capturing: Christian Siemer
Charactermodelling: Sergej Bjakow
Isabell Mönius
Game Development: Christian Neubauer
Maximilian Seyfert


Projektplanung

Kurzbeschreibung

Das Ziel des Projektes war es mit einfachen Mitteln Motion Capture Aufnahmen eines Kampfsportlers zu realisieren. Es war geplant die Bewegungen mit der Microsoft Kinect zu erfassen und über eine Schnittstelle an Blender, als unsere freie 3D-Grafiksoftware, weitergegeben werden. Blender enthält Funktionen, um dreidimensionale Körper zu modellieren, sie zu texturieren, zu animieren und zu rendern. Um eine Interaktion mit den modellierten Charakteren und Ihren Bewegungen zu schaffen haben wir uns entschlossen sie in eine Spiel-Engine einzubinden. Wir haben uns hierbei für Unity3D, eine Spiel-Engine von Unity Technologies entschieden.

Aufgaben und Ziele

  1. Definition der Aufgabenstellung
  2. Aufarbeiten der Theorie
  3. Ausarbeiten der Volere
  4. Einarbeiten in Blender/Brekel/IPI/Unity
  5. Konzept des Spiels
  6. Map Gestaltung in Unity
  7. Synchronisation von zwei Kinects
  8. Erstellung der Charaktere in Blender
  9. Implementieren der Programmfunktionen
  10. Erstellung des Charaktercontrollers
  11. Zwischentest des Spiels
  12. Prüfung der Ergebnisse (Qualitätsmanagement)
  13. Erstellen einer ausreichenden Dokumentation
  14. Erstellen von Präsentationen
  15. Projektendkontrolle, ausgedehnte Tests

Zeitliche Planung

Nach den ersten Projektbesprechungen wurde von mir das in der Abbildung dargestellte Visio Chart für den Projektablauf festgelegt. Es war uns von Anfang an klar, dass es sich dabei nur um einen ersten Grobentwurf handeln konnte, da wesentliche Parameter noch unbekannt waren. In vielen Bereichen gab es einfach keine Vorkenntnisse die zu einer genaueren Planung hätten beitragen können. Es wurden die ersten wichtigen Meilensteine festgelegt, sodass nach diesem Plan jeder der Projektmitarbeiter seine Aufgaben und Termine kannte.

Ergebnis

Das Ergebnis unserer Arbeit ist ein Beat‘em Up - Spiel mit zwei eigens für das Spiel gefertigten Charakteren. Angefangen von der Modellierung in Blender, bis zur Erstellung des Skeletts. Die Bewegungen wurden mit Hilfe von zwei kalibrierten und synchronisierten Kinects aufgenommen. In der Spielumgebung Unity3D wurde die Map gebaut, sowie die Charaktere mit Ihren Bewegungen importiert. Mit Hilfe eines programmierten Charakter- Controllers und einer Eingabe an der Tastatur ist die Interaktion mit dem Spiel möglich. Das Spiel kann ohne Installation per Doppelklick gestartet werden.

Lerneffekt

Das Projekt bietet eine Vielzahl an unterschiedlichen Lerneffekten. Vor allem das Vertiefen in unterschiedliche Bereiche mit denen man zum Beginn keinerlei Erfahrungen hatte ist hier zu nennen. Durch das Aufteilen in Gruppen die sich jeweils auf einen bestimmten Bereich konzentriert haben, konnte die gesamte Gruppe von dem Know-how der Spezialisten profitieren und so in kurzer Zeit komplexes Wissen erlangen. Da vieles von dem was möglich gewesen wäre aufgrund des knappen Zeitplans nicht verwirklicht werden konnte bietet das Projekt noch Ansatzpunkte für spätere Projektarbeiten und Vertiefungen . Ein weiterer wichtiger Lerneffekt war es die richtige Kommunikation zu entwickeln. Zu Beginn wurden viele Fehler aufgrund falscher, bzw. unzureichender Kommunikation verursacht. Jedoch lernt man schnell aus seinen natürlichen Fehlern, und schafft es so sich ständig weiterzuentwickeln.


Systemübersicht

Hardwarekomponenten

Kinect Desc.png

Microsoft Kinect for Xbox360

Technische Daten

Die Kinect verfügt über eine VGA-Kamera für ein normales Bild, einen Tiefensensor zur Erfassung der Entfernung zu einem Objekt und ein Mikrofon-Array.

  • Sehfeld (h/v): 57°/43°
  • Neigungsspielraum: 27°
  • Abstand (min/max): 1,2 m/ 3,5 m
  • Auflösung: 640x480 Px
  • Audio: 4Mikrofone/ Abtastrate: 16kHz --> Richungsbestimmung

Skeletal Tracking System

Die Kinect kann zwei aktive Nutzer erkennen, jedoch allgemein so viele wie in das Sichtfeld passen. Pro aktivem Nutzer kann das Gerät 20 Gelenke im “Skelett Modell“ erstellen. Das Verfahren dazu nennt sich „skeletonization“. Dabei verfolgt die Kinect die Körper der aktiven Nutzer und stellt sie als Serie von Gelenken im Raum dar. So können die Positionen der einzelnen Gelenke einfach abgerufen werden.

Grenzen der Kinect

  • Erkennbare Gelenke: Momentan nur 20 mögliche Gelenke aufnehmbar
  • Auflösung: Zu gering um bei vorgegebenem Abstand Detailbewegungen (Finger) klar feststellen zu können.
  • Sichtfeld: Einschränkung in der Horizontalen
  • Licht: Die Kinect ist sehr empfindlich gegen Fremdlicht

Softwarekomponenten

Blender

Blenderlogo.svg.png

Eigenschaften

  • GPL lizensierte 3D-Software
  • Schnitteditor
  • Spiel-Engine
  • Programmiersprache: Python

Vorteile

  • Freeware

Nachteile

  • Teilweise kompliziert zu handhaben

Brekel

Eigenschaften

Verwendet: PromeSense OpenNI, NITE
Erlaubt: Skeleton Tracking, Stream in Motionbuilder, Export von .bvh-Files

Vorteile

  • Skeleton Tracking
  • Stream in MotionBuilder
  • Export von .bvh-File Möglich

Nachteile

  • Sehr Performancelastig
  • Probleme bei schnellen Bewegungen, Überdeckungen und Verdrehungen

IPI Soft

Eigenschaften

  • Speichert die gesammte Punktwolke und das RGB-Bild in einem AVI-Container
  • Bearbeitung und Berechnung erst im Nachgang der Aufnahme

Vorteile

  • geringer Performanceanspruch
  • realistische Ergebnisse
  • Möglichkeit der Dual-Kinectaufnahme

Nachteile

  • Große Datenmenge
  • Hoher Nachbearbeitungsaufwand/ Zeitaufwand

Unity3D

Official unity logo.png

Eigenschaften

  • Freemium Model
  • Rund 500.000 registrierte Entwickler
  • Programmiersprachen: JavaScript, C#/C++, Boo

Vorteile

  • Komplette Entwicklungsumgebung(Spiele-Engine/Map-Editor/Script-Editor)
  • Plattformunabhängig (Mac OS X/Win)

Nachteile

  • Weniger Leistung als UDK
  • FBX-Dateien von mit über 65.000 Vertices nicht unterstützt

Motion Capturing

Brekel

Auf der Suche nach einem geeigneten Tool zur Realisierung der Motion Capture Aufnahmen sind wir früh auf Brekel, eine Applikation die Microsoft Kinect, PrimeSense’s OpenNI und NITE verwendet, gestoßen. Mit dieser ist ein „skeleton tracking“ in Echtzeit möglich. Die Ergebnisse daraus können entweder Autodesk`s Motion Builder gestreamt oder als BVH File exportiert werden kann. BVH-Files können ohne Probleme zur Weiterverarbeitung in Blender importiert werden. Leider stellte sich heraus, dass die Software sehr Performance lastig ist was sich in den wiedergegebenen Frames wiederspiegelte. Eine für eine zuverlässige Aufnahme nötige Framerate von min. 25 FPS war ohne neueres Equipment kaum Aufrecht zu halten. Zudem mussten wir schnell feststellen dass die Software extreme Probleme sowohl bei schnellen Bewegungen als auch bei Überdeckungen und Verdrehungen hatte. Bei schnellen Bewegungen wurden einige Punkte zu langsam nachgezogen und gingen so verloren.

Bei der kleinsten Verdeckung ging das Skelett an der verdeckten Stelle verloren und wurde auch im Nachhinein nicht wieder aufgenommen. Die Verrenkung blieb, bis die Kinect und die Person neu kalibriert wurden. Deutlich wurden dieses Problem erst vollends durch Export der .bvh-File und der Verknüpfung des aufgenommenen Skeletts mit dem eines Dummy Charakters in Blender. Die Verrenkungen bzw. die Verluste der Punkte zogen sich durch die gesamte Animation, was zu merkwürdigen Verrenkungen führte. Der Nachbearbeitungsaufwand stieg aus diesem Grund erheblich da teilweise Bewegungen neu modelliert werden mussten. Die Einzigen Lösungen für das Problem waren:

  1. Die Bewegungen extrem langsam ausführen
  2. Einfachere Bewegungen ohne Verdeckungen
  3. Eine Zeitintensive Nachbearbeitung.

Da wir aber natürliche Kampfbewegungen erfassen wollten waren diese Lösungen für uns nicht tragbar, daher musste eine neue Lösung her.

IPI

Single-Kinect Aufnahme

Bald darauf sind wir IPI Soft gestoßen ein Tool das einen anderen Ansatz verfolgt. Hier nimmt ein Recorder die gesamte IR-Punktwolke zusammen mit dem RGB Bild auf und speichert sie in einem AVI Container. Auch wenn hierbei ebenfalls extrem hohe Datenmengen(mehre GB in wenigen Sekunden)anfallen, wird der Prozessor weniger beansprucht da keine komplizierte Skelett Berechnung durchgeführt werden muss. Die Aufnahme kann anschließend mit der Software geöffnet und bearbeitet werden.

Bei IPI wird ein Rig mit der Punktwolke der erfassten Person überlagert. Da dies ab dem ersten Frame geschehen muss bietet sich die T-Stellung als Startposition einer Aufnahme an. Das Überlagern erfordert vile zeit, da es von Hand geschehen muss, denn kleinste Unterschiede wirken sich sofort auf das Ergebnis aus. Ist das Skelett in einem Frame exakt mit der Punktwolke überlagert, kann das Tracking beginnen. Hierbei werden unter hohem Zeitaufwand die aufeinanderfolgenden Frames miteinander verglichen und das Skelett automatisch den Veränderungen angepasst. Dies führt zu bedeutend realistischeren Ergebnissen als sie bei Brekel möglich waren.

Dennoch konnte IPI nicht alle Probleme die bei Brekel schon auftraten beseitigen. So stellte sich heraus das das Problem mit den Verdeckungen ein allgemeines Problem der Kinect ist. Diese kann nur einen bestimmten Bereich in die Tiefe gehen. Bei Verdeckungen erreichen keine IR-Punkte diese Stellen und somit gibt es keine Informationen. Da wir uns für Kampfbewegungen entschieden haben, hatten wir häufig dieses Problem. Bei einem Tracking gibt es in diesem Fall keine Punkte die verfolgt werden können bzw. nur vereinzelt und so kommt zu Verrenkungen ähnlich wie schon bei Brekel.

Zwar liese sich dieses Problem beheben indem man genau die Frames ohne Informationen von Hand nachbearbeitet, jedoch wäre dies erneut sehr zeitintensiv.

Dual-Kinect Aufnahme

Eine Idee an diese Daten heranzukommen war es die Bewegungen mit zwei sich gegenüberliegenden Kinects aufzuzeichnen um die sie von Vorne und Hinten erfassen zu können. Die wichtigsten Fragen hierbei waren wie die beiden Kinects miteinander synchronisiert werden können, wie sieht die Kalibrierung aus und stören sich wohlmöglich die beiden Kinects wenn sie sich gegenüber stehen? Da anders als bei der Methode in Echtzeit die Punktwolke in diesem Fall aufgezeichnet und erst in einem weiteren Schritt das Tracking durchgeführt wird sollte es hier möglich sein mit zwei Kinects aufzuzeichnen. Hierzu müsste man die Punktwolken der beiden Aufnahmen verschmelzen und so eine Aufnahme von einer Punktwolke mit einer doppelt so hohen Intensität an Punkten erhalten. Wichtig für dieses Vorhaben ist die genaue Ausrichtung, bzw. die Kalibrierung der beiden Kinects, sodass die aufgezeichneten Bewegungen synchron und genau überlagert abgespielt werden können.

Zu Anfang war das größte Problem beide Kinects an einem Rechner zum Laufen zu bringen. Entweder kam es zu Problemen bei der Installation der Treiber oder es wurde nur eine Kinect erkannt. Dies hing wohl mit mit der Auswahl der USB-Ports zusammen. Ein weiteres Problem, war die Framerate. Eine Kinect lief problemlos auf 30 Frames, die andere schwächelte mit 10-15 Frames und machte aufgrund des Delays eine Aufnahme unbrauchbar. Eine Verlängerung der USB Kabel, die nötig war um die Kinects in einem Abstand von 5-6,5m ausrichten zu können war leider auch nicht unproblematisch da sich die Verlängerung auch auf die Framerate auswirkte. Erst mit einem neuen leistungsstarken Notebook der Hochschule und einem Aufbau mit dem Rechner in der Szene (an der Grenze der Kabellänge) war das Frame Problem gelöst. Nach ersten Versuchen, zeigte sich die richtige Kalibrierung als ein weiteres Problem. Die beiden Aufnahmen der Punktwolken waren zwar synchron jedoch gab es Probleme bei der Überlagerung. Die Punktwolken, waren ungefähr 30cm verschoben was ein späteres Tracking unmöglich machte. Mit einer Laserabmessung der Szene und einer genauen Berechnung der Standorte der beiden Kinects näherten wir uns der genauen Überlagerung an. Mit Hilfe einer Papptafel von ca. 0,7-1.5m Größe und der Tiefeneinfärbung der Kinect war es möglich eine genaue Kalibrierung vorzunehmen. Eine Überlagerung ist vorhanden sobald das Bild beider Kameras die Tafel (ohne Änderung) komplett Grün darstellt. Diese Szene wurde als Kalibrierung gespeichert. Durch ein Plugin ist es möglich die zwei aufgenommenen Videos zu einem zusammenzufügen und eine Punktwolke zu generieren, die die Informationen beider Kinects enthält. Die Aufnahmen zeigen die aufgebaute Szene, und die RGB-Bilder beider Kameras. Die RGB Bilder sind für uns irrelevant. Das Bild zeigt beide Punktwolken ohne Kalibrierung. Durch Filterung können die Störungen entfernt werden. Um einen besseren Fokus auf die Bewegungen zu haben wird der Hintergrund schwarz gefärbt. Bei Ausführung der gespeicherten Kalibrierung werden beiden Punktwolken sowie die zuvor generierte Kalibrierung mit der Tafel überlagert. Nun wird das Rigg, so wie schon bei einer Kinect, der Aufnahme angepasst. Auch wenn die Verwendung der zwei Kinects das Problem der Überdeckung theoretisch gelöst hat war eine spätere Nachbearbeitung nötig, da es wieder zu Verrenkungen gekommen ist. Die Arbeit für die Nachbearbeitung hat sich jedoch drastisch reduziert und viele Bewegungen wie z.B. Drehkicks die zuvor unmöglich gewesen wären konnten sauber dargestellt werden.


Character Modelling

Grober Körperentwurf

Hierfür wird der Einheitswürfel von Blender verwendet, der in der Mitte geteilt und mit dem sogenannten "Mirror Modifier" versehen wird, der ein synchrones Verschieben der Vertexpunkte auf beiden Seiten der gewählten Spiegelachse ermöglicht, wodurch effektiv nur noch eine Seite der Körperhälfte modelliert werden muss. Als Orientierungshilfe habe kann ein "Character Sheet" im Hintergrund platziert werden, auf dem die Frontal- und Seitenansicht eines menschlichen Körpers dargestellt ist. Mit Hilfe eines Splitscreens lässt sich der bearbeitete Würfel zeitgleich von zwei verschiedenen Perspektiven betrachten um so einen besseren Überblick über die Anpassungen des Würfels an dem Character zu erhalten. Nun wird der Würfel der Silhouette des Bildes angepasst und an den nötigen Stellen geteilt, herausgestanzt und ausgeweitet um der vorgelegten Körperform so nahe wie möglich zu kommen. Die ersten Ansätze für spätere Detailarbeiten werden mittels „Loopcuts“ erreicht, die einen umfassenden Schnitt in ein zusammenhängendes quaderförmiges Gebilde innerhalb des Meshes bewerkstelligen. Hierdurch bilden sich durch die Überschneidung von vertikalen und horizontalen Loopcuts neue Vertices, die verschoben werden können, um beispielsweise eine Brustmuskelpartie anzudeuten oder andere Körperrundungen natürlicher zu gestalten.

Anhaftende Elemente und deren Materialverwaltung

Sobald der grobe Körper fertig modelliert ist, kann man charakterisierende Merkmale, wie Kleidung, Haare und Accessoires ausarbeiten, die je nach Aufwand verschiedene Vorgehensmethoden erfordern. Solche am Körper anhaftende Elemente können entweder aus dem bestehenden Körpermesh herausgestanzt („extrude“) werden, oder als komplett eigene Objekte, separat positioniert werden. Letzteres ist jedoch nicht zu empfehlen, denn sobald sich der Körper bewegt, machen sich laufend Clipping-Fehler bemerkbar, da ein separater Kleidungs-Mesh sich nicht synchron zum darunter liegenden Ursprungskörper bewegt. Das Resultat sind Hautelemente, die immer wieder aus der Kleidung empor schauen. Als Lösung für dieses Problem wird der angestrebte Bereich des Körpers nicht dupliziert, sondern direkt eingefärbt, und die gewünschten offenen Stellen durch Extrusion an der Schulter simuliert. Auf dieselbe Art und Weise wird bei Hosenbeinen vorgegangen, wo erst Knie-Abwärts zwei Mesh-Tunnel verlaufen: Die Beine und die umhüllenden Hosenbeinansätze. Dies reduziert die Clipping-Fehler-Rate auf ein Minimum und spart zusätzlich viele Vertices ein.

Separate Subdivision

Um dem modellierten Charakter den letzten Feinschliff zu verpassen, muss dieser selbstverständlich abgerundet werden, da während des Bearbeitungsprozesses im Edit-Mode mit verhältnismäßig groben Vierecken gearbeitet wurde. Dazu wird der „Subdivision Surface“-Modifier angewandt, um den gesamten Mesh durch das gleichmäßige hinzufügen von mehr Vertices abzuglätten. Der „SubSurf“-Modifier teilt ein Viereck in vier Vierecke und approximiert die Vertices dabei an einen runden Körper, was heißt, dass aus harten Kanten immer Weichere werden. Dadurch ist es möglich im „Sculpt“-Mode feine Details herauszuarbeiten. Dieses Feature jedoch sollte äußerst sparsam eingesetzt werden, da pro Aufteilung die Anzahl der Vertices sich beinahe vervierfacht. Mit steigender Polygonzahl wird an der Rechnerperformance gezehrt und ab einer bestimmten Anzahl von Vertices (ca. 65.000) akzeptiert die Game-Engine „Unity“ diese importierten Objekte nicht mehr, da das zu viele Informationen sind, die in Echtzeit während des Spiels berechnet werden müssten.

Sculpting- Das digitale Töpfern

Der Sculpting-Mode ermöglicht feines Modellieren des Charakters mittels einer Vielzahl an Pinseln. Damit ist der Sculpt Mode mit dem Arbeiten mit Ton, Knetgummi und anderen Elementen aus der klassischen Bildhauerei vergleichbar. So können z.B. Erhebungen bzw. Vertiefungen mittels der Brush „Inflate/Deflate“ geformt werden und dadurch Wangenknochen, Mund oder Augenhöhlen geformt werden. Um Stellen abzurunden und zu glätten verwendet man den Brush „Smooth“. Da im Sculpt-Mode nur mit der bestehenden Anzahl an Vertices gearbeitet werden kann, muss der kantige Mesh vorher mit „Subsurf Subdivide“ geglättet werden. Der „gesculpte“ Hight-Poly-Mesh kann nun „gebaked“/gebacken werden.

Baking

Baking-Texturen sind eine Möglichkeit trotz eines Low-Poly-Meshes nicht auf Details verzichten zu müssen. Hier geht es darum, den Charakter an Ort und Stelle zu duplizieren und das Duplikat auf den gewünschten Detailgrad zu bringen. Dieser High-Poly-Mesh wird durch sinnvoll gewählte Nahtstellen („Seams“) markiert und im „UV-Editor“ aufgefächert, damit die komplette Mesh Struktur des High-Poly Meshes als aufgeklappte 2D-Fläche zu sehen ist und wird anschließend mit einer Test-Textur versehen (UV-Test-Grid). Nun werden mittels der „Bake“ Funktion die gesamten Detailinformationen der Mesh Struktur in die Textur hinein gebrannt. Hier wird quasi der Lichteinfall und die entstehenden Schattierungen anhand der Normalen ermittelt und so auf die Textur projiziert. Wenn nun diese Textur an den Low-Poly-Mesh gemappt wird, erscheint dieser trotz deutlich geringerer Polygonanzahl genauso detailreich, wie das High-Polygon Vorbild. Die Unterschiede machen sich erst bei näherer Betrachtung aus bestimmten Perspektiven bemerkbar, wenn Erhöhungen oder Vertiefungen platt erscheinen, doch solche kleinen Qualitätseinbußen sind für das Endprodukt nicht der Rede wert. Doch Vorsicht, der Textur Export von Blender nach Unity funktioniert nicht ohne Weiteres; hier sind auch Mapping-Kenntnisse in der Game-Engine gefragt.

Anpassung des Meshes an das Skelett

Mit fertigen Charaktermodellen kann sich nun der Hauptaufgabe gewidmet werden, nämlich diese Modelle mit voranimierten Skeletten, Motion-Capture Dateien (.bvh) zum Leben zu erwecken. Die grundlegende Technik ist es, das Skelett im drei dimensionalen Raum so in den Körper hinein zu positionieren, dass jeder Knochen unter der „Haut“ liegt. Dabei muss der Mesh an dem Skelett angepasst werden. Hierbei sind erneut weitere Eingriffe in die Mesh Struktur verlangt, da die Polygondichte um ein Gelenk besonders hoch sein muss. Der Ellebogen des Charakters beispielsweise kann nur dann auch zusammen geknickt werden, wenn an dieser Stelle auch entsprechend ein Edge Loop vorhanden ist. Nur ein solcher Loop jedoch führt dazu, dass der Ellebogen beim zusammen ziehen dünn und platt wird, was einen sehr unnatürlich aussehenden Effekt darstellt. Deswegen müssen an der Gelenkregion mehrere, gut verteilte Loopcuts gemacht werden, damit sich der Mesh am Ellebogen beim zusammenziehen natürlicher und gleichmäßiger verteilen kann. Ist der Mesh nun zurecht proportioniert, kann er an das Skelett (oder auch Rig) „geskinnt“ werden.

Deformationen und Weightpainting

Grundlage für ein erfolgreiches und sehenswertes Skinning ist eine saubere „bvh“ Datei, also ein animiertes Skelett ohne falsch voreingestellten Bonerolls. Daher ist in diesem Falle von der Verwendung von „Brekel Kinect“ für die MoCap-Aufnahmen abzuraten. Die exportieren bvh Dateien bestehen aus verhältnismäßig wenigen Knochen und diese sind teilweise um ihre eigene Achse so verdreht, dass sich der Mesh beim Anhaften dieser Verdrehung angepasst hat, was zu teilweise extrem deformierten Figuren führte. Daher sollte stattdessen „ipiSoft“ verwendet, das einerseits die Synchronisation von zwei Microsoft-Kinects ermöglicht für eine präzisere Bewegungsaufnahme und andererseits auch saubere Rigs liefert ohne verdrehte Knochen.

Automatic Weights

Die Zuweisung des Meshes auf das Skelett läuft über „Automatic Weights“, was bedeutet, dass jeder einzelne Vertice dem ihm sich am nächsten befindlichen Bone mit konstanter Distanz folgt. Dieser Automatismus ist aber nicht zu 100% zuverlässig und lässt daher einige Regionen aus, die nachträglich zugewiesen werden müssten. Separate Teilelemente, wie eben Brille, Augäpfel oder auch Augenbrauen können nicht immer von der automatischen Zuweisung vollständig erfasst werden, lassen sich aber auch bequem nachträglich hinzufügen. Hierzu wird der „Weight Paint“-Modus betreten, der es ermöglicht mit Pinsel-Tools auf malerische Art und Weise Vertices bestimmten Vertexgruppen zuzuweisen, die wiederum an den Knochenstruktur orientiert sind. Da die erwähnten, nicht erfassten Elemente ausschließlich dem Kopf angehören, wird die entsprechende Vertexgruppe aus der Liste angewählt, und die frei im Raum schwebenden Gesichtselemente „angemalt“, wodurch sie direkt zurück in das Gesicht projiziert werden.

Game Development

Unter Game Development wird hier verstanden, mit Hilfe von Unity3D die erhaltenen Daten zu einem in Echtzeit gerenderten Spiel, das sich durch Nutzereingaben über die Tastatur steuern lässt, zusammenzusetzen.

Unity 3D

Unity 3D Editor.png

Die Unity 3D ist eine komplette Entwicklungsumgebung, um Spiele oder in Echtzeit gerenderte Szenen zu erstellen. Sie wird von Unity Technologies in San Francisco hergestellt und beinhaltet eine Spiele Engine, einen Map-Editor und einen Script-Editor. Der Script-Editor MonoDevelop funktioniert mit vier unterschiedlichen Programmiersprachen, JavaScript, C#, Boo, und C++. Die Entwicklungsumgebung kann sowohl auf Mac OS X System als auch auf Windows Systemen betrieben werden. Unity 3D basiert auf dem Freemium Model, d.h. als Basisversion ist sie kostenlos und kann für eigene Entwicklungen eingesetzt werden. Die Pro Version beinhaltet weitere Funktionen wie zum Beispiel den Export der Projekte auf Spielekonsolen wie Sony Playsation 3 oder die XBOX 360 von Microsoft. Außerdem ist ein Export nach Adobe Flash möglich, der das Spielen im Webbrowser realisiert. Die Unity 3D erfreut sich immer größerer Beliebtheit und ist mittlerweile die meistgenutzte Engine neben der Unreal Engine. Die Unity hat aktuell rund 500.000 registrierte Entwickler.

Projektaufbau

Generell wird beim Starten eines Spiels zuerst das Main Menu angezeigt, bevor man mit dem Spielen beginnen kann. Um dies zu realisieren, mussten wir uns den Aufbau der Projekte in Unity 3D anschauen. Hier werden alle eigenständigen Spielszenen aufgeführt, die dem Spieler als abgetrennte Bereiche erscheinen, d.h. auch das Menu ist eine Szene. Es wird genauso erstellt wie eine Map und auch genau so gehandhabt. Zu Beginn des Spiels wird also die erste Map aufgerufen, welche im Regelfall das Main Menu ist. Von dort aus wird der Nutzer dann mit Hilfe Scripts in die jeweilige Map oder den einen gespeicherten Spielabschnitt weitergeleitet. In der Projektorganisation von Unity 3D werden alle im Projekt vorhandenen Szenen durchnummeriert wodurch leicht die richtige Szene ausgewählt werden kann.

Map Design

2D Map.png

Konzepte

Es wurden zwei Konzepte des Map Design ausprobiert. Zum eine eine Komplett in 3D gehaltene Map und eine 2D Version, die wie eine Bühnenkulisse eines Theaterstück aufgebaut ist. Hier gibt es verschiedene Ebenen, auch Layers genannt, die hintereinander angeordnet werden. Diese Ebenen werden dann wie eine Leinwand mit Texturen belegt. Betrachtet man die aufeinander liegenden Layer aus der richtigen Perspektive, gewinnt man einen Eindruck der Tiefe. Wenn man die Kamera also seitlich bewegt, bewegt sich der Hintergrund mit unterschiedlicher Geschwindigkeit und das Gesamtkonzept wirkt dadurch realistischer. Hierbei ist aber genau darauf zu achten, wie die Position der Kamera gewählt wird. Unterläuft hier ein Fehler, ist der gewonnene Eindruck wieder zerstört. Es wurde sich für das 2D Map Design entschieden, weil es damit einfacher ist mit geringem Aufwand bessere Ergebnisse zu erzielen und es für das Setting zweckdienlicher war.

Beleuchtung

Auch im 2D Map Design ist es wichtig, die Objekte richtig zu beleuchten, um einen lebendigeren Eindruck für den Betrachter zu realisieren. Hierfür stellt die Unity 3D mehrere Arten von Lichtquellen zur Verfügung. Directional Light, Point Light und Spotlight. Das Directional Light kann als Ersatz für das in der Wirklichkeit vorkommende Sonnenlicht gesehen werden. Es ist wie ein Objekt, das aus einer unendlichen Entfernung Licht in eine bestimmte Richtung sendet. Point Light ist vergleichbar mit einem Rücklicht am Auto. Es beleuchtet den Kunststoff, strahlt aber nicht unbedingt in eine Richtung. Im Gegenzug ist das Spotlight vergleichbar mit dem gleichnamigem Licht in einem Theater, d.h. es wirft einen Lichtkegel in eine bestimmte Richtung.

Texturierung

Bei der Texturierung der Objekte in der Map, ist darauf zu achten das die Texturen richtig gemappt werden d.h. , richtig auf den zuvor erstellten Objekten aufgebracht werden, damit sie nicht verzogen oder Pixelfragmentiert werden. Zudem muss berücksichtigt werden, wie man die Texturen in die Unity 3D importiert. Hier kann man verschiedene Einstellungen vornehmen zum Beispiel die Auflösung der Textur oder die Beschaffenheit.

Audio Design

Ein weiterer Bereich des Map Designs sind die 3D Audio-Quellen, diese sind entsprechend anzuordnen, so dass ein relativ realitätsgetreues Hörgefühl simuliert wird. Das Konzept hierfür funktioniert so, das in der Kamera ein Audio-Listener vorhanden ist, und je nach dem wo dieser sich befindet, werden Geräusche, die auf der Map vorhanden sind, unterschiedlich laut wahrgenommen.

Characterimport

Einbinden der Charaktermodelle

Um die Charaktermodelle in die Unity einbinden zu können ist es nötig die Modelle als .fbx – File aus Blender zu exportieren, die dann in Unity als Asset importiert werden kann. Hierbei ist darauf zu achten, dass sich Blender und Unity in den von ihnen verwendeten Standardgrößeneinheiten unterscheiden. So geht Blender von Zentimetereinheiten aus und Unity von Metern. Da Unity davon ausgeht, dass aber auch alle anderen Programme mit Metern arbeiten wird beim Importieren automatisch ein Faktor von 0.01 auf die Skalierung angewendet. Um das auszugleichen muss man entweder in Blender bereits den Scalefaktor auf 100 erhöhen oder aber nachträglich den Scalefaktor in der Unity zurück auf Null setzen.

Einbinden der Animationen

Um später die Animationen abspielen zu können, die zuvor in Blender auf das Modell gebunden wurden, müssen diese in den importierten Modellen bereitgestellt werden. Dies kann auf zwei Arten geschehen: Entweder werden die Animationen bereits in Blender vorgeschnitten, dann liegen sie schon benannt vor und man hat an diesem Punkt nichts mehr zu tun oder sie stehen nur als eine einzige Animation zur Verfügung. In diesem Fall muss man diesen Animationsblock noch in der Unity schneiden und die benötigten Teilstücke auswählen. Hierfür wählt man im Fenster Projekt das importierte Modell aus scrollt im Inspector nach unten zu „ Animation“ und kontrolliert ob der Haken bei „Split Animation“ gesetzt ist. Anschließend betätigt man im grauen Kasten direkt darunter das „+“- Zeichen ganz rechts in der Tabelle und fügt damit eine neue „idle – Animation“ ein.

Name Start End WrapMode Loop
idle 1 10 default -

Dann ersetzt man „idle“ durch einen Namen der die Animation treffend beschreibt, sodass man sie später aus dem Programmcode heraus aufrufen kann. Bei Start und End stellt man Start- und Endframe der Animation ein. Hier ist es sinnvoll sich vorher anhand der Blenderdatei bzw. aus Blender selbst die Frameintervalle der benötigten Animationen herauszuschreiben. Unter „Wrapmode“ wählt man aus wie das Wiederholungsverhalten der Animation sein soll, z.B. Dauerschleife, einfach, vorwärts-rückwärts. Bei Loop kann man noch mal einen Haken für die Dauerwiederholung setzen und auf dem „-“ kann man alles wieder entfernen. Abschließend bestätigt man das alles mit „Apply“ oder verwirft es wieder mit „Revert“. Wir sind in der endgültigen Fassung zum zweiten Verfahren gewechselt. Dieses macht zwar einiges mehr Arbeit bei der ersten ist es allerdings nicht mehr möglich den Wrapmode für jeden Animationsclip einzeln einzustellen

Positionieren des Modells

Die fertig importierten Modelle lassen sich auf zwei Arten in die aktuelle Szene Einbinden. Entweder zieht man sie erst aus dem Projektfenster in die „Hierarchie“ was sie im Punkt (0,0,0) der Szene einfügt oder man positioniert sie direkt per Drag&Drop an die gewünschte Position.

Vorbereitung für die Steuerung

Um die Modelle anschließend steuern zu können und ihnen die Möglichkeit zu geben Handlungen auszuführen muss den Charaktermodellen zuerst ein Standard - „Character Controller“ hinzugefügt werden. Diesen erhält man indem man in der Hierarchy einen Charakter auswählt und dann in der Menüleiste unter „Component  Physics“ den „Character Controller“ auswählt. Dieser zeigt sich in der Form einer kleinen grünen Kapsel die im Umfeld des Charaktermodells erscheinen sollte. Es empfiehlt sich diese nun an das Modell anzupassen. Wichtig ist es hierbei, dass der Controller mittig in der Figur steckt und wenn möglich die gleiche Höhe wie das Modell besitzt, sodass er das Modell von Kopf bis Fuß durchdringt. Der Sinn dieses Controllers liegt darin, dass wir etwas im Modell brauchen dass man mit der Unity ansteuern kann. Man steuert also eigentlich nur den Controller und das Modell bewegt sich außen herum mit. Hierfür bietet der „Character Controller“ die nötigen Grundfunktionalitäten.

Scripting

Erstellung eines Character Controllers

Da dieser „Character Controller“ für sich alleine diese Funktionen aber nicht nutzen kann benötigt man Scripte die auf diese Funktionalitäten zugreifen. Um die von uns benötigten Funktionalitäten zu erreichen haben wir die für diese Controller nötigen Scripte verfasst und speziell jeweils auf den Nerd- und Player- Charakter angepasst. Orientiert haben wir uns dabei an dem ergebnissen der Tutorialserie „Third Person Char System“ von 3DBuzz(www.3dbuzz.com). Das Grundprinzip des Controllers ist eine Dreiteilung, ähnlich dem aus der objektorientierten Programmierung und App-Entwicklung bekannten Modell -View-Controller- Prinzip, in eine Controller-, eine Motor und eine Animator- Klasse. Hierbei erfüllt der Controller die Aufgabe die Interaktion mit dem Bediener herzustellen und Eingaben an den Motor zur räumlichen Bewegung und an den Animator zur Animation des Modells zu delegieren. Geschrieben haben wir diese drei Klassen in C#. Um in der Unity ein neues C#-Script zu erstellen wählt man im Projektfenster unter „Create“ „C#“ aus. Diesem gibt man dann einen passenden Namen und doppelklickt es um es im Monodevelop – Editor zu öffnen. Dieser bietet alle Annehmlichkeiten der gängigen Editoren, wie Debugging, Autovervollständigung, usw. Im Folgenden möchten wir einige Besonderheiten der einzelnen Klassen aufzeigen

Besonderheiten Controller-Klasse

void GetLocomotionInput()
	{
		var deadZone = 0.1f;
 
		if(Input.GetAxis("VerticalNerd")> deadZone || Input.GetAxis("VerticalNerd")< -deadZone)
			Nerd_Motor.Instance.MoveVector += new Vector3(0,0,Input.GetAxis("VerticalNerd"));
 
		if(Input.GetAxis("HorizontalNerd")> deadZone || Input.GetAxis("HorizontalNerd")< -deadZone)
			Nerd_Motor.Instance.MoveVector += new Vector3(Input.GetAxis("HorizontalNerd"),0,0);
 
		Nerd_Animator.Instance.DetermineCurrentMoveDirection();
 
	}
 
void HandleActionInput()
	{
		if(Input.GetButton("JumpNerd"))
			Jump();
		if(Input.GetKeyDown(KeyCode.M))
			Die();
		if(Input.GetKeyDown(KeyCode.K))
			Kick();
		if(Input.GetKeyDown(KeyCode.P))
			Punch();	
	}

Die Controllerklasse stellt die Schnittstelle zwischen dem Benutzer und dem Charakter dar. Sie nimmt die Eingaben des Benutzers auf und gibt die Informationen gezielt an den Motor und den Animator weiter. Hierfür besitzt die Controller- Klasse zwei markante Funktionen „GetLocomotionInput()“ und „HandleActionInput()“. Bei „GetLocomotionInput()“ fragt der Controller nach in welche Richtung sich der Charakter bewegen soll und gibt diese Information direkt als Vektor an den Motor weiter. Die Information dafür erhält er über von der Unity vorgegebene Bewegungsfunktionen deren Tastenbelegung über das Menü „EditProject Settings Input“ selbst vergeben werden können. In unserem Projekt haben wir dort extra zwei getrennte Blöcke für Nerd und Player eingerichtet um die Steuerung zweier Charaktere zu ermöglichen. Bei „HandleActionInput()“ fragt der Controller nach ob spezielle Tasten gedrückt werden und startet daraufhin eine Fuktion die eine Animation im Animator auslöst. An dieser Stellewird realisiert das man auf Knopfdruck z.B. einen Tritt ausführen kann.

Besonderheiten Motor-Klasse

void ProcessMotion()
	{
		// Transform MoveVector to World Space
		if(!Nerd_Animator.Instance.IsDead)
			MoveVector = transform.TransformDirection(MoveVector);
		else
			MoveVector = new Vector3(0,MoveVector.y,0);
 
		// Normalize MoveVector of Magnitude > 1
		if(MoveVector.magnitude >1)
			MoveVector = Vector3.Normalize(MoveVector);
 
		// Apply Sliding if applicable and enabled
		//if(EnableSliding)
		//	ApplySlide();
 
		// Multiply MoveVector by MoveSpeed
		MoveVector *= MoveSpeed();
 
		// Reapply VerticalVelocity MoveVector.y
		MoveVector = new Vector3 (MoveVector.x,
		                          VerticalVelocity,
		                          MoveVector.z);
		// Apply Gravity 
		ApplyGravity();
 
		// Move Character in World Space
		Nerd_Controller.CharacterController.Move(MoveVector*Time.deltaTime);
	}

Der Motor ist dafür zuständig eine möglichst realistische Bewegung des Modells sicherzustellen besonders hervorzuheben sind dabei die Funktionen „ProcessMotion()“ und „Float MoveSpeed()“ „ProcessMotion()“ ist die Funktion, die die vom Controller ausgesendeten Bewegungsvektoren aufnimmt und verarbeitet, sodass eine Charakterbewegung in Weltkoordinaten ausgeführt werden kann. Dafür wird erst der Vektor in Weltkoordinaten gewandelt und auf eine Größe >1 Normalisiert. Danach wird das Ergebnis mit der Bewegungsgeschwindigkeit und Beschleunigung verrechnet und anschließend unter Schwerkrafteinfluss gesetzt. Jetzt kann das Modell bewegt werden. Die Funktion „float MoveSpeed()“ errechnet die eben verwendet Bewegungsgeschwindigkeit auf der Grundlage des aktuellen „CharacterState“ des Charakters den sie sich aus dem Animator zieht. So erhält man den Effekt dass das Modell sich je nach Bewegungsrichtung unterschiedlich schnell bewegt.

float MoveSpeed()
	{
		var moveSpeed = 0f;
 
		switch(Nerd_Animator.Instance.MoveDirection)
		{
		case Nerd_Animator.Direction.Stationary:
			moveSpeed = 0f;
			break;
		case Nerd_Animator.Direction.Backward	:
			moveSpeed = BackwardSpeed;
			break;	
		case Nerd_Animator.Direction.Forward	:
			moveSpeed = ForwardSpeed;
			break;
		case Nerd_Animator.Direction.Left	:
			moveSpeed = StrafingSpeed;
			break;
		case Nerd_Animator.Direction.Right:
			moveSpeed = StrafingSpeed;
			break;
		case Nerd_Animator.Direction.LeftBackward		:
			moveSpeed = BackwardSpeed;
			break;	
		case Nerd_Animator.Direction.LeftForward		:
			moveSpeed = ForwardSpeed;
			break;
		case Nerd_Animator.Direction.RightBackward		:
			moveSpeed = BackwardSpeed;
			break;
		case Nerd_Animator.Direction.RightForward		:
			moveSpeed = ForwardSpeed;
			break;
		}
		return moveSpeed;
	}

Besonderheiten Animator-KlasseDer Animator ist die größte der drei Klassen. Sie ist in verschiedene „Regions“ (Bereiche) aufgegliedert „Methods“, „CharacterState Methods“ und „Start Action Methods“

Unter Methods ermittelt der Animator zuerst auf Grundlage des Bewegungsvektors des Motors die Bewegungsrichtung. Diese gibt er einerseits an den Motor weiter, andererseits bildet sie einen Faktor in der Ermittlung des aktuellen „CharacterStates“. Dieser wird entweder durch eine Bewegung oder eine Aktion ausgelöst und stellt sicher, dass die richtigen Animationen zur richtigen Zeit abgespielt werden und sich nicht überschneiden. Unter „CharacterState Methods“ wird sichergestellt, dass die States immer auf dem richtigen Stand sind bzw. immer nach Ablauf der zugehörigen Animation wieder auf „Idle“ zurückgesetzt werden, sodass wieder ein neuer „CharacterState“ ermittelt werden kann. Unter „Start Action Methods“ befinden sich die Funktionen die vom Controller über Tastendruck ausgelöst werden.

Beispiel: Kick-Animation

Wir möchten anhand der Kick-Animation den zur Realisierung eines Animationsablaufes nötigen Code darstellen :

 	void HandleActionInput()
	{
		if(Input.GetButton("JumpNerd"))
			Jump();
		if(Input.GetKeyDown(KeyCode.M))
			Die();
		if(Input.GetKeyDown(KeyCode.K))
			Kick();
		if(Input.GetKeyDown(KeyCode.P))
			Punch();	
	}
	public void Kick()
	{
		Nerd_Animator.Instance.Kick();
	}
  • Aus dem Controller benötigt man sowohl die „HandleActionInput()“ Funktion um die Aktion auslesen zu können als auch die „Kick()“Funktion um den Befehl an den Animator weiterzugeben.
 
	public void Kick()
	{
			State = CharacterState.Kicking;
			animation.CrossFade("Kick");
	}
  • Im Animator benötigt man die entsprechende „Kick()“ Funktion die vom Controller aufgerufen wird. Diese setzt den „CharacterState“ auf „kick“ und startet die Kick-Animation in diesem Fall „HookKickNerd“
  • Anschließend nutzt man die Funktion „ProcessCurrentState()", die basierend auf dem aktuellen CharakterState die entsprechende „CharakterState Method“,in diesem Beispiel also „Kicking()“, aufruft.
 	void ProcessCurrentState()
	{
		switch(State)
		{
		case CharacterState.Idle:
				Idle();
			break;
		case CharacterState.Jumping	:
				Jumping();
			break;
		case CharacterState.Kicking	:
				Kicking();
			break;
		case CharacterState.Punching:
				Punching();
			break;
		case CharacterState.Running	:
				Running();
			break;
		case CharacterState.StrafingLeft:
				StrafingLeft();
			break;
		case CharacterState.StrafingRight:
				StrafingRight();
			break;
		case CharacterState.WalkBackwards:
				Walkbackwards();
			break;
		case CharacterState.Dead:
				Dead();
			break;
 
		}
	}
 
	void Kicking()
	{		if(!animation.isPlaying)
		{
			State = CharacterState.Kicking;
			animation.CrossFade("Idle");
		}
	}
  • Am Ende sorgt die „Kicking()“ Funktion dafür das nach Abschluss der Animation wieder zurück in den „Idle“- State geblendet wird.


Weblinks