Well here is what I have. The scrolling is done in the main window. I would like to create that type of map system but the calculations are far beyond what I would have imagined - they get complicated. You can zoom in on this map until cell sizes reach 256 pixels. My idea is that I never want the map to have borders. In other words if you zoom out far enough, I never want you to be able to see off of the map. My calculations are as follows. It works fine for computing the scrolling maxX and maxY, but when you zoom in, scroll down, and then zoom out.....everything goes to um...crap.
//Set new scroll sizes based on map size and current cellsize
//or grid size
//Get pointer to document class for map info
CZeldaEditorDoc *pDoc=(CZeldaEditorDoc *)GetDocument();
//Retrieve width and height of client rect
//Set max scroll
//Don't show borders
if (m_iMaxScrollX<0) m_iMaxScrollX=0;
if (m_iMaxScrollY<0) m_iMaxScrollY=0;
//If map size won't fit in window because map is too small
//Re-adjust cell size so map fits perfectly in window
//Transforms mouse clicks to always click on nearest tile
//to cursor regardless of zoom and scroll
void CZeldaEditorView::TransformMouseXY(CPoint mouse,CRect &rect,DWORD &offset)
//Find out remainder of m_iScrollX/m_iGridSize
int offsetx=(m_iScrollX % m_iGridSize);
int offsety=(m_iScrollY % m_iGridSize);
//Temporaries so I don't have to use mouse.x, mouse.y every
//Subtract mouse by offsets
//Snap to grid size
mx-=(mx % m_iGridSize);
my-=(my % m_iGridSize);
//Re-adjust on-screen rect to new grid rectangle
//Compute actual memory location of grid clicked
int MemoryX=(mouse.x - m_iScrollX)/m_iGridSize;
int MemoryY=(mouse.y - m_iScrollY)/m_iGridSize;
//Set offset in document
CZeldaEditorDoc *ptrDoc=(CZeldaEditorDoc *)GetDocument();
Sorry for the mess but MFC code is a mess. Basically I'm adhering to the document/view architecture so that the document handles the info about the map and the view just draws the map according to the info. It's a real pain in the arse.
These calculations work but zooming only zooms in on the upper left corner. I know why it does this, but how would I zoom in on an area? Do I have to draw my map relative to the center of the screen, or can I just start at the upper left corner and set m_iScrollX and m_iScrollY to different values when zooming to get the zoom in on a point effect?
Hard to explain.
Maybe showing the actual OnDraw() will help.
Pardon the mess but it's a major work in progress.
Now the reason that the OnDraw is so extensive is because I did not want to compute the memory offset inside of the loop to render the grid. That would be slow. So I pre-compute the memory offset - which is already taking into account the scrolling values and so it is at the right spot in memory to correctly display the grid. The entire visible portion of the map can be drawn without re-computing the offset. When the column counter reaches the map size OR the X value of the render reaches the right side of the client rectangle, the row counter is incremented, column counter is reset (originX at current is always 0), starting offset is incremented by one row (m_iMapSizeX), and current offset is set to the starting offset.
void CZeldaEditorView::OnDraw(CDC* pDC)
//Pointer to document class
CZeldaEditorDoc* pDoc = (CZeldaEditorDoc *)GetDocument();
//Pointer to frame clasee
CMainFrame *ptrFrame=(CMainFrame *)AfxGetMainWnd();
//Number of maps (layers)
//Handle to destination DC
//Black brush for drawing
//Retrieve client rect
//Fill it with black
//Check for tile manager and map count
//Don't draw if null or maps=0
if (pDoc->m_pTileManager && numMaps>1)
//Tile size from bitmap
//Horizontal offset - debug info
//Vertical offset - debug info
//Function to compute relative to center of screenrect
//Algos don't work right with this yet
//Iterate through maps
//m_dwStartMapNum is starting map
//m_dwEndMapNum is ending map
//To view one layer set start to layer and end to start+1
for (DWORD map=m_dwStartMapNum;map<m_dwEndMapNum;map++)
//Get pointer to map
//Get pointer to movement map - disabled
//Screen calculations for memory offset
//Remember CurrentX before it changes
//Compute memory offset
int offsetx=m_iScrollX % m_iGridSize;
int offsety=m_iScrollY % m_iGridSize;
//Save starting offset
//Rect for grid display - if enabled
//Draw the map finally
for (int i=offsety;i<ScreenRect.bottom;i+=m_iGridSize)
for (int j=offsetx;j<ScreenRect.right;j+=m_iGridSize)
//Retrieve ID from map
//If this block is not empty
//Get DC of tile bitmap from tile manager
//Get handle to tile bitmap DC
//Get transparent color of tile
//Do a transparent blit
//Draw the grid
//If view->Grid checked then draw grid
//Increment offset into memory
//Increment cell column counter
//If column is at width of map break
if (CurrentX>=pDoc->m_iMapSizeX-1) break;
//Set column counter to origin
//Increment startoffset by one row
//Increment row counter
//If no more rows, break
if (CurrentY>pDoc->m_iMapSizeY-1) break;
It'a a pain in the arse but it's a linear algorithm and it's quite fast.
The array is indexed in linear fashion and the render is done in linear fashion. The Tile manager holds all of the tiles. But get this. Instead of storing bitmaps each tile is an object with a device context that has been prepared beforehand so the image data is on the device context. Then blitting the image is as simple as using TransparentBlt from the API using the handles to the source DC and the dest DC. Simple.
It all works great. But zooming is a mess. The ComputeStartGrid() function is a function that can center the grid inside of the client rect, but then all of the LButtonDown and MouseMove() functions don't compute the mouse cursor to grid and mouse cursor to memory right.
If you'ev ever done a tile engine I'm sure you will understand in the editor you must transform mouse clicks to align with the displayed grid or you cannot place tiles correctly.
Also if the current tile is the selected tile and you click on the tile in the map, it erases it. The selected tile is stored in the m_pTileManager class and is set by the tile tool dialogs when you select a tile to use.
This hasn't been easy so forgive me if the algo sucks.