Konzeptfrage "Event-Handling"

Übersicht BlitzMax, BlitzMax NG Allgemein

Neue Antwort erstellen

 

Gray Fox

Betreff: Konzeptfrage "Event-Handling"

BeitragFr, Sep 21, 2012 9:18
Antworten mit Zitat
Benutzer-Profile anzeigen
Guten Morgen,

ich arbeite aktuell an einem kleinen Framework, das als Basissystem für meine zukünftigen Spiele dienen soll. Momentan beschäftige ich mich mit dem Thema "Event Handling" bzw. damit, wie meine Objekte (Interface-Elemente, ImageContainer, etc) auf die Mauseingaben reagieren können (onMouseover, onClick usw).

Geplant war, dass ich meine Objekte in einer Event-Handling-Klasse registriere (Eine Referenz des Objektes in einer TMap speichere). Ich liege doch richtig, dass (komplexe) Objekte nur als Referenz übergeben? In der Grundversion soll vorerst nur geprüft werden, ob sich die Maus über einem registrierten Objekt befindet.

Das wollte ich wie folgt realisieren:

Zu Anfang habe ich eine Grundklasse entworfen, die über die Basisattribute und Basismethoden verfügt. Die weiteren Objektklassen werden davon abgeleitet(?). Ich weiß nicht genau, wie man das Vorgehen nennt, aber ich meine "klasse B extends Klasse A".

Beim Aufruf der Update-Methode der Event-Handling-Klasse werden die Koordinaten der Maus ermittelt. Anschließend wird die TMap, in der die Referenzen der registrierten Objekte gespeichert sind, durchlaufen und die Mauskoordinaten werden mit den Objekt-Attributen X,Y, Width und Height verglichen und sollte sich die Maus innerhalb der Fläche des Objektes befinden, wird beispielsweise dessen interne "onMouseover" -Methode aufgerufen. (Event-Bubbling / Event-Order mal außen vorgelassen)

Jetzt kommt der Knackpunkt, Werte werden in einer TMap nur als Typ "Object" gespeichert. Somit müssten die Werte, in dem Fall die unterschiedlichen Klassen, erst wieder in die korrekte Klasse umgewandelt (umgecastet??) werden oder irre ich mich da?

Da ich gerade nicht an meinem Rechner sitze, kann ich auch leider keinen Sourcecode posten, außer einem kleinen Konzeptbeispiel, deshalb hoffe ich, dass meine Erläuterungen verständlich sind. Meine Hauptfrage ist, ob der Ansatz durchführbar ist oder ob ich nicht lieber das Thema aus einem anderen Blickwinkel betrachten sollte?

So sieht mein Code im Kern bisher aus:

Innerhalb der "main.bmx"
BlitzMax: [AUSKLAPPEN]


'bla bla
Global system:systemCore

system = New systemCore

'bla bla

system.eventHandler.registerObject(name, Object)

'#####main loop#####

'bla bla

system.eventHandler.update(system.mouse.getEvent())

'bla bla

'#####End main loop#####

'bla bla


'Innerhalb der systemCore-Klasse:
BlitzMax: [AUSKLAPPEN]

system.mouse = New systemSubMouse
system.eventHandler = New systemSubEventHandler

Xeres

Moderator

BeitragSa, Sep 22, 2012 9:42
Antworten mit Zitat
Benutzer-Profile anzeigen
Grüße!

- Objekte werden als Referenz übergeben, das ist richtig.
- Abgeleitet oder Vererbte Klassen, richtig.
- Du solltest vielleicht kein Object speichern, sondern als eine Klasse, die die richtigen Methoden besitzt. In eine unbekannte Klasse zu casten könnte kompliziert werden.

und ein letzter Hinweis:
Code: [AUSKLAPPEN]
system.eventHandler.update(system.mouse.getEvent())
Wenn getEvent Klammern besitzt, wird die Funktion/Methode aufgerufen und das Ergebnis übergeben, ohne wird die Referenz übergeben.
Win10 Prof.(x64)/Ubuntu 16.04|CPU 4x3Ghz (Intel i5-4590S)|RAM 8 GB|GeForce GTX 960
Wie man Fragen richtig stellt || "Es geht nicht" || Video-Tutorial: Sinus & Cosinus
T
HERE IS NO FAIR. THERE IS NO JUSTICE. THERE IS JUST ME. (Death, Discworld)
 

Gray Fox

BeitragSa, Sep 22, 2012 10:23
Antworten mit Zitat
Benutzer-Profile anzeigen
Guten Morgen,

