[GELÖST] Organisation bzgl Levelswitch etc.

Übersicht BlitzMax, BlitzMax NG Beginners-Corner

Neue Antwort erstellen

Neoxit

Betreff: [GELÖST] Organisation bzgl Levelswitch etc.

BeitragSo, Jun 24, 2012 16:21
Antworten mit Zitat
Benutzer-Profile anzeigen
Heydiho! Habe mich endlich mal von BlitzBasic losgerissen und mir die Testversion von BMax geholt. Ich bin mehr als zufrieden und denke mal das wird nicht lange dauern bis ich da hineininvestiere. Wink

Nun aber mal zu meiner Frage die mir bei meinen derzeitigen dingen weniger Probleme bereitet, aber es vllt doch einen besseren, sinnvolleren weg gibt als das was ich mache.

Meine frage ist wie managed ihr Level / Screenswitch zwischen Hauptmenü, Leveln oder ähnlichem?

Mein Weg war bisher immer folgender (Zumindest in BB):

BlitzMax: [AUSKLAPPEN]

'Globals
Global system_level:Int = 1 ' 1 Steht hier für das Hauptmenü. - 2 wäre das Level.

'Hauptschleife
Repeat
Cls

If system_level = 1 Then
' Hier kommt alles rein was das hauptmenü betrifft. Unter anderem auch ein Button, welcher dann das
' System Level auf 2 stellt und somit das level starten lässt.
EndIf

If system_level = 2 Then
' Hier kommt dann das level. Über eine taste könnte man dann wieder system_level auf 1 stellen um ins
' Hauptmenü zurückzukommen.
EndIf

Flip
Until KeyHit(KEY_ESCAPE) End


Zu dem habe ich nochmal eine weiter Frage.
In BMax gibt es ja zum glück das ach so tolle OOP Very Happy
Macht es dabei sinn wirklich fast alles was häufiger als 1 mal vorkommt (bspw. das was die Maus angeht würde ich nun nicht in einem Type speichern) in Types zu organisieren? Übersichtlicher ist es dann ja alle male. Nur habe ich so meine Probleme bei einigen einfachen Verständnisfragen.

Ich habe z.b. in dem Spiel logischerweise mehrer Buttons. Die habe ich in BB immer als 1 Funktion gehandhabt, wobei diese dann CreateButton(x,y,img_norm,img_hover,img_click,modi) hieß. Diese hat mir dann entsprechend den button an der entsprechenden Stelle erstellt und mit Modi dann entweder bei 1 ein Beenden oder 2 ein neues Spiel, 3 speichern etc. pp. gemacht.

Wie kann ich diese nun allerdings SINNVOLL in Types nutzen / Sollte man das überhaupt? Meine derzeitigen gehversuche diesbzgl. (Habe gestern abend mit Bmax begonnen Wink ) sehen so aus:

BlitzMax: [AUSKLAPPEN]

'=== Button ===
Type TButton
Field KOORD_x:Int, KOORD_y:Int
Field IMG_Norm:TImage
Field IMG_Hover:TImage
Field IMG_Click:TImage

Method Draw()

DrawImage IMG_Norm, KOORD_x, KOORD_y

If ImagesCollide(IMG_Norm, KOORD_x, KOORD_y, 0, IMG_Maus, Maus_X, Maus_Y, 0) Then
DrawImage IMG_Hover, KOORD_x, KOORD_y
If MouseHit(MOUSE_LEFT) Then

EndIf
End If

End Method

Function Create:TButton(x:Int, y:Int, norm:TImage, hover:TImage, click:TImage)
Local a:TButton = New TButton
a.KOORD_x = x
a.KOORD_y = y
a.IMG_Norm = norm
a.IMG_Hover = hover
a.IMG_Click = click

a.Draw()

Return a
End Function

End Type

'---------------------------------------------------------
'Buttons
Global IMG_Testbutton_norm:TImage = LoadImage("button_test_norm.png")
Global IMG_Testbutton_hover:TImage = LoadImage("button_test_hover.png")

'---------------------------------------------------------

'=========HAUPTSCHLEIFE===========
Repeat
Cls

'Testbutton Zeichnen
Local b:TButton = TButton.Create(100, 100, IMG_Testbutton_norm, IMG_Testbutton_hover, IMG_Testbutton_hover)
Local c:TButton = TButton.Create(200, 300, IMG_Testbutton_norm, IMG_Testbutton_hover, IMG_Testbutton_hover)

Flip
Until KeyHit(KEY_ESCAPE) End



Natürlich befindet sich darin noch eine Maus-Koord-Variable wie man dem Type oben entnehmen kann.
Kann man dies alles vereinfachen / sollte man das oder ist das schon ein guter weg den ich nehme?

