I didn't choose C# because I don't have time to learn it right now. I'll probably buy some books on it soon, but for this project I really need to get the editor to the map makers.
I'm quite anxious to get the maps up and running. So far we have all the pieces of the engine nearly complete. All that's left to do is create the maps based on paper designs, write some test level scripts, select the sound fx and music, and let the thing run its course.
Very interested to see how the script interacts with the engine. I know how it's supposed to work, but 'supposed' to and 'will' may be two different things.
Lots of work has gone into both the engine and the editor with the primary focus having been on correct and efficient resource management as well as a highly optimized render loop, which isn't the easiest thing to do for 2D in a 3D API.
Switching to C# mid-course would only cause further delays with no added benefit from the start. There are 3 fan games planned for this series so perhaps I will use C# for those editors. However, I will probably just add functionality to the C++/MFC editor.
Still having problems with the doggone tilesheet. Problem is that a tile sheet will not display if no pages have been added. But after you have 1 page and then call SetActivePage(), it displays itself. No biggie until I realize my tile renderer window for the tile page needs to use GetClientRect() to figure out it's dimensions. Well GetClientRect() will fail if m_hWnd is not a valid handle and if the property sheet is not displaying, it will throw and exception. I've gotten around it but now the tile sheet disappears which isn't cool. Last time I had this working but in a different design. Not sure what to do as of yet.
To give you an idea of the complexity here I'll show you the code for OnFileImportBMP() which is called when the user selects File->Import->BMP. This is the method you use to import new bitmaps into the editor. The editor will then extract tiles of a pre-set tile height and width (in editor options) from the bitmap. Once this is done, the next time the project is loaded, they will appear w/o having to import the bitmap.
Code:
void CMainFrame::OnImportBmp()
{
//Common file dialog
CFileDialog *dlg=new CFileDialog(true,
L"*.bmp",
NULL,
OFN_FILEMUSTEXIST,
L"Windows bitmaps (*.bmp)|*.bmp|");
INT_PTR result=dlg->DoModal();
if (result==IDOK)
{
//Get pointer to active document
CTileEditorDoc *pDoc=(CTileEditorDoc *)GetActiveDocument();
//Get current number of tiles
DWORD dwNumTiles=m_pDoc->m_pTileMgr->GetVecSize();
//Debug message box
AfxMessageBox(L"Adding tiles");
//Add tiles from image and return bitmap dimensions in size
CSize size=pDoc->m_pTileMgr->AddFromImage(dlg->GetPathName(),m_pView);
//Set some local vars for later use
int iTileH=m_pDoc->m_pTileMgr->GetHeight();
int iTileW=m_pDoc->m_pTileMgr->GetWidth();
//If tile sheet does not exist, we must use the following code
//otherwise we must use a different method
if (!m_pTileSheet)
{
//Construct tile sheet object
m_pTileSheet=new CTileSheet(L"Tile palette",this);
//Create new map manager for tile sheet and pass on doc tile mgr
m_pTileSheet->Init(m_pDoc->m_pTileMgr);
//Add 1 map to the map manager
//Size is computed from return values from AddFromImage()
m_pTileSheet->m_pMapMgr->Add(size.cx/iTileW,
size.cy/iTileH);
//Fill map with values from 0 to dwNumTiles
m_pTileSheet->m_pMapMgr->FillValues(0,dwNumTiles);
//Create tile page
CTilePalettePg *pg=new CTilePalettePg;
pg->Construct(IDD_TILEPALETTEPG);
//Add page to tile sheet and to vector for later cleanup
m_pTileSheet->AddPage(pg);
m_pTileSheet->AddPageToVec(pg);
//Debug message
AfxMessageBox(L"Creating tile sheet");
//Create tile sheet as layered tool window
m_pTileSheet->Create(this,-1,WS_EX_LAYERED | WS_EX_TOOLWINDOW | WS_SYSMENU);
//Set active page -> if property sheet is not visible at this time,
//this will make it visible
AfxMessageBox(L"Setting active page");
m_pTileSheet->SetActivePage(0);
//First, remove CLOSE from system menu
CMenu *pMenu=m_pTileSheet->GetSystemMenu(FALSE);
pMenu->EnableMenuItem(SC_CLOSE,MF_BYCOMMAND | MF_GRAYED);
pMenu->RemoveMenu(SC_CLOSE,MF_BYCOMMAND);
//Since window is now visible, client rects are valid
//So init the renderer for the new tile page
pg->Init(m_pTileSheet,0);
pg->Invalidate();
//Since the tile sheet is valid, now set it's alpha
m_pTileSheet->SetLayeredWindowAttributes(0,220,LWA_ALPHA);
//Redraw entire view beneath tile sheet
m_pView->Invalidate();
}
else
{
//Add 1 map to map manager
m_pTileSheet->m_pMapMgr->Add(size.cx/iTileW,
size.cy/iTileH);
//Fill map with correct values
m_pTileSheet->m_pMapMgr->FillValues(m_pTileSheet->m_pMapMgr->GetVecSize()-1,
dwNumTiles);
//Create tile page
CTilePalettePg *pg=new CTilePalettePg;
pg->Construct(IDD_TILEPALETTEPG);
//Add page to tile sheet and select it to view it
m_pTileSheet->AddPage(pg);
m_pTileSheet->AddPageToVec(pg);
m_pTileSheet->SetActivePage(m_pTileSheet->GetPageCount()-1);
//Since window is now visible, client rects are valid
//So init the renderer for the new tile page
pg->Init(m_pTileSheet,m_pTileSheet->m_pMapMgr->GetVecSize()-1);
}
//Assign filename as title of our tab in tab control
CTabCtrl *pTab=m_pTileSheet->GetTabControl();
TCITEM item;
item.mask=TCIF_TEXT;
CString strTitle=dlg->GetFileTitle();
int sizeOfString = (strTitle.GetLength() + 1);
LPTSTR lpsz = new TCHAR[ sizeOfString ];
_tcscpy_s(lpsz, sizeOfString, strTitle);
item.pszText=lpsz;
pTab->SetItem(m_pTileSheet->GetPageCount()-1,&item);
delete [] lpsz;
//Redraw entire view
m_pView->Invalidate();
//Set focus back to the view away from the tilesheet
m_pView->m_pTileRdr->SetFocus();
//Update list
//This is a hack for right now since docking window system is not fully functional
//Dock window 0 is the only dock window for this app
//Dock dialog #1 inside of dock window #0 is always tile list
//Update it's list control to reflect new bitmaps added
//Get pointer to tile list dialog object
CTileListDlg *pTileListDlg=(CTileListDlg *)m_pToolsDlg->m_ptabMyTabCtrl->GetPage(1);
//Update it's list to reflect all tiles loaded in memory
pTileListDlg->UpdateList(dwNumTiles,m_pDoc->m_pTileMgr->GetNumTiles());
}
//Delete the common dialog object
delete dlg;
}
The reason for the complexity is that the document is the sole owner of the tiles and of the tile maps. However, the maps being used to display the tile pages in the tile sheet are owned by the tile sheet object. Each map layer represents one tile page. This way I can use the same map class as the main tile map, and yet gain different functionality.
CTileEditorDoc
- CTileMgr
- CMapMgr
CTileSheet
- CTileMgr (pointer to CTileEditorDoc::CTileMgr)
- CMapMgr (this object is not CTileEditorDoc::CMapMgr)
- std::vector<CTilePalettePg *> m_vTilePages - vector of pages that have been added to the tile sheet
CTileMgr is a CGDITileMgr object which manages a collection of CGDITile objects.
CMapMgr is a CMapMgr2D object which manages a collection of CMap2D objects.