Jump to content
Unity Insider Forum

Wir bauen uns ein Space Shoot em Up [Teil 15 - HUD]


Mark

Recommended Posts

Wenden wir uns der UI zu, wir wollen darstellen wie es um unser Schiff bestellt ist, was bedeutet dass wir folgendes anzeigen werden:

 

Lebenspunkte

Schildstatus

 

Jeder kann natürlich nach belieben mehr hinzufügen.

 

Starten wir indem wir uns ein neues Script mit dem namen "HUD" erstellen. Fügt nun folgendes hinzu:

 

public Destructable Player;
private Shild shild;

 

Wir werden, nachdem wir das Script auf die MainCamera gelegt haben, unser Spieler Raumschiff auf den Player Slot zuweisen. Damit wissen wir quasi immer über den Zustand des Spielers bescheid. Die Shield Komponente werden wir uns in der Update Methode suchen wenn wir noch keins zugewiesen haben:

 

if (!shild && Player)
{
shild = Player.GetComponent<Shild>();
}

 

Fügt jetzt die Methode hinzu welche unsere UI darstellen soll, Unity wird diese automatisch ausführen:

 

void OnGUI()
{
}

 

Als Anfang werden wir unsere Lebenspunkte darstellen:

 

if (!Player)
{
return;
}

GUILayout.BeginHorizontal();
GUILayout.Space(20);
GUILayout.Label("Lebenspunkte: " + Player.CurrentHealthPoints + "/" + Player.MaxHealthPoints);
GUILayout.EndHorizontal();

 

4 neue Methoden und alle ufern in GUILayout, Unity bietet diverse möglichkeiten eine UI darzustellen, alle bisherigen (bis Unity 4.5) sind mieserabel und jeder sollte entweder das neue UI System in Unity 4.6 (Beta) benutzen oder eines der vielen verfügbaren UI Systeme im AssetStore.

 

GUILayout aber werden wir benutzen da es uns erlaubt auf einfache Art und Weise zu erstellen, automatisches Layouting ist der Kernpunkt von GUILayout, die Positionierung wird ganz Unity überlassen.

 

BeginHorizontal startet eine Horizontale Anreihung bon GUI Elementen, welche mit EndHorizontal wieder beendet wird. Wir wollen also einige UI Controls horizontal anordnen. Bisher sind das ein Space, ein Leerraum mit 20 Pixeln Breite und ein Label welches unsere aktuellen Lebenspunkte anzeigen soll.

 

Nun wollen wir noch anzeigen wie unser Schildstatus ist wenn wir denn einen haben. Fügt also folgendes vor GUILayout.EndHorizontal ein:

 

GUILayout.FlexibleSpace();

if (shild)
{
GUILayout.Label("Schild: " + shild.CurrentShild + "/" + shild.MaxShild);
}
GUILayout.Space(20);

 

FlexibleSpace sorgt dafür dass der verbliebende Platz zum Ende der maximalen Breite (Bildbreite in diesem Fall) ausgefüllt wird.

Was dazu führt dass unser Label für unser Schild auf der rechten oberen Seite angezeigt wird.

 

Wir sollten uns noch eine Art Radar aufbauen, dazu benötigen wir alle aktiven Feinde. Wir haben bereits den WaveManager an dem sich die Feinde registrieren, leidr geschieht dies ohne dass angegeben wird wer genau sich am WaveManager registriert und deregistriert.

 

Wir ändern dies nun, öffnet den WaveManager und fügt folgendes using hinzu:

 

using System.Collections.Generic;

 

Dieses namespace enthält einige Generische Kollektionen, zB ein dynamisches Array was einen bestimmten Typen enthalten darf, dynamisch weil es wachsen und schrumpfen darf.

 

Fügt nun dieses dynamische Array als Variable hinzu:

 

private List<WaveEnemy> enemies = new List<WaveEnemy>();

 

Um diese List zu befüllen erweitern wir nun die RegisterEnemy Methode, aus:

 

public void RegisterEnemy()
{
remainingEnemies++;
}

 

machen wir:

 

public void RegisterEnemy(WaveEnemy enemy)
{
enemies.Add(enemy);
}

 

Wir benutzen und benötigen remainingEnemies nicht mehr, da wir diese Information direkt aus der enemies List erfahren können, entfernt daher

diese Variable komplett aus dem WaveManager. Die einzige Methode die darauf zurückgreift ändern wir nun, ändert die UnregisterEnemy Methode zu folgendem:

 

public void UnregisterEnemy(WaveEnemy enemy)
{
enemies.Remove(enemy);
if (enemies.Count == 0)
{
	SpawnNewWave();
}
}

 

Wir entfernen unseren Feind einfach wieder aus der List und prüfen wie zuvor die Anzahl verbliebender Gegner.

 

Nun benötigen wir nur noch eine Möglichkeit um an die vorhandenen Gegner heraun zu kommen, wir hätten einfach die List als public anstatt als private markieren können, aber wir wollen nicht das Elemente von aussen manipuliert werden können daher fügen wir nun folgendes hinzu:

 

public IEnumerable<WaveEnemy> Enemies
{
get
{
	return enemies;
}
}

 

Dies ist ein Property, eine Eigenschaft. Diese Eigenschaft wird benutzt wie eine Variable anstatt wie eine Methode und ermöglicht es uns anzugeben was passieren soll wenn wir den Wert abfragen (get) oder setzen (set, welchen wir nicht benutzen).

 

Wir hätten anstelle des Properties auch folgendes schreiben können:

 

IEnumerable<WaveEnemy> GetEnemies()
{
return enemies;
}

 

IEnumerable<> ist ein Typ der angibt dass wir ihn durchgehen können, ein Array oder eine List sind IEnumerables und können damit in der foreach Schleife verwendet werden.

 

Passt nun noch die WaveEnemy Klasse an, indem ihr beim Aufruf der RegisterEnemy und UnregisterEnemy Methoden this als Parameter angebt.

 

Auf gehts zur Darstellung des Radars. Geht also wieder zur HUD Klasse und fügt folgende Variablen hinzu:

 

public WaveManager WaveManager;
public float RadarSize = 0.05f;
public float MarkerSize = 5;
public Texture2D MarkerTexture;

 

Wir können und sollten diese Variablen im Inspektor befüllen. MarkerTexture wird dabei unsere erste im Tuorial erwähnte Textur tragen die wir später noch in unser Projekt hinzufügen werden.

 

Fügt anschließend ans Ende der OnGUI Methode folgenden Aufruf hinzu:

 

RenderRadar();

 

Anschließend könnt ihr auch schon die RenderRadar Methode erstellen:

 

private void RenderRadar()
{
}

 

Wir benötigen später das Zentrum der UI, deswegen fügt folgendes hinzu:

 

var center = new Vector3(Screen.width / 2, Screen.height / 2, 0);

 

Wir wollen alle Gegner finden die ausserhalb des sichtfeldes sind, um anschließend die Richtung zu ihnen zu zeichnen, wir fangen damit an indem wir alle Gegner durchgehen:

 

foreach (var enemy in WaveManager.Enemies)
{
}

 

Wir berechnen die Position des Markers der anzeigt in welche Richtung dieser Gegner ist wie folgt:

 

var markerPosition = (enemy.transform.position - Player.transform.position) * RadarSize;
markerPosition = Quaternion.Inverse(Player.transform.rotation) * markerPosition;
markerPosition.y = markerPosition.z;
markerPosition += center;
markerPosition.y = Screen.height - markerPosition.y;

 

Wir berechnen die MarkerPosition indem wir einen Richtungs Vector Berechnen und diesen mit unserer RadarSize multiplizieren. Mit Quaternion.Inverse(Player.transform.rotation) * markerPosition drehen wir die Position so dass sie immer relativ zu unserer Blickrichtung bleibt. Wir korrigieren dabei die Y Position da diese entgegen der GUI Y Position welche für die Darstellung verwendet wird Unten bei 0 liegt, die GUI fängt dabei aber von Oben an.

 

Und zeichnen den Marker dann so:

 

GUI.DrawTexture(new Rect(markerPosition.x - MarkerSize,
markerPosition.y - MarkerSize / 2,
MarkerSize,
MarkerSize), MarkerTexture);

 

GUI.DrawTexture benötigt ein Rect Objekt, welches angibt auf welchen rechteckigen Bereich wir unsere Textur anzeigen wollen, wir berechnen das Rechteck so dass es genau um unsere MarkerPosition aufgespannt wird.

 

Verwendet als Textur für den Marker am besten eine mit einer auffälligen Farbe.

 

Als Textur sollten wir eine Bild Datei in unser Projekt kopieren welche relativ klein ist und evtl nur einen Kreis darstellt.

 

Hier noch die finale HUD.cs Datei:

 

using UnityEngine;
using System.Collections;

public class HUD : MonoBehaviour {

public Destructable Player;
private Shild shild;
public WaveManager WaveManager;
public float RadarSize = 0.05f;
public float MarkerSize = 5;
public Texture2D MarkerTexture;

void Update()
{
	if (!shild && Player)
	{
		shild = Player.GetComponent<Shild>();
	}
}

void OnGUI()
{
	if (!Player)
	{
		return;
	}

	GUILayout.BeginHorizontal();

	GUILayout.Space(20);
	GUILayout.Label("Lebenspunkte: " + Player.CurrentHealthPoints + "/" + Player.MaxHealthPoints);

	GUILayout.FlexibleSpace();

	if (shild)
	{
		GUILayout.Label("Schild: " + shild.CurrentShild + "/" + shild.MaxShild);
	}
	GUILayout.Space(20);

	GUILayout.EndHorizontal();

	RenderRadar();

}

void RenderRadar()
{
	var center = new Vector3(Screen.width / 2, Screen.height / 2, 0);

	foreach (var enemy in WaveManager.Enemies)
	{
		var screenPos = Camera.main.WorldToScreenPoint(enemy.transform.position);

           var markerPosition = (enemy.transform.position - Player.transform.position) * RadarSize;
           markerPosition = Quaternion.Inverse(Player.transform.rotation) * markerPosition;
           markerPosition.y = markerPosition.z;
           markerPosition += center;
           markerPosition.y = Screen.height - markerPosition.y;

		GUI.DrawTexture(new Rect(markerPosition.x - MarkerSize,
			markerPosition.y - MarkerSize / 2,
			MarkerSize,
			MarkerSize), MarkerTexture);
	}
}
}

 

Hier eine Marker.png die verwendet werden kann (hat hässliche Alpha Fehler da ich den Alpha Channel nicht direkt bearbeiten konnte):

post-894-0-29069000-1397331463.png

 

Unitypackage:

https://dl.dropboxus...15.unitypackage

 

Webplayer:

https://dl.dropboxus...t15/Part15.html

Link zu diesem Kommentar
Auf anderen Seiten teilen

  • 6 months later...

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Gast
Auf dieses Thema antworten...

×   Du hast formatierten Text eingefügt.   Formatierung jetzt entfernen

  Only 75 emoji are allowed.

×   Dein Link wurde automatisch eingebettet.   Einbetten rückgängig machen und als Link darstellen

×   Dein vorheriger Inhalt wurde wiederhergestellt.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Lädt...
×
×
  • Neu erstellen...