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
Neil Walker
Member #210
April 2000
avatar

I dont understood why J said to use a virtual void draw();

I haven't read the thread, but he's probably referring to polymorphism, i.e. loose coupling is king.

It means you can have lots of sub-classes but still have call draw() called correctly when using the base class, e.g. assuming Sprite1 and Sprite2 were inherited from Object, then:

void MyGreatFunction(Object* a)
{
a->draw();
}

if not using virtual then Object::draw would be called. If using virtual then the correct ones are. The drawback is a slight overhead in determining the correct method to call.

Neil.
MAME Cabinet Blog / AXL LIBRARY (a games framework) / AXL Documentation and Tutorial

wii:0356-1384-6687-2022, kart:3308-4806-6002. XBOX:chucklepie

bamccaig
Member #7,536
July 2006
avatar

I think that J-Gamer starting giving one suggestion and ended up giving a better one. Ignore the part about the draw method. Game objects generally shouldn't know how to draw themselves (they should have no concept of a screen, etc.). A third-party, like the Renderer concept should know how to draw all objects. It's the getSprite or getFrame method that you'll want to be virtual (and likely abstract AKA pure virtual) in the base class.

J-Gamer
Member #12,491
January 2011
avatar

bamccaig said:

I think that J-Gamer starting giving one suggestion and ended up giving a better one.

Did I? ;D

bamccaig said:

Ignore the part about the draw method.

I always have a draw() method in each game object. Those functions mostly take (BITMAP* buffer,float x,float y) as parameters. I do it this way, because e.g. my spacecraft has to be drawn in different ways according to a frame and mode variable within the spaceship 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

james_lohr
Member #1,947
February 2002

Well, at least you were able to identify that your code looks ugly. That's an important step. Now you just need a decade or so of experience and you might be able to write some decent code. :P

Gabriel Campos
Member #12,034
June 2010
avatar

So lets see if I understand . . .

I must have a class cObject, a class cRenderer . . .

#SelectExpand
1class cObject{ // Create a cObject class that will manager all drawable objects 2}; 3 4class cRenderer{ // Renderer class 5 BITMAP *Buffer; 6 7void Render(cObject * object, cCamera *camera){ 8 masked_blit(object->getSprite(), Buffer, object->getFrame()*object->getW(), Object->getStance()*object->getH(), object->getX - camera->getX, object->getY - camera->getY(), object->getW(), object->getH()); 9} 10}; 11 12 13int main(){ 14 std::vector<cObject *>Objects; 15 for(int i = 0; i < Objects.length(); i++){ 16 Renderer.Render(Objects[i], &Camera); 17 } 18}; 19END_OF_MAIN();

Now my last doubt(I think) . .
First, is this code correct?
And, where I put the virtual draw??
in the class cObject, in the class cRenderer or in the other classes (player, hud, enemy)??:-/

J-Gamer
Member #12,491
January 2011
avatar

And, where I put the virtual draw??
in the class cObject, in the class cRenderer or in the other classes (player, hud, enemy)??:-/

You're mixing up two concepts here... You can do it by putting a virtual draw function in the cObject class, and making all classes like player,hud,enemy,... members of it with their own non-virtual draw that draws them. OR, you can have a cRenderer class draw function that checks all Objects for their type and draws them accordingly. In the latter, you have all drawing code in the cRenderer class, whereas in the former, you have the corresponding drawing code specified in each of the classes.

" 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:

I do it this way, because e.g. my spacecraft has to be drawn in different ways according to a frame and mode variable within the spaceship class.

Out of curiosity, what kinds of things vary based on the "frame" and "mode"? :)

J-Gamer
Member #12,491
January 2011
avatar

Most of the time: which of the bitmaps in the different sequences are drawn.

I have e.g.: a flying mode, propulsion mode, explosion mode, vanish mode, etc.
For the flying mode, nothing special, just a plain spaceship sprite.
Propulsion mode: increase frame, draw corresponding bitmap in the propulsion sequence
Explosion: idem as propulsion, except it doesn't loop trough the sequence.
vanish mode: get smaller and smaller, according to the frame(amount of frames is set beforehand)

" 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

