看来楼上是行家,一眼就识破了,的确用了两层纹理,一共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的东西,想想也头大。