Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » Drawing to display in class member functions not working

This thread is locked; no one can reply to it. rss feed Print
Drawing to display in class member functions not working
mir0slaw
Member #15,677
July 2014

Hi!
I'm quite new to Allegro 5 and programming in general. I've watched those tutorials:
http://fixbyproximity.com/2d-game-development-course/
http://fixbyproximity.com/oop-game-development/
and they're the main source of my knowledge.

In "Globals.h" which is included by everything, I have
static ALLEGRO_DISPLAY *display=NULL;
The display is initialized later in "main.cpp".
Everything with the display has been working fine, the game's running. The only problem is this:
My class "Player" has a function Player::Render(). It's worked before, but when I try to draw something to display, or use al_get_backbuffer(display); the following happens:
The code compiles, but when I run it I instantaneously get an exception looking like I'm trying to delete something that's already deleted, etc. (at least that's what I read on the internet)
It's similar to this one: Unhandled exception at 0x0F1FE0C4 (allegro-5.0.10-monolith-mt.dll) in TEST.exe: 0xC0000005: Access violation reading location 0x00000008. but the numbers vary.

It's happened in two different places and right now I just moved those troublesome drawing sequences into the main, next to the place where Render() would be called. But it's really annoying and my code will soon get messy if I don't know how to handle this.

Please, help me! :D
Cheers

Elias
Member #358
May 2000

Replace the "static" with "extern". "static" means to make a new copy of the variable for each file it is included from (so for you they are all uninitialized as the one in main.cpp is different from all the others).

--
"Either help out or stop whining" - Evert

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

mir0slaw
Member #15,677
July 2014

I'll try extern in a few minutes.
I did similar things with colors, so they'd go global. I wrote:
In globals.h:
static ALLEGRO_COLOR awesomeCol;
After initialiazing Allegro in main.cpp:
awesomeCol=al_map_rgb(69, 69, 69);

I don't really understand the static thingy, but I hoped it would let me make display global and visible to all classes defined in .h files that #included "Globals.h".
Do you know what I mean?

Now I'll try to work on my code and use this extern keyword. I'll tell you what the results are.

Thanks for helping me out

EDIT:

Now I got this (http://s30.postimg.org/7wauftgb3/Bez_tytu_u.png) (don't pay attention to the warning, it's been there before and is not causing any problems).

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

Yeah, to make a variable globally visible you use the extern keyword.

You declare it once in your header as extern :

header.h

#ifndef HEADER_H
#define HEADER_H

extern ALLEGRO_DISPLAY* display;

#endif // HEADER_H

And then define it once in one source module somewhere.

module.cpp

#include "header.h"

ALLEGRO_DISPLAY* display = 0;

mir0slaw
Member #15,677
July 2014

So I have 3 questions now.

1) Should I do this #ifndef stuff for each of my externs?

2) I did as you advised me and I still have problems with the program.

#SelectExpand
1void Player::Render() 2{ 3 GameObject::Render(); 4 5 al_draw_filled_circle(x, y, radius, color1); 6 al_draw_line(x, y, x + radius*cos(aimAngle), y - radius*sin(aimAngle), color3, barrelWidth); 7 ALLEGRO_FONT *font = al_load_font("aSongForJennifer.ttf", 30, NULL); 8 //HEALTH: 9 al_draw_textf(font, al_map_rgb(210, 15, 0), 25, 345, ALLEGRO_ALIGN_CENTER, "%d", static_cast<int>(maxHealth)); 10 //al_draw_bitmap_region(healthBar, 0, 0, health / maxHealth, al_get_bitmap_height(healthBar), 50, 345, 0); //THIS WAS CAUSING PROBLEMS 11 al_draw_rectangle(50, 345, 250, 375, al_map_rgb(1, 2, 3), 1); 12 al_draw_textf(font, al_map_rgb(210, 15, 0), 275, 345, ALLEGRO_ALIGN_CENTER, "%d", static_cast<int>(maxHealth)); 13 al_destroy_font(font); 14}

With al_draw_bitmap_region commented out the program works properly, but it also worked like this before, when I had static ALLEGRO_DISPLAY in my Globals.h

With al_draw_bitmap_region not commented out it still gives me the same "unhandled exception" error just after I run the program. And there's a yellow arrow pointing at this, so this is where the error happens:

#SelectExpand
1for (iter = objects.begin(); iter != objects.end(); ++iter) 2 (*iter)->Render();

And one more question: in the example above I have to load the font again, I would like not to have to do this and only load it once, somewhere. I suppose the solution to this might be similar to the solution to my main problem, but anyway I don't know how to do that.

Cheers!

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

mir0slaw said:

1) Should I do this #ifndef stuff for each of my externs?

