Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » Two questions: Code seperation, and Game map logic.

This thread is locked; no one can reply to it. rss feed Print
Two questions: Code seperation, and Game map logic.
sec_goat
Member #12,838
May 2011

I am looking for a couple of pieces of advice.

First can some one show me how to split my code between headers and cpp files? I have managed to start splitting my code, but when I try to access a function from a secondary CPP from my main.cpp it says the function has not been defined.

here is a very basic example of what I have:

#SelectExpand
1//map.h 2vector<string> redaMap(); 3void displayMap(); 4 5//map.cpp 6vector<string> readMap() 7{ 8 read map in from a file. 9} 10void displayMap() 11{ 12blit tiles to the screen 13} 14 15//main.cpp 16 17#include "map.h" 18 19level_map = readMap();

When running this it tells me that readMap is not defined in this scope. So I have tried map::readMap() and map.readMap().. . but i am missing something, can some one provide some simple examples and reading on how to get this working?

My next question is this: I have a map that is something like 250 X 250 tiles, and my bitmaps are 32x32, this means I am unable to fit the entire map onto the screen at one time. How can I tell my program to only show the 25 tiles surrounding my player at any given time? (Disclaimer: I have not actually tried to accomplish this yet, but I am having a hard time comprehending how or what I need to do.)

Thanks!

taron 
Member #10,584
January 2009
avatar

map.cpp should include map.h

And readMap has been spelled wrong in map.h.

SpectreNectar
Member #10,969
May 2009
avatar

Do you use header guards?

Header guards are constants that prevent the same header files from being included more than once. So for instance to use map.h you would typically have these lines before and after everything:

#ifndef map_h_included
#define map_h_included
...
#endif

For your second question, there's two things to it. 1, you have to read from a two dimensional array (or other data source representing your map) and draw the tiles relative to the main character.
2, you have to make sure not to draw tiles outside the map when near the edges.

Step 1 is done by doing a bit of conversions like this: draw_x = player_x - total_width/2 + tile_width*map_column

So what you need is to loop through the screen horizontally and vertically in a double for loop figuring out these coordinates and drawing the sub bitmaps at the spot.

Step 2 is simply continuing the for loop if the coordinate is outside

for( int r=player_y/32-25; r<player_y/32+25; ++r ) {
    for( int c=player_x/32-25; c<player_x/32+25; ++c ) {
        if(c<0 || r<0 || c>=255 || r>=255) continue;
        //draw map[r][c] at player_pos (x or y) - 255*32/2 +32*cell (c or r)
    }
}

I did not test this...

sec_goat
Member #12,838
May 2011

SpectreNectar:

Yes I am using the header guards, code::blocks added them automagically and I had to go read up on what it was. I am not sure what I am doing wrong, but I cannot seem to call code from my main cpp that is contained in another cpp file. Is there any basic tutorial out there that can hold my hand through the ewxplanation?

As for the 2 dimensional array, I am actually using a vector filled with strings, from there I loop through each stirng character by character to display and or create items, so far this is workign well, but my code is quickly turning into a hot mess in my main.cpp. . .

Any way I will take a look at your ideas and see if I can't implement somehting similar to get the rendering to work for me.

Thanks~!

SpectreNectar
Member #10,969
May 2009
avatar

You are welcome.

This is where I learned about header files:
http://www.cplusplus.com/forum/articles/10627/

...and a vector will do fine in the case of drawing your tiles - just iterate through it :)

Edgar Reynaldo
Member #8,592
May 2007
avatar

sec_goat
Member #12,838
May 2011

Taron, and Edgar,

Thank you for pointing out my typo, however this is just a bit of sample code here if you must see is the real code:

#SelectExpand
1//map.h 2#ifndef MAP_H_INCLUDED 3#define MAP_H_INCLUDED 4 5#include <vector> 6#include <string> 7 8std::vector<std::string> readMap(); 9 10void displayMap(std::vector<std::string>& vMap); 11 12#endif // MAP_H_INCLUDED 13 14 15//map.cpp 16 17#include <iostream> 18#include <fstream> 19#include <string> 20#include <vector> 21#include <allegro5/allegro.h> 22#include <allegro5/allegro_image.h> 23#include "map.h" 24#include "images.h" 25 26 27 28using namespace std; 29 30vector<string> readMap() 31 { 32 string line; 33 vector<string> vec; 34 ifstream myfile ("lvl/lvl1.txt"); 35 36 if(myfile.is_open()){ 37 while(getline(myfile, line)){ //this will keep the loop goign and grab a new line each iteration 38 vec.push_back(line); 39 } 40 41 myfile.close(); 42 } 43 44 else{ 45 cout << "Unable to open File!\n"; 46 } 47 return vec; 48 } 49 50void displayMap(vector<string> vMap) 51{ 52 int x,y; 53 for(unsigned int i = 0; i < vMap.size(); i++){ 54 for(unsigned int j = 0; j < vMap[i].size() + 1; j++){ 55 y = i * 32; 56 x = j * 32; 57 if (vMap[i][j] == '#'){ 58 al_draw_bitmap(wall, x, y, 0); 59 60 } 61 else if(vMap[i][j]== ' '){ 62 al_draw_bitmap(floor, x, y, 0); 63 64 65 } 66 67 } 68 } 69 al_flip_display(); 70 } 71 72 73 74//main.cpp 75 76#include <stdio.h> 77#include <fstream> 78#include <string> 79#include <vector> 80#include <iostream> 81#include <allegro5/allegro.h> 82#include <allegro5/allegro_image.h> 83#include "images.h" 84 85 86using namespace std; 87 88//----Constatnts----------------------------------------------------------------------------------- 89const float FPS = 60; 90const int SCREEN_W = 640; 91const int SCREEN_H = 480; 92const int IMG_SIZE = 32; 93enum MYKEYS{ KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT}; 94 95 96//-----MAIN------------------------------------------------------------------------------------------------ 97int main(int argc, char **argv) 98{ 99 ALLEGRO_DISPLAY *display = NULL; 100 ALLEGRO_EVENT_QUEUE *event_queue = NULL; 101 ALLEGRO_TIMER *timer = NULL; 102 //ALLEGRO_BITMAP *bouncer = NULL; 103 //ALLEGRO_BITMAP *player = NULL; 104 float bouncer_x = SCREEN_W / 2.0 - IMG_SIZE / 2.0; 105 float bouncer_y = SCREEN_H / 2.0 - IMG_SIZE / 2.0; 106 //float bouncer_dx = -4.0, bouncer_dy = 4.0; 107 bool key[4] = {false, false, false, false}; 108 bool redraw = true; 109 bool doexit = false; 110//----Init Allegro---------------------------------------------------------------- 111 if(!al_init()) //initialize allegro 112 { 113 fprintf(stderr, "failed to initialize allegro!\n"); 114 return -1; 115 } 116//----Install keyboard---------------------------------------------------------------- 117 if(!al_install_keyboard()) 118 { 119 fprintf(stderr, "Failed to initialize keyboard!\n"); 120 return -1; 121 } 122//---------------------------------------------------------------------------------------- 123 if(!al_install_mouse()) 124 { 125 fprintf(stderr, "Failed to initialize the mouse!\n"); 126 return -1; 127 } 128 129//------------------------------------------------------------------------------------- 130 timer = al_create_timer(1.0 / FPS); 131 if (!timer) 132 { 133 fprintf(stderr, "Failed to create timer.\n"); 134 return -1; 135 } 136//-------------------------------------------------------------------------------------- 137 138 if(!al_init_image_addon()) 139 { 140 fprintf(stderr, "Failed to load image addon for allegro!\n"); 141 al_destroy_timer(timer); 142 return -1; 143 } 144//---------------------------------------------------------------------------------- 145 display = al_create_display(SCREEN_W, SCREEN_H); 146 if(!display) 147 { 148 fprintf(stderr, "failed to create display!\n"); 149 al_destroy_timer(timer); 150 al_shutdown_image_addon(); 151 return -1; 152 } 153//---LOAD or CREATE IMAGES------------------------------------------------------------------------------------- 154 bouncer = al_create_bitmap(IMG_SIZE, IMG_SIZE); 155 if(!bouncer) 156 { 157 fprintf(stderr, "Failed to load bitmap.\n"); 158 al_destroy_display(display); 159 al_destroy_timer(timer); 160 al_shutdown_image_addon(); 161 return -1; 162 } 163 player = al_load_bitmap("images/player.bmp"); 164 wall = al_load_bitmap("images/wall.bmp"); 165 floor = al_load_bitmap("images/floor.bmp"); 166 if (!player || !wall || !floor) 167 { 168 fprintf(stderr, "Failed to load Player.bmp.\n"); 169 return -1; 170 } 171 172 al_set_target_bitmap(bouncer); 173 al_clear_to_color(al_map_rgb(255,0,255)); 174 al_set_target_bitmap(al_get_backbuffer(display)); 175//------------------------------------------------------------------------------------------ 176 event_queue = al_create_event_queue(); 177 if(!event_queue) 178 { 179 fprintf(stderr, "Failed to create event queue.\n"); 180 al_destroy_bitmap(bouncer); 181 al_destroy_timer(timer); 182 al_destroy_display(display); 183 return -1; 184 } 185//----------------------------------------------------------------------------------------------- 186 al_register_event_source(event_queue, al_get_display_event_source(display)); 187 al_register_event_source(event_queue, al_get_timer_event_source(timer)); 188 al_register_event_source(event_queue, al_get_mouse_event_source()); 189 al_register_event_source(event_queue, al_get_keyboard_event_source()); 190 191 al_clear_to_color(al_map_rgb(0,0,0)); 192 al_flip_display(); 193 194 al_start_timer(timer); 195 196 197 vector<string> level_map; 198 level_map = readMap(); 199 displayMap(level_map); 200 201 202 while(!doexit) 203 { 204 ALLEGRO_EVENT ev; 205 al_wait_for_event(event_queue, &ev); 206 207 if(ev.type == ALLEGRO_EVENT_TIMER) 208 { 209 if(key[KEY_UP] && bouncer_y >= 4.0) 210 { 211 bouncer_y -= 4.0; 212 } 213 if(key[KEY_DOWN] && bouncer_y <= SCREEN_H - IMG_SIZE - 4.0) 214 { 215 bouncer_y += 4.0; 216 } 217 if(key[KEY_LEFT] && bouncer_x >= 4.0) 218 { 219 bouncer_x -= 4.0; 220 } 221 if(key[KEY_RIGHT] && bouncer_x <= SCREEN_W - IMG_SIZE - 4.0) 222 { 223 bouncer_x += 4.0; 224 } 225 redraw = true; 226 227 } 228 else if(ev.type == ALLEGRO_EVENT_DISPLAY_CLOSE) 229 { 230 break; 231 } 232 else if (ev.type == ALLEGRO_EVENT_KEY_DOWN) 233 { 234 switch(ev.keyboard.keycode) 235 { 236 case ALLEGRO_KEY_UP: 237 key[KEY_UP] = true; 238 break; 239 240 case ALLEGRO_KEY_DOWN: 241 key[KEY_DOWN] = true; 242 break; 243 244 case ALLEGRO_KEY_LEFT: 245 key[KEY_LEFT] = true; 246 break; 247 248 case ALLEGRO_KEY_RIGHT: 249 key[KEY_RIGHT] = true; 250 break; 251 } 252 } 253 254 else if(ev.type == ALLEGRO_EVENT_KEY_UP) 255 { 256 switch(ev.keyboard.keycode) 257 { 258 case ALLEGRO_KEY_UP: 259 key[KEY_UP] = false; 260 break; 261 262 case ALLEGRO_KEY_DOWN: 263 key[KEY_DOWN] = false; 264 break; 265 266 case ALLEGRO_KEY_LEFT: 267 key[KEY_LEFT] = false; 268 break; 269 270 case ALLEGRO_KEY_RIGHT: 271 key[KEY_RIGHT] = false; 272 break; 273 274 case ALLEGRO_KEY_ESCAPE: 275 doexit = true; 276 break; 277 } 278 } 279 280 else if(ev.type == ALLEGRO_EVENT_MOUSE_AXES || 281 ev.type == ALLEGRO_EVENT_MOUSE_ENTER_DISPLAY) { 282 283 bouncer_x = ev.mouse.x; 284 bouncer_y = ev.mouse.y; 285 } 286 else if(ev.type == ALLEGRO_EVENT_MOUSE_BUTTON_UP) { 287 break; 288 } 289 290 if(redraw && al_is_event_queue_empty(event_queue)) 291 { 292 293 redraw = false; 294 al_clear_to_color(al_map_rgb(0,0,0)); 295 displayMap(level_map); //My call to start drawing the map 296 al_draw_bitmap(bouncer, bouncer_x, bouncer_y, 0); 297 al_flip_display(); 298 } 299 300 } 301 302 al_destroy_bitmap(bouncer); 303 al_destroy_timer(timer); 304 al_destroy_display(display); 305 al_destroy_event_queue(event_queue); 306 307 return 0; 308}

