Tuesday, September 16, 2014

Codefest: Hadoop/HDInsight Einführung Big Data mit HDInsight/Hadoop für .NET Entwickler


“HDInsight is Microsoft’s Hadoop-based service that brings an Apache Hadoop-based solution to the cloud. It gives you the ability to gain the full value of Big Data with a modern, cloud-based data platform that manages data of any type, whether structured or unstructured, and of any size.”
Quelle: http://azure.microsoft.com/en-us/pricing/details/hdinsight/
image

Big Data mit Hadoop und HDInsight

In dieser Blog-Reihe über Big Data mit Hadoop und HDInsight möchte ich euch, den Entwicklern, den Einstieg in die Welt von Big Data schmackhaft machen. Das natürlich auf Basis der Microsoft Lösung HDInsight, die ja zu 100% Hadoop beinhaltet. Aber bevor wir so richtig mit Hands-On loslegen können, sollten wir uns ein wenig mit der vorhandenen Technologie in der Theorie beschäftigen, die dem Ganzen zu Grunde liegt. Keine Angst, ich bin eher ein „Mann der Praxis“, ich werde mich also so kurz wie möglich halten, damit wir recht bald einmal die Praxis beleuchten können.

Was ist Big Data?

Bevor wir uns den Technologie-Stack selbst genauer ansehen können, sollten wir uns dem Begriff “Big Data” einmal genauer widmen. Nun ja, da beginnen die Schwierigkeiten bereits. Big Data kann nämlich für jeden etwas anderes bedeuten. Für mich bedeutet es, dass Daten vorliegen, die einfach zu komplex sind, um sie in traditionellen relationalen Datenbanken UND auch in multidimensionalen Datenbanken zu analysieren. Das ist aber eben nur eine Begründung für Big Data, nämlich rein die Anzahl der Daten, bzw. eben die Datenmenge. Aber für mich ist auch schon ein einzelnes Buch „Big Data“, da die Wörter in einem Buch komplett unstrukturiert vorliegen (Vorsicht in etwaigen Diskussionen über Big Data in eurem Lieblingsliteraturzirkel, der Autor eines Buches sieht das naturgemäß etwas anders) und es nun einmal schwierig ist, die Anzahl der Hauptwörter oder alle Bindewörter in einem, oder mehreren Büchern einfach einmal so zu zählen. Also wie ihr möglicherweise schon an anderer Stelle gelesen habt, zieht man sich in der einschlägigen Literatur zu dem Thema auf eine allgemeine Beschreibung zurück.
Im Allgemeinen haben sich, wenn man von Big Data spricht, die „3V’s“ eingebürgert (Doug Laney von Gartner hat schon 2001 darüber sinniert, siehe hier) . Das sind Volume, Variety und Velocity. Sehen wir uns jeden dieser drei einmal kurz an:
· Volume – Datenmenge: Hierunter fallen sowohl große Datenmengen von Terrabytes bis Petabytes, als auch viele kleine Datenmengen, die es gemeinsam zu analysieren gilt.
· Variety – Datenvielfalt: Nicht nur die (sehr) großen Datenmengen an sich, sondern auch die Vielfältigkeit der anfallenden Daten sind eine der Herausforderungen im Big Data Bereich. Ihr werdet das vermutlich selbst kennen aus eurer Vergangenheit, oder in eurer aktuellen Firma, wo die Daten aus unternehmensinternen, und/oder aber auch aus externen Quellen stammen. Diese unterschiedlichen Daten liegen entweder
o strukturiert (z. B. in relationalen Datenbanken)
o halb-strukturiert (z. B. Log-Dateien von Web-Servern) und/oder
o komplett unstrukturiert (z. B. Texte, Webseiten, Video-, Audio-Dateien) vor.
· Velocity – Geschwindigkeit: Daten, die sich andauernd verändern und kurze Gültigkeit/Aktualität beinhalten. Diese erfordern eine andauernde Generierung und Verarbeitung – sehr häufig in Echtzeit.
image
Noch extra erwähnen möchte ich, dass die zugrundeliegenden Daten natürlich vertrauenswürdig und korrekt sein MÜSSEN, denn wenn die Datenbasis schon schlecht ist, dann wird es mit der besten Technologie nicht gelingen brauchbare Ergebnisse für den Endbenutzer zu erzeugen, aber das war ja schon in der Vergangenheit bei euren Projekten mit relationalen Datenbanken und Analysis Services ja auch schon ein kritischer Punkt.
Soweit so gut, aber ich kann es schon richtig hören, wie ihr mir zuruft:
Die 3V’s sind nett und gut, aber SQL Server und/oder SQL Server Analysis Services mit den Integration Services erledigen diesen Job für die oben angeführten Problemstellungen doch hervorragend. Hmm, ja, möglicherweise, für strukturierte Daten. Möglicherweise.
Mit den unstrukturierten Daten und der komplexen Bearbeitung derselben sind wir nun schon eher bei des Pudels Kern angelangt, warum wir mit den bisher eingesetzten Technologien vermutlich an Grenzen stoßen werden und was das Thema Big Data im Endeffekt ausmacht. Wir wollen ja nicht „nur“ einen Server, der sich um die großen Daten, die unstrukturiert vorliegen (oder vielen kleinen Daten) kümmert, sondern die Bearbeitung der Daten soll mit mehreren Maschinen durchgeführt werden.
Und, Schwupps sind wir schon beim nächsten Thema angelangt. Die verwendete Technologie für das Thema Big Data. Aber eines sollte hier noch klar gesagt werden: Big Data wird keine der erwähnten Technologien ersetzen, sondern im Gegenteil. Es wird ergänzend eingesetzt. Für manche von euch ist das bestimmt eine Frohbotschaft, es kommt noch mehr Technologie auf euch zu….

Die Big Data Technologie

Die Daten sollen also mit „geballter Power“, also mit mehreren Maschinen, „beackert“ werden, und das nicht mit SQL oder MDX. Die Daten werden eher in einer Art Batch-Processing bearbeitet, das Resultat landet möglicherweise (muss nicht sein) wieder in Tabellenform. Nun, das genau ist eine (sehr kurze) Zusammenfassung der Kernfunktionalität von Hadoop. Das verteilte Bearbeiten von Daten, innerhalb eines Clusters mit einem Head-Node und mehreren Data-Nodes, wo jedes Node an einem Teil der Daten die Bearbeitung durchführt, um das gewünschte Resultat zu erzeugen.

Hadoop Übersicht

Hadoop selbst ist ein Open Source Projekt und ein Apache Projekt - http://hadoop.apache.org. Der innerste Kern besteht aus 2 Teilen:
· Hadoop Distributed File System (HDFS) – speichert die Daten im Cluster. Die Daten werden in Blöcke geteilt und auf verschiedene Nodes im Cluster verteilt.
· MapReduce – ein Programmiermodell, das in zwei Phasen aufgegliedert ist. Wie der Name schon sagt eben Map und Reduce. Damit ist es möglich die Datenbearbeitung auf mehrere Nodes zu verteilen, in der Map Phase werden so Zwischenergebnisse erzeugt, die in der Reduce Phase – ebenfalls parallelisiert - zu einem Gesamtergebnis weiter verarbeitet werden.
Mit diesem Wissen können wir uns schon einmal ansehen, wie die Basis Komponenten innerhalb eines Hadoop Clusters aussehen könnten (sofern dieser einmal läuft, was aber keine große Hexerei ist, wie wir in meinem nächsten Blog Eintrag sehen werden) und wie diese zusammen interagieren:
· Der Name Node (auch oft “Head Node” innerhalb des Clusters genannt): Hält primär die Metadaten für HDFS und da die Daten ja verteilt von den Daten Nodes in einzelnen Datenblöcken bearbeitet werden, muss jemand die Ablage der Daten koordinieren und den Cluster überwachen und bei einem Ausfall dementsprechend reagieren, d. h. die Daten neu auf die verbliebenen Data Nodes verteilen. Dies ist die Aufgabe des Name Node, was ihn aber auch gleichzeitig zum „Single Point of Failure“ in einem Hadoop Cluster macht.
· Data Node: Speichert ihm zugewiesene Daten in sogenannten HDFS Datenblöcken. Diese Datenblöcke werden innerhalb eines Hadoop-Clusters repliziert um Ausfallsicherheit zu gewährleisten. Jeder Data Node meldet sich fortwährend beim Name Node, der ansonsten auf einen Ausfall reagieren muss.
Soweit zur Datenhaltung, die Überwachung eine MapReduce-Jobs obliegt zwei sogenannten Trackern:
· Der Job Tracker verteilt die einzelnen Aufgaben innerhalb des MapReduce Jobs, ist aber im Fehlerfall weniger kritisch zu handhaben als der Ausfall des Name Nodes, da bei Neustart des Job Tracker alle Jobs erneut gestartet werden.
· Der Task Tracker erledigt die eigentliche Arbeit bei dem angeforderten MapReduce Job, läuft üblicherweise auf jedem Data Node.
Bildlich gesprochen lässt sich das folgendermaßen darstellen:
image
Soweit zum Core, weitere zugehörige Hadoop-Projekte, die durchaus hilfreich sind und das Gesamtbild - und die damit verbundenen technologischen Möglichkeiten - doch um einiges erweitern:
· Hive – Die Ergebnisse, die mit MapReduce Jobs erzeugt werden können mittels Hive in einer SQL ähnlichen Abfragesprache, der Hive Query Language (HQL) aus dem Hive store abgefragt werden. Hive ist ideal, um die mittels MapRedue erzeugten Ergebnisse in die bestehende BI Landschaft zu integrieren.
· Pig – Eine weitere Möglichkeit, die Daten von MapReduce Jobs abzufragen ist Pig, welches eine Scripting Engine mit dem Namen Pig Latin zur Verfügung stellt. Mit dieser können Abfragen ‚anprogrammiert‘ werden, also Prozedural abgefragt werden, im Gegensatz zu Hive, das wie erwähnt, ähnlich zu SQL – also deklarativ – arbeitet.
· HCatalog – Dieser setzt auf den Hive Metastore auf und stellt Schnittstellen für MapReduce und Pig zur Verfügung, um Daten in Tabellenform darzustellen.
· Oozie – Eine Workflow Engine für mehrere MapReduce Jobs.
· Sqoop – Wird angewendet, um Daten aus und in relationale Datenbanken in HDFS zu importieren und/oder zu exportieren.
Weitere sind noch: Fume (zum Importieren von Daten in HDFS), Mahout (machine-learning), oder Ambari (Monitoring Tool für den Cluster). Zusammengefasst sieht die Landschaft dann im Großen und Ganzen so aus:
image
Wie man deutlich erkennen kann ist Hadoop nicht etwa (nur) eine Datenbank, bzw. ein Datenbankmanagementsystem (wie SQLServer z. B. eines ist). Es ist ein verteiltes Dateisystem, das mit riesigen (unstrukturierten) Datenmengen – mit dutzenden Terabytes - gefüttert werden kann, diese wiederum mit MapReduce Jobs bearbeitet und sortiert werden können - und das mit Hilfe der zu Grunde liegende Cluster-Technologie in Sekunden. In Kombination mit den erwähnten Unterprojekten kann Hadoop Prozesse ausführen, die weit über die Leistung eines (normalerweise) eingesetzten relationalen Datenbank, oder Datawarehouse-Systems hinausgehen.
Sehen wir uns doch einmal gemeinsam ein paar Einsatzszenarien für Big Data und Hadoop/HDInsight an.

