發表文章

目前顯示的是 5月, 2009的文章

good::gx介面

good::gx是負責繪圖的介面。它很簡單,至少目前來說還很簡單。只有二個介面:Image及Graphics。 Image介面對貼圖作一層薄薄的包裝,good執行的時候,所有使用到的貼圖的地方全都透過這一層介面作存取。下面是Image介面定義。 template<class ImgT> class Image { public: bool isValid() const; int getWidth() const; int getHeight() const; bool hasKeyColor() const; int getKeyColor() const; void setKeyColor(int keycolor); }; Graphics介面提供了很少的功能,目前只有貼圖和畫色塊二個功能,因為目前good的成像目前只需要這二個功能就足夠畫出所有東西。下面是Graphics介面定義。 template<class ImgT> class Graphics { public: bool drawImage(int x, int y, ImgT const& img, int srcx, int srcy, int srcw, int srch); bool drawImage(int x, int y, ImgT const& img) { return drawImage(x, y, img, 0, 0, img.getWidth(), img.getHeight()); } bool fillSolidColor(int left, int top, int width, int height, int color); }; + + + 實際應用的時候,只需要針對不同繪圖平台作不同實作品的提供,對於good內部而言,幾乎不必作任何修改。目前good::gx有四種實作品,GDI、SDL、imgp及OpenGL。

OpenGL 畫色塊

圖片
前幾天用OpenGL glDrawArrays方法來畫色塊時遇到問題。色塊是畫出來了,不過另外一個不使用顏色的貼圖方塊也被疊上了和色塊一樣的顏色,真是莫名其妙。 原來,畫色塊的時候使用glColor,而且當畫貼圖方塊時,glColor指定的顏色也會被使用到,所以要再以glColor指定一個白色的顏色值進去(0xffffffff),這樣畫出來的結果才會正確。 看起來好像對了,不過還是不對勁,因為我畫的是一個RGB(0x88ffff00)的黃色色塊,可是這顏色怪怪的。經過幾次測試發現這顏色和畫色塊之前所畫的一個貼圖方塊的顏色有關係。 經過研究後,發現問題還是出在狀態上,畫色塊的時候除了要以glDisableClientState關掉GL_TEXTURE_COORD_ARRAY狀態外,也還要再以glDisable把GL_TEXTURE_2D狀態關閉,這樣子就得到正確的結果了。 + + + 換說回來,雖然作過幾個3D應用的專案,甚至還參與過3D引擎的實作,我還是不懂3D。往好處想,這表示我還有很大的成長空間~

編輯魔法寶石遊戲資源

圖片
底下是J2ME版小香咪咪方塊的寶石方塊圖形資源,直接可以拿來使用,有這張圖就足夠來作個魔石寶石小遊戲了。 全部共有六種不同顏色的寶石方塊,再加上一個威力方塊P,不過這次我們只會用到那六種不同顏色的方塊。 + + + 首先開個新專案,視窗解析度設成和J2ME版小香咪咪方塊的大小一樣,176x208,其它保留預設值,然後專案存檔。 接著把圖形資源加入,每個Tile大小是16x16。一一把六種不同顏色的方塊和它對應的同色爆炸方塊加入製成精靈資源。在加入方塊精靈資源時,有個小地方特別注意的是,先加入一個顏色的方塊後,接著再加入這個顏色方塊的爆炸版本。依照這個規則把所有方塊加入,共12個精靈。 如上圖,接著再加入一個地圖資源。大小是11x13,每個Tile是16x16。上面缺的一塊是故意保留,而不是畫到一半就抓圖下來。缺的這一塊到時候會在關卡編輯器裡增加一個色塊來填,為了作出方塊是由正上方落下來的效果,我們會拿一個色塊來作遮擋。 接下來如上圖所示新增一個關卡資源,大小設定成和視窗一樣,指定黑色作為背景清除顏色。加入事先編輯好的地圖資源,一個遮擋用的色塊,還有三個作了提示下一回合要出現的方塊。以上完成所有遊戲資源編輯,剩下的就是用Lua編寫遊戲邏輯,下回再繼續。

魔法寶石消除演算法