No, you just do it once for each header that you have. It prevents it from being accidentally included twice, which would cause errors.

Quote:

2) I did as you advised me and I still have problems with the program.

You haven't checked any return values. Your fonts and bitmaps might not always load properly, and you have to check for that yourself. Is HealthBar a valid bitmap that has been created somewhere else?

Show more code after you check the return values if you're still having a problem.

Quote:

And one more question: in the example above I have to load the font again, I would like not to have to do this and only load it once, somewhere. I suppose the solution to this might be similar to the solution to my main problem, but anyway I don't know how to do that.

Either pass the font as a parameter to the function, or make the font global like you just did with 'display'.

beoran
Member #12,636
March 2011

A better design is not to have global variables, but functions that return pointers to them.

For example if you have a display.c that is responsible for opening the display then in that you can put

#SelectExpand
1static ALLEGRO_DISPLAY * my_display = NULL; 2 3ALLEGRO_DISPLAY * display my_create_display(int w, int h) { 4 my_display = al_create_display(w, h); 5 return my_display; 6} 7 8 9ALLEGRO_DISPLAY * display my_get_display() { 10 return my_display; 11}

In the header file display.h

#SelectExpand
1ALLEGRO_DISPLAY * display my_create_display(int w, int h); 2ALLEGRO_DISPLAY * display my_get_display();

And then include that header file in any .c filewhere you need to use the display. And use my_create_display to open the display, and my_get_display to get a reference to it.

mir0slaw
Member #15,677
July 2014

To beoran:
So do I understand correctly that I would like to use my_create_display only once, in main, after initializating allegro?
And then use my_get_display() everytime I would normally use display?

I'll check if the suggestions from both of you work in a minute. Thanks!

EDIT:
Ok, so here's my not working FloorGenerator::Update()

display.h:

#SelectExpand
1#pragma once 2#include<allegro5\allegro5.h> 3 4ALLEGRO_DISPLAY * my_create_display(int w, int h); 5ALLEGRO_DISPLAY * my_get_display();

display.cpp:

#SelectExpand
1#include "display.h" 2 3static ALLEGRO_DISPLAY *my_display = NULL; 4 5ALLEGRO_DISPLAY * my_create_display(int w, int h) 6{ 7 my_display = al_create_display(w, h); 8 return my_display; 9} 10ALLEGRO_DISPLAY * my_get_display() 11{ 12 return my_display; 13}

FloorGenerator.h:

#SelectExpand
1//My class Floor has a private member: 2ALLEGRO_BITMAP *floorMap

Then we have FloorGenerator.cpp, with a function Update() which causes the unhandled exception. I don't load any fonts or samples, so it's not where the problem is.

#SelectExpand
1void Floor::Update() 2{ 3 al_set_target_bitmap(floorMap); 4 int width = al_get_bitmap_width(floorMap); 5 int height = al_get_bitmap_height(floorMap); 6 for (int j = 0; j < getFloorSize(); j++) 7 { 8 for (int i = 0; i < getFloorSize(); i++) 9 { 10 if (getRoomSpawned(i + j*getFloorSize())) 11 { 12 al_draw_filled_rectangle(i*width / (getFloorSize()), j*height / (getFloorSize()), 13 (i + 1)*width / (getFloorSize()) - 1, (j + 1)*height / (getFloorSize()) - 1, lightGrey); 14 al_draw_rectangle(i*width / (getFloorSize()), j*height / (getFloorSize()), 15 (i + 1)*width / (getFloorSize()) - 1, (j + 1)*height / (getFloorSize()) - 1, simplyBlack, 1); 16 } 17 18 19 else 20 { 21 al_draw_filled_rectangle(i*width / (getFloorSize()), j*height / (getFloorSize()), 22 (i + 1)*width / (getFloorSize()) - 1, (j + 1)*height / (getFloorSize()) - 1, darkGrey); 23 al_draw_rectangle(i*width / (getFloorSize()), j*height / (getFloorSize()), 24 (i + 1)*width / (getFloorSize()) - 1, (j + 1)*height / (getFloorSize()) - 1, simplyBlack, 1); 25 } 26 } 27 } 28 al_set_target_bitmap(al_get_backbuffer(my_get_display())); 29} 30 31void Floor::Render() 32{ 33//STUFF THAT'S CERTAINLY WORKING 34al_draw_scaled_bitmap(floorMap, 0, 0, al_get_bitmap_width(floorMap), al_get_bitmap_height(floorMap), 15, 15, 270, 270, 0); 35}

I just want to mention, that the exact same code put in main worked, of course if I created the floorMap in main too.

What's the cause of the problem? My other problem is in the Player::Render() but it's probably caused by a similar thing. I will get to that once we deal with that.

Cheers

beoran
Member #12,636
March 2011

mir0slaw yes, that's what I ment.

Are you using any multi-treading perchance? you can only render to the display from the thread that opened it (OS limitation).

mir0slaw
Member #15,677
July 2014

Oh man, I have absolutely no idea what those threads mean and what they do.

But I have the following project settings:

http://s17.postimg.org/y3watnzfj/Bez_tytu_u.png

I use them because now I can use Advanced Installer to make an installer for my game. I learnt that from the following tutorial: https://www.youtube.com/watch?v=Ny-rsCSYkzU
This guy said those project settings are needed. And I don't wanna have a game that I can't give my friends easily.
I really can't use the display conveniently in my class member functions without changing those settings?
Cheers

beoran
Member #12,636
March 2011

Well, if you don't know what threads are then I doubt you are using them. So please don't worry about it for now. :) I'm not sure since I don't use VisualStudio, but those settings look OK as well on first sight.

Which briings us back to your problem. Perhaps, if you don't mind you could just zip up all the sources of your project and post them here (of a link if they are on a code hosting site like GitHub or so). Then we can try to compile and run them ourselves to try to help you better.

mir0slaw
Member #15,677
July 2014

Oh yeah, sure. That'd be great.
I attached the zip file to this post.
Cheers

beoran
Member #12,636
March 2011

Thanks for posting this.

Sorry to say this, but I feel I can't help you yet with Allegro, because after reading your code, I feel you still need to learn more about the basics of programming.

I'd recommend to forget about object orientation or C++ and focus on just C. Once you get proficient in C, you can always learn C++ later if need be. But to use Allegro, there is no need to learn any C++ or object orientation at all. One good book I used myself way back when to learn C programming is this one: http://www.amazon.com/C-Plain-English-Brian-Overland/dp/1558284303.

There are many other resources available online as well for learning C.

And sorry to be not so helpful to you.

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

You do not want to be using static variables in a header. If you need to share variables between source modules then you need to declare them as 'extern' and define them in ONE source file w/o the extern on the definition.

my_header.h

extern int setting;

my_source.cpp

#include "my_header.h"

int setting = 0;

http://en.wikipedia.org/wiki/Static_%28keyword%29

To show you why, here is a little demo code :

globals.h

#ifndef globals_HPP
#define globals_HPP

#include <cstdio>

struct ALLEGRO_DISPLAY;

static ALLEGRO_DISPLAY* display = 0;

void mod2();

#endif // globals_HPP

module1.cpp

#SelectExpand
1 2#include "globals.h" 3 4int main(int argc , char** argv) { 5 6 7 (void)argc; 8 (void)argv; 9 10 printf("Main says address of display is %p\n" , &display); 11 12 mod2(); 13 14 return 0; 15}

module2.cpp

#include "globals.h"


void mod2() {
   printf("Module 2 says address of display is %p\n" , &display);
}

Output of program :

c:\ctwoplus\progcode\allegro5\test\statictest>statictest.exe
Main says address of display is 00407020
Module 2 says address of display is 00407024

So each module is getting its own copy of each variable declared as static. That's not what you want. You want one instance of each variable shared among modules, and that's what extern is for.

Also, there were a ton of warnings compiling your code. I understand it is still in development but you should enable warnings and remove as many of them as you can.

mir0slaw
Member #15,677
July 2014

That's strange, I only have had one warning before starting to mess with the externs, it was the warning that my switch does only have default and doesn't have any cases.
However, there's a lot of work for me to do with the shared variables then. I hope I've understood how to use them thanks to your comment. If not, I'll learn more about extern and static on the internet, which is a good idea anyway.
Thanks to both of you for your time. I'll update this post probably today, telling you the results of my work.

Cheers

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

Go to: