Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » Looking for a Game Tutorial

This thread is locked; no one can reply to it. rss feed Print
 1   2   3   4 
Looking for a Game Tutorial
jhetfield21
Member #12,379
November 2010

i hadn't thought of it like that....interesting :).

bamccaig
Member #7,536
July 2006
avatar

The very slight overhead with virtual methods is because the program has to lookup the function in a vtable at run-time (every time the method is called using a pointer or reference). It's this very mechanism that allows inheritance and polymorphism. Nearly every language that supports these concepts will implement it in a similar way. It's nothing to be concerned about. It will almost never be a bottleneck in your code.

@J-Gamer: /me is writing an example program. It's nearly 500 lines and counting, but that initial overhead should be outweighed by the code reuse later on (in a real game, when there's many different types of objects to be drawn). I think all I should have to do now is A5 stuff (initializing A5, loading some bitmaps, etc.). Well, and then debug it. :-X

Audric
Member #907
January 2001

J-Gamer said:

a spaceship needs multiple bitmap vectors/arrays to store different animation sequences.

Hmmmm be careful that "each instance of a spaceship" doesn't need it.

For small games, you should load such bitmaps once, on startup, and release them once, when quitting program.

Putting them as static members of each game object's class isn't an universal solution either : It prevents you from having several game elements that "share" the same image: For example a "computer-controlled allied ship" that needs the same sprites as the player ship.

Dario ff
Member #10,065
August 2008
avatar

I think the Speed game included in the latest Allegro 5 releases demonstrates well a situation where you render the same objects in different ways:

video

Having draw methods for each of these different camera styles for each object would be complicated, while different renderers could do this just by having different attributes(Screen position and size as well).

While the Speed game is pure C, it uses different "view" structures for rendering the same game objects.

TranslatorHack 2010, a human translation chain in a.cc.
My games: [GiftCraft] - [Blocky Rhythm[SH2011]] - [Elven Revolution] - [Dune Smasher!]

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

void cObject::Draw(cObject *Object, cCamera *Camera) {...}

You don't want to include a parameter for the cObject in cObject::Draw because it is already there in the hidden 'this' parameter.

Gabriel Campos said:

class cPlayer : public cObject{
...
void Draw();
...
};

Note that cPlayer::Draw() is not derived from your virtual cObject::Draw(cObject* Object , cCamera* Camera) function. They have two different function signatures. You're overloading the name, but not the function. The function signature of your Draw function in CPlayer should match the function signature of Draw in the cObject base class :

class Base {
...
   virtual void Function(int x);
}

class Derived : public Base {
...
   void Function(int x);// correct, function signatures match
   void Function();// incorrect, function signatures are different
}

You might want to take a look at the cplusplus.com tutorial, or the section on polymorphism.

bamccaig
Member #7,536
July 2006
avatar

I kept it all in one file to keep it simple on the forum, but I would put each class in its own header and source file for real. The actual main program stuff is hacked in and hard coded, but the polymorphic API is what matters anyway. :) Sadly, I have noticed one annoying shortcoming of my particular implementation. The player has dynamic dimensions. In order to retrieve them you need to query the current animation for the current frame, which requires the current animation to be set and started, and therefore requires game time as well. It's a little bit heavy doing all of that to determine the width and height, but one could always code static dimensions if they needed it to be cheaper/easier. The only reason I really needed it was to center the "player" on the screen for aesthetic reasons. I was able to get it done with prior knowledge anyway.

The code is pretty long. I counted 745 lines using wc. It's obviously a lot of overhead for what little it accomplishes in this simple example, but the code is reusable now. You can create n different types of objects that follow these same interfaces. It should save a lot of code, though you may find the interfaces need revision to deal with cases that I haven't accounted for.

