• Advertisement
Sign in to follow this  

Allegro: BITMAP and create_sub_bitmap problem crashes game

This topic is 4040 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi guys! I have an Animation class, a class that has static members and serves as the main point of entry to play the game and other classes such as enemies. Now, I also have a BitmapManager class which looks like this.
#include "globals.h"

BitmapManager::BitmapManager()
{
    //can never be instantiated outside the class!!!
}    

BitmapManager::~BitmapManager()
{
} 

BITMAP* BitmapManager::addBitmap(char* key, BITMAP* bitmap)
{
    bitmapTable.insert( std::pair<char*, BITMAP*>(key, bitmap) );
    
    return bitmapTable[key];
}

BITMAP* BitmapManager::addBitmap(char* key, char* fileName)
{
    BITMAP* bitmap = load_bitmap(fileName, NULL);
    
    bitmapTable.insert( std::pair<char*, BITMAP*>(key, bitmap) );   

    return bitmap;
}    

BITMAP* BitmapManager::getBitmap(char* key)
{ 
    std::cout << "BITMAP: " << key << (bitmapTable[key] == NULL ? " NULL" : " SOMETHING") << std::endl;
        
    return bitmapTable[key];
}    

bool BitmapManager::lookupBitmap(char* key)
{   
    if ( bitmapTable[key] != NULL )
    {
        return true;
    }
    else
    {
        return false;
    }        
}    

void BitmapManager::removeBitmap(char* key)
{
    std::map<char*, BITMAP*>::iterator it = bitmapTable.find(key);
    
    BITMAP* b = it->second;
    
    if ( it != bitmapTable.end() )
    {
        bitmapTable.erase(it);
        destroy_bitmap(b);
    }    
    
}
    
void BitmapManager::removeAll()
{
    std::map<char*, BITMAP*>().swap(bitmapTable);
}    

std::map<char*, BITMAP*> BitmapManager::bitmapTable;




and the Animation class looks like this.
#include "globals.h"

Animation::Animation(BITMAP* bitmap, int frameDelay, int maxFrames, FrameExtractionMethods extractionMethod)
{
    this->frameDelay = frameDelay;
    this->maxFrames = maxFrames;
    
    currentFrame = 0;
    frameTick = 0;
    
    //get bitmap dimensions
    int bw = bitmap->w;
    int bh = bitmap->h;

    //set frame dimensions
    int frameWidth = (int)bw / maxFrames; //e.g. 200 / 4 == 50
    int frameHeight = bh;
    
    //EXTRACT IMAGES BY_WIDTH
    int currentX = 0; //to determine the extraction coordinate
    
    //std::cout << "Extracting animation frames...";
    
    //std::cout << "bitmap->w: " << bw << std::endl; 
    //std::cout << "maxFrames: " << maxFrames << std::endl;
    //std::cout << bw << " / " << maxFrames << std::endl;  
    //std::cout << "frameWidth: " << frameWidth << std::endl;
    
    
    for (int i = 0; i < maxFrames - 1; i++)
    {
        animationFrames = create_sub_bitmap(bitmap, currentX, 0, frameWidth, frameHeight);
        currentX += frameWidth;
        //std::cout << "currentX: " << currentX << "\n";
    }    
    
}    

Animation::~Animation()
{
    delete[] animationFrames;
    //std::cout << "Deleting Animation...\n";
}    

void Animation::previousFrame()
{
    if (currentFrame > 0)
    {
        currentFrame--;
    }    
}

void Animation::nextFrame()
{
    if (currentFrame < maxFrames )
    {
        currentFrame ++;
        
        if ( currentFrame == maxFrames )
        {
            currentFrame = 0;
        }    
    }   
}   

void Animation::animate()
{
    //std::cout << "Animating on frame: " << currentFrame << "\n";
    
    if (frameTick == frameDelay)
    {
        frameTick = 0;
        nextFrame();
    }
    
    frameTick++;        
}   

BITMAP* Animation::getCurrentAnimationFrame()
{
    return animationFrames[currentFrame];
}      





and before, when I create an instance of the animation class, I would do something like this...
    flyingAnimation = new Animation(BitmapManager::addBitmap("ASTEROID_SPRITE_FLYING", "graphics/asteroid_flying.bmp"),  10, 4, BY_WIDTH);




but I thought it was too heavyweight because my addBitmap makes a call to BITMAP* bitmap = load_bitmap(fileName, NULL) everytime the function addBitmap is called. So I thought I'd change my strategy and just load the bitmap first. In my PlayState class(a singleton, this is being put in a state stack) constructor, I load these images
#include "globals.h"

