Du bist nicht angemeldet.

#1 05.04.2014 18:38

Ronny
Administrator
Ort: Chemnitz
Registriert: 08.11.2001
Beiträge: 11.704
Webseite

Blitzmax - Gameloop und Objektupdates

[EDIT Ron]
Weiterfuehrung aus dem Thread "Blitzmax - Listen/Arrays"
http://www.gamezworld.de/phpforum/viewtopic.php?id=13395

Hier geht es eher um Gameloops und wie man allgemein mit Updates und Ereignissen umgeht
[/EDIT Ron]



Dafuer empfehle ich dir von dig

Import "base.util.input.bmx"

Dann kannst du mit MouseManager.ChangeStatus() den aktuellen Zustand abspeichern (machst Du einmal im "loop"). Denn "MouseHit()" was BlitzMax anbietet, setzt sich durch die Abfrage zurueck. -- doof wenn man das in einem "Loop" mehrfach abfragen will.


bye
Ron


sigimg2.php?id=1

Offline

#2 05.04.2014 20:53

sushiTV
Mitglied
Ort: Leipzig
Registriert: 10.03.2014
Beiträge: 496

Re: Blitzmax - Gameloop und Objektupdates

Sooo,

eher nur für dich Ron, hier mal das was ich jetzt gemacht habe.

Also ich habe es hinbekommen das es so wie ich mir das vorstellte zumindest ersteinmal funktioniert tongue
Der Code naja ... gibt bestimmt genug leute die sich totlachen würden wenn se sich das anschauen tongue

(Es ist schlimmer geworden als was ich vorher gedacht hätte)

http://www.workupload.com/file/N6GnhE57 (zip mit exe,png und bmx)

als kontrollbild:

screen_winz0014qsif.png
So sollte der Screen aussehen mit geöffnetem Untermenü smile (Maus ist da bei "Bank")

Derzeit hat ja auch nur "Zur Stadt" ein Untermenü

Davon abgesehen das ich jetzt noch nicht wirklich in's Framework geschaut habe, kannst du mir ja aber ansonsten vielleicht noch ein paar tipps geben.

Die verwendeten Bilder sind dieses mal alle CCO also Public Domain.

gruß
sushi

Nachtrag: Das Bild da oben links hat eigentlich auch eine Schwarze Kontur, die wird aber aus irgendwelchen gründen nicht dargestellt.

Beitrag geändert von sushiTV (05.04.2014 20:58)


das Leben ist ein scheiß Spiel, hat aber ne geile Grafik

Offline

#3 05.04.2014 21:02

Ronny
Administrator
Ort: Chemnitz
Registriert: 08.11.2001
Beiträge: 11.704
Webseite

Re: Blitzmax - Gameloop und Objektupdates

Print "Create Image"
bild_bg = CreateImage (800, 600)
bild_bgp1 = CreateImage (254, 154)

Print "Load Image"
bild_bg = LoadImage ("bg_01.png")
bild_bgp1 = LoadImage ("bgp_weingut.png")

Das ist unnoetig - du musst kein "createImage" machen. CreateImage erzeugt ein neues Bild (mit "Muellpixeln" drin)..

es reicht das "LoadImage".


Sleep 20

Nimm lieber "Delay 20" - dann laeufts auch unter Linux biggrin. Generell wuerde ich dir aber hier zum Framework raten. - Du vermengst noch "Logik" und "Zeichnen".


Eine "GameLoop" sieht immer so aus:

While not SpielbeendenVariable = True
  Update()
  Render()
Wend

Natuerlich kann man jetzt - wie bei unserem Framework die Updates und Renders auf eine bestimmte Menge pro Sekunde begrenzen (um die CPU und GPU zu schonen)

