From a73cb20f6adb01fb492859ae9bc83ce9a6c1eb0b Mon Sep 17 00:00:00 2001 From: nickf2k <44032235+nickf2k@users.noreply.github.com> Date: Thu, 14 Mar 2024 22:16:03 +0700 Subject: [PATCH 1/7] add header KlondikeWorld class --- doc/tutorials/klondike/step5.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/tutorials/klondike/step5.md b/doc/tutorials/klondike/step5.md index 9f55cb73887..05d8bc0afa8 100644 --- a/doc/tutorials/klondike/step5.md +++ b/doc/tutorials/klondike/step5.md @@ -412,6 +412,7 @@ The proposal is to have a new KlondikeWorld class, which replaces the default `w FlameGame. The new world contains (almost) everything we need to play the game and is created or re-created during each of the above actions. +### KlondikeWorld class ### A stripped-down KlondikeGame class From 7f224a2633bb8e161e61a6fd3bdc4e27402e496e Mon Sep 17 00:00:00 2001 From: nickf2k <44032235+nickf2k@users.noreply.github.com> Date: Mon, 18 Mar 2024 23:21:42 +0700 Subject: [PATCH 2/7] add Creating KlondikeWorld section and modify KlondikeGame example --- doc/tutorials/klondike/step5.md | 42 +++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/doc/tutorials/klondike/step5.md b/doc/tutorials/klondike/step5.md index 05d8bc0afa8..0c1be83aa7c 100644 --- a/doc/tutorials/klondike/step5.md +++ b/doc/tutorials/klondike/step5.md @@ -412,8 +412,50 @@ The proposal is to have a new KlondikeWorld class, which replaces the default `w FlameGame. The new world contains (almost) everything we need to play the game and is created or re-created during each of the above actions. + ### KlondikeWorld class +In Flame, a World is a type of Component that can contain other Components, such as Piles. + +You can learn more about Worlds in game programming here: + + + +We won't dive too deep into Worlds here, just understand their purpose for now. + + +#### Creating KlondikeWorld + +Let's create a World for our Klondike game, called KlondikeWorld. +At the start of the game, we'll create a World. Each new game will be represented by a new World. +Worlds are also created when the player restarts the game. +Each World is responsible for loading its own Components and dealing the cards accordingly. +Therefore, the onLoad() method will be moved from the KlondikeGame class to KlondikeWorld. +First, let's modify the KlondikeGame class: + +```dart + // KlondikeWorld is our new World +class KlondikeGame extends FlameGame { + // we keep all constant values here + KlondikeGame() : super(world: KlondikeWorld()); // init a World + + // delete the onLoad() method + + // keep below method + Sprite klondikeSprite(double x, double y, double width, double height) { + // ... + } +} + +``` + + +#### what properties? + + +#### what actions we should implement at this class + + ### A stripped-down KlondikeGame class Here is the new code for the KlondikeGame class (what is left of it). From bf1592a5c51c8c2368ffd616e8e8127a8a323012 Mon Sep 17 00:00:00 2001 From: nickf2k <44032235+nickf2k@users.noreply.github.com> Date: Wed, 20 Mar 2024 23:56:18 +0700 Subject: [PATCH 3/7] add explanation about World initialization in KlondikeGame --- doc/tutorials/klondike/step5.md | 50 +++++++++++++++++++++++++++------ 1 file changed, 42 insertions(+), 8 deletions(-) diff --git a/doc/tutorials/klondike/step5.md b/doc/tutorials/klondike/step5.md index 0c1be83aa7c..f9116b4580f 100644 --- a/doc/tutorials/klondike/step5.md +++ b/doc/tutorials/klondike/step5.md @@ -415,23 +415,24 @@ re-created during each of the above actions. ### KlondikeWorld class -In Flame, a World is a type of Component that can contain other Components, such as Piles. +In Flame, a ```World``` is a type of ```Component``` that can contain other ```Components```, + such as Piles. -You can learn more about Worlds in game programming here: +You can learn more about World in game programming here: -We won't dive too deep into Worlds here, just understand their purpose for now. +We won't dive too deep into World here, just understand their purpose for now. #### Creating KlondikeWorld -Let's create a World for our Klondike game, called KlondikeWorld. -At the start of the game, we'll create a World. Each new game will be represented by a new World. +Let's create a ```World``` for our Klondike game, called ```KlondikeWorld```. +At the start of the game, we'll create a ```World```. Each new game will be represented by a new World. Worlds are also created when the player restarts the game. -Each World is responsible for loading its own Components and dealing the cards accordingly. -Therefore, the onLoad() method will be moved from the KlondikeGame class to KlondikeWorld. -First, let's modify the KlondikeGame class: +Each ```World``` is responsible for loading its own Components and dealing the cards accordingly. +Therefore, the ```onLoad()``` method will be moved from the ```KlondikeGame``` class to ```KlondikeWorld```. +First, let's modify the ```KlondikeGame``` class: ```dart // KlondikeWorld is our new World @@ -449,6 +450,39 @@ class KlondikeGame extends FlameGame { ``` +The code above shows that when ```FlameGame``` is initialized, a ```KlondikeWorld``` is +also initialized. +Previously, without the ```KlondikeWorld``` class, FlameGame would create a +default ```World``` upon +initialization. It's important to note that a Game can have multiple Worlds, +but only one World is displayed at a time. + +We removed the ```onLoad()``` method from the ```KlondikeGame``` class and now +need to re-implement it in ```KlondikeWorld```. + +First, create a file called ```klondike_world.dart``` in the lib folder and add +the following ```KlondikeWorld``` class: + +```dart + class KlondikeWorld extends World with HasGameReference { + final cardGap = KlondikeGame.cardGap; + final topGap = KlondikeGame.topGap; + final cardSpaceWidth = KlondikeGame.cardSpaceWidth; + final cardSpaceHeight = KlondikeGame.cardSpaceHeight; + + final stock = StockPile(position: Vector2(0.0, 0.0)); + final waste = WastePile(position: Vector2(0.0, 0.0)); + final List foundations = []; + final List tableauPiles = []; + final List cards = []; + @override + Future onLoad() async { + // ... + + } +} +``` + #### what properties? From 329ad8cad6c9404230dc183dd69d1441529cbcfe Mon Sep 17 00:00:00 2001 From: nickf2k <44032235+nickf2k@users.noreply.github.com> Date: Tue, 2 Apr 2024 22:56:27 +0700 Subject: [PATCH 4/7] docs: move onLoad from KlondikeGame to KlondikeWorld --- doc/tutorials/klondike/step5.md | 54 +++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/doc/tutorials/klondike/step5.md b/doc/tutorials/klondike/step5.md index f9116b4580f..070711d3998 100644 --- a/doc/tutorials/klondike/step5.md +++ b/doc/tutorials/klondike/step5.md @@ -482,6 +482,60 @@ the following ```KlondikeWorld``` class: } } ``` +In the above code we have changed a bit compared to KlondikeGame from step 4. +By declaring stock, waste, foundations, tableauPiles, cards, +we confirm that it is owned by the KlondikeWord class. +And so onLoad has also changed a bit +```dart + @override + Future onLoad() async { + await Flame.images.load('klondike-sprites.png'); + // set up the stock and waste piles + stock.position = Vector2(cardGap, topGap); + waste.position = Vector2(cardSpaceWidth + cardGap, topGap); + + // set up the foundation piles + for (var i = 0; i < 4; i++) { + foundations.add( + FoundationPile( + i, + position: Vector2((i + 3) * cardSpaceWidth + cardGap, topGap), + ), + ); + } + // set up the tableau piles + for (var i = 0; i < 7; i++) { + tableauPiles.add( + TableauPile( + position: Vector2( + i * cardSpaceWidth + cardGap, + cardSpaceHeight + topGap, + ), + ), + ); + } + // set up the cards + for (var rank = 1; rank <= 13; rank++) { + for (var suit = 0; suit < 4; suit++) { + final card = Card(rank, suit); + card.position = stock.position; + cards.add(card); + } + } + add(stock); + add(waste); + addAll(foundations); + addAll(tableauPiles); + addAll(cards); + + } +``` +This code only has the same settings from step 4 and + changes a few points as follows: +- The Piles have been declared before, so we just set +the position and add elements to the list +- there are 4 foundations, 7 tableaus, 52 cards +- replace world.add with add. because we are now in the World class #### what properties? From 7d8e4ba7c0e43aef47d1741e56e438e0e3e8eff9 Mon Sep 17 00:00:00 2001 From: nickf2k <44032235+nickf2k@users.noreply.github.com> Date: Tue, 2 Apr 2024 22:58:46 +0700 Subject: [PATCH 5/7] docs: markdown-fix by melos --- doc/tutorials/klondike/step5.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/doc/tutorials/klondike/step5.md b/doc/tutorials/klondike/step5.md index 070711d3998..40321106f9a 100644 --- a/doc/tutorials/klondike/step5.md +++ b/doc/tutorials/klondike/step5.md @@ -482,10 +482,12 @@ the following ```KlondikeWorld``` class: } } ``` -In the above code we have changed a bit compared to KlondikeGame from step 4. -By declaring stock, waste, foundations, tableauPiles, cards, + +In the above code we have changed a bit compared to KlondikeGame from step 4. +By declaring stock, waste, foundations, tableauPiles, cards, we confirm that it is owned by the KlondikeWord class. And so onLoad has also changed a bit + ```dart @override Future onLoad() async { @@ -530,9 +532,11 @@ And so onLoad has also changed a bit } ``` + This code only has the same settings from step 4 and changes a few points as follows: -- The Piles have been declared before, so we just set + +- The Piles have been declared before, so we just set the position and add elements to the list - there are 4 foundations, 7 tableaus, 52 cards - replace world.add with add. because we are now in the World class From 5d7b4fad446eaf315bd75b47eefdf39fd2bc1712 Mon Sep 17 00:00:00 2001 From: nickf2k <44032235+nickf2k@users.noreply.github.com> Date: Thu, 10 Oct 2024 23:47:46 +0700 Subject: [PATCH 6/7] docs: tutorial klondike step 5 change docs for KlondikeWorld --- doc/tutorials/klondike/step5.md | 134 ++++++-------------------------- 1 file changed, 25 insertions(+), 109 deletions(-) diff --git a/doc/tutorials/klondike/step5.md b/doc/tutorials/klondike/step5.md index 40321106f9a..4e1dbf50a32 100644 --- a/doc/tutorials/klondike/step5.md +++ b/doc/tutorials/klondike/step5.md @@ -413,133 +413,49 @@ FlameGame. The new world contains (almost) everything we need to play the game a re-created during each of the above actions. -### KlondikeWorld class +### KlondikeWorld Class -In Flame, a ```World``` is a type of ```Component``` that can contain other ```Components```, - such as Piles. -You can learn more about World in game programming here: +#### Introduction to `World` in Flame - - -We won't dive too deep into World here, just understand their purpose for now. +In Flame, `World` is a special type of `Component` that can contain other `Components`, +such as Piles in the Klondike game. Think of `World` as the playground where everything +happens—dealing cards, arranging them, and player actions. +You can learn more about `World` in game programming here: -#### Creating KlondikeWorld +- An introduction to `World` in game programming: [Ecampus](https://ecampusontario.pressbooks.pub/gamedesigndevelopmenttextbook/chapter/what-is-a-game-world/) +- The concept of `World` in Flame: [Flame Docs](https://docs.flame-engine.org/latest/flame/camera_component.html#world) -Let's create a ```World``` for our Klondike game, called ```KlondikeWorld```. -At the start of the game, we'll create a ```World```. Each new game will be represented by a new World. -Worlds are also created when the player restarts the game. -Each ```World``` is responsible for loading its own Components and dealing the cards accordingly. -Therefore, the ```onLoad()``` method will be moved from the ```KlondikeGame``` class to ```KlondikeWorld```. -First, let's modify the ```KlondikeGame``` class: +We won’t go too deep here; just understand that `World` is where everything is managed in the game. -```dart - // KlondikeWorld is our new World -class KlondikeGame extends FlameGame { - // we keep all constant values here - KlondikeGame() : super(world: KlondikeWorld()); // init a World - // delete the onLoad() method +#### Creating `KlondikeWorld` - // keep below method - Sprite klondikeSprite(double x, double y, double width, double height) { - // ... - } -} +Let’s dive in! When the game starts, we’ll create a new “World” called `KlondikeWorld` to hold + all the components of the game. Each new game round will create a new `World`—like resetting the playing + field every time a new game starts. This ensures that everything is set up from + scratch, keeping the game fresh. -``` -The code above shows that when ```FlameGame``` is initialized, a ```KlondikeWorld``` is -also initialized. -Previously, without the ```KlondikeWorld``` class, FlameGame would create a -default ```World``` upon -initialization. It's important to note that a Game can have multiple Worlds, -but only one World is displayed at a time. +#### Transitioning from `KlondikeGame` to `KlondikeWorld` -We removed the ```onLoad()``` method from the ```KlondikeGame``` class and now -need to re-implement it in ```KlondikeWorld```. +Previously, `KlondikeGame` managed all `Piles` and `Cards`. However, in step 5, we move this responsibility + to `KlondikeWorld` for better separation. As a result, the `onLoad()` method is transferred from `KlondikeGame` + to `KlondikeWorld`. This change makes the code easier to manage and aligns with object-oriented programming + principles. -First, create a file called ```klondike_world.dart``` in the lib folder and add -the following ```KlondikeWorld``` class: - -```dart - class KlondikeWorld extends World with HasGameReference { - final cardGap = KlondikeGame.cardGap; - final topGap = KlondikeGame.topGap; - final cardSpaceWidth = KlondikeGame.cardSpaceWidth; - final cardSpaceHeight = KlondikeGame.cardSpaceHeight; - - final stock = StockPile(position: Vector2(0.0, 0.0)); - final waste = WastePile(position: Vector2(0.0, 0.0)); - final List foundations = []; - final List tableauPiles = []; - final List cards = []; - @override - Future onLoad() async { - // ... - } -} -``` +#### Updating the `KlondikeGame` Class -In the above code we have changed a bit compared to KlondikeGame from step 4. -By declaring stock, waste, foundations, tableauPiles, cards, -we confirm that it is owned by the KlondikeWord class. -And so onLoad has also changed a bit +First, we need to modify `KlondikeGame` to use `KlondikeWorld` instead of Flame’s default `World`: ```dart - @override - Future onLoad() async { - await Flame.images.load('klondike-sprites.png'); - // set up the stock and waste piles - stock.position = Vector2(cardGap, topGap); - waste.position = Vector2(cardSpaceWidth + cardGap, topGap); - - // set up the foundation piles - for (var i = 0; i < 4; i++) { - foundations.add( - FoundationPile( - i, - position: Vector2((i + 3) * cardSpaceWidth + cardGap, topGap), - ), - ); - } - // set up the tableau piles - for (var i = 0; i < 7; i++) { - tableauPiles.add( - TableauPile( - position: Vector2( - i * cardSpaceWidth + cardGap, - cardSpaceHeight + topGap, - ), - ), - ); - } - // set up the cards - for (var rank = 1; rank <= 13; rank++) { - for (var suit = 0; suit < 4; suit++) { - final card = Card(rank, suit); - card.position = stock.position; - cards.add(card); - } - } - add(stock); - add(waste); - addAll(foundations); - addAll(tableauPiles); - addAll(cards); - - } -``` - -This code only has the same settings from step 4 and - changes a few points as follows: +class KlondikeGame extends FlameGame { + KlondikeGame() : super(world: KlondikeWorld()); -- The Piles have been declared before, so we just set -the position and add elements to the list -- there are 4 foundations, 7 tableaus, 52 cards -- replace world.add with add. because we are now in the World class + // No need for the onLoad() method here anymore +} #### what properties? From 0233bdc8ca3dcec8975689b96bda9a15402327b8 Mon Sep 17 00:00:00 2001 From: nickf2k Date: Sat, 12 Oct 2024 17:32:29 +0700 Subject: [PATCH 7/7] docs: replace A stripped-down KlondikeGame class section --- doc/tutorials/klondike/step5.md | 69 +++++++++------------------------ 1 file changed, 18 insertions(+), 51 deletions(-) diff --git a/doc/tutorials/klondike/step5.md b/doc/tutorials/klondike/step5.md index 4e1dbf50a32..5872dfede23 100644 --- a/doc/tutorials/klondike/step5.md +++ b/doc/tutorials/klondike/step5.md @@ -438,35 +438,16 @@ Let’s dive in! When the game starts, we’ll create a new “World” called ` scratch, keeping the game fresh. -#### Transitioning from `KlondikeGame` to `KlondikeWorld` +### Transitioning from `KlondikeGame` to `KlondikeWorld` -Previously, `KlondikeGame` managed all `Piles` and `Cards`. However, in step 5, we move this responsibility - to `KlondikeWorld` for better separation. As a result, the `onLoad()` method is transferred from `KlondikeGame` - to `KlondikeWorld`. This change makes the code easier to manage and aligns with object-oriented programming - principles. +Previously, `KlondikeGame` managed all `Piles` and `Cards`. However, in step 5, we move this responsibility to +`KlondikeWorld` for better separation of concerns and cleaner code organization. This change improves the +structure by adhering to object-oriented programming principles, allowing for more modular and maintainable code. #### Updating the `KlondikeGame` Class -First, we need to modify `KlondikeGame` to use `KlondikeWorld` instead of Flame’s default `World`: - -```dart -class KlondikeGame extends FlameGame { - KlondikeGame() : super(world: KlondikeWorld()); - - // No need for the onLoad() method here anymore -} - - -#### what properties? - - -#### what actions we should implement at this class - - -### A stripped-down KlondikeGame class - -Here is the new code for the KlondikeGame class (what is left of it). +Now, we modify the `KlondikeGame` class to utilize `KlondikeWorld` instead of Flame's default `World`. Here’s the new structure: ```dart enum Action { newDeal, sameDeal, changeDraw, haveFun } @@ -486,27 +467,28 @@ class KlondikeGame extends FlameGame { ); // Constant used when creating Random seed. - static const int maxInt = 0xFFFFFFFE; // = (2 to the power 32) - 1 + static const int maxInt = 0xFFFFFFFE; - // This KlondikeGame constructor also initiates the first KlondikeWorld. + // Constructor initiates the first KlondikeWorld. KlondikeGame() : super(world: KlondikeWorld()); - // These three values persist between games and are starting conditions - // for the next game to be played in KlondikeWorld. The actual seed is - // computed in KlondikeWorld but is held here in case the player chooses - // to replay a game by selecting Action.sameDeal. + // These values persist between games and are used as starting conditions + // for the next game in KlondikeWorld. int klondikeDraw = 1; int seed = 1; Action action = Action.newDeal; } ``` -Huh! What happened to the `onLoad()` method? And what's this `seed` thing? And how does -KlondikeWorld get into the act? Well, everything that used to be in the `onLoad()` method is now -in the `onLoad()` method of KlondikeWorld, which is an extension of the `World` class and is a type -of `Component`, so it can have an `onLoad()` method, as can any `Component` type. The content of -the method is much the same as before, except that `world.add(` becomes just `add(`. It also brings -in some `addButton()` references, but more on these later. +#### Explanation of Changes + +Ohhh, so what happened to the `onLoad()` method? Well, previously, `KlondikeGame` handled everything in the `onLoad()` method, managing all the game components like `Piles` and `Cards`. But now, to make things cleaner and more organized, we’ve moved that responsibility over to `KlondikeWorld`. + +Everything that used to be inside the `onLoad()` method of `KlondikeGame` is now in `KlondikeWorld`'s `onLoad()` method. This keeps the code more modular, meaning it's easier to maintain and update. Instead of `KlondikeGame` getting messy with too many tasks, we let `KlondikeWorld` handle the game setup and logic. + +And what’s this `seed` thing? Ah, good question! So, the `seed` attribute is like a magic key. It lets you replay the same game configuration by selecting `Action.sameDeal`. This is super useful when you want to give players the option to retry the exact same game setup. The `seed` ensures that the randomness in the game (like card shuffling) can be repeated exactly the same way. Think of it as a way to recreate the same starting point in a game. + +So, with the `seed` safely stored in `KlondikeGame`, the `KlondikeWorld` can use it to keep everything consistent between rounds. If you choose `Action.sameDeal`, it will load the same shuffled deck and game state, letting you replay that round with the same starting conditions. Pretty cool, right? ### Using a Random Number Generator seed @@ -517,21 +499,6 @@ reproducible behavior when you are in the development and testing stage. Here it provide exactly the same deal of the Klondike cards when the player requests `Same deal`. -### Introducing the new KlondikeWorld class - -The `class KlondikeGame` declaration specifies that this extension of the FlameGame class must -have a world of type KlondikeWorld (i.e. `FlameGame`). Didn't know we could do -that for a game, did we? So how does the first instance of KlondikeWorld get created? It's all in -the KlondikeGame constructor code: - -```dart - KlondikeGame() : super(world: KlondikeWorld()); -``` - -The constructor itself is a default constructor, but the colon `:` begins a constructor -initialization sequence which creates our world for the first time. - - ### Buttons We are going to use some buttons to activate the various ways of restarting the Klondike Game. First