回 帖 发 新 帖 刷新版面

主题:为什么是在20毫秒以后才能进行下一次的处理,不是小于20毫秒就直接返回了吗?

将一个物体沿Z轴进行移动,了为防止物体移动过快或过慢,程序中使用了定时控制,这样在不同的机器上可以得到大差不多的移动速度.
使用一个全局变量来保存WINDOWS启动后毫秒数,在进入渲染处理函数时,调用GetTickCount()函数获取当前的毫秒数。在进行转换和渲染操作以后,使用一个循环进行等待。
while((GetTickCount() - g_Time) < 20)

    return;
即在20毫秒以后才能进行下一次的处理。通过这种定时方式,可以保证程序每一帧的处理时间在任何机器上都相同,除非机器慢得处理一次转换和渲染的时间大于等待的时间。

为什么是在20毫秒以后才能进行下一次的处理,不是小于20毫秒就直接返回了吗?
VOID RenderSene()
{
    //检查Direct3D设备是否已经创建,如果没有创建就直接返回
    if( NULL == g_pMyd3dDevice )
        return;

    g_Time = GetTickCount();    
    
    MoveAndRota();
    
    // 清除Direct3D设备的后台表面,背景颜色为蓝色RGB(0,0,255)
    g_pMyd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(255,255,255), 1.0f, 0 );
    
    // 打开3D渲染开关
    g_pMyd3dDevice->BeginScene();
    
    // 设置待渲染顶点的来源
    g_pMyd3dDevice->SetStreamSource( 0, g_pMyVxBuffer, sizeof(CUSTOMVERTEX) );

    // 进行3D渲染,在这里设置渲染的顶点格式,并调用DrawPrimitive完成顶点渲染处理
    g_pMyd3dDevice->SetVertexShader( D3DFVF_CUSTOMVERTEX );

    g_pMyd3dDevice->DrawPrimitive( D3DPT_TRIANGLESTRIP, 0, 2 );
    // 关闭3D渲染开关
    g_pMyd3dDevice->EndScene();
    
    // 展现后台表面到窗口的客户区域中,即把渲染后的图像显示到窗口中
    g_pMyd3dDevice->Present( NULL, NULL, NULL, NULL );
    
    while((GetTickCount() - g_Time) < 20)

    return;
}

int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{
    // 定义一个Windows类,指定消息的处理函数为MsgProc
    WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, MsgProc, 0L, 0L, 
                      GetModuleHandle(NULL), NULL, NULL, NULL, NULL,
                      MY_WINCLASS_NAME, NULL };
    // 注册这个窗口类
    RegisterClassEx( &wc );

    // 创建应用程序窗口
    HWND hWnd = CreateWindow( MY_WINCLASS_NAME, "3D游戏编程——世界坐标系转换", 
                              WS_OVERLAPPEDWINDOW, 100, 100, 400, 300,
                              GetDesktopWindow(), NULL, wc.hInstance, NULL );

    // 调用InitMy3D函数进行3D对象和设备的创建,传入参数为窗口的句柄hWnd
    if( SUCCEEDED( InitMy3D( hWnd ) ) )
    { 
        // 显示窗口
        ShowWindow( hWnd, SW_SHOWDEFAULT );
        UpdateWindow( hWnd );

        // 程序主循环,进行窗口消息的分发,消息的处理在MsgProc函数中完成
        MSG msg; 
        while( msg.message != WM_QUIT)
        {
            if(PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ))
            {
                TranslateMessage( &msg );
                DispatchMessage( &msg );
            }
            else
            {
                RenderSene();
            }
        }
    }

    // 调用Cleanup函数清除3D对象和设备
    Cleanup();

    // 注销窗口类
    UnregisterClass( MY_WINCLASS_NAME, wc.hInstance );
    return 0;
}

回复列表 (共7个回复)

沙发

看不懂你想表达什么意思,你是想让一个物体以一定的速度变化吧?这样你可以改变世界矩阵,而不是在渲染时加入时间的判断。

而且你这个:

while((GetTickCount() - g_Time) < 20)

    return;

有什么用呢?不管是小于20MS还是大于,一样要返回呀,程序的思路太怪了,说明楼主的基础还要加强

板凳

两个地方要结合起来理解。
[code=c]g_Time = GetTickCount();
/* 渲染操作 */
while((GetTickCount() - g_Time) < 20)
    return;[/code]

这段代码利用CPU来进行延时。
假设渲染操作消耗的时间为X毫秒,则第一次到while循环的时候,GetTickCount()函数返回的值大约就是g_Time + X。因此GetTickCount() - g_Time的值就是X了。进一步的说,[color=00FF00]每次计算GetTickCount() - g_Time,所得到的值就是从g_Time = GetTickCount()这句开始到现在所经历的总时间[/color]。
while循环的意思就很明显了,如果经历的时间不足20毫秒,则一直循环,直到经历的时间达到20毫秒为止。所以,调用一次RenderSene函数至少会花费20毫秒的时间,这样可以限制帧速至多为1000/20 = 50。

延时有多种方法,CPU延时是最通用的一种,也是效果最差的一种。因为在等待的时候CPU无法响应其它的消息,并且CPU使用率会很高。换一个函数Sleep(20 + g_Time - GetTickCount())可以降低CPU使用率,但是精确程度会下降。
通常可以使用定时器来实现延时。简单的定时器可以用SetTimer函数,精确到秒。要求高精度时用多媒体定时器,timeSetEvent函数,精确到1毫秒(或者几毫秒,视硬件配置而定)。唯一需要注意的是多媒体定时器是在另外的线程中运行的。

3 楼

while((GetTickCount() - g_Time) < 20)
    return;

经历的时间不足20MS时,还是return啊!

应该改成
while((GetTickCount() - g_Time) < 20)
{ }
return;

吧.

而且这种方法,绝对是蠢方法,把FPS的控制加到渲染函数中,以后加入逻辑判断后又该怎么办呢?要想控制速度,可以仅对方块的位置的更新使用这个,而不是整个渲染都用.总之,这种方法实在是..........

4 楼

啊,是我弄错了。这个地方确实有问题。

不知道小小C用什么来限制帧速呢?比如要限制FPS为60,如果以毫秒为单位,则每两帧之间间隔时间为1000/60 = 16.666...,处理起来不方便。(虽然可以按照16, 17, 17这样的间隔时间来进行处理)

5 楼

我用DXUT库里的FPS控制

6 楼

我晕死,C++版的版主,连这会看错.游戏开发版的版主,是不是对这种问题不屑一顾啊?还是根本什么也不知道呢?

7 楼

呵呵,见笑了。

我来回复

您尚未登录,请登录后再回复。点此登录或注册