跳到主要內容

發表文章

Win32 拖曳Tree-View項目

處理Win32的Tree-View Item主要分為三個步驟。 開始拖曳通知。 拖曳中處理。 拖曳結束。 底下對拖曳程序的每個階段作個簡介,並提供實作範例。 開始拖曳通知 當使用者開始拖曳(Drag)Tree-View Item時,Tree-View元件會經由WM_NOTIFY訊息發送一個TVN_BEGINDRAG通知給父視窗,這時應用便可以以這個通知得知使用者從什麼位置開始拖曳那一個Item,並開始進入拖曳程序。 case WM_NOTIFY: switch (((LPNMHDR)lParam)->code) { case TVN_BEGINDRAG: // ...由此進入拖曳程序 Main_OnBeginDrag(); break; } break; Main_OnBeginDrag是應用程式定義的一個函式,主要的任務是設定好相關狀態表示進入拖曳程序,並由Tree-View取得一個暫時的ImageList元件,可以用來畫出拖曳中的Item。 void Main_OnBeginDrag(HWND hwndTV, LPNMTREEVIEW lpnmtv) { // 取得暫時的ImageList. HIMAGELIST himl = TreeView_CreateDragImage(hwndTV, lpnmtv->itemNew.hItem); // 開始進入拖曳. ImageList_BeginDrag(himl, 0, 0, 0); ImageList_DragEnter(hwndTV, lpnmtv->pt.x, lpnmtv->pt.x); SetCapture(GetParent(hwndTV)); g_fDragging = TRUE; } 拖曳中處理 當使用者以滑鼠左鍵開以拖曳物件時,Tree-View送出一個TVN_BEGINDRAG,接著應用程式需要處理WM_MOUSEMOVE訊息來反應使用者拖曳的動作,直到使用者將滑鼠按鍵放開產生一個WM_LBUTTONUP訊息時,才結束拖曳動作。附帶一提,如果使用者是以滑鼠右鍵開始拖曳物件,則產生的通知是TVB_BEGINRDRAG,並以對應的WM_RBUTTONUP訊息作結束訊號。 void Main_OnMouseMove(HWND hwndParent, HWND hwn...

good spec

程式語言 程式語言:C/C++ 編輯器 系統平台:Windows 視窗框架: WTL 執行時期 系統平台:Windows,MacOS/iPhone 程式庫:smallworld2,stge 第三方程式庫: Lua / SDL / zlib / Boost.Spirit 繪圖(gx) GDI SDL OpenGL 編輯器使用WTL作視窗框架實作,所以主要是以C++ Template實作。而Runtime部份則整個使用C++ Template實作,主要區分成三大模組:good、ed和rt。另外還有個gx模組,簡單定義了圖形介面讓其它模組使用來作圖形繪製。 good模組提供了資源管理的功能,ed模組繼承good模組再擴充編輯功能,而rt模組繼承good模組實作Runtime及提供Script擴充介面。其實正確來說,rt模組並非繼承good模組,而是包含good模組,透過包含及使用good模組作為資源管理。 good模組是個狀態管理器,只能載入資源和讀取內容。ed模組則繼承good模組提供編輯資源內容的功能,可以修改資源並存檔。而rt模組透過good模組載入資源,再經由RunTime及Script將資源和物件關聯起來,並付予物件動作邏輯,最後再透過gx模組將結果呈現在畫面上。

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;...