1. Trang chủ
  2. » Công Nghệ Thông Tin

Game Programming All in One 2 nd Edition phần 5 pps

74 217 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Tiêu đề Game Programming All in One 2nd Edition phần 5 pps
Trường học University of Software Engineering & Technology
Chuyên ngành Game Programming
Thể loại Sách hướng dẫn
Năm xuất bản 2023
Thành phố Hà Nội
Định dạng
Số trang 74
Dung lượng 518,05 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

An animated sprite, then, is an array of sprites drawn using new properties, such as timing, direction, and velocity.. The AnimSprite program loads these six image files, each containing

Trang 1

explode_bmp = load_bitmap(“explode.bmp”, NULL);

Trang 2

}

//look for a direct hit using basic collision

//tank is either 0 or 1, so negative num = other tank

}

}

///////////////////////////////////////////////////////////////////// // fireweapon function

// set bullet direction and speed and activate it

///////////////////////////////////////////////////////////////////// void fireweapon(int num)

Trang 3

bullet_bmp = load_bitmap(“bullet.bmp”, NULL);

Trang 4

bullets[num].xspd = 0;

bullets[num].yspd = BULLETSPEED;

break;

//SW case 5:

}

The next section of code covers the keyboard input code, including forward,backward,turnleft,

turnright, and getinput These functions are largely the same as before, but they now mustsupport eight directions (evident in the ifstatement within turnleftandturnright)

Trang 6

//WASD - SPACE keys control tank 1

textprintf(screen, font, SCREEN_W-70*(player+1), 1,

BURST, “P%d: %d”, player+1, points);

}

Thesetuptanks function has changed dramatically from the last version because that iswhere the new tank bitmaps are loaded Since this game uses the rotate_spritefunction togenerate the sprite images for all eight directions, this function takes care of that by firstcreating each image and then blitting the source tank image into each new image with aspecified rotation angle The end result is two tanks fully rotated in eight directions

/////////////////////////////////////////////////////////////////////

// setuptanks function

// load tank bitmaps and position the tank

/////////////////////////////////////////////////////////////////////

Trang 7

//load first tank bitmap

tank_bmp[0][0] = load_bitmap(“tank1.bmp”, NULL);

//rotate image to generate all 8 directions

//load second tank bitmap

tank_bmp[1][0] = load_bitmap(“tank2.bmp”, NULL);

//rotate image to generate all 8 directions

Trang 8

The next section of the code includes the setupscreen function The most importantchange to this function is the inclusion of a single line calling set_color_depth(32), whichcauses the game to run in 32-bit color mode Note that if you don’t have a 32-bit videocard, you might want to change this to 16 (which will still work).

textprintf(screen, font, 1, 1, BURST,

“Tank War - %dx%d”, SCREEN_W, SCREEN_H);

//draw screen border

rect(screen, 0, 12, SCREEN_W-1, SCREEN_H-1, TAN);

rect(screen, 1, 13, SCREEN_W-2, SCREEN_H-2, TAN);

}

Finally, the last section of code in the third enhancement to Tank War includes the

all-important mainfunction Several changes have been made in main, notably the removal ofthe calls to clearpath(which checked for bullet hits by looking directly at pixel color) Thecall to rest now has a value of 10 to speed up the game a bit in order to have smootherbullet trajectories There is also a line of code that displays the direction of each tank, as Iexplained previously

Trang 9

textprintf(screen, font, 0, SCREEN_H-10, WHITE,

“DIRS %d , %d”, tanks[0].dir, tanks[1].dir);

//erase the tanks

Trang 10

This marks the end of perhaps the most interesting chapter so far, at least in my opinion.The introduction to sprites that you have received in this chapter provided the basicswithout delving too deeply into sprite programming theory The next chapter covers someadvanced sprite programming topics, including the sorely needed collision detection

I will also get into sprite animation in the next chapter There are many more changes on

the way for Tank War as well The next several chapters will provide a huge amount of new functionality that you can use to greatly enhance Tank War, making it into a truly top-

notch game with a scrolling background, animated tanks, a radar screen, and many morenew features!

