Vererbung und Create()

Übersicht BlitzMax, BlitzMax NG Beginners-Corner

Neue Antwort erstellen

 

Soul Reaver

Betreff: Vererbung und Create()

BeitragDi, Feb 17, 2009 17:39
Antworten mit Zitat
Benutzer-Profile anzeigen
So ich hab mich mal ein Bisschen mit BlitzMax gespielt und stehe vor jenem Problem:

Ich habe einen Typ "TButton"... Dann gibt es noch die Typen "TExitButton" "TSaveButton" usw., die alle von "TButton" erben:

Code: [AUSKLAPPEN]

Type TButton
  'Hier gibt es ein paar Methoden, Fields und auch eine abstrakte Methode
End Type

Type TExitButton Extends TButton
   'Hier gibt es eine Methode, die sich bei allen Buttons unterscheidet und folgende Funktion:
   Function Create:TExitButton(,x,y,width,height,value:String)
    Local t:TExitButton=New TExitButton
    t.x=x
    t.y=y
    t.width=width
    t.height=height
    t.value=value
    Return t
  End Function
End Type


Und diese Createfunktion habe ich in jedem Button (außer in "TButton"), obwohl sie immer dasselbe tut. Das schmerzt sehr. Da dachte ich mir, dass ich die Funktion schon "TButton" definiere, doch dann würde ja immer eine Instanz von "TButton" entstehen wenn ich z.B. "TExitButton.Create()" aufrufen würde...

Irgendwelche Ideen?

Mr.Hyde

Newsposter

BeitragDi, Feb 17, 2009 17:53
Antworten mit Zitat
Benutzer-Profile anzeigen
Ich verstehe nicht ganz, warum du für jeden Button-Typ eine eigene Unterklasse erstellst. Speicher doch einfach in der Super Klasse was für eine Art Button es ist und behandel ihn danach. Dann brauchst du auch nicht für jeden eine eigene Methode bzw Funktion und nur für das, was nicht in der Super Klasse abgedeckt werden kann, erstellst du Unterklassen. Mir fällt jetzt aber nichts ein, was das sein sollte.
BBP News RSS | Chaos Interactive | Watanien 2 Screens, Infos und Download | Watanien 2 Worklog | PuzzleMasters
http://abgeordnetenwatch.de - http://www.regierungs-beratung.de - Der Regierung auf die Finger schauen
 

Soul Reaver

BeitragDi, Feb 17, 2009 19:08
Antworten mit Zitat
Benutzer-Profile anzeigen
Naja, natürlich könnte ich das so regeln, doch das wäre nicht sehr elegant. Dann würde die Methode, in der ein Klick behandelt wird, sehr lang werden, da ich ja noch mehr Buttons einbauen werde. Meine Struktur finde ich einfach viel übersichtlicher, als eine 1-Seiten Methode...

Mr.Hyde

Newsposter

BeitragDi, Feb 17, 2009 19:15
Antworten mit Zitat
Benutzer-Profile anzeigen
Hehe, das wäre sehr viel eleganter, weil du so ohne weiteres Buttons mit diversen Zwecken hinzufügen kannst. Den Event, den ein Button auslöst verwaltet man ja nicht in der Button Klasse sondern Beispielweise in der Eventklasse und wenn die von der Buttonklasse gesendet bekommt "Event-Exit_Game" dann macht die das und nicht, wie du das vor hast, der Button. So ist das extrem unübersichtlich und er Vorteil von OOP vollständig verspielt.
BBP News RSS | Chaos Interactive | Watanien 2 Screens, Infos und Download | Watanien 2 Worklog | PuzzleMasters
http://abgeordnetenwatch.de - http://www.regierungs-beratung.de - Der Regierung auf die Finger schauen
 

Ava

Gast

BeitragDi, Feb 17, 2009 19:51
Antworten mit Zitat
Und noch eleganter ist meiner Ansicht nach ein einfacher, direkter Funktionspointer, der beim click aufgerufen wird ... Rolling Eyes

Jolinah