danke für den Hinweis, aber hast du auch eine Idee, wie ich die Klassen speichern könnte? Immerhin könnten ja verschiedene Klassen als Referenz übergeben werden. Wobei alle Klassen von einer Basisklasse abgeleitet werden.

Wäre es alternativ möglich, den Code so anzupassen, dass, statt des Objektes selbst, "nur" die "Update-Methode des Objektes als Referenz übergeben wird? Würde das Sinn machen?

Lobby

BeitragSa, Sep 22, 2012 10:43
Antworten mit Zitat
Benutzer-Profile anzeigen
Nun, wäre doch toll, wenn die Basisklasse alle Methoden die du im EventHandler aufrufen können würdest wollen, schon festlegt. Dann brauchst du in den Klassen, die von dieser Hauptklasse erben, diese Methoden nur noch (neu) definieren und der EventHandler muss immer nur Objekte in die Basisklasse casten um die entsprechenden Methoden aufrufen zu können.

Leider kann man in BlitzMax keine Methoden übergeben. Häufig wird das damit umgangen, dass man eine Funktion übergibt, sowie ein Objekt, das als Parameter für diese Funktion dient. Innerhalb der Funktion wird dann das Parameterobjekt wieder in die gewünschte Klasse umgecasted und kann so verwendet werden. Allerdings ist diese Vorgehensweise eben recht aufwändig und verfehlt damit ihren eigentlichen Zweck.
TheoTown - Eine Stadtaufbausimulation für Android, iOS, Windows, Mac OS und Linux

Xeres

Moderator

BeitragSa, Sep 22, 2012 10:49
Antworten mit Zitat
Benutzer-Profile anzeigen
Ich bin mir ziemlich sicher, dass es keinen Sinn macht, die Methode zu übergeben; Was soll denn aufgerufen werden, wenn das Objekt die Methode nicht besitzt oder es kein Objekt gibt, auf das die Methode angewandt werden kann?
Wenn die Basis-klasse die Methoden besitzt, sollte es keine Probleme geben. Die Kind Klassen werden dann ihre eigenen Methoden aufrufen:

BlitzMax: [AUSKLAPPEN]
SuperStrict

Type TEventHandler
Field ObjList:TList

Function Create:TEventHandler()
Local E:TEventHandler = New TEventHandler
E.ObjList = New TList
Return E
End Function

Method Add(_obj:Object)
Self.ObjList.AddLast(_obj)
End Method

End Type

Type TBasis

Method Draw()
Print("Basis")
End Method

End Type

Type TEx Extends TBasis

Method Draw()
Print("Child")
End Method

End Type

Type TEx2 Extends TBasis

End Type

Local EH:TEventHandler = TEventHandler.Create()
EH.Add(New TBasis)
EH.Add(New TEx)
EH.Add(New TEx2) '* Besitzt keine eigene Draw-Methode -> ruft die der Elternklasse auf

For Local o:TBasis = EachIn EH.ObjList
o.Draw()
Next
Win10 Prof.(x64)/Ubuntu 16.04|CPU 4x3Ghz (Intel i5-4590S)|RAM 8 GB|GeForce GTX 960
Wie man Fragen richtig stellt || "Es geht nicht" || Video-Tutorial: Sinus & Cosinus
T
HERE IS NO FAIR. THERE IS NO JUSTICE. THERE IS JUST ME. (Death, Discworld)
 

Gray Fox

BeitragSa, Sep 22, 2012 11:03
Antworten mit Zitat
Benutzer-Profile anzeigen
Vielen Dank erstmal für eure Hinweise. Ich werde das mit der Basisklasse gleich einmal ausprobieren, aber gehen nicht die Attribute (Fields), die in den Kindklassen definiert wurden, verloren, wenn ich das übergebene Objekt in die Basisklasse umcaste?

Xeres

Moderator

BeitragSa, Sep 22, 2012 11:17
Antworten mit Zitat
Benutzer-Profile anzeigen
Die wären dann nicht Sichtbar, dass Stimmt.
Wenn du den direkten Zugriff brauchst, solltest du eine ID vergeben, damit du weißt, in welchen Typ du zurück casten musst.
Win10 Prof.(x64)/Ubuntu 16.04|CPU 4x3Ghz (Intel i5-4590S)|RAM 8 GB|GeForce GTX 960
Wie man Fragen richtig stellt || "Es geht nicht" || Video-Tutorial: Sinus & Cosinus
T
HERE IS NO FAIR. THERE IS NO JUSTICE. THERE IS JUST ME. (Death, Discworld)

Lobby

BeitragSa, Sep 22, 2012 11:19
Antworten mit Zitat
Benutzer-Profile anzeigen
Mag sein, dass du bei einer Referenz einer von der Basisklasse vererbten Klasse nicht auf die Felder zugreifen kannst, wenn du es zur Basisklasse castest, aber das brauchst du ja auch gar nicht. Dafür sind die Methoden da, und da automatisch immer die Methode aufgrufen wird, die in der Klasse steckt von der du ursprüngich die Instanz erstellt hast, sind in dieser auch alle von dir für diese Klasse definierten Felder vorhanden.
Verloren geht nichts, es wird nur für dich unsichtbar Wink .
TheoTown - Eine Stadtaufbausimulation für Android, iOS, Windows, Mac OS und Linux
 

Gray Fox

BeitragSa, Sep 22, 2012 11:34
Antworten mit Zitat
Benutzer-Profile anzeigen
Ich muss sagen, das ergibt alles Sinn Wink

Xeres Vorschlag hört sich gut an, man definiert in der Basisklasse ein Field / Attribute "cType oder controlType", castet das Objekt erstmal in die Basisklasse und macht dann eine select case-Abfrage und castet das Objekt entsprechend um.

EDIT:

Ich habe es mal ausprobiert, es scheint sogar zu funktionieren Very Happy

Jetzt muss ich mir mal um das "Naming" meiner Klassen Gedanken machen.

Gegen eine Beschriftung im Stile von "obj_typ_name" ist doch nichts einzuwenden, oder?

Beispiel: obj_control_imageList

BlitzMax: [AUSKLAPPEN]
Local obj_imageList = New obj_control_imageList


Wobei ich eigentlich das Folgende bevorzuge:

BlitzMax: [AUSKLAPPEN]
Local obj_imageList = New objControlImageList
 

Gray Fox

BeitragDi, Okt 02, 2012 9:38
Antworten mit Zitat
Benutzer-Profile anzeigen
So, ich muss mich leider nochmal melden. Smile

Ich habe noch kleinere Probleme, bei denen ich einen Denkanstoß gebrauchen könnte.

Gibt es in BlitzMax anonyme Funktionen, ähnlich denen in Javascript?

Code: [AUSKLAPPEN]

   element.onEvent = function() {
      //do something
   }


---------

Wie könnte ich folgendes Problem am geschicktesten lösen?

Ich habe meinen EventHandler soweit fertiggestellt, dass es möglich ist, bei jedem registrierten Objekt ein "onMouseOver"- und "onClick"-Event auszulösen. Im Moment besitzt jedes Objekt ein "onMouseOver"- und ein "onClick"- Feld (Field). Diesen Feldern werden dann individuelle Funktionen zugewiesen (Nennt sich dann doch Funktionspointer?)

Das Problem ist nun, dass ich innerhalb von Funktionen nicht mit "self" arbeiten kann, weil es ja keine Methoden sind. Mein Ansatz war, dass ich eine Referenz des Objektes, bei dem ein Event ausgelöst wurde, an die Funktion übergebe. Da ich aber vermeiden will, dass ich in jeder Funktion vorher den Typ des Objektes umcasten muss, wollte ich es so regeln, dass es nur einen allgemeingültigen Objekttyp gibt, der je nach Bedarf angepasst wird und ein anderes "Steuerelement / Control" darstellt.

So würde das dann beispielsweise aussehen:
BlitzMax: [AUSKLAPPEN]

Type universalObj

Field attributes:TMap = New TMap
Field onClick:Object(obj:universalObj, parameter:TMap)
Field parent:systemCore
//weitere Fields

Method init(parent:systemCore, cType:String, name:String)
Self.parent = parent
//setze einige Standardattribute

//setze spezielle Attribute entsprechend des Wertes von cType
Select cType
Case "imageBox"
Self.setAttribute("background_image", Null)
Case "text"
Self.setAttribute("font", "Arial")
Self.setAttribute("fontSize", "12")
End Select

Self.parent.eventHandler.registerObject(Self.getAttribute("name"), Self)
End Method

Method setAttribute:Int(attribute:String, value:Object)

End Method

Method getAttribute:Object(attribute:String)

End Method

End Type

Function tuWas:Int(obj:universalObj, parameter:TMap)
obj.setAttribute("background_image", TImage)
End Function

myObj:universalObj = New universalObj
myObj.init(system, "imageBox", "im_01")

myObj.onClick = tuWas

//Main loop

system.eventHandler.update(system.mouse.getEvent())


Kann man den Ansatz so lassen oder sieht das "unschön" oder unprofessionell aus?
Ich versuche das Hintergrundsystem auf diese Art so universell wie möglich zu gestalten, damit ich es später relativ einfach für andere (Spiele)Projekte einsetzen kann.

Xeres

Moderator

BeitragDi, Okt 02, 2012 19:12
Antworten mit Zitat
Benutzer-Profile anzeigen
Der inoffizielle Standard ist "T"<name> für Types/Klassen um sie von Objekten/Instanzen zu unterscheiden.
Anonyme Funktionen gibt es nicht.
So mit Strings zu arbeiten halte ich für etwas unschön, weil langsam und sieht sehr hardcoded aus.
Da man kein Attribut/Field on the fly deklarieren kann wie in Javascript, müsste dein Universal-Type auch alle Fields besitzen die man irgendwann mal gebrauchen wollte. Und die kann man auch nicht als String ansprechen.
BlitzMax ist kein Javascript und ich würde nicht so tun, als wäre es so.
Win10 Prof.(x64)/Ubuntu 16.04|CPU 4x3Ghz (Intel i5-4590S)|RAM 8 GB|GeForce GTX 960
Wie man Fragen richtig stellt || "Es geht nicht" || Video-Tutorial: Sinus & Cosinus
T
HERE IS NO FAIR. THERE IS NO JUSTICE. THERE IS JUST ME. (Death, Discworld)
 

Gray Fox

BeitragDi, Okt 02, 2012 19:37
Antworten mit Zitat
Benutzer-Profile anzeigen
Xeres hat Folgendes geschrieben:
Der inoffizielle Standard ist "T"<name> für Types/Klassen um sie von Objekten/Instanzen zu unterscheiden.


Das finde ich wiederum unschön, aber nun gut, vielleicht sollte man sich den Konventionen der eingesetzten Programmiersprache einfach unterwerfen Wink

Zitat:
Anonyme Funktionen gibt es nicht.


Schade..

Zitat:
So mit Strings zu arbeiten halte ich für etwas unschön, weil langsam und sieht sehr hardcoded aus.
Da man kein Attribut/Field on the fly deklarieren kann wie in Javascript, müsste dein Universal-Type auch alle Fields besitzen die man irgendwann mal gebrauchen wollte. Und die kann man auch nicht als String ansprechen.


In einer TMap kann ich doch verschiedene Dateitypen speichern, somit kann ich doch theoretisch alle Attribute / Fields "on the fly" generieren?

Wie sehe denn ein besserer Ansatz aus?

Xeres

Moderator

BeitragDi, Okt 02, 2012 19:52
Antworten mit Zitat
Benutzer-Profile anzeigen
Ah, die TMap habe ich leicht übersehen... kann natürlich klappen, aber alles über Strings an zu sprechen halte ich trotzdem nicht für das wahre. Statt des cType Strings würde ich aber vom Universalobjekt ableiten. Ein riesiges Select Case Konstrukt ist nicht besonders schick oder praktisch.
Aber lass dich nicht von mir von etwas abbringen, am besten merkt man nach dem 3. Anlauf, was man hätte machen sollen. :p
Win10 Prof.(x64)/Ubuntu 16.04|CPU 4x3Ghz (Intel i5-4590S)|RAM 8 GB|GeForce GTX 960
Wie man Fragen richtig stellt || "Es geht nicht" || Video-Tutorial: Sinus & Cosinus
T
HERE IS NO FAIR. THERE IS NO JUSTICE. THERE IS JUST ME. (Death, Discworld)
 

Gray Fox

BeitragDi, Okt 02, 2012 20:06
Antworten mit Zitat
Benutzer-Profile anzeigen
Xeres hat Folgendes geschrieben:
ich aber vom Universalobjekt ableiten. Ein riesiges Select Case Konstrukt ist nicht besonders schick oder praktisch.


Das habe ich anfangs gemacht, aber ich habe dann trotzdem noch das beschriebene Problem, welches ich weiter oben beschrieben habe, und das ich mit dem jetzigen Konstrukt lösen wollte.

Neue Antwort erstellen


Übersicht BlitzMax, BlitzMax NG Allgemein

Gehe zu:

Powered by phpBB © 2001 - 2006, phpBB Group