看來樓上是行家,一眼就識破了,的確用了兩層紋理,一共8個通道,1個通道做光照,另外7個通道做混合,用的SM2.0,不過昨天加入了shadowmap支持的陰影,ps象素運算指令數超過了64,逼迫我用了SM3.0,郁悶,我今天準備來優化
所有的場景都是用我的用這個編輯器生成,和繪制的
那個黃色的圈就是筆刷,地形高低,紋理,光照都是刷子刷出來的,參照了OGRE的goof插件編輯方式 ,還有參考了photoshop的刷子,那個是刷平面,我是刷3d的場景呵呵,有點不同的感覺
模型是從編輯器拖放到場景中去的和3dmax感覺差不多,這就是個3d游戲的場景編輯器,是游戲引擎的一部分,完全自主獨立開發,下午又發現了一些bug,正在改進之中...
昨天和你聊了聊,所獲不小
搞了半天還算是鄰居
以后多多交流啊
re: utf8編碼轉換 李侃 2008-04-16 23:26
我之前也寫過一個,和這個基本一樣。
不過還是收藏了。
謝謝!
@alexandercer
網上找的代碼,是個高人寫的。
你就在google里面搜索:mfc 自動隱藏,就可以找到了。
他擴展了MFC的控件
我下了他的代碼,拿過來用了,呵呵。。。要是自己寫可麻煩了
插件的形式?是生成 h文件 & lib & DLL 然后去組裝重用嗎?
等做的差不多的時候,我會統一打包成組件。
然后在游戲里面去調用這些DLL
re: 關于資源包的新的比較完善的想法 李侃 2008-03-13 11:54
對,我也這樣想,所以,暫時也不打算在這上面花太多時間。
現在的封裝基本已經夠用了,只是還沒有把加密算法加入進去
這個運用現有的算法庫應該很快
re: 關于資源包的新的比較完善的想法 李侃 2008-03-12 21:58
呵呵,似乎很多人都不愿意這樣去做。
也許真的是太復雜了。
re: IOCP Tips 李侃 2008-03-12 12:35
應該把OVERLAPPED 結構(一個或多個)綁定到你的session上去,避免OVERLAPPED 被多個用戶共用,會減少很多“不必要”的麻煩,另外一個OVERLAPPED 做完一件事情之前,不要讓它再去做另外一件事情,這兩點是我比較深刻的體會,
思路3
直接打包文件進行操作
實現數據塊向前搬移操作,這個相當于刪除操作了
而 刪除+添加=修改
不需要太高效的情況下,這個方法思路更好一些
boost 庫感覺太復雜,看源碼好辛苦啊,memory 序列化已經實現,現在想寫文件序列化,可能會說我再做重復勞動,可自己寫也會有很多好處的。
我現在只是想做個輕量級的,游戲資源包生成器,上面的方案對于修改來說的確是個問題。
還有兩個思路
1. 寫個腳本,用執行腳本的方式來生成資源包文件,并且資源包分類,多搞一些資源包,生成資源包嚴格用腳本來控制,“慢”就慢一點,方便就好,最重要的是保證讀取和運行的效率高,修改那個資源包就重新生成哪個資源包。
2. 如果要修改文件,把整個文件都讀入到 vector<char> buf 里面去然后對vector<char>buf 進行修改,然后一次性覆蓋回去實現修改。
re: 這段時間加入了網絡序列化的功能 李侃 2008-03-06 16:47
恩,好像大家都這么說,有時間的話,我也去看看Boost的序列化的源碼,看看有什么更好的可取之處
re: 再談sizeof()的問題 李侃 2008-03-04 13:04
字節對齊問題,可以取消pack(4)到pack(1) ,結果就又不一樣了哦
re: 這段時間加入了網絡序列化的功能 李侃 2008-03-02 13:21
具體實現就不發上來了,給你看一段測試的代碼,模版的好處太多了
template <typename T>
void TestSerialize(T& val, bool enableTag)
{
ObjectStream s(enableTag);
StreamBuffer buf;
DWORD size = s.GetLength(val);
buf.realloc(size);
// write
assert( s.BeginWriting(&buf) );
assert( s.Write(val) );
assert( s.EndWriting() );
DumpBuffer(buf);
// read
T val_read;
assert( s.BeginReading(&buf) );
assert( s.Read(val_read) );
assert( s.EndReading() );
// nothing left to read
assert( buf.GetRestLength() == 0 );
// we read what we wrote
assert( val == val_read );
}
class Foo:public SerializeObject
{
public:
Foo(){
ZeroMemory(m_chr, 20);}
void Init(const std::string& s, const std::vector<DWORD>& v, DWORD d, const char * chr)
{
m_s = s;
m_v = v;
m_d = d;
strcpy(m_chr, chr);
}
bool operator==(const SerializeObject &other) const
{
return m_d == ((Foo&)other).m_d &&
m_v == ((Foo&)other).m_v &&
m_s == ((Foo&)other).m_s &&
strcmp(m_chr, ((Foo&)other).m_chr)==0;
}
SerializeTag ComputeTag()
{
return eTAG_USERCLASS;
}
bool Write(ObjectStream& stream)
{
return stream.Write(m_d) &&
stream.Write(m_s) &&
stream.Write(m_v) &&
stream.WriteRaw(m_chr, (DWORD) strlen(m_chr));
}
bool Read(ObjectStream& stream)
{
return stream.Read(m_d) &&
stream.Read(m_s) &&
stream.Read(m_v) &&
stream.ReadRaw((void *)m_chr);
}
DWORD GetLength(ObjectStream& stream)
{
return stream.GetLength(m_d) +
stream.GetLength(m_s) +
stream.GetLength(m_v) +
stream.GetRawLength((DWORD) strlen(m_chr));
}
public:
DWORD m_d;
std::string m_s;
std::vector<DWORD> m_v;
char m_chr[100];
};
void TestUserDefclass()
{
printf("test UserDefclass:\r\n");
printf("====================================\r\n");
printf("test User Def Class Foo with tags and no tags \r\n");
printf("---------------------------------\r\n");
Foo foo;
std::vector<DWORD> v;
v.push_back(1);
v.push_back(2);
char strs[20];
strcpy(strs, "jack");
foo.Init("hello", v, 3, strs);
TestSerialize(foo, true);
TestSerialize(foo, false);
}
void testSTL
{
printf("test std::set with tags and no tags \r\n");
printf("---------------------------------\r\n");
std::set<DWORD> st;
st.insert(9);
st.insert(11);
st.insert(10);
TestSerialize(st, true);
TestSerialize(st, false);
printf("\r\n");
}
ObjectStream 可以讀和寫任何類型的對象到StreamBuffer(StreamBuffer里面有個char[],二進制序列存這里)中去,這都歸功于模版啊,
恩,也是,最重要的是要找到重大的性能的瓶頸在哪里,也許我想太多了。
明天我再從群里找幾個人來幫我看看這個帖,希望能多些經驗方面的交流
這兩天,我把序列化的庫重新寫了一遍,已經能支持基本數據類型,字符串和Stl的list vector set map 等等了...方便是方便,但想想,還是存在上面的問題,最終接收成對象的時候,少不了分配內存(創建對象) -> 數據拷貝->處理對象->釋放對象 (我想這是個可能會成為將來的一個性能瓶頸),對此我真的是耿耿于懷
如果直接去處理語意的內存塊,真的又很是麻煩。
用序列化的語義,內部直接傳內存塊?
是不是:接收數據的時候,省略掉重新組裝成對象這一步?這也是個思路
效率是高了,只是缺少了最終接收的對象,以字節的方式處理字節塊的語意的邏輯就不很直觀了,也是件繁瑣的事情。
游戲里面發送的數據,并不需要包含指針和虛函數的vtable,所以我才敢這樣去做,僅僅是針對游戲數據通訊的需求而已。
不過這幾天,再三思量,看來不能圖一時痛快,太早這樣斷言
我的做法,前人應該也用過,目前可能夠用,將來也未必如此。
為了擴展性和高質量的代碼,我現在試試用自定義序列化的方式來做。
這兩天認真閱讀了看了前人寫的一些服務器端的代碼,感受頗多
只是覺得最大的問題是為了要提高效率還得要盡可能減少內存拷貝和動態內存的創建和銷毀這也是個挑戰。如果搞的不好,看似強大的序列化過程在負載嚴重的服務器上就沒效率可言了。
我之前面的做法雖然不優雅,但讀數據就是對象,少了解碼就不存在內存拷貝的過程了,也無需額外再去創建一個對象,效率方面應該是很高的
所以...,還是想再好好想想一個折中的方案,結合各自的優點。
我得要把標題改一下才好。
網絡數據,以結構體的方式直接傳送還是轉化成特定字節順序形式來傳送。
我當然知道傳統方式是采用樓上的方法。我只是想打破常規。
我的出發點是想自定義一個容器,相當于一個SmartStruct能裝下各種各樣的東西,其實這本身就相當于在序列化了,只是和復雜的序列化過程還是有些的差異而已
說來說去就是數據的字節組織形式是按原樣收發,還是變個特定順序收發
真不好意思,這篇文章確實是有些小小的混淆概念。
至于eXile 所說,數據驗證這些目前還沒有去考慮
object->(按一定順序拆分)write->bytes->(按拆分順序組裝)read->object
這才是序列化,和結構體直接發送的區別你都不知道?
樓上的正如你的名字“小白”一個
CollectionPack collection;
StringPack p1;
strcpy(p.buffer, "jack");
StringPack p2;
strcpy(p2.buffer, "cat");
collection.appendPack(p1);
collection.appendPack(p2);
socket.send(collection, collection.size());
遍歷collection代碼大致如下:
void visttest(CollectionPack * c)
{
BasePack *p = 0;
while(c->next(&p))
{
if(p->type==ROLESIMPLEINFO_PACK)
{
RoleInfoSimplePack * aaa = (RoleInfoSimplePack *) p;
printf("%s \r\n", aaa->username);
} else if(p->type==LOGIN_PACK)
{
LoginPack * aaa = (LoginPack *) p;
printf("%s \r\n", aaa->username);
} else if(p->type == STRING_PACK)
{
StringPack *aaa = (StringPack *) p;
printf("%s \r\n", aaa->buffer);
}
else if(p->type == COLLECTION_PACK) //帶嵌套的COLLECTION_PACK
{
CollectionPack *t=(CollectionPack *)p;
visttest(t); //遞歸方式
// return;
}
}
}
只要封裝的好,怎么會是BUG的溫床呢?
我的結構體測試過了,作為容器,內部嵌套子容器,再套N個變長結構體或子結構體都沒問題,比序列化方便多了。
因為有了這個實現,我才有這個底氣寫這篇文的,要不然我早用傳統方式了
re: 今天完成了下線通知功能 李侃 2008-01-26 22:13
這一塊其實也是比較復雜的,我的設計并非傳奇那樣的關卡式,我的目標是做成超大的地形,整個外景一張大地圖就ok的,我的客戶端已經實現了,就是服務器端怎么與客戶端結合的問題了,還有很多AI的問題,以及很多效率方面的因素要考慮,實現起來還是相當復雜的。不過實現的方法大致都想到了,并非NxN的通訊方式,能優化的余地還是很大的,這一切都來源于客戶端的無限地形的實現。
移動數據的通訊估計要做到完善,估計要到年后了。唉。。。
還有一些AI的東西,想想也頭大。