Chapter Quiz

You can find the answers to this chapter quiz in Appendix A, “Chapter Quiz Answers.”

1 What is the term given to a small image that is moved around on the screen?

Trang 11

5 Which function draws a vertically-flipped sprite?

Trang 13

If Chapter 7 provided the foundation for developing bitmap-based games, then

Chapter 8 provided the frame, walls, plumbing, and wiring (House analogies are frequently used to describe software development, so they may be used to describe

game programming as well.) Therefore, what you need from this chapter are the sheetrock,

finishing, painting, stucco, roof tiles, appliances, and all the cosmetic accessories that

com-plete a new house—yes, including the kitchen sink

The other sections of this chapter (on RLE sprites, compiled sprites, and collision

detec-tion) are helpful, but might be considered the Italian tile of floors, whereas linoleum will

work fine for most people But the segment on animated sprites is absolutely crucial in

your quest to master the subject of 2D game programming So what is an animated sprite?

You already learned a great deal about sprites in the last chapter, and you have at your

dis-posal a good tool set for loading and blitting sprites (which are just based on common

bitmaps) An animated sprite, then, is an array of sprites drawn using new properties, such

as timing, direction, and velocity

Here is a breakdown of the major topics in this chapter:

I Working with animated sprites

I Using run-length encoded sprites

I Working with compiled sprites

I Understanding collision detection

Trang 14

Animated Sprites

The sprites you have seen thus far were handled somewhat haphazardly, in that no realstructure was available for keeping track of these sprites They have simply been loadedusingload_bitmapand then drawn using draw_sprite, with little else in the way of control

or handling To really be able to work with animated sprites in a highly complex game

(such as a high-speed scrolling shooter like R-Type or Mars Matrix), you need a

frame-work for drawing, erasing, and moving these sprites, and for detecting collisions For all

of its abstraction, Allegro leaves this entirely up to you—and for good reason No singleperson can foresee the needs of another game programmer because every game has aunique set of requirements (more or less) Limiting another programmer (who may be farmore talented than you) to using your concept of a sprite handler only encourages thatperson to ignore your handler and write his own That is exactly why Allegro has no spritehandler; rather, it simply has a great set of low-level sprite routines, the likes of which youhave already seen

What should you do next, then? The real challenge is not designing a handler for workingwith animated sprites; rather, it is designing a game that will need these animated sprites,and then writing the code to fulfill the needs of the game In this case, the game I am tar-

geting for the sprite handler is Tank War, which you have improved in each new chapter— and this one will be no exception In Chapter 8, you modified Tank War extensively to

convert it from a vector- and bitmap-based game into a sprite-based game, losing somegameplay along the way (The battlefield obstacles were removed.) At the end of this chapter,you’ll add the sprite handler and collision detection—finally!

Drawing an Animated Sprite

To get started, you need a simple example followed by an explanation of how it works Ihave written a quick little program that loads six images (of an animated cat) and drawsthem on the screen The cat runs across the screen from left to right, using the spriteframes shown in Figure 9.1

The AnimSprite program loads these six image files, each containing a single frame of the

animated cat, and draws them in sequence, one frame after another, as the sprite is movedacross the screen (see Figure 9.2)

Figure 9.1 The animated cat sprite, courtesy of Ari Feldman

Trang 15

#include <conio.h>

#include <stdlib.h>

#include <stdio.h>

#include “allegro.h”

#define WHITE makecol(255,255,255)

#define BLACK makecol(0,0,0)

Trang 16

textout(screen, font, “AnimSprite Program (ESC to quit)”,

framecount = 0;

curframe++;

if (curframe > 5) curframe = 0;

} acquire_screen();

//draw the sprite draw_sprite(screen, kitty[curframe], x, y);

//display logistics textprintf(screen, font, 0, 20, WHITE,

“Sprite X,Y: %3d,%3d”, x, y);

textprintf(screen, font, 0, 40, WHITE,

“Frame,Count,Delay: %2d,%2d,%2d”, curframe, framecount, framedelay);

release_screen();

Trang 17

