• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            麒麟子

            ~~

            導(dǎo)航

            <2013年2月>
            272829303112
            3456789
            10111213141516
            17181920212223
            242526272812
            3456789

            統(tǒng)計(jì)

            常用鏈接

            留言簿(12)

            隨筆分類

            隨筆檔案

            Friends

            WebSites

            積分與排名

            最新隨筆

            最新評(píng)論

            閱讀排行榜

            評(píng)論排行榜

            #

            游戲中的資源管理――資源高速緩存

            《游戲中的資源管理――資源高速緩存》
            轉(zhuǎn)載請(qǐng)注明出處:http://groups.google.com/group/jianguhan


            1.什么是資源高速緩存
                資源高速緩存的原理與其它內(nèi)存高速緩存的工作原理是相似的。在游戲的狀態(tài)轉(zhuǎn)換過(guò)程中,有些數(shù)據(jù)是剛才使用過(guò)的,那么直接從資源高速緩存中載入即可。例如,RPG­游戲中主角從大地圖進(jìn)入一個(gè)房間,探索一番后主角退出房間,此時(shí)只要直接從緩存中載入大地圖數(shù)據(jù)即可,節(jié)省了從硬盤(pán)載入數(shù)據(jù)的時(shí)間,要知道從硬盤(pán)載入數(shù)據(jù)是非常­慢的。當(dāng)然,如果你的游戲所使用的數(shù)據(jù)文件很少,那么你可以在游戲運(yùn)行過(guò)程中把這些數(shù)據(jù)完全儲(chǔ)存在內(nèi)存中,而不使用資源高速緩存。


            2.一個(gè)簡(jiǎn)單的資源高速緩存管理器
                下面我將向你展示一個(gè)比較簡(jiǎn)單的資源高速緩存管理器,源代碼來(lái)自我上一個(gè)游戲,如果你需要知道更多關(guān)于資源高速緩存方面的知識(shí),請(qǐng)參考<<Game Coding Complete>>的第八章。
            首先,需要一個(gè)機(jī)制來(lái)唯一標(biāo)識(shí)一個(gè)資源,我們用下面這個(gè)結(jié)構(gòu)來(lái)做資源句柄:
            struct ResHandle
            {
                 ResHandle(std::string &resName, void *buffer, int size)
                 {
                     m_resName = resName;
                     m_size   = size;
                     m_buffer = buffer;
                 }


                 ~ResHandle()
                 {
                     if (m_buffer != 0) delete[] m_buffer;
                 }


                 std::string   m_resName;    //資源名
                 void          *m_buffer;    //資源句柄所標(biāo)識(shí)的資源
                 DWORD         m_size;       //資源所占內(nèi)存大小


            };


            好了,現(xiàn)在我們可以從資源名來(lái)找出這個(gè)資源了,接下來(lái)實(shí)現(xiàn)這個(gè)資源高速緩存管理器:
            class CacheManager
            {
            public:
                 CacheManager();
                 ~CacheManager();

                 //載入資源,resName為資源名,若載入成功size被設(shè)為該資源的大小
                //注意,管理中的資源不能在管理器外用delete顯示的刪除它
                void*    Load(std::string resName, DWORD *size = 0);
                //設(shè)置緩存大小,單位MB
                 void      SetCacheSize(int sizeMB)    { m_cacheSize = sizeMB * 1024 * 1024; }
                 //得到緩存大小,單位MB
                 int      GetCacheSize()              { return m_cacheSize / 1024 /1024; }


            private:
                 void     Free();                          //釋放lru鏈表中最后一個(gè)資源
                 void     *Update(ResHandle *res);         //更新lru鏈表
                 ResHandle *Find(std::string &resName);     //找出該資源名的資源句柄


            private:
                 DWORD m_cacheSize;     //緩存大小
                 DWORD m_allocated;     //已使用的緩存大小


            //lru鏈表,記錄最近被使用過(guò)的資源
                 std::list<ResHandle*>                m_lru;  
                //資源標(biāo)識(shí)映射
                 std::map<std::string, ResHandle*>    m_resources;

             

            };


            CacheManager:: CacheManager ()
            {
                 m_cacheSize = 0;
                 m_allocated = 0;


            }


            CacheManager::~ CacheManager ()
            {
                      while (!m_lru.empty()) Free();   //釋放所有管理中的資源


            }


            void * CacheManager::Load(std::string resName, DWORD *size)
            {
                 ResHandle *handle = Find(resName);   //查找該資源是否在緩存中

                 if (handle != 0) //如果找到該資源句柄,則返回該資源并更新lru鏈表
                 {
                     if (size != 0) *size = handle->m_size;
                     return Update(handle);
                 }
                 else
                 {
                     //先檢測(cè)資源大小
                     DWORD _size = 資源大小;


                     //是否有足夠空間?
                     while (_size > (m_cacheSize - m_allocated))
                     {
                          if (m_lru.empty()) break;
                          Free();
                     }
                     m_allocated += _size;


                     buffer = new char[_size];
            //在這里用任何你能想到的辦法載入資源文件到buffer
                     …
                     …


            //記錄當(dāng)前資源
                     ResHandle *handle = new ResHandle(resName, buffer, _size);
                     m_lru.push_front(handle);
                     m_resources[resName] = handle;


                     if (size != 0) *size = _size;
                     return buffer;
                 }


                 return 0;

             

            }


            void CacheManager::Free()
            {
                 std::list<ResHandle*>::iterator gonner = m_lru.end();
                 gonner--;
                 ResHandle *handle = *gonner;
                 m_lru.pop_back();
                 m_resources.erase(handle->m_resName);
                 m_allocated -= handle->m_size;
                 delete handle;


            }


            void * CacheManager::Update(ResHandle *res)
            {
                 m_lru.remove(res);
                 m_lru.push_front(res);
                 m_size = res->m_size;
                 return res->m_buffer;


            }

            ResHandle * CacheManager::Find(std::string &resName)
            {
                 std::map<std::string, ResHandle*>::iterator it = m_resources.find(resName);
                 if (it == m_resources.end()) return 0;
                 return (*it).second;


            }

            至此,你已經(jīng)可以在游戲中緩存任何你想緩存的資源了^_^

            3. 資源管理進(jìn)階
                至此你已經(jīng)可以在游戲中緩存任何你想緩存的資源了,但是你的任務(wù)還沒(méi)完成,當(dāng)你請(qǐng)求的資源存在于緩存之外時(shí),那個(gè)閃耀的硬盤(pán)燈可能就是玩家最感興趣的東西了。
            因此你必須根據(jù)不同的游戲類型使用不同的載入方式: 
                一次載入所有東西:適用于任何以界面或關(guān)卡切換的游戲 
                只在關(guān)鍵點(diǎn)載入資源:很多射擊游戲都使用這樣的設(shè)計(jì),如“半條命” 
                持續(xù)載入:適用于開(kāi)放型地圖的游戲,如“俠盜獵車(chē)手”
                如果有可能的話,你還可以使用緩存預(yù)測(cè)機(jī)制,當(dāng)CPU有額外時(shí)間的時(shí)候可以把未來(lái)可能用到的資源載入到資源高速緩存。
                最后,盡管在游戲的資源管理中資源打包不是必須的,但仍然建議大家把資源文件按類型分別打包到單一的文件中,這將為你節(jié)省磁盤(pán)空間,并加快游戲的載入速度。

            posted @ 2009-05-17 23:48 麒麟子 閱讀(606) | 評(píng)論 (0)編輯 收藏

            [轉(zhuǎn)] extern "C"

            前些天,編程序是用到了很久以前寫(xiě)的C程序,想把里面的函數(shù)利用起來(lái),連接發(fā)現(xiàn)出現(xiàn)了找不到具體函數(shù)的錯(cuò)誤:

            以下是假設(shè)舊的C程序庫(kù)

            C的頭文件

            /*-----------c.h--------------*/
            #ifndef _C_H_
            #define _C_H_
            extern int add(int x, int y);
            #endif

            C的源文件

            /*-----------c.c--------------*/
            int
            add(int x, int y){
            return
            x+y;
            }

            C++的調(diào)用

            /*-----------cpp.cpp--------------*/
            #include "c.h"
            void main()
            {

            add(1, 0);
            }

            這樣編譯會(huì)產(chǎn)生錯(cuò)誤cpp.obj : error LNK2001: unresolved external symbol "int __cdecl add(int,int)" (?add@@YAHHH@Z),原因是找不到add的目標(biāo)模塊

            這才令我想起C++重載的函數(shù)命名方式和C函數(shù)的命名方式,讓我們回顧一下:C中函數(shù)編譯后命名會(huì)在函數(shù)名前加以"_",比如add函數(shù)編譯成obj文件時(shí)的實(shí)際命名為_(kāi)add,而c++命名則不同,為了實(shí)現(xiàn)函數(shù)重載同樣的函數(shù)名add因參數(shù)的不同會(huì)被編譯成不同的名字

            例如

            int add(int , int)==>add@@YAHHH@Z,

            float add(float , float )==>add@@YAMMM@Z,

            以上是VC6的命名方式,不同的編譯器會(huì)不同,總之不同的參數(shù)同樣的函數(shù)名將編譯成不同目標(biāo)名,以便于函數(shù)重載是調(diào)用具體的函數(shù)。

            編譯cpp.cpp中編譯器在cpp文件中發(fā)現(xiàn)add(1, 0);的調(diào)用而函數(shù)聲明為extern int add(int x, int y);編譯器就決定去找add@@YAHHH@Z,可惜他找不到,因?yàn)镃的源文件把extern int add(int x, int y);編譯成_add了;

            為了解決這個(gè)問(wèn)題C++采用了extern "C",這就是我們的主題,想要利用以前的C程序庫(kù),那么你就要學(xué)會(huì)它,我們可以看以下標(biāo)準(zhǔn)頭文件你會(huì)發(fā)現(xiàn),很多頭文件都有以下的結(jié)構(gòu)

            #ifndef __H
            #define __H
            #ifdef __cplusplus
            extern "C" {
            #endif

            extern
            int f1(int, int);
            extern
            int f2(int, int);
            extern
            int f3(int, int);


            #ifdef __cplusplus
            }
            #endif

            #endif /*__H*/

            如果我們仿制該頭文件可以得到

            #ifndef _C_H_
            #define _C_H_
            #ifdef __cplusplus
            extern "C" {
            #endif

            extern
            int add(int, int);

            #ifdef __cplusplus
            }
            #endif

            #endif /* _C_H_ */

            這樣編譯

            /*-----------c.c--------------*/
            int
            add(int x, int y){
            return
            x+y;
            }

            這時(shí)源文件為*.c,__cplusplus沒(méi)有被定義,extern "C" {}這時(shí)沒(méi)有生效對(duì)于C他看到只是extern int add(int, int);
            add函數(shù)編譯成_add(int, int);

            而編譯c++源文件

            /*-----------cpp.cpp--------------*/
            #include "c.h"
            void main()
            {

            add(1, 0);
            }

            這時(shí)源文件為*.cpp,__cplusplus被定義,對(duì)于C++他看到的是extern "C" {extern int add(int, int);}編譯器就會(huì)知道 add(1, 0);調(diào)用的C風(fēng)格的函數(shù),就會(huì)知道去c.obj中找_add(int, int)而不是add@@YAHHH@Z

            這也就為什么DLL中常看見(jiàn)extern "C" {},windows是采用C語(yǔ)言編制他首先要考慮到C可以正確調(diào)用這些DLL,而用戶可能會(huì)使用C++而extern "C" {}就會(huì)發(fā)生作用

            posted @ 2009-05-17 23:46 麒麟子 閱讀(317) | 評(píng)論 (0)編輯 收藏

            [轉(zhuǎn)]C++中宏的使用

            關(guān)于## 和 #及#@的用法


            ## 是連接符號(hào) 連接兩個(gè)宏,##被稱為連接符(concatenator),用來(lái)將兩個(gè)Token連接為一個(gè)Token。注意這里連接的對(duì)象是Token就行,而不一定是宏的變
            量。比如你要做一個(gè)菜單項(xiàng)命令名和函數(shù)指針組成的結(jié)構(gòu)體的數(shù)組,并且希望在函數(shù)名和菜單項(xiàng)命令名之間有直觀的、名字上的關(guān)系。那就可以使用:宏參數(shù)##
            固定部分。當(dāng)然還可以n個(gè)##符號(hào)連接 n+1個(gè)Token,這個(gè)特性也是#符號(hào)所不具備的。
            #define LINK_MULTIPLE(a,b,c,d) a##_##b##_##c##_##d
            typedef struct _record_type LINK_MULTIPLE(name,company,position,salary);
            // 這里這個(gè)語(yǔ)句將展開(kāi)為:
            //      typedef struct _record_type name_company_position_salary;


            #@       功能是將其后面的宏參數(shù)進(jìn)行字符化。

            #define makechar(x)  #@x
            a
            = makechar(b);
            //a = 'b';


            #   是把名字代替成字符串,宏體中,#的功能是將其后面的宏參數(shù)進(jìn)行字符串化操作(Stringfication),簡(jiǎn)單說(shuō)就是在對(duì)它所引用的宏變量通過(guò)替換后在其左右各加上一個(gè)
            雙引號(hào)。
            #define WARN_IF(EXP)        \
                    do{ if (EXP)        \
                            fprintf(stderr, "Warning: " #EXP "\n"); }       \
                    while(0)
            那么實(shí)際使用中會(huì)出現(xiàn)下面所示的替換過(guò)程:
            WARN_IF (divider == 0);
            被替換為
            do {
                    if (divider == 0)
            fprintf(stderr, "Warning" "divider == 0" "\n");
            } while(0);
            這樣每次divider(除數(shù))為0的時(shí)候便會(huì)在標(biāo)準(zhǔn)錯(cuò)誤流上輸出一個(gè)提示信息。


            !IF constantexpression

            如果 constantexpression 計(jì)算結(jié)果為非零值,則處理 !IF 和下一個(gè) !ELSE!ENDIF 之間的語(yǔ)句。
            !ENDIF

            標(biāo)記 !IF!IFDEF!IFNDEF 塊的結(jié)尾。同一行上 !ENDIF 后面的所有文本被忽略。

            posted @ 2009-05-17 23:41 麒麟子 閱讀(325) | 評(píng)論 (0)編輯 收藏

            游戲開(kāi)發(fā)中常用的設(shè)計(jì)模式

            出自http://blog.csdn.net/duzhi5368/archive/2008/04/22/2314232.aspx

            使用設(shè)計(jì)模式來(lái)提高程序庫(kù)的重復(fù)利用性是大型程序項(xiàng)目開(kāi)發(fā)必須的。但是在“四人幫”的設(shè)計(jì)模式概述中提到了23種標(biāo)準(zhǔn)設(shè)計(jì)模式,不但難以記住,而且有些設(shè)計(jì)模式更多的適用于應(yīng)用程序開(kāi)發(fā),對(duì)游戲項(xiàng)目引擎設(shè)計(jì)并沒(méi)有很多的利用價(jià)值。根據(jù)經(jīng)驗(yàn),精挑細(xì)選后,篤志在這里記錄一些自認(rèn)為有利用價(jià)值的設(shè)計(jì)模式,以便之后自己設(shè)計(jì)時(shí)使用。

             

            一:觀察者Observer

             

            觀察者的設(shè)計(jì)意圖和作用是 它將對(duì)象與對(duì)象之間創(chuàng)建一種依賴關(guān)系,當(dāng)其中一個(gè)對(duì)象發(fā)生變化時(shí),它會(huì)將這個(gè)變化通知給與其創(chuàng)建關(guān)系的對(duì)象中,實(shí)現(xiàn)自動(dòng)化的通知更新。

             

                   游戲中觀察者的適用環(huán)境有

            1UI控件管理類。當(dāng)我們的GUI控件都使用觀察者模式后,那么用戶的任何界面相關(guān)操作和改變都將會(huì)通知其關(guān)聯(lián)對(duì)象-----我們的UI事件機(jī)。

            2:動(dòng)畫(huà)管理器。很多時(shí)候我們?cè)诓シ乓粋€(gè)動(dòng)畫(huà)楨的時(shí)候,對(duì)其Frame有很大興趣,此時(shí)我們?cè)O(shè)置一個(gè)FrameLister對(duì)象對(duì)其進(jìn)行監(jiān)視,獲得我們關(guān)心的事件進(jìn)行處理是必須的。

             

            觀察者偽代碼

            //-------------------------------------------------------------------------------------------------------

            // 被觀察對(duì)象目標(biāo)類

            Class Subject

            {

                   // 對(duì)本目標(biāo)綁定一個(gè)觀察者 Attach( Observer );

                   // 解除一個(gè)觀察者的綁定   DeleteAttach( Observer );

                   // 本目標(biāo)發(fā)生改變了,通知所有的觀察者,但沒(méi)有傳遞改動(dòng)了什么

                   Notity()

                   {

                          For ( …遍歷整個(gè)ObserverList …)

                          { pObserver ->Update(); }

            }

            // 對(duì)觀察者暴露的接口,讓觀察者可獲得本類有什么變動(dòng)GetState();

            }

            //-------------------------------------------------------------------------------------------------------

            // 觀察者/監(jiān)聽(tīng)者類

            Class Observer

            {

                   // 暴露給對(duì)象目標(biāo)類的函數(shù),當(dāng)監(jiān)聽(tīng)的對(duì)象發(fā)生了變動(dòng),則它會(huì)調(diào)用本函數(shù)通知觀察者

            Void Update ()

            {

            pSubject ->GetState(); // 獲取監(jiān)聽(tīng)對(duì)象發(fā)生了什么變化

            TODODisposeFun(); // 根據(jù)狀態(tài)不同,給予不同的處理

            }

            }

            //-------------------------------------------------------------------------------------------------------

             

            非程序語(yǔ)言描述

            AB的好朋友,對(duì)B的行為非常關(guān)心。B要出門(mén),此時(shí)A給了B一個(gè)警報(bào)器,告訴B說(shuō):“如果你有事,立刻按這個(gè)警報(bào)器告訴我。”。結(jié)果B在外面遇上了麻煩,按下警報(bào)器(Update()),B就知道A出了事,于是就調(diào)查一下B到底遇到了什么麻煩(GetState()),當(dāng)知道B原來(lái)是因?yàn)楸蝗舜蛄耍谑橇⒖踢M(jìn)行處理DisposeFun(),派了一群手下幫B打架。

            當(dāng)然關(guān)心A的人可以不止一個(gè),CD可能也對(duì)A很關(guān)心,于是A這里保存一個(gè)所有關(guān)心它的人的鏈表,當(dāng)遇到麻煩的時(shí)候,輪流給每個(gè)人一份通知。

             

            二:?jiǎn)渭J?/span>Singleton

            單件模式的設(shè)計(jì)意圖和作用是    保證一個(gè)類僅有一個(gè)實(shí)例,并且,僅提供一個(gè)訪問(wèn)它的全局訪問(wèn)點(diǎn)。

             

            游戲中適用于單件模式的有

            1:所有的Manger。在大部分的流行引擎中都存在著它的影子,例如SoundManager, ParticeManager等。

            2:大部分的工廠基類。這一點(diǎn)在大部分引擎中還是見(jiàn)不到的,實(shí)際上,我們的父類工廠采用唯一實(shí)例的話,我們子類進(jìn)行擴(kuò)展時(shí)也會(huì)有很大方便。

             

            單件模式偽代碼

            //-------------------------------------------------------------------------------------------------------

            Class Singleton

            {

                   Static MySingleton;       // 單件對(duì)象,全局唯一的。

                   Static Instance(){ return MySingleton;}              // 對(duì)外暴露接口

            }

            //-------------------------------------------------------------------------------------------------------

             

            三:迭代器Iterator

                  

                   迭代器設(shè)計(jì)意圖和作用是    提供一個(gè)方法,對(duì)一個(gè)組合聚合對(duì)象內(nèi)各個(gè)元素進(jìn)行訪問(wèn),同時(shí)又不暴露該對(duì)象類的內(nèi)部表示。

             

                   游戲中適用于迭代器模式的有    因?yàn)?/span>STL的流行,這個(gè)設(shè)計(jì)已經(jīng)廣為人知了,我們對(duì)任何形式的資源通一管理時(shí),不免會(huì)將其聚合起來(lái),或者List,或者Vector,我們都需要一個(gè)對(duì)其進(jìn)行訪問(wèn)的工具,迭代器無(wú)疑是一個(gè)利器。

             

                   迭代器偽代碼

            //-------------------------------------------------------------------------------------------------------

            // 迭代器基類

                   Class Iterator

            {

                          Virtual First();              

                          Virtual Next();

                          Virtual End();

                          Virtual CurrentItem();    // 返回當(dāng)前Item信息

            }

            //-------------------------------------------------------------------------------------------------------

            // 聚合體的基類

                   Class ItemAggregate

            {

                          Virtual CreateIterator(); // 創(chuàng)建訪問(wèn)自身的一個(gè)迭代器

            }

            //-------------------------------------------------------------------------------------------------------

            // 實(shí)例化的項(xiàng)目聚合體

                   Class InstanceItemAggregate : public ItemAggregate

                   {

                          CreateIterator(){ return new InstanceIterator(this); }

            }

            //-------------------------------------------------------------------------------------------------------

             

            四:訪問(wèn)者模式Visitor

             

                   訪問(wèn)者設(shè)計(jì)意圖和作用是    當(dāng)我們希望對(duì)一個(gè)結(jié)構(gòu)對(duì)象添加一個(gè)功能時(shí),我們能夠在不影響結(jié)構(gòu)的前提下,定義一個(gè)新的對(duì)其元素的操作。(實(shí)際上,我們只是把對(duì)該元素的操作分割給每個(gè)元素自身類中實(shí)現(xiàn)了而已)

             

                   游戲中適用于訪問(wèn)者模式的有    任何一個(gè)比較靜態(tài)的復(fù)雜結(jié)構(gòu)類中都適合采用一份訪問(wèn)者。這里的“比較靜態(tài)的復(fù)雜結(jié)構(gòu)類”意思是,該結(jié)構(gòu)類中元素繁多且種類復(fù)雜,且對(duì)應(yīng)的操作較多,但類很少進(jìn)行變化,我們就能夠?qū)ⅲ瑢?duì)這個(gè)結(jié)構(gòu)類元素的操作獨(dú)立出來(lái),避免污染這些元素對(duì)象。

                   1:例如場(chǎng)景管理器中管理的場(chǎng)景節(jié)點(diǎn),是非常繁多的,而且種類不一,例如有Ogre中的Root, Irrchit中就把攝象機(jī),燈光,Mesh,公告版,聲音都做為一種場(chǎng)景節(jié)點(diǎn),每個(gè)節(jié)點(diǎn)類型是不同的,雖然大家都有共通的Paint(),Hide()等方法,但方法的實(shí)現(xiàn)形式是不同的,當(dāng)我們外界調(diào)用時(shí)需要統(tǒng)一接口,那么我們很可能需要需要這樣的代碼

                   Hide( Object )

                   { if (Object == Mesh) HideMesh(); if (Object == Light) HideLight(); … }

            此時(shí)若我們需要增加一個(gè)Object新的類型對(duì)象,我們就不得不對(duì)該函數(shù)進(jìn)行修正。而我們可以這樣做,讓Mesh,Light他們都繼承于Object,他們都實(shí)現(xiàn)一個(gè)函數(shù)Hide(),那么就變成

                   Mesh::Hide( Visitor ) { Visitor.Hide (Mesh); }

                   Light::Hide(Visitor ){ Visitor.Hide (Light); }

            我們?cè)谡{(diào)用時(shí)只需要Object.Hide(Visitor){ return Visitor.Hide(Object); }

             

            這樣做的好處,我們免去了對(duì)重要函數(shù)的修正,Object.Hide(Visitor){}函數(shù)我們可以永久不變,但是壞處也是很明顯的,因?yàn)閷⒎椒◤膶?duì)象集合結(jié)構(gòu)中抽離出來(lái),就意味著我們每增加一個(gè)元素,它必須繼承于一個(gè)抽象的被訪問(wèn)者類,實(shí)現(xiàn)其全部函數(shù),這個(gè)工作量很大。

            所以,訪問(wèn)者是僅適合于一個(gè)裝載不同對(duì)象的大容器,但同時(shí)又要求這個(gè)容器的元素節(jié)點(diǎn)不應(yīng)當(dāng)有大的變動(dòng)時(shí)才使用。另外,廢話一句,訪問(wèn)者破壞了OO思想的。

             

                   訪問(wèn)者偽代碼

            //-------------------------------------------------------------------------------------------------------

            // 訪問(wèn)者基類

                   Class Visitor

            {

                          Virtual VisitElement( A ){ … };             // 訪問(wèn)的每個(gè)對(duì)象都要寫(xiě)這樣一個(gè)方法

                          Virtual VisitElement( B ){ … };

            }

             

            // 訪問(wèn)者實(shí)例A

            Class VisitorA

            {

                   VisitElement( A ){ … };         // 實(shí)際的處理函數(shù)

            VisitElement( B ){ … };        // 實(shí)際的處理函數(shù)

            }

             

            // 訪問(wèn)者實(shí)例B

            Class VisitorB

            {

                   VisitElement( A ){ … };         // 實(shí)際的處理函數(shù)

            VisitElement( B ){ … };        // 實(shí)際的處理函數(shù)

            }

             

             

            // 被訪問(wèn)者基類

            Class Element

            {

                   Virtual Accept( Visitor );        // 接受訪問(wèn)者

            }

             

            // 被訪問(wèn)者實(shí)例A

            Class ElementA

            {

                   Accecpt( Visitor v ){ v-> VisitElement(this); };    // 調(diào)用注冊(cè)到訪問(wèn)者中的處理函數(shù)

            }

             

            // 被訪問(wèn)者實(shí)例B

            Class ElementB

            {

                   Accecpt( Visitor v ){ v-> VisitElement(this); };    // 調(diào)用注冊(cè)到訪問(wèn)者中的處理函數(shù)

            }

             

            //-------------------------------------------------------------------------------------------------------

             

            五:外觀模式Façade

                  

                   外觀模式的設(shè)計(jì)意圖和作用是  將用戶接觸的表層和內(nèi)部子集的實(shí)現(xiàn)分離開(kāi)發(fā)。實(shí)際上,這個(gè)模式是個(gè)紙老虎,之后我們看偽代碼立刻就會(huì)發(fā)現(xiàn),這個(gè)模式實(shí)在用的太頻繁了。

             

                   游戲中需要使用外觀模式的地方是   這個(gè)非常多了,舉幾個(gè)比較重要的。

                   1:實(shí)現(xiàn)平臺(tái)無(wú)關(guān)性。跨平臺(tái)跨庫(kù)的函數(shù)調(diào)用。

                   2:同一個(gè)接口去讀取不同的資源。

                   3:硬件自動(dòng)識(shí)別處理系統(tǒng)。

             

                   外觀模式偽代碼

            //-------------------------------------------------------------------------------------------------------

                   // 用戶使用的接口類

                   Class Interface

            {

            // 暴露出來(lái)的函數(shù)接口函數(shù),有且僅有一個(gè),但內(nèi)部實(shí)現(xiàn)是調(diào)用了兩個(gè)類

                   Void InterfaceFun()

            {

                   // 根據(jù)某種條件,底層自主的選擇使用AB的方法。用戶無(wú)須關(guān)心底層實(shí)現(xiàn)

                   If ( XXX )

                   {

                          ActualA->Fun();

            }

            Else

            {

                   ActualB->Fun();

            }

            };   

            }

             

            // 實(shí)際的實(shí)現(xiàn),不暴露給用戶知道

            Class ActualA

            {

                   Void Fun();

            }

             

            // 實(shí)際的實(shí)現(xiàn),不暴露給用戶知道

            Class ActualB

            {

                   Void Fun();

            }

             

            怎么樣,紙老虎吧,看起來(lái)很高深摸測(cè)的命名而已。

            //-------------------------------------------------------------------------------------------------------

             

            六:抽象工廠模式AbstractFactory

                  

                   抽象工廠的設(shè)計(jì)意圖和作用是    封裝出一個(gè)接口,這個(gè)接口負(fù)責(zé)創(chuàng)建一系列互相關(guān)聯(lián)的對(duì)象,但用戶在使用接口時(shí)不需要指定對(duì)象所在的具體的類。從中文命名也很容易明白它是進(jìn)行批量生產(chǎn)的一個(gè)生產(chǎn)工廠的作用。

             

                   游戲中使用抽象工廠的地方有    基本上任何有批量的同類形式的子件地方就會(huì)有工廠的存在。(補(bǔ)充一句:下面代碼中的ConcreteFactory1實(shí)例工廠就是工廠,而抽象工廠僅僅是工廠的一個(gè)抽象層而已。

            1:例如,在音頻方面,一個(gè)音頻的抽象工廠派生出不同的工廠,有音樂(lè)工廠,音效工廠。音效工廠中又有一個(gè)創(chuàng)建3D音效節(jié)點(diǎn)的方法,一個(gè)創(chuàng)建普通音效節(jié)點(diǎn)的方法。最終用戶只需要SoundFactory->Create3DNode( pFileName );就可以創(chuàng)建一個(gè)節(jié)點(diǎn)了。

            2:場(chǎng)景對(duì)象。

            3:渲染對(duì)象。

            4:等等……

            工廠與單件,管理器Manager關(guān)系一定是非常緊密的。

             

                   抽象工廠偽代碼

            //-------------------------------------------------------------------------------------------------------

                   class AbstractProductA {}; // 抽象的產(chǎn)品A基類
              class AbstractProductB {}; //抽象的產(chǎn)品B基類

             

            // 抽象工廠基類
              class AbstractFactory
              
            {
              
            public:
               virtual AbstractProductA* CreateProductA() = 0 ;// 創(chuàng)建
            ProductA
               virtual AbstractProductB* CreateProductB() = 0 ;// 創(chuàng)建
            ProductB
                   } ;

             

              class ProductA1 : public AbstractProductA {};    // 產(chǎn)品A的實(shí)例1
              class ProductA2 : public AbstractProductA {};    // 產(chǎn)品A的實(shí)例2

              class ProductB1 : public AbstractProductB {};    // 產(chǎn)品B的實(shí)例1
              class ProductB2 : public AbstractProductB {};    // 產(chǎn)品B的實(shí)例2

             

            // 實(shí)例工廠1

                 class ConcreteFactory1 : public AbstractFactory
              
            {
                
            virtual AbstractProductA* CreateProductA() { return new ProductA1() ; }
                
            virtual AbstractProductB* CreateProductB() { return new ProductB1() ; }
                   static ConcreteFactory1* Instance() { }        // 實(shí)例工廠盡量使用單件模式

              } ;

             

            // 實(shí)例工廠2

            class ConcreteFactory2 : public AbstractFactory
              {
                
            virtual AbstractProductA* CreateProductA() { return new ProductA2() ; }
                
            virtual AbstractProductB* CreateProductB() { return new ProductB2() ; }
                static ConcreteFactory2* Instance() {} 
                   // 實(shí)例工廠盡量使用單件模式
              } ;

            }

            //-------------------------------------------------------------------------------------------------------

            客戶端代碼

            Void main()
            {
              AbstractFactory *pFactory1 = ConcreteFactory1::Instance() ;
              
            AbstractProductA *pProductA1 = pFactory1->CreateProductA() ;
              
            AbstractProductB *pProductB1 = pFactory1->CreateProductB() ;
              
            AbstractFactory *pFactory2 = ConcreteFactory2::Instance() ;
              
            AbstractProductA *pProductA2 = pFactory2->CreateProductA() ;
              
            AbstractProductB *pProductB2 = pFactory2->CreateProductB() ;
            }

            //-------------------------------------------------------------------------------------------------------

            posted @ 2009-05-17 23:36 麒麟子 閱讀(886) | 評(píng)論 (0)編輯 收藏

            [轉(zhuǎn)]通過(guò)例子學(xué)習(xí)Lua

            據(jù)說(shuō)本文作者是OGDEV的HACK達(dá)人

            通過(guò)例子學(xué)習(xí)Lua(1) ---- Hello World

            1.前言
            游戲中少不了用到腳本語(yǔ)言. Lua是一種和C/C++結(jié)合非常緊密的腳本語(yǔ)言,效率極高。
            一般是對(duì)時(shí)間要求比較高的地方用C++寫(xiě),而經(jīng)常需要改動(dòng)的地方用Lua寫(xiě)。

            偶最近在學(xué)習(xí)Lua, 所以寫(xiě)出心得和大家共享. Lua是一種完全免費(fèi)的腳本語(yǔ)言,
            它的官方網(wǎng)站在http://www.lua.org.在網(wǎng)站上可以下載到lua的源碼, 沒(méi)有可
            執(zhí)行版本, 不過(guò)不用擔(dān)心, 因?yàn)閘ua源碼可以在任何一種C/C++的編譯器上編譯.

            如果要學(xué)習(xí)Lua, 官方網(wǎng)站上的Reference是必備的,上面有每個(gè)命令的用法,非常詳
            細(xì)。
            參考手冊(cè) http://www.lua.org/manual/5.0/
            作者寫(xiě)的Programming in Lua http://www.lua.org/pil/

            2.編譯
            如果用的VC, 可以下載所需的project文件,地址在
            http://sourceforge.net/project/showfiles.php?group_id=32250&package_id=115604
            偶用的是cygwin和linux, 打入以下命令即可,
            tar -zxvf lua-5.0.2.tar.gz
            cd lua-5.0.2
            sh ./configure
            make
            這樣就OK了。
            為了以后使用方便,最好把bin目錄加入到path里面。

            3."Hello, world!"
            現(xiàn)在開(kāi)始偶們的第一個(gè)小程序"Hello, world!"
            把以下程序打入文件e01.lua

            例1:e01.lua
            -- Hello World in Lua
            print("Hello World.")

            Lua有兩種執(zhí)行方式,一種是嵌入到C程序中執(zhí)行,還有一種是直接從命令行方式下執(zhí)
            行。
            這里為了調(diào)試方便,采用第二種方式,執(zhí)行命令 lua e01.lua

            輸出結(jié)果應(yīng)該是:
            Hello World.

            4.程序說(shuō)明
            第一行 -- Hello World in Lua
            這句是注釋,其中--和C++中的//意思是一樣的
            第二行 print("Hello World.")
            調(diào)用lua內(nèi)部命令print,輸出"Hello World."字符串到屏幕,Lua中的字符串全部是
            由"括起來(lái)的。
            這個(gè)命令是一個(gè)函數(shù)的調(diào)用,print是lua的一個(gè)函數(shù),而"Hello World."是print的參
            數(shù)。

            5.試試看
            在Lua中有不少字符串的處理操作,本次的課后試試看的內(nèi)容就是,找出連接兩個(gè)字符串
            的操作,
            并且print出來(lái)。
            --

            通過(guò)例子學(xué)習(xí)Lua(2) --- Lua基礎(chǔ)

            1. 函數(shù)的使用
            以下程序演示了如何在Lua中使用函數(shù), 及局部變量
            例e02.lua
            -- functions
            function pythagorean(a, b)   
                   local c2 = a^2 + b^2   
                   return sqrt(c2)
            end
            print(pythagorean(3,4))

            運(yùn)行結(jié)果
            5

            程序說(shuō)明
            在Lua中函數(shù)的定義格式為:
            function 函數(shù)名(參數(shù))
                   ...
            end
            與Pascal語(yǔ)言不同, end不需要與begin配對(duì), 只需要在函數(shù)結(jié)束后打個(gè)end就可以了.
            本例函數(shù)的作用是已知直角三角形直角邊, 求斜邊長(zhǎng)度. 參數(shù)a,b分別表示直角邊長(zhǎng),
            在函數(shù)內(nèi)定義了local形變量用于存儲(chǔ)斜邊的平方. 與C語(yǔ)言相同, 定義在函數(shù)內(nèi)的代
            碼不會(huì)被直接執(zhí)行, 只有主程序調(diào)用時(shí)才會(huì)被執(zhí)行.
            local表示定義一個(gè)局部變量, 如果不加local剛表示c2為一個(gè)全局變量, local的作用域
            是在最里層的end和其配對(duì)的關(guān)鍵字之間, 如if ... end, while ... end等。全局變量

            作用域是整個(gè)程序。

            2. 循環(huán)語(yǔ)句
            例e03.lua
            -- Loops
            for i=1,5 do   
                   print("i is now " .. i)
            end

            運(yùn)行結(jié)果
            i is now 1
            i is now 2
            i is now 3
            i is now 4
            i is now 5

            程序說(shuō)明
            這里偶們用到了for語(yǔ)句
            for 變量 = 參數(shù)1, 參數(shù)2, 參數(shù)3 do
                   循環(huán)體
            end
            變量將以參數(shù)3為步長(zhǎng), 由參數(shù)1變化到參數(shù)2
            例如:   
            for i=1,f(x) do print(i) end
            for i=10,1,-1 do print(i) end

            這里print("i is now " .. i)中,偶們用到了..,這是用來(lái)連接兩個(gè)字符串的,
            偶在(1)的試試看中提到的,不知道你們答對(duì)了沒(méi)有。
            雖然這里i是一個(gè)整型量,Lua在處理的時(shí)候會(huì)自動(dòng)轉(zhuǎn)成字符串型,不需偶們費(fèi)心。

            3. 條件分支語(yǔ)句
            例e04.lua
            -- Loops and conditionals
            for i=1,5 do
                   print(“i is now “ .. i)
                     if i < 2 then       
                           print(“small”)   
                     elseif i < 4 then       
                           print(“medium”)   
                     else       
                           print(“big”)   
                     end
            end

            運(yùn)行結(jié)果
            i is now 1
            small
            i is now 2
            medium
            i is now 3
            medium
            i is now 4
            big
            i is now 5
            big

            程序說(shuō)明
            if else用法比較簡(jiǎn)單, 類似于C語(yǔ)言, 不過(guò)此處需要注意的是整個(gè)if只需要一個(gè)end,
            哪怕用了多個(gè)elseif, 也是一個(gè)end.
            例如
                if op == "+" then
                  r = a + b
                elseif op == "-" then
                  r = a - b
                elseif op == "*" then
                  r = a*b
                elseif op == "/" then
                  r = a/b
                else
                  error("invalid operation")
                end


            4.試試看
            Lua中除了for循環(huán)以外, 還支持多種循環(huán), 請(qǐng)用while...do和repeat...until改寫(xiě)本文
            中的for程序

            --
            通過(guò)例子學(xué)習(xí)Lua(3) ---- 數(shù)據(jù)結(jié)構(gòu)

            1.簡(jiǎn)介
            Lua語(yǔ)言只有一種基本數(shù)據(jù)結(jié)構(gòu), 那就是table, 所有其他數(shù)據(jù)結(jié)構(gòu)如數(shù)組啦,
            類啦, 都可以由table實(shí)現(xiàn).

            2.table的下標(biāo)
            例e05.lua
            -- Arrays
            myData = {}
            myData[0] = “foo”
            myData[1] = 42

            -- Hash tables
            myData[“bar”] = “baz”

            -- Iterate through the
            -- structure
            for key, value in myData do
            print(key .. “=“ .. value)
            end

            輸出結(jié)果
            0=foo
            1=42
            bar=baz

            程序說(shuō)明
            首先定義了一個(gè)table myData={}, 然后用數(shù)字作為下標(biāo)賦了兩個(gè)值給它. 這種
            定義方法類似于C中的數(shù)組, 但與數(shù)組不同的是, 每個(gè)數(shù)組元素不需要為相同類型,
            就像本例中一個(gè)為整型, 一個(gè)為字符串.

            程序第二部分, 以字符串做為下標(biāo), 又向table內(nèi)增加了一個(gè)元素. 這種table非常
            像STL里面的map. table下標(biāo)可以為L(zhǎng)ua所支持的任意基本類型, 除了nil值以外.

            Lua對(duì)Table占用內(nèi)存的處理是自動(dòng)的, 如下面這段代碼
            a = {}
            a["x"] = 10
            b = a -- `b' refers to the same table as `a'
            print(b["x"]) --> 10
            b["x"] = 20
            print(a["x"]) --> 20
            a = nil -- now only `b' still refers to the table
            b = nil -- now there are no references left to the table
            b和a都指向相同的table, 只占用一塊內(nèi)存, 當(dāng)執(zhí)行到a = nil時(shí), b仍然指向table,
            而當(dāng)執(zhí)行到b=nil時(shí), 因?yàn)闆](méi)有指向table的變量了, 所以Lua會(huì)自動(dòng)釋放table所占內(nèi)存

            3.Table的嵌套
            Table的使用還可以嵌套,如下例
            例e06.lua
            -- Table ‘constructor’
            myPolygon = {
            color=“blue”,
            thickness=2,
            npoints=4;
            {x=0, y=0},
            {x=-10, y=0},
            {x=-5, y=4},
            {x=0, y=4}
            }

            -- Print the color
            print(myPolygon[“color”])

            -- Print it again using dot
            -- notation
            print(myPolygon.color)

            -- The points are accessible
            -- in myPolygon[1] to myPolygon[4]

            -- Print the second point’s x
            -- coordinate
            print(myPolygon[2].x)

            程序說(shuō)明
            首先建立一個(gè)table, 與上一例不同的是,在table的constructor里面有{x=0,y=0},
            這是什么意思呢? 這其實(shí)就是一個(gè)小table, 定義在了大table之內(nèi), 小table的
            table名省略了.
            最后一行myPolygon[2].x,就是大table里面小table的訪問(wèn)方式.

            --
            通過(guò)例子學(xué)習(xí)Lua(4) -- 函數(shù)的調(diào)用

            1.不定參數(shù)
            例e07.lua
            -- Functions can take a
            -- variable number of
            -- arguments.
            function funky_print (...)
            for i=1, arg.n do
            print("FuNkY: " .. arg)
            end
            end

            funky_print("one", "two")

            運(yùn)行結(jié)果
            FuNkY: one
            FuNkY: two

            程序說(shuō)明
            * 如果以...為參數(shù), 則表示參數(shù)的數(shù)量不定.
            * 參數(shù)將會(huì)自動(dòng)存儲(chǔ)到一個(gè)叫arg的table中.
            * arg.n中存放參數(shù)的個(gè)數(shù). arg[]加下標(biāo)就可以遍歷所有的參數(shù).


            2.以table做為參數(shù)
            例e08.lua
            -- Functions with table
            -- parameters
            function print_contents(t)
            for k,v in t do
            print(k .. "=" .. v)
            end
            end
            print_contents{x=10, y=20}

            運(yùn)行結(jié)果
            x=10
            y=20

            程序說(shuō)明
            * print_contents{x=10, y=20}這句參數(shù)沒(méi)加圓括號(hào), 因?yàn)橐詥蝹€(gè)table為參數(shù)的時(shí)候,
            不需要加圓括號(hào)
            * for k,v in t do 這個(gè)語(yǔ)句是對(duì)table中的所有值遍歷, k中存放名稱, v中存放值


            3.把Lua變成類似XML的數(shù)據(jù)描述語(yǔ)言
            例e09.lua
            function contact(t)
            -- add the contact ‘t’, which is
            -- stored as a table, to a database
            end

            contact {
            name = "Game Developer",
            email = "hack@ogdev.net",
            url = "http://www.ogdev.net,
            quote = [[
            There are
            10 types of people
            who can understand binary.]]
            }

            contact {
            -- some other contact
            }

            程序說(shuō)明
            * 把function和table結(jié)合, 可以使Lua成為一種類似XML的數(shù)據(jù)描述語(yǔ)言
            * e09中contact{...}, 是一種函數(shù)的調(diào)用方法, 不要弄混了
            * [[...]]是表示多行字符串的方法
            * 當(dāng)使用C API時(shí)此種方式的優(yōu)勢(shì)更明顯, 其中contact{..}部分可以另外存成一配置文


            4.試試看
            想想看哪些地方可以用到例e09中提到的配置方法呢?

            --

            通過(guò)例子學(xué)習(xí)Lua(5) ---- Lua與C交互入門(mén)

            1.簡(jiǎn)介

            Lua與C/C++結(jié)合是很緊密的, Lua與C++交互是建立在Lua與C的基礎(chǔ)上的, 所
            以偶先從Lua與C講起.

            正如第一講所說(shuō), 運(yùn)行Lua程序或者說(shuō)調(diào)用Lua主要有兩種方式:
            * 通過(guò)命令行執(zhí)行"Lua"命令
            * 通過(guò)Lua的C庫(kù)
            雖然此前偶們一直用第一種方式, 但偶要告訴你, 通過(guò)Lua的C庫(kù)執(zhí)行才是游戲中
            常用的方式.

            2.Lua的C庫(kù)

            Lua的C庫(kù)可以做為Shared Library調(diào)用, 但一般開(kāi)發(fā)游戲時(shí)會(huì)把Lua的所有源程序
            都包含在內(nèi), 并不把Lua編譯成共享庫(kù)的形式. 因?yàn)長(zhǎng)ua程序只有100多K, 而且?guī)缀?
            可以在任何編譯器下Clean Compile. 帶Lua源程序的另一個(gè)好處時(shí), 可以隨時(shí)對(duì)Lua
            本身進(jìn)行擴(kuò)充, 增加偶們所需的功能.

            Lua的C庫(kù)提供一系列API:
            * 管理全局變量
            * 管理tables
            * 調(diào)用函數(shù)
            * 定義新函數(shù), 這也可以完全由C實(shí)現(xiàn)
            * 垃圾收集器Garbage collector, 雖然Lua可以自動(dòng)進(jìn)行, 但往往不是立即執(zhí)行的,
            所以對(duì)實(shí)時(shí)性要求比較高的程序, 會(huì)自己調(diào)用垃圾收集器
            * 載入并執(zhí)行Lua程序, 這也可以由Lua自身實(shí)現(xiàn)
            * 任何Lua可以實(shí)現(xiàn)的功能, 都可以通過(guò)Lua的C API實(shí)現(xiàn), 這對(duì)于優(yōu)化程序的運(yùn)行速度
            有幫助. 經(jīng)常調(diào)用的共用的Lua程序片斷可以轉(zhuǎn)成C程序, 以提高效率. 連Lua都是C寫(xiě)的
            還有什么C不能實(shí)現(xiàn)呢?

            3.Lua與C集成的例子
            例e10.c
            /* A simple Lua interpreter. */
            #include <stdio.h>
            #include <lua.h>
            int main(int argc, char *argv[]) {
            char line[BUFSIZ];
            lua_State *L = lua_open(0);
            while (fgets(line, sizeof(line), stdin) != 0)
            lua_dostring(L, line);
            lua_close(L);
            return 0;
            }

            編譯
            Linux/Cygwin
            * 先編譯Lua, 并把頭文件放入include路徑
            * gcc e10.c -llua -llualib -o e10

            VC6/VC2003
            * 先編譯Lua, 在Option中設(shè)置頭文件和庫(kù)文件路徑
            * 新建工程,在工程配置中加入附加庫(kù)lua.lib和lualib.lib
            * 編譯成exe

            運(yùn)行結(jié)果
            本程序的功能是實(shí)現(xiàn)一個(gè)Lua解釋器, 輸入的每行字符都會(huì)被解釋成Lua并執(zhí)行.

            程序說(shuō)明
            * #include <lua.h> 包含lua頭文件, 然后才可以使用API
            * lua_State *L = lua_open(0) 打開(kāi)一個(gè)Lua執(zhí)行器
            * fgets(line, sizeof(line), stdin) 從標(biāo)準(zhǔn)輸入里讀入一行
            * lua_dostring(L, line) 執(zhí)行此行
            * lua_close(L) 關(guān)閉Lua執(zhí)行器


            例e11.c
            /* Another simple Lua interpreter. */
            #include <stdio.h>
            #include <lua.h>
            #include <lualib.h>
            int main(int argc, char *argv[]) {
            char line[BUFSIZ];
            lua_State *L = lua_open(0);
            lua_baselibopen(L);
            lua_iolibopen(L);
            lua_strlibopen(L);
            lua_mathlibopen(L);
            while (fgets(line, sizeof(line), stdin) != 0)
            lua_dostring(L, line);
            lua_close(L);
            return 0;
            }

            運(yùn)行結(jié)果
            本程序的功能是實(shí)現(xiàn)一個(gè)Lua解釋器, 輸入的每行字符都會(huì)被解釋成Lua并執(zhí)行.
            與上例不同的是, 本例調(diào)用了Lua的一些標(biāo)準(zhǔn)庫(kù).

            程序說(shuō)明
            * #include <lualib.h> 包含Lua的標(biāo)準(zhǔn)庫(kù)
            * 以下這幾行是用來(lái)讀入Lua的一些庫(kù), 這樣偶們的Lua程序就可以有更多的功能.
            lua_baselibopen(L);
            lua_iolibopen(L);
            lua_strlibopen(L);
            lua_mathlibopen(L);

            4.試試看
            把上面兩個(gè)小例子在你熟悉的編譯器中編譯執(zhí)行, 并試試能否與Lua源碼樹(shù)一起編譯

            --
            通過(guò)例子學(xué)習(xí)Lua(6) ---- C/C++中用Lua函數(shù)

            參考英文文檔http://tonyandpaige.com/tutorials/lua2.html

            1.簡(jiǎn)介
            偶們這次主要說(shuō)說(shuō)怎么由Lua定義函數(shù), 然后在C或者C++中調(diào)用. 這里偶們
            暫不涉及C++的對(duì)象問(wèn)題, 只討論調(diào)用函數(shù)的參數(shù), 返回值和全局變量的使用.

            2.
            這里偶們?cè)趀12.lua里先定義一個(gè)簡(jiǎn)單的add(), x,y為加法的兩個(gè)參數(shù),
            return 直接返回相加后的結(jié)果.

            例e12.lua
            -- add two numbers
            function add ( x, y )
            return x + y
            end

            在前一次里, 偶們說(shuō)到 lua_dofile() 可以直接在C中執(zhí)行l(wèi)ua文件. 因?yàn)榕紓?
            這個(gè)程序里只定義了一個(gè)add()函數(shù), 所以程序執(zhí)行后并不直接結(jié)果, 效果相當(dāng)
            于在C中定義了一個(gè)函數(shù)一樣.

            Lua的函數(shù)可以有多個(gè)參數(shù), 也可以有多個(gè)返回值, 這都是由棧(stack)實(shí)現(xiàn)的.
            需要調(diào)用一個(gè)函數(shù)時(shí), 就把這個(gè)函數(shù)壓入棧, 然后順序壓入所有參數(shù), 然后用
            lua_call()調(diào)用這個(gè)函數(shù). 函數(shù)返回后, 返回值也是存放在棧中. 這個(gè)過(guò)程和
            匯編執(zhí)行函數(shù)調(diào)用的過(guò)程是一樣的.

            例e13.cpp 是一個(gè)調(diào)用上面的Lua函數(shù)的例子
            #include <stdio.h>

            extern "C" { // 這是個(gè)C++程序, 所以要extern "C",
            // 因?yàn)閘ua的頭文件都是C格式的
            #include "lua.h"
            #include "lualib.h"
            #include "lauxlib.h"
            }

            /* the Lua interpreter */
            lua_State* L;

            int luaadd ( int x, int y )
            {
            int sum;

            /* the function name */
            lua_getglobal(L, "add");

            /* the first argument */
            lua_pushnumber(L, x);

            /* the second argument */
            lua_pushnumber(L, y);

            /* call the function with 2
            arguments, return 1 result */
            lua_call(L, 2, 1);

            /* get the result */
            sum = (int)lua_tonumber(L, -1);
            lua_pop(L, 1);

            return sum;
            }

            int main ( int argc, char *argv[] )
            {
            int sum;

            /* initialize Lua */
            L = lua_open();

            /* load Lua base libraries */
            lua_baselibopen(L);

            /* load the script */
            lua_dofile(L, "e12.lua");

            /* call the add function */
            sum = luaadd( 10, 15 );

            /* print the result */
            printf( "The sum is %d\n", sum );

            /* cleanup Lua */
            lua_close(L);

            return 0;
            }

            程序說(shuō)明:
            main中過(guò)程偶們上次已經(jīng)說(shuō)過(guò)了, 所以這次只說(shuō)說(shuō)luaadd的過(guò)程
            * 首先用lua_getglobal()把a(bǔ)dd函數(shù)壓棧
            * 然后用lua_pushnumber()依次把x,y壓棧
            * 然后調(diào)用lua_call(), 并且告訴程序偶們有兩個(gè)參數(shù)一個(gè)返回值
            * 接著偶們從棧頂取回返回值, 用lua_tonumber()
            * 最后偶們用lua_pop()把返回值清掉

            運(yùn)行結(jié)果:
            The sum is 25

            編譯方法
            Linux下把程序存成e13.cpp
            g++ e13.cpp -llua -llualib -o e13
            ./e13

            VC下編譯方法
            * 首先建立一個(gè)空的Win32 Console Application Project
            * 把e13.cpp加入工程中
            * 點(diǎn)project setting,然后設(shè)置link選項(xiàng), 再加上lua.lib lualib.lib兩個(gè)額外的庫(kù)
            * 最后編譯

            建立好的project可以在這里下載
            VC http://tonyandpaige.com/tutorials/luaadd.zip
            Linux http://tonyandpaige.com/tutorials/luaadd.tar.gz

            3.全局變量
            上面偶們用到了lua_getglobal()但并沒(méi)有詳細(xì)講, 這里偶們?cè)倥e兩個(gè)小例子來(lái)說(shuō)下全局
            變量
            lua_getglobal()的作用就是把lua中全局變量的值壓入棧
            lua_getglobal(L, "z");
            z = (int)lua_tonumber(L, 1);
            lua_pop(L, 1);
            假設(shè)Lua程序中定義了一個(gè)全局變量z, 這段小程序就是把z的值取出放入C的變量z中.

            另外Lua中還有一個(gè)對(duì)應(yīng)的函數(shù)lua_setglobal(), 作用是用棧頂?shù)闹堤畛渲付ǖ娜肿?

            lua_pushnumber(L, 10);
            lua_setglobal(L, "z");
            例如這段小程序就是把lua中的全局變量z設(shè)為10, 如果lua中未定義z的話, 就會(huì)自動(dòng)創(chuàng)
            建一個(gè)
            全局變量z并設(shè)為10.

            4.試試看
            自己寫(xiě)個(gè)函數(shù)用C/C++來(lái)調(diào)用下試試

            --
            通過(guò)例子學(xué)習(xí)Lua(7) ---- Lua中調(diào)用C/C++函數(shù)

            1.前言
            上次偶說(shuō)到從C/C++中調(diào)用Lua的函數(shù), 然后就有朋友問(wèn)從Lua中如何調(diào)用C/C++的
            函數(shù), 所以偶們這次就來(lái)說(shuō)說(shuō)這個(gè)問(wèn)題. 首先偶們會(huì)在C++中建立一個(gè)函數(shù), 然后
            告知Lua有這個(gè)函數(shù), 最后再執(zhí)行它. 另外, 由于函數(shù)不是在Lua中定義的, 所以
            無(wú)法確定函數(shù)的正確性, 可能在調(diào)用過(guò)程中會(huì)出錯(cuò), 因此偶們還會(huì)說(shuō)說(shuō)Lua出錯(cuò)處
            理的問(wèn)題.

            2.Lua中調(diào)用C函數(shù)
            在lua中是以函數(shù)指針的形式調(diào)用函數(shù), 并且所有的函數(shù)指針都必須滿足如下此種
            類型:
            typedef int (*lua_CFunction) (lua_State *L);

            也就是說(shuō), 偶們?cè)贑++中定義函數(shù)時(shí)必須以lua_State為參數(shù), 以int為返回值才能
            被Lua所調(diào)用. 但是不要忘記了, 偶們的lua_State是支持棧的, 所以通過(guò)棧可以
            傳遞無(wú)窮個(gè)參數(shù), 大小只受內(nèi)存大小限制. 而返回的int值也只是指返回值的個(gè)數(shù)
            真正的返回值都存儲(chǔ)在lua_State的棧中. 偶們通常的做法是做一個(gè)wrapper, 把
            所有需要調(diào)用的函數(shù)都wrap一下, 這樣就可以調(diào)用任意的函數(shù)了.

            下面這個(gè)例子是一個(gè)C++的average()函數(shù), 它將展示如何用多個(gè)參數(shù)并返回多個(gè)值

            例e14.cpp
            #include <stdio.h>

            extern "C" {
            #include "lua.h"
            #include "lualib.h"
            #include "lauxlib.h"
            }

            /* the Lua interpreter */
            lua_State* L;

            static int average(lua_State *L)
            {
            /* get number of arguments */
            int n = lua_gettop(L);
            double sum = 0;
            int i;

            /* loop through each argument */
            for (i = 1; i <= n; i++)
            {
            /* total the arguments */
            sum += lua_tonumber(L, i);
            }

            /* push the average */
            lua_pushnumber(L, sum / n);

            /* push the sum */
            lua_pushnumber(L, sum);

            /* return the number of results */
            return 2;
            }

            int main ( int argc, char *argv[] )
            {
            /* initialize Lua */
            L = lua_open();

            /* load Lua base libraries */
            lua_baselibopen(L);

            /* register our function */
            lua_register(L, "average", average);

            /* run the script */
            lua_dofile(L, "e15.lua");

            /* cleanup Lua */
            lua_close(L);

            return 0;
            }

            例e15.lua
            -- call a C++ function

            avg, sum = average(10, 20, 30, 40, 50)

            print("The average is ", avg)
            print("The sum is ", sum)


            程序說(shuō)明:
            * lua_gettop()的作用是返回棧頂元素的序號(hào). 由于Lua的棧是從1開(kāi)始編號(hào)的,
            所以棧頂元素的序號(hào)也相當(dāng)于棧中的元素個(gè)數(shù). 在這里, 棧中元素的個(gè)數(shù)就
            是傳入的參數(shù)個(gè)數(shù).
            * for循環(huán)計(jì)算所有傳入?yún)?shù)的總和. 這里用到了數(shù)值轉(zhuǎn)換lua_tonumber().
            * 然后偶們用lua_pushnumber()把平均值和總和push到棧中.
            * 最后, 偶們返回2, 表示有兩個(gè)返回值.
            * 偶們雖然在C++中定義了average()函數(shù), 但偶們的Lua程序并不知道, 所以需
            要在main函數(shù)中加入

            /* register our function */
            lua_register(L, "average", average);

            這兩行的作用就是告訴e15.lua有average()這樣一個(gè)函數(shù).
            * 這個(gè)程序可以存成cpp也可以存成c, 如果以.c為擴(kuò)展名就不需要加extern "C"

            編譯的方法偶們上次說(shuō)過(guò)了, 方法相同.
            e15.lua執(zhí)行的方法只能用上例中的C++中執(zhí)行, 而不能用命令行方式執(zhí)行.

            3.錯(cuò)誤處理
            在上例中, 偶們沒(méi)有對(duì)傳入的參數(shù)是否為數(shù)字進(jìn)行檢測(cè), 這樣做不好. 所以這里偶
            們?cè)偌由襄e(cuò)誤處理的片斷.

            把這段加在for循環(huán)之內(nèi):
            if (!lua_isnumber(L, i)) {
            lua_pushstring(L, "Incorrect argument to 'average'");
            lua_error(L);
            }
            這段的作用就是檢測(cè)傳入的是否為數(shù)字.

            加上這段之后, 偶們debug的時(shí)候就會(huì)簡(jiǎn)單許多. 對(duì)于結(jié)合兩種語(yǔ)言的編程, 它們之
            間傳遞數(shù)據(jù)的正確性檢測(cè)是非常重要的.

            這里有別人寫(xiě)好的例子:
            VC的 http://tonyandpaige.com/tutorials/luaavg.zip
            Linux的 http://tonyandpaige.com/tutorials/luaavg.tar.gz

            至此, Lua與C的結(jié)合就基本講完了, 下次偶要開(kāi)始說(shuō)說(shuō)Lua與面向?qū)ο?
            但是偶自己還沒(méi)有學(xué)完, 所以大家可能要多等兩天了. Sorry!

            --

            posted @ 2009-05-16 21:37 麒麟子 閱讀(4041) | 評(píng)論 (1)編輯 收藏

            僅列出標(biāo)題
            共38頁(yè): First 22 23 24 25 26 27 28 29 30 Last 
            人妻精品久久无码专区精东影业| 久久本道久久综合伊人| 一本大道加勒比久久综合| 狠狠色婷婷综合天天久久丁香| 久久99精品久久久久久水蜜桃| 亚洲欧美日韩久久精品| 人妻精品久久无码区| 九九久久精品国产| 久久久久久久久久久久久久 | 久久久黄片| 久久久久亚洲AV无码网站| 久久九九久精品国产免费直播| 亚洲国产另类久久久精品黑人| 国产精品激情综合久久| 九九精品99久久久香蕉| 久久久久久综合网天天| 亚洲&#228;v永久无码精品天堂久久| 日韩久久久久久中文人妻| 久久久艹| 午夜精品久久久久久影视777| 国产精品欧美久久久天天影视| 狠狠色婷婷久久综合频道日韩| 久久精品视屏| 国内精品久久久久久不卡影院| 97久久综合精品久久久综合| 香蕉久久av一区二区三区| 久久久久亚洲av综合波多野结衣| 久久乐国产精品亚洲综合| 国内精品伊人久久久久影院对白| 97久久超碰国产精品2021| 人妻无码久久一区二区三区免费| 久久免费看黄a级毛片| 久久香综合精品久久伊人| 亚洲国产成人久久笫一页| 热久久视久久精品18| 久久经典免费视频| 精品综合久久久久久98| 97久久国产露脸精品国产| 婷婷伊人久久大香线蕉AV| 国内精品九九久久久精品| 久久国产精品久久精品国产|