Sind ein paar mehr fragen, und ich bin über jede noch so helfende Antwort tierisch Dankbar!

Mfg
Neoxit Wink
  • Zuletzt bearbeitet von Neoxit am So, Jul 08, 2012 20:09, insgesamt einmal bearbeitet

Xeres

Moderator

BeitragSo, Jun 24, 2012 16:40
Antworten mit Zitat
Benutzer-Profile anzeigen
OOP kann toll sein. Je größer ein Projekt ist, desto mehr zahl es sich aus, einzelne Teile als ein Objekt zu betrachten. Ab einer bestimmten Größe sind Objekte allein deshalb da, um den NameSpace sauber zu halten:
BlitzMax: [AUSKLAPPEN]
Type TUI_IMG
Field buttonA:TImage
Field buttonB:TImage

Method New()
Self.buttonA = LoadImage(...)
Self.buttonB = LoadImage(...)
End Method

End Type

UI:TUI_IMG = New TUI_IMG

'[...]

DrawImage(UI.buttonA, x, y)

Level würde ich möglichst immer als Objekt umsetzen. Wenn du jedes Level von der Platte lädst, reicht ein Globales Objekt, dessen Inhalt du veränderst. Karten, auf denen man sich hin und her bewegt kann man einmal laden und schnell hin und her wechseln... Je nach dem, was du erreichen willst.

Ein Bedingungs- oder Select-Case Block würde ich bei kleinen Spielen für den BCC akzeptieren, aber alles darüber findet man zu unsauber.
Man kann Gamestates in Objekte oder Funktionen verpacken - macht keinen zu großen Unterschied. Man muss nur acht geben, dass man keinen Zustand erreichen kann, der irgendwie undefiniert ist.

Buttons handle ich als einzelne Funktionen ab... Wenn man deren Zustand nicht speichern muss, zeigen sie ja nur 2 Bilder an und geben beim Klick True zurück.
Wenn du aber Objekte erstellst, dann besser nicht in der Schleife. Globale Variablen in Funktionen behalten ihren Wert:
BlitzMax: [AUSKLAPPEN]
Function Foo()
'* Einmalig erstellen:
Global b:TButton = TButton.Create(100, 100, IMG_Testbutton_norm, IMG_Testbutton_hover, IMG_Testbutton_hover)

If b.Update() Then DoSomething()

End Function

Simple Antwort: Probiere aus, was du als Objekte und was als simplere Strukturen verarbeiten willst.
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)

Neoxit

BeitragSo, Jun 24, 2012 17:18
Antworten mit Zitat
Benutzer-Profile anzeigen
Erstmal danke für die kleine Nebeninformation das ich die Bilder auch innerhalb des Types laden kann und nicht unbedingt erstmal global laden muss. Damit hätte ich schonmal ein doppeltes Laden weniger. Smile

Und auch vielen dank Xeres für deine recht ausführliche Antwort.
Jedoch gehen wir mal davon aus ich organisiere mein Projekt wie folgt (src technisch)

1 - Mainfile (Hier befindet sich auch die Hauptschleife so wie das setup der Grafik und weiter Systembezogenen Dinge)
2 - Typefile (Hier würde ich alle (oder die meisten Types unterbringen)
3- Hauptmenüfile (Hier möchte ich das Komplette Menü gestalten. Mit zugriff auf Types die dann wiederrum in der Typefile liegen etc.)
4 - Level 1 ( Hier möchte ich das 1. Level gestalten)
5 - Level 2 ( Hier das 2te.) etc.

Und da ist nämlich meine konkrete Frage. Wie kann man die Files nachher so legen, das sie in der Mainfile / Hauptschleife zusammenlaufen? Oder habe ich da durch meine jahrelange BB logik nun einfache denkfehler drinne wie es grundsätzlich dank OOP vieel besser geht?

Ich habe meine Programme ja bisher so ca. unterteilt:

Systemsettings (Grafikmodus, apptitle)
Types
Globals
Grafiken
Sound
Starteinstellungen

Hauptschleife

Funktionen

So sah im groben immer meine BB file aus. Ich habe also eher selten mit weiteren Dateien gearbeitet, und wenn doch, dann habe ich lediglich eine für Types, eine Für Globals eine für Funktion etc erstellt und dann einfach vor der hauptschleife mit Include eingebunden. Ich habe das doofe gefühl mir fehlt hier wirklich nur ein quäntchen verständnis Confused

Xeres

Moderator

BeitragSo, Jun 24, 2012 17:41
Antworten mit Zitat
Benutzer-Profile anzeigen
Types/Objekte sind groß in BlitzMax. Funktionen & Methoden sind gebündelt in einer Struktur und entsprechend sollte man große Types in einer Datei halten.
Mainfile.bmx, TGame.bmx, TGameStates.bmx usw. wäre nicht unüblich.
Im Mainfile includierst du alle Codes, Initialisiert alles nötige und startest die Hauptschleife, die Entweder nur dein TGameManager.Update() aufruft oder per Select-Case die richtige Funktion aufruft (so jedenfalls mein System).
Das Hauptmenü sollte eine Funktion (Objekt) sein und jeder Spielzustand.

Die Struktur hängt von der Größe des Projekts ab. Man kann auch alles in einer Datei behalten, aber irgendwann wird das eben unübersichtlich. Das hat nicht mal so viel mit OOP zu tun - du kannst alles in Funktionen Gliedern, aber auch die müssen irgendwann ausgelagert werden.
Die Wiederverwendbarkeit steigt, wenn du alles sortierst; Include "TColor.bmx" und du benutzt deine Objekte für Farben & Verläufe ohne den Inhalt von Dateien nach dem richtigem Abschnitt zu durchsuchen und stückweise zu kopieren.
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)