Now for that explanation, as promised The difference between AnimSprite and DrawSprite

(from the previous chapter) is multifaceted The key variables,curframe,framecount, and

framedelay, make realistic animation possible You don’t want to simply change the frame

every time through the loop, or the animation will be too fast The frame delay is a static

value that really needs to be adjusted depending on the speed of your computer (at least

until I cover timers in Chapter 11, “Timers, Interrupt Handlers, and Multi-Threading”)

The frame counter, then, works with the frame delay to increment the current frame of

the sprite The actual movement of the sprite is a simple horizontal motion using the x

A really well thought-out sprite handler will have variables for both the velocity (x, y) and

velocity (x speed, y speed), along with a velocity delay to allow some sprites to move quite

slowly compared to others If there is no velocity delay, each sprite will move at least one

pixel during each iteration of the game loop (unless velocity is zero, which means that

This concept is something I’ll explain shortly

Creating a Sprite Handler

Now that you have a basic—if a bit rushed—concept of sprite animation, I’d like to walk

you through the creation of a sprite handler and a sample program with which to test it

Now you’ll take the animation code from the last few pages and encapsulate it into a

struct If you were using the object-oriented C++ language instead of C, you’d no doubt

Trang 18

“class it.” That’s all well and good, but I don’t care what C++ programmers claim—it’smore difficult to understand, which is the key reason why this book focuses on C ThatAllegro itself is written in C only supports this decision The actual bitmap images for thesprite are stored separately from the sprite struct because it is more flexible that way.

In addition to those few animation variables seen in AnimSprite, a full-blown animated

sprite handler needs to track several more variables Here is the struct:

typedef struct SPRITE

to the size of the sprite image(stored outside the struct) Thevelocity elements (xspeed,yspeed)determine how many pixels thesprite will move in conjunctionwith the velocity delay (xdelay,

xcount and ydelay, ycount) Thevelocity delay allows somesprites to move much slowerthan other sprites on thescreen—even more slowly thanone pixel per frame This givesyou a far greater degree of control over how a sprite behaves The animation elements(curframe,maxframe,animdir) help the sprite animation, and the animation delay elements(framecount,framedelay) help slow down the animation rate The animdirelement is of par-ticular interest because it allows you to reverse the direction that the sprite frames aredrawn (from 0 to maxframeor from maxframeto 0, with looping in either direction) The mainreason why the BITMAParray containing the sprite images is not stored inside the struct isbecause that is wasteful—there might be many sprites sharing the same animation images

Figure 9.3 The SPRITEstruct and its elements help abstract

sprite movement into reusable code

Trang 19

Now that we have a sprite struct, the actual handler is contained in a function that I will

As you can see,updatespriteaccepts a pointer to a SPRITEvariable A pointer is necessary

because elements of the struct are updated inside this function This function would be called

at every iteration through the game loop because the sprite elements should be closely tied

to the game loop and timing of the game The delay elements in particular should rely

upon regular updates using a timed game loop The animation section checks animdirto

increment or decrement the framecountelement

Trang 20

However,updatespritewas not designed to affect sprite behavior, only to manage the tics of sprite movement After updatesprite has been called, you want to deal with thatsprite’s behavior within the game For instance, if you are writing a space-based shooterfeaturing a spaceship and objects (such as asteroids) that the ship must shoot, then youmight assign a simple warping behavior to the asteroids so that when they exit one side ofthe screen, they will appear at the opposite side Or, in a more realistic game featuring alarger scrolling background, the asteroids might warp or bounce at the edges of the uni-verse rather than just the screen In that case, you would call updatesprite followed byanother function that affects the behavior of all asteroids in the game and rely on custom

logis-or random values flogis-or each asteroid’s struct elements to cause it to behave slightly differentlythan the other asteroids, but basically follow the same behavioral rules Too many pro-grammers ignore the concept of behavior and simply hard-code behaviors into a game

I love the idea of constructing many behavior functions, and then using them in a game

at random times to keep the player guessing what will happen next For instance, a simplebehavior that I often use in example programs is to have a sprite bounce off the edges ofthe screen This could be abstracted into a bounce behavior if you go that one extra step

in thinking and design it as a reusable function

One thing that must be obvious when you are working with a real sprite handler is that itseems to have a lot of overhead, in that the struct elements must all be set at startup.There’s no getting around that unless you want total chaos instead of a working game! Youhave to give all your sprites their starting values to make the game function as planned.Stuffing those variables into a struct helps to keep the game manageable when the sourcecode starts to grow out of control (which frequently happens when you have a truly greatgame idea and you follow through with building it)

The SpriteHandler Program

I have written a program called SpriteHandler that demonstrates how to put all this

together into a workable program that you can study This program uses a ball sprite with

16 frames of animation, each stored in a file (ball1.bmp, ball2.bmp, and so on to ball16.bmp).One thing that you must do is learn how to store an animation sequence inside a singlebitmap image and grab the frames out of it at run time I’ll show you how to do that shortly

Figure 9.4 shows the SpriteHandler program running Each time the ball hits the edge, it

changes direction and speed

#include <conio.h>

#include <stdlib.h>

#include <stdio.h>

#include “allegro.h”

#define BLACK makecol(0,0,0)

#define WHITE makecol(255,255,255)

Trang 21

//define the sprite structure

typedef struct SPRITE

Figure 9.4 The SpriteHandler program demonstrates a full-featured

animated sprite handler

Trang 22

void erasesprite(BITMAP *dest, SPRITE *spr)

{

//erase the sprite using BLACK color fill

rectfill(dest, spr->x, spr->y, spr->x + spr->width, spr->y + spr->height, BLACK);

if (—spr->curframe < 0) spr->curframe = spr->maxframe;

} else if (spr->animdir == 1) {

if (++spr->curframe > spr->maxframe) spr->curframe = 0;

} }

}

