能力值:
( LV3,RANK:20 )
|
-
-
2 楼
我个人认为,在砍出时,客户端已执行了显示特效,扣血计算时才会到服务器计算,然后发送给客户端
|
能力值:
( LV2,RANK:10 )
|
-
-
3 楼
主要是怎么显示多人的问题,在绘制函数中去绘制每个人的状态?
|
能力值:
( LV2,RANK:10 )
|
-
-
4 楼
你没考虑电脑执行速度
可以自己搞3个程序来测试一下 一个服务器接受 2个客户端消息
然后 2个客户端处理 并且画图 不需要画2个人的 只需要2个窗口 接受到消息开始画就行了 应该能看出点东西
|
能力值:
( LV3,RANK:20 )
|
-
-
5 楼
如果客户端更新了其它用户的状态,哪么就很可能从服务器拿到了状态数据。(还有一种情况可能不需要,哪就是被攻击者的状态,可能就在本机计算直接得出结果),扣血时才会在服务器上校验
|
能力值:
( LV2,RANK:10 )
|
-
-
6 楼
我知道绘制很快,
但是假设砍这个事件有7个图片,而渲染函数只有1个,也就是说在接到砍得消息的时候就一次性把7刀渲染完?那么传奇怎么做到A显示砍的第一张图片,B却显示到了第三张?
|
能力值:
( LV2,RANK:10 )
|
-
-
7 楼
不知道我理解的是不是对的。
像是GDI(+)或者DirectDraw里,都是有后备页(双缓冲)的,绘制的时候在后备页里绘制,刷新的时候GDI好像需要编程将后备页绘制到前台页面,DirectDraw是交换指针(将N个后备页中的要显示页和前台页指针交换),这个动作都是很快的。
而绘制后备页的动作都是由程序控制了。比如AB互砍这个动作,在编程的时候就应该设计好绘图框架(或者使用游戏引擎),对绘制的每一帧都进行控制,即:绘制当前帧时,查询一下有多少个绘制对象(地图、场景效果、人物etc),检查各个对象当前状态(如地图,如果人物没有走动,那么绘制过程就不需要全部绘制,仅仅更新一下人物所占的那一小块面积即可),然后根据整个检查结果安排绘制。
就AB互砍来说,
第一帧绘制A砍杀动作的第一帧,更新显示。
第二帧绘制A砍杀动作的第二帧,更新显示。
第三帧绘制A砍杀动作的第三帧,并且查询到B的状态改变了,绘制B砍杀动作的第一帧,更新显示。
...
所有绘制对象的状态改变等等并不与绘制同一线程。
服务器处理的仅仅是接收AB发出砍人动作的命令,计算伤害,返回给客户端。至于客户端怎么绘图,服务器不去处理的。客户端接收到服务器的返回,改变自己绘图对象的状态就可以了,然后绘图动作查询对象状态,根据对象状态绘制游戏画面。
|
能力值:
( LV2,RANK:10 )
|
-
-
8 楼
感觉你说的是对的。
谢谢哥们了
|
能力值:
( LV4,RANK:50 )
|
-
-
9 楼
游戏中有一个帧的概念,每帧之间有时间间隔。
void print()
{
list.getmessage();//这个是同步的
//异步发送到游戏的绘制线程
}
//每帧都会执行下面这个函数,一般游戏每秒都有60多帧
//1. 第一帧, A打B
// 2. 第二帧, A打B绘制到第二张图片
// 3. 第三帧, B打了A, 开始绘制B打A的第一张图标,并且绘制A打B的第三张图片
// 4. 第四帧
游戏的绘制线程
{
}
==
总结下:
1. 接受消息和界面绘制不是一个线程,是异步的
2. 界面绘制有一个帧的概念,每帧执行时候有时间间隔,所以第一帧时候需要绘制A打B,在第8帧时候要同时绘制A打B和B打A
|
能力值:
( LV2,RANK:10 )
|
-
-
10 楼
非常感谢。
|
能力值:
( LV2,RANK:10 )
|
-
-
11 楼
程序上,并没有侦这个概念的。
拿D3D的Win32游戏程序来说,绘图函数Vender()的位置一般在消息处理结束的地方,也就是下面这样:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
switch (message)
{
case WM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
// 分析菜单选择:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
// TODO: 在此添加任意绘图代码...
EndPaint(hWnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
///////////////////////////////////////////////////////////////////////////////////////////////
Vender();//没什么消息需要处理的话,那我就绘图吧
///////////////////////////////////////////////////////////////////////////////////////////////
return 0;
}
上面是Windows程序的消息处理过程,在处理过程的末尾,有一个绘图函数Vender(),这个绘图函数是要自己实现的。
也就是说,没有消息需要处理的话,我就绘图。这也就是为什么性能高的机器,FPS就高的原因。因为Vender()完成的速度在不同性能的机器上完成的时间是不一样的。配置低的机器一秒钟仅能完成几次(FPS只有几)。
程序里,可能会在Vender()保存一个自身被调用次数计数的变量,但是Vender()被调用多少次实际上程序多半是不怎么关心的,可能也是不可控的(如果可控,那么配置低的机器也能实现很高的FPS。当然,程序里人为的降低FPS还是没问题的,提高的话就很难)。
所以,你说的绘图是根据第几侦这个说法,其实是不正确的。
简单的说,
比如,第一侦绘制,
性能高的电脑:
仅需要0.001s就完成了Vender(),,然后没有新的消息到来,那么就绘制第二侦,这时所有的参数都还没有变化,所以Vender()绘制的第二侦跟第一侦完全一样。
性能低的电脑:
需要3秒钟才完成Vender(),然后前面已经有很多消息到来,并且已经处理了。非常多的参数都变化了,然后又调用Vender(),绘制第二侦,这个第二侦跟第一侦区别非常多了(游戏里面的卡屏,哈哈,第一侦刚扬起刀,画面停滞3秒钟之后,你被砍死了。实际上如果是你被砍死了,而不是一直在逻辑上砍杀的话,说明这个程序写的有问题,因为绘图阻塞了逻辑处理。更好的处理方法是分别用不同的后台线程去处理,或者逻辑运算交给服务器,绘图、表现交给客户机去实现,这样画面更流畅,对防作弊也有一定好处,当然,对网络环境要求也高了很多。像是剑网三,不只这些,剑网三的逻辑和绘图除了把逻辑运算放到服务器,本地绘图还有一个“呼吸”的概念,这样可以运行lua脚本,思想非常先进)。
可以看到,同样的程序,同样的第二侦,运行的内容却是不一样的。
上面的例子就说明了你的关于游戏绘图的理解是不正确的。
至于FPS统计,如果是游戏本身的统计,那么在绘图里搞个计数器就好了。如果是外挂式的,统计的地点是不在这里的。多半会统计某个显卡驱动或者系统的API什么的吧
|
|
|