BeitragDi, Feb 17, 2009 20:05
Antworten mit Zitat
Benutzer-Profile anzeigen
Hier ein Beispiel mit einem Funktionszeiger:

Code: [AUSKLAPPEN]
Type TButton
  Field Click(button:TButton, mousebutton:Int, x:Int, y:Int)

  Method Update()
    If MouseOver() And MouseDown(1) And (Not Self.clicked) Then
      Self.clicked = True
      If Click <> Null Then
        'Event bzw. Callback aufrufen
        Click(Self, 1, MouseX(), MouseY())
      EndIf
    EndIf
  End Method

  ...
End Type


Local button1:TButton = TButton.Create(10, 10, 100, 20, "Ok")
button1.Click = button1_Click

Local button2:TButton = TButton.Create(10, 10, 100, 20, "Ok")
button2.Click = button2_Click


....

Function button1_Click(button:TButton, mousebutton:Int, x:Int, y:Int)
  Notify("Exit")
End Function

Function button2_Click(button:TButton, mousebutton:Int, x:Int, y:Int)
  Notify("Save")
End Function

...


So kannst du beliebig viele Buttons machen die etwas unterschiedliches auslösen.

Aber um auf die eigentliche Frage bzw. das Problem zurück zu kommen: Das hat mich auch schon genervt... wär toll wenn der Konstruktor in BMax Parameter akzeptieren würde.
  • Zuletzt bearbeitet von Jolinah am Di, Feb 17, 2009 20:07, insgesamt einmal bearbeitet

DaysShadow

BeitragDi, Feb 17, 2009 20:05
Antworten mit Zitat
Benutzer-Profile anzeigen
@ Ava: meinst du sowas?

Code: [AUSKLAPPEN]
Function ButtonFunction()
      'bla Button macht das was er machen soll
EndFunction

Type TButton

    Field FuncPtr()

    Method CallFunction()

        FuncPtr()

    EndMethod

EndType

Local button:TButton = New TButton

button.Script = ButtonFunction

button.CallFunction() ' oder eben auch button.FuncPtr() funzt ja genauso


Edit: hehe, zwei mit ungefähr der gleichen idee ^^ ich bin ebenfalls auf das Problem gestoßen und hatte auch schon vor, das mit den Funktionszeigern zu erledigen, aber umgesetzt habe ich es noch nicht...hat man denn die selben Probs auch in C++ z.B? Ich erwäge nämlich ernsthaft mich von BB zu verabschieden ^^
Blessed is the mind too small for doubt
 

Ava

Gast

BeitragDi, Feb 17, 2009 23:00
Antworten mit Zitat
Jap, genau sowas in der Richtung meinte ich.

Naja, und was euer Create-Problem betrifft, so kann man sich einen Grossteil des gleichbleibenden Inhalts in einer Vererbung ersparen, wenn man diese Funktion lediglich ein Objekt erstellen lässt und anschliessend eine Setup-Method des übergeordneten Objektes mit den übergebenen Parametern aufruft. Okay, das erspart oft nur wenig Schreib- bzw. Kopierarbeit, aber es garantiert, dass man bei einer Veränderung sehr viel weniger Aufwand hat und sehr viel weniger Fehler produzieren kann.

Mich hat euer "Problem" aber bisher nie wirklich gestört, da ich mir in der Regel eh für jedes Objekt eine eigenständige Funktion á la CreateButton:TButton ( parameter:blah ) "auslagere". ^^

BtbN

BeitragMi, Feb 18, 2009 8:32
Antworten mit Zitat
Benutzer-Profile anzeigen
In C++ hat man solche probleme erstmal garnicht, weil man keine Buttons hat. Bei Qt ist es über ein Eventsystem geregelt, welches aber so in BMax nicht umsetzbar ist, da es in Bmax meines wissens nach nicht möglich ist eine Method anhand ihres Namens aufzurufen. Wenn das mittels refraction(o.ä.) doch möglich ist, liesse sich ein solches system zumindest ansatzweise auch in bmax umsetzen.

Xeres

Moderator

BeitragMi, Feb 18, 2009 13:39
Antworten mit Zitat
Benutzer-Profile anzeigen
Es ist möglich, BtbN Wink
Eins der Beispiele des Reflection Moduls:
Code: [AUSKLAPPEN]
Strict

Type TMyType
   Method Update( t# )
      Print "TMyType.Update:"+t
   End Method
End Type

Local obj:TMyType=New TMyType
Local id:TTypeId=TTypeId.ForObject( obj )

Local update:TMethod=id.FindMethod( "Update" )

update.Invoke obj,[String( .25 )]
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)

Firstdeathmaker

BeitragMi, Feb 18, 2009 19:29
Antworten mit Zitat
Benutzer-Profile anzeigen
Also ich habe es immer mit einem Message-System gelöst, das ich Function-Pointer nicht so dolle finde wenn ich alles schön OOP halten möchte:

Alle Menü's, Button's etc. sind von einer Hauptklasse TGUI oder so abgeleitet, welche eine Methode besitzt, welche Messages Empfangen und verarbeiten kann.
Code: [AUSKLAPPEN]

Type TGUI abstract
Method recvMsg(msg:TMessage) abstract
end type

Type
field id:string
field content:object
end type


Wenn man jetzt einen Button erstellt, kann man diesem ein Target und eine Message übergeben, welche er an dieses schicken soll wenn er geklickt wurde. Das Zielobjekt sollte dann in der recvMsg-Methode allerdings auf diese Message regieren Wink


z.B.:
Code: [AUSKLAPPEN]

Type Menueobject Extends TGUI
   
   Method init()
      Local b:TButton = New TButton
      b.onClickMsg = New TMessage
      b.onClickMsg.id = "end"
      b.onClickTarget = self
   End Method
   
   Method recvMsg(msg:TMessage)
      Select msg.id
      Case "end" End
      End Select
   End Method
   
End Type


Type Button Extends TGUI
   
   Field onClickMsg:TMessage
   Field onClickTarget:TGUI
   
   Method logic()
      If mouseover() And mouseclick() logicClick()
   End Method
   
   Method logicClick()
      onClickTarget.recvMsg(onClickMsg)
   End Method
   
   Method recvMsg(msg:TMessage)
   End Method
End Type

www.illusion-games.de
Space War 3 | Space Race | Galaxy on Fire | Razoon
Gewinner des BCC #57 User posted image
 

Ava

Gast

BeitragMi, Feb 18, 2009 23:39
Antworten mit Zitat
Zitat:
Also ich habe es immer mit einem Message-System gelöst, das ich Function-Pointer nicht so dolle finde wenn ich alles schön OOP halten möchte

Also diese Aussage verstehe ich nun gar nicht O_o

Ich finde ein solches Massagesystem in diesem Bezug unnötig und umständlich und unnötig umständlich (*g*). Es löst auf grossen Umwegen etwas, dass sich auf einem anderem Wege sehr viel einfacher, schneller und effektiver abhandeln lässt Exclamation Es werden zusätzliche Objekte erstellt, die der Programmierer erst wieder auswerten muss, um den Programmablauf entsprechend zu lenken, anstelle dies direkt zu tun. Das schadet nicht nur der Übersicht, es macht es auch wesendlich umständlicher im Gebrauch Exclamation
 

Dreamora

BeitragDo, Feb 19, 2009 6:52
Antworten mit Zitat
Benutzer-Profile anzeigen
MessageSysteme sind extrem mächtig und sie lösen einige der Probleme die 10'000 vererbungen und Unterklassen überhaupt erst erzeugen.
Speziell wenn man das MessageSystem mit einem Komponenten System (also gekapselte Funktionalität die man an eine Trägerklasse anhängen kann) kombiniert, erreicht man damit eine einfachheit im design die nicht wirklich zu übertreffen ist.

Als kleines Gedankenbeispiel:

Du hast eine Kriegerklasse, die kann angreifen
Der Krieger hat eine peitsche und flügel

Wenn du das ganze nun animieren willst, sagen wir "Angriff!", verlagt das, dass der krieger sich bewusst ist, was er überhaupt am körper haben kann (nicht im sinn vom item type, aber im sinn von welcher slot etc) und das zu compile zeit, und das dann anweist zu animieren.
Das ist nicht sonderlich flexibel, denn so sind die slots fix definiert, wenn du mehr brauchst musst du den code ändern und recompilen.


Jetzt nehmen wir das ganze mal als Komponenten - Message System.
Hier haben wir einen Krieger mit einem Komponentenmanager. Das wars.
Der Komponentenmanager, unabhängig vom Krieger, enthält die Animationsdaten des Kriegers, aber auch die Peitsche und die Flügel
Wenn jetzt der Krieger angreift kann etwas von aussen oder der Krieger selbst, dem Komponentenmanager die Nachricht "Angriff!" durchgeben ohne zu wissen was sie in der blackbox damit macht. Dennoch werden brav alle dinge korrekt ausgeführt denn der komponentenmanager broadcasted diese Message an alle angehängt Komponenten.



Für kleine spielereien mag der weg des hardcodings ja noch funktionieren.
Aber schon ein tile based RPG mit ein wenig scripting und flexibilität profiert bereits in extremem Masse davon das man flexible moderne OO Ansätze nutzt um solche Dinge zu lösen und nicht mit "Höhlenmalereien" (reine Verarbung und ähnliche Ansätze die so seit mehr als einem Jahrzehnt nicht mehr wirklich genutzt werden an orten wo man interesse daran hat geld zu sparen, wegen der erheblichen Folgekosten die ihr Unterhalt mit sich bringt) versucht eine Lösung aufzuzeichnen.
Ihr findet die aktuellen Projekte unter Gayasoft und könnt mich unter @gayasoft auf Twitter erreichen.

Firstdeathmaker

BeitragDo, Feb 19, 2009 15:44
Antworten mit Zitat
Benutzer-Profile anzeigen
@ Ava: Seh ich total anders, hab mal den zwischenstand einer kleinen GUI angehängt um das zu verdeutlichen.

Ich hab ein Basis-GUI Element, von dem alle anderen GUI-Elemente abgeleitet werden. Dabei unterscheide ich zwischen Basiselementen (Abstract) und Finalen, welche jeweils static Konstruktoren bekommen.

Die Types TMessage und TAction sind Kommunikationsobjekte. Über TAction kann man Aktionen definieren, wie z.B. bei einem Button wenn er geklickt wurde. Diese Actions können alles mögliche machen, aber vor allem kann man über die Action TGUI_SendMessageAction dem Button den Befehl geben, eine Message an ein anderes GUI_Element zu schicken (hier das Menü). Über die Messages können sich die GUI-Elemente untereinander abstimmen.
(Man könnte in meinem Beispiel genausogut eine Action erstellen, welche einen Funktionspointer übergeben bekommt und diesen dann ausführt.)

Ein Menü (TGUI_MainMenue) ist wiederum eine Ableitung aus der Basisklasse TGUI_Element, nur dass hier die processMessage() methode implementiert wurde. Ich finde die Klasse TGUI_MainMenue übrigens nicht sehr kompliziert, man sieht doch relativ gut was sie macht, oder?

Lauffähiges Beispiel:
Code: [AUSKLAPPEN]



'TEST
Graphics 800,600

Global GUI:TGUI_Element = New TGUI_MainMenue


Repeat
   GUI.Process()
   Cls
      GUI.render()
   Flip
Until AppTerminate()
End



Type TGUI_MainMenue Extends TGUI_Element Final
   Method New()
      elementType = "MainMenue"
      TGUI_Message.Create("InitRoot" , Self , Self).send()
   End Method
      
   Method processMessage(m:TGUI_Message)
      Super.processMessage(m)
      Select m.id
         Case "end" End
         
         Case "InitRoot"
            Self.removeChildren()
            TGUI_RectButton.CreateSimple(300 , 270 , 200 , 30 , "Start" , "InitStart" , Self)
            TGUI_RectButton.CreateSimple(300 , 300 , 200 , 30 , "Options" , "InitOptions" , Self)
            TGUI_RectButton.CreateSimple(300 , 330 , 200 , 30 , "End" , "end" , Self)
      
         Case "InitStart"
            Self.removeChildren()
            TGUI_RectButton.CreateSimple(300 , 300 , 200 , 30 , "Back" , "InitRoot" , Self)
            
         Case "InitOptions"
            Self.removeChildren()
            TGUI_RectButton.CreateSimple(300 , 270 , 200 , 30 , "SFX" , "InitSfx" , Self)
            TGUI_RectButton.CreateSimple(300 , 300 , 200 , 30 , "Back" , "InitRoot" , Self)
         Case "InitSfx"
            Self.removeChildren()
            TGUI_RectButton.CreateSimple(300 , 300 , 200 , 30 , "Back" , "InitOptions" , Self)
            
      End Select
   End Method
End Type



'############ IMPLEMENTATION OF TGUI_Action ###############

Type TGUI_SendMessageAction Extends TGUI_Action Final
   Field id:String
   Field source:TGUI_Element
   Field target:TGUI_Element
   Field content:Object
   
   Method process()
      TGUI_Message.Create(id,source,target,content).send()
   End Method
   
   'Functions
   Function Create:TGUI_SendMessageAction(id:String , source:TGUI_Element , target:TGUI_Element , content:Object = Null)
      Local a:TGUI_SendMessageAction = New TGUI_SendMessageAction
      a.id = id
      a.source = source
      a.target = target
      a.content = content
      Return a
   End Function
End Type


'############ IMPLEMENTATIONS OF TGUI_Element ###############

Rem
   bbdoc: A simple button with 2 different images and a "on Click" action.
End Rem
Type TGUI_ImageButton Extends TGUI_Element Final
   Field image:TImage[2]
   
   Method New()
      elementType = "ImageButton"   
   End Method
   
   Method setImage(off:TImage , on:TImage)
      image[0] = off
      image[1] = on
      calcButtonSize()
   End Method
   
   Method calcButtonSize()
      Local w:Float = Max(ImageWidth(image[0]) , ImageWidth(image[1]) )
      Local h:Float = Max(ImageHeight(image[0]) , ImageHeight(image[1]) )
      Self.setSIze(w,h)
      calcSize()
   End Method
   
   Field onClickAction:TGUI_Action
   Method setOnClickAction(action:TGUI_Action)
      Self.onClickAction = action
   End Method
   
   Field mouseOver:Byte
   
   Method process()
      Super.process()
      mouseOver = overElement(MouseX() , MouseY() )
      If mouseOver And MouseHit(1)
         processClick()
      EndIf
   End Method
   
   Method processClick()
      If onClickAction onClickAction.process()
   End Method
   
   Method render()
      Super.render()
      SetColor 255,255,255
      DrawImage Self.image[mouseOver],Self.position[0],Self.position[1]
   End Method
   
   
   'Functions
   Function CreateSimple:TGUI_ImageButton(x:Float,y:Float,image0:TImage , image1:TImage , cmd:String , parent:TGUI_Element)
      Local b:TGUI_ImageButton = New TGUI_ImageButton
      Local a:TGUI_Action = New TGUI_SendMessageAction
      b.setPosition(x,y)
      b.setImage(image0 , image1)
      b.setOnClickAction(TGUI_SendMessageAction.Create(cmd , b , parent , Null) )
      b.setParent(parent)
      Return b
   End Function
End Type



Rem
   bbdoc: A very simple Button, with options for text and colors and a onClick action
End Rem
Type TGUI_RectButton Extends TGUI_Element Final
   Field text:String
   Field overColor:TColor = New TColor
   Field basicColor:TColor = New TColor
   Field textColor:TColor = New TColor
   
   Method New()
      elementType = "RectButton"
      overColor.setInt($FFFFFFFF)
      basicColor.setInt($FF90909090)
      textColor.setInt($FF000000)
   End Method
   
   Field onClickAction:TGUI_Action
   Method setOnClickAction(action:TGUI_Action)
      Self.onClickAction = action
   End Method
   
   Field mouseOver:Byte
   
   Method process()
      Super.process()
      mouseOver = overElement(MouseX() , MouseY() )
      If mouseOver And MouseHit(1)
         processClick()
      EndIf
   End Method
   
   Method processClick()
      If onClickAction onClickAction.process()
   End Method
   
   Method render()
      Super.render()
      If mouseOver
         overColor.Apply()
      Else
         basicColor.Apply()
      EndIf
      DrawRect position[0] , position[1] , size[0] , size[1]
      textColor.Apply()
      DrawText Self.text,position[0] + (size[0] - TextWidth(text))/2,position[1] + (size[1] - TextHeight(text))/2
   End Method
   
   
   'Functions
   Function CreateSimple:TGUI_RectButton(x:Float,y:Float,width:Float,height:Float,text:String, cmd:String , parent:TGUI_Element)
      Local b:TGUI_RectButton = New TGUI_RectButton
      Local a:TGUI_Action = New TGUI_SendMessageAction
      b.setPosition(x,y)
      b.setSize(width,height)
      b.setOnClickAction(TGUI_SendMessageAction.Create(cmd,b,parent,Null))
      b.setParent(parent)
      b.text = text
      Return b
   End Function
End Type




'################ BASIC TYPES OF GUI ###############################

Type TGUI_Element Abstract
   'Big Main Methods
   Method process()
      processView()
      processChildren()
      processMessages()
   End Method
   
   Method render()
      renderChildren()
   End Method
   
   Method remove()
      setParent(Null)
   End Method
   
   'Id
   Field id:String = "NoName"
   Field elementType:String = "Element"
   
   Method setId(id:String)
      Self.id = id
   End Method
   
   
   'Parent / Child
   Field parent:TGUI_Element
   Field parentLink:TLink
   Field children:TList = New TList
   
   Method setParent(p:TGUI_Element)
      If Self.parentLink RemoveLink(Self.parentLink)
      Self.parent = p
      If p<>Null Self.parentLink = p.children.addlast(Self)
   End Method
   
   Method addChildren(c:TGUI_Element)
      If c = Null Return
      c.SetParent(Self)
   End Method
   
   Method processChildren()
      For Local c:TGUI_Element = EachIn children
         c.process()
      Next
   End Method
   
   Method renderChildren()
      For Local c:TGUI_Element = EachIn children
         c.render()
      Next
   End Method
   
   Method removeChildren()
      For Local c:TGUI_Element = EachIn children
         c.remove()
      Next
   End Method
   
   Method setViewDirtyChildren()
      For Local c:TGUI_Element = EachIn children
         c.viewDirty = True
      Next
   End Method
   
   
   'View
   Field position:Float[2]   'upper left corner of element DONT WRITE THIS
   Field size:Float[2]      'outer size of element DONT WRITE THIS
   Field size_inner:Float[2]'inner size of element DONT WRITE THIS
   Field viewDirty:Byte 'means, next turn, everything needs an update
   
   Field border_abs:Float[4] 'element border size, 0=left 1=upper 2=right 3=lower
   
   Field size_abs:Float[2] 'this is the "normal outer size the user should see and manipulate"
   Field size_rel:Float[2] 'rel to parent
   
   Field position_abs:Float[2] 'this is the "normal position the user should see and manipulate"
   Field position_rel:Float[2] 'rel to parent size
   
   Field position_handleAbs:Float[2]
   Field position_handleRel:Float[2]
   
   Rem
      bbdoc: set absolute position of this element
   End Rem
   Method setPosition(x:Float , y:Float)
      position_abs[0] = x
      position_abs[1] = y
      position_rel[0] = 0
      position_rel[1] = 0
      viewDirty = True
   End Method
   
   
   Rem
      bbdoc: sets absolute outer size
   End Rem
   Method setSize(w:Float , h:Float)
      size_abs[0] = w
      size_abs[1] = h
      size_rel[0] = 0
      size_rel[1] = 0
      viewDirty = True
   End Method
   
   Rem
      bbdoc: sets inner size of element (changes also outer size!)
   End Rem
   Method setInnerSize(w:Float , h:Float)
      size_abs[0] = w + border_abs[0] + border_abs[2]
      size_abs[1] = h + border_abs[1] + border_abs[3]
      size_rel[0] = 0
      size_rel[1] = 0
      viewDirty = True
   End Method
   
   Rem
      bbdoc: set border sizes in pixel
   End Rem
   Method setBorder3(w:Float)
      setBorder2(w , w ) 
   End Method
   
   Rem
      bbdoc: set border sizes in pixel w = side, h=up&down
   End Rem
   Method setBorder2(w:Float , h:Float)
      setBorder(w , h , w , h) 
   End Method
   
   Rem
      bbdoc: set border sizes in pixel l=left, u=up, r=right, d=down
   End Rem
   Method setBorder(l:Float , u:Float , r:Float , d:Float)
      Self.border_abs[0] = l
      Self.border_abs[1] = u
      Self.border_abs[2] = r
      Self.border_abs[3] = d
      viewDirty = True
   End Method
   
   Rem
      bbdoc: refreshes position and size if viewDirty flag is set.
   End Rem
   Method processView()
      If Not Self.viewDirty Return
      calcSize()
      calcPos()
      setViewDirtyChildren()
      Self.viewDirty = False
   End Method
   
   Method calcPos()
      position[0] = position_abs[0] + position_handleAbs[0] + position_handleRel[0]*Self.size[0]
      position[1] = position_abs[1] + position_handleAbs[1] + position_handleRel[1]*Self.size[1]
      If parent
         position[0]:+parent.position[0] + parent.border_abs[0] + parent.position[0] * position_rel[0]
         position[1]:+parent.position[1] + parent.border_abs[1] + parent.position[1] * position_rel[1]
      EndIf
   End Method
   
   Method calcSize()
      size[0] = size_abs[0]
      size[1] = size_abs[1]
      If parent
         size[0]:+ parent.size_inner[0] * size_rel[0]
         size[1]:+ parent.size_inner[1] * size_rel[1]
      EndIf
      size_inner[0] = size[0] - border_abs[0] - border_abs[2]
      size_inner[1] = size[1] - border_abs[1] - border_abs[3]
   End Method
   
   Method overElement:Byte(x:Float,y:Float)
      If x > position[0] And x < position[0] + size[0]
      If y > position[1] And y < position[1] + size[1]
         Return True
      EndIf
      EndIf
      Return False
   End Method
   
   
   'Messages
   Field messages:TList = New TList
   
   Method recvMessage:TLink(msg:TGUI_Message)
      Return Self.messages.addlast(msg)
   End Method
   
   Method processMessages()
      For m:TGUI_Message = EachIn Self.messages
         processMessage(m)
         m.remove()
      Next
   End Method
   
   Method processMessage(m:TGUI_Message)
   End Method
End Type

Type TGUI_Message Final
   Field id:String
   Field source:TGUI_Element
   Field target:TGUI_Element
   Field content:Object
   Field _link:TLink
   
   Method send()
      _link = target.recvMessage(Self)
   End Method
   
   Method remove()
      If _link RemoveLink(_link)
   End Method
   
   Function Create:TGUI_Message(id:String , source:TGUI_Element , target:TGUI_Element , content:Object = Null)
      Local m:TGUI_Message = New TGUI_Message
      m.id = id
      m.source = source
      m.target = target
      m.content = content
      Return m
   End Function
End Type

Type TGUI_Action Abstract
   Method process()
   End Method
End Type

Type TColor
   Field a:Int
   Field r:Int
   Field g:Int
   Field b:Int
   
   Method New()
      a = 0
      r = 255
      g = 255
      b = 255
   End Method
   
   Function Create:TColor(alpha:Byte, RED:Byte, green:Byte, blue:Byte)
      Local c:TColor = New TColor
      c.Set(alpha:Byte, RED:Byte, green:Byte, blue:Byte)
      Return c
   End Function
   
   Function CreateInt:TColor(color:Int)
      Local c:TColor = New TColor
      c.SetInt(color)
      Return c
   End Function
   
   Method Set(alpha:Byte, RED:Byte, green:Byte, blue:Byte)
      Self.a = alpha
      Self.r = RED
      Self.g = green
      Self.b = blue
   End Method
   
   Method SetInt(color:Int)
      Self.a = color Shr 24
      Self.r = ((color Shl 8) Shr 8) / $10000
      Self.g = ((color Shl 16) Shr 16) / $100
      Self.b = ((color Shl 24) Shr 24)
   End Method
   
   Method Apply()
      SetColor r, g, b
      SetAlpha (Float(a) / 255.0)
   End Method
End Type


Anmerkung: Die beiden Button-Types sind nur Beispielhaft, das ganze muss natürlich noch über Styleklassen variabel gemacht werden. Zudem braucht man noch ein richtiges State-System (um zwischen verschiedenen Programstates wie Menü, Spiel, Highscores umschalten zu können) sowie ein System um Frames über anderen einzuzeichnen, zu fokussieren etc.
www.illusion-games.de
Space War 3 | Space Race | Galaxy on Fire | Razoon
Gewinner des BCC #57 User posted image

Jolinah

BeitragDo, Feb 19, 2009 18:14
Antworten mit Zitat
Benutzer-Profile anzeigen
Message-Patterns find ich auch sehr schön, allerdings - aber das ist wohl Geschmackssache - würd ich mit Messages nicht das Standardverhalten eines Objektes quasi festlegen.

Vielleicht hab ich beim Überfliegen des Codes nicht alles verstanden, aber bei deiner Implementation vom MainMenue scheint das ja immer genau die gleichen Elemente zu enthalten, oder ist es so gedacht dass ein GUI-Benutzer sich so dann seinen eigenen Type für's Menu erstellt?

Jedenfalls find ich persönlich Objekte wo man mit Methoden dynamisch Unterobjekte usw. hinzufügen kann viel schöner, als wenn der Type selber schon definiert welche Untermenüs erstellt werden etc., z.B:

Code: [AUSKLAPPEN]
Local menu:TGUI_MainMenue = new TGUI_MainMenue
Local itemFile:TGUI_MenuItem = menu.AddItem("Datei")
itemFile.AddItem("Öffnen").setOnClickAction(...)
...


Aber wie gesagt, das ist einerseits Geschmackssache und andererseits abhängig von der jeweiligen Anwendung/Verwendung Wink

Firstdeathmaker

BeitragDo, Feb 19, 2009 18:32
Antworten mit Zitat
Benutzer-Profile anzeigen
Zitat:
oder ist es so gedacht dass ein GUI-Benutzer sich so dann seinen eigenen Type für's Menu erstellt?


genau! Im Endeffekt hat man für jeden State einen eigenen Type:

TMainMenueState
TOptionState
TGameState
THighscoreState

und ein einziges globales Type welches diesen ganzen Menüs nochmal übergeordnet ist.
www.illusion-games.de
Space War 3 | Space Race | Galaxy on Fire | Razoon
Gewinner des BCC #57 User posted image

Jolinah

BeitragDo, Feb 19, 2009 22:22
Antworten mit Zitat
Benutzer-Profile anzeigen
Ah ok, ich hätte jetzt für die Buttons noch eine Basisklasse gemacht (abgeleitet von TGUI_Element), da sich das MouseOver und Click wiederholt und das MainMenü wie oben erwähnt etwas genereller gestaltet, aber ansonsten find ich es wirklich gut. Ist eigentlich das woran ich gedacht hatte, hab es vorhin wahrscheinlich nur etwas zu schnell überflogen Smile

Je nach Situation wärs vielleicht auch noch praktisch wenn einem Event mehrere Actions zugeteilt werden könnten (damit man nicht 1 Action hat, die zuviel auf einmal macht, zur besseren Wiederverwendung/Aufteilung von Actions). Aber das liesse sich bei Bedarf ja ganz einfach über eine spezielle Action machen, die mehrere untergeordnete Actions enthält und ausführt.

Neue Antwort erstellen


Übersicht BlitzMax, BlitzMax NG Beginners-Corner

Gehe zu:

Powered by phpBB © 2001 - 2006, phpBB Group