You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Merge remote-tracking branch 'origin/main' into offline
* origin/main: (30 commits)
fix: use absolute URLs for assets
chore: update my newsletter link
fix: typo & clarity in source control
feat: add kenney assets to Game Dev Resources
chore(source-control): improve clarity
chore: further thank vlevo
chore(ch12): improve clarity & proper itch.io formatting
chore: add DR Zine to Outro
Update README.md
06-time-attack.md
Update README.md
03-spit-fire.md
02-player-movement.md
01-hello-dragon.md
I keep shooting 'cause I keep missing introduction.md
grammar README.md
tweaks
tweaks
tweaks
tweaks
...
Copy file name to clipboardExpand all lines: src/01-hello-dragon.md
+4-4Lines changed: 4 additions & 4 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -10,7 +10,7 @@ We'll start by rendering an image and some simple text on the screen. But first,
10
10
11
11
You're ready to work on your game. Let's get to it!
12
12
13
-
**ProTip:** don't delete the zip file! You can unzip it again when the times comes to start working on your next game. It's helpful to keep it around.
13
+
**ProTip:** don't delete the zip file! You can unzip it again when the time comes to start working on your next game. It's helpful to keep it around.
14
14
15
15
## What's in the Engine Zip
16
16
@@ -51,7 +51,7 @@ This isn't a game... yet! But it is doing three key things:
51
51
52
52
And you haven't even written any code yet. Not a bad starting place.
53
53
54
-
DRGTK handles the boring stuff when it comes to making games—dealing with low-level APIs like graphics, sound, and the game window. We can instead focus on creating our game instead of, for example, figuring out how to save data in a way that's compartible with Linux, Mac, Windows, Android, iOS, and web.
54
+
DRGTK handles the boring stuff when it comes to making games—dealing with low-level APIs like graphics, sound, and the game window. We can instead focus on creating our game instead of, for example, figuring out how to save data in a way that's compatible with Linux, Mac, Windows, Android, iOS, and the web.
55
55
56
56
## An Overview of the Main Game File
57
57
@@ -171,7 +171,7 @@ Let's take a detour down Screen Coordinates Road. The `x` and `y` values are coo
171
171
172
172
DRGTK games are made up of a window that's 1280x720 pixels in size. That's 1280 pixels wide and 720 pixels tall. The rectangle of the game screen contains 921600 pixels, that's those two numbers multiplied. Each of those pixels has a coordinate on the plane. It makes it easy to refer to a specific pixel by using its `x` and `y` position.
173
173
174
-
DRGTK starts 0, 0 in the lower left. So 1280, 720 would be the upper right. **Note:**this varies from most game engines, libraries, and tools, but it's intentional to make it easier to think about gravity and follows the geometric 2D plane that is taught in mathematics.
174
+
DRGTK starts 0, 0 in the lower left. So 1280, 720 would be the upper right. **Note:**This varies from most game engines, libraries, and tools, but it's intentional to make it easier to think about gravity and follows the geometric 2D plane that is taught in mathematics.
175
175
176
176
It's important to keep coordinates in mind, as we'll be using them a lot when making our game. A major aspect of games is moving things around on the screen, which we do by changing their coordinates.
177
177
@@ -215,7 +215,7 @@ The new code refactors (changes the implementation of the code without changing
215
215
216
216
The `"Hello #{friend}!"` code does what's called string interpolation. It takes whatever `friend` is, hopefully a name as a string, and inserts it. It's pretty similar to this code: `"Hello " + friend + "!"`, but quite a bit friendlier to use. The `#{}` tells Ruby to run any Ruby code within those curly braces.
217
217
218
-
Methods in Ruby return a value. Return values can then be used by the caller for whatever purposes are needed. In the example above, the return value is the string we built. Ruby returns the vaue of the last line of the method definition automatically. But you can explicitly return early with `return`, which can be useful if you want to end the execution of a method early.
218
+
Methods in Ruby return a value. Return values can then be used by the caller for whatever purposes are needed. In the example above, the return value is the string we built. Ruby returns the value of the last line of the method definition automatically. But you can explicitly return early with `return`, which can be useful if you want to end the execution of a method early.
Copy file name to clipboardExpand all lines: src/02-player-movement.md
+10-10Lines changed: 10 additions & 10 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -29,7 +29,7 @@ There are a couple of new things here:
29
29
-`args.state`
30
30
-`||=`
31
31
32
-
Let's start with `args.state`. It's basically a blob that can be anything you want it to be, a bit like Kirby. Feed it `player_x` and it keeps track of it. Whatever you feed the `args.state`, it'll be accessible in future ticks. Keeping track of game state across ticks is important! It's part of the game loop. If we don't know where the player last was, how can we calculate where they should move to? We need to keep track of it in someplace. `args.state` is a fine place to start.
32
+
Let's start with `args.state`. It's basically a blob that can be anything you want it to be, a bit like Kirby. Feed it `player_x` and it keeps track of it. Whatever you feed the `args.state`, it'll be accessible in future ticks. Keeping track of the game state across ticks is important! It's part of the game loop. If we don't know where the player last was, how can we calculate where they should move to? We need to keep track of it in someplace. `args.state` is a fine place to start.
33
33
34
34
You can define anything on `args.state`, so it's up to you to use useful names. You could make `args.state.bleh` and set it to your favorite color, `args.state.bleh = "blue"` or your age, `args.state.age = 30`. Much like Kirby, `args.state` doesn't care what you feed it. It's just hungry for your data.
35
35
@@ -53,7 +53,7 @@ That calls the `puts` method and passes our variable `name` to it as a parameter
53
53
54
54
Remember how tick is running once every 60 seconds? We don't want to always set `args.state.player_x` to `120`. We just want to set it to that initially and then we'll update that value when we press keys on our keyboard or buttons on our gamepad. We haven't done that yet, but that's what's next.
55
55
56
-
Wow! That was a lot of explanation for two measly lines of code. But I'm telling ya', they're two really important lines of code when it comes to game programming.
56
+
Wow! That was a lot of explanation for two measly lines of code. But I'm telling ya, they're two really important lines of code when it comes to game programming.
57
57
58
58
Then, finally, we change the `x` and `y` value for the dragon sprite to be the value stored in `args.state` so that we can actually make use of that value instead of our hard-coded position before.
59
59
@@ -96,7 +96,7 @@ elsif args.inputs.right
96
96
end
97
97
```
98
98
99
-
This section checks for horizontal movement. If the left input is pressed, reduce the player's x position by 10 pixels. `-=` means, subtract the value on the right from the value on the left. It's the same as `args.state.player_x = args.state.player_x - 10`, but it's much more concise. We increase `player_x` to move right, decrease it to move left.
99
+
This section checks for horizontal movement. If the left input is pressed, reduce the player's x position by 10 pixels. `-=` means, subtract the value on the right from the value on the left. It's the same as `args.state.player_x = args.state.player_x - 10`, but it's much more concise. We increase `player_x` to move right and decrease it to move left.
100
100
101
101
`if` and `elsif` are conditional checks. The code only runs if the value is true (more specifically, truthy, but let's not worry about that yet).
102
102
@@ -107,7 +107,7 @@ elsif args.inputs.down
107
107
args.state.player_y -=10
108
108
end
109
109
```
110
-
Then we check for vertical movement. We add to `player_y` to move up, decrease it to move down.
110
+
Then we check for vertical movement. We add to `player_y` to move up and decrease it to move down.
111
111
112
112
What if we wanted our dragon to move faster though? We could change those four instances of `10` to be `12` and see how that feels, sure. But that's annoying to update it all over. Let's make use of a variable! We'll call it `speed`:
113
113
@@ -147,27 +147,27 @@ Our dragon won't leave the screen. Woot woot! We've got some serious code here!
147
147
148
148
We moved the width and height of the player into variables so that they're easier to reference and reuse. Boom. We need those to do some math on the boundaries too. There's a general programming idea out there known as Don't Repeat Yourself (DRY). As soon as you have a piece of code, especially a number, that represents a value and is used multiple times, put it in a variable. This makes its intent clear as to what it represents and makes it easier to change. Win-win.
149
149
150
-
Here's the good stuff. We check the boundary for the xaxis:
150
+
Here's the good stuff. We check the boundary for the x-axis:
151
151
152
152
```ruby
153
153
{{#include code/chapter_02/app/main.rb:20:26}}
154
154
```
155
155
156
156
We check the right side of the screen: if the current player's x position plus their width is greater than `args.grid.w`, then we set the x position to the width of the screen (`args.grid.w`) minus the width of the sprite. For example, if we move the sprite so it has the x position of 1284, 4 pixels past the right edge of the screen, we override that change and set it to 1280 minus the player's width.
157
157
158
-
It's so important that this happens after checking for input. You don't want to change `args.state.player_x` after this check, otherwise the boundary won't be enforced. Order matters with the code we write within `tick`.
158
+
It's so important that this happens after checking for input. You don't want to change `args.state.player_x` after this check, otherwise, the boundary won't be enforced. Order matters with the code we write within `tick`.
159
159
160
160
`args.grid.w` is the width of the screen. It's always 1280, but we don't want to have that magic number in our code. So we use `args.grid.w`.
161
161
162
-
Next we check the left side of the screen: if the player's x is less than 0, then we set it to zero. That's a bit similar to the right side, just simpler.
162
+
Next, we check the left side of the screen: if the player's x is less than 0, then we set it to zero. That's a bit similar to the right side, just simpler.
163
163
164
164
Then we do the same thing for the top and bottom of the screen by checking the y position.
165
165
166
166
## Extra Credit
167
167
168
-
- When you move the dragon horizontally and vertically at the same time, the dragon moves twice as fast. How could you make it so the dragon moves at a uniform speed still when that happens?
169
-
- Ruby has a method which ensures that a numeric value is between some bounds, find it and replace our boundschecking code with it.
168
+
- When you move the dragon horizontally and vertically at the same time, the dragon moves twice as fast. How could you make it so the dragon still moves at a uniform speed when that happens?
169
+
- Ruby has a method which ensures that a numeric value is between some bounds, find it and replace our bounds-checking code with it.
170
170
171
171
## What's Next
172
172
173
-
In the next chapter we'll make our dragon spit fireballs when we press a key or button. Watch out!
173
+
In the next chapter, we'll make our dragon spit fireballs when we press a key or button. Watch out!
Copy file name to clipboardExpand all lines: src/03-spit-fire.md
+5-3Lines changed: 5 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -58,7 +58,7 @@ Then where we check for the action input, push a fireball into the `arg.state.fi
58
58
59
59
All we have to do is render our fireballs by pushing them into the `args.outputs.labels` collection. DragonRuby is smart enough to know that if we push an array into any `args.outputs` collection it'll flatten it and display them correctly. Thanks, DragonRuby!
60
60
61
-
We've been using arrays to represent the fields for different data in our game like labels and sprites, but arrays have other uses too. Arrays are a great way to keep track of information we need in a list. The array we've created in the code above tracks our fireballs.
61
+
We've been using arrays to represent the fields for different data in our game like labels and sprites, but arrays have other uses too. Arrays are a great way to keep track of information that we need in a list. The array we've created in the code above tracks our fireballs.
62
62
63
63
Play your game and see what happens! Fireballs everywhere. Wait! You're not impressed by those fireballs? I'd be pretty frightened if the word "fireball" was flying at me.
64
64
@@ -70,7 +70,7 @@ Wait, where are you going? Why are you muttering "I didn't sign up to read no st
70
70
71
71
Guess what? We're sticking with ole "fireball" for now! It's silly and fun and I haven't found a good fireball sprite to use. We'll get there, we'll get there. But let's first make the fireballs move across the screen.
72
72
73
-
When we moved our player dragon, we took the x and y position and added or subtracted values in each `#tick` based upon if any directional input was pressed. Our fireballs will move regardless of any button pressed once they're extruded from our dragon's mouth. Because our game is simple and the dragon only faces to the right, all of the fireballs will move to the right. How do we go about that on our X-Y axis? We just increase the `x` position of the fireball each tick. Let's do that and see what happens:
73
+
When we moved our player dragon, we took the x and y position and added or subtracted values in each `#tick` based on if any directional input was pressed. Our fireballs will move regardless of any button pressed once they're extruded from our dragon's mouth. Because our game is simple and the dragon only faces to the right, all of the fireballs will move to the right. How do we go about that on our X-Y axis? We just increase the `x` position of the fireball each tick. Let's do that and see what happens:
Copy file name to clipboardExpand all lines: src/06-time-attack.md
+2-2Lines changed: 2 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -2,7 +2,7 @@
2
2
3
3
We've _almost_ got a game. But we need some way for the game to end. A lot of game loops end with the player's character dying, where they respawn or start over again. Other game loops end when the player reaches the end of a level.
4
4
5
-
For our simple game, let's add a 30second timer that counts down. The objective of our game will be to see how many targets the player can hit in that time window. Let's call our game **Target Practice**. Every dragon needs some practice before they head out into battle, right?
5
+
For our simple game, let's add a 30-second timer that counts down. The objective of our game will be to see how many targets the player can hit in that time window. Let's call our game **Target Practice**. Every dragon needs some practice before they head out into battle, right?
6
6
7
7
Adding a timer to our game introduces a few new concepts we'll build out in this chapter:
8
8
@@ -42,7 +42,7 @@ Way at the bottom of `#tick`, let's display a label with the time remaining:
42
42
43
43
We use the same pattern of creating a `labels` array, pushing in the player's score and the time, in ticks, remaining. In order to display the time remaining as seconds, we divide it by 60 and round. We do the opposite of what we did when we set the total time in ticks.
44
44
45
-
The `alignment_enum` lets us specify that we want the text to be right-aligned instead of the default left alignment. This let's us nicely position our timer in the upper right corner of the game.
45
+
The `alignment_enum` lets us specify that we want the text to be right-aligned instead of the default left alignment. This lets us nicely position our timer in the upper right corner of the game.
46
46
47
47

Copy file name to clipboardExpand all lines: src/07-high-score.md
+10-10Lines changed: 10 additions & 10 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,15 +1,15 @@
1
1
# High-Score
2
2
3
-
Saving and loading data is a key piece of functionality when it comes to making games. We may want to keep track of all sorts of important data across play sessions. For _Target Practice_, let's keep it simple and track the high-score each time a new one is set.
3
+
Saving and loading data is a key piece of functionality when it comes to making games. We may want to keep track of all sorts of important data across play sessions. For _Target Practice_, let's keep it simple and track the highscore each time a new one is set.
4
4
5
5
## Load & Save Data
6
6
7
-
When the game is over, let's display whether or not a new high-score was achieved. If it is higher than the previous, we'll save that new high-score. Otherwise, we'll display the high-score and encourage the player to try to beat it.
7
+
When the game is over, let's display whether or not a new highscore was achieved. If it is higher than the previous one, we'll save that new highscore. Otherwise, we'll display the highscore and encourage the player to try to beat it.
8
8
9
9
This will require two parts:
10
10
11
-
1. Saving the score when a new high-score is achieved
12
-
2. Loading the previous high-score to compare the player's score to
11
+
1. Saving the score when a new highscore is achieved
12
+
2. Loading the previous highscore to compare the player's score to
13
13
14
14
DragonRuby GTK gives us two handy methods to do so:
15
15
@@ -26,26 +26,26 @@ We'll be working exclusively in `#game_over_tick`:
26
26
27
27
We read the value from the `HIGH_SCORE_FILE`, which is `high-score.txt`. If the file doesn't exist, it'll be `0` because we call `#to_i` on the file reading process.
28
28
29
-
Then, if we haven't saved the high-score yet and the player's score is greater than the high-score, we save it in the file and set a value in `args.state.saved_high_score` so that we don't save it every single time `#game_over_tick` gets called each frame of the game.
29
+
Then, if we haven't saved the highscore yet and the player's score is greater than the highscore, we save it in the file and set a value in `args.state.saved_high_score` so that we don't save it every single time `#game_over_tick` gets called each frame of the game.
When we're constructing our `labels` to render, we add a condition that checks if we've got a new high-score. If we do, then we let the player know. Otherwise we display the current high-score for them to chase after.
35
+
When we're constructing our `labels` to render, we add a condition that checks if we've got a new highscore. If we do, then we let the player know. Otherwise, we display the current highscore for them to chase after.
36
36
37
-

37
+

38
38
39
-

39
+

40
40
41
41
## Summary
42
42
43
43
We load and save data relating to how our player has done. While saving one value is likely to be a bit too trivial for most games, the core concepts are pretty similar. You'll write your data to a file, read that file, and then do whatever you need to with it.
44
44
45
45
## Extra Credit
46
46
47
-
- How would you save the date and time the high-score was achieved at?
48
-
- Displaying one high-score is neat. But what if it showed the last 5 scores in addition to the highest score?
47
+
- How would you save the date and time the highscore was achieved at?
48
+
- Displaying one highscore is neat. But what if it showed the last 5 scores in addition to the highest score?
0 commit comments