青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

posts - 94, comments - 250, trackbacks - 0, articles - 0
  C++博客 :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

N3的場景管理最為核心的一個類是GrphicsServer, 它包含一些"stage"和"View".

Stage把圖形實體(模型, 攝像機, 燈光)進行分類渲染. 它的主要工作是在連接的圖形實體間加速可見性查詢. 不同的可見性查詢由不同的Stage子類來實現(xiàn). N3會提供了一些不同用途的Stage子類, 但你也可以根據(jù)程序需要自己來實現(xiàn)可見性查詢機制.

可見性查詢適用于這些實體:

  • Camera->Light: 查找對于指定攝像機可見的所有燈光
  • Camera->Model: 查找對于指定攝像機可見的所有模型
  • Light->MOdel: 查找被指定光源照射到的所有模型

這些可見性查詢在圖形實體間建立了一些所謂的"可見性鏈接", 再利用低級的渲染子系統(tǒng)來加速渲染.

要渲染一個Stage的內(nèi)容, 需要至少一個View對象. 一個View對象通過綁定一個攝像機實體把Stage渲染到一個render target. 可以并存任意數(shù)目的View, 也可能都被綁定到任意Stage. 此外, View對象之間可能存在依賴關(guān)系(結(jié)果就是一個View對象會在渲染自身時首先請求它所依賴的View對象).

圖形實體表示了可以被連接到Stage的一個最小圖形對象, 它分為以下三種:

  • ModelEntity: 一個可見的模型實例
  • LightEntity: 一個光源
  • CameraEntity: 一個攝像機

可見性查詢使圖形實體間形成一種雙向的鏈接關(guān)系. 一個CameraEntity鏈接到所有對于這個攝像機來說可見的ModelEntity和LightEntity. 因為可見性鏈接是雙向的, 所以ModelEntity和LightEntity也知道它們對于哪個攝像機可見. LightEntity有它們影響到的ModelEntity的鏈接, ModelEntity也知道它們被哪個光源照亮.

==========================================================

N3 畫個東西真簡單, 想畫個模型, 創(chuàng)建出來設(shè)置一下位置扔給Stage就好了

  1. this->model = ModelEntity::Create();
  2. this->model->SetTransform(matrix44::translation(0.0f, 3.0f, 0.0f));
  3. this->model->SetResourceId(ResourceId("mdl:examples/eagle.n2"));
  4. this->stage->AttachEntity(this->model.upcast<GraphicsEntity>());

模型是黑的? 再往場景里扔個燈就好了:

  1. // attach a light entity
  2.             matrix44 lightTransform = matrix44::multiply(matrix44::scaling(100.0f, 100.0f, 100.0f), matrix44::lookatrh(point(20.0f, 20.0f, 20.0f), point::origin(), vector::upvec()));
  3. this->lightEntity = SpotLightEntity::Create();
  4. this->lightEntity->SetCastShadows(true);
  5. this->lightEntity->SetTransform(lightTransform);
  6. this->lightEntity->SetColor(float4(4.0f, 2.0f, 1.0f, 1.0f));        
  7. this->stage->AttachEntity(this->lightEntity.upcast<GraphicsEntity>());

想控制的話, 再扔個攝像機進去就OK了.......

  1.         GraphicsServer* gfxServer = GraphicsServer::Instance();
  2. // setup the camera util object
  3. this->mayaCameraUtil.Setup(point(0.0f, 0.0f, 0.0f), point(0.0f, 0.0f, 10.0f), vector(0.0f, 1.0f, 0.0f));
  4. // setup a stage
  5. this->stage = gfxServer->CreateStage(StringAtom("DefaultStage"), SimpleStageBuilder::Create());
  6. // attach a camera to the stage
  7. this->cameraEntity = CameraEntity::Create();
  8.         cameraEntity->SetTransform(this->mayaCameraUtil.GetCameraTransform());
  9. this->stage->AttachEntity(cameraEntity.upcast<GraphicsEntity>());
  10. // setup a default view
  11. this->view = gfxServer->CreateView(View::RTTI, StringAtom("DefaultView"), true);
  12. this->view->SetStage(this->stage);        
  13. this->view->SetFrameShader(FrameServer::Instance()->GetFrameShaderByName(ResourceId(DEFAULT_FRAMESHADER_NAME)));
  14. this->view->SetCameraEntity(cameraEntity);

別忘了處理輸入事件:

可以參考ViewerApplication::OnProcessInput().

posted @ 2008-12-14 21:55 Condor 閱讀(628) | 評論 (0)編輯 收藏

Nebula2的腳本系統(tǒng)實現(xiàn)了一個面向C++的腳本接口, 它把腳本命令直接映射到了C++方法. 從技術(shù)角度來說, 這是一個簡捷的思路, 但是對于需要把游戲邏輯和行為腳本化的關(guān)卡設(shè)計師來說, Nebula2的腳本系統(tǒng)太底層和透明了.

關(guān)卡邏輯腳本一般來說構(gòu)架于比C++接口更高級的層次上, 直接把腳本命令映射到C++方法會把腳本層次弄得錯綜復(fù)雜. Bug甚至?xí)韧瑯拥腃++代碼更多, 因為腳本語言一般缺少強類型檢查和”編譯時”的錯誤檢測, 所以在本應(yīng)在C++編譯時發(fā)現(xiàn)的Bug會在腳本運行時才發(fā)現(xiàn)(這對于不同的腳本語言有所不同). 這是我們從Project Nomads中得出的經(jīng)驗, 它就是用Nebula2的腳本系統(tǒng)驅(qū)動的.

所以教訓(xùn)就是: 把你的腳本架構(gòu)在一個正確的抽象層上, 并且: 把你的C++接口映射到一種腳本語言是沒有意義的, 因為那樣你不如從一開始直接用C++來做這些東西.

相應(yīng)的, 新的Nebula3腳本哲學(xué)為關(guān)卡設(shè)計師提供一些在”正確的抽象層”的(大多是限于特定應(yīng)用)積木. 當然, “正解的抽象層” 很難來定義, 因為這要在靈活性跟易用性之間找到一個平衡( 例如, 一個”Pickup” 命令是不是應(yīng)該把角色移動到拾取范圍內(nèi)呢? )

除了太底層以外, Nebula2的腳本系統(tǒng)也有一些其它的缺點:

  • C++方法必須遵循可以轉(zhuǎn)化為腳本的原則( 只有簡單數(shù)據(jù)類型才可以做為參數(shù) )
  • 給程序員帶來麻煩. 每個C++方法都需要額外的腳本接口代碼( 每個方法幾行 )
  • 只有派生自nRoot的類可以腳本化
  • 對象關(guān)聯(lián)到腳本系統(tǒng)( 思路簡單, 但是增加的依賴性會使重構(gòu)非常困難 )

下面是Nebual3的底層腳本的大概:

  • 腳本系統(tǒng)的基礎(chǔ)是Script::Command類
  • Script::Command是一個完全腳本語言無關(guān)的, 它包含了一個命令名稱, 一些輸入?yún)?shù)的集合還有一些輸出參數(shù)的集合.
  • 一個新的腳本命令通過派生Script::Comand類來創(chuàng)建, 腳本的C++功能代碼可以寫入子類的OnExecute()方法
  • ScriptServer類是腳本系統(tǒng)中僅有一個腳本語言相關(guān)的類, 它會把Command對象注冊成新的腳本命令, 并且把命令參數(shù)在腳本語言和C-API之間做翻譯.

這個觀念比Nebula2更為簡單, 最重要的是, 它不會跟Nebula3的其它部分交織在一起. 甚至可以通過改變一個#define來編譯一個沒有腳本支持的Nebula3.

當然, 書寫腳本命令的C++代碼跟Nebula2一樣煩人, 這是NIDL的由來. NIDL的是全稱是”Nebula Interface Definition Language”. 基本思想是通過為腳本命令定義一個簡單的XML schema并把XML描述編譯成派生了Script::Command的C++代碼, 來盡量減少書寫腳本命令的重復(fù)性工作.

對于一個腳本命令必不可少的信息有:

  • 命令的名稱
  • 輸入?yún)?shù)的類型和名稱
  • 輸出參數(shù)的類型和名稱
  • 對應(yīng)的C++代碼( 通常只有一行 )

還有一些非必須, 但是可以帶來便利性的信息:

  • 關(guān)于命令的作用和每個參數(shù)的意義的描述, 這可以作為運行時的幫助系統(tǒng)
  • 一個唯一的FourCC(四字符碼), 可以更快的通過二進制通道傳輸

大部分的腳本命令翻譯成了大約7行的XML-NIDL代碼. 這些XML文件再用”nidlc”NIDL編譯器工具編譯為C++代碼. 這個預(yù)處理是VisualStudio完全集成的, 所以使用NIDL文件不會為程序員代來任何困難.

