"Cacti VS Llamas" - BCC #98
Warum die CTCharacter Klasse etwas übers Animieren wei&
![](/themes/Standard/Forum/icon_minipost.png)
Ein paar Links, die mich vor ~10 Tagen dazu bewogen haben, aus dem CTCharacter Typ mehr zu machen als so etwas wie ein reines Model Object. Jetzt fragt der CTCharacter selbst Animationen an, um Schadens- und Todesanimationen abzuspielen. Das ganze so zu entkoppeln, dass man "reine" business objects hat, die in mehreren Schichten von dem gemalten Bildschirminhalt getrennt sind, hat nur Probleme gemacht. Denn der Zustand einer Spielfigur in Bewegung hängt direkt von der Bewegungsanimation ab. "Reine" Entitiies haben keinen Sinn für vergangene Spielzeit.
Ein Spiel ist keine Tabellenkalkulation. Man muss andere patterns anwenden.
https://gamedev.stackexchange....rite-class
https://gamedev.stackexchange....ent-timing
https://gamedev.stackexchange....o-complete
Ein Spiel ist keine Tabellenkalkulation. Man muss andere patterns anwenden.
https://gamedev.stackexchange....rite-class
https://gamedev.stackexchange....ent-timing
https://gamedev.stackexchange....o-complete
Stand zur Bltiz Coding Compo #98
![](/themes/Standard/Forum/icon_minipost.png)
Kein eigentlich spielbares Spiel. Die meisten UI Mechaniken sind fertig und ich könnte jetzt leicht weiter den zweiten Spieler einbauen und dann Charaktere konfigurieren.
Demo:
Was fehlt:
- Aufstellung vom zweiten Spieler
- Verschiedene Stats für die Kämpfer (für Spielvariation)
- Verschiedene Angriffsmodi als "deal X damage" und Randomisierung von Ergebniswerten, also die eigentliche Spielmechanik
Demo:
![user posted image](https://i.imgur.com/0PiBUiv.gif)
Was fehlt:
- Aufstellung vom zweiten Spieler
- Verschiedene Stats für die Kämpfer (für Spielvariation)
- Verschiedene Angriffsmodi als "deal X damage" und Randomisierung von Ergebniswerten, also die eigentliche Spielmechanik
![Smile](/forum/images/smiles/icon_smile.gif)
Llama & Cacti on Envato Elements
![](/themes/Standard/Forum/icon_minipost.png)
![user posted image](https://i.imgur.com/gpibD9t.png)
Envato Elements hat doch tatsächlich gewagt, mir mein Thema zu klauen
![Smile](/forum/images/smiles/icon_smile.gif)
BCC kaputt #3
![](/themes/Standard/Forum/icon_minipost.png)
Natürlich ist die verfügbare bmx-ng Version immer noch 0.99, mit all den Bugs, die seit Februar gefixt wurden.
Gestern habe ich mehrere Stunden damit verbracht, Travis (Continuous Integration service) so einzurichten, dass es mit der letzten Version von der Website den aktuellen bcc Code kompiliert und als Paket anbietet. Ich habe die Hoffnung, dass damit in Zukunft regelmäßigere Releases von bmx-ng möglich werden.
Hilft mir mit den Bugs für den BCC herzlich wenig
Ich hoffe mal, dass sich bis zum Wochenende noch was tut.
Gestern habe ich mehrere Stunden damit verbracht, Travis (Continuous Integration service) so einzurichten, dass es mit der letzten Version von der Website den aktuellen bcc Code kompiliert und als Paket anbietet. Ich habe die Hoffnung, dass damit in Zukunft regelmäßigere Releases von bmx-ng möglich werden.
Hilft mir mit den Bugs für den BCC herzlich wenig
![Smile](/forum/images/smiles/icon_smile.gif)
Animationssequenzen und Game Events
![](/themes/Standard/Forum/icon_minipost.png)
Ein komisches Problem bei Spielen ist der Mix aus Model Code und Animationen.
Wenn ein Charakter so viel Schaden nimmt, dass er stirbt, dann kann er eine Nachricht an andere Objekte schicken. Aber wenn das Spiel noch eine 10-sekündige Todesanimation abspielt, dann kann der Auslöser dieser Animatio nicht schon das "OnDeath" Ereignis sein. Erst nach Abspielen der Animation sollte die Nachricht verschickt werden. Sonst zieht der nächste Spieler in der Zwischenzeit schon wieder, oder das Spiel beendet, bevor der Effekt richtig abgespielt wurde. Das muss ja alles seine Ordnung haben.
Damit hat man auf einmal eine Sequenz von Ereignissen.
- Charakter registriert HP <= 0: sendet "OnWillDie" Nachricht
- Daraufhin werden Animationen abgespielt und senden "OnDeathAnimationDidFinish"
- Daraufhin wird das "OnCharacterDidDie" Ereginis als finaler Effekt versandt
Damit muss das Model, der Kämpfer, wissen, dass er sterben wird, aber nicht, dass er gestorben ist. Sein Gestorbensein hängt von Animationen ab. Das ist schräg.
Wenn ein Charakter so viel Schaden nimmt, dass er stirbt, dann kann er eine Nachricht an andere Objekte schicken. Aber wenn das Spiel noch eine 10-sekündige Todesanimation abspielt, dann kann der Auslöser dieser Animatio nicht schon das "OnDeath" Ereignis sein. Erst nach Abspielen der Animation sollte die Nachricht verschickt werden. Sonst zieht der nächste Spieler in der Zwischenzeit schon wieder, oder das Spiel beendet, bevor der Effekt richtig abgespielt wurde. Das muss ja alles seine Ordnung haben.
Damit hat man auf einmal eine Sequenz von Ereignissen.
- Charakter registriert HP <= 0: sendet "OnWillDie" Nachricht
- Daraufhin werden Animationen abgespielt und senden "OnDeathAnimationDidFinish"
- Daraufhin wird das "OnCharacterDidDie" Ereginis als finaler Effekt versandt
Damit muss das Model, der Kämpfer, wissen, dass er sterben wird, aber nicht, dass er gestorben ist. Sein Gestorbensein hängt von Animationen ab. Das ist schräg.
Game Scene Transitions & Fight effects
![](/themes/Standard/Forum/icon_minipost.png)
Alles als Video: https://imgur.com/cHJplFN
Die Aufteilung in Szenen funktioniert gut, und das Projekt lässt sich leicht in Szenen als einzelne Module organisieren. Hier sind die aktuellen.
[size=large]1. Party Picker[/size]
Wähle Kämpfer aus deiner Armee für den nächsten Kampf aus.
[size=large]2. Party Placement[/size]
Stelle die Charaktere auf der Karte/dem Schlachtfeld auf.
[size=large]3. Kampf[/size]
Der Spieler wählt einen Charakter aus, eine Aktion aus dem Aktionsmenü, und ein Ziel. Hier sieht man, wie "Foo" Schaden nimmt.
Die Schadensmechanik und das Ausscheiden aus dem Kampf werden schon mitgeteilt, aber Sterben hat noch keine Konsequenz.
Die Aufteilung in Szenen funktioniert gut, und das Projekt lässt sich leicht in Szenen als einzelne Module organisieren. Hier sind die aktuellen.
[size=large]1. Party Picker[/size]
Wähle Kämpfer aus deiner Armee für den nächsten Kampf aus.
![user posted image](https://i.imgur.com/pLn4mWx.png)
[size=large]2. Party Placement[/size]
Stelle die Charaktere auf der Karte/dem Schlachtfeld auf.
![user posted image](https://i.imgur.com/v5Qkz0R.png)
[size=large]3. Kampf[/size]
Der Spieler wählt einen Charakter aus, eine Aktion aus dem Aktionsmenü, und ein Ziel. Hier sieht man, wie "Foo" Schaden nimmt.
Die Schadensmechanik und das Ausscheiden aus dem Kampf werden schon mitgeteilt, aber Sterben hat noch keine Konsequenz.
![user posted image](https://i.imgur.com/nvJgAJk.png)
Programmieren besteht auf Organisation und Boilerplate
![](/themes/Standard/Forum/icon_minipost.png)
Einer der wichtigsten Skills, den ich in den letzten 10 Jahren als Programmierer entwickelt habe, ist wahrscheinlich, dass ich boilerplate code aushalten kann.
Wenn ich mich an meine Versuche erinnere, 2001 am QuickRPG zu arbeiten, dann erinnere ich mich an viele Probleme mit der Organisation des Code. Die große Main Loop habe ich in Subroutinen aufgeteilt, aber die Tastatureingabe hat direkt die Charakterkoordinaten verändert, und auch die Kamera bewegt. Selbst Map-Wechsel, Animationen und Skripte haben dabei funktioniert. Aber das ließ sich nicht gut erweitern.
18 Jahre später und ich kann Prinzipien von OOP und functional programming verwenden, um ein Dutzend Objekte und Funktionen zu schreiben, die selbst augenscheinlich überhaupt nichts tun.
Der einzige Zweck dieser Teile: den Code leichter verständlich machen. Kleine Einheiten zu schaffen, die ich verstehen kann.
Ich habe jetzt noch immer kein spielbares Spiel, aber schon 70 (!!!!) .bmx Dateien, jede mit einem Typ oder einer handvoll Interfaces. QuickRPG bestand aus insgesamt weniger als 10 Dateien.
Wenn ich mich an meine Versuche erinnere, 2001 am QuickRPG zu arbeiten, dann erinnere ich mich an viele Probleme mit der Organisation des Code. Die große Main Loop habe ich in Subroutinen aufgeteilt, aber die Tastatureingabe hat direkt die Charakterkoordinaten verändert, und auch die Kamera bewegt. Selbst Map-Wechsel, Animationen und Skripte haben dabei funktioniert. Aber das ließ sich nicht gut erweitern.
18 Jahre später und ich kann Prinzipien von OOP und functional programming verwenden, um ein Dutzend Objekte und Funktionen zu schreiben, die selbst augenscheinlich überhaupt nichts tun.
Der einzige Zweck dieser Teile: den Code leichter verständlich machen. Kleine Einheiten zu schaffen, die ich verstehen kann.
Ich habe jetzt noch immer kein spielbares Spiel, aber schon 70 (!!!!) .bmx Dateien, jede mit einem Typ oder einer handvoll Interfaces. QuickRPG bestand aus insgesamt weniger als 10 Dateien.
Action Systems
![](/themes/Standard/Forum/icon_minipost.png)
Video mit einer Übersicht:
https://www.youtube.com/watch?v=NtUNI59UzMU
Meine Notizen (kriege kein vernünftiges Listenformat hin, darum Code, sorry)
Code: [AUSKLAPPEN]
https://www.youtube.com/watch?v=NtUNI59UzMU
Meine Notizen (kriege kein vernünftiges Listenformat hin, darum Code, sorry)
Code: [AUSKLAPPEN]
Terms:
- First/Second Turn Advantage: one player is only reactive and the other has the advantage
- "Swing" Advantage: the game swings in one faction's favor and you have to do something about it to even out the odds
- Action Economy: actions per turn
- Resource Economy: items, ammo, equipment
Action Systems:
- Alternating Action System: each player controls one character per turns only
- hard to take back the "Swing"
- Multi-Alternating Action System: each player issues commands for multiple characters at once
- complex tactics are possible
- you can be pummeled to death by a concentrated attack easily
- Simultaneous Initiative Action System: player issue commands en bloc, but it plays out according to the characters's initiative
- balances out swing and first-turn-advantages via character traits
- possible on computers; the ones above could be done on boards
- Independent Initiative Action System: action speed determines when a character is next
- affects action economy directly
- what is a "turn"?
Final Fantasy Tactics:
- Clock Tick: action point charges
- each loop increment the character's `CT` value by his speed
- when `CT >= 100`, the character can do an action that costs `CT` (40 or 60 per action)
- some actions have a speed value which means they not only cost `CT` but also take in-game time/ticks
- transparent action economy
- First/Second Turn Advantage: one player is only reactive and the other has the advantage
- "Swing" Advantage: the game swings in one faction's favor and you have to do something about it to even out the odds
- Action Economy: actions per turn
- Resource Economy: items, ammo, equipment
Action Systems:
- Alternating Action System: each player controls one character per turns only
- hard to take back the "Swing"
- Multi-Alternating Action System: each player issues commands for multiple characters at once
- complex tactics are possible
- you can be pummeled to death by a concentrated attack easily
- Simultaneous Initiative Action System: player issue commands en bloc, but it plays out according to the characters's initiative
- balances out swing and first-turn-advantages via character traits
- possible on computers; the ones above could be done on boards
- Independent Initiative Action System: action speed determines when a character is next
- affects action economy directly
- what is a "turn"?
Final Fantasy Tactics:
- Clock Tick: action point charges
- each loop increment the character's `CT` value by his speed
- when `CT >= 100`, the character can do an action that costs `CT` (40 or 60 per action)
- some actions have a speed value which means they not only cost `CT` but also take in-game time/ticks
- transparent action economy
TMap
![](/themes/Standard/Forum/icon_minipost.png)
Today I Learned, dass die "Compare" Methode dafür verantwortlich ist, festzustellen, ob ein Key in einer TMap scchon vorhanden ist oder nicht.
Für eine 2D-Position, die ich als Key nutze, um Figuren zu platzieren, habe ich jetzt also folgendes eingebaut:
BlitzMax: [AUSKLAPPEN]
Damit werden Positionen zuerst nach ihrer X-Komponente (Spalte) sortiert, dann nach Y (Reihe).
Für eine 2D-Position, die ich als Key nutze, um Figuren zu platzieren, habe ich jetzt also folgendes eingebaut:
BlitzMax: [AUSKLAPPEN]
Type CTTokenPosition
Field column%, row%
Method New(column%, row%)
Assert column >= 0 Else "column negative"
Assert row >= 0 Else "row negative"
Self.column = column
Self.row = row
End Method
Method Compare:Int(other:Object)
Local otherPosition:CTTokenPosition = CTTokenPosition(other)
If Not otherPosition Then Return Super.Compare(other)
If Self.column < otherPosition.column
Return -1
Else If Self.column > otherPosition.column
Return 1
Else ' ==
If Self.row = otherPosition.row Then Return 0
If Self.row < otherPosition.row Then Return -1
If Self.row > otherPosition.row Then Return 1
End If
End Method
Method IsEqual:Int(other:CTTokenPosition)
Return (Compare(other) = 0)
End Method
End Type
Damit werden Positionen zuerst nach ihrer X-Komponente (Spalte) sortiert, dann nach Y (Reihe).
Cactus Party Picker
![](/themes/Standard/Forum/icon_minipost.png)
Nächster großer Meilenstein: Game Scenes laufen. Damit wechseln sich verschiedene, in sich abgeschlossene Szenen oder Bildschirme ab.
Beispiel Asteroids-Clone: Hauptmenü, Credits, Scores, und eigentliches Spiel.
In meinem Fall: man sucht sich aus der Kaktusarmee eine Kampftruppe aus, und die wird dann auf dem Schlachtfeld angezeigt.
Größte Schwierigkeit hierbei war, neben der BCC Bugs, das Interface für den "Party Picker". Aufbauend auf den Dingen, die ich bisher implementiert hatte, sind die zwei Spalten im Party Picker unabhängige Menüs. Wenn man im linken Menü etwas auswählt, wird es ins rechte eingefügt, und umgedreht.
Der Aufbau davon ist folgendermaßen:
- SplitListView enthält zwei Menu Objekte
- aktiviert man die SplitListView, aktiviert diese eines der Menüs, das dann Tastendruck verarbeitet
- Die beiden Menüs schlucken Tasten-Events nicht, sondern geben sie an die SplitListView weiter
- SplitListView reagiert nur auf horizontale Bewegung und aktiviert das je passende Menü (links oder rechts)
Indem ich jeweils ein Menü aktiviere und eins deaktiviere, kann ich mit einer SplitListView zwei Menüs unmabhängig voneinander kontrollieren.
Source Code: https://github.com/DivineDomin...ew.bmx#L74
Die SplitListView registriert sich als Callback (delegate) für die Menüs. Wenn man in einem Menü etwas wählt, gibt die SplitListView das ihrem eigenen Callback weiter: was wurde gewählt, und auf welcher Seite.
Source Code: https://github.com/DivineDomin...w.bmx#L141
Der Callback, der die Auswahl eigentlich erst verarbeitet, ist ein anderes Objekt. In diesem Falle eine PartyPickerView. Diese besteht aus der geteilten Liste, dem zentrierten Label mit dem Status, wieviele Charaktere ausgewählt wurden, und den Bestuatigungsknöpfen ganz unten. Im Grunde malt sie bloß alles an den richtigen Platz und leitet Events ihrer Subviews weiter an ihren eigenen Callback.
So kümmert sich jedes GUI Element um seine Sachen und informiert sein Parent-Element bei Bedarf.
Code: [AUSKLAPPEN]
+----------------+ show +---------------------+
| +------------------->+ +------------------------+
| CTPickParty | | CTPartyPickerView | |
| +<-------------------+ +--+ |
+----------------+ didSelectParty +-----+---------------+ | |
| | | proceed/cancel
lists | | |
| status | |
+---------------v--+ +---v-v-------+ +-------v----------+
| | | | | |
| CTSplitListView | | CTLabel | | CTDialog |
| | | | | |
+--+------------+--+ +-------------+ +-------+----------+
| | |
left | |right | is a horizontal menu
+----------v-+ +--v--------+ +-------v----------+
| | | | | |
| CTMenu | | CTMenu | | CTMenu |
| | | | | |
+------------+ +-----------+ +------------------+
Die horizontalen Buttons ganz unten sind auch ein Menu Objekt, nur horizontal gemalt.
Das use case/Service Objekt, das die Views erst erstellt und das eigentlich interessante Event abbekommt, ist PickParty und der Code ist hier:
https://github.com/DivineDomin...kParty.bmx
Dieses Objekt informiert die PickPartyGameScene über die Auswahl, und diese Szene wechselt zum Kampf. Dann geht's los.
Beispiel Asteroids-Clone: Hauptmenü, Credits, Scores, und eigentliches Spiel.
In meinem Fall: man sucht sich aus der Kaktusarmee eine Kampftruppe aus, und die wird dann auf dem Schlachtfeld angezeigt.
![user posted image](https://i.imgur.com/G4lOV1c.gif)
Größte Schwierigkeit hierbei war, neben der BCC Bugs, das Interface für den "Party Picker". Aufbauend auf den Dingen, die ich bisher implementiert hatte, sind die zwei Spalten im Party Picker unabhängige Menüs. Wenn man im linken Menü etwas auswählt, wird es ins rechte eingefügt, und umgedreht.
Der Aufbau davon ist folgendermaßen:
- SplitListView enthält zwei Menu Objekte
- aktiviert man die SplitListView, aktiviert diese eines der Menüs, das dann Tastendruck verarbeitet
- Die beiden Menüs schlucken Tasten-Events nicht, sondern geben sie an die SplitListView weiter
- SplitListView reagiert nur auf horizontale Bewegung und aktiviert das je passende Menü (links oder rechts)
Indem ich jeweils ein Menü aktiviere und eins deaktiviere, kann ich mit einer SplitListView zwei Menüs unmabhängig voneinander kontrollieren.
Source Code: https://github.com/DivineDomin...ew.bmx#L74
Die SplitListView registriert sich als Callback (delegate) für die Menüs. Wenn man in einem Menü etwas wählt, gibt die SplitListView das ihrem eigenen Callback weiter: was wurde gewählt, und auf welcher Seite.
Source Code: https://github.com/DivineDomin...w.bmx#L141
Der Callback, der die Auswahl eigentlich erst verarbeitet, ist ein anderes Objekt. In diesem Falle eine PartyPickerView. Diese besteht aus der geteilten Liste, dem zentrierten Label mit dem Status, wieviele Charaktere ausgewählt wurden, und den Bestuatigungsknöpfen ganz unten. Im Grunde malt sie bloß alles an den richtigen Platz und leitet Events ihrer Subviews weiter an ihren eigenen Callback.
So kümmert sich jedes GUI Element um seine Sachen und informiert sein Parent-Element bei Bedarf.
Code: [AUSKLAPPEN]
+----------------+ show +---------------------+
| +------------------->+ +------------------------+
| CTPickParty | | CTPartyPickerView | |
| +<-------------------+ +--+ |
+----------------+ didSelectParty +-----+---------------+ | |
| | | proceed/cancel
lists | | |
| status | |
+---------------v--+ +---v-v-------+ +-------v----------+
| | | | | |
| CTSplitListView | | CTLabel | | CTDialog |
| | | | | |
+--+------------+--+ +-------------+ +-------+----------+
| | |
left | |right | is a horizontal menu
+----------v-+ +--v--------+ +-------v----------+
| | | | | |
| CTMenu | | CTMenu | | CTMenu |
| | | | | |
+------------+ +-----------+ +------------------+
Die horizontalen Buttons ganz unten sind auch ein Menu Objekt, nur horizontal gemalt.
Das use case/Service Objekt, das die Views erst erstellt und das eigentlich interessante Event abbekommt, ist PickParty und der Code ist hier:
https://github.com/DivineDomin...kParty.bmx
Dieses Objekt informiert die PickPartyGameScene über die Auswahl, und diese Szene wechselt zum Kampf. Dann geht's los.