Ich bau jetzt mal den "notwendigen" Teil um - und poste gleich den Code hier rein. (Ich baue nur den Teil um, der notwendig ist um das zum laufen zu bekommen.


bye
Ron


sigimg2.php?id=1

Offline

#4 05.04.2014 21:31

Ronny
Administrator
Ort: Chemnitz
Registriert: 08.11.2001
Beiträge: 11.704
Webseite

Re: Blitzmax - Gameloop und Objektupdates

Ich habe das jetzt ein wenig aufgeraeumt - da war einiges wohl doppelt.

Manches sieht nun aus wie "doppelte Arbeit". Man kann bestimmte Dinge ("Menues verschieben" durchaus auch bei "Render" machen ... aber das ist Geschmackssache).

Generell musst du Dinge aufsplitten: Logik ("Sachen verkaufen", Partikel erzeugen/bewegen/... ) und Rendern (also Buttons zeichnen usw.)

Ich habe jetzt exemplarisch bestimmte Zeilen von Dir mit "Dig-Code" ersetzt. Rein damit du ein wenig "Geschmaekle" dran finden kannst tongue.


'
'My first BlitzMax program
'Datum: 2014.04.05
'Autor: ~s~

Import "base.util.helper.bmx" 'hilfsfunktionen
Import "base.util.event.bmx" 'mit neuem DigUpdate nicht noetig, hab das beim deltatimer vergessen :D
Import "base.util.deltatimer.bmx"

'Include BRL.PNGLoader


Graphics 800, 600, 0, 0

Global exitApp:int = FALSE 'spiel beenden?

Global bild_bg:TImage
Global bild_bgp1:TImage
Global Ustatus:Int = - 1
Global MenuStatus:Int = - 1
Global Hauptmenu:TMenu[]
Global Stadtmenu:TMenu[]
Global Dummy:String



Type TPlayer
	Field Home:String
	Field Konto:Float
	Field Keller:TKeller
End Type

Type TKeller
	Field Flaschen:Int
	Field Lagerplatz:Int
End Type


Type TMenu
	Field x:Int
	Field y:Int
	Field b:Int
	Field h:Int
	Field name:String
	Field status:Int
	
	Method Draw()
		If status = 0 Then
			SetColor(232, 222, 191)
		ElseIf status = 1 Then
			SetColor(223, 144, 26)
		ElseIf status = 2 Then
			SetColor(243, 202, 11)
		EndIf
		DrawText(name, x, y)
		'Farbe wieder zuruecksetzen - nicht vergessen :D
		SetColor(255, 255, 255)
	End Method
End Type

Function MenuInit()
Local x, y, h, b:Int

	x = 75
	y = 180
	h = 19
	b = 120

	For i = 0 To 5
		Local NewMenu:TMenu
		NewMenu = New TMenu
		NewMenu.x = x
		NewMenu.y = y + (i * h)
		NewMenu.b = b
		NewMenu.h = h
		NewMenu.status = 0
		Hauptmenu:+ [NewMenu]
	Next

	x = 75 + h
	y = 180 + 19
	h = 19
	b = 120

	
	For i = 0 To 6
		Local NewMenu1:TMenu
		NewMenu1 = New TMenu
		NewMenu1.x = x
		NewMenu1.y = y + (i * h)
		NewMenu1.b = b
		NewMenu1.h = h
		NewMenu1.status = 0
		Stadtmenu:+ [NewMenu1]
	Next

	
	Hauptmenu[0].name = "Zur Stadt"
	Hauptmenu[1].name = "Weinberg"
	Hauptmenu[2].name = "Schuppen"
	Hauptmenu[3].name = "Lagerhaus"
	Hauptmenu[4].name = "Kellerei"
	Hauptmenu[5].name = "Wohnhaus"
	
	Stadtmenu[0].name = "Sabotage"
	Stadtmenu[1].name = "Bank"
	Stadtmenu[2].name = "Arbeitsamt"
	Stadtmenu[3].name = "Grosshandel"
	Stadtmenu[4].name = "Einzelhandel"
	Stadtmenu[5].name = "Rathaus/Amt"
	Stadtmenu[6].name = "Werbebuero"
	
End Function



Function MenuDraw()
Local x:int = 75, y:int = 180, h:int = 19, b:int= 120

	Hauptmenu[0].Draw()

	If Ustatus = 0 And MenuStatus = 1 Then
		MenuStatus = -1
		SetColor(129, 8, 39)
		DrawRect(x, y + h, b, h * 6)
				y = y + (7 * h)
		'Farbe wieder zuruecksetzen - nicht vergessen :D
		SetColor(255, 255, 255)

		For i = 1 To 5
			Hauptmenu[i].y = y + (i * h)
			Hauptmenu[i].Draw()
		Next
		
		For i = 0 To 6
			Stadtmenu(i).Draw()
		Next


	ElseIf MenuStatus = 1
		MenuStatus = - 1
		SetColor(129, 8, 39)
		DrawRect(x, y + h, 120, 380)
		'Farbe wieder zuruecksetzen - nicht vergessen :D
		SetColor(255, 255, 255)
		For i = 1 To 5
			Hauptmenu[i].y = y + (i * h)
		Next
	EndIf
	
	
	
End Function






'spiellogik aktualisieren
Function UpdateWorld:int()
	'refresh mouse/keyboard
	MouseManager.ChangeStatus()
	KeyManager.ChangeStatus()

	For i = 0 To 5
		If THelper.MouseIn(Hauptmenu[i].x, Hauptmenu[i].y, Hauptmenu[i].b, Hauptmenu[i].h) Then
			Hauptmenu[i].status = 1
			If MouseManager.IsDown(1) Then
				Hauptmenu[i].status = 2
				Ustatus = i
				MenuStatus = 1
			EndIf
		Else
			Hauptmenu[i].status = 0
		EndIf
	Next


	If Ustatus = 0 Then
		For i = 0 To 6
			If THelper.MouseIn(Stadtmenu[i].x, Stadtmenu[i].y, Stadtmenu[i].b, Stadtmenu[i].h) Then
				Stadtmenu[i].status = 1
				If MouseManager.IsDown(1) Then
					Stadtmenu[i].status = 2
	'				MenuStatus = 1
				EndIf
			Else
				Stadtmenu[i].status = 0
			EndIf
		Next
	EndIf

	'spiel beendbar mit ESC
	If KeyManager.IsHit(KEY_ESCAPE) then exitApp = TRUE
End Function


Function RenderWorld:int()
	DrawImage bild_bg, 0, 0
	DrawImage bild_bgp1, 15, 15

	SetColor(232, 222, 191)
	DrawText (Dummy, 280, 47)
	'Farbe wieder zuruecksetzen - nicht vergessen :D
	SetColor(255, 255, 255)

	MenuDraw()

	For i = 0 To 5
		Hauptmenu[i].Draw()
	Next

	If Ustatus = 0 Then
		For i = 0 To 6
			Stadtmenu[i].Draw()
		Next
	EndIf


	Flip 0 'alles gezeichnete "an die grafikkarte schicken"
End Function



'wir sagen dem deltatimer, was die update- und renderfunktionen sind
GetDeltaTimer()._funcUpdate = UpdateWorld
GetDeltaTimer()._funcRender = RenderWorld
'und dass wir 30 Logikupdates und 60 Renders pro Sekunde wollen
GetDeltaTimer().Init(30, 60)

'Sachen initialisieren
MenuInit()

Dummy = "Datum: Jan 1980     Ort: Weingut Sachsen     Konto: 100.000"
Print "Load Image"
bild_bg = LoadImage ("bg_01.png")
bild_bgp1 = LoadImage ("bgp_weingut.png")


'game loop
Repeat
	'run the deltatimer's loop
	'which runs the hooked update/render function
	GetDeltaTimer().loop()

	
	'falls du mit events arbeitest - muessen die natuerlich hier
	'abgearbeitet werden
	'EventManager.update()
Until AppTerminate() Or exitApp


End

Ich hoffe Du fuehlst dich jetzt nicht von mir ueberrannt.

Uebungstodos fuer dich:

Statt in Updateworld diese For-Schleife mit den Menuepunkten und Ueberpruefungen zu haben: Lege eine "Method Update:int()" fuer den Type TMenue an ... und rufe dann nur noch fuer menues "xyzmenu.Update()" auf.

Jedes Menue fuer sich selbst checkt dann ab, ob die Maus drin ist oder nicht.
Versuche sozusagen die Objekte fuer sich selbst sorgen zu lassen. Dann weisst Du spaeter auch, an welcher Stelle die Menuepunkte veraendert/aktualisiert werden und es liegt nicht mehr verstreut im Code herum.


bye
Ron


sigimg2.php?id=1

Offline

#5 05.04.2014 22:03

sushiTV
Mitglied
Ort: Leipzig
Registriert: 10.03.2014
Beiträge: 496

Re: Blitzmax - Gameloop und Objektupdates

Ronny schrieb:

Jedes Menue fuer sich selbst checkt dann ab, ob die Maus drin ist oder nicht.
Versuche sozusagen die Objekte fuer sich selbst sorgen zu lassen. Dann weisst Du spaeter auch, an welcher Stelle die Menuepunkte veraendert/aktualisiert werden und es liegt nicht mehr verstreut im Code herum.


Genau das ist mein Problem zu verstehen wie man etwas "vernünftig" coded tongue

Wenn ich da hier und da in TVT-Code geschaut habe und versucht habe das zu verstehen, hatte ich aber manchmal schon das gefühl das das eine oder andere ganz alleine wie von "zauberhand" funktioniert ....

Ich denke das meine blickrichtung und denkweise da ganz einfach auch falsch ist.

Ich denke ich muss alles Organisieren im Code ....
Und das wird dann meist halt sehr Unorganisiert und verstreut tongue

Ich fühle mich gar nicht "überrant" im gegenteil, ich bin erstaund wie du mit ein paar wenigen änderungen das aber schon deutlich besser machen kannst und das in kurzer zeit. (ich hoffe jetzt zumindest das dies dich nicht zu viel zeit gekostet hat.=

Und besonders Interessant dabei ist ja jetzt .... Anfänger Coding und nutzen des Frameworks und das auch im Vergleich smile

Ich muss mir das aber trotzdem auch noch etwas genauer betrachten smile

gruß
sushi

Beitrag geändert von sushiTV (05.04.2014 22:05)


das Leben ist ein scheiß Spiel, hat aber ne geile Grafik

Offline

#6 05.04.2014 22:11

Ronny
Administrator
Ort: Chemnitz
Registriert: 08.11.2001
Beiträge: 11.704
Webseite

Re: Blitzmax - Gameloop und Objektupdates

Ok ... also Du hast ja nun schon diese Typen angelegt.

Wenn Du nun mit "Methoden" arbeitest (Das sind "Funktionen" die fuer ein spezielles Objekt - also bspweise ein spezifisches Menu - aufgerufen werden), dann bietet es sich dort an mit "Update" und "Render" (oder "Draw") loszulegen.

Innerhalb von der Methode "Update" des Types TMenue wuerdest du also fuer diesen Menuepunkt spezifische Updates machen: heisst schauen ob die Maus drueber ist, ob geklickt worden ist usw.


Das alles erlaubt dann spaeter naemlich etwas ganz wichtiges: "Ueberschreiben". Wenn du verschiedene Menuearten hast - behandeln manche Menuearten ja die Maus  ganz anders. Sie leuchten auf oder was auch immer. Und bei denen ueberschreibt man dann einfach diese "Method Draw:int()" und macht dort alles anders biggrin



Deine Blickrichtung... ist nicht falsch, nur noch nicht richtig ausgerichtet - dass kommt mit der Uebung von ganz alleine.

In der "UpdateWorld" kannst Du dann bspweise die "Zeit" messen ... sind X Millisekunden vorbei: naechste Runde. Oder alternativ: "Naechste Runde"-Text zum anklicken und der erhoeht die Runde.
... aber er sagt nicht einfach "round = round +1", nein es wird die Funktion "NextRound" aufgerufen. In dieser machst Du dann die rundenspezifischen Sachen ... "GenerateNewWeather()" usw.

Immer so, dass sich alles gut finden laesst.


Edit: und wenn du dich dann immer weiter damit "auskennst", kannst Du dich dann an das "Screensystem" des Frameworks ranwagen. Denn gerade fuer so ein Spiel ist das natuerlich optimal.
Screens sind sozusagen "Bildschirme" - Startbildschirm, Levelladebildschirm. Hier kann es aber auch sein: "Hauptmenue", "Stadt", "Lager" ... und fuer jeden dieser Bildschirme kannst Du dann eigene "Update" und "Render" bestimmen - oder aber (das wuerde ich dann noch erklaeren - oder ein Beispiel machen) fuer bestimmte Dinge "Vorlagen" anlegen. Alle "Menu"bildschirme basieren dann auf einem "TMenuScreen" der immer einen blauen Hintergrund zeichnet oder anderes.

Wenn man zwischen den Bildschirmen wechselt kann man "Effekte" einbinden (Schwarzblende oder aehnliches). Vereinfacht vieles. Statische Elemente ("Dekoration") laesst sich dann ohne groesseren Aufriss in die "Layer" eines "Screens" packen. Andere Frameworks machen das zumindest so ... bei TVTower sind wir noch nicht soweit (das muss ich noch umstellen).

So... bastel Du mal weiter.

PS: MouseManager.GetX(), MouseManager.GetY() - und MouseManager.getScrollwheelMovement() sind schoene Hilfsfunktionen. Auch "IsClicked(1 oder 2)" (wartet aufs "loslassen" der Maustaste) kann dir ganz hilfreich sein.

Ich kommentiere jetzt noch ein paar Framework-Codes - vielleicht hilft es dir dann weiter.


bye
Ron


sigimg2.php?id=1

Offline

#7 05.04.2014 22:27

sushiTV
Mitglied
Ort: Leipzig
Registriert: 10.03.2014
Beiträge: 496

Re: Blitzmax - Gameloop und Objektupdates

Ronny schrieb:

Innerhalb von der Methode "Update" des Types TMenue wuerdest du also fuer diesen Menuepunkt spezifische Updates machen: heisst schauen ob die Maus drueber ist, ob geklickt worden ist usw.

das ist halt das was ich nicht so wirklich verstehe.

Ich habe doch folgendes gemacht:

eine function mousehandling() welche in sich dann alle möglichen zustände prüft und dann eventuelle variablen ändert und reaktionen auslößt.
(okay, ich gebe zu, das die reaktionen wohl allgemein dann eher vom Typ "Render" waren)

Das was du meinst, ist doch aber das ein angelegtes objekt dann selber checkt ob sich die mouse in "ihrem" (ist ein objekt eine sie? *lach*) wirkungsbereich befindet oder nicht.

wenn dieses objekt mit seinen eigenschaften von ausdehnung/größe  oder standort/platzierung auf dem bildschirm immer selbständig checkt in wie weit es von der Mouse berührt wird (auf ereignisse reagieren muss) ....

dann ist das doch genau die gegenteilige herrangehensweise als was ich das getan habe?

Ich habe mit den Augen der Mouse gesehen *blinkerblinker* smile

aber wie löße ich denn so ein selbstständiges überwachen aus .... das ist mir alles ein rätzel smile (das objekt schaut die Mouse an ? )

gruß
sushi

Beitrag geändert von sushiTV (05.04.2014 22:36)


das Leben ist ein scheiß Spiel, hat aber ne geile Grafik

Offline

#8 05.04.2014 22:33

sushiTV
Mitglied
Ort: Leipzig
Registriert: 10.03.2014
Beiträge: 496

Re: Blitzmax - Gameloop und Objektupdates

ahhhhh ......

ich lege eine methode update eines objektes an und löße diese in der loop aus?

in der update wird geprüft ob die mouse sich im eigenen bereich befindet und eventuell ein butten gedrückt ist etc. .....

je nach dem kann ich reagieren und darauf ein "render" des objects aus der update auslösen ?

ich muss halt in der gameloop oder so nur permanent bei allen objekten dann das update() auslößen ?

gruß´~s~

Ich korrigiere selber:

updates in der update()
gerendert wird dann über render()

deswegen wohl auch diese trennung, damit das nacheinander erfolgt und nicht quer durcheinander

Beitrag geändert von sushiTV (05.04.2014 23:05)


das Leben ist ein scheiß Spiel, hat aber ne geile Grafik

Offline

#9 05.04.2014 23:01

Ronny
Administrator
Ort: Chemnitz
Registriert: 08.11.2001
Beiträge: 11.704
Webseite

Re: Blitzmax - Gameloop und Objektupdates

ja in der "gameloop" muss immer artig geupdated werden.

Damit du die "gameloop" aber nicht selber erweiterst, haben wir ja die Funktionen "UpdateWorld" und "RenderWorld" erstellt.

Innerhalb der UpdateWorld muss alles aktualisiert und bei RenderWorld alles gezeichnet werden.

Alles natuerlich im Sinne von: "alles aktive".


auf TMenue gemuenzt:
Innerhalb der UpdateWorld()-Funktion rufst du fuer jeden Menuepunkt "update" auf (weil du ja derzeit das "Menue" aktiv hast - irgendwo darstellst und somit auf Mausklicks reagieren willst).

Innerhalb dieser "Update()"-Methoden von TMenue ueberpruefst du dann ob was passiert ist (Maus drueber oder so).


An deinem Code:

Function UpdateWorld:Int()
...

	For i = 0 To 5
		If THelper.MouseIn(Hauptmenu[i].x, Hauptmenu[i].y, Hauptmenu[i].b, Hauptmenu[i].h) Then
			Hauptmenu[i].status = 1
			If MouseManager.IsDown(1) Then
				Hauptmenu[i].status = 2
				Ustatus = i
				MenuStatus = 1
			EndIf
		Else
			Hauptmenu[i].status = 0
		EndIf
	Next
...
End Function

wird dann:

Function UpdateWorld:Int()
...

	For i = 0 To 5
		HauptMenu[i].Update()
	Next
...
End Function

Natuerlich muss der nun fehlende code wohin... und zwar bei TMenue in eine Updatemethode

Type TMenue
...

	Method Update:Int()
		'standardmaessig ausschalten
		status = 0

		'wenn die maus nicht drin ist - nix machen
		If not THelper.MouseIn(x, y, b, h) Then return FALSE

		'du kannst mit "self." explizit hinweisen, was du meinst
		'".status" (also nur punkt) wuerde  explizit eine "global" variable
		'ansprechen
		self.status = 1
		If MouseManager.IsDown(1) Then
			self.status = 2
			Ustatus = i
			MenuStatus = 1
		EndIf
	End Method
...

Du siehst gleich, wie ich die "if"-Verschachtelung entfernt habe - bringt manchmal mehr Uebersicht, laesst sich aber nicht immer so machen (wenn man fuer "if" viel zu tun hat und fuer "else" aber auch). Wenn es aber nur darum geht, "wenn etwas der fall ist, dann mache" - bietet es sich an einfach abzubrechen, wenn es nicht der fall ist (Ausschlusskriterium).


Man kann diese for-schleifen auch anders schreiben

for local i:int = 0 to 5
	HauptMenu[i].Update()
Next

for local i:int = 0 to HauptMenu.length -1 'arrays fangen ja mit 0 an
	HauptMenu[i].Update()
Next

for local menu:TMenu = EachIn HauptMenu
	menu.Update()
Next

Damit musst Du nicht immer wissen, wieviele Menues du drin gespeichert hast.


EDIT: Warum willst du alles vom Standpunkt der Maus aus betrachten? Was passiert dann, wenn du auch auf die Tastatur achten moechtest? Oder Touchscreens?

Statt "if mousedown then status=1;ustatus=2..." kannst Du dir auch weitere Hilfsmethoden schreiben: "SetActive()" - die dann den status aendert. Vorteil ist dann: du hast eine zentrale Anlaufstelle, den Status zu aendern. Denn dies kann mittels Mausklick ausgeloest werden - oder mittels eines Wetterereignisses biggrin.

Ein "Menupunkt.SetActive()" kann natuerlich auch eine globale "welcher menupunkt ist aktiv"-variable ueberschreiben. Anstatt also in jedem Menuepunkt zu speichern, ob er "aktiv" ist, speichert man einfach einmalig WELCHER punkt aktiv ist (oder "null" fuer keinen). Jeder Menupunkt kann dann mit "if self = aktiverMenupunktGlobaleVariable then ..." ueberpruefen ob er der aktive ist. Verkuerzt macht man das dann in einer weiteren Helfermethode:

Methode IsActive:int()
  return (self = aktiverMenupunktGlobaleVariable)
End Method

und schwupps kann man mit "Menupunkt.IsActive()" schauen ob der Menupunkt der Aktive ist. Wenn man hingegen bei jedem Menupunkt speichert, ob er aktiv ist - kann man im Eifer des Gefechts vergessen einen Punkt als nichtaktiv zu setzen und schwupps hat man mehrere aktive ... bloed biggrin.


So - und bevor nun dein Kopf raucht - experimentier ein wenig weiter und lass dich nicht vom alten Quatschkopf Ronny so zutexten.

Freundin kommt eh bald von Arbeit - da ists hier vorbei mit der Computerei.



bye
Ron


sigimg2.php?id=1

Offline

#10 05.04.2014 23:11

sushiTV
Mitglied
Ort: Leipzig
Registriert: 10.03.2014
Beiträge: 496

Re: Blitzmax - Gameloop und Objektupdates

Okay,

ich danke dir, ich denke das ich auf grund des codebeispiels und der erklärung und das mir da ebend selber schon ein licht aufgegangen ist und du das jetzt nochmals mit code belegt hast .....

ich das jetzt so ein bissel begriffen habe ...

das hat bisher keine erklärung oder code geschaft tongue

das dauerhaft umzusetzen wird nicht direkt gelingen, wie du ja sagtest, kommt einiges auch erst mit übung smile

thx
~sushi~


das Leben ist ein scheiß Spiel, hat aber ne geile Grafik

Offline

#11 05.04.2014 23:16

Ronny
Administrator
Ort: Chemnitz
Registriert: 08.11.2001
Beiträge: 11.704
Webseite

Re: Blitzmax - Gameloop und Objektupdates

Hab obigen Beitrag noch einen "Edit" verpasst.


Was sicher auch noch ein kleines Uebungsbeispiel fuer dich ist:

mit "HideMouse()" kannst du den Systemmauscursor verschwinden lassen... Wenn man nun an der Stelle "MouseManager.GetX(), MouseManager.GetY()" ein Mauscursor-Bild zeichnet - hat man einen schoenen InGame-Cursor. Wenn die "Spitze" des Mauszeigers dann nicht exakt die Position einnimmt - verschiebe die Grafik einfach ein wenig (...GetX() - 5, ... GetY() -10).

Du hast Dir uebrigens mit "Menuepunkten" den optimalen Kandidaten fuer ein Eventsystem rausgesucht, aber dazu spaeter mal mehr (wenn du magst).


bye
Ron


sigimg2.php?id=1

Offline

#12 05.04.2014 23:29

sushiTV
Mitglied
Ort: Leipzig
Registriert: 10.03.2014
Beiträge: 496

Re: Blitzmax - Gameloop und Objektupdates

Hmmm,

also das erste bei "Winzer" war ja jetzt das es für's erste ein "Klone" bleiben sollte ..... also "Screen" erstellen (auch das hätte ich mit DrawRect machen können) ....

und dann Halt das erste Handling im Spiel und das sind Menüs .... und darauf dann die Bildschirme also "Räume" .... etc. und so weiter ....

so war jetzt mein gedanke ....
Grundlegend ist doch ersteinmal der hauptaufbau (fenster, menü - steuerung) ?

Für das spiel letzten endes interessanter ist alles das was dahinter abläuft .... nur das kann es nicht wenn man es ja nicht steuern kann tongue



Menüpunkte und Eventsystem ?
immer raus damit tongue

nachtrag:

okay, dein nochmaliger nachtrag deines beitrags hat den noch etwas mehr erweitert smile und interessante aspekte beleuchtet tongue

das muss ich erst noch kappieren smile

ich denke mal deine freundin ist nu auch da, dann bis bald smile

Beitrag geändert von sushiTV (05.04.2014 23:40)


das Leben ist ein scheiß Spiel, hat aber ne geile Grafik

Offline

#13 05.04.2014 23:56

sushiTV
Mitglied
Ort: Leipzig
Registriert: 10.03.2014
Beiträge: 496

Re: Blitzmax - Gameloop und Objektupdates

Ronny schrieb:

EDIT: Warum willst du alles vom Standpunkt der Maus aus betrachten? Was passiert dann, wenn du auch auf die Tastatur achten moechtest? Oder Touchscreens?

Ich habe darüber heute abend auch nachgedacht.

(vielleicht habe ich ja deswegen keine freundin?)

Ich denke das es für mich als viel unaufwendiger und weniger rechenlastig erschien der mouse zu folgen und nur wenn diese "sensible" zonen erreicht auch etwas zu verändern etc. ....

Aber das vernünftige ist natürlich das die Objekte selber die überprüfung machen - vorallem programmiertechnisch .... stell dir mal vor das winzer hätte 250 buttons die man anklicken kann? aus der sicht der mouse das zu checken? ein irrsinn smile dann doch lieber das aller paar millisecunden alle objekte checken ob die mouse bei ihnen ist und eventuell gedrück oder nicht smile

wobei mir das auslößen des Update() auch noch nicht 100% klar ist ....

aber ich denke, das du da eventuell mit dem Thema Eventsystem da vielleicht nocht etwas weiter ausholen wolltest.

gruß
sushi

Beitrag geändert von sushiTV (06.04.2014 00:19)


das Leben ist ein scheiß Spiel, hat aber ne geile Grafik

Offline

#14 06.04.2014 09:39

Ronny
Administrator
Ort: Chemnitz
Registriert: 08.11.2001
Beiträge: 11.704
Webseite

Re: Blitzmax - Gameloop und Objektupdates

vielleicht habe ich ja deswegen keine freundin?

Dafuer gibt es meist einfachere Gruende: Man ist seltsam, spricht keine neuen Leute an und verkehrt mit einem "festen" Freundeskreis, man sieht einfach scheisse aus - oder: man verdaut noch die letzte Beziehung bzw. ist noch im "mit der Ex-Vergleichen"-Modus. So Scherz beiseite.


Das mit dem Eventsystem ist natuerlich keine "Magie" - wie du schon richtig erkannt hast, muss ja alles irgendwie ein "Update" haben und immer schoen ausgefuehrt werden. Das "alles" kann man dann wieder filtern. Bei den GUI-Elementen uebergibt man dem GUIManager bestimmte "States" und es werden dann alle GUIElemente "uebersprungen" die sich fuer diesen State interessieren. Dies kann man nun so bewerkstelligen: jeder "State" hat eine Liste mit interessierten Elementen - erschwert das Management wenn ein Objekt mal mehr mal weniger States beobachtet. Oder man hat eine Liste mit GUIElementen - und fragt pro Durchgang alle Elemente ab. Hat das Element Interesse am "State", tut es was, ansonsten wird es uebersprungen.

Eventuell kannst Du dir nun denken, dass beide "Ansaetze" sogenannte "Tradeoffs" besitzen. Wenn Du jetzt 1 Million Elemente aber nur 2 "States" benutzt - ist es bloed immer 1 Mio Elemente abzufragen, dann waere es sinniger zwei Listen zu fuehren und immer nur die gewollte abzufragen - man spart da ja 500.000 Abfragen.
Bei einem Partikelsystem wuerde man also statt einer grossen Liste eher alles so gruppieren, dass ein "ParticleEmitter" diese in einer eigenen Liste gruppiert. Problem waeren dann hier aber "Kollisionen" - wenn also ein PartikelA von EmitterA mit PartikelB von EmitterB kollidieren soll. Da EmitterA EmitterB nicht kennt - und es ja auch Emitter120440 geben koennte, muss hier wieder geschaut werden, ob es sich immer lohnt so vorzugehen oder ob nicht doch ein anderer Ansatz praktischer waere. Im Fall von Partikeln waere es sowas wie eine "Quadrantenliste" - ein Partikel ist immer zusaetzlich in einer Liste des jeweiligen Quadranten in dem er sich befindet - bzw entsprechend seiner Groesse koennen dass auch "mehrere Quadranten" sein. Damit ist dann gewaehrleistet, dass der Partikel immer nur einen "Bruchteil" der gesamten Partikel "gegenchecken" muss. Tradeoff ist hier: mit jeder Bewegung muss er ja abaendern in welchen Listen er so gespeichert ist (hier kommt hinzu, dass man da wohl besser mit Arrays arbeiten wuerde statt mit Listen - bzw mit Arrays die man aller X Sekunden "bereinigt" um Luecken zu schliessen).

Ok zurueck zum Thema "Menues": Jeder Menupunkt wuerde in einem Eventsystem trotzdem sein "Update" ausfuehren. Irgendwie willst Du ja auf "Klicks" mit irgendwas reagieren. Das jetzt in einer einzigen grossen Funktion abzurufen ist umstaendlich und fehleranfaellig. Deswegen kannst du verschiedene Moeglichkeiten probieren:

a) du gibst jedem TMenu ein "Field clickFunc:int()" - dann kannst du jedem Menupunkt eine eigene "Klickfunktion" zuweisen (kennt man von VisualBasic, Delphi, wenn man doppelt auf ein GUIElement geklickt hat - und die "onclick"-Funktion kreiiert worden ist)

Type TMenu
  'eine Funktion die int zurueckgibt und das
  'geklickte Menu im Parameter erwartet
  'praktisch um "mehrere Menus" in einer Funktion
  'abarbeiten zu koennen
  Field onClickFunc:int(sender:TMenu)

...
  Method Update:int()
    if angeklickt then onClick()
  End Method

  'extra onClick-Methode damit man die in einem
  'erweiterten Menutyp ueberschreiben koennte
  Method onClick()
    'ist eine funktion "verknuepft", dann ruf die auf
    'menu selbst ist der "sender"
    if func then onClickFunc(self)
  End Method
...
End Type

'beispiel click handler - der nicht innerhalb des
'TMenu stehen muss - aber auch koennte
Function menuOnClick:Int(sender:TMenu)
  'jemand hat die Funktion nicht korrekt aufgerufen
  if not sender then return False

  print sender.name + " wurde angeklickt"  
End Function

'beispiel click handler - der nicht innerhalb des
'TMenu stehen muss - aber auch koennte
Function specialMenuOnClick:Int(sender:TMenu)
  'jemand hat die Funktion nicht korrekt aufgerufen
  if not sender then return False

  print sender.name + " wurde angeklickt - und das ist sonderbar"  
End Function



local menu:TMenu = new TMenu
menu.onClickFunc = menuOnclick

local menu2:TMenu = new TMenu
menu.onClickFunc = menuOnclick

local menu3:TMenu = new TMenu
menu.onClickFunc = specialMenuOnClick

...

Natuerlich kann man auch ein "Standardverhalten" (eine DefaultOnClick-Funktion) festlegen und nur bei Bedarf eine andere "drueberlegen". In obigem Fall: "menuOnClick" wuerde "DefaultOnClick" heissen und in TMenu gespeichert werden. Das die Field-Zeile dann "Field onClickFunc:int(sender:TMenu) = TMenu.DefaultOnClick".

Wenn Du unterscheiden willst, mit was geklickt worden ist: entweder neue Funktionen "onRightclick" oder ein weiterer Parameter in die Klickfunktionen um uebergeben mit welcher Maustaste das geschah (oder an welcher Koordinate).


b)
Du kannst das Eventsystem nutzen. Statt dann "onClick" aufzrufen, feuerst du einen selbstbenannten Event ab - mit dem Objekt als "Sender", der "Payload" (also "Anhaengsel") wer geklickt hat, mit was er geklickt hat, usw. .
Bis dahin passiert dann erstmal nix - praktisch wenn nicht jedes Element auf Mausklicks reagiert.

Bist Du an einem Klick auf ElementXY interessiert, erstellst Du dir einen "EventListener" (siehe Demoapp - die macht das fuer die GUIObjektklicks) der nur auf das gewuenschte Objekt reagiert (oder nur auf den "Typ" oder ...). Dieser Listener fuehrt im einfachsten Fall eine uebermittelte Funktion aus sobald ein solches Event abgefeuert wird. Diese Funktionen sehen dann aehnlich aus wie die Funktionen aus Variante A) - nur mit anderen Parametern (das Eventobjekt wird uebergeben).


So was ist der Vorteil?

a) Kein zusaetzlicher Overhead eines Eventsystems, es ist sofort klar, was wie passiert


b) Das Eventsystem kapselt Dinge voneinander ab. Ein "Listener" muss den "Sender" nicht kennen. Es werden nur relevante Informationen uebermittelt. Ein Objekt A kann also im Quellcode hinzukommen ohne dass wir Objekt B dies mitteilen muessten. Vorallem bei "modularem Code" ist dies ein enormer Vorteil - man muss nicht alles in eine einzige grosse Datei schreiben - oder in jeder Datei die anderen "referenzieren". Das ganze nennt sich "Abhaengigkeit".

- ObjektA.bmx - importiert ObjektB.bmx
- ObjektB.bmx will aber Informationen von ObjektA.bmx - muesste sie also auch importieren
-> nicht moeglich.
Also kann man nicht "importieren" (Import = Code der autark ist, "Include" ist eher so, dass diese Zeile dann mit dem Inhalt der Datei ersetzt wird) sondern muss "include" nutzen. Man koennte also gleich eine Datei schreiben: ObjektA_und_ObjektB.bmx
Doof, wenn man in einem Projekt dann mal nur ObjektA braucht ... ohne den Kram von ObjektB.

