Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » Multiple layer scrolling problem

This thread is locked; no one can reply to it. rss feed Print
Multiple layer scrolling problem
Giorgans
Member #23,450
July 2022

Hi im making my first project-game with the allegro library.Im trying to make the first level of super mario bros for NES on C++ .Im using an Intel Mac with the latest version of allegro 5 that is supported.
Im trying to do basic scrolling without any collision at the moment.Im using different bitmap layer for the background and different for the action(terrain) and in my render function i draw each layer to the backbuffer after i drew the key color first.
My problem is that while scrolling sometimes the tiles of both the background and the terrain are staying at the same place as before while drawing it again at the place it supposed to be after i scrolled. After a lot of running I noticed that this happens only when I scroll with more than 1 layers.While scrolling with 1 layer i don't have that problem. I will attach a screenshot of a moment when it's ok and when it has the problem . The weirdest thing is that its happening randomly and not at the same place of the level when it happens..
Any idea what is the cause of that? ???

Dizzy Egg
Member #10,824
March 2009
avatar

It could be that you're not clearing the back layer properly, or it could be something else, but you'll need to show some code to get the help you need.

----------------------------------------------------
Please check out my songs:
https://soundcloud.com/dont-rob-the-machina

Giorgans
Member #23,450
July 2022

Yes of course . This is my render function which runs in my game loop:

#SelectExpand
1void Rendering(void) { 2 if(window == nullptr){ 3 window = al_create_display(DISPLAY_W,DISPLAY_H); 4 al_set_display_icon(window, al_load_bitmap(ICON_FILE_PATH)); 5 } 6 if (background == nullptr) 7 background = new TileLayer(MAX_HEIGHT, MAX_WIDTH, al_load_bitmap(TILESET_FILE_PATH), BACKGROUND_CSV_FILE_PATH); 8 if (terrain == nullptr) 9 terrain = new TileLayer(MAX_HEIGHT, MAX_WIDTH, al_load_bitmap(TILESET_FILE_PATH), TERRAIN_CSV_FILE_PATH); 10 11 al_set_target_backbuffer(window); 12 al_clear_to_color(KEY_COLOR); 13 background->Display(al_get_backbuffer(window), DisplayArea); 14 terrain->Display(al_get_backbuffer(window), DisplayArea); 15 if (displayGrid) 16 terrain->GetGrid()->Display(al_get_backbuffer(window), DisplayArea); 17 al_flip_display(); 18 al_unlock_bitmap(al_get_backbuffer(window)); 19}

the layer class:

#SelectExpand
1class TileLayer { 2private: 3 Index map[MAX_HEIGHT][MAX_WIDTH] ; 4 GridLayer *grid; 5 Dim totalRows = 0, totalColumns = 0; 6 ALLEGRO_BITMAP *tileSet = nullptr; 7 Rect viewWin{0,0,DISPLAY_W,DISPLAY_H}; 8 ALLEGRO_BITMAP *dpyBuffer = nullptr; 9 bool dpyChanged = true; 10 Dim dpyX = 0, dpyY = 0; 11 void Allocate(void) { 12 this->dpyBuffer = al_create_bitmap((MAX_WIDTH + 2) * TILE_WIDTH, (MAX_HEIGHT + 2) * TILE_HEIGHT); 13 } 14public: 15 ALLEGRO_BITMAP *GetBuffer(){return this->dpyBuffer;} 16 GridLayer *GetGrid(){return this->grid;} 17 void SetTile (Dim row,Dim col, Index index) { this->map[row][col] = index; } 18 Index GetTile (Dim row,Dim col) { return this->map[row][col]; } 19 const Point Pick (Dim x, Dim y) const { 20 return { DIV_TILE_WIDTH(x + viewWin.x), 21 DIV_TILE_HEIGHT(y + viewWin.y) }; 22 } 23 const Rect& GetViewWindow (void) const { return viewWin; } 24 void SetViewWindow (const Rect& r) { viewWin = r; dpyChanged = true; } 25 void Display (ALLEGRO_BITMAP *dest, const Rect& displayArea); 26 ALLEGRO_BITMAP *GetBitmap (void) const { return dpyBuffer; } 27 int GetPixelWidth (void) const { return viewWin.w; } 28 int GetPixelHeight (void) const { return viewWin.h; } 29 unsigned GetTileWidth (void) const { return DIV_TILE_WIDTH(viewWin.w); } 30 unsigned GetTileHeight (void) const { return DIV_TILE_HEIGHT(viewWin.h); } 31 void Scroll (float dx, float dy); 32 Dim getCols(){return totalColumns;} 33 bool CanScrollHoriz (float dx){ 34 //Checking if the level has ended 35 if(GetViewWindow().x + dx == (getCols()*TILE_WIDTH - 20*TILE_WIDTH)) return true; 36 else return false; 37 } 38 bool CanScrollVert (float dy) const; 39 auto ToString (void) const -> const std::string; 40 bool FromString (const std::string&); 41 void Save (const std::string& path) ; 42 bool Load (const std::string& path); 43 FILE* WriteText (FILE* fp) const { fprintf(fp, "%s", ToString().c_str()); return fp; } 44 bool ReadText (std::string path); 45 TileLayer (Dim rows, Dim cols, ALLEGRO_BITMAP *tileSet,std::string path); 46 ~TileLayer (); 47};

And the implementation of the display function:

#SelectExpand
1void TileLayer::Display(ALLEGRO_BITMAP *dest, const Rect &displayArea){ 2 Allocate(); 3 if (dpyChanged) { 4 auto startCol = DIV_TILE_WIDTH(viewWin.x); 5 auto startRow = DIV_TILE_HEIGHT(viewWin.y); 6 auto endCol = DIV_TILE_WIDTH(viewWin.x + viewWin.w - 1); 7 auto endRow = DIV_TILE_HEIGHT(viewWin.y + viewWin.h - 1); 8 dpyX = MOD_TILE_WIDTH(viewWin.x); 9 dpyY = MOD_TILE_WIDTH(viewWin.y); 10 dpyChanged = false; 11 for (Dim row = startRow; row <= endRow; ++row) 12 for (Dim col = startCol; col <= endCol; ++col) 13 PutTile(GetBuffer(), MUL_TILE_WIDTH(col - startCol), MUL_TILE_HEIGHT(row - startRow), tileSet,GetTile(row, col)); 14 } 15 BitmapBlitScaled(GetBuffer(),{dpyX,dpyY, viewWin.w,viewWin.h},dest,{0,0}); 16 al_destroy_bitmap(GetBuffer()); 17}

Basic scroll func for testing:

void TileLayer::Scroll (float dx, float dy){
    auto w = GetViewWindow();
    w.x+=dx;
    w.y+=dy;
    SetViewWindow(w);
    this->GetGrid()->SetViewWindow(w);

}

And the scroll part from the input function

        else if (event.type == ALLEGRO_EVENT_KEY_CHAR && !terrain->CanScrollHoriz(8)) {
            int dx = 4;
            if (event.keyboard.keycode == ALLEGRO_KEY_RIGHT) {
                    background->Scroll(dx, 0);
                    terrain->Scroll(dx, 0);
                break;
            }

Dizzy Egg
Member #10,824
March 2009
avatar

Quick tip, you should wrap your code in "<code>" tags to make it easier to read ;)

I can't see anywhere that you clear the dpyBuffer in the tile class. I see you clear the display backbuffer, but you will need to clear the dpyBuffer so when you draw the tiles, the old tiles will be cleared first. So before you call PutTile(), you should be calling ClearTiles(), that will clear the previous dpyBuffer.

EDIT:

Nevermind, I guess you're drawing all the tiles in one big hit....

----------------------------------------------------
Please check out my songs:
https://soundcloud.com/dont-rob-the-machina

Giorgans
Member #23,450
July 2022

Im sorry for the inconvenience. Just edited it .
I thought i could clear the buffer with "al_destroy_bitmap(GetBuffer());" am i wrong? ???

edit: i have the tileset bitmap and i draw the tiles that supposed to be visible. from the tileset bitmap

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

Giorgans
Member #23,450
July 2022

Yes i know , i just misunderstood about clear. Although i already use clear .before each layer i use clear to color the key color that i ignore from the tiles. And i use al_destroy_bitmap to free the recourses of the layer's buffer

Dizzy Egg
Member #10,824
March 2009
avatar

In your Display() function, you're creating bitmaps (Allocate()) and destroying bitmaps - this is not the way.

I also suspect there's some issues with the math that you eventually use when calling BlitBitmapScaled.

I can't quite work it out, but it looks like you're calling BlitBitmapScaled to draw to the display backbuffer, but shifting the x/y drawing co-ordinates using dpx/dpy. The layers should always be drawn at 0/0, but the tiles should be drawn onto their bitmaps to imitate scrolling.

You shouldn't be destroying the tile layers, you should create them at startup, and then draw->clear->draw->clear.

----------------------------------------------------
Please check out my songs:
https://soundcloud.com/dont-rob-the-machina

Giorgans
Member #23,450
July 2022

Just rechecked it taking in mind your advice and i think it works now.Thank you very much! I really appreciate your time to help.About the math part i think it works good , it was the first thing i worked before i continue to multiple layers. It works but I'll do more testing later to be 100% sure.

I removed the al_destroy_bitmap() (although im thinking using it to destroy the buffer when i call the destructor of the class)
and then added this part when the dpy is changed:

The updated display function:

void TileLayer::Display(ALLEGRO_BITMAP *dest, const Rect &displayArea){
    if(Buffer()==nullptr) Allocate();
    if (dpyChanged) {
        al_set_target_bitmap(GetBuffer());
        al_clear_to_color(TRAN);
        al_unlock_bitmap(GetBuffer());
        /**
             dpychange math
        **/

}

    BitmapBlitScaled(Buffer(),{x,y,w,h},destination,0,0);
}

Dizzy Egg
Member #10,824
March 2009
avatar

Cool, glad it’s all working!

----------------------------------------------------
Please check out my songs:
https://soundcloud.com/dont-rob-the-machina

Peter Hull
Member #1,136
March 2001

What's the difference between Buffer() and GetBuffer()?

Also you shouldn't al_unlock_bitmap unless you locked it at some point (which I don't think you did?)

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

Go to: