HGE 引擎学习笔记 1——如何模拟物理碰撞

2022-03-24 14:22:30 浏览数 (1)

模拟物理碰撞要解决的几个问题:

  1. 怎样模拟速度的变化? 设置一个摩擦系数 friction(0<friction<1.0) 和响应用户按键之后的一个 X 坐标单时间片增量 dx,一个 Y 坐标单时间片增量 dy,每隔一个时间片 dx = friction; dy = friction; 只要参数设置得当,看起来就会觉得速度自然地减慢。因为我们所用的浮点数的数据类型的精度限制,物体在经过一定数量时间片后就会停下来。
  2. 怎样模拟碰撞? 每个时间片处理过程中,判断物体的边缘坐标加上 dx 与 dy 后有没有超过屏幕边缘,如果超过,则采取一定的策略重新设置物体坐标让其在正常范围内,如 X 轴超过,则对 dx 取反;如 Y 轴超过,则对 dy 取反。计算好坐标之后再进行绘图。
  3. 碰撞过程中的声音处理 这里涉及到音量,左右声道,播放速度。对音量和播放速度可以按照场景来设置,可以考虑根据 X,Y 坐标作为其中一个参数,左右声道比较合理的处理方案是根据窗口宽度和物体 X 坐标来决定左右声道的混合比例。

WINDOWS SDK 窗口对此过程的模拟(仅摹仿了速度和碰撞等,对声音的相关处理貌似比较复杂,还没搞清楚怎么写。由于是做个简单 DEMO,并没有加入多线程等技术,所以程序里的坐标等数据的同步并不精准,上、下、左、右键最好是短暂地点一下即松开,连着按的话会出现速度的突兀变化):

代码语言:javascript复制
/**
 * FILE : collision.cpp
 * 功能 : 模拟一个小球在一个封闭区域内的碰撞等活动
 * 作者 : Zhuang Ma( http://www.mazhuang.org )
 * 声明 : 版权没有 盗版不究
 */

#include <windows.h>

float x = 100.0f;    // 球的中心点 X 坐标
float y = 100.0f;    // 球的中心点 Y 坐标
float speed = 10.0f;    // 球响应按钮后的初始速度
float friction = 0.99f; // 球与地面的摩擦系数
float dx = 0.0f;    // X 轴增量
float dy = 0.0f;    // Y 轴增量

LRESULT CALLBACK MainWndProc(HWND, UINT, WPARAM, LPARAM);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int nCmdShow)
{
    WNDCLASS wc;
    MSG msg;
    HWND hWnd;

    if( !hPrevInstance )
    {
        wc.lpszClassName = "GenericAppClass";
        wc.lpfnWndProc = MainWndProc;
        wc.style = CS_OWNDC | CS_VREDRAW | CS_HREDRAW;
        wc.hInstance = hInstance;
        wc.hIcon = LoadIcon( NULL, IDI_APPLICATION );
        wc.hCursor = LoadCursor( NULL, IDC_ARROW );
        wc.hbrBackground = (HBRUSH)( COLOR_WINDOW 1 );
        wc.lpszMenuName = "GenericAppMenu";
        wc.cbClsExtra = 0;
        wc.cbWndExtra = 0;

        RegisterClass( &wc );
    }

    hWnd = CreateWindow( "GenericAppClass",
            "Happy Ball",
            WS_OVERLAPPEDWINDOW & (~WS_MAXIMIZEBOX) & (~WS_THICKFRAME),
            100,
            100,
            800,
            600,
            NULL,
            NULL,
            hInstance,
            NULL
            );

    ShowWindow( hWnd, nCmdShow );

    while( GetMessage( &msg, NULL, 0, 0 ) ) {
        TranslateMessage( &msg );
        DispatchMessage( &msg );
    }

    return msg.wParam;
}

LRESULT CALLBACK MainWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    static PAINTSTRUCT ps;
    static HDC hDC;
    static HBRUSH hBrush;
    static RECT rect;
    GetClientRect(hwnd, &rect);
    const int nRadius = 16;

    switch (message)
    {
        case WM_CREATE:
            SetTimer(hwnd, 1, 5, NULL);
            hBrush = CreateSolidBrush(RGB(0,0,0));
            return 0;

        case WM_PAINT:
            hDC = BeginPaint(hwnd, &ps);
            SelectObject(hDC, hBrush);
            Ellipse(hDC, x - nRadius, y - nRadius, x   nRadius, y   nRadius);
            EndPaint(hwnd, &ps);
            break;

        case WM_KEYDOWN:
            switch (wParam)
            {
                case VK_UP:
                    dy -= speed;
                    break;
                case VK_DOWN:
                    dy  = speed;
                    break;
                case VK_LEFT:
                    dx -= speed;
                    break;
                case VK_RIGHT:
                    dx  = speed;
                    break;
            }
            break;

        case WM_TIMER:
            dx *= friction;
            dy *= friction;
            x  = dx;
            y  = dy;
            if (x > rect.right - nRadius) { x = (rect.right - nRadius) - (x - (rect.right - nRadius)); dx = -dx; }
            if (x < nRadius) { x = nRadius   nRadius - x; dx = -dx; }
            if (y > rect.bottom - nRadius) { y = rect.bottom - nRadius - (y - (rect.bottom - nRadius)); dy = -dy; }
            if (y < nRadius) {y = nRadius   nRadius - y; dy = -dy; }
            InvalidateRect(hwnd, &rect, TRUE);

            break;

        case WM_DESTROY:
            KillTimer(hwnd, 1);
            DeleteObject(hBrush);
            PostQuitMessage(0);
            return 0;
    }
    return DefWindowProc(hwnd, message, wParam, lParam);
}

0 人点赞