Einsatz-Szenarien

1. Hadoop als ETL-Tool

Wenn große Datenmengen zur Verfügung stehen, dann steht man immer wieder vor der großen Herausforderung die für das Unternehmen relevanten Daten herauszufiltern. Nun, Hadoop ist hier ein eleganter Ansatz. Die Daten werden in Hadoop geladen und mit MapReduce Jobs bearbeitet. Mit eigenen Filtern und eigener Logik - das Resultat sind strukturierte, summierte Daten. Diese können mit bestehenden Analyse-Werkzeugen verfeinert werden, bzw. dem Reporting zugeführt werden.Normalerweise wird nu rein kleiner Prozentsatz vom gesamten vorhandenen Datenstrom für ein bestimmtes Business-Problem benötigt. Hadoop mit MapReduce, dem Clusteraufbau und den weiteren Unterprojekten ist dafür eine sehr gut geeignete Plattform.

2. Hadoop als Exploration-Engine

Wenn die Daten einmal im Cluster sind können nahezu beliebige Tools zur Analyse verwendet werden. Dies werden wir in einem der Blog-Einträge noch genauer betrachten, wenn wir Excel, PowerBI, Reportung Services usw. auf unseren (lokalen) Cluster loslassen. Neue Daten werden hinzugefügt, die bestehenden Ergebnisse bleiben weiter vorhanden und können aber natürlich zur erneuten/weiteren Analyse der Daten verwendet, bzw. weiter verdichtet werden. Das Laden in andere Systeme, wie einem SQLServer, bzw. einem SQL Server Analysis Server ist problemlos möglich und so können die Ergebnisse innerhalb des Unternehmens weiteren Anwendern zur Verfügung gestellt werden.

3. Hadoop als Archiv

Historische Daten Daten werden nicht ewig vorgehalten, sondern landen irgendwann einmal im Archiv. Werden die Daten für eine Analyse wieder benötigt, so ist es häufig mühsam diese wieder herzustellen. Mit dem Ergebnis, dass historische Daten oft nicht für die Analyse zur Verfügung stehen, da man versucht diesen Aufwand zu vermeiden. Die zugrunde liegende Speichertechnik in HDFS, sowie die effektiven Indizierungsmöglichkeiten innerhalb des Clusters - und da Speicher heutzutage günstig ist - können die (originalen) Daten in einem Hadoop Cluster weiter vorgehalten werden. Der Aufwand der Wiederherstellung entfällt und die Daten stehen jederzeit für eine historische Analyse zur Verfügung.

Microsoft HDInsight - Hadoop unter Windows

HDInsight ist die Microsoft Implementierung für Big Data Lösungen mit Apache Hadoop als Unterbau. Ihr braucht jetzt keine Angst zu haben, falls ihr denkt, dass Microsoft hier nun etwas eigenes gebaut hätte.
HDInsight ist zu 100 Prozent kompatibel mit Apache Hadoop und baut komplett auf den Open Source Framework auf.
Microsoft hat das Projekt großartig unterstützt, indem es die Funktionalität zur Verfügung gestellt hat, um Hadoop unter Windows laufen zu lassen, da Hadoop ursprünglich nur für Linux entwickelt wurde. Sie haben den notwendigen Source Code der Community zur Verfügung gestellt und diese hat dann diesen dankenswerter Weise (für uns Windows Entwickler) komplett in den Source einfließen lassen. Somit läuft Hadoop unter Windows und Microsoft hat hier die Unterstützung noch nicht gestoppt. Es wurden weitere APIs geschrieben, um mit C# Hadoop anprogrammieren zu können, bzw. den ODBC-Treiber entwickelt um auf die Daten von Hadoop auch aus bestehenden Applikation zugreifen zu können. Auch das werden wir in im Laufe dieser Blog-Reihe sehen können.
HDInsight ist in zwei Ausprägungen verfügbar, als Cloud-Lösung und als lokale Installation:
1. Windows Azure HDInsight Service: Solltet Ihr noch keine Azure Subscription euer eigen nennen, ist es nun soweit, HDInsight steht euch unter Azure zur Verfügung und wartet nur darauf, von euch als Cluster verwendet zu werden. Wir werden die Installation und die anfallenden Kosten im nächsten Blog-Eintrag näher beleuchten.
2. Windows Azure HDInsight Emulator: Eine hervorragende Lösung von Microsoft, die einen single-node, geeignet als Einzelplatz Installation, zur Verfügung stellt, welcher z. B. auf eurer Entwicklungsmaschine bestimmt eine ganz gute Figur macht. Dafür ist er nämlich gemacht: als lokale Big Data Entwicklungsinstallation zu dienen. Eure Resultate könnt ihr dann in das Azure HDInsight Service überführen und in einen richtigen Cluster in Produktion bringen. Erst einmal ohne anfallender Kosten für den Cluster.
Und genau diese beiden Lösungen werden wir uns im zweiten Teil dieser Blog-Reihe genauer ansehen.

Zusammenfassung

Hadoop an sich eignet sich perfekt für die Verarbeitung umfangreicher Batchjobs. Hadoop kann Dutzende Terabyte dateibasierter Daten sehr schnell verarbeiten. Mit diesen grundlegenden, aber leistungsfähigen Funktionen ist Hadoop sehr gut geeignet, um große Dateien zu durchsuchen, Informationen zu aggregieren oder grundlegende mathematische Funktionen wie Summe, Mittelwert oder Durchschnittswert auszuführen. HDInsight ist Microsoft’s Cloud Lösung für Big Data, zu 100% basierend auf den Open Source Framework Hadoop.
So, erst einmal genug der Theorie, ab jetzt wird kräftig mit der Software gearbeitet, ich hoffe ihr seid bei den nächsten Einträgen wieder fleißige Leser!

Wednesday, September 10, 2014

CodeFest.at Blog-Eintrag: SignalR und Unity in euren Multiplayer-Indie-Games

Ihr, als .Net-Entwickler, kennt SignalR möglicherweise bereits als Microsoft-Programmierbibliothek um Nachrichten in Echtzeit zu verschiedenen Clients zu übertragen. Das klingt zuerst einmal schwierig, ist es in Wirklichkeit aber nicht.

Grübelt ihr noch über ein Einsatzszenario? Nun, wie wäre es mit einem großen Monitor, oder Fernseher für euer Support-Team? Auf diesem wird der aktuelle Status von verschiedenen System angezeigt. Sollte eines der Systeme etwas Ungewöhnliches entdecken, dann schickt es zum SignalR-Hub eine Meldung. Alle angeschlossenen Clients erhalten diese Meldung umgehend und können dementsprechend reagieren.
clip_image002
Ich hoffe, ihr seid ein wenig neugierig geworden und wollt jetzt wissen, wie das genau funktioniert? Nun das hat mir mein Blogger-Kollege Toni Pohl schon hier in diesem Blog-Eintrag von letztem Jahr abgenommen. Ich empfehle euch diesen Eintrag zu lesen, dann können wir anschließend an dieser Stelle weiter machen.

Unity und SignalR

Ich möchte euch zeigen, wie man Unity mit SignalR verwenden kann. Für Multi-Player-Games bietet es sich ja geradezu an. Mit einer eventuell bereits vorhandenen Azure-Subscription habt ihr noch dazu alles, was ihr als Infrastruktur in Produktion benötigt. Dazu aber möglicherweise ein anderes Mal mehr. Ich würde gerne mit euch gemeinsam einen Teaser für ein Spiel schreiben. Die heutige Funktionalität ist recht einfach zusammengefasst:
· Unity als Entwicklungstool.
· Ein witziger Hintergrund.
· Ein Sprite (in diesem Fall eine geöffnete Hand), welches sich mit der Maus bewegen lässt.
· Wenn die linke Maustaste gedrückt wird, dann soll eine Faust gebildet werden, also in etwa so, als würde man nach einem Objekt greifen.
· Ein Web-Client, der den gleichen Hintergrund verwendet.
· Ebenfalls die geöffnete Hand, sowie die geschlossene Hand.
· Wenn das Unity-Spiel startet, dann soll sich auf der Webseite die Hand genauso bewegen, wie in dem Unity-Client.
· Wird im Unity-Spiel die Maustaste gedrückt, dann wird die Hand zu einer Faust, das soll der Web-Client widerspiegeln.
Lange Rede, kurzer Sinn, das soll so funktionieren, wie ich es hier auf youtube kurz vorzeige.
Was benötigt ihr dazu? Ich nehme an, wenn ihr diesem Blog folgt, dann habt ihr das schon installiert:
· Unity, die Testversion von http://unity3d.com/ reicht vollkommen.
· Visual Studio 2013, wenn ihr die Visual Studio Integration mit Unity verwenden wollt.
· Visual Studio Tools für Unity von hier: http://unityvs.com/ diese bitte herunterladen und installieren.
Für die ganz Ungeduldigen unter euch, den Source Code gibt es wieder hier zum Download.

Spiele-Sprites