PlayState::PlayState()
{
    currentLevel = 1;

    //create buffer image
    GameManager::buffer = create_bitmap(SCREEN_W, SCREEN_H);
    
    //load sprites
    BitmapManager::addBitmap("SHIP_SPRITE", "graphics/fs.bmp");
    BitmapManager::addBitmap("VULCAN_SPRITE", "graphics/vulcan.bmp");
    BitmapManager::addBitmap("ASTEROID_SPRITE_FLYING", "graphics/asteroid_flying.bmp");    
    
    //ship = new Ship(BitmapManager::SHIP_SPRITE, 50, 50, 50, 32, 1);
    ship = new Ship(BitmapManager::getBitmap("SHIP_SPRITE"), 50, 50, 50, 32, 3, 5, 1000);
    
    //add a weapon for the ship
    ship->addWeapon(VULCAN_GUN, WeaponFactory::getWeapon(VULCAN_GUN) );
    ship->setCurrentWeapon(VULCAN_GUN);
}    

PlayState::~PlayState()
{
    BitmapManager::removeAll();
    
    destroy_bitmap(GameManager::buffer);
    
    delete ship;
}

PlayState* PlayState::getInstance()
{
    if (instance == NULL)
    {
        instance = new PlayState();
    }
    
    return instance;    
}    

void PlayState::init()
{
}

void PlayState::pause()
{
}

void PlayState::run()
{     
    loadLevel(currentLevel);
    
    //begin main game loop
    while ( !key[KEY_ESC] )
    {        
        while (GameTimer::game_ticker > 0)
        {
            //move left
            if ( key[KEY_A] )
            {
                ship->move( ship->getX() - ship->getSpeed(), ship->getY() );
            }    
            //move down
            if ( key[KEY_S] )
            {
                ship->move( ship->getX(), ship->getY() + ship->getSpeed() );
            }    
            //move right
            if ( key[KEY_D] )
            {
                ship->move( ship->getX() + ship->getSpeed(), ship->getY() );
            }    
            //move up
            if ( key[KEY_W] )
            {
                ship->move( ship->getX(), ship->getY() - ship->getSpeed() );
            }
            //fire weapon
            if ( key[KEY_K] )
            {
                ship->fireWeapon();
            }
            //change weapon
            if ( key[KEY_TAB] )
            {
                 //ship->fireWeapon();
            }
            //fire special
            if ( key[KEY_L] )
            {
            }
            
            //exit to menu
            if ( key[KEY_ESC] )
            {
                break;
            }    
            
            //scroll the background
            bg->update();
            
            //animate the ship
            ship->update();
            
            bg->draw();
            
            //move all enemies
            for(std::vector<Enemy*>::iterator it = EnemyFactory::enemyPool.begin(); it != EnemyFactory::enemyPool.end(); ++it)
            {
                Enemy* e = *it;
                e->update();
                e->draw();
            }    
            
            //move all projectiles
            for (std::vector<Projectile*>::iterator it = ProjectileFactory::projectilePool.begin(); it != ProjectileFactory::projectilePool.end(); ++it)
            {                    
                if ( (*it)->isVisible() == true )
                {
                    (*it)->update();
                    (*it)->draw();
                }
                else
                { 
                    Projectile* t = *it;
                    it = ProjectileFactory::projectilePool.erase(it);
                    // we must decrement, because it will be incremented when the loop starts over again
                    --it;
                    delete t;
                }  
            }    
     
            
            //recharge weapon
            ship->getCurrentWeapon()->recharge();
            
            //control game speed
            GameTimer::game_ticker--;                                   
            
            ship->draw();
        
            textout_ex(GameManager::buffer, font, "Life: ", 10, 10, makecol(0, 0, 255), -1);
            textprintf_centre_ex(GameManager::buffer, font, SCREEN_W/2, 10, makecol(255, 255, 255), makecol(0, 0, 0), "Current Weapon: %s", ship->getCurrentWeapon()->getWeaponName() );                      
            textprintf_centre_ex(GameManager::buffer, font, SCREEN_W/2, 176, makecol(255, 255, 255), makecol(0, 0, 0), "num=%d", GameTimer::game_ticker);
        
            //blit buffer
            GameManager::blitBuffer();                      
        }    
              
    }
    
    unloadLevel(currentLevel);    
}

void PlayState::update()
{
}

void PlayState::clean()
{
}                        