Neoxit

BeitragSo, Jun 24, 2012 18:13
Antworten mit Zitat
Benutzer-Profile anzeigen
Okay also sollte ich demnach bspw. das Gesamte Hauptmenü in eine Funktion "schmeißen". Diese Funktion ruft dann widerrum weitere Funktionen wie, Button Funktionen (oder Types) oder ähnliches wie Effekte, Hintergrundfunktionen auf. (Verschachteln?)
Und im endeffekt steht dann in der Hauptschleife der Mainfile einfach "ZeichneDeinHauptmenue()"? (Vorrausgesetzt der Spieler befindet sich gerad in dem Hauptmenü).

Wäre das aber nicht das selbe Prinzip, welches ich ganz zu beginn nannte, nur etwas anders dargestellt im sinne von if system_level = 1 then...?

Das Types definitiv größer werden können habe ich schon bemerkt. Das habe ich an der Spielertype (welche noch relativ simpel ist) gemerkt. Da die ganzen Methoden etc. ja weitreichend sind.

Ich würde mir wirklich ganz gerne einfach mal einen Simplen aufbau einer Mainfile, einer Hauptmenue File etc. anschauen. Auch der Switch vom Menü in meinetwegen ein anderes Menü, oder ein Level.

(Um die Frage mal zu klären ich gehe allgemein vom Aufbau von einem Größeren Projekt aus, was dementsprechend auch die möglichkeit hat, präzise an kleineren "bausteinen" weiterzuarbeiten ohne dafür in 5 weiteren quellcodes viel zu ändern. (Was auch bei Teamarbeit wichtig ist denke ich) Daher habe ich ja die ganze zeit das Wort OOP herausgehoben.)

Allerdings fehlt mir hier nach wie vor (sogut du mir bisher auch versucht hast zu Helfen Xeres und dafür danke ich dir auch zutiefst ^^) irgendwie dieser "Aha!"-Effekt. Der Zusammenhang, die Einzelteile nachher gut zusammenzufügen das sie sauber miteinander Arbeiten. Klar gibt es da wohl mehrere Wege zum Erfolg welche für den Endbenutzer nachher nicht (zumindest weitgehend) sichtbar oder spürbar wäre.

Ich möchte halt ungerne 10.000 Zeilen code in die Hauptschleife hauen ohne Funktionen, Types oder ähnliches. Dann weiß ich nachher nicht mehr wo oben und Unten ist. Rolling Eyes

Simple Beispiele wären hier sehr gefragt um mir bei dieser dusseligen verständnisfrage zu Helfen Crying or Very sad

[EDIT]

Nochmal darüber nachgedacht also bspw folgendermaßen:

Mainfile:
BlitzMax: [AUSKLAPPEN]

'====Mainfile====
Graphics 800,600,0,60

'INCLUDES
Include Hauptmenue.bmx
Include Spielertypes.bmx ' etc....
Include GameManager.bmx

Repeat
Cls

UpdateGame()

Flip
Until KeyHit(KEY_ESCAPE) End


GameManager:
BlitzMax: [AUSKLAPPEN]

Function UpdateGame

Select system_level
Case 1
MacheHauptmenue() ' Funktion für das Hauptmenue zu finden im Hauptmenue.bmx
Case 2
MacheLevel1() 'Funktion für das erste Level welches in Level01.bmx zu finden ist
End Select

End Function



Hauptmenue.bmx
BlitzMax: [AUSKLAPPEN]

Function MacheHauptmenue()
'Hier das Hauptmenue gestalten
CreateButton()
CreateSlider() 'etc...
End Function


So meintest du?

Xeres

Moderator

BeitragSo, Jun 24, 2012 18:28
Antworten mit Zitat
Benutzer-Profile anzeigen
Ausschnitt aus Unit42 (BCC#59):
BlitzMax: [AUSKLAPPEN]
Const GS_Menu:Int = 0, GS_Play:Int = 1
Const GS_CharMenu:Int = 2, GS_CharMenu2:Int = 3, GS_LevelMenu:Int = 4
Const GS_UpgradeMenu:Int = 5
Global GameState:Int = GS_Menu

'[...]

Repeat
Cls

Select GameState
Case GS_Menu; Run_Menu()
Case GS_CharMenu; Run_CharMenu()
Case GS_CharMenu2; Run_CharMenu2()
Case GS_LevelMenu; Run_LevelMenu()
Case GS_Play; Run_Play()
Case GS_UpgradeMenu; Run_UpgradeMenu()
End Select

Flip(0)
FrameTimer.Wait()
Until AppTerminate()
End

Ist nicht so viel anders als If Level, aber es ist etwas strukturierter. Wie Level & Editor auf den selben Code zugreifen, findest du auch in dem Paket (ohne viele Kommentare). Kein Herausragendes Beispiel, wie man arbeiten sollte, aber es funktioniert.

Objekt Orientiertes Version könnte so umgesetzt werden:
BlitzMax: [AUSKLAPPEN]

'* Dieses Objekt wird mit der Verwaltung von TGameState Objekten beauftragt:
Type TGameStateManager

'* Alle registierten Gamestates werden in einem Array gespeichert:
Global AllStates:TGameState[]
Global State:TGameState '* Speichert den Aktiven GameState

Function Add(_state:TGameState)
'* Neuen GameState an das Array anfügen
AllStates:+[_state]
End Function

Function Run()

'* Erstellen und Speichern der GameState Objekte
Add(New TGS_Title)
Add(New TGS_Story)
Add(New TGS_Exit)
'* Das Programm soll hier starten:
Switch("Titlescreen")

rem
Hauptschleife (nur für dieses Beispiel)
Muss nicht zwangläufig in der Run Funktion liegen.
endrem

Repeat
State.Do()
Forever
End Function

Function Switch(_StateName:String) '* Zu einem State wechseln

'* Die bekannten GameStates nach dem korrekten durchsuchen
'* (Eine TMap wäre noch performanter, aber bei 3-10 Einträgen macht es keinen unterschied)
For Local s:TGameState = EachIn AllStates
If s.Name = _StateName Then '* Name gefunden
Rem
Wenn State ganz zu Anfang null ist und in Run zum
Titlescreen gewechselt wird, darf keine Methode aufgerufen werden.
endrem

If State Then State.Leave()
'* Aktiven Status speichern
State = s
'* Was zum beginn des GameStates ausgeführt werden soll:
State.Enter()
Return
EndIf
Next

RuntimeError("No such Gamestate ~q" + _StateName + "~q")

End Function

End Type

Rem
Dieser GameState ist Abstrakt.
Soetwas wie "Sitzgelegenheit" beschreibt, was ein Objekt ist, aber existiert nicht selbst.
Ein Stuhl oder eine Couch sind Sitzgelegenheiten und teilen sich dessen eigenschaften (man
kann drauf sitzen) aber spezifizieren diese, z.B. kann man auf einer Couch mehrere sitzen
oder einer liegen.
Endrem

Type TGameState Abstract
Field Name:String

Method New()
Self.Name = "UNKNOWN"
End Method

Method Enter() Abstract '* Aufruf (einmalig) beim wechsel zu diesem State
Method Leave() Abstract '* Aufruf (einmalig) beim verlassen von diesem State
Method Do() Abstract '* Die Aktionen des States
End Type

Rem
Die Abstracten Methoden, die jeder GameState haben muss, werden
jetzt mit der tatsächlichen Aktion befüllt.
endrem

Type TGS_Title Extends TGameState

Method New()
'* Ein neu erstelltes Objekt bekommt einen eigenen Namen zugewiesen
'* (das überschreibt "UNKNOWN")
Self.Name = "Titlescreen"
End Method

Method Enter()
Print("Starte " + Self.Name)
End Method

Method Leave()
Print("Verlasse " + Self.Name)
End Method

'* Was soll jedes mal in der Hauotschleife getan werden?
Method Do()
Print(Self.Name)
Local in:String = Input()
Select in
Case "q", "quit"
TGameStateManager.Switch("Exit")
Case "s", "story"
TGameStateManager.Switch("Story")
End Select
End Method

End Type


Rem
Dieses Objekt wird mit einem counter erweitert.
Er zählt, wie oft zu diesem GameState gewechselt wurde.
Endrem

Type TGS_Story Extends TGameState

Field counter:Int

Method New()
Self.Name = "Story"
Self.counter = 0
End Method

Method Enter()
Self.counter:+1
Print("Starte " + Self.Name + " [" + Self.counter + "]")
End Method

Method Leave()
Print("Verlasse " + Self.Name)
End Method

Method Do()
Print(Self.Name)
Local in:String = Input()
If in <> "" Then TGameStateManager.Switch("Titlescreen")
End Method

End Type

Type TGS_Exit Extends TGameState

Method New()
Self.Name = "Exit"
End Method

Method Enter()
Print("Und Tschüss")
End Method

Method Leave()
Print("...")
End
End Method

Method Do()
Self.Leave()
End Method

End Type


Rem
Nachdem alles definiert wurde, wird die ganze
Maschinerie nur noch angeworfen:
Endrem

TGameStateManager.Run()


Vielleicht hilft dir das etwas weiter.
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)
  • Zuletzt bearbeitet von Xeres am Mo, Jun 25, 2012 18:49, insgesamt einmal bearbeitet

Neoxit

BeitragMo, Jun 25, 2012 18:22
Antworten mit Zitat
Benutzer-Profile anzeigen
Xeres du hast post.

@ All:

Der zweite code sieht sehr gut aufeinander aufbauend / professionell aus jedoch steige ich durch einige dinge nicht durch. Haben vllt noch mehr Leute Erfahrungen auszutauschen? Oder eine kleine Erklärung bzgl des zweiten?

BladeRunner

Moderator

BeitragMo, Jun 25, 2012 18:29
Antworten mit Zitat
Benutzer-Profile anzeigen
Was verstehst du denn nicht- stell doch einfach genauere Fragen Wink
Zu Diensten, Bürger.
Intel T2300, 2.5GB DDR 533, Mobility Radeon X1600 Win XP Home SP3
Intel T8400, 4GB DDR3, Nvidia GF9700M GTS Win 7/64
B3D BMax MaxGUI

Stolzer Gewinner des BAC#48, #52 & #92

Neoxit

BeitragMo, Jun 25, 2012 18:54
Antworten mit Zitat
Benutzer-Profile anzeigen
Ist mir manchmal n bissl Peinlich weil ich ungerne die Antwort bzgl Grundwissen höre, denn das habe ich soweit eig, DENKE ich Embarassed . Nur mir fehlt manchmal bei Fertigen Code der Gedanke den der Programmierer davon verfolgt hat. Soweit ich das nun in Kurzform sehen kann in 2tem Code von Xeres, gibt es einen "HauptType" den TGameStateManager. Davon gibt es Vererbungen demnach die entsprechenden GameStates selbst (in dem Fall wohl das Level, das Hauptmenü, das Untermenü etc pp.). Soweit klar. Dann gibt es aber noch einen Abstract Type TGameState so wie die Funktion mit dem Präfix welcher mir hier wohl komischerweise eine etwas unbekannte Rolle spielt "_" (Ohne Anführungszeichen). Und ab da komme ich ins Stottern. Um genau zu sein, verliere ich so leicht den überblick der "Zusammenarbeit". Evtl. weil es alles viel "Verzweigter" ist als ein Typisches Funktion in Funktion aufrufen oder ähnliches.

Kurz gesagt:

Der Abstract Type, die Switch() funktion und die Run() Funktion wll mir nicht klar einleuchten. Mir fehlt irgendwie der gewisse bezug zu dem ganzen (klar wenn man sowas noch nie gemacht hat im OOP sinne und dann gleich mal sieht wie sowas "gut" funktioniert) Embarassed

Und Peinlich ist es mir auch noch zu fragen weil ich mir dann teilweise tatsächlich ein bisschen Doof vorkomme ^^ Embarassed oh man oh man ^^

Xeres

Moderator

BeitragMo, Jun 25, 2012 18:56
Antworten mit Zitat
Benutzer-Profile anzeigen
Ich habe ein paar Kommentare ergänzt. Die Parameter von Methoden/Funktionen beginnen nur mit Unterstrichen, weil das mein Stil ist - in manchen Quellcodes werden Felder mit einfachen oder doppelten Unterstrichen als "Privat - um Himmels willen pack das Teil nur mit der richtigen Methode an!" gekennzeichnet.

Die eckigen Klammern in AllStates:TGameState[] kennzeichnen es als Array. Eine TMap oder TList könnte man ebenso gut benutzen - der Unterschied wäre minimal.

Frag ruhig so lange weiter, bis dir klar ist, was passiert.
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)

Neoxit

BeitragMo, Jun 25, 2012 19:14
Antworten mit Zitat
Benutzer-Profile anzeigen
Ahhh danke Xeres! Idea

Jetzt leuchtet mir vieles davon ein. Ich werde mal selber einen solchen GameState Manager schreiben. Die Abstract Methode definiert in diesem Falle nur vor (wie eine Schablone?) was die extends davon "können"?
Somit Quasi eine "einfacherer Erweiterung / ineinadergreifung" der einzelnen Types ermöglicht, wobei im Endeffekt der TGameStateManager nur auf dieses Abstract zugreift (Hoffe ich habe meinen Satz gerad nich allzu kompliziert ausgedrückt ^^)

Ich denke nun konnte ich dem ganzen gleich viel besser folgen!
Wie gesagt ich werde mich da mal ransetzen und euch mal meine Version davon Präsentieren. Smile

Xeres

Moderator

BeitragMo, Jun 25, 2012 19:26
Antworten mit Zitat
Benutzer-Profile anzeigen
Abstract bedeutet nur, das man ein solches Objekt nicht erstellen kann. Du kannst nicht die Abstrakte Vorstellung "Fahrzeug" tatsächlich bauen. Aber du kannst ein Auto oder Motorrad bauen, die die Eigenschaften eines Fahrzeugs haben.
Die Abstrakten Methoden bedeuten, dass das richtige Objekt diese wirklich implementieren muss.

Der Manager kann alle Objekte wie ein GameState behandeln, da alle diese Methoden besitzen müssen.
Ob Auto oder Motorrad: Wenn es ein Fahrzeug ist, hat es Reifen, auf die man zugreifen kann.

Diese Vererbungsgeschichten sind nicht ganz einfach, ich glaube nicht, dass ich da vollends alles Verstanden haben, was möglich ist.
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)

Neoxit

BeitragMo, Jun 25, 2012 21:01
Antworten mit Zitat
Benutzer-Profile anzeigen
So ich habe hier mal dein Beispiel noch einmal aufgebaut mit leichten änderungen und nochmal versucht selber zu kommentieren. Wollte es so wie es jetzt ist eigentlich einmal testen jedoch gibt mir mein compiler den Fehler aus: "Unable to convert "Int" to "TGameState Array".
Muss ich den Array selber vorher nochmal als Int definieren oder ähnliches oder was ist hier einfach falsch?

Hier der src:

BlitzMax: [AUSKLAPPEN]

Graphics 800, 600, 0, 60

'Der GameManager Main-Type
Type TGameManager

Field AllGameStates:TGameState[] 'Einen Array erstellen, welcher alle Gamestates besitzt.
Field ActiveGameState:TGameState 'Der Aktive Gamestate

'Die Funktion zum Hinzufügen weiterer GameStates (Level 1, 2, Hauptmenü etc.)
Function Add(Add_State:TGameState)
AllGameStates:+[Add_State]
End Function

'Starte den Gesamten Zyklus
Function Run()

'Hinzufügen der Gamestates in den Array.
Add(New TGS_Title)
' Add(New TGS_Level_01)
' Add(New TGS_Level_02)

'Zum Init-GameState wechseln (ID)
SwitchGameState(1)

'Die Hauptschleife! :)
Repeat
ActiveGameState.Execute()
Forever

End Function

'Funktion zum Wechseln der GameStates
Function SwitchGameState(ID_GameState:Int)

'Die GameState Liste durchgehen
For Local s:TGameState = EachIn AllGameStates
If s.ID = ID_GameState Then 'Wenn die Entsprechende ID gefunden wurde auf diese Wechseln.


If ActiveGameState = True Then ActiveGameState.Leave()

ActiveGameState = s 'Den Aktiven GameState auf die vorher Entsprechende gefilterte ID setzen

ActiveGameState.Enter() 'Den GameState Starten

Return 'Return das es funktioniert hat

End If
Next

'Runtime Error ausgeben wenn der GameState nicht gefunden wurde.
RuntimeError("Der Entsprechende GameState wurde nicht gefunden! (ID?)")



End Function

End Type

Type TGameState Abstract

Field ID:Int
Field Name:String
Field Desc:String

Method New()
Self.ID = 0 'Keine ID
Self.Name = "Unbekannt"
End Method

Method Enter() Abstract
Method Leave() Abstract
Method Execute() Abstract

End Type


Type TGS_Title Extends TGameState

Method New()
'Hier die Informationen für den TGameState Type setzen.
Self.ID = 1 'ID SETZEN
Self.Name = "Title" 'Name des Gamestates
Self.Desc = "Dies ist der Titlescreen wuhuu!" 'Beschreibung des Gamestates
End Method

Method Leave()

End Method

Method Enter()

End Method

'Der wichtige Hauptschleifenpart
Method Execute()
DrawText ("Du befindest dich derzeitig in: " + Self.Name + " mit der ID: " + Self.ID + " !"), 5, 5
DrawText ("Sonstige Informationen:"), 5, 100
DrawText (Self.Desc), 5, 150

'Userinformationen
DrawText ("1. Level 01"), 5, 300
DrawText ("2. Level 02"), 5, 315
DrawText ("3. Programm Beenden"), 5, 350

End Method

End Type

TGameManager.Run()

Xeres

Moderator

BeitragMo, Jun 25, 2012 21:18
Antworten mit Zitat
Benutzer-Profile anzeigen
Field <> Global
Mein TGameManager benutzt Globale Variablen. Wenn du mit Fields arbeiten willst, musst du eine Instanz mit New erstellen und kannst aus den Funktionen Methoden machen.
Da ich nur einen Manager sinnig fand, habe ich es mit Globalen gemacht. Bzw. der Manager Type sammelt nur Globale und Funktionen unter seinem Namen zusammen - ein richtiges Objekt wird nie erzeugt.
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)

Neoxit

BeitragMo, Jun 25, 2012 21:43
Antworten mit Zitat
Benutzer-Profile anzeigen
Stumpf von mir, ja du hast recht logisch ist das logisch, weil warum sollte es mehr als 1 GameManager geben. Rolling Eyes Danke für die schnelle Antwort ^^

Neoxit

BeitragMo, Jun 25, 2012 22:45
Antworten mit Zitat
Benutzer-Profile anzeigen
Soooooooooo, Quellcode ist Kopierbar und direkt ausführbar.
Würde mich freuen wenn du es dir einmal anschaust Xeres! Smile

Ich danke dir sooooooooo sehr an dieser Stelle, bin gerade (nein, nicht gelogen) vom stuhl aufgesprungen mit nem Handklatscher und nen "Yeah!" ^^

Also hier der Code: (Wenn noch Mängel oder dinge die Anders gemacht werden sollten, wissen lassen! (Kann man in der Execute Method das jeweilige Level ruhig darin aufbauen?)

BlitzMax: [AUSKLAPPEN]

Graphics 800, 600, 0, 60

'Der GameManager Main-Type
Type TGameManager

Global AllGameStates:TGameState[] 'Einen Array erstellen, welcher alle Gamestates besitzt.
Global ActiveGameState:TGameState 'Der Aktive Gamestate

'Die Funktion zum Hinzufügen weiterer GameStates (Level 1, 2, Hauptmenü etc.)
Function Add(Add_State:TGameState)
AllGameStates:+[Add_State]
End Function

'Starte den Gesamten Zyklus
Function Run()

'Hinzufügen der Gamestates in den Array.
Add(New TGS_Title)
Add(New TGS_Level_01)
Add(New TGS_Level_02)

'Zum Init-GameState wechseln (ID)
SwitchGameState(1)

'Die Hauptschleife! :)
Repeat
Cls
ActiveGameState.Execute()
Flip
Forever

End Function

'Funktion zum Wechseln der GameStates
Function SwitchGameState(ID_GameState:Int)

'Die GameState Liste durchgehen
For Local s:TGameState = EachIn AllGameStates
If s.ID = ID_GameState Then 'Wenn die Entsprechende ID gefunden wurde auf diese Wechseln.


If ActiveGameState Then ActiveGameState.Leave()

ActiveGameState = s 'Den Aktiven GameState auf die vorher Entsprechende gefilterte ID setzen

ActiveGameState.Enter() 'Den GameState Starten

Return 'Return das es funktioniert hat

End If
Next

'Runtime Error ausgeben wenn der GameState nicht gefunden wurde.
RuntimeError("Der Entsprechende GameState wurde nicht gefunden! (ID?)")



End Function

End Type

Type TGameState Abstract

Field ID:Int
Field Name:String
Field Desc:String

Method New()
Self.ID = 0 'Keine ID
Self.Name = "Unbekannt"
End Method

Method Enter() Abstract
Method Leave() Abstract
Method Execute() Abstract

End Type


Type TGS_Title Extends TGameState

Method New()
'Hier die Informationen für den TGameState Type setzen.
Self.ID = 1 'ID SETZEN
Self.Name = "Title" 'Name des Gamestates
Self.Desc = "Dies ist der Titlescreen wuhuu!" 'Beschreibung des Gamestates
End Method

Method Leave()

End Method

Method Enter()

End Method

'Der wichtige Hauptschleifenpart
Method Execute()
DrawText ("Du befindest dich derzeitig in: " + Self.Name + " mit der ID: " + Self.ID + " !"), 5, 5
DrawText ("Sonstige Informationen:"), 5, 100
DrawText (Self.Desc), 5, 150

'Userinformationen
DrawText ("1. Level 01"), 5, 300
DrawText ("2. Level 02"), 5, 315
DrawText ("3. Programm Beenden"), 5, 350

If KeyHit(KEY_1) Then
TGameManager.SwitchGameState(2) 'Wechsle auf GameStateID 2 = Level 01
End If

If KeyHit(KEY_2) Then
TGameManager.SwitchGameState(3) 'Wechsle auf GameStateID 3 = Level 02
End If

If KeyHit(KEY_3) Then
End
End If
End Method

End Type

'Das erste Level
Type TGS_Level_01 Extends TGameState
Method New()
Self.ID = 2
Self.Name = "Level 01"
Self.Desc = "Die Erde - Level 01!"
End Method

Method Leave()

End Method

Method Enter()

End Method

Method Execute()
DrawText Self.Name, 5, 5
DrawText Self.Desc, 5, 30

DrawText ("Mit Escape ins Titlemenü!"), 5, 100

If KeyHit(KEY_ESCAPE) Then
TGameManager.SwitchGameState(1) 'Wechsle auf GameStateID 1 = Hauptmenü
End If

End Method

End Type

'Das Zweite Level
Type TGS_Level_02 Extends TGameState
Method New()
Self.ID = 3
Self.Name = "Level 02"
Self.Desc = "Baumkronenfroschteich - Level 02!"
End Method

Method Leave()

End Method

Method Enter()

End Method

Method Execute()
DrawText Self.Name, 5, 5
DrawText Self.Desc, 5, 30

DrawText ("Mit Escape ins Titlemenü!"), 5, 100

If KeyHit(KEY_ESCAPE) Then
TGameManager.SwitchGameState(1) 'Wechsle auf GameStateID 1 = Hauptmenü
End If
End Method

End Type

TGameManager.Run()



Mir ist aufgefallen das der Thread mittlerweile übrigens auch ziemlich vielen anderen die sich dafür Interessieren sein könnte wie sowas abläuft Smile


[ E D I T ]

Sorry Doppelpost -.-* Embarassed

Xeres

Moderator

BeitragMo, Jun 25, 2012 23:11
Antworten mit Zitat
Benutzer-Profile anzeigen
Erstens:
Code: [AUSKLAPPEN]
TGameManager.SwitchGameState(2) 'Wechsle auf GameStateID 2 = Level 01
Der Kommentar ist nötig, weil 2 als Zahl eindeutig aber aussage los ist. Wie weiter oben bei der Select-Case Konstruktion mit Funktionen solltest du Konstanten verwenden, um die Lesbarkeit zu erhöhen.
Ob normale Konstanten oder z.B. im TGameState verpackt, kannst du nach deinem Geschmack variieren.
BlitzMax: [AUSKLAPPEN]
Type TGameState Abstract

Field ID:Int
Field Name:String
Field Desc:String

Const GS_Title:Int = 1
'[...]

Type TGS_Title Extends TGameState

Method New()
'Hier die Informationen für den TGameState Type setzen.
Self.id = TGameState.GS_Title 'ID SETZEN
Self.Name = "Title" 'Name des Gamestates
Self.Desc = "Dies ist der Titlescreen wuhuu!" 'Beschreibung des Gamestates
End Method
'[...]

TGameManager.SwitchGameState(TGameState.GS_Title)


Zweitens: Willst du Level wirklich als einzelne GameStates umsetzen? Normalerweise würde ich nur ein Play Status verwenden und in dem Level laden und darstellen. So macht das nur Sinn, wenn a) die Level krass unterschiedlich sind, oder keine uniforme Struktur haben und b) komplett statisch aus dem Code erzeugt werden.
Für den BCC z.B. wäre es vollkommen okay es so zu machen, aber sobald du Level aus Dateien lädst, macht der Ansatz keinen Sinn mehr.
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)

Neoxit

BeitragMo, Jun 25, 2012 23:27
Antworten mit Zitat
Benutzer-Profile anzeigen
Das mit den Konstanten ist mir auch schon in den Sinn gekommen. Geändert werden diese ja sowieso nicht sondern sind von anfang an festgelegt und sollten demnach auch so bleiben (Es sei denn ich lade ID's etc aus Dateien).

Bzgl der Aufteilung das ich nun als Beispiel Level 01. und Level 02. genommen habe, war einfach weil ich einfach nur ein paar Orientierungspunkte brauchte. Der Gamestate ist nachher ein einziger, welcher sich dementsprechend ändert (Das level eben) .

Das war alles rein aus demonstrativen Gründen so benannt.

Nehmen wir mal anderes Bsp:

1 Titlescreen, 1 Mapscreen (oder wie in deinem Bsp: PlayState), 1 Kommandoscreen (bspw. in einem anderen Genre oder so).

Neue Antwort erstellen


Übersicht BlitzMax, BlitzMax NG Beginners-Corner

Gehe zu:

Powered by phpBB © 2001 - 2006, phpBB Group