為了減少亂七八糟的文件(編譯生成的), 相關(guān)的腳本命令被組織到一個叫作庫的集合中. 一個庫由一個單獨的NIDL-XML文件表示, 并且它只會被翻譯一個C++頭文件和一個C++源代碼文件. 腳本庫可以在程序啟動時注冊到ScriptServer, 所以如果你的應(yīng)用程序不需要腳本訪問文件的話, 僅僅不注冊IO腳本庫就可以了. 這會減小可執(zhí)行文件的體積, 因為連接器會把沒有用到的腳本庫丟棄掉.

最后, Nebula3放棄了TCL作為標準的腳本語言, 而采用了運行時代碼更加小巧的LUA. LUA已經(jīng)成為游戲腳本的準規(guī)范, 這也使得尋找熟練的LUA關(guān)卡設(shè)計師更加容易.

posted @ 2008-12-14 21:55 Condor 閱讀(643) | 評論 (0)編輯 收藏

相對于其他的子系統(tǒng)來說, 輸入系統(tǒng)是比較簡單的. 很多游戲根本就沒有對這一塊進行封裝, 而直接采用了Win32的消息機制.

不過經(jīng)過封裝的輸入系統(tǒng)使用起來很方便, 呵呵.

N3中有三種輸入設(shè)備, 鍵盤, 鼠標, 手柄. 分別是基于Win32消息, DirectInput, XInput實現(xiàn)的. 這里有一個繼承圖能夠很好的說明輸入系統(tǒng)的組織結(jié)構(gòu):

基本的消息處理機制是這樣的一個流程:

InputServer里有默認的一個鍵盤, 一個鼠標, 一個手柄的"handler", 在每幀開始時InputServer會檢測當前的輸入消息,  得到一個InputEvent, 由相應(yīng)的InputHandler來處理.  各個InputHandler都保存著當前幀各種輸入狀態(tài)的緩存(如鼠標左鍵是否按下), 因此, 在程序運行過程中, 我們只要在繪制結(jié)束前檢測各個InputHandler的狀態(tài)就相當于知道當前用戶是怎樣輸入的了.

一般只需要關(guān)心這么幾個函數(shù)就夠了:

  1. ////////////////////// Mouse////////////////////////////
  2. /// return true if button is currently pressed
  3. bool ButtonPressed(Input::MouseButton::Code btn) const;
  4. /// return true if button was down at least once in current frame
  5. bool ButtonDown(Input::MouseButton::Code btn) const;
  6. /// return true if button was up at least once in current frame
  7. bool ButtonUp(Input::MouseButton::Code btn) const;
  8. /// return true if a button has been double clicked
  9. bool ButtonDoubleClicked(Input::MouseButton::Code btn) const;
  10. /// return true if mouse wheel rotated forward
  11. bool WheelForward() const;
  12. /// return true if mouse wheel rotated backward
  13. bool WheelBackward() const;
  14. /// get current absolute mouse position (in pixels)
  15. const Math::float2& GetPixelPosition() const;
  16. /// get current screen space mouse position (0.0 .. 1.0)
  17. const Math::float2& GetScreenPosition() const;
  18. /// get mouse movement
  19. const Math::float2& GetMovement() const;
  1. //////////////////////Keyboard//////////////////////
  2. /// return true if a key is currently pressed
  3. bool KeyPressed(Input::Key::Code keyCode) const;
  4. /// return true if key was down at least once in current frame
  5. bool KeyDown(Input::Key::Code keyCode) const;
  6. /// return true if key was up at least once in current frame
  7. bool KeyUp(Input::Key::Code keyCode) const;
  8. /// get character input in current frame
  9. const Util::String& GetCharInput() const;

GamePad先略過, 原理相同

測試例子, 在上一次的代碼中添加一段:

  1. void OnRenderFrame()
  2.     {
  3. if (this->inputServer->GetDefaultMouse()->ButtonDown(MouseButton::LeftButton))
  4.         {
  5.             MessageBoxA(this->displayDevice->GetHwnd(), "Left Button Down", NULL, 0);
  6.         }
  7. //...//
  8.     }

效果:

posted @ 2008-12-14 21:53 Condor 閱讀(1574) | 評論 (0)編輯 收藏

概述

  • 一些為了兼容Nebula2的代碼所做的修改, 主要是一些宏的名字受到影響(DeclareClass -> __DeclareClass, ImplementSingleton -> __ImplementSingleton etc...)
  • 著手刪除#ifndef/#define/#endif 這些防止重復(fù)include的宏, 因為幾乎所有的編譯器(VStudio, GCC, Codewarrior) 都支持#pragma once 
  • 把同的樣Win32 和Xbox360 代碼移動到一個共同的Win360 命名空間來消除代碼冗余
  • 加入了一個新的Toolkit層, 它包含了一些導(dǎo)出工具和輔助類
  • 加入和整理了一些 Doxygen(文檔) 頁面

編譯系統(tǒng)

  • 重新組織了 VStudio解決方案的結(jié)構(gòu), 讓所有的依賴工程都在一個解決方案中, 這樣就不用再同時打開多個VStudio了
  • 現(xiàn)在可以通過.epk編譯腳本來導(dǎo)入VStudio工程(對于不在Nebula3 SDK目錄下的工程很有用)
  • 新的"projectinfo.xml" 文件為一些有用的導(dǎo)出工具定義了工程和平臺特有的屬性
  • 把 export.zip 檔案文件分割到一個獨立的平臺無關(guān)文件和幾個特定平臺的文件 (export.zip 包含所有平臺無關(guān)的文件, export_win32.zip, export_xbox360.zip, export_wii.zip 包含特定平臺的文件)
  • 加入一個統(tǒng)一的多平臺支持到 asset-pipeline (如 "msbuild /p:Platform=xbox360" 來生成XBOX360的東西)
  • 一個新的命令行生成工具 (有代碼):
    • audiobatcher3.exe (包裝了音頻導(dǎo)出)
    • texturebatcher3.exe (包裝了紋理導(dǎo)出)
    • shaderbatcher3.exe (包裝了 shader 編譯)
    • buildresdict.exe (生成資源詞典文件)
    • 這些工具大部分只是調(diào)用其它的生成工具(像xactbld3.exe, nvdxt.exe, 還有其它命令下的生成工具)
  • 注意公開的N3-SDK因為法律原因只包含Win32平臺的支持

基礎(chǔ)層

  • 修正Core::RefCounted 和Util::Proxy 引用計數(shù)線程不安全的BUG
  • 加入 WeakPtr<> 類用于更好地處理環(huán)形引用
  • 在 Ptr<>中加入類型轉(zhuǎn)換的方法
  • 簡化System::ByteOrder 類接口
  • 加入平臺相關(guān)的面向任務(wù)的"virtual CPU core id" (如 MainThreadCode, RenderThreadCore, 等等...)
  • 加入一個 System::SystemInfo 類
  • 加入 Threading::ThreadId 類型和 Threading::Thread::GetMyThreadId()靜態(tài)方法
  • 現(xiàn)在可以在VStudio調(diào)試器和其它的高度工具中看到線程的固有名稱了
  • SetThreadIdealProcessor() 現(xiàn)在用于在Win32平臺上把線程分配給可用CPU核心
  • 新的線程子系統(tǒng)的HTTP 調(diào)試頁面(現(xiàn)在只列出Nebula3的活動線程)
  • MiniDump支持: 崩潰, n_assert()和 n_error() 現(xiàn)在在Win32平臺上會生成 MiniDump 文件
  • 新的 Debug 子系統(tǒng)用于代碼分析:
    • 提供 DebugTimer 和 DebugCounter 對象
    • HTTP 調(diào)試頁面允許在運行時檢查DebugTimers和 DebugCounters
  • 新的Memory::MemoryPool 類來分配同樣大小的內(nèi)存塊(加快分配速度和減少內(nèi)存碎片)
  • Math::matrix44在中的一些新的和改名的方法
  • Http 子系統(tǒng)現(xiàn)在運行在它自己的線程里
  • 把 SVG 支持加入到 Http 子系統(tǒng)(Http::SvgPageWriter 和Http::SvgLineChartWriter) (xoyojank:難道是Scalable Vector Graphics?這樣的話可以輸出圖表了)
  • 加入 IO::ExcelXMLReader 流讀取類, 允許讀取XML模式的MS Excel電子表格文件
  • 在Messaging::AsyncPort加入行為方式, 定義了處理線程怎樣去等待新的消息:
    • WaitForMessage: 在消息到達前一直阻塞
    • WaitForMessageOrTimeOut: 在消息到達或超時前一直阻塞
    • DoNotWait: 不等待消息
  • 加入 Remote 子系統(tǒng), 允許通過TCP/IP連接遠程控制N3應(yīng)用程序