Durch die Anonymisierung mittels des Eventsystems loest man dieses Problem. Hat aber dafuer einen Mehraufwand - man muss immer "Casten" ob der Sender (gespeichert als "Object") vom richtigen Typ ist.

Ein weiterer Vorteil ist aber: es kann bequem mehrere Listener geben, die sich nicht kennen muessen. Im Spiel waere das bspweise sowas:
- ein Listener interessiert sich fuer Menuklicks um den naechsten Bildschirm zu laden (entsprechend geklicktem Menu)
- ein anderer Listener interessiert sich fuer Menuklicks im allgemeinen - um einen Klicksound abzuspielen

Der Teil im Code, der sich um Bildschirme kuemmert, muss also nix vom Soundsystem wissen - ob das nun da ist oder nicht, scheissegal.

Auch koennte ein MenuB einen Listener registrieren, der darauf reagiert, wenn auf MenuA geklickt wird.



Dennoch: nicht fuer alles Events einsetzen: denn auch hier gilt: es gibt Tradeoffs - vorallem fuer einfache Dinge (wo man weiss: das "interessiert" nur das eine Objekt/den einen Typ) kann man das klassische Prinzip mit der verknuepfbaren Funktion nutzen (wie in Beispiel a) ).


Nochmals kurz zur "Magie" - in TVTower funktioniert auch nix "komplett automatisch" ... sondern irgendwo wird ein Stein angeschubst - der andere dann umkippt. Ein "TPlayer.UpdateAll" ruft die Methode "Update" fuer jeden Spieler auf. Diese Methode ruft wiederum die Updates fuer andere Objekte von "TPlayer" auf... diese rufen dann wieder ...

Im Quellcode sieht dass dann natuerlich nach wenig aus - eine Zeile mit einem Update ... aber nach hinten kommt der Rattenschwanz.

Mach dir wegen ein paar Updates pro "UpdateWorld" keine Gedanken. Wenn es dann doch mal zuviel wird (viele Ueberpruefungen obwohl gar nix gemacht werden muss) - dann wechsel zu einem eventbasierten System, denn dass macht ja - wenn nix passiert - nur die Ueberpruefung ob ein Event vorliegt biggrin

Die groesste Last in einem Spiel erzeugt der "Zeichnen"-Teil ... Beides limitieren wir ja aber - "Logik" 30x pro Sekunde, "Rendern" bis zu 60x (Logik hat Vorrang - damit man auch jeden Klick mitbekommt - sehen muss man ihn ja nicht). Es gibt natuerlich Engines die 200 oder mehr "Updates" pro Sekunde durchfuehren. Fuer unser Anliegen reichen aber theoretisch sogar 10 oder 5.
Denn: es gibt noch das sogenannte "Tweening" - wenn man fuer ein Objekt die alte und die neue Position speichert, kann man mittels Tweening selber festlegen wo das Objekt derzeit sein muesste. Dazu gibt es einen Faktor der ansagt, wie weit (in Prozent) wir vom letzen Update entfernt sind (0.5 waere also: das Zeichnen findet genau zwischen zwei UpdateWorld() statt). Sowas ist aber nur interessant, wenn wir Animationen haben (bei TVTower der "Mond").

Will man auf Tweening verzichten und hat Animationen -- einfach die Updaterate hoeher als die Renderrate ansetzen (dann wird ja pro Rendern mindestens einmal Update ausgefuehrt).

Dazu aber erst bei Bedarf "mehr".


So...hoffe der Kopf qualmt ein wenig - aber nicht zu viel biggrin


bye
Ron


sigimg2.php?id=1

Offline

#15 06.04.2014 10:01

sushiTV
Mitglied
Ort: Leipzig
Registriert: 10.03.2014
Beiträge: 496

Re: Blitzmax - Gameloop und Objektupdates

zum glück habe ich keine rauchmelder in der wohnung, die würden ordentlich allarm geben smile


das Leben ist ein scheiß Spiel, hat aber ne geile Grafik

Offline

#16 06.04.2014 11:21