The program mostly just dumps errors and exits if anything goes wrong, but it doesn't have to. It's built using RAII types and exception handling, so in theory it should be easy to handle errors gracefully (as can be seen in the drawing section of the main loop). I will say this: it's a lot easier to write this sort of code using Allegro 5 than it was with Allegro 4. Intern's Quest[1] wasn't designed as well as this from the start (though I learned a lot from it that lead me to here). That aside, it was still a lot more difficult to write because of all of the global state in Allegro 4 that we had to try to wrap and work around. With Allegro 5 there's less global state to deal with, and the global state that you are stuck with is already neatly hidden beneath interfaces. If I wore hats I would take one of them off to honor the developers! :P

Teh Codez

(A.cc seems to be wrongly counting my post as 90 KB long, when in fact it's only 19 KB long, so I'm forced to attach the code and link to it instead of inlining it, which would have been a much more convenient way to read it IMHO, but I digress)

           (see below)

that --> al5polytut.tar.gz <-- that

            ^ this

The following QnD GNU Makefile was used during the development of this demonstration and could be used to build it given a compatible platform.

Makefile#SelectExpand
1al5polytut: main.cpp 2 g++ -o $@ -Wall main.cpp `pkg-config --cflags --libs allegro-5.1`

If you're on an incompatible platform then you'll just need to know how to compile a single C++ source file and link to the core of Allegro 5. :)

Meh. This is the way that I like to code. It's more fun for me, but then I've never finished a game... :P I also find it a lot easier to work with though. It's a lot less error prone because you're writing once instead of writing over and over again.

The code could still use revision, but I think it's a pretty good start. If anybody actually finds it useful enough to use directly in a game then I would appreciate a mention in any credits or whatever, but I'm not going to demand it. :)

Update: moving camera now so you can see it working.

References

  1. The project is stalled, but planned to be one day resurrected. It has also been moved to GitHub.
Gabriel Campos
Member #12,034
June 2010
avatar

You don't want to include a parameter for the cObject in cObject::Draw because it is already there in the hidden 'this' parameter.

So, if a I understand i can do this

#SelectExpand
1class cObject{ 2 private: 3 int x, y, W, H; // all objects will inherit these variables 4 5 public: 6 virtual void Draw(cCamera *Camera){ 7 masked_blit(this->sprite, Buffer, this->frame*this->W, this->stance*this->H, this->x, this->y, this->W, this->H); 8} 9};

Sorry if I am wrong...I am studying and trying to learn...is a bit dificulty ::)
Thanx for the advice of the virtual function...so it must be exactlly equal to the derived classes functions...and thanx to the site..very good...
I am working now >:(, in home I will read with attention . ..

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

So, if a I understand i can do this

When writing a class method, you don't need to use the this pointer to refer to data members, but you can if it helps you keep your code straight. You probably don't want to hardcode Buffer into your classes either, but use a parameter for the bitmap to draw to.

#SelectExpand
1 2class cObject{ 3private: 4 BITMAP* sprite; 5 int x, y, W, H; // all derived classes will inherit these variables 6 7public: 8 virtual void Draw(BITMAP* dest , cCamera *Camera) { 9 masked_blit(sprite , dest , 0 , 0 , x - Camera->x , y - Camera->y , W , H); 10 } 11 12}; 13 14enum STANCES { 15 CROUCH_LEFT = 0, 16 CROUCH_RIGHT = 1, 17 STAND_LEFT = 2, 18 STAND_RIGHT = 3 19}; 20 21#define NUM_STANCES 4 22#define NUM_FRAMES 4 23 24class Player : public cObject { 25private : 26 BITMAP* frames[NUM_STANCES][NUM_FRAMES]; 27 STANCES stance; 28 int frame_num; 29 30public : 31 void SetStance(STANCES new_stance) { 32 stance = new_stance; 33 frame_num = 0; 34 sprite = frames[stance][frame_num]; 35 } 36 // No need to redefine cObject::Draw here, but you can if you like : 37 virtual void Draw(BITMAP* dest , cCamera* Camera) { 38 cObject::Draw(dest , Camera);// call the base class method until we decide otherwise 39 } 40};

J-Gamer
Member #12,491
January 2011
avatar

Isn't it also better to have the x and y variables of cCamera private? This to prevent accidental changes of these values. Just have a getX() and getY() function in the class.

With accidental changes, I mean like looking over something like this:
if(camera.y=0)
instead of
if(camera.y==0)
If they're private, the compiler will throw an error here... If not, each time this if-statement is called, the code underneath will execute and camera.y will be set to 0.

I had this problem a lot when learning C++ after I had been programming in a BASIC language for a long time.

" There are plenty of wonderful ideas in The Bible, but God isn't one of them." - Derezo
"If your body was a business, thought would be like micro-management and emotions would be like macro-management. If you primarily live your life with emotions, then you are prone to error on the details. If you over-think things all the time you tend to lose scope of priorities." - Mark Oates

Thomas Fjellstrom
Member #476
June 2000
avatar

bamccaig said:

(A.cc seems to be wrongly counting my post as 90 KB long, when in fact it's only 19 KB long, so I'm forced to attach the code and link to it instead of inlining it, which would have been a much more convenient way to read it IMHO, but I digress)

It counts the actual size of the post-processed contents, after its done transforming bb-code and <code> tags, and highlighting code causes the size to bloat up significantly.

--
Thomas Fjellstrom - [website] - [email] - [Allegro Wiki] - [Allegro TODO]
"If you can't think of a better solution, don't try to make a better solution." -- weapon_S
"The less evidence we have for what we believe is certain, the more violently we defend beliefs against those who don't agree" -- https://twitter.com/neiltyson/status/592870205409353730

Audric
Member #907
January 2001

J-Gamer: use the -Wall option of gcc to get warnings on all suspicious code, and it will warn you on each "if (x=y)".

Gabriel Campos
Member #12,034
June 2010
avatar

My Solution:

#SelectExpand
1class cObject{ // this class will handle all objects... 2 3 public: 4 virtual void GetX(); // Dont need to comment... 5 virtual void GetY(); 6 virtual void GetW(); 7 virtual void GetH(); 8 virtual void GetImage(); 9 // and other virtual functions used to draw 10}; 11 12............................................................................... 13 14class cDrawManager(){ 15 16 private: 17 BITMAP *Buffer; // Edgar Reynaldo said to not use buffer here, but I couldn't understand why..someone give me a light 18 BITMAP *Sprite[x]; // x will be the number of all sprites in the game 19 20 public: 21 void Draw(cObject *Object, cCamera *Camera){ 22 masked_blit(Sprite[Object->GetImage()], Buffer, Object->GetFrame() * Object->GetW(); Object->GetStance() * Object->GetH(); Object->GetX() - Camera->GetX(); Object->GetY() - Camera->GetY(); Object->GetW(), Object->GetH()); 23 } 24 25 void DrawScreen(){ 26 draw_sprite(screen, Buffer, 0, 0); 27 } 28}; 29 30................................................................................. 31 32class cPlayer : public cObject{ 33 34 private: 35 int x, y, W, H, Image; // and all variables (I can put in the cObject too) 36 37 public: 38 void GetX(); // All functions derived from virtual function of cObject class... 39 void GetY(); 40 void GetW(); 41 void GetH(); 42 void GetImage(); 43}; // Dont need make any draw function in the player 44// All objects will follow this recipe.. 45 46.................................................................................. 47 48int main(){ 49 cDrawManager DrawManager; 50 cPlayer Player; 51 cCamera Camera; 52 53while(...){ 54 DrawManager.Draw(&Player, &Camera); 55 DrawManager.DrawScreen(); // if there is some problem with buffer in the DrawManager class I can send from here as a parameter (DrawScreen(Buffer); :) 56}

This is the solution I liked...Suggestions will be very apreciate..
But I still have doubts...
I create a vector and doesnt work... :P

#SelectExpand
1int main() 2#include <vector> 3 4 std::vector<cDrawManager *> SpriteObjects; 5 6 for(int i = 0; i < SpriteObjects.length(); i++){ 7 DrawManager.Draw(SpriteObjects[i], &Camera); 8 }

The compiler says that vector SpriteObjects has no member named length();
If I use .size(), .capacity(), it accepts....but not length()..
I use allegro 4.2 and DevC++...
Someone??? ???

gnolam
Member #2,030
March 2002
avatar

The compiler says that vector SpriteObjects has no member named length();

That's because std::vector doesn't have a length() member function. The one you're after is size(). :P

--
Move to the Democratic People's Republic of Vivendi Universal (formerly known as Sweden) - officially democracy- and privacy-free since 2008-06-18!

Gabriel Campos
Member #12,034
June 2010
avatar

Ok..but when a put size it doesnt draw anything on screen...
Sorry if I am making a topic too long...
What is the problem with my logic??

bamccaig
Member #7,536
July 2006
avatar

You're either showing us parts of your program or you're forgetting to actually initialize your objects and draw to the screen. :P If you're still having issues, show us the full program. :)

gnolam
Member #2,030
March 2002
avatar

Also: the code you're giving us shouldn't even compile...

Quote:
masked_blit(Sprite[Object->GetImage()], Buffer, Object->GetFrame() * Object->GetW(); Object->GetStance() * Object->GetH(); Object->GetX() - Camera->GetX(); Object->GetY() - Camera->GetY(); Object->GetW(), Object->GetH());

--
Move to the Democratic People's Republic of Vivendi Universal (formerly known as Sweden) - officially democracy- and privacy-free since 2008-06-18!

Gabriel Campos
Member #12,034
June 2010
avatar

gnolam said:

Also: the code you're giving us shouldn't even compile...

Why?? I couldnt realize . . .
I make all functions of get() virtual....

well,
I will try later in home...If doesnt work i will post here . .

J-Gamer
Member #12,491
January 2011
avatar

J-Gamer: use the -Wall option of gcc to get warnings on all suspicious code, and it will warn you on each "if (x=y)".

I didn't know that... thanks :)
But I think it's still better to make them private, just for OOP's sake ;)

BITMAP *Sprite[x]; // x will be the number of all sprites in the game

I hope you don't think this is a dynamic array... You need to replace the x by a hard-coded number, OR you should make it a vector/list/deque/whatever to let it be able to have a costumable size.

Quote:

Sprite[Object->GetImage()]

What is this supposed to do? In this case, Object->GetImage should return the index in the Sprite array of the current sprite that should be drawn... This makes code from the object to change when the order of the sprites in the Sprite array changes...

" There are plenty of wonderful ideas in The Bible, but God isn't one of them." - Derezo
"If your body was a business, thought would be like micro-management and emotions would be like macro-management. If you primarily live your life with emotions, then you are prone to error on the details. If you over-think things all the time you tend to lose scope of priorities." - Mark Oates

Gabriel Campos
Member #12,034
June 2010
avatar

When I wrote BITMAP *Sprite[x], x will be the number of all sprites..Lets supose that we will use 10 enemies + 1 player...will be 11 sprites, so

#SelectExpand
1BITMAP *Sprite[11]; 2//then load all bitmaps 3Sprite[0] = load_bitmap(.......); 4... 5... 6...

So when I create a object...lets supose...Player I will refer his sprite with the number of this array...
if Sprite[0] = load_bitmap("Data\\Gfx\\Player.bmp", NULL);
then the variable Image will be 0....
when the class call Sprite[Object->GetImage()], it will return the number of the image, in this case 0..

gnolam said:

Also: the code you're giving us shouldn't even compile...

Maybe because I wrote
virtual VOID GetX()???
I think it is
virtual INT GetX() . . . Always do it...My compiler is working hard you know

J-Gamer
Member #12,491
January 2011
avatar

When compiling, the compiler wants to know how much memory it should allocate for each instance of a class... this means that when using an array, it needs to know the size on beforehand. This way, you can't do this:

BITMAP *Sprite[11];
//then load all bitmaps
Sprite[0] = load_bitmap(.......);
...
...
...

In a constructor/initialisation function because the amount of bitmaps in the array sprite is set to x, for which the compiler will throw an error if it can't find an int called x that is already been given a value(it has to be global to do this, somebody please correct me if I'm wrong).

when the class call Sprite[Object->GetImage()], it will return the number of the image, in this case 0..

This means that the Object needs to know which location the image to be drawn has in the Sprite array(which is in another class)...

" There are plenty of wonderful ideas in The Bible, but God isn't one of them." - Derezo
"If your body was a business, thought would be like micro-management and emotions would be like macro-management. If you primarily live your life with emotions, then you are prone to error on the details. If you over-think things all the time you tend to lose scope of priorities." - Mark Oates

Dario ff
Member #10,065
August 2008
avatar

J-Gamer: Just use the <quote> </quote> tags without specifying anything and the a.cc's magic searching bunnies will do the work for you.

Example:
<quote>When compiling, the compiler wants to know how much memory it should allocate for each instance of a class...</quote>

J-Gamer said:

When compiling, the compiler wants to know how much memory it should allocate for each instance of a class...

TranslatorHack 2010, a human translation chain in a.cc.
My games: [GiftCraft] - [Blocky Rhythm[SH2011]] - [Elven Revolution] - [Dune Smasher!]

J-Gamer
Member #12,491
January 2011
avatar

Thanks... I was wondering how you guys all do that :p
Edited the quotes in the previous message

" There are plenty of wonderful ideas in The Bible, but God isn't one of them." - Derezo
"If your body was a business, thought would be like micro-management and emotions would be like macro-management. If you primarily live your life with emotions, then you are prone to error on the details. If you over-think things all the time you tend to lose scope of priorities." - Mark Oates

Gabriel Campos
Member #12,034
June 2010
avatar

J-Gamer said:

In a constructor/initialisation function because the amount of bitmaps in the array sprite is set to x, for which the compiler will throw an error if it can't find an int called x that is already been given a value(it has to be global to do this, somebody please correct me if I'm wrong).

You dont understand me...I will put explicit the number 11...the x is not a variable or a parameter or nothing...it was just a example....
When using Object->GetImage()...it will return the number...its simple..

#SelectExpand
1// Its like this . . 2Sprite[5]; 3 4int x = 3; 5 6int GetX(){ 7 return x; 8} 9 10void Draw() 11 draw_sprite(Buffer, Sprite[x], 0, 0); 12// the x will be 3 (in the array number 2)

Simple...

J-Gamer
Member #12,491
January 2011
avatar

Could somebody please explain to me why the getImage() function is a function returning an int and is part of the class cObject, while it is giving the index of an image in an array of the class cDrawManager? ???

This would mean the cObject class should be aware of the contents of the cDrawManager's Sprite array.

" There are plenty of wonderful ideas in The Bible, but God isn't one of them." - Derezo
"If your body was a business, thought would be like micro-management and emotions would be like macro-management. If you primarily live your life with emotions, then you are prone to error on the details. If you over-think things all the time you tend to lose scope of priorities." - Mark Oates

bamccaig
Member #7,536
July 2006
avatar

J-Gamer said:

Thanks... I was wondering how you guys all do that :p

acc.js :-X

J-Gamer said:

Could somebody please explain to me why the getImage() function is a function returning an int and is part of the class cObject, while it is giving the index of an image in an array of the class cDrawManager? ???

This would mean the cObject class should be aware of the contents of the cDrawManager's Sprite array.

That's not necessarily true. Think of file descriptors. A file descriptor is basically an index into some table that the kernel has for an application. That's why STDIN, STDOUT, and STDERR are 0, 1, and 2, respectively. Your application doesn't actually know anything about that table, and couldn't get access to it if it did.

The object doesn't actually need to know what the image index refers to because (presumably) it will be passed in and set from the outside world by something that does know. Using indexes like that for your sprites isn't a terrible idea, but there are better ways to do it, especially in C++. For example, you could store boost::shared_ptr<BITMAP> and then store a copy of the sprite's smart pointer directly in your class. You could even load your sprites into a std::map<std::string, boost::shared_ptr<BITMAP> > so that you could later refer to them by name. That's generally how I have done it.

 1   2   3   4 


Go to: