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
Copy file name to clipboardExpand all lines: chapters/game_design/chapter.md
+37-36Lines changed: 37 additions & 36 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -72,16 +72,17 @@ Let’s start with our testApp. There are a few things we definitely know we’l
72
72
73
73
###Gamestates
74
74
75
-
First let’s create the basic structure of our game. Games typically have at least three parts: a start screen, the game itself, and an end screen. We need to keep track of which section of the game we’re in, which we’ll do using a variable called a game state. In this example, our game state variable is a string, and the three parts of our game are `"start"`, `"game"`, and `"end"`. Let’s add a score and a player at this point as well.
76
-
```
75
+
First let’s create the basic structure of our game. Games typically have at least three parts: a start screen, the game itself, and an end screen. We need to keep track of which section of the game we’re in, which we’ll do using a variable called a game state. In this example, our game state variable is a string, and the three parts of our game are `"start"`, `"game"`, and `"end"`. Let’s add a score and a player at this point as well.
76
+
77
+
```cpp
77
78
string game_state;
78
79
int score;
79
80
Player player_1;
80
81
```
81
82
82
83
We’ll then divide up `testApp`’s `update()` and `draw()` loops between those game states:
Finally, let’s make sure that we can move forward from the start screen. In this example, when the player is on the start screen and releases the space key, they’ll be taken to the game.
@@ -257,7 +258,7 @@ Let’s make our bullets next. In order to have a variable number of bullets on
257
258
258
259
Our bullet class will look a lot like the player class, having a position, speed, width, pointer to an image, and various functions. The big difference is that the bullets will keep track of who they came from (since that will affect who they can hurt and which direction they move).
@@ -306,7 +307,7 @@ Now that our bullet class is implemented, we can go back to `testApp::setup()` a
306
307
307
308
For now, our `update_bullets()` function will call the `update()` function in each bullet, and will also get rid of bullets that have flown offscreen in either direction.
@@ -386,7 +387,7 @@ Remember, the first parameter in the bullet’s setup is whether it comes from t
386
387
Let’s move on to our enemy. This process should be familiar by now. Add an `ofImage enemy_image;` and a `vector<Enemy> enemies;` to `testApp.h`. Additionally, add `float max_enemy_amplitude;` and `float max_enemy_shoot_interval;` to `testApp.h`--these are two of the enemy parameters we’ll affect with OSC.
387
388
Your enemy class will look like this:
388
389
389
-
```
390
+
```cpp
390
391
class Enemy {
391
392
public:
392
393
ofPoint pos;
@@ -409,7 +410,7 @@ public:
409
410
Our enemy’s horizontal movement will be shaped by the values fed to a sine wave (which we’ll see in a moment). We’ll keep track of our amplitude variable (so different enemies can have different amplitudes). We’ll also want to keep track of whether enough time has passed for this enemy to shoot again, necessitating the start_shoot and shoot_interval variables. Both of these variables will actually be set in our setup() function. Finally, we’ll have a boolean function that will tell us whether the enemy can shoot this frame or not.
@@ -441,7 +442,7 @@ In update, we’re using the current elapsed time in frames to give us a constan
441
442
In `time_to_shoot()`, we check to see whether the difference between the current time and the time this enemy last shot is greater than the enemy’s shooting interval. If it is, we set `start_shoot` to the current time, and return true. If not, we return false.
442
443
Let’s integrate our enemies into the rest of our `testApp.cpp`:
Great! Except… we don’t have any enemies yet! Definitely an oversight. This is where our level controller comes in. Add `LevelController level_controller;` to your `testApp.h`.
546
547
Our level controller class is super-simple:
547
548
548
-
```
549
+
```cpp
549
550
classLevelController {
550
551
public:
551
552
float start_time;
@@ -560,7 +561,7 @@ As you might guess, all it’ll really do is keep track of whether it’s time t
560
561
561
562
Inside our `LevelController.cpp`:
562
563
563
-
```
564
+
```cpp
564
565
void LevelController::setup(float s) {
565
566
start_time = s;
566
567
interval_time = 500;
@@ -578,7 +579,7 @@ When we set up our level controller, we’ll give it a starting time. It’ll us
578
579
579
580
We’ll wait to set up our level controller until the game actually starts--namely, when the game state changes from `"start"` to `"game"`.
@@ -791,14 +792,14 @@ And let’s add the line to import the OSC at the top of your file after your pr
791
792
792
793
Add the following:
793
794
794
-
```
795
+
```cpp
795
796
#include<iostream>
796
797
#include"ofxOsc.h"
797
798
```
798
799
799
800
Next let’s set up all of our variables we are going to use to receive OSC data and map it to in game values. Add the following code into your class.
800
801
801
-
```
802
+
```cpp
802
803
classLiveTesting
803
804
{
804
805
public:
@@ -852,7 +853,7 @@ At this point, go ahead and launch TouchOSC on your device and the Touch OSC des
852
853
853
854
We are going to make this interface now and deploy it to our phone. We will make this interface to control these parameters in our game:
854
855
855
-
```
856
+
```cpp
856
857
//these are the values we will be tweaking during testing
857
858
float max_enemy_amplitude;
858
859
int interval_time;
@@ -947,7 +948,7 @@ Switch back to your device. You should see your computer listed under FOUND HOST
947
948
948
949
Finally, TouchOSC is set up. Let’s link it to our game and run our very first playtest. Go back to the programming IDE. Open up `LiveTesting.cpp`. In our default constructor, we will now set up our game to send and receive values over the network. To do this we will need to know which Ip address and port on our device we will send to as well as set up a port on our local computer’s network to receive incoming data. Your computer will have only one IP address but it can send and receive data on thousands of ports. While we aren’t going too deep into ports there, you can think of the IP address like a boat pier. Lots of boats can be docked at a single pier. This is no different. Your ports are your docks and your IP address is your pier. You can think of the data like the people departing and arriving. You’ll need a separate port for each activity in this scenario. If a port isn’t used by your operating system, you can send and receive data there. We are going to use `8000` and `8001`. The final thing to establish is the Address Pattern. It will look like a file path and it will allow us to specify the address pattern match our messages to their right values. Add this code:
949
950
950
-
```
951
+
```cpp
951
952
#include"LiveTesting.h"
952
953
953
954
LiveTesting::LiveTesting(){
@@ -977,7 +978,7 @@ Let’s move on to the next major function we want to write. We need to run an u
977
978
978
979
Each time we make a change on our device, it will send over the updates to our code via Touch OSC. We want to make sure we get all of the incoming messages that are being sent so we will create a simple while loop. We will loop through the whole list of messages that came into our game that frame and match it to the corresponding variable in our game via if statements.
979
980
980
-
```
981
+
```cpp
981
982
while (receiver.hasWaitingMessages()) {
982
983
//get the next message
983
984
ofxOscMessage m;
@@ -987,7 +988,7 @@ Each time we make a change on our device, it will send over the updates to our c
987
988
988
989
Every incoming message will come with its own unique address tag and new arguments. You can get access to a message's address via the getAddress function. For example,`if(m.getAddress() == "/game/max_enemy_amplitude")`, will test to see if the message address is /game/max_enemy_amplitude. If it is, set the variable equal to that value in your game's codebase and they are linked together. Every swipe of the knob will translate to direct changes in your game. We do this for every single value we want to set.
989
990
990
-
```
991
+
```cpp
991
992
if(m.getAddress() == "/game/max_enemy_amplitude")
992
993
{
993
994
max_enemy_amplitude = m.getArgAsFloat(0);
@@ -1009,7 +1010,7 @@ To pack up all of the values in our current running game and send them back to t
1009
1010
1010
1011
Here's the complete code to add to your LiveTesting.cpp file
1011
1012
1012
-
```
1013
+
```cpp
1013
1014
voidLiveTesting::update()
1014
1015
{
1015
1016
//our simple while loop to make sure we get all of our messages
0 commit comments