C++代写:ICS214 Iceman Part2

从头开始实现一款大型的RPG游戏Iceman,第二部分是介绍游戏框架原理。

So how does a video game work?

Fundamentally, a video game is composed of a bunch of objects (i.e., class variables); in Iceman, those objects include the Iceman, Regular Protesters, Hardcore Protesters, Goodies (e.g., Sonar Kits, Gold Nuggets, Water, Barrels of oil) that can be picked up, Boulders, Ice, and Squirts (water squirted by the Iceman at Protesters). Let’s call these objects “actors,” since each object is an actor in our video game. Each actor has its own x, y location in the oil field, its own internal state (e.g., the location of the Protester, what direction the Protester is moving, etc.) and its own special algorithms that control its actions in the game based on its own state and the state of the other objects in the oil field. In the case of the Iceman, the algorithm that controls the Iceman’s actor object is the player’s own brain and hand, and the keyboard! In the case of other actors (e.g., Protesters), each object has an internal autonomous algorithm and state that dictates how the object behaves in the game world.

Once a game begins, gameplay is divided into ticks. A tick is a unit of time, for example, 50 milliseconds (that’s 20 ticks per second). During a given tick, the video game calls upon each object’s behavioral algorithm and asks the object to perform its behavior. When asked to perform its behavior, each object’s behavioral algorithm must decide what to do and then make a change to the object’s state (e.g., move the object 1 square to the left), or change other objects’ states (e.g., when a Protester’s algorithm is called by the game, it may determine that it has moved next to a Iceman, and it may shout at the Iceman, irritating him). Typically the behavior exhibited by an object during a single tick is limited in order to ensure that the gameplay is smooth and that things don’t move too quickly and confuse the player. For example, a Protester will move at most square left/right/up/down during each tick, rather than moving two or more squares; if a Protester were to move 5 squares in a single tick, for example, the user would be confused because humans are used to seeing smooth movement in video games, not jerky shifts.

After the current tick is over and all actors have had a chance to adjust their state (and possibly adjust other actor’s states), our game framework (that we provide) animates the actors onto the screen in their new configuration. So if a Protester changed its location from x=10, y=5 to x=11, y=5 (moved one square right), then our game framework would erase the graphic of the Protester from location 10,5 on the screen and draw the Protester’s graphic at 11,5 instead. Since this process (asking actors to do something, then animating them to the screen) happens some 10-20 times per second, the user will see smooth-ish animation.

Then, the next tick occurs, and each object’s algorithm is again allowed to do something, our framework displays the updated actors on-screen, and so on.

Assuming the ticks are quick enough (a fraction of a second), and the actions performed by the objects are subtle enough (i.e., a Protester doesn’t move 3 inches away from where it was during the last tick, but instead moves a few millimeters away), when you display each of the objects on the screen over time, it looks like each object is performing a continuous series of fluid motions.

A video game can be broken into three different phases:

Initialization

The game World is initialized and prepared for play. This involves allocating one or more the actors (which are C++ objects) and placing them in the game world so that they will appear in the oil field.

Game play

Game play is broken down into a bunch of ticks. During each tick, all of the actors in the game have a chance to do something, and perhaps die. During each tick, new actors may be added to the game and actors who die must be removed from the game world and deleted.

Cleanup

This phase occurs either when the player has lost a life (but has more lives left), the player has completed the current level, or the player has lost all of their lives and the game is over. This phase frees all of the objects in the World (e.g., Regular Protesters, Ice, Boulders, Goodies, the Iceman, etc.) since the level has ended. If gameplay is not over (i.e., the player has more lives), then the game proceeds back to the

Initialization step, where the oil field is repopulated with new occupants and game play restarts at the current level.

Here is what the main logic of a video game looks like, in pseudocode (we provide some similar code for you in our provided GameController.cpp):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
while (The player has lives left)
{
Prompt_the_user_to_start_playing(); // "press a key to start"

Initialize_the_game_world(); // you're going to write this func

while (The player is still alive)
{
// each pass through this loop is a tick (1/20th of a sec)

// you're going to write code to do the following
Ask_all_actors_to_do_something();
If_any_actors_died_then_delete_them_from_the_world();

// we write this code to handle the animation for
you Animate_all_of_the_alive_actors_to_the_screen();
Sleep_for_50ms_to_give_the_user_time_to_react();
}
// the player died - you're going to write this code
Cleanup_all_game_world_objects(); // you're going to write this
if (The player has more lives)
Prompt_the_player_to_continue();
}

Tell_the_user_the_game_is_over(); // "game over!"; we provide this

And here is what the Ask_all_actors_to_do_something() function might look like (which is one of the functions you’ll have to write):

1
2
3
4
5
6
void Ask_all_actors_to_do_something()
{

for each actor on the level:
if (the actor is still alive)
tell the actor to doSomething();
}

You will typically use a container (an array, vector, or list) to hold pointers to each of your live actors. Each actor (a C++ object) has a doSomething( ) method. In this method, each actor (e.g., a Regular Protester) can decide what to do. For example, here is some pseudo code showing what a hypothetical Regular Protester might decide to do each time it gets asked to do something:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class RegularProtester: public ...
{
public:
virtual void doSomething()
{
If I am facing the Iceman and he is next to me, then
Shout at the Iceman (to annoy him)
Else if the Iceman is visible via direct line of sight, then
Switch direction to face the Iceman
Move one square in this direction
Else if I'm about to run into an obstacle, then
Pick a new direction
Move one square in this direction
Else
Move one square in my current direction
}
...
};

And here’s what the Iceman’s doSomething() method might look like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Iceman: public ...
{
public:
virtual void doSomething()
{
Try to get user input (if any is available)
If the user pressed the UP key and that square is open then
Increase my y location by one
If the user pressed the DOWN key and that square is open then
Decrease my y location by one
...
If the user pressed the space bar to fire and the Iceman has water, then
Introduce a new Squirt object into the game facing the same direction as the player
...
}
...
};