渲染層

  • 把渲染移動了它自己的線程 (InternalGraphics子系統(tǒng)在渲染線程這邊,  Graphics 前端子系統(tǒng)在主線程這邊)
  • 加入了 CoreAnimation 和 Animation 子系統(tǒng) (構(gòu)造中)
  • 為簡單的用戶界面加入了UI子系統(tǒng) (構(gòu)造中) (xoyojank: 這個不錯^_^)
  • 加入CoreAudio和 Audio 子系統(tǒng)(構(gòu)造中):
    • CoreAudio 是后臺的, 運行在自己的線程里
    • Audio 是前臺的"客戶端", 運行在主線程里 (或者其它任何線程)
    • 圍繞XACT的概念設(shè)計
    • 提供 XACT 的包裝實現(xiàn)
  • 加入 CoreGraphics::TextRenderer 和 CoreGraphics::ShapeRenderer 類, 打算用于渲染調(diào)試信息
  • 加入調(diào)試渲染子系統(tǒng)(現(xiàn)在在Debug命名空間下)
  • Frame 子系統(tǒng): FramePostEffect 現(xiàn)也也許會包含 FrameBatch
  • Input 子系統(tǒng): 斷開 XInput 游戲手柄接口現(xiàn)在對于連接中的設(shè)備每隔0.5秒才檢測一次
  • Resources 子系統(tǒng): 加入 ResourceAllocator/ResourceLump 系統(tǒng)為Console平臺真正的資源流做準備

應(yīng)用層和插件:

  • 刪除了 CoreFeature (這東西不得不進入GameApplication類來阻止雞生蛋問題)
  • 加入 NetworkFeature (構(gòu)造中)
  • 加入 UIFeature (構(gòu)造中)
  • 加入 CoreNetwork 和 Multiplayer 插件(RakNet的包裝)

posted @ 2008-12-14 21:52 Condor 閱讀(1829) | 評論 (0)編輯 收藏

可能是還在開發(fā)當中的緣故, 我感覺Nebula3中的lua腳本系統(tǒng)不是很完善. 所有的調(diào)用都是封裝成Command來執(zhí)行的, 并不像LuaBind那樣直接綁定到C++類對象; 而且, 對于C++調(diào)用腳本的接口也不是很方便, 只有一個Eval()來執(zhí)行一個字符串. 如果要實際進行應(yīng)用的話, 我想最好是自己擴展一下, 這里有一篇不錯的文章: Integrating Lua into C++. 當然, 對于需求更高的用戶來說, 可以選擇使用LuaBind等第三方庫來整合腳本系統(tǒng).

Command(命令)
可以這么說, 腳本中調(diào)用的, 都是一個個的Command. 一個新的Command定義了一個腳本語言獨立的新的腳本命令, 你可以通過派生一個Command的子類并注冊到腳本服務(wù)器來實現(xiàn). 也就是說, 新的命令不依賴于你具體使用的腳本系統(tǒng), 可以是lua, 也可以是python等等.

view plaincopy to clipboardprint?

  1. class Print : public Scripting::Command   
  2. {   
  3.     DeclareClass(Print);   
  4. public:   
  5. virtual void OnRegister();   
  6. virtual bool OnExecute();   
  7. virtual Util::String GetHelp() const;   
  8. private:   
  9. void Callback(const Util::String& str);   
  10. };<PRE></PRE> 
class Print : public Scripting::Command
{
    DeclareClass(Print);
public:
    virtual void OnRegister();
    virtual bool OnExecute();
    virtual Util::String GetHelp() const;
private:
    void Callback(const Util::String& str);
};

ScriptServer(腳本服務(wù)器)
ScriptServer是語言無雙的, 也就是說你可以自己派生一個相應(yīng)語言的子來來支持一種腳本言. Nebula3里已經(jīng)實現(xiàn)了一個LuaServer, 不過個感覺沒有LuaBind方便. 所有的腳本執(zhí)行都是通過LuaServer::Eval(const String& str)來完成的. 腳本要調(diào)用C++代碼的話, 需要封裝一個Command, 然后用LuaServer::RegisterCommand()來注冊就可以用了. 具體可以參考Command命名空間里的相關(guān)代碼.

view plaincopy to clipboardprint?

  1. scriptServer->RegisterCommand("print", Print::Create());<PRE></PRE> 
    scriptServer->RegisterCommand("print", Print::Create());

應(yīng)用實例
其實App::ConsoleApplication里就有LuaServer, 并且已經(jīng)注冊了一些IO命名. 我們派生一個從命令行讀取腳本命令執(zhí)行的來做測試:

view plaincopy to clipboardprint?

  1. class ScripTestApp : public App::ConsoleApplication   
  2. {   
  3. public:   
  4. ScripTestApp(void);   
  5. /// open the application
  6. virtual bool Open();   
  7. /// run the application, return when user wants to exit
  8. virtual void Run();   
  9. };   
  10. ScripTestApp::ScripTestApp(void)   
  11. {   
  12. }   
  13. bool ScripTestApp::Open()   
  14. {   
  15. if (ConsoleApplication::Open())   
  16. {   
  17. return true;   
  18. }   
  19. return false;   
  20. }   
  21. void ScripTestApp::Run()   
  22. {   
  23. Util::String input;   
  24. while (true)   
  25. {   
  26.   input = IO::Console::Instance()->GetInput();   
  27. if (!input.IsEmpty())   
  28.   {   
  29. this->scriptServer->Eval(input);   
  30.   }   
  31. }   
  32. }<PRE></PRE> 
class ScripTestApp : public App::ConsoleApplication
{
public:
 ScripTestApp(void);

 /// open the application
 virtual bool Open();
 /// run the application, return when user wants to exit
 virtual void Run();
};

 ScripTestApp::ScripTestApp(void)
{
}

bool ScripTestApp::Open()
{
 if (ConsoleApplication::Open())
 {
  return true;
 }
 return false;
}

void ScripTestApp::Run()
{
 Util::String input;
 while (true)
 {
  input = IO::Console::Instance()->GetInput();
  if (!input.IsEmpty())
  {
   this->scriptServer->Eval(input);
  }
 }
}

運行結(jié)果:

posted @ 2008-12-14 21:33 Condor 閱讀(1543) | 評論 (0)編輯 收藏

Nebula3的網(wǎng)絡(luò)子系統(tǒng)提供了基于TCP協(xié)議的簡單C/S通信模式. 它并沒有打算做成大廳,會話管理還有玩家數(shù)據(jù)同步的面向游戲的高級通信. 這些以后會在更高層的Nebula3子系統(tǒng)中出現(xiàn).

使用IP地址

  一個IpAddress對象通過主機名字或TCP/IP地址加一個端口號定義了一個通信端點. IpAddress對象可以通過多數(shù)方式建立:

1: // 從 TCP/IP 地址和端口號:

2: IpAddress ipAddr("192.168.0.2",1234);

3:

4: // 從主機名和端口號:

5: IpAddress ipAddr("www.radonlabs.de",1234);

6:

7: // 從本機(127.0.0.1) 和端口號:

8: IpAddress ipAddr("localhost",1234);

9:

10: // 從"any" 地址 (0.0.0.0) 和端口號:

11: IpAddress ipAddr("any",1234);

12:

13: // 從廣播地址 (255.255.255.255) 和端口號:

14: IpAddress ipAddr("broadcast",1234);

15:

16: // 從主機的第一個合法網(wǎng)絡(luò)適配器的地址和端口號

17: IpAddress ipAddr("self",1234);

18:

19: // 從主機的第一個連接到互聯(lián)網(wǎng)的網(wǎng)絡(luò)適配器的地址和端口號:

20: IpAddress ipAddr("insetself",1234);

21:

22: // 從一個定義了主機名的URI和端口號:

23: IpAddress ipAddr(IO::URI("http://www.radonlabs.de:2100"));

  一個IpAddress對象可以用于從主機名查找TCP/IP地址:

1: IpAddress ipAddr("www.radonlabs.de",0);

2: String numericalAddr = ipAddr.GetHostAddr();

建立一個客戶端/服務(wù)器系統(tǒng)

  網(wǎng)絡(luò)子系統(tǒng)用TcpServer和TcpClient類實現(xiàn)了一個易用的基于TCP協(xié)議的C/S系統(tǒng). 一個TcpServer可以為任意數(shù)量的TcpClient服務(wù).

  建立一個服務(wù)器可以這么做:

1: using namespace Net;

2:

3: Ptr<TcpServer> tcpServer = TcpServer::Create();

4: tcpServer->SetAddress(IpAddress("any",2352));

5: if(tcpServer->Open())

6: {

7: // TcpServer successfully opened

8: }

  這樣會建立一個在2352端口監(jiān)聽客戶端連接請求的服務(wù)器.

  為了跟TcpServer通信, 需要在客戶端建立一個TcpClient對象:

1: using namespace Net;

2:

3: Ptr<TcpClient> tcpClient = TcpClient::Create();

4: tcpClient->SetBlocking(false);

5: tcpClient->SetAddress(IpAddress("localhost",2352));

