-
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 :)
-
Alter the texture coords of the 4 vertices making the screen quad. This will scroll the image.
-
This is just pure SDL no OpenGL so there are no tex coords although that would make my life much simpler.
-
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.
-
I don't really understand what you are saying, Quantum1024. It has to scroll continuously as in one bitmap scrolling, never ending.
-
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++.
-
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?
-
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.
-
>>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.
-
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.
-
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.
-
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.
-
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.
-
What's it mean to 'finish' something?
:D