Sign in to follow this  
darenking

SOLVED-Allegro: probs with vector of BITMAPs

Recommended Posts

Howdy. I'm storing my sprite BITMAPs in a vector of pointers like this:
BITMAP* newSprite = new BITMAP;
m_Sprites.push_back( newSprite );
newSprite = create_bitmap(width, height);


I've stored other objects in vectors and had no problems accessing their methods. But I'm having trouble getting these sprite BITMAPs to draw to the screen. I've tried drawing the first sprite like this: draw_sprite(buffer, m_Sprites[0], x, y); But the program just crashes horridly. Something to do with dereferencing mebbe? [Edited by - darenking on July 21, 2005 3:42:35 PM]

Share this post


Link to post
Share on other sites
Nope, nothing to do with dereferencing. You cannot create a BITMAP* by allocating memory with new, which just allocates memory leaving all data in undefined state (for example what is the width of the BITMAP?). You must use either create_bitmap(int width, int height) or load_bitmap(const char *filename, PALETTE *pal), and destroy_bitmap(BITMAP *bmp) when you no longer need it. More info in the manual.

Share this post


Link to post
Share on other sites
Sorry, I just left out some of the details in the example. I'm certainly creating the BITMAPs correctly. The full code is more like this:


BITMAP* newSprite = new BITMAP;
m_Sprites.push_back( newSprite );
newSprite = create_bitmap(width, height);
blit( from, newSprite, startX, startY, 0, 0, width, height );



The problem is that I don't know how to reference the contents of the vector in such a way that I can draw the BITMAP to the screen, something like:

draw_sprite(buffer, m_Sprites[0], x, y);

Here I'm attempting to draw the first BITMAP in the vector, presumably referred to as 0.

Any ideas?

Share this post


Link to post
Share on other sites
BITMAP objects should only be created with create_bitmap(). In your code, both the 'new' and the 'create_bitmap()' statements allocate memory, which means you are leaking memory (delete the 'new' one, it's not needed). This should not crash your app however.

Really, I've never encountered any problems using BITMAP stored in a vector. Try putting in the very top of your file '#define DEBUGMODE' and place TRACE() statements between every function call in this block to see where exactly it crashes. Keep in mind that the create_bitmap() statement may fail if there is not enough memory, and blitting to and from an invalid object will crash (is your buffer valid?)

Share this post


Link to post
Share on other sites
I know nothing about allegro, bur your codes looks rather strange to me.

// First you allocate empty space
BITMAP* newSprite = new BITMAP;

// The you store that pointer in your vector
m_Sprites.push_back( newSprite );

// Then you create bitmap overwriting your old pointer,
// but not storing it in the vector.
newSprite = create_bitmap(width, height);

What you get is a vector with a pointer to allocated memory (Which is an empty, uninitialized bitmap), and another pointer to a bitmap which is never used.

When you are attempting to draw with:
draw_sprite(buffer, m_Sprites[0], x, y);
you are accessing the uninitialized bitmap, which contains junk values, which SHOULD crash you program horribly. So this behaviour is expected.

I believe the proper code should look like this:
BITMAP *newSprite = create_bitmap(width, height);
m_Sprites.push_back( newSprite );

then you can draw it with
draw_sprite(buffer, m_Sprites[0], x, y);

And when you are done with the bitmap you deallocate it with
destroy_bitmap(BITMAP *bmp);
as Fiddler said.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
crudbreeder is perfectly right.

You should NEVER allocate BITMAPS * using new. create_bitmap does all the allocation you need.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
And BITMAP is a C struct not a C++ class, so it doesn't have any constructor.
While a "Class * pClass = new Class" would allocate memory and call the default constructor of "Class", "new Struct" will only allocate a block of sizeof(Struct) bytes. This is why your BITMAP * points to a zone of garbage and causes your program to crash.

Share this post


Link to post
Share on other sites
Please, take a look at the code you've posted. You first create a BITMAP with new and then you create it _again_ with create_bitmap(). You must _only_ use create_bitmap().

And, please take the time to find out where exactly it crashes. Doing error checking would be useful, ie assert that your bitmaps are not NULL before using them.

EDIT: What the previous AP said is the reason why you should not use 'new' or malloc() with a BITMAP*. Always use the create_bitmap() and load_bitmap() functions.

EDIT2: Copy and paste crudbreeder's code and see if it works (it should). Try to check what his code does against what your code does.

Share this post


Link to post
Share on other sites
BITMAP* newSprite = new BITMAP;
m_Sprites.push_back( newSprite );
newSprite = create_bitmap(width, height);


Should be


m_Sprites.push_back(create_bitmap(width, height));


There should also be a check to make sure it actually got created.

When it comes time to destroy all the sprites, don't even think about using delete. Use something like this instead:

#include <algorithm>

//...

std::for_each(m_Sprites.begin(), m_Sprites.end(), destroy_bitmap);
m_Sprites.clear();

Share this post


Link to post
Share on other sites
You have to have a pointer to an Allegro BITMAP structure because they're all different sizes, and have extra data at the end of the BITMAP structure that varies depending on the types (video, mode-x, memory, sub-bitmap, etc.), kind of like C++ virtual inheritance.

So again, they're not normal C++ objects, you have to use the Allegro BITMAP functions to create, manipulate, and destroy them.

Share this post


Link to post
Share on other sites
Quote:
Original post by smart_idiot
m_Sprites.push_back(create_bitmap(width, height));


Thank you, smart_idiot. That works right away, and my code is now two lines shorter, which is always a good sign.

Do I really have to delete them? I mean, you don't have to delete integers, for example. Why would you have to delete BITMAPs?

Also, if I want to access that new BITMAP immediatlely, what's the best way to access it? It's the newest BITMAP added to the vector, so maybe just with the vector .size() method? Or is there a better way? (I ask because I'm setting up a clever sprite creation system, that may for example create ten people sprites wearing t-shirts, then draw different things on their t-shirts, for example, effectively creating ten different sprites from just a few bitmaps. So I may want to create the sprite then blit various different images onto it as soon as I've created it.)

Share this post


Link to post
Share on other sites
The last created bitmap would be m_Sprites.back().

And yes, you have to destroy them, using destroy_bitmap, NOT using the delete keyword. As I said above, they don't have a destructor. Allegro allocates a bunch of memory for them, and doesn't release it until you tell it you're done with it.

Share this post


Link to post
Share on other sites
Quote:
Original post by smart_idiot
std::for_each(m_Sprites.begin(), m_Sprites.end(), destroy_bitmap);
m_Sprites.clear();


Am I right in thinking that the first line deletes the actual bitmaps, then the second line clears the vector?

I've put these two lines in and now when I exit the program it crashes (ie instead of ending cleanly a window appears with an ugly message saying the program "has encountered a problem and needs to close"). Without the above two lines, the program ends cleanly, as expected (though perhaps with some memory wasted as I am not destroying the bitmaps).

It's certainly not the algothingy that's making it crash. I've just tried deleting the bitmaps like this and it still crashes:

for ( int sprite=0 ; sprite < m_Sprites.size() ; sprite++ )
destroy_bitmap( m_Sprites[sprite] );

In case it is relevant, the vector is created like this:
std::vector<BITMAP*> m_Sprites;

This presumably means that the vector holds pointers to BITMAPs rather than BITMAPs. Does this change the way I have to destroy the BITMAPs?

[Edited by - darenking on July 9, 2005 5:01:34 AM]

Share this post


Link to post
Share on other sites
Quote:
Original post by darenking
It's certainly not the algothingy that's making it crash. I've just tried deleting the bitmaps like this and it still crashes:

for ( int sprite=0 ; sprite < m_Sprites.size() ; sprite++ )
destroy_bitmap( m_Sprites[sprite] );

Nothing wrong there. Somewhere in the rest of your code, perhaps one or more of those pointers becomes invalid (say, you inadvertantly destroy the bitmap some other way) ...

Share this post


Link to post
Share on other sites
Where should I destroy the BITMAPs? In the destructor for the object that creates them? That's where I've put it, in the destructor for my World object. Here's the destructor. Notice it logs messages to my message object, saying how many sprites it is trying to destroy etc. At least, it tries to, but the program crashes before the message object has a chance to save the log file.


World::~World()
{
g_Message.Log("~[World.~World] World object destroyed");

//delete my Character objects, this works just fine...
for ( int character = 0 ; character < m_Characters.size() ; character++ )
delete m_Characters[character];

//delete my Actor objects, this works too...
for ( int actor = 0 ; actor < m_Actors.size() ; actor++ )
delete m_Actors[actor];

//this is where the pie hits the fan. If I remove this stuff, the program runs fine. With it in, the program crashes at this point...
g_Message.Log("~[World.~World] ****** TOTAL SPRITES before destroy", m_Sprites.size());

for ( int sprite = 0 ; sprite < m_Sprites.size() ; sprite++ )
{
g_Message.Log("~[World.~World] ****** Destroying sprite number", sprite);
destroy_bitmap( m_Sprites[sprite] );
}

g_Message.Log("~[World.~World] ****** TOTAL SPRITES after destroyed", m_Sprites.size());

}



Share this post


Link to post
Share on other sites
Quote:
Original post by darenking
Where should I destroy the BITMAPs? In the destructor for the object that creates them? That's where I've put it, in the destructor for my World object.

One question: is the World object global? Because if so, I've found your problem ...

Share this post


Link to post
Share on other sites
Quote:
Original post by darenking
Nice try! The only global object though is the message logging object, Message.

In general, should BITMAPs be destroyed in the desctructor for the object that creates them?

Sorry; missed this response.

It's good coding practice in general that what creates destroys, so yes. As long as all the creating and destroying is done between allegro_init() and allegro_exit(), and you aren't somehow trying to call destroy_bitmap() on a pointer to a bitmap that has been destroyed already (common sense on that last one), you should be fine ...

Share this post


Link to post
Share on other sites
Quote:
Original post by darenking
Is there a way to check that the pointer is valid still?

Not without writing a semi-elaborate wrapper class for managing memory, since simply checking for null ain't gonna cut it. I'd need to know more about your engine worked to comment more intelligently than that.

Share this post


Link to post
Share on other sites
Do you fancy taking a look? (Or anybody else?) I could post a link to a zipped file. I wrote it in DevC++. It's very well structured, overall, so you'd probly have no problems poking around.

In a sense it's a virtual arcade machine as there are objects for physical things like joystick and screen as well as the things in the game.

main() creates an Engine object, which creates the Joystick and Screen objects, and also a World object, which contains everything that exists in a level of a game, eg the Maze, various Actor objects, and the BITMAPs to display them. (The Game object will control stuff like what level you're on. Or maybe I will just store this in Engine.)

http://www.actionanimals.co.uk/darenkingsgame.zip

Well here's a link to a zip with all the files if anyone fancies having a nose. It's the World object that causes the problem, creating BITMAPs and crashing when I try to destroy them.

If you run the game, you'll just see robot legs walking around, as I'm doing the bodies as separate Actor objects. Press TAB during the game to see an outline of the physical shape of the Actor objects. I have a Message object (my only global). TAB will show the Screen messages. Press L to see the Message Log; objects log to this when they are created, destroyed etc.

Arrow keys and CTRL to move.

When you press ESC to quit, the program should crash. This is caused by the line destroy_bitmap( m_Sprites[sprite] );
in the destructor for the World object.

When the game ends, it's supposed to save a log.txt file in the folder final/data, but it doesn't save this file if the program crashes.

http://www.actionanimals.co.uk/darenkingsgame.zip


Share this post


Link to post
Share on other sites
Well, for one thing, you're exiting allegro before you delete the bitmaps. Your Engine object is a local variable in main(), and is destroyed _after_ your allegro_exit() call. The destruction of the Engine invokes the destructor of World, which deletes (or at least tries to delete [grin]) the bitmaps. So what actually happens is you're calling Allegro back to work after you told it to go home. Nobody likes that :p

Recommended quick fix:
int main()
{
allegro_init();

int mode=setup();

if ( mode == -1 )
{
allegro_exit();
return 0;
}

Engine * engine = new Engine(mode); // <----- *** CHANGED ***

while(! key[KEY_ESC])
{
// *** CHANGED . to -> ***
engine->Input();
engine->Update();
engine->Output();
}

// *** ADDED ***
delete engine;

allegro_exit();
return 0;

}END_OF_MAIN();



I believe I'm a little late with this reply, but ehhh...good luck :)

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this