sushiTV
Mitglied
Ort: Leipzig
Registriert: 10.03.2014
Beiträge: 496

Re: Blitzmax - Gameloop und Objektupdates

Zwei kleine Fragen noch:

1.)Habe ich das richtig verstanden, das jetzt 60 mal in der Sekunde der gesamte Bildschirm/Fenster neu gezeichnet wird? (RenderWorld ausgeführt wird)

2.)Eine Boolen variable gibt's in BMax nicht? nimmt man ein Int?

Beitrag geändert von sushiTV (06.04.2014 11:42)


das Leben ist ein scheiß Spiel, hat aber ne geile Grafik

Offline

#17 06.04.2014 11:48

Ronny
Administrator
Ort: Chemnitz
Registriert: 08.11.2001
Beiträge: 11.704
Webseite

Re: Blitzmax - Gameloop und Objektupdates

Bool gibt es bei BlitzMax nicht

False ist aequivalent zu: Byte 0, String "", Int 0, einem Null-Objekt
Null bei "nicht objekten" ist aequivalent zu "False" (0, "")

also:

local myobject:MyObject 'per se "null"
if myobject then dosomething


@neugezeichnet
ja hast Du richtig verstanden. Es gibt hier keine "DirtyRects"-Methode wie in den 90ern - bei dem der alte Bildschirm immer nur "uebermalt" werden musste. Da der Grafikspeicher nicht kontrolliert wird, muss man immer artig neuzeichnen.

Wenn du einen bildschirmfuellenden Hintergrund nutzt, kannst Du das "Cls" weglassen.


bye
Ron


sigimg2.php?id=1

Offline

#18 06.04.2014 13:40

sushiTV
Mitglied
Ort: Leipzig
Registriert: 10.03.2014
Beiträge: 496

Re: Blitzmax - Gameloop und Objektupdates

Sooo,

Ich habe nun in TMenu weitere Methoden eingefügt smile
(dadurch brauche ich nun status und Ustatus nicht mehr)

Funktioniert auch fast schon .... aber das ganze Verschieben des Menüs ...
da muss ich mir etwas einfallen lassen das ganz anders zu lößen smile
(vielleicht ja durch die onClick sache etc. ...)

Das Weinbergmenü schließt sich ab und an direkt wieder wenn es angeklickt wird,
bestimmt weil das auch von der Programmlogik nicht ganz sauber ist.

Mal sehen ob der Code überhaupt hier rein passt - das nicht das Forum sprengt smile

gruß
sushi


'
'
'My first BlitzMax program
'Title: Winzer Projekt
'Datum: 2014.04.05 - 06
'Autor: ~s~
'
'Using Dig Framework https://github.com/GWRon/Dig
'Using Dig Framework http://www.gamezworld.de/phpforum/viewtopic.php?id=13394
'
'
'

Import "base.util.helper.bmx" 'hilfsfunktionen
Import "base.util.event.bmx" 'mit neuem DigUpdate nicht noetig, hab das beim deltatimer vergessen :D
Import "base.util.deltatimer.bmx"

'Include BRL.PNGLoader


Graphics 800, 600, 0, 0

Global exitApp:int = FALSE 'spiel beenden?

Global bild_bg:TImage
Global bild_bgp1:TImage

'Global Ustatus:Int = - 1
Global MenuStatus:Int = 1

Global aktivesMenu:TMenu
Global hoverMenu:TMenu

Global Hauptmenu:TMenu[]
Global Stadtmenu:TMenu[]
Global Weinbergmenu:TMenu[]

Global Dummy:String



Type TPlayer
	Field Home:String
	Field Konto:Float
	Field Keller:TKeller
End Type

Type TKeller
	Field Flaschen:Int
	Field Lagerplatz:Int
End Type


Type TMenu
	Field x:Int
	Field y:Int
	Field b:Int
	Field h:Int
	Field name:String
'	Field status:Int
	
	Method Draw()
		If Not Self.IsActive() And Not Self.IsHover() Then
			SetColor(232, 222, 191)
		ElseIf Self.IsHover() And Not Self.IsActive() Then
			SetColor(223, 144, 26)
		ElseIf Self.IsActive() Then
			SetColor(243, 202, 11)
		EndIf
		DrawText(name, x, y)
		'Farbe wieder zuruecksetzen - nicht vergessen :D
		SetColor(255, 255, 255)
	End Method
	
	Method Update:Int()
		'standardmaessig ausschalten
		'status = 0
		
		'wenn die maus nicht drin ist - überprüfen ob Objekt hoverMenu ist
		If Not THelper.MouseIn(x, y, b, h) Then
			If hoverMenu = Self Then
				hoverMenu = Null
				Return False
			Else
				Return False
			EndIf
		EndIf

		'du kannst mit "self." explizit hinweisen, was du meinst
		'".status" (also nur punkt) wuerde  explizit eine "global" variable
		'ansprechen
		Self.SetHover()

		If MOUSEMANAGER.isDown(1) Then
			Self.SetActive()
		EndIf	
	End Method
	
	Method SetActive()
		aktivesMenu = Self
		MenuStatus = 1
	End Method

	Method SetHover()
		hoverMenu = Self
		MenuStatus = 1
	End Method
	
	Method IsActive:Int()
		Return (Self = aktivesMenu)
	End Method
	
	Method IsHover:Int()
		Return (Self = hoverMenu)
	End Method
	
End Type

Function MenuInit()
Local x, y, h, b:Int

	x = 75
	y = 180
	h = 19
	b = 120

	For i = 0 To 5
		Local NewMenu:TMenu
		NewMenu = New TMenu
		NewMenu.x = x
		NewMenu.y = y + (i * h)
		NewMenu.b = b
		NewMenu.h = h
'		NewMenu.status = 0
		Hauptmenu:+ [NewMenu]
	Next

	h = 19
	x = 75 + 10
	y = 180 + h
	b = 120

	
	For i = 0 To 6
		Local NewMenu1:TMenu
		NewMenu1 = New TMenu
		NewMenu1.x = x
		NewMenu1.y = y + (i * h)
		NewMenu1.b = b
		NewMenu1.h = h
'		NewMenu1.status = 0
		Stadtmenu:+ [NewMenu1]
	Next

	h = 19
	x = 75 + 10
	y = 180 + h * 2
	b = 120

	For i = 0 To 5
		Local NewMenu2:TMenu
		NewMenu2 = New TMenu
		NewMenu2.x = x
		NewMenu2.y = y + (i * h)
		NewMenu2.b = b
		NewMenu2.h = h
'		NewMenu2.status = 0
		Weinbergmenu:+ [NewMenu2]
	Next
	
	Hauptmenu[0].name = "Zur Stadt"
	Hauptmenu[1].name = "Weinberg"
	Hauptmenu[2].name = "Schuppen"
	Hauptmenu[3].name = "Lagerhaus"
	Hauptmenu[4].name = "Kellerei"
	Hauptmenu[5].name = "Wohnhaus"
	
	Stadtmenu[0].name = "Sabotage"
	Stadtmenu[1].name = "Bank"
	Stadtmenu[2].name = "Arbeitsamt"
	Stadtmenu[3].name = "Grosshandel"
	Stadtmenu[4].name = "Einzelhandel"
	Stadtmenu[5].name = "Rathaus/Amt"
	Stadtmenu[6].name = "Werbebuero"
	
	Weinbergmenu[0].name = "Weinbergmenu 1"
	Weinbergmenu[1].name = "Weinbergmenu 2"
	Weinbergmenu[2].name = "Weinbergmenu 3"
	Weinbergmenu[3].name = "Weinbergmenu 4"
	Weinbergmenu[4].name = "Weinbergmenu 5"
	Weinbergmenu[5].name = "Weinbergmenu 6"
	
End Function



Function MenuDraw()
Local x:Int = 75, y:Int = 180, h:Int = 19, b:Int = 120


	If Not MenuStatus = 1 Then Return False
	
	MenuStatus = - 1
	
	Print "Menue Zeichnen"
	SetColor(129, 8, 39)
	DrawRect(x, y, 130, 500)
	'Farbe wieder zuruecksetzen - nicht vergessen :D
	SetColor(255, 255, 255)

	'Hauptmenu in jedem Fall Zeichnen,
	'da es keine Bedingung gibt für die nicht mit IF abgeglichenen Menüs
	'das ist alles müll :)
	

	For Local i:Int = 0 To Hauptmenu.length - 1	
		Hauptmenu[i].x = x
		Hauptmenu[i].y = y + (i * h)
		Hauptmenu[i].Draw
	Next
	
	If aktivesMenu = Hauptmenu[0] Then
		SetColor(129, 8, 39)
		DrawRect(x, y, 130, 500)
		SetColor(255, 255, 255)
		x = 75
		y = 180

		Hauptmenu[0].Draw
		For Local k:Int = 0 To Stadtmenu.length - 1
			y = y + h
			'Stadtmenu Koordinaten müssen nicht geändert werden
			Stadtmenu[k].Draw()
		Next

		For Local a:Int = 1 To Hauptmenu.length - 1
			y = y + h
			Hauptmenu[a].y = y
			Hauptmenu[a].Draw		
		Next
	EndIf

	If aktivesMenu = Hauptmenu[1] Then
		SetColor(129, 8, 39)
		DrawRect(x, y, 130, 500)
		SetColor(255, 255, 255)
		x = 75
		y = 180

		Hauptmenu[0].Draw
		y = y + h
		Hauptmenu[1].y = y
		Hauptmenu[1].Draw
		
		For Local k1:Int = 0 To Weinbergmenu.length - 1
			y = y + h
			'Koordinaten müssen nicht geändert werden
			Weinbergmenu[k1].Draw()
		Next
		
		For Local a1:Int = 2 To Hauptmenu.length - 1
			y = y + h
			Hauptmenu[a1].y = y
			Hauptmenu[a1].Draw		
		Next
	EndIf
	
End Function






'spiellogik aktualisieren
Function UpdateWorld:int()
	'refresh mouse/keyboard
	MouseManager.ChangeStatus()
	KeyManager.ChangeStatus()

	For Local menu:TMenu = EachIn Hauptmenu
		menu.Update()
	Next
	
	If aktivesMenu = Hauptmenu[0] Then
		For Local menu2:TMenu = EachIn Stadtmenu
			menu2.Update()
		Next
	EndIf

	If aktivesMenu = Hauptmenu[1] Then
		For Local menu3:TMenu = EachIn Weinbergmenu
			menu3.Update()
		Next
	EndIf

	'spiel beendbar mit ESC
	If KeyManager.IsHit(KEY_ESCAPE) then exitApp = TRUE