Well it does appear there was a typo even in the real header.... but still getting the
'readMap' was not declared in this scope
and
'displayMap' was not declared in this scope

Thanks for the article SpectreNectar I will start reading up on headers there.

SpectreNectar
Member #10,969
May 2009
avatar

Main.cpp doesn't include map.h. It should.

sec_goat
Member #12,838
May 2011

EDIT: I am trying to get some more files split up and I am having some trouble.
I have a pointer to my player object and if I keep it in main, then my Mob.cpp file cannot find it. If i put it in a header called Globals.h it tells me I have multiple definitions of the pointer.

How can I have a variable like this that is available from all classes?

And another EDIT:

I am passing variables to the functions as needed, and this seems to work well. Still open to hearing suggestions on best practice.

bamccaig
Member #7,536
July 2006
avatar

sec_goat
Member #12,838
May 2011

Thanks bamccaig.
This is a very good article, a lot of this stuff I am already doing, header guards to not appear to be stopping the duplicate definition for me however.

I did change some to pass variables around and that works.

However I am hitting a very strange behavior with my header files.

I have a World class and a Mob class. The world class:

#SelectExpand
1#ifndef WORLD_H 2#define WORLD_H 3 4#include <list> 5#include "mob.h" 6 7using namespace std; 8 9class World 10{ 11 public: 12 /** Default constructor */ 13 World(); 14 /** Default destructor */ 15 virtual ~World(); 16 17 //members 18 list<Mob> m_Mobs; 19 20 //functions 21 22 void SpawnMobs(ALLEGRO_BITMAP *mob); 23 void RemoveMob(const Mob *); 24 25 protected: 26 private: 27}; 28 29#endif // WORLD_H

and the Mob class:

#SelectExpand
1#ifndef MOB_H 2#define MOB_H 3 4#include "entity.h" 5#include "Player.h" 6#include "world.h" 7#include <list> 8 9 10class Mob : public Entity 11{ 12 public: 13 /** Default constructor */ 14 Mob(ALLEGRO_BITMAP *image, int x, int y, int w, int h); 15 /** Default destructor */ 16 virtual ~Mob(); 17 static std::list<Mob> m_Mobs; 18 19 void Move(int x, int y, Player *player, World *world); 20 protected: 21 float m_Speed; 22 23 private: 24}; 25 26#endif // MOB_H

World needs to know what Mob is to keep track of them, and Mob needs to know what world is so it can accept a pointer object to it.

When Mob.H contains world.h and vice versa it will start telling me that Mob is not defined in World or World is not defined in Mob. .

Exact message is:

error: 'World' has not been declared

bamccaig
Member #7,536
July 2006
avatar

That is a circular inclusion. You'll need to forward declare one of the types and not include the other header file in one of them. As an extension to this, you must only use a pointer to the type within that header. E.g.,

Mob.hpp#SelectExpand
1#ifndef MOB_HPP 2 #define MOB_HPP 3 4// Forward declare World. DON'T include Mob header file. 5class World; 6 7class Mob 8{ 9// ... 10public: 11// ... 12 13 /* 14 * We know there is a World class. That's all we need to know for a 15 * pointer to it. 16 */ 17 void move(int, int, Player *, World *); 18 19// ... 20}; 21 22#endif

World.hpp#SelectExpand
1#ifndef WORLD_HPP 2 #define WORLD_HPP 3 4 // DO include Mob header. 5 #include "Mob.hpp" 6 7class World 8{ 9// ... 10public: 11// ... 12 13 // We know what 'Mob' is; no problem. 14 std::list<Mob> mobs_; 15 16// ... 17}; 18 19#endif

Mob.cpp#SelectExpand
1/* 2 * NOW we need to know what a World actually is. Include the World header 3 * file from Mob source file. 4 */ 5#include "Mob.hpp" 6#include "World.hpp" 7 8// ... 9 10void Mob::move(int x, int y, Player * player, World * world) 11{ 12 // We know what 'World' is where it counts. 13}

World.cpp#SelectExpand
1// No need for Mob header. Already included in World header. 2#include "World.hpp" 3 4// ...

Something like that anyway... Somebody should extend the Wiki article with the solution for circular inclusion if it doesn't already explain it. :-/ There are limitations, of course. You can't actually use World objects within the Mob header because in the Mob header we don't necessarily know what a World object actually is yet.

All that said, you might find that you don't want a list of Mob objects. For that to work reliably, your Mob type must follow the rules for a proper object, and even then it might be a good waste creating and destroying objects as the list is manipulated. I'm not really an expert on the STL or C++ in general. :) In any case, if you store pointers, it's inexpensive to manipulate the list, and you know it won't break anything. You do have to worry about managing pointers though. So you might prefer the best of both worlds; a list of smart pointers.

And as I've mentioned in another thread, you might opt to not have the Mob know about the World at all, which will eliminate this whole problem. :)

someone972
Member #7,719
August 2006
avatar

Just a random on the spot idea, it may have been suggested before (I'm being lazy and not reading the whole thing). Could you have another class that takes a Mob and a World and preforms the necessary work between them?

______________________________________
As long as it remains classified how long it took me to make I'll be deemed a computer game genius. - William Labbett
Theory is when you know something, but it doesn't work. Practice is when something works, but you don't know why. Programmers combine theory and practice: Nothing works and they don't know why. -Unknown
I have recklessly set in motion a chain of events with the potential to so-drastically change the path of my life that I can only find it to be beautifully frightening.

Mark Oates
Member #1,146
March 2001
avatar

Could you have another class that takes a Mob and a World and preforms the necessary work between them?

Yes. That's one of the better ways to do it.

Go to: