Screen Layout & Background Image 靜宜大學資工系 蔡奇偉 副教授 2006-2007
內容大綱 Screen Layout 座標系統 Background Image Painter’s Algorithm
Screen Layout 佈置螢幕的畫面,以達到下列目標: 美觀 營造遊戲氣氛 清楚的遊戲資訊展示 螢幕可以顯示下列的畫面: 遊戲開始的操作畫面 遊戲主畫面 即時性的遊戲資訊 遊戲設定、遊戲角色屬性展示或調整的 GUI 影片(開場、 橋段、結局) 終場 兩者通常同時顯現
GBA Final Fantasy VI Advance
失落的星球:極限狀態
暗黑破壞神 2資料片:毀滅之王
暗黑破壞神 2資料片:毀滅之王
伊蘇:始源
迷城國度 中文版
爆走卡車之橫越美洲
救世英豪 3 中文版
終極動員令 3:泰伯倫戰爭
終極動員令 3:泰伯倫戰爭
座標系統 右手座標系統 左手座標系統 y x (0, 0) x (0, 0) y SDL 的螢幕和繪圖頁採用左手座標系統。
遊戲裏的座標系統 世界座標系統 描述遊戲世界所採用的座標系統。可以是左手或右手座標系統; 其範圍也可能超過遊戲窗口的大小。 螢幕座標系統 通常是左手座標系統 遊戲窗口座標系統 顯示遊戲世界的窗口,因此通常採用與世界同類型的座標系統。 繪圖頁座標系統 物件座標系統 定義遊戲物件屬性所用的座標系統,可以是左手或右手座標系統。
世界座標與螢幕座標間的轉換 window screen viewport world 以後使用下面的簡稱: W: world S: screen V: viewport
Case 1: W = S = V, 左手座標系統 W S = V
Case 2: W (右) = S (左) = V (右) S W HS V
Case 3: W = V < S, 左手座標系統 W S V
Case 4: W (右) = V (右) < S (左)
Case 5: 左手座標系統 W S V
Case 6: W (右), V (右), S (左) S W V
座標系統的選擇 全部採用左手座標系統 優點:具有一致性,座標轉換的計算也比較簡單 缺點:不符合日常的直覺。 螢幕和繪圖頁使用左手座標系統,其他用右手座標系統 優點:世界座標的使用符合日常的直覺。 缺點:座標轉換的計算比較複雜。 不過,我們可以運用一些輔助程式把圖資裏的右手座標先轉成左手座標,然後才匯入遊戲程式使用。
Background Image 合成方式 一整張圖當背景圖 優點:比較容易繪製也比較容易管理。 缺點:若是圖很大,一次整個讀入會佔用很多的記憶體, 對某些掌上型遊樂器而言,可能無法負擔。 分割成若干比較小的子圖 優點:不必一次讀入整個背景圖,因而可減少記憶體的需求。 缺點:遊戲程式需要把這些分割圖合成原來的背景圖,同時也必 須判斷目前使用到的子圖為何。
範例 B1 B2 B3 B4 B5 B6 B7 B8 背景圖由 8 張子圖 B1, …, B8 組成。 viewport 跨佔 B2, B3, B6, 和 B7。 因此,程式必需保留可儲存四張子圖的記憶體。
由圖磚 (tiles) 組成背景圖 圖磚 背景圖中,許多圖磚是相同的圖形。
+ Tile Map int background_map[4][3] = { 1, 2, 3, 2, 2, 3, 3, 2, 1, 3, 1, 3, }
範例 gamebackground.zip 1600x400 採用全圖式的背景圖 全都用左手座標系統 程式的遊戲鏡頭左右來回地顯示背景圖片
CGameBackground 類別 private: // 儲存背景圖的繪圖頁 CSurface m_backgroundImage; // 遊戲鏡頭的位移量 int m_dx, m_dy; // 螢幕上的 viewport 矩形 CRect m_viewport; // 遊戲世界的 window 矩形 CRect m_window;
// Default Constructor CGameBackground() : m_dx(0), m_dy(0) { } // 載入背景圖。若參數 bDisplayFormat 為 true,則背景圖會 // 轉換成螢幕的像素格式。 bool LoadImage (const char *file, bool bDisplayFormat = true) return m_backgroundImage.LoadImage(file, bDisplayFormat);
W S V // 設定 viewport 矩形,同時把 window 設成一樣大小的 // 矩形,而且擺在背景圖的左上角位置。 void SetViewport (const CRect &rect) { m_viewport = rect; m_window.w = rect.w; m_window.h = rect.h; } W S V
bool FirstShow () { return m_backgroundImage.Show (m_window, m_viewport); } bool Show () if (m_dx != 0 || m_dy != 0) m_window.x += m_dx; m_window.y += m_dy; return true;
// 水平移動遊戲鏡頭 dx 個像素。參數 dx 為正,表示右移, // 否則為左移。 int HPan (int dx) { if (dx > 0) int gap = m_backgroundImage.Width() – (m_window.x + m_window.w); if (dx > gap) dx = gap; if (dx >= 0) m_dx = dx; } else if (dx < 0) if (m_window.x < -dx) dx = -m_window.x; else m_dx = 0; return dx; // 傳回真正的移動量 gap
// 垂直移動遊戲鏡頭 dy 個像素。參數 dy 為正,表示下移, // 否則為上移。 int VPan (int dy) { if (dy > 0) int gap = m_backgroundImage.Height() – (m_window.y + m_window.h); if (dy > gap) dy = gap; if (dy >= 0) m_dy = dy; } else if (dy < 0) if (m_window.y < -dy) dy = -m_window.y; else m_dy = 0; return dy; // 傳回真正的移動量
// 任意方向地移動遊戲鏡頭可以用如下的 HPan() 和 VPan() // 合成方式。 void Pan (int dx, int dy) { HPan(dx); VPan(dy); }
// main.cpp #include <SDL/OOSDL.h> #include "gamebackground.h" // The attributes of the screen const int SCREEN_WIDTH = 640; const int SCREEN_HEIGHT = 480; const int SCREEN_BPP = 32; const char* WINDOW_TITLE = "Scroll Game Background";
class CMyGameApp : public CGameApp { public: CMyGameApp () : CGameApp() {} protected: virtual bool InitializeApp (); virtual bool AppMain (); private: CGameBackground m_background; int m_panSpeed; // 鏡頭移動的速度 };
bool CMyGameApp::InitializeApp () { CRect viewport(40, 40, 560, 400); m_background.LoadImage ("bg.png"); m_background.SetViewport(viewport); m_pScreen->Clear(); m_background.FirstShow(); m_panSpeed = 3; return true; }
bool CMyGameApp::AppMain () { int speed = m_background.HPan(m_panSpeed); if (speed == 0) m_panSpeed = -m_panSpeed; m_background.Show(); return true; }
int main( int argc, char* argv[] ) { CMyGameApp myGameApp; myGameApp.InitializeSDL(); myGameApp.SetScreen(); myGameApp.SetCaption(WINDOW_TITLE); myGameApp.Run(); return EXIT_SUCCESS; }