[Artikel][XNA] Interfaces mit Hintergrundmasken

Hier können Artikel, Tutorials, Bücherrezensionen, Dokumente aller Art, Texturen, Sprites, Sounds, Musik und Modelle zur Verfügung gestellt bzw. verlinkt werden.
Forumsregeln
Möglichst sinnvolle Präfixe oder die Themensymbole nutzen.
Antworten
Mandriva
Beiträge: 18
Registriert: 12.05.2009, 20:01

[Artikel][XNA] Interfaces mit Hintergrundmasken

Beitrag von Mandriva »

Hallo Community,

In diesem Artikel erkläre ich, wie ich das Interface für mein Projekt implementiert habe.

Einleitung
Wer mit C# bereits Windows Anwendungen erstellt hat, wird in XNA den Komfort von fertigen Schaltflächen vermissen. Hier gibt es keine Button Klasse, welche sich benutzen lässt. Jedoch gibt es eine Möglichkeit, mit XNA in einer Form zu rendern und dort auf die aus den Winforms bekannten Klassen zuzugreifen. Dies ist jedoch nicht Bestandteil des Artikels.

Die Hintergrundmaske
Eine Hintergrundmaske ist eine schwarze Textur mit eingefärbten Bereichen. Diese Bereiche stellen die Schaltflächen dar (siehe Bild 1). Jeder Bereich hat eine eigene Farbe, auch wenn dies mit dem Auge nicht erkennbar ist. Bei einem Klick wird nun der Farbwert des angeklickten Pixels ausgelesen und damit die auszuführende Aktion festgelegt. Die Hintergrundmaske selber wird nicht gezeichnet, da sie nur zur Auswertung der angeklickten Bereiche dient. Anstelle der Maske wird das eigentliche Hintergrundbild und die Schaltflächen gezeichnet.

Implementation
Einlesen der Pixelinformationen
Zuerst müssen wir ein Array erzeugen, welches jeden Pixel der Textur speichern kann. In der zweiten Zeile wird dieses Array mit den Informationen aus der Textur gefüllt. MaskTexture enthält die Grafik der Hintergrundmaske und ist vom Typ Texture2D. Das Laden der Textur wird hier nicht gezeigt.

Code: Alles auswählen

private uint[] MaskData;
private Texture2D MaskTexture;

public void GetMaskData()
{
   this.MaskData = new uint[this.MaskTexture.Width * this.MaskTexture.Height];
   this.MaskTexture.GetData<uint>(this.MaskData);
}
Auslesen der Pixelfarbe
Nachdem wir die Pixel in unserem Array gespeichert haben, können wir nun die Farbe auslesen. Dazu bestimmen wir zuerst die Position auf der Textur, wo wir geklickt haben. Es ist wichtig, dass bei diesem Vorgehen die Hintergrundmaske die volle Größe des Fensters einnimmt, da sich sonst die Berechnung der Position verändern würde. Im zweiten Schritt formatieren wir den Farbwert in das Hex-Format um. Die Parameter sind die X- und Y-Koordinaten der Maus.

Code: Alles auswählen

public string GetMaskPixelColor(int xCoordinate, int yCoordinate)
{
   uint Position = Convert.ToUInt32(yCoordinate) * Convert.ToUInt32(this.MaskTexture.Width) + Convert.ToUInt32(xCoordinate);
   return String.Format("{0:x2}", MaskData[Position]);
}
Abfrage des angeklickten Bereichs
Das ist der leichteste Teil. Wir fragen über das Switch-Konstrukt die aktuelle Farbe ab und reagieren entsprechend.

Code: Alles auswählen

switch (GetMaskPixelColor(xCoordinate, yCoordinate))
{
   // Schaltfläche 1
   case "ff646464":
      // Hier abfragen ob Mausklick gemacht wurde und entsprechend reagieren
   break;
   // Schaltfläche 2
   case "ff6e6e6e":
      // Hier abfragen ob Mausklick gemacht wurde und entsprechend reagieren
   break;
}
Vor- und Nachteile
Vorteile
  • beliebig Komplexe Buttons möglich
  • Design lässt sich ohne Änderungen im Code verändern, es muss nur die neue Grafik eingebunden werden
  • Lässt sich mit der Alternative kombinieren für komplexere Buttons bei der Alternative.
Nachteile
  • schwerer zu implementieren als die Alternative (siehe unten)
Alternative
Wer nur einfache Schaltflächen darstellen möchte, oder eine veränderbares Interface hat, kann auch auf die Klasse Rectangle zurückgreifen und mithilfe der Intersect Methode prüfen, welches Rechteck angeklickt wurde.

Anmerkungen
Ich habe den Code nicht 1:1 aus meinem Projekt übernommen um ihn lesbarer zu machen und hoffe dabei keine Fehler eingebaut zu haben.
Wollt ihr Hovereffekte für die Schaltflächen erstellen, dann solltet ihr die Methode GetMaskPixelColor immer in eurer Update Methode des Spiels aufrufen, könnt ihr jedoch auf diese Spielerei verzichten, dann braucht ihr GetMaskPixelColor erst beim Betätigen einer Maustaste aufrufen.


Gruß Thomas

Update 1
Ich habe nun eine Zip-Datei angehängt, die eine Klassendatei beinhaltet. Diese Klasse setzt das oben beschriebene um.
Es kann entweder im Konstruktor oder aber über eine Eigenschaft die Textur zugewiesen werden und über die Methode GetColor() lässt sich die Farbe der übergebenenen Koordinaten bestimmen.

Update 2
Ich habe die Vor- und Nachteile dieser Methode aus der Diskussion ergänzt und die Alternative aufgeführt.
Dateianhänge
BackgroundMask.zip
Quellcode
(660 Bytes) 353-mal heruntergeladen
Bild 1
Bild 1
start_menu_mask.jpg (2.33 KiB) 5339 mal betrachtet
Zuletzt geändert von Mandriva am 17.05.2009, 12:10, insgesamt 2-mal geändert.
Benutzeravatar
dowhilefor
Moderator
Beiträge: 173
Registriert: 27.02.2009, 15:44
Alter Benutzername: 6SidedDice
Echter Name: Nico Probst
Wohnort: Bochum
Kontaktdaten:

Re: [Artikel][XNA] Interfaces mit Hintergrundmasken

Beitrag von dowhilefor »

Danke erstmal für den Artikel und die damit verbundene Mühe die du dir gemacht hast.

Aber ganz verstehe ich nicht warum du es dir so kompliziert machst. Für einen Button reicht ein einfaches Rectangle und die darin enthaltene Intersect Methode(Namen weiß ich jetzt nicht genau). Dem übergibst du die geklickte Bildschirm Koordinate und reagierst darauf. Zusammen mit Events, bekommst du so sogar einen ähnlichen Ansatz wie das .NET Framework hin.
Das mit den Pixel kann man immernoch machen, wenn es einem wichtig ist, das man in den Alphabereich des Buttons klickt, ohne den Button auszulösen, aber imo overkill.
Kann natürlich sein, dass du einen völlig anderen Ansatz verfolgst als ich ...
Mein Gehirn besteht nur noch aus einem hash-index, ich weiss was ich kenn aber kenn nicht was ich weiss
Benutzeravatar
Aramis
Moderator
Beiträge: 1458
Registriert: 25.02.2009, 19:50
Echter Name: Alexander Gessler
Wohnort: 2016
Kontaktdaten:

Re: [Artikel][XNA] Interfaces mit Hintergrundmasken

Beitrag von Aramis »

Die Methode ist für Rechtecke imho overkill, für andersartig geformte Buttons aber sehr zu empfehlen. Ich hab sie damals beim HUD/NavigationsUI für AssimpView verwendet, es hat unzählige Stunden gespart und ist absolut pixelgenau. Hier die Maske.
Mandriva
Beiträge: 18
Registriert: 12.05.2009, 20:01

Re: [Artikel][XNA] Interfaces mit Hintergrundmasken

Beitrag von Mandriva »

Hallo,

Danke für das Feedback. Wie Aramis schon geschrieben hat, lassen sich damit super Schaltflächen realisieren, die nicht rechteckig sind. Ich denke auch, dass diese Methode schneller ist, als wenn bei jedem Rechteck die Intersects Methode aufgerufen wird, um herauszufinden, welcher Knopf gedrückt wurde.


Gruß Thomas
Benutzeravatar
dowhilefor
Moderator
Beiträge: 173
Registriert: 27.02.2009, 15:44
Alter Benutzername: 6SidedDice
Echter Name: Nico Probst
Wohnort: Bochum
Kontaktdaten:

Re: [Artikel][XNA] Interfaces mit Hintergrundmasken

Beitrag von dowhilefor »

Ich denke auch, dass diese Methode schneller ist, als wenn bei jedem Rechteck die Intersects Methode aufgerufen wird, um herauszufinden, welcher Knopf gedrückt wurde.
Also das wage ich mal arg zu bezweifeln, du liest aus einem Array und vergleichst, meine Methode wäre ein Intersect test mit 2 Ifs.

Code: Alles auswählen

bool Intersect( const T& aX, const T& aY )
{
      if( aX > iLeft && aX < iRight )
	  if( aY > iTop && aY < iBottom )
		return true;
      return false;
}
Ich will auch nur sagen, wer einen Button braucht, wird recht bald auch mehr brauchen ... ein Interface besteht nicht nur aus Buttons, sonst würden ja auch die Nummerntasten für ein Menü reichen, wenn es schon spartanisch sein soll. Gegen den Pixel genauen Test sage ich ja nix, aber man könnte es kombinieren. Ein Test per Intersect in welchem Button der Mauszeiger ungefähr geklickt hat und dann Pixelgenau ob auch wirklich ein Sichtbarer Pixel vom Button getroffen wurde.
Mein Gehirn besteht nur noch aus einem hash-index, ich weiss was ich kenn aber kenn nicht was ich weiss
Mandriva
Beiträge: 18
Registriert: 12.05.2009, 20:01

Re: [Artikel][XNA] Interfaces mit Hintergrundmasken

Beitrag von Mandriva »

Hallo dowhilefor,

Was die Performance angeht, handelt es sich bei mir auch nur um eine Vermutung. Genaueres könnte nur eine Messung dazu sagen. Jedoch wäre der Unterschied eh in einem vernachlässigbaren Bereich.

Ein anderer Vorteil der Hintergrundmasken ist der, dass du das Design verändern kannst, ohne in den Code eingreifen zu müssen. Du musst lediglich die neue Grafik zur Verfügung stellen. Bei der anderen Methode musst du die neue Position und/oder Größe des Rechtecks einstellen.


Gruß Thomas
kkrahl
Beiträge: 56
Registriert: 20.10.2008, 13:41

Re: [Artikel][XNA] Interfaces mit Hintergrundmasken

Beitrag von kkrahl »

Ich habe mir auch lange überlegt für mein UI System so was zu machen. Ich bin aber zu den Schluss gekommen das es für meinen Fall zu aufwendig ist. Der Grund dafür ist das ich ein dynamisch konfigurierbares UI System habe wo ich jederzeit neue Fester definieren, nachladen und anzeigen kann und da natürlich auch den Fokus wechseln kann, das würde in meinem Fall heißen das bei jeder Veränderung so ein Bild neu zu erstellen ist und das in der zu diesem Zeitpunkt richtigen Drawingorder.

Für geformte Buttons will ich später das jedes Objekt nicht nur anhand seiner Position und Größe ermittelt ob es fokussiert ist sonder auch nach dem selben System wie du es hier vorschlägst einen Active Area Check macht.

mfg

Karl
Antworten