void PlayState::loadLevel(int level)
{     
    PACKFILE* levelMap;
    
    std::string line;
    char buf[255];
    
    int currX = 0;
    int currY = 0;
    int xIncrement = (SCREEN_W / 20); 
    int yIncrement = (SCREEN_H / 20);
    
    //std::cout << "xIncrement: " << xIncrement << std::endl;
    //std::cout << "yIncrement: " << yIncrement << std::endl;
    
    switch(level)
    {
        case 1:
            levelMap = pack_fopen("levels/level_01.lvl", "r");
            
            if (!levelMap)
            {
                allegro_message("COULD NOT CREATE LEVEL!: %s", allegro_error);
            }    
            
            while ( pack_feof(levelMap) == 0 )
            {
                //read a line from the file (levelMap)
                line = pack_fgets(buf, sizeof(buf), levelMap);
                
                //loop through each character in the line
                for (int i = 0; i < line.length(); i++)
                {           
                                     
                    //only an empty space
                    if ( line.at(i) == ' ')
                    {
                        currX += xIncrement;    
                    }
                    else //create an object
                    {
                        Enemy* e = EnemyFactory::getEnemy( line.at(i) );
                        //set initial position of the enemy
                        e->move(currX, currY);    
                        EnemyFactory::enemyPool.push_back(e);
                        
                        currX += xIncrement;
                    }        
                }
                
                currX = 0;
                currY += yIncrement;    
            }    
            
            bg = new GameBackground(BitmapManager::addBitmap("SPACE_BACKGROUND", "graphics/bg.bmp"), HORIZONTAL_LEFT, 3, SCREEN_W, SCREEN_H);
            
            break;
        default:
            break;        
    }
    
    if (levelMap)
    {
        pack_fclose(levelMap);
    }                
}

void PlayState::unloadLevel(int level)
{
    switch(level)
    {
        case 1:
            std::vector<Enemy*>().swap(EnemyFactory::enemyPool);
            delete bg;
            break;
        default:
            break;
    }    
}

PlayState* PlayState::instance;
int PlayState::currentLevel;




So instantiating an Animation object is now changed from
    flyingAnimation = new Animation(BitmapManager::addBitmap("ASTEROID_SPRITE_FLYING", "graphics/asteroid_flying.bmp"),  10, 4, BY_WIDTH);




to
flyingAnimation = new Animation(BitmapManager::getBitmap("ASTEROID_SPRITE_FLYING"),  10, 4, BY_WIDTH);




Now here is where the problem occurs, in the loadLevel function, when I instantiate an Enemy using a factory it would seem that I'm referencing to a NULL object and my game crashes. How can this be? Before any instance of the Enemy class is created, the BITMAP(ASTEROID_SPRITE_FLYING) is in the map. Then suddenly, when I instantiate an object it's gone(NULL)! I can see if the BITMAP* pointer points to something or NULL because I have a cout statement.
BITMAP* BitmapManager::getBitmap(char* key)
{ 
    std::cout << "BITMAP: " << key << (bitmapTable[key] == NULL ? " NULL" : " SOMETHING") << std::endl;
        
    return bitmapTable[key];
}   


Here is the code for my Enemy, actually asteroid class(there is nothing in the Enemy class but an empty destructor).

#include "globals.h"

Asteroid::Asteroid(int x, int y, int w, int h, int sp, Status stat)
{
    this->x = x;
    this->y = y;
    width = w;
    height = h;
    speed = sp;
    status = stat;

    flyingAnimation = new Animation(BitmapManager::getBitmap("ASTEROID_SPRITE_FLYING"),  10, 4, BY_WIDTH);
    //dyingAnimation = new Animation();
    currentAnimation = flyingAnimation;
}

Asteroid::~Asteroid()
{
    std::cout << "destroying asteroid...\n";
    delete flyingAnimation;
}

void Asteroid::update()
{
    currentAnimation->animate();
    move(x - speed, y);
}

void Asteroid::move(int x, int y)
{
    this->x = x;
    this->y = y;
}

void Asteroid::draw()
{
    draw_sprite(GameManager::buffer, currentAnimation->getCurrentAnimationFrame(), x, y);
}    

/*
void Asteroid::draw(BITMAP* buffer)
{
        
} 
*/                   




Please help guys... Thanks! [Edited by - armond on January 30, 2007 7:44:43 AM]

Share this post


Link to post
Share on other sites
Advertisement
I haven't looked at your code, but perhaps the following warning from the manual explains the crashes:
Quote:
Remember to free the sub bitmap before freeing the parent bitmap to avoid memory leaks and potential crashes accessing memory which has been freed.

To be more clear, you have to keep the parent bitmap around for as long as the sub bitmaps are around. And you need to destroy the sub ones before destroying the parent.

Share this post


Link to post
Share on other sites
Funny thing though is... I'm not even destroying anything from any point that the crash happens...?

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement