Unreal2初步分析(u3的底層架構基本不變,跟u2相同)
Unreal2打造一套和腳本緊密結合的引擎
總概:
具體過程:
一。生成類信息體PrivateStaticClass:
dll裝載時:
dll各類全局靜態類信息對象PrivateStaticClass的構造函數中產生對應類的信息(對應包名,分配函數,靜態構造函數),
各對象存入GAutoRegister鏈表.
二。初始各類信息體:
appInit()調用時:
1. 遍歷GAutoRegister鏈表, 注冊每一個靜態類信息對象, 使用對應包名(所在dll)為其產生對應UPackage對象,獲得對應dll句柄。
2. 并構造類成員Defaults缺省對象,將基類全局靜態對象中成員default數據拷給它(所謂繼承), 并用其作為this調用類對應靜態構造函數,
3. 可在靜態構造函數中注冊類成員變量,注冊的變量將作為原PrivateStaticClass對象的children結點.
5. 接著遍歷全局靜態對象的Children,將它們歸類放在PropertyLink和ConfigLink鏈中(其中PropertyLink是全部所有屬性)
6. 再來就是可通過config裝載它自己的屬性初始值到Default對象中。
三。 引擎初始:
1. 先初始引擎類:
EngineClass = UObject::StaticLoadClass( UGameEngine::StaticClass(), NULL, TEXT("ini:Engine.Engine.GameEngine"), NULL, LOAD_NoFail, NULL );
a. 為其產生ULinkerLoad:
在ULinkerLoad構造函數中:
1). 打開對應的包文件Engine.u, 并將它的LinkerRoot指向該類對應的包對象,
2). 從文件中讀取Summary,名字表,導入導出表,最后將此ULinkerLoad對象存入GObjLoaders列表中,
3). 再通過 VerifyImport(i)來遍歷導入表檢查每一個導入項的有效性,
下面具體分析a. 步驟中VerifyImport(i)的內容:
每個導入表記錄了在哪個包的名稱,對應哪個類的名稱,這個校驗首先查找創建對應包對象(存dll句柄)以及對應ULinkerLoad對象(存包對應的.u文件句柄)
而在為導入項產生ULinkerLoad的構造函數中又回到了 1). 的步驟中,如此循環不停地裝載所有依賴的dll及對應的.u文件。
b. 通過ULinkerLoad對象來產生對應的GameEngine對象
Linker->Create()即是通過.u文件讀入的導入類來產生該對象(同時讓SuperField指向產生根結點)并加到GObjLoaded鏈表中,如果產生失敗,則
轉換Result = StaticFindObject( ObjectClass, InOuter, InName );來通過dll中的類來產生它。
c. EndLoad()
遍歷GObjLoaded鏈表,裝載用Object->_LinkerIndex取出對應FObjectExport,將其屬性內容讀入該對象內容。
[注意]是先從基對象SuperField->Preload()開始再到其派生類逐步讀入。另外在UStruct的Serialize函數中會裝載children,
這又會依賴其它導入導出庫,因此會重復多次觸發導入導出相關處理。
另外如果是Native類,就不會通過link的create,而是通過StaticFindObject返回一個
2. 再產生引擎實例:
UEngine* Engine = ConstructObject<UEngine>( EngineClass );
這是使用第1步生成的EngineClass(即GameEngine對象)來創建一個新的名為GameEngine0的對象.(這會將GameEngine對象的內容克隆給GameEngine0)
然后再調用類的構造函數UGameEngine::UGameEngine()來初始成員。
3. 初始引擎:
Engine->Init();
遍歷GObjObjects鏈表,進行加載
四。關于.u文件的生成:
由uccdepend轉調用ucc.exe來遍歷查找所有packages來讀取uc腳本生成。
本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/flipcode/archive/2010/04/06/5453730.aspx
posted @
2010-11-03 23:13 flipcode 閱讀(318) |
評論 (0) |
編輯 收藏
每個種族對應一個AiInterace對象,這個對象有所有該族的所有角色對象指針,并擁有以下AiRule成員:
AiRuleWorkerHarvest
AiRuleRefreshHarvester
AiRuleScoutPatrol
AiRuleRepair
AiRuleReturnBase
AiRuleMassiveAttack
AiRuleAddTasks
AiRuleBuildOneFarm
AiRuleProduceResourceProducer
AiRuleProduce
AiRuleBuild
AiRuleUpgrade
AiRuleExpand
一。遍歷所有種族的AiInterace對象,進行AiRule的更新:
1. 在AiRuleXXXX的::test()測試該話是否要進行處理
2. 在AiRuleXXXX的::execute()中進行處理,為對應要處理的角色giveCommand(CommandType)
二。Command的更新:
每種cmd調用它成員UnitUpdater中對應的更新函數(比如HarvestCommandType::update中調用unitUpdater->updateHarvest),
在這更新中為對應角色指定對應的技能類形, 據技能類型進行處理。
AIRule-->CMD-->skilltype
另外操作一般都是以種族為類別進行的,所以選中一個單位后雙擊可以選中所有相同類型的角色。搜礦搜樹什么的也歸類搜索就減少消耗。
posted @
2010-11-03 23:12 flipcode 閱讀(175) |
評論 (0) |
編輯 收藏
天龍早期版本武俠世界瀏覽看了下,發現
服務端用的select輪詢方式,風格比較統一工整,消息處理成buf再組成pak進行發送或處理。
客戶端界面用xml配置加lua事件觸發
游戲對象使用結點樹目錄結構(這個n年前nebula引擎所用的技術,個人也喜歡這個方式)
游戲中的事件處理系統簡單實用。
水用比較老的處理方式:系列圖+深度圖過渡,效果一般效率高
region+dijkstra廣度優先的導航系統(天龍已修改成網絡導航)
lightmap靜態光照及動態光源
后期處理bloom以及熱浪之類,效果都挺廢.
渲染效果不太好但游戲整體比較完整
編輯器地表貼圖列表加載被注釋,對應文件也不見(打開并重寫該文件后可用)
它的地表只有兩層啊,其實沒什么技術含量,分開刷的,那個自動拼接硬編碼,刷第2層時通過if else判斷居然有1200多行。。。
不過這種地表處理可以方便刷翻轉墻壁。這很好。
小道消息暴下料:
1. 網絡代碼中居然殘留韓文亂碼(通過轉換可看到對應韓文,轉成其它語種則不行,可能是當初借鑒了些少代碼)
2. 天龍八部是武俠世界版本基礎上開發的(新改動不算大,尋路應該是由原來的dijkstra(不知道為什么用廣度搜索不用a*)+region變成了網格導航)
3. 成吉是在天龍基礎上做的(這個大家都知道,他們在打官司)
4. 天龍八部的前身武俠世界確實是是北京紅圖公司的,那個公司有一款名叫武俠世界web版本的mmorpg.(網上搜索不出他們之間有什么關系)
我覺得天龍2和成吉2基本上都是針對上一產品換個視角而已,技術及內容上沒什么變化。。。
其實現有國產網游效果都比較樸實(除了劍3天下2之類外),卻往往打著次世代的晃子。。。
目前網游已進入山寨抄襲換膚量產時代,當然高麗棒也是醬樣子。。。
posted @
2010-10-21 22:08 flipcode 閱讀(458) |
評論 (0) |
編輯 收藏
地表的處理方式很多:
1. 神跡的預生成處理,渲染時只有一層
2. glest加載時自動計算過渡層處理,渲染時只有一層。
3. hon的頂點alpha混合過渡處理,渲染時最多只有兩層
4. 天龍的自動拼接,渲染時最多只有2層(天龍為了實現這個可謂花費大力氣if else寫了1000來行。。。。)
5. 海盜王的自動拼接,渲染時最多4層混合
6. 三國爭霸,war3和夢3的自動拼接(帶隨機細節圖塊),渲染時最多4層混合
7. 當前流行的splatting地表使用alphamap進行混合,如: 虛幻2,wow, 泰坦之旅(這個可無限層,越多層越慢)
另外天機,神鬼也是這種方式(限死最多4層每個patch塊)
posted @
2010-09-19 18:09 flipcode 閱讀(204) |
評論 (0) |
編輯 收藏
測試了下實時shadowmap,比較簡單取巧的方法:使用project shadowmap動態調整正交區域,這個取巧目前只適合這種視角游戲,而且速度也很快。(這種2.5d游戲也可以使用平面陰影速度快地渲染,而且沒這么多鋸齒) 上述水用了gem2中wave撓動,反射處理,沒折射,邊緣沒作過渡(可簡單傳入水高圖處理)
gif動畫圖:
posted @
2010-09-10 09:36 flipcode 閱讀(320) |
評論 (0) |
編輯 收藏
有地方需要用,這里臨時放下
posted @
2010-01-19 10:26 flipcode 閱讀(267) |
評論 (0) |
編輯 收藏
動態類型識別以及池創建
由于頻敏的new效率較低,需要避免直接new,這有兩種辦法:
1. 讓程序重載new統一將內存分級別鏈來管理分配內存(可參考nebula1中的內存管理)
2. 使用對象池(可參考boost object pool)
我這里臨時使用boost的object pool以及u2的rtti類概念寫了一個池創建類
這樣處理后就可以很方便地將類似派生樹這樣的類分別使用池方式統一接口來管理。類似
class kWindow;
class kButton : public kWindow;
class kLabel : public kWindow;
假如有很多這樣的控件,如果你用類boost的object pool,那你要手寫每個類對應一個pool mgr,
在釋放時還要知道釋放的對象屬于哪個poolmgr, 再用這個poolmgr來釋放。。。。
使用下面的類可簡化為:
kButton *p = kTClass::Create("kButton"); // 統一創建接口(只需控件類名稱)
p->Release(); // 統一釋放接口
template <typename T, bool bTPool>
struct kAllocMgr;
template <typename T>
struct kAllocMgr<T, false>
{
static object_new_delete<T, default_user_allocator_new_delete> pool;
};
template <typename T>
object_new_delete<T, default_user_allocator_new_delete> kAllocMgr<T, false>::pool;
template <typename T>
struct kAllocMgr<T, true>
{
static object_pool<T, default_user_allocator_new_delete> pool;
};
template <typename T>
object_pool<T, default_user_allocator_new_delete> kAllocMgr<T, true>::pool;
//------------------------------------------------------------
//
flipcode@msn.comclass kRoot;
class kClass
{
friend class kKernelServer;
LPCTSTR m_szClassName;
INT m_nObjSize;
kRoot* (*m_pfnCreate)();
kClass* m_pBaseClass;
kClass* m_pNextClass;
public:
kClass (LPCTSTR szClassName, int nObjSize, kRoot* (*NewObject)(), kClass *baseClass);
LPCTSTR GetName(){
return m_szClassName;
}
};
//根據類名取得定義類
#define GETCLASS(ClassName) (&ClassName::m_class##ClassName)
//類定義聲明宏
#define K_RTTI_DEC(ClassName) \
public: \
static kClass m_class##ClassName; \
virtual const kClass* GetClass(VOID) const; \
static kRoot* Create(); \
void Destory();
//類定義實現宏
#define K_RTTI_IMP(ClassName, baseClass) \
static TCHAR sz##ClassName[] = _T(#ClassName); \
kClass ClassName::m_class##ClassName(sz##ClassName, sizeof(ClassName), ClassName::Create, baseClass); \
const kClass* ClassName::GetClass() const \
{ return &ClassName::m_class##ClassName; } \
kRoot* ClassName::Create() \
{ \
ClassName *p = kAllocMgr<ClassName, true>::pool.construct(); \
return p; \
} \
void ClassName::Destory(){ \
kAllocMgr<ClassName,true>::pool.destroy(this); \
}
另外配合nebula的結點結象樹的方法可以作到比較的對象管理(幾年前我已寫過教程):
class kKernelServer : public kSingleton<kKernelServer>
{
friend class kClass;
map<STRING, kClass*> m_ClassMap;
kClass *m_pFirstClass;
kRoot m_Root;
kRoot *m_pCWD;
kRoot* _NewObject( LPCTSTR szClassName, LPCTSTR szName );
void ReisgerClass(kClass* pNewClass);
public:
kKernelServer(void);
~kKernelServer(void);
kRoot* NewNode( LPCTSTR szClassName, LPCTSTR szPath );
kRoot* NewNode( LPCTSTR szClassName, LPCTSTR szPath, LPCTSTR szName);
kRoot* Lookup( LPCTSTR szPath );
void SetCwd( kRoot *o ){
this->m_pCWD = o ? o : &m_Root;
}
kClass* GetClass(LPCTSTR szClassName);
BOOL IsKindOf(const kClass* pThisClass, const kClass* pBaseClass) const;
BOOL LoadPlugin(LPCTSTR szPluginName, void * pParam);
void ShutDown(void);
protected:
struct PLUGININFO
{
HMODULE hModule;
STRING strPluginFile;
FUNDLLINIT pfnDllInit;
FUNDLLRELEASE pfnDllRelease;
};
void FreeAllPlugin(void);
std::vector< PLUGININFO > m_vecPlugin;
};
posted @
2009-12-10 10:44 flipcode 閱讀(974) |
評論 (1) |
編輯 收藏
查找游戲中突然卡機問題小結
一。 查其原因主要是
lua局部表引起的,類似這樣:
local tt[] = {}
tt[1] = {1,2,3}
tt[2] = {1,2}
...
總共就20個元素左右,并不多,這樣的表格在極端情況下偶然發生消耗幾百毫秒(在我們測試機上似乎是500ms左右)
二。 是new
腳本有些類似調用
for i=100, 5000, 1
。。。
addkeypos(i, p1, p2)
end
由于程序在addkeypos函數中使用了new,這導致有時偶然有些new消耗10多ms,
4000次下來累計偶然會占高。解決辦法就是讓程序去掉new處理成池的方式, 具體參考我的另一文章:
http://www.shnenglu.com/flipcode/archive/2009/12/10/102902.html三。還有就是頻率調用字符轉換函數,類似sprintf("%s%d", "sdfdf", 2100 )也是一個非常消耗的地方!
解決辦法就是用itoa以及atoi來轉換
四。哦,還有就是lua的回收也是消耗很大,簡單辦法就是用它提供的分步回收(不在同一幀收完),看到云風博客提到分state方法,感覺比較煩,我們沒有使用.
posted @
2009-12-10 10:23 flipcode 閱讀(180) |
評論 (0) |
編輯 收藏
三國爭霸論壇中玩家制作的漂亮截圖
www.7fgame.com




posted @
2009-11-17 10:47 flipcode 閱讀(235) |
評論 (0) |
編輯 收藏
高光+暴光效果
posted @
2009-10-25 00:27 flipcode 閱讀(346) |
評論 (0) |
編輯 收藏