6: TcpClient::Result res = tcpClient->Connect();

  這里假設(shè)服務(wù)端和客戶端運行在同一臺機器上(因為客戶端連接到了”localhost”).

  像上面那樣非阻塞的情況, Connect()方法不是返回TcpClient::Success(這意味著連接建立好了)就是TcpClient::Connecting, 如果這樣的話, 應(yīng)用程序需要繼續(xù)調(diào)用Connect()方法. 如果連接錯誤, 會返回一個TcpClient::Error的返回值.

  如果是阻塞的, Connect()方法直到連接建立(結(jié)果是TcpClient::Success)或發(fā)生錯誤才會返回.

  注意:一個交互式應(yīng)用程序不應(yīng)該在網(wǎng)絡(luò)通信時阻塞, 而應(yīng)不斷地為用戶提供反饋.

  一旦連接建立, 服務(wù)端會為每個客戶機建立一個TcpClientConnection對象. TcpClientConnection在服務(wù)器上表示客戶機, 并且負責從客戶機收發(fā)數(shù)據(jù).

  要進行接收和發(fā)送數(shù)據(jù)的話, 需使用IO::Stream對象. 在通信流上連接IO::StreamReader和IO::StreamWriter對象后, 從流中編碼和解碼數(shù)據(jù)是一件非常容易的事情.

  注意:發(fā)送數(shù)據(jù)并不是即時的, 而是在Send()方法被調(diào)用之前會一直保存在發(fā)送流當中.

  要客戶端給服務(wù)器發(fā)送一些文本數(shù)據(jù)話, 只要從發(fā)送流獲取一個指針, 向其中寫入數(shù)據(jù)后調(diào)用Send()方法就可以了:

1: using namespace Net;

2: using namespace IO;

3:

4: // obtain pointer to client's send stream and attach a TextWriter

5: const Ptr<Stream>& sendStream = tcpClient->GetSendStream();

6: Ptr<TextWriter> textWriter = TextWriter::Create();

7: textWriter->SetStream(sendStream);

8: textWriter->Open())

9: textWriter->WriteString("Hello Server");

10: textWriter->Close();

11:

12: // send off the data to the server

13: if(this->tcpClient->Send())

14: {

15: // data has been sent

16: }

  在服務(wù)器端接收客戶端數(shù)據(jù), 應(yīng)用程序需要要頻繁地(每幀一次)緩存帶有客戶羰數(shù)據(jù)的TcpClientConnection. 可能不只一個TcpClientConnection在等待處理, 因此處理循環(huán)應(yīng)該像這樣:

1: // get array of client connections which received data since the last time

2: Array<Ptr<TcpClientConnection>> recvConns = tcpServer->Recv();

3: IndexT i;

4: for(i =0; i < recvConns.Size(); i++)

5: {

6: // get receive stream from current connection, attach a text reader and read content

7:      Ptr<TextReader> textReader = TextReader::Create();

8:      textReader->SetStream(recvConns[i]->GetRecvStream());

9:      textReader->Open();

10:      String str = textReader->ReadString();

11:      textReader->Close();

12:

13: // process received string and send response back to client

14: // create a TextWriter and attach it to the send stream of the client connection

15:      Ptr<TextWriter> textWriter = TextWriter::Create();

16:      textWriter->SetStream(recvConns[i]->GetSendStream());

17:      textWriter->Open();

18:      textWriter->WriteString("Hello Client");

19:      textWriter->Close();

20:

21: // finally send the response back to the client

22:      recvConns[i]->Send();

23: }

  在客戶端獲得服務(wù)器的應(yīng)答, 調(diào)用TcpClient::Recv()方法會在數(shù)據(jù)到達之前一直阻塞(在阻塞模式下), 或者立即返回(在非阻塞模式下), 并在有服務(wù)器數(shù)據(jù)時返回true:

1: // check if data is available from the server

2: if(tcpClient->Recv())

3: {

4: // yep, data is available, get the recv stream and read the data from it

5: const Ptr<Stream>& recvStream = tcpClient->GetRecvStream();

6:      Ptr<TextReader> textReader = TextReader::Create();

7:      textReader->SetStream(recvStream);

8:      textReader->Open();

9:      String responseString = textReader->ReadString();

10:      n_printf("The server said: %s\n", responseString.AsCharPtr());

11:      textReader->Close();

12: }

  客戶端也應(yīng)該通過調(diào)用IsConnected()訪求檢查連接是否有效. 如果因為某些原因使連接斷開, 這個方法會返回false.

  注意:

TcpServer和TcpClient并沒有為能夠跟不相關(guān)的客戶端和服務(wù)器端而實現(xiàn)一個潛在的通信協(xié)議(例如, 一個TcpServer可以跟標準的Web瀏覽器客戶端一起工作, 還有一個TcpClient類可以跟一個標準的HTTP服務(wù)器通信).

  現(xiàn)實世界的情況是, 一個應(yīng)用程序應(yīng)該實現(xiàn)自己的健壯的通信協(xié)議, 它至少會編碼負載數(shù)據(jù)的長度. 如果負載比最大包大小還要大, 數(shù)據(jù)會以多個包發(fā)送并在客戶端接收. 客戶端應(yīng)該把數(shù)據(jù)解碼成一個完整的消息, 否則需要等待消息的數(shù)據(jù)接收完畢.

字節(jié)次序問題

  服務(wù)器和客戶端可能運行在不同字節(jié)次序的的CPU上. 如果二進制數(shù)據(jù)通過網(wǎng)絡(luò)發(fā)送, 數(shù)據(jù)必需轉(zhuǎn)換成兩個客戶端都一致的”網(wǎng)絡(luò)字節(jié)順序”. Nebula3在IO::BinaryReader和IO::BinaryWriter類中提供字節(jié)順序的自動轉(zhuǎn)換. 只需要簡單地調(diào)用下面的方法在網(wǎng)絡(luò)通信流上讀寫就可以了:

1: binaryReader->SetStreamByteOrder(System::ByteOrder::Network);

2: binaryWriter->SetStreamByteOrder(System::ByteOrder::Network);

Socket

  網(wǎng)絡(luò)子系統(tǒng)提供了一個把傳統(tǒng)socket函數(shù)包裝成C++接口的Socket類. 一般情況下應(yīng)用程序不直接使用Socket類, 而是使用更高級的像TcpServer這樣的類. 但也不是不可能在有的時候直接使用socket函數(shù)比Socket類更方便.

posted @ 2008-12-14 21:32 Condor 閱讀(1226) | 評論 (0)編輯 收藏

上一次熟悉了IO系統(tǒng)后, 寫個程序來練練手.

正好這次看到App命名空間, 正好熟悉一下ConsoleApplication的用法. 因為Nebula3內(nèi)置了ZipFileSystem, 但不支持壓縮, 只支持解壓縮, 就試著寫了一個命令行的unzip.exe, 算是對之前所學(xué)的一個總結(jié).

沒想解壓縮就像拷貝文件一樣簡單! 因為當zip文件掛載到IO系統(tǒng)后, 可以像本地文件一樣使用其中的文件, 呵呵.

 1: /********************************************************************
 2: 	created:	2008/07/08
 3: 	created:	8:7:2008   16:15
 4: 	filename: 	UnZip.cpp
 5: 	author:		xoyojank
 6: 	
 7: 	purpose:	zip file extract test
 8: *********************************************************************/
 9: 
10: #include "stdneb.h"
11: #include "UnZipApp.h"
12: 
13: using namespace Util;
14: 
15: //------------------------------------------------------------------------------
16: /**
17: */
18: void __cdecl
19: main(int argc, const char** argv)
20: {
21: 	CmdLineArgs args(argc, argv);
22: 	UnZipApp app;
23: 	app.SetCompanyName("Xoyojank");
24: 	app.SetAppName("UnZip");
25: 	app.SetCmdLineArgs(args);
26: 	if (app.Open())
27: 	{
28: 		app.Run();
29: 		app.Close();
30: 	}
31: 	system("pause");
32: 	app.Exit();
33: }
 1: /********************************************************************
 2: 	created:	2008/07/08
 3: 	created:	8:7:2008   16:16
 4: 	filename: 	UnZipApp.h
 5: 	author:		xoyojank
 6: 	
 7: 	purpose:	UnZip Application
 8: *********************************************************************/
 9: #pragma once
10: #include "stdneb.h"
11: #include "app/consoleapplication.h"
12: 
13: class UnZipApp : public App::ConsoleApplication
14: {
15: public:
16: 	UnZipApp(void);
17: 
18: 	/// open the application
19: 	virtual bool Open();
20: 	/// run the application, return when user wants to exit
21: 	virtual void Run();
22: 
23: private:
24: 	/// a recursion method to unzip the files under "dir"
25: 	void UnZipDir(Util::String& dir);
26: private:
27: 	Util::String zipFileName;
28: 	Util::String sourcePath;
29: 	Util::String targetPath;
30: };
 1: /********************************************************************
 2: 	created:	2008/07/08
 3: 	created:	8:7:2008   16:19
 4: 	filename: 	UnZipApp.cpp
 5: 	author:		xoyojank
 6: 	
 7: 	purpose:	UnZip Application
 8: *********************************************************************/
 9: #include "UnZipApp.h"
10: 
11: 
12: UnZipApp::UnZipApp(void)
13: {
14: }
15: 
16: bool UnZipApp::Open()
17: {
18: 	if (ConsoleApplication::Open())
19: 	{
20: 		// help info
21: 		if (this->args.HasArg("-help"))
22: 		{
23: 			n_printf("-file: the .zip file to unzip.\n");
24: 			n_printf("-path: where are the files unzip to, if this args is omitted, the file will be unzip into current directory.\n");
25: 			return false;
26: 		}
27: 
28: 		Util::String zipFile;
29: 		zipFile = this->args.GetString("-file");
30: 		// current .exe directory
31: 		this->sourcePath = Util::String("bin:") + zipFile;
32: 		bool fileValid = this->ioServer->MountZipArchive(this->sourcePath);
33: 		if (!fileValid)
34: 		{
35: 			// absolute path
36: 			this->sourcePath = Util::String("file:///") + zipFile;
37: 			fileValid = this->ioServer->MountZipArchive(this->sourcePath);
38: 			if (!fileValid)
39: 			{
40: 				n_error("Cannot open zip file.\n");
41: 				return false;
42: 			}
43: 		}
44: 		this->zipFileName = zipFile.ExtractFileName();
45: 		this->zipFileName.StripFileExtension();
46: 		this->sourcePath = this->sourcePath.ExtractDirName() + "/";
47: 
48: 		// target directory
49: 		this->targetPath = this->args.GetString("-path");
50: 		if (this->targetPath.Length() <= 1 || this->targetPath[1] != ':')
51: 		{// relative path
52: 			this->targetPath = Util::String("bin:") + this->targetPath;
53: 		}
54: 		else
55: 		{// absolute path
56: 			this->targetPath = Util::String("file:///") + this->targetPath;
57: 		}
58: 		this->targetPath += "/";
59: 		if (this->sourcePath == this->targetPath)
60: 		{
61: 			n_printf("the source diretory cannot be the same with the destination!");
62: 			return false;
63: 		}
64: 		return true;
65: 	}
66: 	return false;
67: }
68: 
69: void UnZipApp::Run()
70: {
71: 	UnZipDir(this->zipFileName);
72: }
73: 
74: void UnZipApp::UnZipDir( Util::String& dir )
75: {
76: 	// create a new directory
77: 	this->ioServer->CreateDirectory(this->targetPath + dir);
78: 	// unzip the files in this directory
79: 	Util::Array<Util::String> listFile = this->ioServer->ListFiles(this->sourcePath + dir, "*");
80: 	for (IndexT i = 0; i < listFile.Size(); i++)
81: 	{
82: 		Util::String curFile = this->targetPath + dir + "/" + listFile[i];
83: 		this->ioServer->CopyFile(this->sourcePath + dir + "/" + listFile[i], curFile);
84: 		n_printf("%s\n", curFile.AsCharPtr());
85: 	}
86: 	// unzip the sub directories
87: 	Util::Array<Util::String> listDir = this->ioServer->ListDirectories(this->sourcePath + dir, "*");
88: 	for (IndexT i = 0; i < listDir.Size(); i++)
89: 	{
90: 		Util::String curDir = dir + "/" + listDir[i];
91: 		n_printf("%s\n", (this->targetPath + curDir).AsCharPtr());
92: 		UnZipDir(curDir);
93: 	}
94: }

調(diào)試參數(shù):

運行結(jié)果:

posted @ 2008-12-14 21:29 Condor 閱讀(1283) | 評論 (0)編輯 收藏

IO子系統(tǒng)

Nebula3的IO系統(tǒng)相對于Nebula1和2是一個巨大的進步, 新系統(tǒng)的主要設(shè)計目標有:

  • 使用更標準的機制, 如用URI來定位資源, 用MIME類型來區(qū)分數(shù)據(jù)格式
  • 一個靈活的流模型, 它不關(guān)心數(shù)據(jù)是來自文件, 內(nèi)存, HTTP連接還是其它地方
  • 從流讀寫不數(shù)據(jù)的數(shù)據(jù)類型也更方便, 例如要讀取的XML格式數(shù)據(jù)來自文件/內(nèi)存/網(wǎng)絡(luò)都沒問題
  • 另外, 新的流和讀寫類可以在運行時注冊到IO系統(tǒng)中
  • 相對于系統(tǒng)平臺的特定IO函數(shù), 像fopen()這樣的C Lib函數(shù)會有額外的性能或內(nèi)存損失. 所以在保證可移植性的前提下不損失性能, 必須使用特定平臺的IO函數(shù)

IO子系統(tǒng)的一些主要概念:

  • 一個中樞的IO::Console 對象連接控制臺處理器(console handler)來進行文本的輸入和輸出. 這保證了所有的Nebula3的文本輸出都通過一個集中的進出通道. 特定的控制臺處理器可以用特定的方式處理文本輸出(例如輸出到stdout, 游戲控制臺, 日志文件或網(wǎng)絡(luò)連接).
  • 重定向符做為路徑別名. 大體的功能跟Nebula1和2差不多, 除了從AmigaOS 的重定向符得到的靈感. Nebula3重定向符的一個新特性就是它們可以做為URI的別名. 例如, 重定向符”textures:”可以定義為 "http://www.radonlabs.de/textures", 這樣簡化的資源路徑"textures:mytexture.dds"就會解釋成這個絕對路徑: "http://www.radonlabs.de/textures/mytexture.dds" (太NB了, 把紋理放到網(wǎng)站上加載? 哈哈, 拿來做內(nèi)置廣告肯定很爽)
  • 流(Stream)做為基本的數(shù)據(jù)進出通道. 它提供了基本的API函數(shù) Open()/Close()/Read()/Write(), 但是可能完全隱藏了傳輸和存儲通道. 典型的例子有IO::FileStream, IO::MemoryStream, 或 Net::HttpStream
  • Stream reader 和 writer 是連接到流上并且實現(xiàn)了簡單易用的接口來讀寫數(shù)據(jù)格式. 例如你可以把IO::XmlReader連接到IO::FileStream來從文件系統(tǒng)讀取XML格式的數(shù)據(jù), 或者連接到IO::HttpStream來從HTTP連接讀取XML格式的數(shù)據(jù).

這里有個很好的代碼例子可以反映出Nebula3輸入輸出系統(tǒng)的強大:

1: IO::FileServer::Instance()->CopyFile("http://www.radonlabs.de/index.html", "temp:index.html");

這一行代碼從HTTP服務(wù)器拷貝了一個文件到當用戶的臨時目錄里去. 再多加幾行代碼, 你可以創(chuàng)建一個流對象指向HTTP服務(wù)器上的HTML文件, 連接一個XML reader到這個流上, 然后就可以在不存儲中間文件的基礎(chǔ)上進行解析HTML了.

標準重定向符

Nebula3初始化了以下幾個重定向符:

  • home: 指向應(yīng)用程序目錄, 一般在” C:\Program Files “下. Nebula3把這個目錄當成只讀的, 為的是不需要管理員權(quán)限就能運行.
  • user: 這個指向當前登錄的用戶目錄, 一般是指” C:\Documents and Settings\[username] “. Nebula3會自動創(chuàng)建一個本地目錄來避免不同程序覆寫掉它們的數(shù)據(jù). 所以說一般情況下把數(shù)據(jù)寫入用戶目錄是安全的. 這個地方可以用于保存游戲數(shù)據(jù)和配置, 或者程序需要調(diào)用的持久性數(shù)據(jù).
  • temp: 這個指向當前用戶的臨時目錄, 一般是可寫的, 但是不要假設(shè)下一次啟動程序時數(shù)據(jù)還存在.
  • bin: 這個指向應(yīng)用程序可執(zhí)行文件的目錄. 它可以跟home相同, 也可能不同. 這個目錄應(yīng)該也當成是只讀的來對待.

其它重定向符可以在程序運行時進行定義. 通常情況下會定義一些抽象資源路徑, 如textuers, sound, data等等. 這樣的話資源的路徑就可以只更改重定向符的定義而是不是去替換所有的路徑. 重定向符的另一個好處就是減少了路徑字符串的長度, 在一定程序上節(jié)省了內(nèi)存占用.

URI(統(tǒng)一資源定位符)

在Nebula3中的資源位置通常都是用URI定義的. URI一般包括下面這幾部, 有一些是可選的:

  • 模式(協(xié)議?), 如"http:", "file:", 等... Nebula3 沒有硬編碼任何模式, 而跟流類綁定在一起注冊到IO::StreamServer 單件
  • 一個可選的用戶信息字段, 這是一個用戶名和密碼用于HTTP或FTP主機的身份驗證
  • 一個主機名, 如"www.radonlabs.de"
  • 一個在主機名后可選的端口號
  • 一個本地路徑, 指向主機上的一個資源
  • 一個可選的片段, 通常指向資源內(nèi)部的一個位置
  • 一個可選的查詢部分, 一般包含一個PHP腳本或其它相似的動態(tài)響應(yīng)機制的參數(shù)

IO::URI類用來傳遞URI并且解析URI字符串到它的各個部分中. 值得注意的是URI對象比字符串占用更多的內(nèi)存, 所以有時把URI保存在字符串中, 并在需要分割的時候才使用IO::URI類會更好一些.

這里有一些URI的例子:

1: file:///c:/temp/bla.txt

2: file://samba/temp/bla.txt

3: http://www.radonlabs.de/index.html

4: http://user:password@www.myserver.com:8080/index.html#main

通過使用重定位符會大大簡化路徑名稱. 要引用一個程序目錄的文件你可以使用”home:bla.txt”, 等價于file:///c:/Program Files/[myapp]/bla.txt.

Stream, Reader 和 Writer

流(Stream)提供了用于儲存和傳輸原始數(shù)據(jù)的接口. 一個流對象提供了傳統(tǒng)的Open()/Close()/Read()/Write()/Seek()接口, 其中有些還提供內(nèi)存映射, 這樣數(shù)據(jù)的讀寫可以直接通過內(nèi)存訪問來實現(xiàn). Stream對象用一個IO::URI對象來定義它們的資源位置. 通常情況下, 一個URI格式映射到一個特定的流對象. 例如”http:”URI格式一般映射到Net::HttpStream類, 而”file:”格式則映射到IO:FileStream類. 這個映射由StreamServer構(gòu)造一個流對象并匹配一個URI. 一個Nebula3應(yīng)用程序通過StreamServer::Register()方法來注冊這個映射關(guān)系, 這也是新的流對象和URI格式的注冊方法.

讓我們來看看有哪些重要的類:

  • IO::FileStream: 提供了訪問主機文件系統(tǒng)的功能
  • IO::MemoryStream: 一個具有流接口的動態(tài)內(nèi)存緩沖
  • IO::HttpStream: 提供了一個流接口來訪問HTTP服務(wù)器文件

Stream reader和writer類提供了一些舒適的接口專門處理特定的數(shù)據(jù)格式. 這里有一些stream reader和writer:

  • IO::BinaryReader/IOBinaryWriter: 讀寫二進制數(shù)據(jù)
  • IO::TextReader/IOTextWriter: 讀寫文本數(shù)據(jù)
  • IO::XmlReader/IOXmlWriter: 讀寫XML格式的數(shù)據(jù)
  • Messaging::MessageReader/MessagingMessageWriter: 消息序列化

這里有一個用XmlReader從HTTP服務(wù)器訪問文件的簡單例子

1:     using namespace IO;

2:

3:     Ptr<Stream> stream = StreamServer::Instance()->CreateStream("http://www.radonlabs.de/index.html");

4:     Ptr<XmlReader> xmlReader = XmlReader::Create();

5:     xmlReader->SetStream(stream);

6: if (xmlReader->Open())

7: {

8: // parse content here using the XmlReader interface

9: }

File Server(文件服務(wù)器)

Nebula3 IO::FileServer類提供了一個單件用于訪問主機的文件系統(tǒng)進行一些全局操作, 像定義重定向符, 復(fù)制, 刪除和檢查文件是否存在, 列出目錄內(nèi)容, 等等.

這個代碼片斷介紹FileServer的一些有用的方法:

using namespace IO;

using namespace Util;

FileServer* fs = FileServer::Instance();

// check if a file or directory exists

bool fileExists = fs->FileExists("home:bla.txt");

bool dirExists = fs->DirectoryExists("temp:bla/blub");

// resolve a path with assigns into an absolute filesystem

// path, this is sometimes necessary to interface with

// 3rd party libraries which don't understand Nebula3 paths directly

String absPath = fs->ResolveAssings("user:myapp/savegames");

// create a directory, note that all missing subdirectories will

// be created as well

fs->CreateDirectory("user:myapp/savegames");

// copy and delete files

fs->CopyFile("home:movie.mpg", "temp:movie.mpg");

fs->DeleteFile("temp:movie.mpg");

// list files in a directory matching a pattern

Array<String> files = fs->ListFiles("temp:", "*.txt");

// list all subdirectories in temp:

Array<String> dirs = fs->ListDirectories("temp:", "*");

控制臺

一般不直接調(diào)用IO::Console, 直接n_printf(), n_error(), n_dbgout(), n_warning()@_@

posted @ 2008-12-14 21:28 Condor 閱讀(1140) | 評論 (0)編輯 收藏

Nebula3工具庫, 包含一些工具類, 容器類, 還有一個強大的String類.
下面分別來看一下有哪些東東:
Array< TYPE >
動態(tài)數(shù)組, 類似std::vector, 自帶了排序方法和二分查找
Atom< TYPE >
對于持續(xù)存在對象的共享引用. 簡單得來說, 就是一個生命周期很長的對象的智能指針, Atom<String>是最常用的, 作為常量字符串的封裝.
Blob
大塊內(nèi)存空間的封裝, 可以比較, 復(fù)制, 計算Hash值
CmdLineArgs
通用的命令行參數(shù)解析器, 格式: cmd arg0[=]value0 arg1[=]value1 arg2[=]value2
Crc
計算一段內(nèi)存的CRC值
Dictionary< KEYTYPE, VALUETYPE >
詞典類, 用于存儲映射. 類似于std::map. 取元素的時間復(fù)雜度為O(log n). 內(nèi)部是一個排序的Array實現(xiàn)的. 注意它只是在需要排序時才排, 所以加入元素很快, 而第一次的搜索會慢一些.
FixedArray< TYPE >
定長數(shù)組, 一維
FixedTable< TYPE >
表格, 定長二維數(shù)組
FourCC
四字符編碼, 相當于一個uint, 可以做為ID, 具有可讀性. 前面的工廠方法就用到了. (第一次見單引號里寫多個字符@_@, 如uint = ‘ABCD’;)
Guid
全局統(tǒng)一標識符(GUID), 每臺機器在不同時間生成的都不一樣, 可以說是唯一性的.
HashTable< KEYTYPE, VALUETYPE >
跟Dictionary很像, 不過內(nèi)部是用哈希表實現(xiàn)的, 搜索時間更快(O(1)), 內(nèi)存占用要大一些. 相當于stdext::hash_map
做KEY的類必需實現(xiàn)這個方法: IndexT HashCode() const
KeyValuePair< KEYTYPE, VALUETYPE >
相當于std::pair
List< TYPE >
雙向鏈表, 相當于std::list
Proxy< TYPE >
相當于帶引用計數(shù)的智能指針, 普通類也可以用它進行包裝, 而不用繼承Core::RefCounted
Queue< TYPE >
隊列, 相當于std::queue
SimpleTree< VALUETYPE >
簡單的樹型結(jié)構(gòu), 結(jié)構(gòu)存儲在Array中
Stack< TYPE >
堆棧, 相當于std::stack
String
字符串類, 相當于std::string, 但是功能強大得多. 提供了與其它Nebula數(shù)據(jù)類型的轉(zhuǎn)換方法, 還有文件名操作函數(shù).
Variant
通用數(shù)據(jù)類型, 相當于COM中的VARIANT
關(guān)于各個類的詳細用法,可以參考testfoundation_win32工程.

posted @ 2008-12-14 21:26 Condor 閱讀(1031) | 評論 (0)編輯 收藏

核心子系統(tǒng)

核心庫(Core namespace)實現(xiàn)了這些特性:

  • 一個實現(xiàn)了引用計數(shù)的RefCounted基類
  • 一個運行時類型信息系統(tǒng)(RTTI)
  • 一個模板智能指針, 用于處理RefCounted對象的生命周期
  • 一個由類名創(chuàng)建C++對象實例的工廠機制
  • 一個中央Server對象用于建立基本的Nebula3運行環(huán)境

對象模型

Nebula3在C++對象模型的基礎(chǔ)之上實現(xiàn)了下面這些新特性:

  • 基于引用計數(shù)和智能指針的生命周期管理
  • 基于類名或四字符編碼的對象創(chuàng)建
  • 一個運行時類型信息系統(tǒng)

實現(xiàn)一個新的Nebula3類

當實現(xiàn)一個新的類時首先要考慮它是一個傳統(tǒng)的C++類還是要從Core::RefCounted繼承. 以下幾點可以幫你找到答案:

  • 如果這個類需要使用Nebula3的擴展對象特性, 如引用計數(shù), RTTI等, 則它必須從Core::RefCounted繼承.
  • 如果這個類是一個典型的小工具類, 如動態(tài)數(shù)組, 數(shù)學(xué)向量, 或其它相似的東西, 那么它從Core::RefCounted 繼承也沒有什么意義.

從Core::RefCounted類繼承有一些限制:

  • RefCounted派生類不應(yīng)該在棧上創(chuàng)建對象, 因為棧對象的生命周期是由C++來管理的(他們會在離開當前上下文時被銷毀, 從而繞過了Nebula3的引用計數(shù)生命周期 管理)
  • RefCounted的派生類只有一個默認的構(gòu)造函數(shù).
  • RefCounted的派生類必須有一個虛析構(gòu)函數(shù).
  • RefCounted的派生類不能進行拷貝, 因為這樣會造成引用計數(shù)機制混亂.

要使用Nebula3的對象模型特性, 除了需要從Core::RefCounted繼承外, 還需要在頭文件新類的聲明中進行額外的標注:

一個標準的RefCounted派生類一般這樣聲明:

1: namespace MyNamespace

2: {

3: class MyClass : public Core::RefCounted

4: {

5: DeclareClass(MyClass);

6: public:

7: /// constructor

8:     MyClass();

9: /// destructor

10: virtual ~MyClass();

11: ...

12: };

13: RegisterClass(MyClass);

注意DeclareClass()宏, 構(gòu)造函數(shù), 析構(gòu)函數(shù)還有類外面的RegisterClass()宏. DeclareClass()宏加入了RTTI和工廠機制所需的最小代價的信息, 它隱藏了Nebula3的對象模型, 希望可以在不影響已有類的基礎(chǔ)進上進行內(nèi)部機制的變更. RegisterClass()宏是可選的, 它把當前類在中央工廠進行注冊. 如果你知道這個類永遠不會由類名或四字符編碼進行創(chuàng)建, 這個宏可以省略.

在這個類的.cpp文件里需要包含Nebula3特有的信息:

1: namespace MyNamespace

2: {

3: ImplementClass(MyNamespace::MyClass, 'MYCL', Core::RefCounted);

4:

5: }

ImplementClass()宏注冊類的RTTI機制, 第一個參數(shù)描述了類的名字(注意命名空間必須包含). 第二個參數(shù)是類的四字符編碼, 它必須是所有類中唯一的(如果有重復(fù), 你會在啟動程序時得到一個錯誤提示). 第三個參數(shù)是父類的名字, 用于RTTI系統(tǒng)去構(gòu)造類的關(guān)系樹.

引用計數(shù)和智能指針

Nebula3使用傳統(tǒng)的引用計數(shù)來管理對象的生命周期. 一個模板智能指針類Ptr<>對程序員隱藏了引用計數(shù)的實現(xiàn)細節(jié). 一般來說, 應(yīng)該一直使用智能指針指向RefCounted的派生對象, 除非你能肯定在給出的代碼塊中這個對象的引用計數(shù)不會發(fā)生變化.

智能指針相對于一般指針有很多好處:

  • 訪問一個空指針會給你一個斷言警告而不是一個內(nèi)存錯誤
  • 你不需要對引用計數(shù)的對象調(diào)用AddRef()或Release() (事實上如果你調(diào)了, 會了發(fā)生嚴重的錯誤)
  • 智能指針可以在容器類里良好地工作, 一個智能指針的數(shù)組會消除所有的一般指針需要的生命周期管理, 你永遠不需要考慮去釋放指針所指針的對象, 數(shù)組包含的像是真正的C++對象一樣
  • 用智能指針不需要考慮指針的所屬, 不需要為誰delete對象而煩惱

智能指針也有一些缺點:

  • 性能: 拷貝和賦值會引起對象的引用計數(shù)的變化, 解除引用會引起指針的斷言檢查. 這導(dǎo)致的性能消耗一般是可以忽略的, 但是你最好保證它不在內(nèi)部循環(huán)中發(fā)生.
  • 應(yīng)該銷毀的對象還存在: 因為智能指針管理的對象只有在最后一個引用放棄時才會銷毀, 這樣會使對象存在超過預(yù)訂的時間. 這經(jīng)常會導(dǎo)致一個BUG的產(chǎn)生. 不過引用計數(shù)泄露(程序退出時還仍然存在的對象)時Nebula3會提醒你.

創(chuàng)建Nebula3對象

從Core::RefCounted繼承的類可以通過3種不同的方式進行創(chuàng)建:

直接通過靜態(tài)的Create方法:

1: Ptr<MyClass> myObj = MyClass::Create();

靜態(tài)的Create()方法是之前提到的DeclareClass()宏加入的, 相對于new操作符來說, 它并沒有多做什么. 注意正確使用智能指針來保存新建的對象.

另一種創(chuàng)建方式是通過類名:

1: using namespace Core;

2: Ptr<MyClass> myObj = (MyClass*)Factory::Instance()->Create("MyNamespace::MyClass");

當你在運行時通過類名來創(chuàng)建十分有用, 特別是對象的反序列化和腳本接口的使用. 注意類型轉(zhuǎn)換是必須的, 因為工廠的Creat()方法返回的是RefCounted指針.

由類名創(chuàng)建的變種是根據(jù)四字符編碼進行創(chuàng)建:

1: using namespace Core;

2: using namespace Util;

3: Ptr<MyClass> myObj = (MyClass*) Factory::Instance()->Create(FourCC('MYCL'));

這個方法看上去沒有那個直觀, 但是它比類名創(chuàng)建快得多. 并且四字符編碼比類名占用的空間更少, 這更利于對象寫入二進制流或從中讀取.

運行時類型信息系統(tǒng)

Nebula3的RTTI系統(tǒng)可以讓你在運行時訪問對象的類型, 檢查一個對象是不是某個類的實例, 或者某個派生類的實例. 你也可以直接獲得一個對象的類名和四字符編碼. 所有這些功能是由DeclareClass() 和 ImplementClass() 宏在背后實現(xiàn)的.

這時有示例程序:

1:     using namespace Util;

2:     using namespace Core;

3:

4: // check whether an object is instance of a specific class

5: if (myObj->IsInstanceOf(MyClass::RTTI))

6: {

7: // it's a MyClass object

8: }

9:

10: // check whether an object is instance of a derived class

11: if (myObj->IsA(RefCounted::RTTI))

12: {

13: // it's a RefCounted instance or some RefCounted-derived instance

14: }

15:

16: // get the class name of my object, this yields "MyNamespace::MyClass"

17: const String& className = myObj->GetClassName();

18:

19: // get the fourcc class identifier of my object, this yields 'MYCL'

20: const FourCC& fourcc = myObj->GetClassFourCC();

你也可以向中央工廠查詢一個類是否已經(jīng)注冊:

1:     using namespace Core;

2:

3: // check if a class has been registered by class name

4: if (Factory::Instance()->ClassExists("MyNamespace::MyClass"))

5: {

6: // yep, the class exists

7: }

8:

9: // check if a class has been registered by class fourcc code

10: if (Factory::Instance()->ClassExists(FourCC('MYCL')))

11: {

12: // yep, the class exists

13: }

Nebula3單件

很多Nebula3的核心對象都是單件, 就是只存在一個實例, 并且所有其它對象都知道它.

你可以通過靜態(tài)方法Instance()來訪問單件, 它返回唯一實例的一個指針. 返回的指針保證是合法的. 如果在調(diào)用Instance()方法時對象實例不存在, 一個斷點會被拋出:

1: // obtain a pointer to the Core::Server singleton

2:     Ptr<Core::Server> coreServer = Core::Server::Instance();

你也可以檢查單件是否存在:

1: // does the Core::Server object exist?

2: if (Core::Server::HasInstance())

3: {

4: // yep, the core server exists

5: }

Nebula3提供了一些輔助的宏來實現(xiàn)單件:

1: // declare a singleton class

2: class MySingletonClass : public Core::RefCounted

3: {

4: DeclareClass(MySingletonClass);

5: DeclareSingleton(MySingletonClass);

6: public:

7: /// constructor

8:     MySingletonClass();

9: /// destructor

10: virtual ~MySingletonClass();

11: ...

12: };

13:

14: // implement the singleton class

15: ImplementClass(MyNamespace::MySingletonClass, 'MYSC', Core::RefCounted);

16: ImplementSingleton(MyNamespace::MySingletonClass);

17:

18: //------------------------------------------------------------------------------

19: /**

20:     Implements the Singleton constructor.

21: */

22: MySingletonClass::MySingletonClass()

23: {

24: ConstructSingleton;

25: }

26:

27: //------------------------------------------------------------------------------

28: /**

29:     Implements the Singleton destructor.

30: */

31: MySingletonClass:~MySingletonClass()

32: {

33: DestructSingleton;

34: }

DeclareSingleton()和ImplementSingleton()宏跟DeclareClass()和ImplementClass()宏差不多.它們在類中添加了一些靜態(tài)方法(也就是Instance()和HasInstance()). 類的構(gòu)造函數(shù)和析構(gòu)函數(shù)必須包含ConstructSingletonDestructSingleton宏. ContructSingleton初始化了一個私有的單件指針并保證沒有其它的類實例存在(如果不是, 會拋出斷言). DestructSingleton讓私有的單件指針無效化.

單件的訪問默認是只有本地線程. 這意味著在一個線程中創(chuàng)建的單件無法被其他線程訪問. 這使得”并行Nebula”大大簡化了多線程編程. “并行Nebula”的基本思想是, 一個典型的Nebula3應(yīng)用程序包含一些”Fat線程”, 每一個Fat線程都是運行在一個單獨的CPU核心上. Fat線程可以用于實現(xiàn)異步IO, 渲染, 物理等等. 每一個Fat線程都初始化了它們自己的Nebula3運行環(huán)境, 它們執(zhí)行特性任務(wù)所需的最少依賴. 這基本上消除了大部分Nebula3代碼的同步問題, 并且把線程相關(guān)的代碼集中到一個明確定義的代碼區(qū)域中. “并行Nebula”的另一個好處就是, 程序員在多線程環(huán)境中編程時不需要關(guān)心太多. 大多數(shù)Nebula3代碼看起來就像單線程代碼一樣, 但是它們卻運行在各自的Fat線程中.

性能與內(nèi)存占用的考慮

Nebula3核心層的一個設(shè)計目標就是減少底層代碼的內(nèi)存占用, 來更好的適應(yīng)微型平臺, 像手持設(shè)備. 這里有一些已經(jīng)完成的目標:

  • RefCounted 類在每個實例中只增加了4byte用于引用計數(shù).
  • RTTI機制在開頭增加了30 到 60 byte, 但是這是對于每個類來說的, 而是不是每個實例.
  • 一個智能指針僅僅4 byte, 就像普通指針一樣.
  • 一些監(jiān)控結(jié)構(gòu)只會在debug模型下創(chuàng)建, 特別是用來檢測引擎計數(shù)泄露的RefCountedList.

這里一些用三種不種的創(chuàng)建方法創(chuàng)建一百萬個RefCounted 對象所需的時間信息. 這些時間信息是在臺Intel Pentium 800 MHz的筆記本上得出的.  

  • Create(): 0.29 seconds
  • FourCC: 0.65 seconds
  • 類名: 1.45 seconds

posted @ 2008-12-14 21:04 Condor 閱讀(972) | 評論 (0)編輯 收藏

僅列出標題
共10頁: First 2 3 4 5 6 7 8 9 10 
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            亚洲人成网站在线观看播放| 欧美一区二区大片| 亚洲欧美日韩精品久久奇米色影视| 最新精品在线| 亚洲区免费影片| 日韩视频精品| 亚洲欧美在线高清| 久久久久久久性| 亚洲国产91精品在线观看| 亚洲国产另类久久精品| av成人免费在线观看| 亚洲综合三区| 久久天堂成人| 国产精品qvod| 亚洲大片免费看| 亚洲免费视频在线观看| 久久亚洲精品中文字幕冲田杏梨| 欧美黄色视屏| 亚洲欧美日韩国产综合| 欧美mv日韩mv国产网站app| 欧美视频在线免费看| 精品电影在线观看| 亚洲一区日韩在线| 免费看亚洲片| 亚洲欧美日韩一区二区三区在线观看| 老鸭窝亚洲一区二区三区| 欧美日韩一区视频| 亚洲福利视频网站| 久久成人亚洲| 夜夜嗨av一区二区三区中文字幕| 久久久久九九九九| 国产精品午夜电影| 一本大道久久a久久精二百| 快she精品国产999| 亚洲男女自偷自拍| 欧美视频在线视频| 一区二区三区www| 欧美成人免费大片| 欧美一区影院| 中文在线不卡视频| 久久九九国产精品怡红院| 国产精品伦理| 亚洲小少妇裸体bbw| 亚洲观看高清完整版在线观看| 性欧美暴力猛交69hd| 国产精品国产a级| 9国产精品视频| 亚洲大胆在线| 久久综合亚州| 在线观看亚洲精品| 久久这里只有精品视频首页| 亚洲一品av免费观看| 欧美日韩喷水| 亚洲午夜伦理| 日韩亚洲视频在线| 欧美日韩精品一区视频| 亚洲麻豆一区| 亚洲精品国产欧美| 欧美日本不卡高清| 一区二区三区日韩在线观看| 亚洲激情网址| 欧美看片网站| 亚洲影院在线观看| 亚洲一级片在线观看| 国产毛片精品视频| 久久爱91午夜羞羞| 欧美在线不卡| 亚洲第一久久影院| 亚洲国产日韩欧美在线图片| 欧美大片91| 一区二区三区成人精品| 日韩午夜在线视频| 国产乱码精品一区二区三区不卡 | 欧美不卡视频| 亚洲七七久久综合桃花剧情介绍| 欧美1区免费| 欧美成人精品激情在线观看 | 欧美激情网站在线观看| aa级大片欧美| 亚洲一区二区三区四区中文 | 久久九九精品| 久久欧美中文字幕| 99精品视频免费在线观看| 日韩一区二区免费看| 国产农村妇女精品| 女仆av观看一区| 欧美日韩一区三区| 久久久久久九九九九| 欧美v亚洲v综合ⅴ国产v| 亚洲天堂网站在线观看视频| 亚洲欧美视频一区| 亚洲精品一区二区三区四区高清 | 国产精品综合av一区二区国产馆| 欧美一级在线播放| 美女免费视频一区| 亚洲免费在线| 久久久久久久国产| 亚洲影视中文字幕| 老司机免费视频一区二区三区| 在线视频一区二区| 久久精品二区亚洲w码| 宅男在线国产精品| 久久精品国产精品亚洲综合| 日韩性生活视频| 久久久精品国产免大香伊| 亚洲私人影吧| 久久综合五月| 久久成人精品无人区| 欧美日韩一区三区| 亚洲高清二区| 伊人久久亚洲影院| 亚洲欧美日韩另类| 正在播放亚洲一区| 噜噜噜噜噜久久久久久91| 欧美亚洲专区| 欧美婷婷在线| 亚洲欧洲精品一区二区三区不卡 | 日韩视频欧美视频| 在线成人中文字幕| 中文精品一区二区三区| 欧美国产欧美亚洲国产日韩mv天天看完整 | 久久久久久夜| 国产精品福利av| 最新成人在线| 91久久久国产精品| 久久婷婷久久一区二区三区| 欧美在线免费看| 国产精品久久久久久久久| 亚洲精品亚洲人成人网| 亚洲精品极品| 欧美大片在线观看| 亚洲国产老妈| 一区二区三区四区五区精品| 欧美日韩国产二区| 亚洲精品一二三| 亚洲视频在线观看免费| 欧美性理论片在线观看片免费| 亚洲精品网站在线播放gif| 一区二区三区国产盗摄| 欧美丝袜一区二区| 一区二区三区视频在线观看| 亚洲图片在线| 国产精品一区视频| 欧美一级久久久久久久大片| 久久aⅴ国产欧美74aaa| 国内一区二区在线视频观看| 久久久久久亚洲综合影院红桃 | 久久精品日产第一区二区三区| 国产欧美一区二区在线观看| 欧美激情一区二区三区不卡| 国产一区二区三区直播精品电影| 午夜国产不卡在线观看视频| 久久本道综合色狠狠五月| 国产视频在线一区二区| 久久国产高清| 亚洲风情在线资源站| 亚洲精品综合久久中文字幕| 欧美视频一区在线| 欧美在线一级视频| 欧美激情乱人伦| 在线视频一区观看| 国产午夜精品一区理论片飘花| 欧美在现视频| 亚洲精品国产精品国自产观看浪潮 | 免费亚洲电影在线| 亚洲精品自在在线观看| 欧美亚洲一级| 伊人久久综合97精品| 欧美精品久久久久a| 亚洲一区二区免费| 久久人人爽人人爽爽久久| 亚洲看片一区| 国产欧美三级| 免费成人黄色| 亚洲一区欧美激情| 欧美电影免费观看网站| 午夜精品福利视频| 亚洲国产视频一区| 国产精品久久久久免费a∨| 久久久亚洲国产美女国产盗摄| 91久久精品国产91性色tv| 久久久蜜桃一区二区人| 亚洲激情在线观看| 国产欧美日韩视频一区二区三区| 美女久久一区| 西西人体一区二区| 一本一本久久| 欧美激情在线有限公司| 欧美一级久久久久久久大片| 91久久久久久久久| 国产一区二区久久| 国产精品一区二区久久久| 欧美www视频在线观看| 性欧美xxxx大乳国产app| 亚洲欧洲中文日韩久久av乱码| 久久影音先锋| 久久精品国产清自在天天线 | 日韩视频专区| 黄色日韩在线| 国产精品黄色在线观看|