圖片
魔法寶石類遊戲的消除演算法主要有二種,一種我稱為米字形規則,另一種我稱它為十字形規則。 米字形規則 米字形規則是以一個方塊為中心,假如在這個方塊的上下左右或斜向的方向上包含自己本身有超過三個以上相同顏色的方塊存在,則這個方塊就可以被消除。 如上圖中間的區塊部份,1號方塊周圍用紅色線框框起來的四個方向,垂直(A)、水平(B)及二條斜向(C及D)。以此規則,上圖右側區塊部份中標示1到5的五個橘色方塊可以被消除。 檢查的時候,每一條方向是各自獨立的,也就是說上述超過三個以上相同顏色的方塊這個限制必須是在同一方向上才能成立。只要其中一條方向消除條件成立,剩下的方向就可以略過不作檢驗。同理在同一個方向上只要檢查出有三個以上相同方塊存在,剩下的方塊就不必再檢查。 以垂直方向為例。 首先檢查1號方塊上方的方塊是否和1號方塊是相同顏色,假如不同的話再往上一個方塊也沒必要再作檢查。假如相同的話就再檢查最上面那個A方塊,假如也是一樣的話,那剩下的下面二個A方塊就沒必要再檢驗了,因為已經有三個一樣的方塊存在了。 假如最上面的A方塊和1號方塊不同的話,則再檢查1號方塊下方的A方塊。假如這個方塊和1號方塊不同的話,那最下面的A方塊也沒必要再作檢查了。反過來說,假如這個方塊和1號方塊是一樣顏色的話,再加上1號方塊上面那塊就有三個一樣顏色的方塊。 同理可以推出反向及其它方向的檢驗規則。 十字形規則 十字形規則是以一個方塊為中心,如果包含自身在這個方塊的上下左右的相隣方塊中有四個以上相同顏色的方塊存在,則這個方塊就可以被消除。 如上圖中,可以看到要判定1號方塊是否可以消除的範圍比米字形規則大的多,依據十字規則的檢驗,最後可以找出1到4號的四個黃色方塊可以被消除。 十字形規則的檢查方法,其實就是一個暴力搜尋法。關鍵原理是建立一個表,一開始把要檢查是否可消除的方塊(X)加入這個表。接著以如下的迴圈不斷的檢查,直到結果出來為止。 如果檢查表為空,則結束迴圈並返回X不可消除。 從表中取出一個要檢查的方塊(Y),並檢查Y的上下左右的四個方塊是否和X相同: 如果Y(上)和X相同,則計數加1同時把Y(上)加入檢查表。如果計數值為3,則跳出迴圈返回X可消除。 如果Y(下)和X相同,則計數加1同時把Y(下)加入檢查表。如果計數值為3,則跳出迴圈返回X可消除。 如果Y(左)和X相同,則計數加1同時把Y(左)加入檢查表

good的資料格式

自從使用 Ini 之後我就不再碰 XML 了,在任何情況下,只要情況允許我都會儘可能的使用Ini作為資料格式或設定檔案。雖然Ini不像XML那樣天生就能表達結構化的資料,但也不是完全不行,至少應用在good上就沒什麼問題。 good專案資料格式使用Ini,再透過smallworld2的Ini模組就能很簡單的操作讀寫。 + + + 專案檔頭 每個good專案檔裡,至少會有個名稱叫作good的Section,這相當於檔案的Header。這個Section包含了整個專案的內容資訊,是個總表。底下是個典型的範例。 [good] version=0.3 name=mmc window=176 208 texs=1 maps=2 3 sprites=4 5 6 7 8 9 10 11 12 13 14 15 levels=18 如上。格式版本是0.3;專案名稱是mmc;視窗的大小或解析度是176X208;有一個ID為1的貼圖資源;有二個ID分別是2和3的地圖資源;有12個精靈資源;以及一個ID為18的關卡資源。 多麼簡單一目了然,這就是我喜歡使用Ini的原因。 Script資源 接著有個叫scripts的Section,這個Section的內容是Script資源列表,和其它類型的資源比較起來是比較特別的,因為它不是在Section good裡面的一個項目,而是獨立出來一個Section。底下是個範例。 [scripts] 16=./mmc.lua 在scripts Section裡,每一個項目表示一個Script資源,項目的Key值表示為這個Script資源的ID,而項目的Value值表示為Script資源的檔案路徑。在good裡面的所有牽涉到檔案路徑的欄位,內容一律都是相對於專案檔本身的相對路徑。 貼圖資源 在專案檔頭裡列出的貼圖資源,每一個項目都對應到一個貼圖資源Section,底下是個簡單的範例。 [tex5] name=tileset fileName=./weeder.bmp hasKeyColor=1 keyColor=253 0 255 貼圖資源Section的名稱固定以tex開始,後面的數字表示這個資源的ID。其它不同類型的資源也是以同樣的規則定義,所以你大概可以猜到Section map2就是ID為2的地圖資源。 上面的例子可以清楚的看到,這個貼圖的ID為5;

實作迷你薩爾達傳說

圖片
夢見島的地圖是由160乘128個Tile所組成,每個畫面大小是10乘8個Tile,也就是說完整的地圖是由16乘16共256個畫面組成。每次當林克移動到畫面邊緣的時候,整個畫面會往林克前進的方向捲動,一次捲動一整個畫面。 先以編輯器編輯好遊戲資源。使用地圖TileSet編輯好地圖資源,並開一個新關卡資源加地圖加入。然後再加入幾個簡單的精靈資源,主要是林克的4個行走方向,還有幾朵不同顏色的花。接著就可以開始用 Lua 來實作Gameplay。 + + + 因為這是個簡單的Demo,主要要實作的功能是要可以在地圖上行走,也要可以作簡單的碰撞,當走到地圖邊緣的時候要能捲動到下一張地圖。 為了簡單起見,我把它劃分為二個類別:Game及Link。Game類別只負責作地圖捲動的工作。而Link類別則負責處理使用方向鍵輸入在地圖上行走,行走的時候作碰撞簡單,以及走到地圖邊緣時觸發事件讓Game類別作地圖捲動。 另外在也圖上常常可以見到一些會動的小花,假如要在關卡編輯器裡面,一個一個用精靈物件的方式種上去太費時費力了。所以需要使用動態生成的方式,在每次地圖捲動後把可見範圍內的小花動態產生出來,同時釋放捲出畫面範圍外的小花物件。 所以最後整個關卡裡只需放二個物件,一個是地圖,一個是林克(在靠畫面中央小屋前的小傢伙)。然後在關卡的Script欄位填上Game,林克的Script欄位填上Link。而視窗的大小設定為一張小地圖的大小,160x128。 + + + 在關卡上的每一個物件都有個位置,可以透過Good.GetPos和Good.SetPos來作存取。而關卡本身也有位置屬性,一樣可以透過Good.GetPos和Good.SetPos來作存取。 改變關卡的位置,可以製作出地圖捲動的效果。當然也可以去改變地圖物件的位置來作出捲動效果,但是使用這個方法的話,所有相對於地圖的物件,像是林克或地圖上的小花也都要一起移動。雖然可以把這件物件的父親都設成地圖,這樣只要移動地圖就好,但還是比不上移動關卡來的直覺容易,畢盡關卡是所有物件的最上層父物件,只要移動它所有底下的物件都可以全跟著動起來。 Game.OnCreate = function(param) local idLvl = param._id scrolling = false Good.SetPos(idLvl, 320, 1280

分解字串成Token

偶爾我們會需要自己將字串分解成一個個的Token,對於簡單的需求我們通常都自己來,而不特別使用Tokenizer。使用C語言的話,我們會用strtok這個函式來完成我們的需求,不過我比較偏好C++的作法。 string s("this is a string"); vector<string> v; v.assign(   istream_iterator<string>(stringstream(s)),   istream_iterator<string>() ); 如上所示,執行後v的內容會包含4個Token:this, is, a, string。 有一點要提醒的是,因為stringstream的Ctor會對s作copy的動作而不是直接使用s作為來源,所以當s的是一個很大的字串的話,在效能上會受到影響。 上面的範例在分解字串時是以空白字元作分隔,那假如要使用其它不同的字元作分隔符號該怎麼作呢? getline可以提供這基本的需求。 stringstream ss(s); string token; for (;;) {   getline(ss, token, ',');   if (ss.fail())     break;   cout << token << endl; } 上面這個範例示範使用getline以','字元作分隔符號將字串分解。 + + + 反向的操作有很多作法,底下舉一個STL的作法。 stringstream ss; copy(   v.begin(), v.end(),   ostream_iterator<string>(ss, " ") ); string s(ss.str()); stringstream的str方法也會產生一份新的string,所以當字串很大時效能也會受到影響。