Für den Spiel-Teaser selbst benötigen wir noch ein Hintergrund-Bild, dieses findet ihr als .psd-Datei hier, hat die Größe von 800x500 Pixel und sieht so aus:
clip_image004
Damit wir auch etwas Bewegung in die Sache bringen - die beiden Bilder für die sich bewegende Hand:
clip_image005 clip_image006
Das war es bereits, es kann also losgehen mit dem Unity-Client.
Ein kleiner Hinweis noch, solltet ihr noch nie mit Unity gearbeitet haben, dann lege ich euch meinen letzten Blog-Eintrag ans Herz, da werden einige der Schritte, die ich hier nur kurz anreiße – vor allem beim Anlegen des Projektes – genauer beschrieben. Diesen findet ihr hier.

Der Unity Client Teil 1 – noch ohne SignalR

Im ersten Teil wollen wir den Unity-Client so weit bringen, dass die Hand mit der Maus bewegt werden kann und beim Klicken der Maus sich die Hand schließt und auch wieder öffnet. Im Unity-Client müssen wir somit folgendes tun:
· Ein neues 2D-Unity-Projekt erstellen.
· Den Hintergrund und die Hand in die Spielszene einfügen.
· Ein „wenig“ Physik für die Hand hinzufügen.
· Ein Script erstellen, damit die Hand bewegt werden kann.
· Wenn die Maus gedrückt wird, schließt sich die Hand und öffnet sich wieder, wenn die Maustaste losgelassen wird.

Ein neues 2D-Unity-Projekt erstellen

Im geöffneten Unity erstelle ich ein neues 2D-Projekt und gebe diesem den Namen SignalRUnitySample.
clip_image007
Eine gewisse Ordnerstruktur in Unity ist immer eine gute Sache, um die Dateien nicht irgendwie herumfliegen zu lassen. Das sollte ungefähr so aussehen:
clip_image008
Ich habe auch gleich die drei Bilder in den Ordner Textures mit Drag & Drop hineingezogen.

Den Hintergrund und die Hand in die Spielszene einfügen

Dazu ziehen wir den Hintergrund aus dem Assets\Textures-Folder auf die Main Camera in der Hierarchy-Ansicht. Danach holen wir aus dem Assets\Textures-Folder den Sprite mit dem Namen OpenHand als eigenständiges Objekt – ich habe dieses dann umbenannt auf Player.
clip_image009
Ich habe die Anzeigegröße in Unity noch angepasst, um das Resultat mit dem im Browser besser vergleichen zu können. Dazu klickt ihr in Unity auf Game und in der DropDown fügt ihr mittels Add eine neue Auflösung mit 800x500 Pixel ein.
clip_image010
Danach skaliert ihr noch den Hintergrund und die Hand, dementsprechend. D. h. den Hintergrund skaliert ihr auf der X- und Y-Achse 2 mal. Das macht ihr auch mit dem Player-Objekt (der Hand). Des Weiteren sollte die Hand im aktuellen Layer weiter vorne liegen (Wert 1), damit wir diese auch sehen und diese nicht hinter dem Hintergrund gezeichnet wird.
clip_image012 clip_image014
Jetzt sollte noch etwas Bewegung in die Sache kommen.

Physik und Skript für das Player-Objekt

Wie schon bei dem PhoneCatcher-Spiel das letzte Mal, benötigt unser Player-Objekt einen RigidBody 2D, an dem aber „Is Kinematic“ gesetzt wird, um die Schwerkraft abzuschalten. Die Spieler-Hand wird ja mit der Maus, durch den Spieler selbst, bewegt. Dieser wird mit dem Add Component-Button hinzugefügt (an dem Player Objekt).
clip_image016 clip_image017
Jetzt noch ein Skript mit dem Namen PlayerController, wiederum mit dem Add Component an dem Player Objekt. Falls ihr das alles richtig gemacht habt, dann sieht das im Inspector bisher so aus:
clip_image018
Das Skript ist bisher ebenfalls keine große Überraschung, da es sich an dem vom letzten Mal orientiert. Wir benötigen die Grenzen des Spielfeldes, damit die Hand nicht darüber hinaus bewegt werden kann. Zusätzlich muss das Spieler-Sprite ausgetauscht werden, wenn die Maustaste gedrückt wird – und wieder zurückgesetzt werden, damit wieder die geöffnete Hand sichtbar ist, wenn die Maus losgelassen wird.
using UnityEngine;
public class PlayerController : MonoBehaviour { 
    public Camera cam;
    private float maxWidth;
    private float maxHeight;
 
    bool isHandOpen = true;
    public Sprite clickedSprite;
    private Sprite standardSprite;
 
    void Start()
    {
        if (cam == null)
            cam = Camera.main;
        var corner = new Vector3(Screen.width, Screen.height, 0f);
        var targetWidth = cam.ScreenToWorldPoint(corner);
        float playerWidth = renderer.bounds.extents.x;
        float playerHeight = renderer.bounds.extents.y;
        maxWidth = targetWidth.x - playerWidth;
        maxHeight = targetWidth.y - playerHeight;
 
        standardSprite = this.GetComponent().sprite;
    }
    void FixedUpdate()
    {
        var currentPosition = cam.ScreenToWorldPoint(Input.mousePosition);
        float targetWidth = Mathf.Clamp(currentPosition.x, -maxWidth, maxWidth);
        float targetHeight = Mathf.Clamp(currentPosition.y, -maxHeight, maxHeight);
        var newPosition = new Vector3(targetWidth, targetHeight, 0f);
        rigidbody2D.MovePosition(newPosition);
 
        if (Input.GetMouseButtonDown(0))
        {
            isHandOpen = false;
            MouseDown();
        }
        else if(Input.GetMouseButtonUp(0))
        {
            isHandOpen = true;
            MouseUp();
        }
    }
 
    private void MouseDown()
    {
        Debug.Log("MouseDown");
        this.GetComponent().sprite = clickedSprite;
    }
 
    private void MouseUp()
    {
        Debug.Log("MouseUp");
        this.GetComponent().sprite = standardSprite;
    }
}
Zurück in Unity muss noch das Sprite für die Public-Variablen clickedSprite befüllt werden, dazu muss aus Assets\Textures das ClosedHand-Sprite mittels Drag & Drop auf die Skript-Variable gezogen werden.
clip_image019
Wenn das geklappt hat, dann solltet ihr das einmal ausprobieren. Die Hand müsste über den Bildschirm flitzen, wenn ihr die Maus bewegt. Wenn die Maustaste gedrückt wird, dann seht ihr die Faust. Nach dem Loslassen der Maustaste ist wieder die geöffnete Hand sichtbar. Der Unity-Client ist erst einmal fertig, jetzt müssen wir den SignalR-Teil erstellen.

Echtzeit-Kommunikation mit SignalR

Der Unity-Client wird sich am SignalR-Hub, der als Web-Applikation läuft, anmelden und Meldungen absetzen. Der SignalR-Hub informiert alle angemeldeten Clients und diese werden dementsprechend reagieren. Wir benötigen also eine neue Web-Applikation, ihr könnt dafür entweder eine komplett neue Solution in Visual Studio anlegen, oder auch die von Unity angelegte Solution verwenden. Wie auch immer ihr euch entscheidet, wir benötigen eine neue ASP.NET Web Application, ich habe den Namen SignalRUnityWeb ausgewählt. Diese soll leer sein und per Default noch keinerlei Funktionalität zur Verfügung stellen.
clip_image021clip_image023
Wie mittlerweile fast schön üblich benötigen wir zwei NuGet Packages. Dazu drückt am Web-Projekt die rechte Maustaste und wählt „Manage NuGet Packages…“ an.
Das erste Package ist Microsoft ASP.NET SignalR, derzeit in der Version 2.1.1
clip_image025
Das zweite Package ist Microsoft.Owin.Host.SystemWeb in der Version 3.0.0
clip_image027
Jetzt können wir mit dem Implementieren loslegen, wir benötigen eine neue SignalR Hub Class (v2), dazu drückt ihr die rechte Maustaste am Web-Projekt und wählt Add-> SignalR Hub Class (v2), sollte das bei euch nicht zu sehen sein, dann bitte über Add->New Item und sucht nach SignalR – das nächste Mal, taucht der Eintrag dann in der Liste auf.
clip_image029
Diese sollte den Namen SignalRSampleHub bekommen.
clip_image030
Wir passen die Default-Implementierung nur rudimentär an, ich hätte gerne eine Send-Methode mit zwei Parametern, und zwar WER hat die Meldung abgesetzt und WAS ist der Inhalt.
using Microsoft.AspNet.SignalR;
namespace SignalRUnityWeb
{
    public class SignalRSampleHub : Hub
    {
        public void Send(string name, string message)
        {
            // Call the broadcastMessage method to update clients.
            Clients.All.broadcastMessage(name, message);
        }
    }
}
Wenn wir SignalR 2.0 verwenden, benötigen wir noch eine OWIN Startup class, wiederum mit der rechten Maustaste am Web-Projekt und Add-> OWIN Startup class, bzw. über Add->New Item.
clip_image031
Der Name soll Startup.cs sein.
clip_image032
Die Implementierung ist kurz und bündig und stellt unseren SignalR-Hub zur Laufzeit zur Verfügung.
using Microsoft.Owin;
using Owin; 
[assembly: OwinStartup(typeof(SignalRUnityWeb.Startup))]
namespace SignalRUnityWeb
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            app.MapSignalR();
        }
    }
}
Wenn ihr die Web-Applikation ausführt, so wird das leider nicht klappen, da es da noch einen Versionskonflikt mit der aktuellen Microsoft.Owin-DLL gibt.
clip_image034
Diesen können wir in der Web.config lösen, indem wir der .NET-Laufzeitumgebung mitteilen, sodass die Version 3.0 der DLL auch funktioniert.

  
    
    
  
  
    
      
                                publicKeyToken="31bf3856ad364e35" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-2.1.0.0" newVersion="3.0.0.0" />
      
    
  

Jetzt benötigen wir noch eine Webseite in unserer Web-Applikation, ich würde vorschlagen wir nennen dieseindex.html. Diese soll das Hintergrundbild und die Hand darstellen, sowie sich an unserem SignalR-Hub anmelden. Wenn eine neue Meldung eintrifft, dann muss die Hand bewegt werden und bei gedrückter Maustaste das Bild gewechselt werden. Dank Microsoft und jQuery sind das nur ein paar Zeilen JavaScript-Code.
<!DOCTYPE html>


    SignalR-Unity Sample
    
 


    
 
    
        
    
 
    
    
    
    
    
    
    
    
    


Nun sollten noch die drei Bilder, die in dem Unity-Client verwendet werden, in die Web-Applikation kopiert werden, und zwar in einen Unterordner mit dem Namen Images.
clip_image035
Solltet ihr eine eigene Visual Studio Solution für diese Web-Applikation erstellt haben, dann startet diese nun. Der Signal-R Hub ist damit erstellt und aktiv. Die Webseite ist nun am Hub als erster Client angemeldet und wartet auf Meldungen vom SignalR-Hub.
clip_image036
Kommen wir zum letzten Abschnitt, SignalR und Unity zu verbinden.

Der Unity Client Teil 2 – Kommunikation mit SignalR

Zurück in Unity legen wir ein leeres GameObject an und geben diesem den Namen SignalRObject.
clip_image037clip_image038
Leere Game-Objekte sind sehr praktisch, in unserem Fall ist es so, dass wir nur ein Script benötigen (um den SignalR-Hub zu informieren), aber keinerlei sichtbaren Aktionen am Bildschirm ausführen werden. Also fügen wir gleich das Skript hinzu, wir geben diesem den Namen SignalRUnityController.
clip_image039
Speichert bitte nun eure Spiel-Szene im Scenes Folder, ich habe dieser den Namen GameScene gegeben. Das ist an dieser Stelle WICHTIG, da ihr sonst möglicherweise durch einen Absturz von Unity alles, was ihr bisher gemacht habt, verlieren könnt. Das liegt weniger an Unity, als an der Programmbibliothek, die wir uns im nächsten Schritt zu unserem Projekt dazuholen.
clip_image040
Bearbeiten wir nun gleich das Skript in Visual Studio 2013 und erweitern es, um den SignalR-Hub anzusprechen.
Leider verwendet Unity nicht die .Net 4.0 Laufzeitumgebung, sondern Mono mit der .NET 3.5 Runtime. Dadurch können wir leider das Microsoft NuGet Package (Microsoft.AspNet.SignalR.Client) für SignalR nicht in unserem Unity C# Projekt verwenden. Es gibt aber auf Github ein Projekt, welches für unsere Zwecke ausreichend ist, in diesem wurde die SignalR-Bibliothek auf .NET 2.0 „heruntermigriert“. Dieses findet ihr hier:https://github.com/jenyayel/SignalR.Client.20
Ich habe dieses Projekt meiner Unity-Solution als eigenständiges Projekt hinzugefügt und danach die Referenz zu dem C#-Projekt dazugegeben.
clip_image042clip_image044
Jetzt können wir die SignalRUnityController skripten. Ich dachte mir, dass wir die SignalR-Funktionalität abschaltbar machen, das kann sehr praktisch sein, wenn man den Unity-Client etwas „tweaked“, was ja vor allem gegen Ende in der Spiele-Entwicklung recht viel Zeit in Anspruch nimmt. Die URL zum SignalR-Hub sollte ebenfalls konfigurierbar sein (dort setzt auch bitte eure URL ein, die ist in eurem Web-Projekt vermutlich anders als meine – zumindest der verwendete Port). Der Rest ist sehr ähnlich dem JavaScript-Client, der Unity Client muss sich am Hub anmelden und möchte (im Gegensatz zum JavaScript-Client) Informationen senden. Die Informationen bestehen aus einem String (wer sendet, der UnityClient) und einem weiteren String, dieser wird allerdings als JSON versendet. Dadurch ist diese Klasse in mehreren meiner Projekte einsetzbar.
using SignalR.Client._20.Hubs;
using UnityEngine; 
public class SignalRUnityController : MonoBehaviour {
    public bool useSignalR = true;
    public string signalRUrl = "http://localhost:5225/";
 
    private HubConnection _hubConnection = null;
    private IHubProxy _hubProxy;
    private Subscription _subscription; 
    void Start()
    {
        if (useSignalR)
            StartSignalR();
    }
    void StartSignalR()
    {
        if (_hubConnection == null)
        {
            _hubConnection = new HubConnection(signalRUrl);
 
            _hubProxy = _hubConnection.CreateProxy("SignalRSampleHub");
            _subscription = _hubProxy.Subscribe("broadcastMessage");
            _subscription.Data += data =>
            {
                Debug.Log("signalR called us back");
            };
            _hubConnection.Start();
        }
        else
            Debug.Log("Signalr already connected...");
    }
    public void Send(string method, string message)
    {
        if (!useSignalR)
            return;
 
        var json = "{" + string.Format("\"action\": \"{0}\", \"value\": {1}", method, message) + "}";
        _hubProxy.Invoke("Send", "UnityClient", json);
    }
}
Was jetzt noch fehlt ist, dass die Informationen aus der FixedUpdate-Methode im PlayerController in der Send-Methode der SignalRUnityController-Klasse landen.
Dazu reichen uns insgesamt vier Zeilen Code. Das Trickreiche daran ist zum einen, dass wir eine Referenz auf das leere Gabe-Objekt benötigen, an dem das Skript zum Senden der Informationen hängt, zum anderen müssen die Koordinaten vom Unity-Format in Weltkoordinaten umgerechnet werden. Abschließend noch einen JSON-String gebaut und es kann auch schon losgehen.
using UnityEngine;
public class PlayerController : MonoBehaviour {
public Camera cam;
private float maxWidth;
private float maxHeight;
bool isHandOpen = true;
public Sprite clickedSprite;
private Sprite standardSprite;
private SignalRUnityController _signalr;
void Start()
    {
if (cam == null)
            cam = Camera.main;
_signalr = (SignalRUnityController)GameObject.Find("SignalRObject")
            .GetComponent(typeof(SignalRUnityController));
var corner = new Vector3(Screen.width, Screen.height, 0f);
var targetWidth = cam.ScreenToWorldPoint(corner);
float playerWidth = renderer.bounds.extents.x;
float playerHeight = renderer.bounds.extents.y;
        maxWidth = targetWidth.x - playerWidth;
        maxHeight = targetWidth.y - playerHeight;
        standardSprite = this.GetComponent().sprite;
    }
void FixedUpdate()
    {
var currentPosition = cam.ScreenToWorldPoint(Input.mousePosition);
float targetWidth = Mathf.Clamp(currentPosition.x, -maxWidth, maxWidth);
float targetHeight = Mathf.Clamp(currentPosition.y, -maxHeight, maxHeight);
var newPosition = new Vector3(targetWidth, targetHeight, 0f);
        rigidbody2D.MovePosition(newPosition);
if (Input.GetMouseButtonDown(0))
        {
            isHandOpen = false;
            MouseDown();
        }
else if(Input.GetMouseButtonUp(0))
        {
            isHandOpen = true;
            MouseUp();
        }
var worldCoordinates = cam.WorldToScreenPoint(newPosition);
var json = "{" + string.Format("\"x\": \"{0}\", \"y\": \"{1}\", \"handOpen\": \"{2}\"",
            worldCoordinates.x, worldCoordinates.y, isHandOpen) + "}";
        _signalr.Send("Position", json);
    }
private void MouseDown()
    {
Debug.Log("MouseDown");
this.GetComponent().sprite = clickedSprite;
    }
private void MouseUp()
    {
Debug.Log("MouseUp");
this.GetComponent().sprite = standardSprite;
    }
}
Damit Unity auch unsere beiden Referenzen kennt, die in unserem Projekt nun notwendig sind, müssen die beiden DLLs
· Newtonsoft.Json.dll
· SignalR.Client.20.dll
in den Assets-Folder kopiert werden. Ihr findet diese im bin\Debug-Verzeichnis von dem SignalR.Client.20 Projekt.
clip_image046
clip_image048
Bevor ihr in Unity den Client startet, achtet bitte darauf, dass die Web-Applikation mit dem SignalR-Hub läuft, da sonst Unity hängen bleibt und ihr Unity erneut starten müsst. ACHTUNG, falls ihr eure Spielszene nicht zuvor gespeichert habt, dann verliert ihr alle ungespeicherten Schritte.
Wenn die Web-Applikation und der Unity-Client läuft und ihr die Maus bewegt, werden sich die Hände gemeinsam bewegen.
Falls ihr SignalR mit anderen Clients verwenden wollt, dann solltet ihr einen Blick auf die folgenden Native-Implementierungen werfen:
· iOS (3rd Party): https://github.com/DyKnow/SignalR-ObjC
· Android (offizieller Android-Client): https://github.com/SignalR/java-client
· Windows Phone 8/8.1 (offizieller .Net Client): https://github.com/SignalR/SignalR
Für Unity muss noch ein Wrapper um diese Bibliotheken herum geschrieben werden, dazu findet ihr Näheres in der Unity-Dokumentation:

Zusammenfassung


Microsoft SignalR und Unity ergeben die perfekte Lösung für Multiplayer-Spiele. Mit wenigen Schritten ist die Echtzeit-Kommunikation bereit gestellt. Ich bin schon gespannt, welche tollen Spiele ihr mit dieser Kombination bauen werdet. Ich freue mich darauf und hoffe ihr habt genauso viel Spaß wie ich beim Entwickeln eurer Indie-Games!

Ein kurzes Video, wie es aussieht findet ihr hier:
http://www.youtube.com/watch?v=QwaOUHUM2Fk

Den Source-Code findet ihr hier:
https://github.com/BerndtHamboeck/UnitySignalRVS2013


Friday, September 5, 2014

Coefest.at Blog-Eintrag: Spiele-Entwicklung mit Unity und Visual Studio 2013

Ein Unity Spiel entwickelt mit Visual Studio 2013

Die Indie-Spieleentwicklung für die Microsoft Plattform hat mittlerweile auch schon eine längere Geschichte. Als damals das XNA Game Studio auf dem Markt erschienen ist, war das für alle Programmierer, die schon immer Ihr eigenes Spiel entwickeln wollten, sensationell. Monogame hat dann quasi die Weiterentwicklung, bzw. eher die Neuentwicklung von XNA fortgesetzt und hat es uns ermöglicht die Spiele mit Xamarin auch auf iOS und Android zu bringen. Tja, und was soll ich sagen, seit ca. 2 Jahren wird die Geschichte der Spieleentwicklung durch Unity quasi neu geschrieben.
Es macht unheimlichen Spaß sein eigenes Spiel mittels Unity zu entwickeln. Die Scripts wurden zu Beginn noch in JavaScript geschrieben, mittlerweile klappt das hervorragend in C#. Die IDE, die dafür verwendet wurde war bisher MonoDevelop. Das war schon sehr, sehr nett, allerdings ist es für einen eingefleischten C# Entwickler doch noch eine nette Zugabe, wenn das auch in und mit Visual Studio funktioniert.
Und was soll man noch mehr sagen? Das klappt. Dank Microsoft. Durch den Zukauf der Firma SyntaxTree, das ist jene Firma, die ein Produkt mit dem Namen UnityVS entwickelt hatte.
Und jetzt ist es soweit, Visual Studio Tools für Unity wurden durch eben diesen Zukauf von Microsoft released. Ein guter Grund sich das einmal genauer anzusehen. Ich dachte mir ich baue ein kleines Spielchen für zwischendurch.

Das Unity VS 2013 Spiel

Die Idee ist eigentlich recht einfach:
· Wir sehen einen Windows Store.
· Da regnet es Windows Phone’s vom Himmel herunter.
· Diese können mit einer Microsoft Tasse aufgefangen werden, das gibt dann Bonuspunkte.
· Aber leider fallen da auch „böse“ iPhones herunter, werden diese gefangen, dann werden natürlich Punkte abgezogen, da diese der Microsoft Tasse nun einmal gar nicht schmecken.
Probespielen ist jederzeit in eurem Browser möglich: http://hambogames.azurewebsites.net/phoneCatcher.html
image
Ich hoffe ich habe euch etwas neugierig gemacht? Nun denn, lasst uns loslegen.
Für diejenigen, die es gar nicht erwarten können, gibt es den Source Code hier zu finden:
https://github.com/BerndtHamboeck/PhoneCatcher

Implementierung

Um das kleine Spielchen mit mir „mitzuprogrammieren“ benötigt Ihre folgendes:
· Einen Rechner mit Visual Studio 2010, 2012, oder 2013(ich verwende 2013 Update 3).
· Und natürlich eine Installation von Unity, die Testversion erfüllt durchaus unsere Zwecke, diese bekommt Ihr hier:https://unity3d.com/unity/download, ich verwende in diesem Beispiel die Version 4.5.2.
· Visual Studio Tools für Unity von hier: http://unityvs.com/, diese bitte herunterladen und installieren.
· Da Unity einen älteren C#-Compiler verwendet benötigt Ihr auch den .NET Framework 3.5 (wichtig, falls Ihr schon unter Windows 8/8.1 entwickelt).
Das war es dann auch schon, wir können loslegen.

Notwenige Schritte

Damit ihr gleich einmal vorab sehen könnt, was euch in den folgenden Abschnitten erwartet hier eine kleine Zusammenfassung:
· Schritt eins:
o Erstellen des Unity Projektes
o Anlegen der Projektstruktur
o Importieren der Game-Sprites
· Schritt zwei:
o Aufbau der Spielansicht
   Hintergrund-Bild hinzufügen
   Microsoft-Tasse zum Auffangen hinzufügen
   Physik hinzufügen
o Erstes Scripting
   Konfiguration der Visual Studio 2013 Unity Integration
   Erstellen des C#-Scripts zum Bewegen der Tasse
   Implementieren des Scripts
   Debuggen mit Visual Studio 2013
· Schritt drei:
o Fallende Windows Phones und iPhones
o Auffangen der Game Sprites
o Zerstören gefangener, bzw. ‚verlorener‘ Game Sprites
o Re-Spawn neuer Telefone
· Schritt vier:
o Punkte für gefangene Windows Phones
o Abzüge für gefangene iPhones
o Ende des Spiels nach N-Sekunden
o Neustart des Spiels
Das klingt nach einer Menge Arbeit, ist aber mit etwas Geschick, und, falls Ihr Unity schon ein wenig kennt, in circa 2h zu schaffen. Bei Probleme könnt Ihr jederzeit im fertigen Source Code nachsehen.
Erstellen des Unity Projektes
Los geht’s, wir öffnen Unity um ein neues Spiel zu erstellen. Ich bin schon richtig aufgeregt, ich hoffe euch geht es genauso!
Dazu müssen wir:
· Unity öffnen
· Im Project Dialog von Unity ein neues Projekt erstellen mit folgenden Einstellungen
   o Project Location: C:\GameDev\PhoneCatcher
   o Setup Defaults for: 2D
imageimage
Die Einstellung für 2D ist von Vorteil, da es sich ja auch um ein 2D Spiel handeln wird und Unity einige Einstellungen dann bereits vorab für uns (und 2D) optimiert.
Sehr gut, das Projekt sollten wir also nun erstellt haben, als nächstes wollen wir die Dateistruktur anlegen und die Grafiken einbinden, die wir in diesem Spiel verwenden wollen.
Anlegen der Projektstruktur
Da wir einige Dateien im Zuge der Entwicklung des Spiels anlegen werden, empfiehlt es sich eine vernünftige Projektstruktur anzulegen. Wir benötigen in diesem Projekt folgende Ordner:
· Prefabs – wir lassen Objekte den Bildschirm herunter regnen. Diese haben nicht nur ein Aussehen, damit diese fallen können, müssen diese Objekte auch den Regeln der Physik gehorchen – also sprich: fallen. Und nicht nur das, wenn diese kollidieren, dann müssen wir reagieren (es gibt dann Punkte, oder eben nicht). Also lange Rede kurzer Sinn, was sind Prefabs? Objekte, die mehr können als nur hübsch aussehen, also zusammengesetzte Objekte, die wir als einzelnes (neues) Objekt verwenden können.
· Scenes – das sind die einzelnen Screens, die in dem Spiel verwendet werden. Wir erstellen dieses Mal nur einen Screen, aber falls weitere dazukommen, hat schon alles seine Ordnung.
· Scripts – Programmieren wird uns in diesem Spiel nicht erspart werden, und schließlich ist es ja genau das, was einem bei der Spiele-Entwicklung am meisten Spaß macht. Der Code, der plötzlich die „Puppen tanzen lässt“ (bei uns sind es aber Texturen von Mobiltelefonen).
· Sprites – Herr, lass Telefone regnen. Die wir auffangen wollen, ach und einen Hintergrund benötigen wir auch noch. Tja, und diese Texturen kommen in diesen Folder. Geht ganz einfach, machen wir dann gleich als nächstes.
Am Ende sollte das in der „Project“-Ansicht dann so aussehen:
image
Das Vorgehen ist immer das Gleiche, ich zeige es euch für den Sprites-Folder. Ihr klickt mit der rechten Maustaste auf Assets und wählt Create -> Folder.
image
Jetzt gebt Ihr den gewünschten Namen ein image. Das führt Ihr bitte für alle Folder aus, bis alle vorhanden sind. Den Sprites Folder wollen wir gleich einmal befüllen.
Importieren der Game-Sprites
Welche Texturen benötigen wir denn nun genau? Ich würde vorschlagen, wir verwenden für dieses Spiel folgende:
· „Gute“ Telefone, Windows Phones
· „Böse“ Telefone, iPhones
· Einen Becher, mit dem wir die Telefone fangen können
· Einen Hintergrund, damit das Ganze nicht so langweilig aussieht.
Ich habe für euch einige Texturen vorbereitet, wie Ihr weiter unten sehen könnt.
image
Ihr könnt diese aus dem Verzeichnis https://github.com/BerndtHamboeck/PhoneCatcher/tree/master/Assets/Sprites einzeln abspeichern, bzw. aus dem Zip von github herauskopieren. Wie auch immer ihr euch entscheidet, anschließend zieht ihr diese in den Sprites Folder, das müsste dann so aussehen:
image
Sehr gut, das Spiel nimmt langsam etwas Formen an, sicherheitshalber speichern wir die momentane (noch leere) „Scene“ gleich einmal ab, solltet Ihr eine kurze Pause benötigen, dann könnt Ihr das Projekt danach jederzeit wieder laden. Das geht ganz schnell, durch File -> Save Scene und wir geben dieser den Namen ‚Main‘. Sieht am Ende so aus:
image
Gut, jetzt wollen wir unsere Spielszene einmal mit Objekten bevölkern.

Aufbau der Spielansicht

Beginnen wir mit dem Einfügen der Microsoft Tasse. Dazu zieht ihr diese aus der Assets -> Sprites Folder Ansicht in die „Hierarchy“ Ansicht. In dieser Ansicht sind alle Objekte angezeigt, die in der momentanen Scene verwendet werden. Bisher ist dort nur die „Main Camera“ zu sehen.
Die Kamera ist dafür da, dem Spieler eure Szene zu zeigen. Dazu kann die Kamera statisch über der Szene angebracht sein (wie in diesem Spiel) oder z. B. einem Charakter folgen, wie das z. B. in einem Jump & Run der Fall wäre. In einer Szene können auch mehrere Kameras vorhanden sein, diese können auch gleichzeitig im Spiel angezeigt werden, wie das in einem Spiel mit Split-Screen der Fall wäre, oder z. B. in einem Rennspiel für eine kleine Übersichtskarte der Rennstrecke.
image
Die Kamera, ist nun nicht ideal positioniert, die Tasse sollte weiter unten zu sehen sein. Wir wollen die Positionierung dieser nun im „Inspector“ noch individuell anpassen. Und zwar wählen wir dafür die „Main Camera“ in der Hierarchy an und ändern die Y Position auf 4.5.
image
Im nächsten Schritt fügen wir das Hintergrundbild hinzu. Dazu ziehen wir wiederum aus Assets -> Sprites den Background in die „Hierarchy“ Ansicht.
image
Das Resultat ist, nun ja, nennen wir es, durchwachsen. Das Bild ist nicht ganz passend, darum sollten wir die Größe und Positionierung anpassen. Dazu gibt es 2 Möglichkeiten, die erste Variante ist, dass der Background Sprite angewählt wird und im Inspector der Wert „Pixels to Units“ angepasst wird. Achtung, erst nachdem Apply gedrückt wird, wird die Änderung sichtbar. Ihr könnt gerne mit den Werten experimentieren, 150 dürfte für unsere Belange genügen.
imageimage
Die zweite Variante wäre gewesen, das Background Objekt in der Scene anzuwählen und die Eigenschaften im Abschnitt „Scale“ anzupassen. Das ist nun nicht mehr notwendig könnt ihr aber ebenfalls gerne einmal ausprobieren. Ihr solltet die Position so anpassen, dass der Hintergrund passend in der Scene zu sehen ist.
image
Soweit so gut, jetzt ist allerdings unsere Tasse verschwunden. Wir sollten Unity mitteilen, in welcher Reihenfolge Objekte zu zeichnen sind. Dafür gibt es den „Sorting Layer“, der steht auf „Default“ (im oberen Bild sehr schön zu sehen), sowohl am Background Objekt, als auch an der Tasse. Um weitere Layer für die Sortierung einzufügen, klickt auf die kleinen Pfeilchen rechts neben Sorting Layer im Sprite Renderer und wählt „Add Sorting Layer“.
image
Wir benötigen zwei weitere Layer. Einen für den Hintergrund (das Hintergrundbild) und einen weiteren für den Vordergrund (die Tasse).
image
Sobald die beiden Layer angelegt wurden, sollten diese zugewiesen werden, dazu wählt ihr zuerst den Background an und weist den „Background“ Layer für die Eigenschaft Sorting Layer zu.
imageimage
Das Gleiche für die Tasse, allerdings den „Foreground“ Layer als Sorting Layer.

Physik ermöglichen

Damit sich Objekte physikalisch realistische verhalten können, steht in Unity für 2D Spiele die Rigidbody2D-Komponente zur Verfügung. Solche ‚gepimpten‘ Objekte sind in einer Szene nicht statisch, sondern fallen von oben nach unten, oder können durch Kollisionen, bzw. Scripte bewegt werden. Und das ist es eigentlich, was wir mit unserer Tasse machen wollen. Diese von links nach rechts bewegen, das herunterfallen müssen wir unterbinden, das bleibt dann später unseren Telefonen vorbehalten.
imageimageimage
Wir aktivieren die Eigenschaft „Is Kinematic“, diese deaktiviert die Physik des Rigidbodies, das Objekt kann nur noch über ein Script bewegt werden. Das ist genau was wir wollen, wir kümmern uns selbst um die Bewegung des Bechers, nämlich darf er sich nach links, oder rechts bewegen.
image
Der nächste Schritt ist nun das Script zu erstellen, welches auch wirklich die Bewegung ausführt.

Erstes Scripting mit Visual Studio 2013

Unity installiert mit MonoDevelop einen eigenen Code-Editor mit. Das ist an und für sich schon toll, wir wollen allerdings Visual Studio verwenden, dazu muss wie eingangs erwähnt Visual Studio Tools für Unity installiert sein, das war es aber noch nicht ganz. Es sind noch weitere Schritte notwendig.

Konfiguration der Visual Studio 2013 Unity Integration

Zuerst muss eine Plattform unter der das Spiel abläuft eingestellt werden, wo die Kommunikation zwischen Unity und dem Editor - also Visual Studio – reibungslos funktionieren kann, das ist als Plattform image. Der Web Player beispielsweise klappt nicht reibungslos, also führen wir diese Einstellungen gleich einmal durch, das macht Ihr über File->Build Settings und wählt PC, Mac & Linux Standalone, Target Plattform ‚Windows‘, aktiviert Development Build und Script Debugging. Abschließend schließt ihr einfach das Fenster.
imageimage
Im zweiten Schritt müsst Ihr das Visual Studio Unity Package installieren. Mit Unity Packages bietet Unity ein Werkzeug, um einzelne Dateien, Objekte, Texturen, bis hin zu ganzen Scenes zu bündeln, die man dann z. B. an jemanden versenden und dann wieder in ein anderes Projekt importieren kann. Und genau das haben die Kollegen von SynaxTree für uns getan, die Visual Studio Integration wird durch ein Unity Package unterstützt und dieses müssen wir zu unserem Projekt hinzufügen. Das geht mit Asset -> Import Package -> Visual Studio 2013 Tools, im neuen Fenster klickt Ihr auf Import.
imageimage
Das war es schon, ob es geklappt hat, sehen wir im nächsten Schritt, wenn wir ein Script erstellen und dieses sich in Visual Studio 2013 öffnen soll.

Skript erstellen

Das Script ist in Unity erst einmal nichts weiter als eine weitere Komponente. Ihr wählt also die Tasse an und drückt im Inspector wieder den imageButton. Die Komponente ist ein „New Script“ mit dem Namen MSCupLogoController und als Sprache verwenden wir CSharp (und nicht JavaScript).
imageimage
Ich würde euch empfehlen das Script in den Scripts-Folder mit Drag & Drop zu verschieben, das Script landet per Default im Assets Folder. Das sollte bei euch dann so aussehen, wie in dem folgenden Bild: Links ist der Scripts Folder angewählt, darin ist unser Script (mittlerer Pfeil) und rechts sieht man, dass das Script aktiv ist und der Tasse zugewiesen ist.
image
Nun wird es spannend, klickt mit der Maus doppelt auf das Script (mittlerer Pfeil). Wenn alles richtig konfiguriert ist, dann sollte sich Visual Studio mit dem von uns erstellten Script öffnen.
imageimage
Solltet Ihr diese Meldung sehen (nachfolgend), dann habt Ihr das Unity Package für Visual Studio nicht hinzugefügt, diese Meldung würde auch erscheinen, wenn Ihr ein anderes Unity Projekt – möglicherweise ein Beispielprojekt aus dem Internet, oder ein komplett neues (wie in meinem Fall auf dem Bild) – öffnet:
image
Die Integration mit Visual Studio könnt Ihr bei Bedarf auch wieder ausschalten, und zwar über das Menü Visual Studio Tools -> Configuration, dann wird wieder MonoDevelop als IDE verwendet.
imageimage
Ich gehe einmal davon aus, dass die Integration zwischen Unity und Visual Studio klappt und Ihr nun vor einem Script-Editor in Visual Studio sitzt. Ok, dann wollen wir die Tasse bewegen!

Implementieren des Scripts

In diesem Script werden wir zwei Methoden implementieren:
· Start – diese wird von Unity für jedes Objekt einmal aufgerufen, hier wollen wir uns die Bildschirmgröße merken, sowie die Breite der Tasse (das Script ist der Tasse zugewiesen, dadurch ist der Zugriff sehr einfach möglich) von der maximalen Breite abziehen. Warum? Erfahrt Ihr im nächsten Schritt.
· FixedUpdate – wird periodisch zu einem fixen Intervall aufgerufen und ist dafür gedacht physikalische Objekte anzupassen. Wir wollen die x-Achsenposition anpassen, passend zur Mausposition. Allerdings darf die Tasse dabei nicht aus dem Bild herausfallen, also die maximale Breite nicht über, bzw. unterschreiten (und darum das Abziehen von der Breite der Tasse, diese würde sonst aus dem Blickfeld des Spielers verschwinden).
Das Script sollte im Endeffekt so wie folgt aussehen, ich habe eine public Variable mit dem Namen cam eingefügt. Public Variablen können in Unity selbst, z. B. durch Drag & Drop zugewiesen werden. Ich benötige diese Variable eigentlich nicht (es ist die aktuelle Kamera), sie wird sich aber im nächsten Schritt als sehr nützlich erweisen.
using UnityEngine;
public class MSCupLogoController : MonoBehaviour
{ 
    public Camera cam;
    private float maxWidth;
    void Start ()
    {
        if (cam == null)
            cam = Camera.main;
 
        var corner = new Vector3(Screen.width, Screen.height, 0f);
        var targetWidth = cam.ScreenToWorldPoint(corner);
        float playerWidth = renderer.bounds.extents.x;
        maxWidth = targetWidth.x - playerWidth;
    } 
    void FixedUpdate ()
    {
        var currentPosition = cam.ScreenToWorldPoint(Input.mousePosition);
        float targetWidth = Mathf.Clamp(currentPosition.x, -maxWidth, maxWidth);
        var newPosition = new Vector3(targetWidth, 0f, 0f);
        rigidbody2D.MovePosition(newPosition);
    }
}
Ihr müsst in Visual Studio nichts kompilieren, sobald Ihr zu Unity zurück wechselt wird automatisch für euch übersetzt und etwaige Fehler werden sofort angezeigt. Ihr könnt auch z. B. in Unity kontrollieren, ob die public Variable cam im Editor auftaucht:
image
Ich hoffe euer Code ist fehlerfrei, denn wir wollen gleich einmal sehen, wie die Tasse über den Bildschirm gleitet (zumindest von links nach rechts).
Starten des Spiels
Am Besten versuchen wir es gleich einmal. Wechselt zurück zu Unity und drückt den Play Button.
image
Und es sollte losgehen (wobei das Ergebnis noch nicht wirklich den Anspruch erhebt ein Welthit zu werden). Die Ansicht wechselt auf „Game“ – von zuvor „Scene“. Mit der Maus könnt Ihr die Tasse von links nach rechts bewegen, diese fällt dabei nicht aus dem Bild (weder links, rechts, noch oben, oder unten).
image
Wichtig ist, dass Ihr nun keine Änderungen an Objekten vornehmt, da diese Änderungen nur während der Laufzeit Gültigkeit hätten. Das ist zwar praktisch, um Änderungen sofort im Spiel zu sehen, aber wie gesagt, diese würden verloren gehen, wenn ihr den Stopp Button drückt (der das Spiel beendet). Solltet ihr also etwas anpassen wollen, weil euch beim Testen des Spiels etwas auffällt, bitte immer zuerst den Stopp Button drücken und erst danach die Änderungen durchführen, dann bleiben diese natürlich erhalten.

Debuggen mit Unity

Visual Studio ist wie Ihr bestimmt wisst ein sehr mächtiges Werkzeug und eines der Stärken ist der eingebaute Debugger. Um diesen mit Unity zu nutzen, setzt Ihr am Besten einmal einen Breakpoint (die if-Abfrage für die cam Variable bietet sich an) und danach imagedrücken.
image
Nun wechselt ihr zurück zu Unity und drückt den Play Button.
image
Das Spiel startet und ihr läuft auf den Breakpoint, setzt das Programm fort (F5-Taste) und ihr könnt wieder die Tasse bewegen.
image
Die Tasse bewegt sich, jetzt wollen wir uns der herabfallenden Telefone widmen, diese müssen erzeugt, gefangen und zerstört werden.

Fallende Windows Phones und iPhones

Jetzt wollen wir einmal etwas “Action” in unser Spiel bringen. Dazu ziehen wir einmal eines der Telefone aus dem Assets->Sprites Folder auf den Hierarchy Bereich. Damit wird das Telefon zu unserer Spielscene hinzugefügt.
Ich habe im Inspector folgende Werte angepasst:
· Damit das Windows Phone am oberen Bildschirmrand angezeigt wird - Y: 8
· Die Textur der Telefone ist etwas zu gross, das wollen wir kleiner haben, ähnlich wie bei dem Hintergrund, dieses Mal aber mit der anderen Variante, nämlich durch setzen von Scale Y - Y: 0.75
· Das Telefon muss auf den richtigen Layer – so wie die Tasse zuvor, also Sorting Layer: Foreground
Jetzt müssen wir dem Telefon wieder das Fallen beibringen, also fügen wir wieder einen RigidBody 2D zu dem Telefon-Objekt hinzu. Wieder, so wie (zuvor an der Tasse) mit dem imageButton im Inspector am Telefon-Objekt.
imageimage
Am Besten ihr versucht gleich wieder einmal das Spiel zu starten, das Telefon wird herunterfallen, wenn ihr versucht das Telefon mit der Tasse zu fangen wird aber noch nichts passieren, das Telefon fällt vor eurer Tasse herunter, das sieht so aus, wie in dem Bild links, sollte aber so aussehen, wie im Bild rechts:
imageimage
Damit das Telefon hinter der Tasse erscheint, setzen wir den Wert an dem Tassen-Objekt für Order in Layer: 1
image

Auffangen der Game Sprites

Die Schwerkraft haben wir jetzt auch schon einmal in unserem Spiel kennen gelernt. Jetzt wollen wir Objekte miteinander kollidieren lassen. Und zwar unsere Telefone mit der Tasse, oder besser gesagt - in Unity-Sprache - zwei Rigidbodies, die aufeinanderstoßen (beim Auffangen, also wenn das Telefon in der Tasse verschwindet). Beim „Zusammenprall“ sollen diese ein physikalisch korrektes Kollisionsverhalten aufweisen, diese müssen, damit das in Unity klappt beide einen sogenannten Collider zugewiesen haben. Wir verpassen also erst einmal unserem Telefon einen Box Collider 2D– wieder mittels image- unter Physics 2D findet sich (unter anderem) der Box Collider 2D. Wenn das geklappt hat, erhält unser Telefon-Objekt in der Scene einen grünen Rahmen. Das passt hervorragend, da das genau der Bereich ist, bei dem eine Kollision mit einem anderen Objekt erkannt werden soll.
imageimageimage
Das war der erste, einfachere, Teil. Jetzt müssen wir der Tasse ebenfalls einen Collider zuweisen. Besser gesagt eigentlich zwei. Wenn das Telefon gefangen wird, dann wollen wir es ‚verschlucken‘, wenn das Telefon allerdings mit der Außenseite des Bechers getroffen wird, dann muss das Telefon abprallen.
Ein Box Collider 2D hilft uns in diesem Fall nicht weiter, wir benötigen eine andere Art von Collider, einen, den wir nach unseren Wünschen formen können. Einen Edge Collider 2D. Fügt diesen nun bitte wiederum mit image hinzu - unter Physics 2D findet ihr den Edge Collider 2D.
Dieser ist als grüne Linie an der Tasse zu sehen. Wenn ihr die Shift-Taste gedrückt haltet, dann könnt ihr den grünen Strich nach euren Wünschen „formen“. Der Collider sollte den Außenbereich der Tasse trapezförmig umschließen. Das erfordert ein klein wenig Geduld und etwas Übung, aber ich bin guter Dinge, dass das am Ende so aussieht, wie bei mir im rechten Bild.
imageimageimage
Am Besten ihr versucht das gleich wieder einmal, wenn ihr das Telefon mit der Aussenseite erwischt, dann müsste es (physikalisch korrekt) weggeschleudert werden. In dem nachfolgenden Bild habe ich die Tasse von rechts nach links verschoben und dabei das Telefon mit der Aussenkante erwischt, sodass diese nach links weggeschleudert wird.
image
Fügt danach noch einen zweiten Edge Collider 2D hinzu, an diesem aktiviert ihr „Is Trigger“, da dieser Collider für den Innenbereich der Tasse dient. Wenn ein Telefon mit diesem Edge Collider 2D kollidiert, dann soll ein Script ausgeführt werden, welches das gefangene Telefon zerstört.
image
Verbreitert den zweiten Edge Collider 2D etwas und schiebt ihn etwas höher. Solltet ihr Probleme haben mit dem Bearbeiten des zweiten Edge Colliders, dann könnt ihr den ersten einstweilen mit der Checkbox links oben deaktivieren.
image
Kollidieren und Fangen von Telefonen ist nun theoretisch möglich.

Zerstören gefangener Telefone

Jetzt benötigen wir ein neues Script. Wenn das Telefon gefangen wurde, soll es zerstört werden.
Also, wiederum ist der Button imageunser Freund und wir wählen Script mit dem Namen DestroyOnContact – ihr solltet das Script wieder in den Scripts-Folder verschieben, da es per Default unter Assets angelegt wird. Wichtig ist, dass das neue Script an der Tasse zu sehen ist.
image
Fehlt nur noch der Code, der ist recht kurz gehalten, da Unity die komplette Arbeit für uns erledigt. Durch das Anwählen der „Is Trigger“ CheckBox am Edge Collider 2D wird das Script aufgerufen – genauer die OnTriggerEnter2D Methode. Praktischerweise bekommen wir auch das Objekt, das mit der Tasse zusammenstößt, unser Telefon, mitgeliefert. Dieses wird kurzerhand aus dem Spiel genommen.
using UnityEngine;
using System.Collections;
 public class DestroyOnContact : MonoBehaviour {
     void OnTriggerEnter2D(Collider2D second)
    {
        Destroy(second.gameObject);
    }
}
Das solltet ihr gleich wieder „Probespielen“. Gefangene Telefone müssen sich jetzt in Luft auflösen. Langsam wird es ja ein richtiges Spiel. Wir haben allerdings noch ein Problem zu lösen. Was passiert mit dem Telefon, wenn es nicht gefangen wird?

Zerstören verpasster Telefone

Nun, nicht gefangene Telefone fliegen unendlich lange, immer weiter und weiter. Das ist natürlich nicht gut, denn wenn wir viele Telefone erzeugen würden – wir wollen schließlich noch mehr als ein Telefon auffangen, es soll ja ein richtiges Spiel werden – würden wir eine Menge Speicher und Ressourcen für die Kalkulation der unendlich fallenden Game-Objekte verbraten.
Wir sind uns also einig, dass verfehlte Telefone zerstört werden müssen. Dazu fügt ihr ein leeres Game-Objekt ein. Das ist ein beliebter Trick in Unity, wenn man nicht sichtbare Objekte in seinem Spiel benötigt, oder Objekte gruppieren möchte. Eingefügt wird es über das Menü GameObject und Create Empty.
image
Ändert den Namen von imageauf imageund weist ihm einen Edge Collider 2D zu, wiederum mit der Eigenschaft „Is Trigger“ aktiviert. Ausserdem zieht ihr das zuvor erstellte Script DestroyOnContact aus dem Asset->Scripts Folder auf das Cleanup-Objekt, da wir dieses Script gleich wiederverwenden wollen (es funktioniert tadellos, also warum ein neues Script schreiben).
image
Soweit so gut, der Edge Collider 2D gehört noch angepasst, dieser sollte außerhalb, mit einem gewissen Respektabstand zur eigentlichen Spielfläche platziert werden - dem Spieler soll hier gar nicht auffallen, dass wir die Telefone zerstören. Wir benötigen den Edge Collider 2D links, unterhalb und rechts der Spielfläche, da das Telefon ja abprallen kann und relativ weit weggeschleudert werden könnte (darum ist der Edge Collider 2D links und rechts auch etwas höher als das Spielfeld). Das sollte im Endeffekt dann ungefähr so aussehen:
image
Wenn ihr das Spiel wieder startet und das Telefon nicht auffängt, dann fällt es zwar aus dem Schirm, kollidiert aber mit dem neuen Edge Collider 2D und wird zerstört. Das wird ersichtlich, wenn ihr die Hierarchy-Ansicht beobachtet, dort verschwindet das Telefon-Objekt, sobald es zerstört wurde.

Re-Spawn neuer Telefone

Ein einzelnes Telefon, welches eingefangen werden kann ist nicht gerade ein spektakuläres Spiel. Es soll mehrere Telefone in verschiedenen Abständen „vom Himmel regnen“. D. h. wir müssen unser Telefon mehrmals erzeugen. Um ein Objekt in einem Spiel mehrfach nutzen zu können, stellt Unity sogenannte Prefabs zur Verfügung. Ein Prefab stellt ein beliebiges, möglicherweise aus mehreren Komponenten zusammengesetztes Objekt dar. Das klingt ganz nach dem Telefon-Objekt, welches wir bereits erstellt haben, also erstellen wir ein „Prefab“ aus unserem Telefon. Dazu zieht ihr das Telefon aus der Hierarchy-Ansicht in den Prefabs-Folder.
image
Fertig, wir haben nun ein Prefab, welches alle Eigenschaften unseres Telefons aufweist, dieses können wir beliebig oft erzeugen. Das Telefon-Objekt in der Hierarchy-Ansicht sollte übrigens weggelöscht werden, da wir dieses nicht mehr benötigen – wir haben nun das Prefab-Objekt, welches wir in unserem Script „on-the-fly“ erzeugen werden. Um das bewerkstelligen zu können, benötigen wir wiederum ein leeres Game-Objekt, dem wir den Namen GameControllervergeben und ein neues Script mit dem Namen GameController mittels imagezuweisen. Nicht zu vergessen ist die Y-Position von 9, da im folgenden Script zwar die X-Position zufällig – innerhalb der erlaubten Grenzen – bestimmt wird, die Y-Position aber vom GameController verwendet wird und diese außerhalb des Bildschirms liegen sollte, damit die Telefone in den Bildschirm fliegen.
image
Dieses neue Script hat als Herzstück die Spawn Methode, diese instanziiert neue Telefone und zwar im Intervall zwischen ¼ Sekunde und ½ Sekunde.
using UnityEngine;
using System.Collections; 
public class GameController : MonoBehaviour
{
    public Sprite[] sprites;
    public Camera cam;
    public GameObject phone;
    private float maxWidth;
    public float timeLeft = 30;
    void Start()
    {
        if (cam == null)
            cam = Camera.main;
        var corner = new Vector3(Screen.width, Screen.height, 0f);
        var targetWidth = cam.ScreenToWorldPoint(corner);
        float phoneWidth = phone.renderer.bounds.extents.x;
        maxWidth = targetWidth.x - phoneWidth;
        StartCoroutine(Spawn());
    }
 
    IEnumerator Spawn()
    {
        yield return new WaitForSeconds(2f);
        while (timeLeft > 0)
        {
            var spawnPosition = new Vector3(
                Random.Range(-maxWidth, maxWidth), transform.position.y, 0f);
            var spawnRotation = Quaternion.identity;
            Instantiate(phone, spawnPosition, spawnRotation);
            var spriteNum = Random.Range(0, sprites.Length - 1);
            phone.GetComponent().sprite = sprites[spriteNum];
            yield return new WaitForSeconds(Random.Range(0.25f, 0.5f));
        }
        yield return new WaitForSeconds(2f);
    }
}
Nun müssen alle Variablen in Unity zugewiesen werden:
· Sprites haben wir insgesamt 9 Stück – 6 Windows Phones, 3 iPhones
· Phone ist unser erstelltes Prefab
Die Variablen können durch Drag & Drop zugewiesen werden. Die Elemente 0 – 8 für die Sprites kommen aus dem Sprites-Folder. Achtet bitte darauf, dass die ersten drei Sprites die iPhones sind, das wird sich später bei der Punkte-Vergabe als hilfreich erweisen.
image
Der Phone Variable wird das Prefab, ebenfalls durch Drag & Drop zugewiesen:
image
Tja, was soll ich sagen, wir sollten das Spiel wieder einmal testen. Jetzt ist es ja auch fast schon ein richtiges Spiel, in unregelmäßigen Abständen fällt ein neues Telefon „vom Himmel“, welches wir mit der Tasse fangen können.

Spiel-Anzeigen und Neustart

Bisher ist es noch ein „Endlosspiel“, das heißt ihr könnt so lange spielen, bis ihr einfach keine Lust mehr habt. Das ist, nun ja, „nett“, und was noch dazu kommt, es gibt keinerlei Punkte für gefangene Windows Phones und keine „Schlechtpunkte“ für gefangene iPhones. Also ich finde, das sollten wir ändern.
Wir bauen zuerst die Anzeigen ein, die wir benötigen:
· Eine Anzeige für die verbleibende Zeit.
· Eine weitere Anzeige für die erzielten Punkte.
· Die verbleibende Spielzeit soll ebenfalls angezeigt werden.
· Abschließend noch eine weitere für einen „Game Over“-Text.
Das Ganze soll also zu einem richtigen Spiel „wachsen“ und sollte ungefähr so aussehen:
image
Dazu benötigen wir 4 neue Game Objekte und zwar vom Typ „GUI Text“. Diese fügt ihr mit Create -> GUI Text im Hierarchy Fenster hinzu (oder über das Menü GameObject->Create Other-> GUI Text).
imageimage
Das Ganze macht ihr somit insgesamt 4 Mal und gebt den Objekten bitte sprechende Namen, ich habe mich für die Namen: Timer Info, Score Info, GameOver Info und Restart Info entschieden.
image
Die Einstellungen für die ersten Beiden (Timer Info und Score Info) sind relativ simpel, ein vernünftiger Text und die richtige Platzierung und es ist geschafft. Einen Vorschlag könnt ihr untenstehend sehen.
imageimage
Ähnlich verhält es sich mit dem GUI Text für das GameOver Info Objekt. Zusätzlich wird dies noch unsichtbar geschaltet, da es erst sichtbar sein soll, wenn das Spiel auch tatsächlich zu Ende ist.
Genauso ist es bei dem GUI Text Restart Info Objekt, hier wird zusätzlich zum unsichtbar schalten noch ein weiteres Script benötigt – mit dem Namen Restart - mit dem das Spiel gestartet werden kann (nämlich dann, wenn der Text sichtbar ist und der Spieler auf dieses Objekt klickt).
imageimage
Das Restart-Script lädt den aktuellen Level erneut, dadurch werden alle Werte zurückgesetzt und der Spass kann von neuem beginnen.
using UnityEngine;
public class Restart : MonoBehaviour {
    void OnMouseDown()
    {
        Application.LoadLevel(Application.loadedLevel);
    }
}

Punkte-Vergabe

In diesem Spiel soll es Punkte für gefangene Windows Phones geben, nämlich genau einen, allerdings auch einen Punkteabzug, wenn ein iPhone in der Tasse landet. Dazu fügen wir ein Script mit dem Namen Score unserer Tasse hinzu.
imageimage
In diesem Skript wird jedes Mal, wenn der Trigger an dem Edge Collider 2D ausgelöst wird festgestellt, ob es ein „gutes“ Telefon ist, oder ein „böses“ Telefon. Nur der „innere“ Edge Collider 2D löst an der Tasse den Trigger aus, also sind das wirklich nur gefangene Telefone, nicht diejenigen die einfach nur an der Tasse abprallen. Je nachdem, werden dann Punkte aufaddiert, oder abgezogen und die Anzeige aktualisiert. Dazu gibt es eine public Variable mit dem Namen scoreText.
using UnityEngine;
public class Score : MonoBehaviour
{
    public GUIText scoreText;
    public int catchValue = 1;
    private int score = 0;
    void Start()
    {
        score = 0;
        UpdateScore();
    }
    void OnTriggerEnter2D(Collider2D second)
    {
        Debug.Log("Tag on trigger: " + second.tag);
        if (second.tag == "Good")
            score += catchValue;
        else
            score -= catchValue;
        UpdateScore();
    }
    private void UpdateScore()
    {
        scoreText.text = "Score: " + score.ToString();
    }
}
Diese wird mit dem GUI Text Score Info Objekt – wiederum in Unity - verbunden.
image
Die Tags, die in dem Script verwendet werden, müssen allerdings noch definiert werden, dazu ist unser Prefab anzuwählen und die ComboBox für die Tags herunterzuklappen und „Add Tag…“ anzuwählen. Die beiden Tags, die wir benötigen sind: Good und Bad.
image
Jetzt müssen wir beim instanziieren eines neuen Telefons noch den Good/Bad Tag setzen. Wenn die Nummer in unserem Sprite-Array für die Telefone kleiner als 3 ist, dann ist es ein iPhone (Bad), ansonsten ein Windows Phone (Good). Dazu reicht eine Zeile Code im GameController Script in der Spawn Methode.
phone.GetComponent().sprite = sprites[spriteNum];
phone.tag = spriteNum < 3 ? "Bad" : "Good";
yield return new WaitForSeconds(Random.Range(0.25f, 0.5f));
Ich würde vorschlagen, dass jetzt wieder eine Runde probe spielen angesagt wäre. Schließlich gibt es jetzt schon Punkte abzustauben!

Ende des Spiels nach N-Sekunden

Das Spiel sollte nach, nun sagen wir einmal, 30 Sekunden beendet werden. Sobald das Spiel zu Ende ist, werden die beiden GUI Text Objekte GameOver Info und Restart Info eingeblendet. Dazu müssen wir unser GameController-Script erweitern.
using UnityEngine;
using System.Collections; 
public class GameController : MonoBehaviour
{
    public Sprite[] sprites;
    public Camera cam;
    public GameObject phone;
    private float maxWidth;
    public float timeLeft = 30; 
    public GUIText timerText;
    public GUIText gameOverText;
    public GUIText restartText; 
    void Start()
    {
        if (cam == null)
            cam = Camera.main;
        var corner = new Vector3(Screen.width, Screen.height, 0f);
        var targetWidth = cam.ScreenToWorldPoint(corner);
        float phoneWidth = phone.renderer.bounds.extents.x;
        maxWidth = targetWidth.x - phoneWidth;
        StartCoroutine(Spawn());
        UpdateTimerText();
    }
    void FixedUpdate()
    {
        timeLeft -= Time.deltaTime;
        if (timeLeft > 0)
            UpdateTimerText();
    }
 
    private string UpdateTimerText()
    {
        return timerText.text = "Time Left: " + 
                Mathf.RoundToInt(timeLeft).ToString();
    }
 
    IEnumerator Spawn()
    {
        yield return new WaitForSeconds(2f);
 
        while (timeLeft > 0)
        {
            var spawnPosition = new Vector3(
                Random.Range(-maxWidth, maxWidth), transform.position.y, 0f);
            var spawnRotation = Quaternion.identity;
 
            Instantiate(phone, spawnPosition, spawnRotation);
            var spriteNum = Random.Range(0, sprites.Length - 1);
            phone.GetComponent().sprite = sprites[spriteNum];
            phone.tag = spriteNum < 3 ? "Bad" : "Good";
            yield return new WaitForSeconds(Random.Range(0.25f, 0.5f));
        }
        yield return new WaitForSeconds(2f);
        gameOverText.enabled = true;
        restartText.enabled = true;
    }
}
Fast geschafft, die drei neuen public Variablen wollen noch in Unity durch Drag & Drop befüllt werden.
image
Das war’s das Spiel ist fertig und wartet nur darauf von euch gespielt zu werden!

Zusammenfassung

Die Spiele-Entwicklung mit Unity macht einen Riesenspaß. Die Erfolge stellen sich sehr schnell ein. Mit Visual Studio 2013 ist es noch einmal einen Tick einfacher (für C#- Entwickler) geworden Spiele zu entwickeln. Ich bin gespannt, ob es euch gelingt einen Welthit mit dieser fantastischen Entwicklungs-Kombi zu programmieren. Ich wünsche euch auf jeden Fall alles Gute und viel Spaß mit der Entwicklung eures Spiels!

CSharpCodeFormatter