void bouncesprite(SPRITE *spr)

{

Trang 23

//simple screen bouncing behavior

Trang 24

//load sprite images

//initialize the sprite with lots of randomness

ball->x = rand() % (SCREEN_W - ballimg[0]->w);

ball->y = rand() % (SCREEN_H - ballimg[0]->h);

textprintf(screen, font, 0, 20, WHITE,

“x,y,xspeed,yspeed: %2d,%2d,%2d,%2d”,

Trang 25

ball->x, ball->y, ball->xspeed, ball->yspeed);

textprintf(screen, font, 0, 30, WHITE,

Grabbing Sprite Frames from an Image

In case you haven’t yet noticed, the idea behind the sprite handler that you’re building in

this chapter is not to encapsulate Allegro’s already excellent sprite functions (which were

covered in the previous chapter) The temptation of nearly every C++ programmer would

be to drool in anticipation over encapsulating Allegro into a series of classes What a

shame and what a waste of time! I can understand classing up an operating system

ser-vice, which is vague and obscure, to make it easier to use In my opinion, a class should be

used to simplify very complex code, not to make simple code more complex just to satisfy

an obsessive-compulsive need to do so

On the contrary, you want to use the existing functionality of Allegro, not replace it with

something else By “something else” I mean not necessarily better, just different The

wrapping of one thing and turning it into another thing should arise out of use, not

com-pulsion Add new functions (or in the case of C++, new classes, properties, and methods)

as you need them, not from some grandiose scheme of designing a library before using it

Thus, you have a basic sprite handler and now you need a function to grab an animation

sequence out of a tiled image So you can get an idea of what I’m talking about, Figure 9.5

shows a 32-frame tiled animation sequence in a file called sphere.bmp

Figure 9.5 This bitmap image contains

32 frames of an animated sphere used as

a sprite Courtesy of Edgar Ibarra

Trang 26

The frames would be easy to capture if they were lined up in a single row, so how wouldyou grab them out of this file with eight columns and four rows? It’s easy if you have thesprite tile algorithm I’m sure someone described this in some mathematics or computergraphics book at one time or another in the past; I derived it on my own years ago I sug-gest you print this simple algorithm in a giant font and paste it on the wall above yourcomputer—or better yet, have a T-shirt made with it pasted across the front.

int x = startx + (frame % columns) * width;

int y = starty + (frame / columns) * height;

Using this algorithm, you can grab an animation sequence that is stored in a bitmap file,even if it contains more than one animation (For instance, some simpler games mightstore all the images in a single bitmap file and grab each sprite at run time.) Now that youhave the basic algorithm, here’s a full function for grabbing a single frame out of an image

by passing the width, height, column, and frame number:

BITMAP *grabframe(BITMAP *source,

int width, int height, int startx, int starty, int columns, int frame) {

BITMAP *temp = create_bitmap(width,height);

int x = startx + (frame % columns) * width;

int y = starty + (frame / columns) * height;

n o t e

The grabframefunction really should have some error detection code built in, such as a check forwhether the bitmap is NULL after blitting it As a matter of fact, all the code in this book is inten-tionally simplistic—with no error detection code—to make it easier to study In an actual game,you would absolutely want to add checks in your code

Trang 27

The SpriteGrabber Program

The SpriteGrabber program demonstrates how to use grabframe by modifying the

SpriteHandler program and using a more impressive animated sprite that was rendered

(courtesy of Edgar Ibarra) See Figure 9.6 for a glimpse of the program

I’m going to list the entire source code for SpriteGrabber and set in boldface the lines that

have changed (or been added) so you can note the differences I believe it would be too

confusing to list only the changes to the program There is a significant learning experience

to be had by observing the changes or improvements to a program from one revision to

#define BLACK makecol(0,0,0)

#define WHITE makecol(255,255,255)

Figure 9.6 The SpriteGrabber program demonstrates how to grab sprite

images (or animation frames) from a tiled source image

Trang 28

//define the sprite structure

typedef struct SPRITE

//erase the sprite using BLACK color fill

rectfill(dest, spr->x, spr->y, spr->x + spr->width, spr->y + spr->height, BLACK);

Trang 29

//update frame based on animdir

Trang 30

spr->y = SCREEN_H - spr->height;

spr->yspeed = rand() % 2 - 6;

spr->animdir *= -1;

}

}

BITMAP *grabframe(BITMAP *source,

int width, int height, int startx, int starty, int columns, int frame) {

BITMAP *temp = create_bitmap(width,height);

int x = startx + (frame % columns) * width;

int y = starty + (frame / columns) * height;

install_timer();

srand(time(NULL));

textout(screen, font, “SpriteGrabber Program (ESC to quit)”,

0, 0, WHITE);

//load 32-frame tiled sprite image

temp = load_bitmap(“sphere.bmp”, NULL);

Trang 31

//initialize the sprite with lots of randomness

ball->x = rand() % (SCREEN_W - ballimg[0]->w);

ball->y = rand() % (SCREEN_H - ballimg[0]->h);

//draw the ball sprite

draw_sprite(screen, ballimg[ball->curframe], ball->x, ball->y);

//display some logistics

textprintf(screen, font, 0, 20, WHITE,

“x,y,xspeed,yspeed: %2d,%2d,%2d,%2d”,

ball->x, ball->y, ball->xspeed, ball->yspeed);

textprintf(screen, font, 0, 30, WHITE,

“xcount,ycount,framecount,animdir: %2d,%2d,%2d,%2d”,

ball->xcount, ball->ycount, ball->framecount, ball->animdir);

//unlock the screen

Trang 32

The Next Step: Multiple Animated Sprites

You might think of a single sprite as a single-dimensional point in space (thinking interms of geometry) An animated sprite containing multiple images for a single sprite is atwo-dimensional entity The next step, creating multiple copies of the sprite, might becompared to the third dimension So far you have only dealt with and explored the con-cepts around a single sprite being drawn on the screen either with a static image or with

an animation sequence But how many games feature only a single sprite? It is really a test

of the sprite handler to see how well it performs when it must contend with many sprites

at the same time

Because performance will be a huge issue with multiple sprites, I will use a double-buffer

in the upcoming program for a nice, clean screen without flicker I will add another level

of complexity to make this even more interesting—dealing with a bitmapped backgroundimage instead of a blank background.rectfillwill no longer suffice to erase the spritesduring each refresh; instead, the background will have to be restored under the sprites asthey move around

Instead of a single sprite struct there is an array of sprite structs, and the code throughoutthe program has been modified to use the array To initialize all of these sprites, you need

to use a loop and make sure each pointer is pointing to each of the sprite structs

//initialize the sprite

for (n=0; n<MAX; n++)

{

sprites[n] = &thesprites[n];

sprites[n]->x = rand() % (SCREEN_W - spriteimg[0]->w);

sprites[n]->y = rand() % (SCREEN_H - spriteimg[0]->h);

sprites[n]->width = spriteimg[0]->w;

sprites[n]->height = spriteimg[0]->h;

sprites[n]->xdelay = rand() % 3 + 1;

Trang 33

This time I’m using a much larger animation sequence containing 64 frames, as shown in

Figure 9.7 The source frames are laid out in an 8×8 grid of tiles

To load these frames into the sprite handler, a loop is used to grab each frame individually

//load 64-frame tiled sprite image

temp = load_bitmap(“asteroid.bmp”, NULL);

Figure 9.7 The source image for the animated

asteroid contains 64 frames

Trang 34

The MultipleSprites Program

The MultipleSprites program animates 100 sprites on the screen, each of which has 64

frames of animation! Had this program tried to store the actual images with every singlesprite instead of sharing the sprite images, it would have taken a huge amount of systemmemory to run—so now you see the wisdom in storing the images separately from the

structs Figure 9.8 shows the MultipleSprites program running at 1024×768 This program differs from SpriteGrabber because it uses a screen warp rather than a screen bounce

behavior

This program uses a secondbuffer to improve perfor-mance Could you imaginethe speed hit after erasingand drawing 100 spritesdirectly on the screen? Evenlocking and unlocking thescreen wouldn’t help muchwith so many writes takingplace on the screen That iswhy this program uses dou-ble-buffering—so all blit-ting is done on the secondbuffer, which is then quicklyblitted to the screen with asingle function call

//update the screen

acquire_screen();

blit(buffer,screen,0,0,0,0,buffer->w,buffer->h);

release_screen();

The game loop in MultipleSprites might look inefficient at first glance because there are

four identical for loops for each operation—erasing, updating, warping, and drawingeach of the sprites

//erase the sprites

Trang 35

It might seem more logical to use a single forloop with these functions inside that loop

instead, right? Unfortunately, that is not the best way to handle sprites First, all of the

sprites must be erased before anything else happens Second, all of the sprites must be

moved before any are drawn or erased Finally, all of the sprites must be drawn at the same

time, or else artifacts will be left on the screen Had I simply blasted the entire background

onto the buffer to erase the sprites, this would have been a moot point The program

might even run faster than erasing 100 sprites individually However, this is a learning

experience It’s not always practical to clear the entire background, and this is just a

demonstration—you won’t likely have 100 sprites on the screen at once unless you are

building a very complex scrolling arcade shooter or strategy game

Following is the complete listing for the MultipleSprites program If you are typing in the

code directly from the book, you will want to grab the asteroids.bmp and ngc604.bmp

files from the CD-ROM (They are located in \chapter09\multiplesprites.)

#include <conio.h>

#include <stdlib.h>

#include <stdio.h>

#include “allegro.h”

#define BLACK makecol(0,0,0)

#define WHITE makecol(255,255,255)

#define MAX 100

#define WIDTH 640

#define HEIGHT 480

#define MODE GFX_AUTODETECT_WINDOWED

//define the sprite structure

typedef struct SPRITE

{

int x,y;

int width,height;

Trang 36

//erase the sprite

blit(back, dest, spr->x, spr->y, spr->x, spr->y, spr->width, spr->height);

Trang 37

BITMAP *grabframe(BITMAP *source,

int width, int height, int startx, int starty, int columns, int frame) {

BITMAP *temp = create_bitmap(width,height);

Ngày đăng: 12/08/2014, 19:20

TỪ KHÓA LIÊN QUAN