End Function


Function RenderWorld:Int()
	DrawImage bild_bg, 0, 0
	DrawImage bild_bgp1, 15, 15

	SetColor(232, 222, 191)
	DrawText (Dummy, 280, 47)
	'Farbe wieder zuruecksetzen - nicht vergessen :D
	SetColor(255, 255, 255)

	MenuDraw()

	Flip 0 'alles gezeichnete "an die grafikkarte schicken"
End Function



'wir sagen dem deltatimer, was die update- und renderfunktionen sind
GetDeltaTimer()._funcUpdate = UpdateWorld
GetDeltaTimer()._funcRender = RenderWorld
'und dass wir 30 Logikupdates und 60 Renders pro Sekunde wollen
GetDeltaTimer().Init(30, 60)

'Sachen initialisieren
MenuInit()

Dummy = "Datum: Jan 1980     Ort: Weingut Sachsen     Konto: 100.000"
bild_bg = LoadImage ("bg_01.png")
bild_bgp1 = LoadImage ("bgp_weingut.png")


'game loop
Repeat
	'run the deltatimer's loop
	'which runs the hooked update/render function
	GetDeltaTimer().loop()

	
	'falls du mit events arbeitest - muessen die natuerlich hier
	'abgearbeitet werden
	'EventManager.update()
Until AppTerminate() Or exitApp


End

Nachtrag:
Ich bin ja auch bissel doof .... eigentlich kann ich doch das ganze "Verschieben" in der MenuDraw() auch einfacher machen ... dachte in eine Schleife mit Unterschleifen aber das wird dann auch wieder doof

Zeichne 1. Menüpunkt
    Prüfe ob erstes Menü aktiv dann Zeichne 
    Untermenü und erhöhe y koordinate ansonsten wird ja nix gemacht

Setze y Koordinate Menü 2 und Zeichne es
    Prüfe ob zweites Menü aktiv dann Zeichne 
    Untermenü und erhöhe y koordinate ansonsten wird ja nix gemacht

Setze y Koordinate Menü 3 und Zeichne es

usw. ... zwar nicht elegant aber müsste funzen
ist aber auch keine lößung smile

Beitrag geändert von sushiTV (06.04.2014 14:40)


das Leben ist ein scheiß Spiel, hat aber ne geile Grafik

Offline

#19 06.04.2014 14:53

sushiTV
Mitglied
Ort: Leipzig
Registriert: 10.03.2014
Beiträge: 496

Re: Blitzmax - Gameloop und Objektupdates

Sooo, nach etwas weiterer Spielerei, werde ich versuchen das mit dem von Ron beschriebener onClick Funktion umzusetzen smile

~s~


das Leben ist ein scheiß Spiel, hat aber ne geile Grafik

Offline

#20 06.04.2014 15:46

Ronny
Administrator
Ort: Chemnitz
Registriert: 08.11.2001
Beiträge: 11.704
Webseite

Re: Blitzmax - Gameloop und Objektupdates

Ich schreibe deinen Code jetzt ein wenig um - so dass mehr "OOP" drin ist ... und Dinge wie "Rekursion" mal mit auftauchen ... dauert mal noch 10 Minuten.


bye
Ron


sigimg2.php?id=1

Offline

#21 06.04.2014 16:07

sushiTV
Mitglied
Ort: Leipzig
Registriert: 10.03.2014
Beiträge: 496

Re: Blitzmax - Gameloop und Objektupdates

HeeHee gw_smiley_zwinkern

naja, ich habe jetzt onClick ereignis bei mir mit in's TMenu gepackt da ist aber irgenwo was falsch, es funktioniert zwar, macht aber nicht das was ich wollte smile

hihi
~s~


das Leben ist ein scheiß Spiel, hat aber ne geile Grafik

Offline

#22 06.04.2014 16:40

Ronny
Administrator
Ort: Chemnitz
Registriert: 08.11.2001
Beiträge: 11.704
Webseite

Re: Blitzmax - Gameloop und Objektupdates

Folgender Code ist noch nicht optimal - Man kann das wie immer auf verschiedensten Wegen loesen.

Ich persoenlich haette alle linken Menuepunkte einer "Menugruppe" zugeordnet - und die wuerde sich dann entsprechend um die Positionierung kuemmern. Denn die "Hauptmenues" sind ja eigentlich auch nur eine Gruppe voneinander abhaengiger Menuepunkte. Sie getrennt zu positionieren ist einfach nur "bloed". Das macht man nur, wenn man einen Menupunkt auch woanders hinstecken woellte.
Wie dann aber reagieren,wenn man den Menuepunkt mittendrin platziert - woher wissen die anderen, dass sie "drumherumfliessen" muessen?

SuperStrict 'fordert ein wenig mehr "Definition" (Variablentypen festlegen usw)

Import "base.util.helper.bmx" 'hilfsfunktionen
Import "base.util.event.bmx" 'mit neuem DigUpdate nicht noetig, hab das beim deltatimer vergessen :D
Import "base.util.deltatimer.bmx"

'Include BRL.PNGLoader


Graphics 800, 600, 0, 0

Global exitApp:int = FALSE 'spiel beenden?

Global bild_bg:TImage
Global bild_bgp1:TImage


Global Hauptmenu:TMenu[]

Global Dummy:String



Type TPlayer
	Field Home:String
	Field Konto:Float
	Field Keller:TKeller
End Type

Type TKeller
	Field Flaschen:Int
	Field Lagerplatz:Int
End Type


Type TMenu
	Field x:Int
	Field y:Int	'das y bei dem das menu DERZEIT ist
	Field initialY:int 'hier ist es, wenn es nicht verschoben waere
	Field b:Int
	Field h:Int
	'ist die x,y relativ zum Elternelement?
	Field absolutePositioned:Int = True
	Field name:String
	Field parentMenu:TMenu
	Field subMenus:TList = CreateList()

	Global activeMenu:TMenu
	Global hoveredMenu:TMenu


	Method Init:TMenu(name:String, x:int=0,y:int=0,b:int=120,h:int=19)
		self.name = name
		self.x = x
		self.y = y
		self.initialY = y
		self.b = b
		self.h = 19
		
		return self
	End Method


	'mittels den Offsets koennen wir Sachen von "aussen" her verschieben
	'das erlaubt, dass die Kinderelemente "relativ" zu ihren Eltern
	'positioniert sein koennen
	Method Draw(offsetX:int=0, offsetY:int=0)
		If IsActive() Then
			SetColor(243, 202, 11)
		elseIf IsHovered() Then
			SetColor(223, 144, 26)
		Else
			SetColor(232, 222, 191)
		EndIf
		DrawText(name, x + offsetX, y + offsetY)
		'Farbe wieder zuruecksetzen - nicht vergessen :D
		SetColor(255, 255, 255)

		'Submenues zeichnen - offset nicht vergessen
		'Submenues interessieren uns nur, wen unser Menupunkt oder
		'eines der Submenues aktiviert ist
		if HasActiveSubMenu()
			For local m:TMenu = eachin subMenus
				m.Draw(offsetX, offsetY)
			Next
		Endif
	End Method

	
	'Anstatt nur "x" abzurufen, nehmen wir einen Getter um den Wert
	'manipulieren zu koennen
	Method GetX:int()
		'hat man ein Elternelement dann addiert man die eigene
		'Koordinate zu dem des Elternmenues
		if not absolutePositioned and parentMenu then return parentMenu.GetX() + x
		return x
	End Method

	Method GetY:int()
		if not absolutePositioned and parentMenu then return parentMenu.GetY() + y
		return y
	End Method

	'ermittelt die Hoehe eines Menues - inklusive der Hoehe durch Submenues
	Method GetHeight:int()
		local height:int = 0
		'falls Submenues vorhanden sind und das menu aktiv ist
		if HasActiveSubMenu()
			For local m:TMenu = eachin subMenus
				'rekursiv alle Untermenues abklappern
				height :+ m.GetHeight()
			Next
		Endif

		height :+ h
		return height
	End Method

	
	Method Update:Int()
		if THelper.MouseIn(GetX(), GetY(), b, h)
			SetHovered()

			If MOUSEMANAGER.isDown(1)
				SetActive(True)
				'wir haben den Mausklick "fuer uns" beansprucht: also Button resetten
				'damit kein "folgender" code (in dem gleichen Loop) nochmal
				'den gedrueckten Button vorfindet
				MOUSEMANAGER.ResetKey(1)
			EndIf
		EndIf

		'submenues interessieren uns nur, wen unser Menupunkt oder
		'eines der Submenues aktiviert ist
		if HasActiveSubMenu()
			local displaceY:int = self.h
			For local m:TMenu = eachin subMenus
				if not m.absolutePositioned
					'wir setzen das y des menues : original y + verschiebung
					m.y = y + m.initialY + displaceY
					'einruecken
					m.x = x + 10 
					displaceY :+ m.GetHeight()
				endif
				m.Update()
			Next
		Endif
	End Method

	Method AddSubmenu(menu:TMenu)
		'festlegen dass der Menuepunkt "relativ" angeordnet ist
		menu.SetAbsolutePositioned(False)

		subMenus.AddLast(menu)
	End Method

	'hier mal eine Methode die das Objekt zurueckgibt, erlaubt
	'"Chaining" ( new TMenu.SetAbsolutePositioned().Init() )
	Method SetAbsolutePositioned:TMenu(bool:Int=True)
		absolutePositioned = bool
		return self
	End Method
	
	Method SetActive(bool:int=True)
		If bool = True
			activeMenu = Self
		Else
			'nur resetten wenn dieses Menu das aktive ist
			if activeMenu = Self then activeMenu = null
		Endif
	End Method

	Method SetHovered(bool:int=True)
		If bool = True
			hoveredMenu = Self
		Else
			'nur resetten wenn dieses Menu das gehoverte ist
			if hoveredMenu = Self then hoveredMenu = null
		Endif
	End Method

	Method HasActiveSubMenu:Int()
		'Rekursive Funktion - ruft fuer jedes Kinderelement die gleiche
		'Funktion nochmal auf - hat ein Submenu ein Submenu wird dort
		'geschaut ob dies ein Submenu hat was aktiv ist ... und so weiter
		For local m:TMenu = eachin subMenus
			if m.HasActiveSubMenu() then return True
		Next

		return IsActive()
	End Method

	Method IsActive:Int()
		Return (Self = activeMenu)
	End Method
	
	Method IsHovered:Int()
		Return (Self = hoveredMenu)
	End Method

End Type


Function MenuInit()
	Local x:int=75, y:int=180, h:int=19, b:int=120

	'5 hauptmenues anlegen
	HauptMenu:+ [new TMenu.Init("Zur Stadt", x, y+0*h)]
	HauptMenu:+ [new TMenu.Init("Weinberg", x, y+1*h)]
	HauptMenu:+ [new TMenu.Init("Schuppen", x, y+2*h)]
	HauptMenu:+ [new TMenu.Init("Lagerhaus", x, y+3*h)]
	HauptMenu:+ [new TMenu.Init("Kellerei", x, y+4*h)]
	HauptMenu:+ [new TMenu.Init("Wohnhaus", x, y+5*h)]

	'dem "Zur Stadt"-Menu Unterpunkte hinzufuegen
	HauptMenu[0].AddSubmenu( new TMenu.Init("Sabotage") )
	HauptMenu[0].AddSubmenu( new TMenu.Init("Bank") )
	HauptMenu[0].AddSubmenu( new TMenu.Init("Arbeitsamt") )
	HauptMenu[0].AddSubmenu( new TMenu.Init("Grosshandel") )
	HauptMenu[0].AddSubmenu( new TMenu.Init("Einzelhandel") )
	HauptMenu[0].AddSubmenu( new TMenu.Init("Rathaus/Amt") )
	HauptMenu[0].AddSubmenu( new TMenu.Init("Werbebuero") )

	'dem "Weinberg"-Menu Unterpunkte hinzufuegen
	HauptMenu[1].AddSubmenu( new TMenu.Init("Weinbergmenu 1") )
	HauptMenu[1].AddSubmenu( new TMenu.Init("Weinbergmenu 2") )
	HauptMenu[1].AddSubmenu( new TMenu.Init("Weinbergmenu 3") )
	HauptMenu[1].AddSubmenu( new TMenu.Init("Weinbergmenu 4") )
	HauptMenu[1].AddSubmenu( new TMenu.Init("Weinbergmenu 5") )
	HauptMenu[1].AddSubmenu( new TMenu.Init("Weinbergmenu 6") )
	
	
End Function



Function MenuDraw()
	SetColor(129, 8, 39)
	DrawRect(75, 180, 130, 500)
	'Farbe wieder zuruecksetzen - nicht vergessen :D
	SetColor(255, 255, 255)

	'alle Hauptmenues zeichnen
	For Local m:TMenu = eachin Hauptmenu	
		m.Draw()
	Next
End Function






'spiellogik aktualisieren
Function UpdateWorld:int()
	'refresh mouse/keyboard
	MouseManager.ChangeStatus()
	KeyManager.ChangeStatus()

	'alle Hauptmenues (und ihre Submenues) aktualisieren
	'Per se ist am anfang kein Menu "gehovert" - der Hoverstatus
	'wird automatisch wiederhergestellt wenn die Maus noch drueber
	'ist
	TMenu.hoveredMenu = null
	'da wir wollen, dass alle menues "relativ" zueinander sind
	'verschieben wir alle dem ersten nachfolgenden Menues
	local displaceY:int = 0
	For Local menu:TMenu = EachIn Hauptmenu
		menu.Update()
		'wir setzen das y des menues : original y + verschiebung
		menu.y = menu.initialY + displaceY
		'neuen displace wert fuer das darauffolgende Menu festlegen
		'dabei beachten: wir wollen nur die Hoehe der "Kinderelemente"
		'also ziehen wir die eigene Hoehe ab.
		'Alternativ: Methode "GetSubMenuHeight" - was nur submenu
		'durchrackern muesste
		displaceY :+ menu.GetHeight() - menu.h
	Next


	'spiel beendbar mit ESC
	If KeyManager.IsHit(KEY_ESCAPE) then exitApp = TRUE
End Function


Function RenderWorld:Int()
	DrawImage bild_bg, 0, 0
	DrawImage bild_bgp1, 15, 15

	SetColor(232, 222, 191)
	DrawText (Dummy, 280, 47)
	'Farbe wieder zuruecksetzen - nicht vergessen :D
	SetColor(255, 255, 255)

	MenuDraw()

	Flip 0 'alles gezeichnete "an die grafikkarte schicken"
End Function



'wir sagen dem deltatimer, was die update- und renderfunktionen sind
GetDeltaTimer()._funcUpdate = UpdateWorld
GetDeltaTimer()._funcRender = RenderWorld
'und dass wir 30 Logikupdates und 60 Renders pro Sekunde wollen
GetDeltaTimer().Init(30, 60)

'Sachen initialisieren
MenuInit()

Dummy = "Datum: Jan 1980     Ort: Weingut Sachsen     Konto: 100.000"
bild_bg = LoadImage ("bg_01.png")
bild_bgp1 = LoadImage ("bgp_weingut.png")


'game loop
Repeat
	'run the deltatimer's loop
	'which runs the hooked update/render function
	GetDeltaTimer().loop()

	
	'falls du mit events arbeitest - muessen die natuerlich hier
	'abgearbeitet werden
	'EventManager.update()
Until AppTerminate() Or exitApp


End

bye
Ron


sigimg2.php?id=1

Offline

#23 06.04.2014 17:06

sushiTV
Mitglied
Ort: Leipzig
Registriert: 10.03.2014
Beiträge: 496

Re: Blitzmax - Gameloop und Objektupdates

Na Prima smile
da wäre ich in 1 Monat nicht hingekommen tongue


das Leben ist ein scheiß Spiel, hat aber ne geile Grafik

Offline

#24 06.04.2014 17:13

Ronny
Administrator
Ort: Chemnitz
Registriert: 08.11.2001
Beiträge: 11.704
Webseite

Re: Blitzmax - Gameloop und Objektupdates

Alles was irgendwie in Abhaengigkeit zu anderen Dingen steht ist dann schon weniger trivial zu realisieren.

Man muss ja immer wissen wo man gerade ist - was abhaengig von "oben drueber" ist. Macht die Sache nicht einfacher.
Ja, man koennte wieder mit Variablen arbeiten die den aktuellen Modus kennen, die aktuelle Position usw. - aber diese mangelnde Dynamik hat dann halt irgendwann auch Probleme (wenn man Sachen erweitern will).

bye
Ron


sigimg2.php?id=1

Offline

#25 06.04.2014 17:39

Ronny
Administrator
Ort: Chemnitz
Registriert: 08.11.2001
Beiträge: 11.704
Webseite

Re: Blitzmax - Gameloop und Objektupdates

So ... hab dir ja gesagt, ich persoenlich wuerde das etwas anders gestalten.

Ich hatte vorher den Typ TMenu etwas erweitert... so mit "Field Root:int = False" usw. Dann innerhalb der Zeichnen/Update-Funktionen immer ueberprueft, ob es das Wurzelelement ist...

Aber dann habe ich mir gedacht: Du willst ja "oop" lernen - und in folgendem Code "vererben" wir ein wenig ... TRootMenu ueberschreibt alle relevanten Funktionen damit bspweise nicht geschaut wird, ob die Maus "drueber" ist.

Man kann sowas dann wieder mit "Field ignoreMouse:int=False" und entsprechender Abfrage gestalten - dass ist dann Geschmackssache.

Noch ein weiterer Hinweis:
Ich habe im Typ TMenu das Feld "depth:int" hinterlegt - damit kann das Menu schnell wissen, wie tief verschachtelt es ist. Natuerlich koennte man auch eine "GetDepth"-Methode schreiben, die immer wieder (rekursiv) vom Elternelement "GetDepth" aufrufen wuerde - und dabei einen Zaehler hochzaehlt.

Method GetDepth:int()
  local depth:int = 0
  if parentMenu then depth :+ 1 + GetDepth()
  return depth 
End Method

Du siehst: hat man kein Elternmenu: bleibt depth = 0, hat es ein parentMenu, wird 1 addiert und dann noch das Ergebnis davon, welche Tiefe das parentMenu bereits besitzt. Dies nennt sich dann wie oben schon angedeutet "Rekursion".
Wenn du diese Methode einbindest, kannst du auf das "Field depth:int" verzichten und "depth" mit "GetDepth()" ersetzen.

Der bereits beschriebene Punkt "TradeOff" ist hier: die Sache wird dynamisch und weniger fehleranfaellig - dafuer muss beim "auslesen" mehr getan werden als nur eine Variable zurueckzugeben.

Bei wirklich komplexen Prozessen wuerde man dann mit "caches" arbeiten (bspweise wenn man dynamisch Bilder erstellt). Dann muss man allerdings darauf achten, wann der Cache geloescht werden muss (bei Bildern bspweise, wenn sich der darauf abzubildende Inhalt aendert). Aber dies ist was fuer spaeter biggrin

So ... hier nun mein "verbesserter" Code:

SuperStrict 'fordert ein wenig mehr "Definition" (Variablentypen festlegen usw)

Import "base.util.helper.bmx" 'hilfsfunktionen
Import "base.util.event.bmx" 'mit neuem DigUpdate nicht noetig, hab das beim deltatimer vergessen :D
Import "base.util.deltatimer.bmx"

'Include BRL.PNGLoader


Graphics 800, 600, 0, 0

Global exitApp:int = FALSE 'spiel beenden?

Global bild_bg:TImage
Global bild_bgp1:TImage


Global linkesMenu:TMenu

Global Dummy:String



Type TPlayer
	Field Home:String
	Field Konto:Float
	Field Keller:TKeller
End Type

Type TKeller
	Field Flaschen:Int
	Field Lagerplatz:Int
End Type


Type TMenu
	'derzeitige Position des Menues
	Field x:Int, y:int
	'Dimension des Menues
	Field b:Int, h:Int
	'wie tief verschachtelt?
	Field depth:int = 0
	'submenues immer anzeigen?
	Field alwaysDisplaySubMenus:int = FALSE
	Field name:String = ""
	Field parentMenu:TMenu
	Field subMenus:TList = CreateList()

	Global activeMenu:TMenu
	Global hoveredMenu:TMenu


	Method Init:TMenu(name:String, x:int=0,y:int=0,b:int=120,h:int=19)
		self.name = name
		self.x = x
		self.y = y
		self.b = b
		self.h = 19
		
		return self
	End Method


	Method DrawSubmenu()
		'Submenues interessieren uns nur, wen unser Menupunkt oder
		'eines der Submenues aktiviert ist
		if HasActiveSubMenu() or alwaysDisplaySubMenus
			For local m:TMenu = eachin subMenus
				m.Draw()
			Next
		Endif
	End Method


	Method Draw()
		If IsActive() Then
			SetColor(243, 202, 11)
		ElseIf IsHovered() Then
			SetColor(223, 144, 26)
		Else
			SetColor(232, 222, 191)
		EndIf
		DrawText(name, GetX(), GetY())
		'Farbe wieder zuruecksetzen - nicht vergessen :D
		SetColor(255, 255, 255)

		'Submenues zeichnen
		DrawSubmenu()		
	End Method

	
	'Anstatt nur "x" abzurufen, nehmen wir einen Getter um den Wert
	'manipulieren zu koennen
	Method GetX:int()
		local result:int = x
		'hat man ein Elternelement dann addiert man die eigene
		'Koordinate zu dem des Elternmenues
		if parentMenu then result :+ parentMenu.GetX()
		'fuer jede "Verschachtelung" gehen wir 10 pixel nach rechts
		result :+ depth*5

		return result
	End Method

	Method GetY:int()
		local result:int = y
		If parentMenu
			result :+ parentMenu.GetY()
			result :+ parentMenu.GetHeight()

			'fuer alle submenueintraege vor mir: deren hoehe hinzufuegen
			For local m:TMenu = eachin parentMenu.subMenus
				'mich selber gefunden - raus aus der Schleife
				if m = self then exit
				'andernfalls: hoehe addieren
				result :+ m.GetHeight()
				result :+ m.GetSubmenuHeight()
			Next
		EndIf

		return result
	End Method

	'ermittelt die Hoehe eines Menues - ohne Submenues
	Method GetHeight:int()
		return h
	End Method

	'liefert die Hoehe der Untermenupunkte zurueck
	Method GetSubmenuHeight:int()
		local height:int = 0
		if HasActiveSubMenu() or alwaysDisplaySubMenus
			For local m:TMenu = eachin subMenus
				'rekursiv alle Untermenues abklappern
				height :+ m.GetHeight()
				height :+ m.GetSubmenuHeight()
			Next
		Endif
		return height
	End Method

	
	Method Update:Int()
		if THelper.MouseIn(GetX(), GetY(), b, h)
			SetHovered()

			If MOUSEMANAGER.isDown(1)
				SetActive(True)
				'wir haben den Mausklick "fuer uns" beansprucht: also Button resetten
				'damit kein "folgender" code (in dem gleichen Loop) nochmal
				'den gedrueckten Button vorfindet
				MOUSEMANAGER.ResetKey(1)
			EndIf
		EndIf
		
		'submenues aktualisieren
		UpdateSubmenu()
	End Method


	Method UpdateSubmenu:Int()
		if HasActiveSubMenu() or alwaysDisplaySubMenus
			local menuDisplaceY:int = 0
			For local m:TMenu = eachin subMenus
				m.Update()
			Next
		Endif
	End Method

	Method AddSubmenu(menu:TMenu)
		'menu ist eins tiefer als das Elternelement
		menu.depth = self.depth + 1
		'dem menu mitteilen, zu wem sie nun gehoeren
		menu.parentMenu = self

		subMenus.AddLast(menu)
	End Method

	Method SetActive(bool:int=True)
		If bool = True
			activeMenu = Self
		Else
			'nur resetten wenn dieses Menu das aktive ist
			if activeMenu = Self then activeMenu = null
		Endif
	End Method

	Method SetHovered(bool:int=True)
		If bool = True
			hoveredMenu = Self
		Else
			'nur resetten wenn dieses Menu das gehoverte ist
			if hoveredMenu = Self then hoveredMenu = null
		Endif
	End Method

	Method HasActiveSubMenu:Int()
		'Rekursive Funktion - ruft fuer jedes Kinderelement die gleiche
		'Funktion nochmal auf - hat ein Submenu ein Submenu wird dort
		'geschaut ob dies ein Submenu hat was aktiv ist ... und so weiter
		For local m:TMenu = eachin subMenus
			if m.HasActiveSubMenu() then return True
		Next

		return IsActive()
	End Method

	Method IsActive:Int()
		Return (Self = activeMenu)
	End Method
	
	Method IsHovered:Int()
		Return (Self = hoveredMenu)
	End Method

End Type


'Der "Start" eines Menus
Type TRootMenu extends TMenu

	'ueberschreiben von TMenu.Init - Achtung: anderen Typ zurueckgeben
	Method Init:TRootMenu(name:String, x:int=0,y:int=0,b:int=120,h:int=19)
		'Super - ruft den Elterntyp auf - hier also "TMenu"
		'-> Init von "TMenu" wird aufgerufen
		Super.Init(name, x, y, b, h)

		'unterpunkte immer anzeigen
		alwaysDisplaySubMenus = True

		return self
	End Method

	'ueberschreiben da der root immer eine tiefe von 0 hat
	Method GetX:int()
		return x
	End Method
	
	'ueberschreiben von "GetHeight" - dieses rootmenu nimmt keinen Platz weg
	Method GetHeight:int()
		return 0
	End Method

	'ueberschreiben: rootMenus koennen nicht hovern
	'wir aktualisieren also nur noch die Submenus
	Method Update:Int()
		UpdateSubmenu()
	End Method

	'ueberschreiben: wir zeichnen nur die submenus
	Method Draw()
		DrawSubmenu()
	End Method
End Type



Function MenuInit()
	Local x:int=75, y:int=180, h:int=19, b:int=120

	linkesMenu = new TRootMenu.Init("", x, y)
	
	'damit wir bequem diese Menues benutzen koennen...
	local stadtMenu:TMenu = new TMenu.Init("Zur Stadt")
	local weinbergMenu:TMenu = new TMenu.Init("Weinberg")

	linkesMenu.AddSubmenu( stadtMenu )
	linkesMenu.AddSubmenu( weinbergMenu )
	linkesMenu.AddSubmenu( new TMenu.Init("Lagerhaus") )
	linkesMenu.AddSubmenu( new TMenu.Init("Kellerei") )
	linkesMenu.AddSubmenu( new TMenu.Init("Wohnhaus") )

	local sabotageMenu:TMenu = new TMenu.Init("Sabotage")
	stadtMenu.AddSubmenu( sabotageMenu )
	stadtMenu.AddSubmenu( new TMenu.Init("Bank") )
	stadtMenu.AddSubmenu( new TMenu.Init("Arbeitsamt") )
	stadtMenu.AddSubmenu( new TMenu.Init("Grosshandel") )
	stadtMenu.AddSubmenu( new TMenu.Init("Einzelhandel") )
	stadtMenu.AddSubmenu( new TMenu.Init("Rathaus/Amt") )
	stadtMenu.AddSubmenu( new TMenu.Init("Werbebuero") )

	'dem sabotage menu Unterpunkte hinzufuegen
	sabotageMenu.AddSubmenu( new TMenu.Init("Sabotage 1") )
	sabotageMenu.AddSubmenu( new TMenu.Init("Sabotage 2") )

	'dem "Weinberg"-Menu Unterpunkte hinzufuegen
	weinbergMenu.AddSubmenu( new TMenu.Init("Weinbergmenu 1") )
	weinbergMenu.AddSubmenu( new TMenu.Init("Weinbergmenu 2") )
	weinbergMenu.AddSubmenu( new TMenu.Init("Weinbergmenu 3") )
	weinbergMenu.AddSubmenu( new TMenu.Init("Weinbergmenu 4") )
	weinbergMenu.AddSubmenu( new TMenu.Init("Weinbergmenu 5") )
	weinbergMenu.AddSubmenu( new TMenu.Init("Weinbergmenu 6") )
	
	
End Function



Function MenuDraw()
	SetColor(129, 8, 39)
	DrawRect(75, 180, 130, 500)
	'Farbe wieder zuruecksetzen - nicht vergessen :D
	SetColor(255, 255, 255)

	linkesMenu.Draw()
End Function






'spiellogik aktualisieren
Function UpdateWorld:int()
	'refresh mouse/keyboard
	MouseManager.ChangeStatus()
	KeyManager.ChangeStatus()

	'alle Hauptmenues (und ihre Submenues) aktualisieren
	'Per se ist am anfang kein Menu "gehovert" - der Hoverstatus
	'wird automatisch wiederhergestellt wenn die Maus noch drueber
	'ist
	TMenu.hoveredMenu = null
	'da wir wollen, dass alle menues "relativ" zueinander sind
	'verschieben wir alle dem ersten nachfolgenden Menues
	linkesMenu.Update()


	'spiel beendbar mit ESC
	If KeyManager.IsHit(KEY_ESCAPE) then exitApp = TRUE
End Function


Function RenderWorld:Int()
	DrawImage bild_bg, 0, 0
	DrawImage bild_bgp1, 15, 15

	SetColor(232, 222, 191)
	DrawText (Dummy, 280, 47)
	'Farbe wieder zuruecksetzen - nicht vergessen :D
	SetColor(255, 255, 255)

	MenuDraw()

	Flip 0 'alles gezeichnete "an die grafikkarte schicken"
End Function



'wir sagen dem deltatimer, was die update- und renderfunktionen sind
GetDeltaTimer()._funcUpdate = UpdateWorld
GetDeltaTimer()._funcRender = RenderWorld
'und dass wir 30 Logikupdates und 60 Renders pro Sekunde wollen
GetDeltaTimer().Init(30, 60)

'Sachen initialisieren
MenuInit()

Dummy = "Datum: Jan 1980     Ort: Weingut Sachsen     Konto: 100.000"
bild_bg = LoadImage ("bg_01.png")
bild_bgp1 = LoadImage ("bgp_weingut.png")


'game loop
Repeat
	'run the deltatimer's loop
	'which runs the hooked update/render function
	GetDeltaTimer().loop()

	
	'falls du mit events arbeitest - muessen die natuerlich hier
	'abgearbeitet werden
	'EventManager.update()
Until AppTerminate() Or exitApp


End

bye
Ron


sigimg2.php?id=1

Offline

Schnellantwort auf dieses Thema

Schreibe deinen Beitrag und versende ihn
Bist Du ein Mensch oder ein Roboter ?

Verifizierung, dass diese Aktion durch eine reale Person vorgenommen wird und nicht von einem Programm.