I think you should be able to accomplish all of those with an animation class and possibly a separate interface for spaceship position vs. spaceship sprite position (if those aren't always the same as the position).

The getSprite(gameTime) or getFrame(gameTime) method should return an appropriate sprite for the given mode/animation/game time. That way the actual drawing can be handled by a single rendering class and the differences in what are drawn get returned by the individual objects (preferably managed by another class under the surface, like an animation class, if non-trivial). This way your objects don't need to know anything about the drawing surface, or how to be drawn at all. They only store the data that is their state (including the visual and audio representation) and separate service objects, like our renderer, determine what to do with that data.

???

Neil Walker
Member #210
April 2000
avatar

I remember about 15 years ago when I first got started into game coding (at that point I thought there was something magical and complicated about how you display moving things, not knowing it was just a case of blanking the screen and drawing stuff elsewhere), I wondered whether when a player picked up an item and moved with it, whether I had to have a graphic with the player and the item and thought this will take a lot of graphics ;)

Neil.
MAME Cabinet Blog / AXL LIBRARY (a games framework) / AXL Documentation and Tutorial

wii:0356-1384-6687-2022, kart:3308-4806-6002. XBOX:chucklepie

Thomas Fjellstrom
Member #476
June 2000
avatar

/me thinks bamccaig is falling into the OO/C++ trap of overcomplicated, overengineered patterned designs...

--
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

bamccaig
Member #7,536
July 2006
avatar

That might be what it sounds like, but in actuality it's simplifying the design instead of complicating it. Instead of making 20 different redundant calls to blit or al_draw_bitmap or whatever, you make one (or a handful) and everything else just works the same way. Your objects have a visual state and another external class knows how to draw that state (but knows nothing of specific types, like spaceships or moonmen or whathaveyou). It's actually a lot simpler of a design, but it requires a bit of forethought. :)

Thomas Fjellstrom
Member #476
June 2000
avatar

bamccaig said:

That might be what it sounds like, but in actuality it's simplifying the design instead of complicating it.

Depends. Every post you come up with more and more complicated patterns to add, for a BEGINNER. ;)

And no, it isn't simpler. :P

--
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

bamccaig
Member #7,536
July 2006
avatar

It's more complicated language-wise because you need to understand polymorphism, but the solution is simpler than redundantly duplicating the same basic logic over and over again. IIRC, the OP said he was knowledgeable in C++. At least, he seems comfortable enough to be introduced to/reminded of polymorphism and its benefits.

Thomas Fjellstrom
Member #476
June 2000
avatar

bamccaig said:

It's more complicated language-wise because you need to understand polymorphism

And a crap ton of new "design patterns". I'm not against a little polymprphosm, but springing a bunch of fancy design patterns on a newbie is silly in the extreme.

Quote:

At least, he seems comfortable enough to be introduced to/reminded of polymorphism and its benefits.

Just my oppinion, but if you haven't been introduced to Polymorphism, you aren't comfortable with C++, and you probably aren't ready for most of the fancy design patterns you are suggesting :P

--
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

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

And a crap ton of new "design patterns". I'm not against a little polymprphosm, but springing a bunch of fancy design patterns on a newbie is silly in the extreme.

You don't need design patterns to use polymorphism, and there aren't any design patterns being used here...

All it is is inheritance, to replicate an interface for different classes, so they can be used interchangeably through a base class pointer.

#SelectExpand
1#include <allegro.h> 2 3class ObjectBase { 4protected : 5 BITMAP* sprite; 6 int x; 7 int y; 8public : 9 ObjectBase(BITMAP* image , int xpos , int ypos) : sprite(image) , x(xpos) , y(ypos) {} 10 virtual void DrawOn(BITMAP* bmp , int offsetx , int offsety) const { 11 ASSERT(bmp); 12 draw_sprite(bmp , sprite , xpos + offsetx , ypos + offsety); 13 } 14}; 15 16class Player : public ObjectBase { 17// ... 18}; 19 20class Camera { 21protected : 22 BITMAP* background; 23 int cx; 24 int cy; 25 int cw; 26 int ch; 27public : 28 Camera(BITMAP* bg , int xpos , int ypos , int width , int height) : 29 background(bg) , cx(xpos) , cy(ypos) , cw(width) , ch(height) {} 30 void SetPos(int xpos , int ypos) { 31 //... adjust xpos and ypos to make sure the camera doesn't go 'offworld' 32 cx = xpos; 33 cy = ypos; 34 } 35 void DrawBackground(BITMAP* bmp , int x , int y) { 36 ASSERT(bmp); 37 blit(background , buffer , cx , cy , x , y , cw , ch); 38 } 39 int X() {return cx;} 40 int Y() {return cy;} 41} 42 43class World { 44protected : 45 vector<ObjectBase*> objects; 46public : 47 void DrawOn(BITMAP* bmp , const Camera& cam) { 48 ASSERT(bmp); 49 cam.DrawBackground(bmp , 0 , 0); 50 int camx = cam.X(); 51 int camy = cam.Y(); 52 for (int i = 0 ; i < objects.size() ; ++i) { 53 objects->DrawOn(bmp , -camx , -camy); 54 } 55 } 56};

Thomas Fjellstrom
Member #476
June 2000
avatar

You don't need design patterns to use polymorphism, and there aren't any design patterns being used here...

I was talking about some of the stuff bamccaig was proposing. Fancy adapters and MVC...

--
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

Neil Walker
Member #210
April 2000
avatar

Good use of polymorphism as I said above helps you with loose coupling. What's worse - having a bit of thought before you write your code, or turning your code into spaghetti within a few weeks with either 30 case statements in a method or 10 methods that do the same thing but slightly different and having to remember what they all are?

I agree design patterns can be bad in the wrong hands and they do over-complicate things, but where it's relevant it makes things better. For example, most people end up coding using the Strategy, Adapter and Facade pattern without even realising it.

Neil.
MAME Cabinet Blog / AXL LIBRARY (a games framework) / AXL Documentation and Tutorial

wii:0356-1384-6687-2022, kart:3308-4806-6002. XBOX:chucklepie

Gabriel Campos
Member #12,034
June 2010
avatar

Ok, lets some code:

#SelectExpand
1class cObject{ // This class will handle all objects in the game 2 private: 3 BITMAP *Buffer; 4 public: 5 virtual void Draw(cObject *Object, cCamera *Camera){ 6 // here comes a little doubt.. what to put here? 7 // Maybe this? masked_blit(Object->GetSprite(), Buffer, Object->GetFrame() * Object->GetW(), Object->GetStance() * Object->GetH(), Object->Getx(), Object->Gety(), Object->GetW(), Object->GetH()); 8} 9 10 void UpdateScreen(){ 11 draw_sprite(screen, Buffer, 0, 0); 12} 13}; 14 15............................................................................. 16 17class cPlayer : public cObject{ 18 private: 19 int x, y, W, H ..... // or maybe put this variables in the cObject class, because is common in all objects 20 BITMAP *Sprite; 21 public: 22 void Draw(){ // Here I declare the Draw in Normal?? This Way? 23 masked_blit(Sprite, Buffer, Frame * W, Stance * H, x, y, W, H); 24} 25}; 26 27............................................................................. 28 29class cCamera{ 30 int x, y....//and others definitions 31}; 32 33.............................................................................. 34 35#include <vector> 36 37int main(){ 38 std::vector<cObject *>Objects; 39 40for(int i=0; i < Objects.lenght(); i++){ 41 Objects[i].Draw(&Player, &Camera); 42} 43END_OF_MAIN();

Please, help me...I am learning a lot here... ;D

jhetfield21
Member #12,379
November 2010

if you have sprites then a good idea is to let the renderer class handle it.just make a spriteholder class(basically a class with a map/list/vector<string,Sprite*> of all the Sprites in your level or even the whole game though it's not advised) and whenever you instantiate a sprite you register it(insert it) in the sprite holder('s map/list/vector).so in the game loop the first thing you do is call the renderer and have it blit everything visible on the screen and afterward do every other calculation to change the state of the game,player,character etc so that in the next loop the renderer has all the updated info on the sprites including the new destination point and possibly another frame of the same film.

i'm currently taking a course in my uni for 2D bitmapped games and this is the way we are doing it.it's simple enough imo.then again it might not fit in this situation,i didn't really see the code but it's a good concept to know.and you don't need to implement any design patterns.

Audric
Member #907
January 2001

This is my take:
(I renamed cObject to cSprite because the name Object can cause a lot of confusion in c++.)

#SelectExpand
1class cSprite{ // This class will handle all moving bitmap objects in the game 2 cSprite() 3 { 4 Sprite=NULL; // Default: invisible (and non-crashing :) ) 5 } 6 public: 7 BITMAP *Sprite; // This points to an already allocated bitmap, or NULL when the object is invisible. 8 int X, Y; // Position in pixels, in "world" coordinates. 9 int W, H; // Any other members that all your cSprites need). 10 virtual void LogicUpdate(int tick); // Called once per game tick (assuming fixed-step timing). Has to be implemented by each derived class. 11} 12 13 void Draw(cCamera *cam, BITMAP *target_buffer){ 14 if (Sprite==NULL) 15 return; // To make an object invisible, set its Sprite to NULL; 16 draw_sprite(target_buffer, Sprite, X - cam->Offset_X, Y - cam->Offset_Y); 17} 18 19}; 20 21............................................................................. 22 23class cPlayer : public cObject{ 24 public: 25 26 cPlayer() 27 { 28 // Initial position 29 X=100; 30 Y=100; 31 } 32 33 void LogicUpdate(int tick) // Logic for player, not very interactive here. 34 { 35 Sprite = Player_sprite[10 + ((tick/6) % 4)]; // Changes the image once every 6 ticks, and uses sprites 10 to 13 for an array. 36 X = (tick % 200) + 100; // Moves from 100 to 300, 1 pixel per tick, then warp back and repeat. 37 } 38}; 39 40.............................................................................. 41 42#include <vector> 43 44int main(){ 45 std::vector<cSprite *> Sprites; 46 47// Code for redrawing all screen: 48// Erase all here 49... 50// Draw all sprites 51for(int i=0; i < Objects.length(); i++){ 52 Objects[i].Draw(&Camera, screen); 53} 54 55.... 56 57// Code for the logic of *** all the game*** : 58for(int i=0; i < Objects.length(); i++){ 59 Objects[i].LogicUpdate(current_frame); 60} 61 62 63END_OF_MAIN();

I didn't want to make this too complex, but note that the sprites will be displayed in the order of the collection (vector) : last ones are "above".
In my own games, Sprite has a member called Priority, and I build a collection for displaying (I omit the invisible sprites at this step) then I sort this collection by Priority, before browsing it to call Draw().

jhetfield21
Member #12,379
November 2010

you have to have priority given one way or the other if you want your characters to be able to go behind graphics as well.
this is called z-ordering.where each priority value represents something like a layer of graphics.where the layer with the lowest priority is the background,the next priority is anything else static or not that you always want your character or other characters to pass in front of,then it's your player's character with any other characters/enemies and stuff and then the highest priority is the layer with all graphics that you want your character or others to pass behind of.this is just an example nothing more.you can have more or less layers depending on what you want to do.

and principally Audric's solution is the same as what i said.

J-Gamer
Member #12,491
January 2011
avatar

The drawback is a slight overhead in determining the correct method to call.

ehh... I didn't know virtual functions cause overhead... BTW, what is overhead exactly(srr for the noobish question)

@bamccaig:
I always have the drawing code in a draw function in each class that is to be displayed on screen. It's easier to debug them because you know where to find the exact drawing code your looking for. In a cRenderer that checks the type etc, you'll have a LOT of get-functions, so you'll have to put all those in the classes as well. As you're looping trough an array of objects, the get-functions need to be declared in the object functions, but not all objects share the same properties. E.g.: a spaceship needs multiple bitmap vectors/arrays to store different animation sequences. a black hole/planet only needs one bitmap.
Because of this, you could have a virtual getcurrentbitmap() function or something like that in the object class, but that would completely destroy the purpose of an external renderer. You can as well have a draw function in your class.
In my case, the only purpose of a renderer is to sort the objects by depth and move the drawing calls out of your main loop, to avoid messy code.

" 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

jhetfield21
Member #12,379
November 2010

overhead is a term to describe the excess work that the CPU or any other part of the pc has to do when given a difficult task.for example if you call too many functions then you get an overhead in the call stack.you make too many records of function calls when you could have made less to attain the same goal.another example of overhead is polling.before interrupts where implemented the CPU had to poll the hard disk to see if a transaction had ended thus having to queue many other processes and perform context switches all the time just to make sure the transaction of a data transfer was completed.then interrupts came and then DMA.when a data transfer needs to be done it is approved and started and then DMA manages it until it's finished without having the CPU involved.

And virtual functions cause a little overhead because every object uses late binding to determine which function to use,that of the super class or the derived.but i don't think it's such an overhead to even be mentioned.i mean with today's hardware and all the object oriented programs in c++ lying around and very often well known ones at that you can see it doesn't pose a problem.

Arthur Kalliokoski
Second in Command
February 2005
avatar

I'd say overhead is best described by the computer doing at runtime with brute force what you were too lazy to do with your brain at program design time.

They all watch too much MSNBC... they get ideas.

 1   2   3   4 


Go to: