發表文章

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

good基礎教學

圖片
這篇文章要來一步步製作一個彈跳球的範例,完成後相信對good會有一個基本的認識。 ; 1, 首先打開good編輯器,這時專案是空白的,按下Ctrl+S將空白專案儲存為bounceball.txt。 2, 接著在工具列上點擊NewTexture或選單Project->NewTexture加入用來作為彈跳球的face.png。 會使用png格式的圖檔的目的是,因為我們這張圖會用到鏤空效果,png格式可以帶alpha,我們可以很容易利用png的alpha來作alpha鏤空。 * 所有會使用到的圖形,我們都需要以加入貼圖的方式事先加入到good專案內才能夠使用。 3, 再用同樣的方式加入一張準備用來作為背景的圖形lace_0103.gif。 到此先按Ctrl+S存檔。(隨時存檔是個需要養成的好習慣) 4, 接下來在工具列上點擊NewSprite或選單Project->NewSprite加入一個精靈。一般在遊戲中,在畫面上會動的人物怪物等等我們把它叫作精靈(Sprite)物件,以我們要製作的範例來說,我們現在要建立一個表示彈跳球的精靈。 我們前面共加入了二張圖,要用來作為彈跳球的圖是tex1,這個名稱我們也可以在加入圖形時自己命名。這張圖的大小是107x107,所以我們在TileWidth/Height上填入了107和107,表示要使用整張圖。同時我們在name欄位自己填入了ball這個名字,當然也可以像加入圖形時保留空白,讓編輯器為我們自動加上名字。 完成後要加入一個空白的精靈,我們需要加入影格才能在畫面上顯示東西。因為我們已經事先設定好要使用整個圖形的大小,所以可以看到在右下角的貼圖檢視區上選取了整張的圖(透明紅色),接著按下貼圖上方的新增影格鈕加入影格。這樣就完成了精靈的設定,再按下Ctrl+S存檔。 5, 接下來在工具列上點擊NewLevel或選單Project->NewLevel新增一個空白關卡,準備來建立我們的遊戲場景。 good的關卡是我們實際執行遊戲時會呈現在畫面上的東西,我們可以建立很多不同的關卡,但同一時間只會有一個正在執行。我們可以在關卡上擺放各種物件,像是背景地圖或精靈等等,這些物件在關卡執行時都會顯示在畫面上。 首先我們加入一個背景圖,在關卡編輯器上方的工具列點擊NewTextureBg選擇tex2也就是我們事先加入的背景貼圖,完成

Stge基礎教學

這篇文章作為stge script的入門教學,會告訴你如何撰寫stge script來描述簡單的彈幕效果並整合到good裡,透過good的顯示功能呈現到畫面上。要補充說明的是,雖然stge script最初是為了射擊遊戲而設計的,但因為它也有基本足夠的彈性,所以也能夠作為粒子效果來應用。 ; 1, 首先打開good Game Editor,按下Ctrl+S儲存空白專案,命名為StgeTest1.txt。 2, 接著在工具列上點擊新增空白關卡(New Level),或點擊選單Project->New Level...加入空白關卡。 3, 在新關卡(level1)的屬性檢視器上的ClearColor,開啟顏色選擇對話盒並設定成黑色。 4, 在工具列上點擊新增空白腳本(New Script),或點擊選單Project->New Script...加入空白腳本,檔案名稱填StgeTest1.lua。 5, 在資源樹上點擊level1回到空白關卡1的屬性檢視器,在Script欄位上填入Level。 6, 按下Ctrl+S儲存檔案。 現在已建立基本的資源,接下來全部使用script來建立我們要的功能。首先撰寫一些good script作出基本框架。 Level= {} Level.OnCreate = function(param)   Stge.RunScript('StgeTest1') end Level.OnNewParticle = function(param, particle)   local obj = Good.GenObj(-1, -1)   Good.SetDim(obj, 0,0, 3, 3)   Good.SetBgColor(obj, 0xffff0000)   Stge.BindParticle(particle, obj) end Level.OnKillParticle = function(param, particle)   Good.KillObj(Stge.GetParticleBind(particle)) end 如上,我們有個叫作Level的table,並加入了三個空白的function,這三個function是good預先定義的event。OnCreate是當物件建立時會被呼叫,並且只會被呼叫一次,

究極超人

圖片
有圖為證!

關於C/C++的指標

我想應該還有不少人在使用指標上有些地方觀念不大清楚,比如說下面二個函式,那個是正確的?為什麼?像這樣的問題如果弄不清楚,寫出來的程式一定非常危險。 // 為簡化忽略檢查 void alloc_mem(char* p) // 版本1 {   p = new char[100]; } void alloc_mem(char** p) // 版本2 {   *p = new char[100]; } 如上,這個函式要配置大小是100個字元的記憶體並從傳入的參數p回傳,這二個版本除了輸入參數不一樣外大致上是一樣的;從第一個版本來看,參數是一個字元指標,記憶體配置出之後直接傳給p,如果觀念正確的人一定可以馬上指出這樣的寫法是錯誤的,第二個版本才是正確能work的。 現在就來說明為什麼,在這之前先要了解在C/C++中,函式的參數是如何傳遞的,在C/C++中函式的呼叫所傳入的參數是透過堆疊(Stack) 來傳入函式的,不懂什麼是堆疊也沒關係,就把它看成是另外一塊記憶體也行,當在程式中呼叫某個函式時,傳入的參數會先被複製到這塊記憶體中,當在函式中要使用這些參數時再從堆疊中去取出來。 以版本1的例子來說明,如下在程式中大概會這樣呼叫。 char* pp = NULL; alloc_mem(pp); pp一開始的初值是NULL,當呼叫alloc_mem時,pp的值會被複製到堆疊中(傳址),這種情況和以下的code事實上是對等的,只不過 p的值一開始被初始化成和pp的值一樣,p就好像一個區域變數一樣,一離開函式後這個變數就無效了,所以在外面的pp的值永遠都不會改變,同時在涵式中 new出來的記憶體也lost掉了。 void alloc_mem() {   char* p = new char[100]; } 再來看版本2,它的參數是一個指標的指標,這是什麼意思,我們先從實際使用上來看,如下。 char* pp = NULL; alloc_mem(&pp); 這次我們把pp這個變數的位址傳入涵式,所以在涵式中所得到的是pp這個變數的位址,在函式中p所含的內容是pp的位址,pp是一個char*形態的變數,p是一個指標它的內容是char*的形態,現在p已經指向pp了,所以對p的內容作改變,相對的pp的值也會跟著改變。 現在來看另外一個類似例子,會比較清楚些。 void change_v

分散式的線上遊戲伺服器

smallworld是smallworld2網路架構的第三層(應用層)。 smallworld2網路架構分成四個階層,最底層是串流層(Stream),負責提供最基本的TCP/IP串流封裝及連線管理。第二層為封包層(Network),負責提供格式化的封包支援以及完整的斷線處理機制。第三層為應用層(Smallworld),提供動態可擴展的分散式網路架構。第四層為遊戲應用層,提供和線上遊戲一般應用邏輯相關支援。 設計smallworld應用層最大的困難在於,必須讓使用者也就是應用程式的開發者,能夠以開發單一伺服器應用的單純方式,來開發一個分散式架構的多人連線應用程式,所有複雜的細節都需由底層處理掉。smallworld建立了二個概念來達成這個目標,分別是Scope及VirtualConnection。 對於伺服器S而言,所有訊息都是透過一條Connection傳送出去的。Connection的另一個端點可能是一個Client,也可能是另一個Server。而這個端點可能是與伺服器S有實際建立連線,也可能是間接和伺服器S建立連線。假如這個Connection與伺服器S間有實體連線,則伺服器S就能直接把訊息傳送給對方,否則就以間接的方式轉送過去。無論這個Connection是直接或間接的連線,對於伺服器S來說,是不必關心的事情,底層自動會想辨法把訊息傳送給這條Connection的對應的端點上去。所以對伺服器S而言,Connection是虛擬的。 Connection的取得一律透過定義Scope作為Filter來獲得。以線上遊戲為例。當一個玩家登入遊戲後,就會有許多個Scope和他建立關係,例如這個玩家的可見視野、玩家加入的組隊、公會、聊天室、P2P交易、商店等等。這些全都是Scope,概念一樣,只是定義不同。透過定義好的Scope,再拿這個定義作為Filter由可以到達的在線上的伺服器收集符合的Connection,之後就可以對這些Connection作操作。 以上是構成smallworld的二個重要Concept。 ------------------ 套用smalllworld的框架就可以很容易建立可以動態擴展的線上遊戲架構,不過在實際應用上還是會有其它問題。舉個例子說明:假如我以單一伺服器的方式實作了一支Server程式,這支程式可以處理完整的虛擬世界。伺服器執行起來後