Scrolling backgrounds

This is a discussion on Scrolling backgrounds within the Game Programming forums, part of the General Programming Boards category; What is the logic behind scrolling backgrounds? I'm doing a 1942 clone so the back ground isn't tiled or anything ...

  1. #1
    ---
    Join Date
    May 2004
    Posts
    1,379

    Scrolling backgrounds

    What is the logic behind scrolling backgrounds? I'm doing a 1942 clone so the back ground isn't tiled or anything it is just one image the same size as the screen and I want the effect that the plane is constantly moving (along the y axis). Any ideas?

    [edit]
    Before somebody talks about DX matrices, I'm back to using SDL again because I have myself a GP2x on order
    Last edited by sand_man; 11-15-2005 at 06:02 AM.

  2. #2
    Super Moderator VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,598
    Alter the texture coords of the 4 vertices making the screen quad. This will scroll the image.

  3. #3
    ---
    Join Date
    May 2004
    Posts
    1,379
    This is just pure SDL no OpenGL so there are no tex coords although that would make my life much simpler.

  4. #4
    Registered User
    Join Date
    Jan 2005
    Posts
    847
    Create a Bitmap that is the height of the screen and with the width of how far it can be scrolled. Then blit an area the size of the screen from the bitmap to the screen and update the y co-ordinates from the source bitmap.

  5. #5
    ---
    Join Date
    May 2004
    Posts
    1,379
    I don't really understand what you are saying, Quantum1024. It has to scroll continuously as in one bitmap scrolling, never ending.

  6. #6
    Super Moderator VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,598
    Well sandman then you will have to code your own scrolling function. It is just a matter of finding the starting offset into the image. You know the image on screen always starts at 0,0 or the upper left corner - it's just the starting offset into the image that changes.

    Code:
    ...
    //Screen size
    DWORD dwTotalScreenSize=dwScreenWidth*dwScreenHeight*dwScreenPitch;
    
    //Screen offset - for the screen bitmap
    DWORD dwScreenCurrentOffset=0;
    
    //Image offsets - for the image bitmap (memory bitmap)
    //Starting offset into the image based on scrolling values
    DWORD dwImageStartOffset=dwScrollY*dwImageWidth+dwScrollX;
    
    //Moves down one row in memory
    DWORD dwImageDownOneRow=dwImageWidth;
    
    //Used for loop variable so we don't corrupt dwStartImageOffset
    DWORD dwImageCurrentOffset=dwImageStartOffset;
    
    //Tracks width and height
    DWORD dwImageWidthCounter=dwScrollX;
    DWORD dwImageHeightCounter=dwScrollY;
    
    do
    {
       //Copy pixel from Image to screen
       Screen[dwScreenCurrentOffset]=Image[dwImageCurrentOffset];
    
       //Increment screen offset
       dwScreenCurrentOffset++;
       
       //Increment image offset
       dwImageCurrentOffset++;
    
       //Increment width counter
       dwImageWidthCounter++;
    
       //If width counter>image width - recalculate
       if (dwImageWidthCounter>=dwImageWidth)
      {
         dwImageWidthCounter=dwScrollX;
         dwImageStartOffset+=dwImageDownOneRow;
         dwImageCurrentOffset=dwImageStartOffset;
      }
    
      //If height counter>image height - recalculate
      if (dwImageHeightCounter>=dwImageHeight)
      {
         dwImageHeightCounter=0;
         dwImageCurrentOffset=dwScrollX;
      }
    } while (dwScreenCurrentOffset<dwTotalScreenSize);
    ...
    This is a linear algo. It starts at the upper left corner of the screen and moves linearily through video memory or surface memory.

    It also calculates the starting offset for the image based on the current scroll values and checks to see when/if we have gone past the width in which case we recalculate and continue drawing - same for height.

    Since both are linear algo's this should be fairly fast. No recomputation of y*width+x will occur for the image. The screen offset is simply linear because we know the image will always fill the screen. So we can just go from 0 to maxscreensize and we know we have drawn the entire screen.

    Another faster way is to do this in asm and then do a 32-bit copy.

    Code:
    mov    esi,[Image]
    mov    edi,[Screen]
    mov    ecx,[ImageSize]
    rep     stosd
    and    ecx,03h
    rep     stosb
    ...
    Of course this assumes that the image is as large as the screen. If not you must do a little more computation and checking to ensure that no memory overruns or underruns occur.

    If you think it will be useful I could code you a complete assembly language blit function that can scroll on x and y. It will be extremely fast - much faster than anything I could do in C/C++.

  7. #7
    ---
    Join Date
    May 2004
    Posts
    1,379
    I was just thinking, the only ways I could do it would be rather slow. Obviously I'm not looking for a free ride here, I do want to do this myself but if you could code something for me I would use it at least until I could find another way.
    And if I do actually release something in the future all credit will most certainly go to you.

    Is there anything specific you need to know about the pixel data or anything?

  8. #8
    Super Moderator VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,598
    Well first things first. Let's write the fastest code we can in C/C++ so we can get a baseline profile of what we need to beat. I'm not saying that in all cases assembly will be faster so we need to verify that assembly is what we need before attempting to write code for this.

    So code it in C and profile it. Find out what the profiler says and then I will code the assembly version and we will profile it again. If there is no speed gain or perhaps a speed loss then we will make corrections respectively.

    The C version and the assembly version will be very similar and I don't see the need for any more information than this:

    • The pitch of the surface (screen?) being blitted to.
    • The data-width of the pixels in the image (8-bit,16-bit, 32-bit).
    • The width and height of the surface being blitted to.
    • The width and height of the image being blitted.
    • The scroll values for the image.


    Other than that no other information is needed. The assembly version is fairly simple to come up with by examining that C code I provided. I will get to work on it.

    There is another way, though. Split the screen into quads - basically a grid, and then scroll the grid.

  9. #9
    ---
    Join Date
    May 2004
    Posts
    1,379
    >>There is another way, though. Split the screen into quads - basically a grid, and then scroll the grid.

    Good idea. Forget about the other way. I know I can create a grid so I'll do that instead. If it is too slow then I'll get back to you. Thanks for the offer and the help.

  10. #10
    Super Moderator VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,598
    Here is a snippet from my tile engine. This snippet only uses 1 texture but you should get the idea.

    CScreenGrid.cpp
    Code:
    #include "CScreenGrid.h"
    const DWORD TLVertex::FVF=D3DFVF_XYZRHW | 
    D3DFVF_DIFFUSE | D3DFVF_TEX1;
    
    
    void CScreenGrid::Create(IDirect3DDevice9 *Device,std::string File)
    {
      
      DebugFile.Create("CScreenGrid.log","CScreenGrid Debug Dump");
    
    
      DebugFile.File << "Loading texture...";
    
      if (FAILED(D3DXCreateTextureFromFile(Device,File.c_str(),&m_pTexture)))
      {
        MessageBox(0,"Failed to load texture",0,0);
        return;
      }
    
      DebugFile.File << "SUCCESS" << endl;
    
      m_pDevice=Device;
    
      //Create vertex buffer
    
      DebugFile.File << "Calculating grid extents" << endl;
      DebugFile.File << "************************" << endl;
    
      m_dwTotalTiles=m_iNumTilesHoriz*m_iNumTilesVert;
      m_dwTotalVerts=m_dwTotalTiles*6;
      m_dwTotalTriangles=m_dwTotalTiles*2;
    
      DebugFile.File << "Total tiles:" << m_dwTotalTiles << endl;
      DebugFile.File << "Total verts:" << m_dwTotalVerts << endl;
      DebugFile.File << "Total tris:" << m_dwTotalTriangles << endl;
      DebugFile.File << "XIncrement:" << m_iIncrementX << endl;
      DebugFile.File << "YIncrement:" << m_iIncrementY << endl << endl;
    
    
      DebugFile.File << "Creating vertex buffer with " << m_dwTotalVerts << " vertexes...";
    
      HRESULT hr=Device->CreateVertexBuffer(m_dwTotalVerts*sizeof(TLVertex),
                                 D3DUSAGE_WRITEONLY,
                                 TLVertex::FVF,
                                 D3DPOOL_MANAGED,
                                 &m_pVB,
                                 0);
    
      if (hr!=D3D_OK)
      {
        DebugFile.File << "FAILED with " ;
        switch (hr)
        {
          case D3DERR_INVALIDCALL: DebugFile.File << "D3DERR_INVALIDCALL" << endl;break;
          case D3DERR_OUTOFVIDEOMEMORY: DebugFile.File << "D3DERR_OUTOFVIDEOMEMORY" << endl;break;
          case E_OUTOFMEMORY: DebugFile.File << "D3DERR_OUTOFMEMORY" << endl;break;
        }
        return;
      } else DebugFile.File << "SUCCESS" << endl;
    
      DebugFile.File << "Locking vertex buffer in prep for write to card...";
    
      TLVertex *Vertices;
      
      hr=m_pVB->Lock(0,0,(void **)&Vertices,0);
    
      if (hr!=D3D_OK)
      {
        DebugFile.File << "FAILED" << endl;
        return;
      } else DebugFile.File << "SUCCESS" << endl;
      
      //Push vertices out to card
      int vertexnum=0;
      float vx=(float)-m_iIncrementX;
      float vy=(float)-m_iIncrementY;
    
      
      DebugFile.File << "Entering vertex loop" << endl;
      int quad=0;
    
      for (int i=0;i<m_iNumTilesVert;i++)
      {
        for (int j=0;j<m_iNumTilesHoriz;j++)
        {
          
          Vertices[vertexnum]=TLVertex(vx,vy,1.0f,0.0f,0.0f);
          Vertices[vertexnum].diffuse=D3DXCOLOR(1.0f,1.0f,1.0f,1.0f);
          
          Vertices[vertexnum+1]=TLVertex(vx+(float)m_iIncrementX,vy,1.0f,1.0f,0.0f);
          Vertices[vertexnum+1].diffuse=D3DXCOLOR(1.0f,1.0f,1.0f,1.0f);
          
          Vertices[vertexnum+2]=TLVertex(vx,vy+(float)m_iIncrementY,1.0f,0.0f,1.0f);
          Vertices[vertexnum+2].diffuse=D3DXCOLOR(1.0f,1.0f,1.0f,1.0f);
          
          Vertices[vertexnum+3]=TLVertex(vx+(float)m_iIncrementX,vy,1.0f,1.0f,0.0f);
          Vertices[vertexnum+3].diffuse=D3DXCOLOR(1.0f,1.0f,1.0f,1.0f);
          
          Vertices[vertexnum+4]=TLVertex(vx+(float)m_iIncrementX,vy+(float)m_iIncrementY,1.0f,1.0f,1.0f);
          Vertices[vertexnum+4].diffuse=D3DXCOLOR(1.0f,1.0f,1.0f,1.0f);
          
          Vertices[vertexnum+5]=TLVertex(vx,vy+(float)m_iIncrementY,1.0f,0.0f,1.0f);
          Vertices[vertexnum+5].diffuse=D3DXCOLOR(1.0f,1.0f,1.0f,1.0f);
       
          DebugFile.File << "Vertices for quad " << quad << endl;
          DebugFile.File << "************************" << endl;
    
          DebugFile.File << "Current X,Y is " << vx << "," << vy << endl;
    
          DebugFile.File << "Vertex #" << vertexnum << ": ";
          DebugFile.File << vx << "," << vy << ",1.0f,0.0f,0.0f" << endl;
          
          DebugFile.File << "Vertex #" << (vertexnum+1) << ": ";
          DebugFile.File << vx+(float)m_iIncrementX << "," << vy << ",1.0f,1.0f,0.0f" << endl;
          
          DebugFile.File << "Vertex #" << (vertexnum+2) << ": ";
          DebugFile.File << vx << "," << vy+(float)m_iIncrementY << ",1.0f,0.0f,1.0f" << endl;
          
          DebugFile.File << "Vertex #" << (vertexnum+3) << ": ";
          DebugFile.File << vx+(float)m_iIncrementX << "," << vy << ",1.0f,1.0f,0.0f" << endl;
          
          DebugFile.File << "Vertex #" << (vertexnum+4) << ": ";
          DebugFile.File << vx+(float)m_iIncrementX << "," << vy+(float)m_iIncrementY << ",1.0f,1.0f,1.0f" << endl;
          
          DebugFile.File << "Vertex #" << (vertexnum+5) << ": ";
          DebugFile.File << vx << "," << vy+(float)m_iIncrementY << ",1.0f,0.0f,1.0f" << endl << endl;
          
          quad++;
          vertexnum+=6;
          vx+=(float)m_iIncrementX;
               
          
        }
        vx=(float)-m_iIncrementX;
        vy+=(float)m_iIncrementY;
      }
    
      DebugFile.File << "Unlocking vertex buffer...";
      hr=m_pVB->Unlock();
      if (hr!=D3D_OK)
      {
        DebugFile.File << "FAILED" << endl;
       
      } else DebugFile.File << "SUCCESS" << endl;
    }
    
    void CScreenGrid::Render(float fTimeDelta)
    {
      
      //m_pDevice->SetRenderState(D3DRS_CULLMODE,D3DCULL_NONE);
    
      //m_pDevice->SetRenderState(D3DRS_FILLMODE,D3DFILL_WIREFRAME);
      
      //Set stream source
      m_pDevice->SetStreamSource(0,m_pVB,0,sizeof(TLVertex));
    
      //Set FVF
      m_pDevice->SetFVF(TLVertex::FVF);
    
      m_pDevice->SetTexture(0,m_pTexture);
    
      //Draw it
      
      HRESULT hr=m_pDevice->DrawPrimitive(D3DPT_TRIANGLELIST,0,m_dwTotalTriangles);
    
      if (hr!=D3D_OK)
      {
        DebugFile.File << "DrawPrimitive() - FAILED with D3DERR_INVALIDCALL";
      }
      
      //
      //m_pDevice->SetRenderState(D3DRS_FILLMODE,D3DFILL_SOLID);
    
      m_pDevice->SetTexture(0,NULL);
    
    
    }
    
    
    void CScreenGrid::Scroll(int irelScrollX,int irelScrollY)
    {
      m_iScrollX+=irelScrollX;
      m_iScrollY+=irelScrollY;
    
      
        
        if (m_iScrollX>=(m_iIncrementX-1))
        {
          irelScrollX=-m_iScrollX+irelScrollX;
          m_iScrollX=0.0f;
        }
      
        if (m_iScrollX<=(-m_iIncrementX+1))
        {
          irelScrollX=-m_iScrollX+irelScrollX;
          m_iScrollX=0.0f;
        }
    
        if (m_iScrollY>=m_iIncrementY)
        {
          irelScrollY=-m_iScrollY+irelScrollY;
          m_iScrollY=0.0f;
        }
      
        if (m_iScrollY<=-m_iIncrementY)
        {
          irelScrollY=-m_iScrollY+irelScrollY;
          m_iScrollY=0.0f;
        
        }
      
      
        TLVertex *Vertices;
        m_pVB->Lock(0,0,(void **)&Vertices,0);
    
        for (DWORD i=0;i<m_dwTotalVerts;i++)
        {
          Vertices[i].Pos.x+=(float)irelScrollX;
          Vertices[i].Pos.y+=(float)irelScrollY;
        }
      
        m_pVB->Unlock();
      
    }
    
    void CScreenGrid::Rotate(float fRadians)
    {
      D3DXMATRIX rot;
      D3DXMatrixRotationZ(&rot,fRadians);
    
      TLVertex *Vertices;
      m_pVB->Lock(0,0,(void **)&Vertices,0);
    
      
      
      for (DWORD i=0;i<m_dwTotalVerts;i++)
      {
        
    
        D3DXVec3TransformCoord(&Vertices[i].Pos,&Vertices[i].Pos,&rot);
    
        //Vertices[i].x+=(float)irelScrollX;
        //Vertices[i].y+=(float)irelScrollY;
      }
      
      m_pVB->Unlock();
    }
    CScreenGrid.h
    Code:
    #ifndef CSCREENGRID
    #define CSCREENGRID
    
    #include "d3dx9.h"
    #include "CFileLog.h"
    #include "CTilemap.h"
    
    
    #include <string>
    
    
    
    struct TLVertex
    {
      D3DXVECTOR3 Pos;
      float rhw;
      D3DCOLOR    diffuse;
      float u,v;
    
      
      static const DWORD FVF;
    
      TLVertex(float _x,float _y,float _z,float _u,float _v):Pos(_x,_y,_z),rhw(1.0f),u(_u),v(_v) {}
      TLVertex(void):Pos(0.0f,0.0f,0.0f),rhw(1.0f),u(0.0f),v(0.0f) {}
    };
    
    class CScreenGrid
    {
      
      protected:
        CTilemap                *TileMap;
    
        IDirect3DTexture9       *m_pTexture;
        IDirect3DDevice9        *m_pDevice;
        IDirect3DVertexBuffer9  *m_pVB;
    
        WORD              m_iScreenHeight;
        WORD              m_iScreenWidth;
        WORD              m_iTileHeight;
        WORD              m_iTileWidth;
        WORD              m_iNumTilesVert;
        WORD              m_iNumTilesHoriz;
        DWORD             m_dwTotalTriangles;
        DWORD             m_dwTotalVerts;
        DWORD             m_dwTotalTiles;
    
    
        int             m_iScrollX;
        int             m_iScrollY;
      
        int             m_iIncrementX;
        int             m_iIncrementY;
        
        DWORD           m_dwScrollBoundsX;
        DWORD           m_dwScrollBoundsY;
    
        DWORD           m_dwWorldX;
        DWORD           m_dwWorldY;
        
    
        //Debugging only
        CFileLog        DebugFile;
       
    
      public:
        CScreenGrid(void):m_pTexture(NULL),m_pDevice(NULL),m_iTileHeight(0),m_iTileWidth(0),
          m_iScreenHeight(0),m_iScreenWidth(0),m_iIncrementX(0),m_iIncrementY(0),
          m_dwTotalTriangles(0),m_dwTotalVerts(0),m_dwTotalTiles(0) {}
        virtual ~CScreenGrid(void)
        {
          if (m_pTexture) m_pTexture->Release();
          if (m_pVB) m_pVB->Release();
          if (m_pSoftwareVerts) delete [] m_pSoftwareVerts;
    
    
          m_pTexture=NULL;
          m_pVB=NULL;
          m_pSoftwareVerts=NULL;
    
    
          DebugFile.Close();
        }
    
        void SetSize(int iTileW,int iTileH,int iScrW,int iScrH)
        {
          m_iTileWidth=iTileW;
          m_iTileHeight=iTileH;
                
          m_iScreenWidth=iScrW;
          m_iScreenHeight=iScrH;
    
          m_iIncrementX=m_iScreenWidth/m_iTileWidth;
          m_iIncrementY=m_iScreenHeight/m_iTileHeight;
        }
    
        
        void SetNumTilesHV(int iNumHorz,int iNumVert,int iScrW,int iScrH)
        {
          m_iTileWidth=iScrW/iNumHorz;
          m_iTileHeight=iScrH/iNumVert;
    
          m_iScreenWidth=iScrW;
          m_iScreenHeight=iScrH;
          
          m_iIncrementX=m_iTileWidth;
          m_iIncrementY=m_iTileHeight;
          
          m_iNumTilesHoriz=iNumHorz+2;
          m_iNumTilesVert=iNumVert+2;
        }
         
        void SetWorldPos(DWORD dwWorldX,DWORD dwWorldY)
        {
          m_dwWorldX=dwWorldX;
          m_dwWorldY=dwWorldY;
        }
    
        void SetScrollBounds(DWORD dwScrollBoundsX,DWORD dwScrollBoundsY)
        {
          m_dwScrollBoundsX=dwScrollBoundsX;
          m_dwScrollBoundsY=dwScrollBoundsY;
        }
        
        void Rotate(float fRadians);
        void Create(IDirect3DDevice9 *Device,std::string File);
        void Render(float fTimeDelta);
        void Scroll(int irelScrollX,int irelScrollY);
        
    };
    
    
    #endif
    To alter this for maps with more than 1 texture.
    • Calculate the offset into the tile map data
    • Retrieve the texture ID from the map
    • Retrieve the texture from the texture manager using the ID
    • If the current texture ID is not the ID in the map, use SetTexture() to change the texture.


    In this code the grid simply just scrolls left and right, up and down. The only difference between this and a real time map is that as you scroll:

    Code:
    ...
    ...
    if (m_iScrollX % m_iTextureSizeX==0)
    {
      m_dwTileMapOffsetX++;
    }
    
    if (m_iScrollY % m_iTextureSizeY==0)
    {
      m_dwTileMapOffsetY++;
    }
    
    DWORD dwTileMapStartingOffset=m_dwTileMapOffsetY*m_dwTileMapWidth+m_dwTileMapOffsetY;
    ...
    ...
    This will change the starting image at the upper left corner of the grid. If you change the starting image then in effect you have changed the entire map position.

    For instance:

    Look at this map: MAP1

    1020000
    1210303
    0704050
    1040305

    Ok now scroll once to the right: MAP2
    020000.
    210303.
    704050.
    040305.

    Notice the first column from MAP1 is not in MAP2

    The periods indicate the edge of the map

    See. The only difference between the two maps is that we start at a different offset on row 1. But the overall grid behind it all is still the same.

    So if we had a cellsize of 1 or 1 pixel then this would work. However we must take into account each tile's size. So if each tile is 64 pixels then we can find out when to increment the starting map offsets for x and y by using modulo arithmetic.

    My code is complex because I do not re-calculate the y*width+x inside of the loop. I calculate once and then increment. To move down one row, I simply add in width - which gives the same result as re-calculation y*width+x, but it doesn't require a multiply so it's faster. So the algo is huge because it is optimized.
    One loop, couple of increments, couple of adds, and that's it. All linear.

    The CTileMap pointer is not used as of yet, but it will simply be a pointer to the current map in memory. This map holds the IDs of the textures. Since know our current offset we simpy retrieve the ID of the tile at Map[offset] and then do TileManager->GetTexture(ID) to get the actual texture.

    The example code I gave you does not do this as of yet. Feel free to change it. It's not far from being a fully functional per-pixel scrolling tile engine renderer.

    The tile editor for this engine does do this. Here is the OnDraw() code from the editor.

    Code:
    void CZeldaEditorView::OnDraw(CDC* pDC)
    {
      
      
      CDC MemDC;
      if (!MemDC.CreateCompatibleDC(GetDC()))
      {
        ::MessageBox(0,"Failed to create memory DC",0,0);
      }
      
      CZeldaEditorDoc* pDoc = (CZeldaEditorDoc *)GetDocument();
    	CMainFrame *ptrFrame=(CMainFrame *)AfxGetMainWnd();
      DWORD numMaps=pDoc->m_pMapManager->GetNumberOfMaps();
      HDC dcDest=pDC->GetSafeHdc();
        
      CBrush blackbrush;
      blackbrush.CreateSolidBrush(RGB(0,0,0));
      
      CRect ScreenRect;
      GetClientRect(&ScreenRect);
    
      pDC->FillRect(&ScreenRect,&blackbrush);
      //MemDC.FillRect(&ScreenRect,&blackbrush);
    
      CPoint mouse;
      ::GetCursorPos(&mouse);
    
      if (pDoc->m_pTileManager && numMaps>1)
      {
      
        int TileSize=pDoc->m_iTileSize;
        int iOffsetHoriz=abs(m_iScrollX/m_iGridSize);
        int iOffsetVert=abs(m_iScrollY/m_iGridSize);
        //int iStartTile=iOffsetVert*m_iMapSizeX+iOffsetVert;
        //int iCurTile=iStartTile;
    
        //ComputeStartGrid();
        
    
        for (DWORD map=m_dwStartMapNum;map<m_dwEndMapNum;map++)
        {
    
          CMap *ptrMap=pDoc->m_pMapManager->GetMapClass(map);
         //CMap *ptrMove=pDoc->m_pMapManager->GetMapClass(0);
       
          
          int CurrentY=(-m_iScrollY/m_iGridSize);
          int CurrentX=(-m_iScrollX/m_iGridSize);
          int OriginX=CurrentX;
    
    
          DWORD offset=CurrentY*pDoc->m_iMapSizeX+CurrentX;
          int offsetx=m_iScrollX % m_iGridSize;
          int offsety=m_iScrollY % m_iGridSize;
    
          
          
    
          DWORD startoffset=offset;
    
          CRect GridRect;
          
          pDC->SetTextColor(RGB(255,0,0));
          pDC->SetBkColor(0);
    
          for (int i=offsety;i<ScreenRect.bottom;i+=m_iGridSize)
          {
            for (int j=offsetx;j<ScreenRect.right;j+=m_iGridSize)
            {
              
                      
              DWORD ID=ptrMap->GetValueAtOffset(offset);
              
              if (ID!=0xFFFFFFFF)
              {
    
                if (m_bViewIDs==false && m_bViewOffset==false && m_bViewRowCol==false)
                {
                  CDC *tempDC=pDoc->m_pTileManager->GetTileDC(ID);
                  HDC dcSource=tempDC->GetSafeHdc();
                  UINT dwTransColor=pDoc->m_pTileManager->GetTransColor(ID);
                
                  ::TransparentBlt(dcDest,
                                   j,i,
                                   m_iGridSize,m_iGridSize,
                                   dcSource,
                                   0,0,
                                   TileSize,TileSize,
                                   dwTransColor);
                }
              }
    
              //Draw the grid
              GridRect.left=j;
              GridRect.top=i;
              GridRect.right=j+m_iGridSize;
              GridRect.bottom=i+m_iGridSize;
              GridRect.NormalizeRect();
              
              if (m_bGrid) 
              {
                          
                CBrush gridbrush;
                gridbrush.CreateSolidBrush(RGB(255,255,255));
    
                pDC->FrameRect(&GridRect,&gridbrush);
              }
              
              if (m_bViewIDs==true || m_bViewOffset==true || m_bViewRowCol==true)
              {
    
    
                int midx=GridRect.Width()>>1;
                int midy=GridRect.Height()>>1;
                
                CString GridText;
                if (m_bViewIDs)
                {
    
                  if (ID==0xFFFFFFFF)
                  {
                    GridText="-1";
                  } else GridText.Format("%u",ID);
                }
                if (m_bViewOffset) GridText.Format("%u",offset);
                if (m_bViewRowCol) GridText.Format("%u/%u",CurrentY,CurrentX);
                 
                CSize size;
                size=pDC->GetTextExtent(GridText);
                int x=j+midx-(size.cx>>1);
                int y=i+midy-(size.cy>>1);
    
                if ( (x+size.cx) < (j+GridRect.Width()) )
                {
                  pDC->TextOut(x,y,GridText);
     
                }
                  
                            
                  
              }
                    
              offset++;
              CurrentX++;
              if (CurrentX>=pDoc->m_iMapSizeX) break;
            }
              CurrentX=OriginX;
              startoffset+=pDoc->m_iMapSizeX;
              offset=startoffset;
    
              CurrentY++;
              if (CurrentY>pDoc->m_iMapSizeY-1) break;     
          }
        }
      }
    
    }
    The editor code attempts to draw the map in the window. It bails and/or moves down one row if:
    • The render for the current row reaches the right side of the window
    • The render for the current row would be below the bottom of the window
    • The current x offset into the map is out of range
    • The current y offset into the map is out of range


    Don't worry about all the DC crapola. It's just standard Windows garbage to get my bitmaps to display. I'm using some nifty tricks to make it display faster.

    This is essentially the portion that retreives the texture based on the ID in the map.

    Code:
    ...
    CDC *tempDC=pDoc->m_pTileManager->GetTileDC(ID);
                  HDC dcSource=tempDC->GetSafeHdc();
                  UINT dwTransColor=pDoc->m_pTileManager->GetTransColor(ID);
                
                  ::TransparentBlt(dcDest,
                                   j,i,
                                   m_iGridSize,m_iGridSize,
                                   dcSource,
                                   0,0,
                                   TileSize,TileSize,
                                   dwTransColor);
    ...
    It took me a long time to get this figured out and working, but it works great.

    It does use two for loops but either loop can bail at any time if those conditions I showed above are met so it renders quite fast.
    Last edited by VirtualAce; 11-16-2005 at 05:37 AM.

  11. #11
    ---
    Join Date
    May 2004
    Posts
    1,379
    I appreciate all the help but it really doesn't need to be that complex. There is no 'map' just a scrolling bitmap that looks like water to make it seem like the object is moving continuously. Maybe in the future I could look into making a map that can be explored but right now i just want arcade style 1942 (mine will never be as good though).

    Anyway, what I did was this;
    1 tile: 16x16 pixels
    Create bitmap in memory, slightly larger than the screen, and fill it with the same tile.
    (screen_width+16)/16 * (screen_height+16)/16

    Blit the bitmap to screen with the top edge hanging off the screen and scroll down 1 pixel at a time.
    Code:
    x = 0, y = -16
    while(1){
      blit(bitmap,x,y++)
      if(y==0)
        y= -16
    }
    and that gives me a nice scroll.

    Thanks Bubba.
    Last edited by sand_man; 11-16-2005 at 05:42 AM.

  12. #12
    Super Moderator VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,598
    No problem. Eventually you will probably want to move to an algo that supports complex tile maps. It can really add to the game.

    As always contact me if you would like assistance.

    Wow. I guess I write a lot of code eh. It doesn't really seem like it but I guess my projects are both becoming quite large.

    Sorry it was too complex. I'm probably needing more functionality than you do for a 1942 clone.
    Last edited by VirtualAce; 11-16-2005 at 05:41 AM.

  13. #13
    ---
    Join Date
    May 2004
    Posts
    1,379
    I'm taking baby steps
    I just want to actually finish something for once. At least something like you said could be added to it in the future. If I can get a basic working demo then I can polish it off later.

  14. #14
    Super Moderator VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,598
    What's it mean to 'finish' something?


Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Scrolling background?
    By Deo in forum Game Programming
    Replies: 6
    Last Post: 06-09-2005, 06:40 PM
  2. Scrolling
    By PJYelton in forum Windows Programming
    Replies: 10
    Last Post: 03-24-2003, 10:04 AM
  3. Bitmap scrolling with scroll bars
    By solar3147 in forum Windows Programming
    Replies: 0
    Last Post: 03-17-2003, 02:39 AM
  4. Vesa. Parallax scrolling.
    By r0x in forum Game Programming
    Replies: 5
    Last Post: 06-10-2002, 06:39 PM
  5. My Console RPG with scrolling terrain ver 4!
    By Jeremy G in forum Game Programming
    Replies: 4
    Last Post: 11-25-2001, 04:39 PM

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21