(Visual C++)游戏开发笔记十八 游戏基础物理建模(一) 匀速与加速运动

来源:岁月联盟 编辑:exp 时间:2012-07-02

本节依旧先是基础知识的讲解,再附上一个demo供大家巩固提

一、基础知识讲解

1.匀速运动
通常情况下,一个会移动的物体都是具有“速度”的,这个速度我们可以进行正交分解,看做各个方向上“速度分量”的合成。

这里我们设一个物体的移动速度为V,x方向的速度分量为Vx,y方向上的速度分量为Vy.


匀速运动实际上就是Vx与Vy保持恒定不变。

在设计2D平面上物体的匀速运动时,每次画面更新时,利用物体速度分量Vx与Vy的值来计算下次物体出现的位置,产生物体移动的效果,这样的原理实现方式我们可以表示为:

下次X轴坐标=在X轴上的速度分量+当前X轴坐标

下次Y轴坐标=在Y轴上的速度分量+当前Y轴坐标

2.加速运动


加速运动就是具有加速度的运动,它的速度会随着时间而改变。

公式我们可以表示如下:

V=Vo+at


这是高中物理运动学里最基本的公式了~其中,V为当前速度,V0为初速度,a为加速度,t为物体从速度为V0时记起的时间

那么同样将此速度分解,我们得到:

Vx=Vxo+axt

Vy=Vyo+ayt

我们设时间间隔t=1

则我们可以推算出加入加速度之后,物体下一刻所在的位置:

Sx=Sxo+Vx*1

Sy=Syo+Vy*1

将这两个公式运用到我们的代码里面就可以实现加速运动的模拟了。


这些知识都是非常基础的,实现方式都非常的简单,但是还有颇多细节,希望好学的你能多思考,多挖掘。


二、在一个完整的demo中将知识融会贯通


了解了基本运动学的原理之后,下面我们就来一起看下这节笔记里面的demo,在实例中将本节知识融会贯通。


这节的demo是一个匀速运动,碰到窗口边缘时就进行反弹的“愤怒的小鸟”,非常的可爱。

浅墨感觉学完这节后大家就可以自己实现win7里的那个”多彩气泡“的屏幕保护程序,有兴趣的朋友可以试着写写看,调用一些Windows API函数就来了。

好了,我们依旧贴出详细注释的源代码~

[cpp]
#include "stdafx.h"  
#include <stdio.h>  
 
 
//全局变量声明  
HINSTANCE hInst; 
HBITMAP   bg,bird; 
HDC       hdc,mdc,bufdc; 
HWND      hWnd; 
DWORD     tPre,tNow,tCheck; 
RECT      rect;             //定义一个RECT结构体,用于储存内部窗口区域的坐标  
int       x=50,y=50,vx=15,vy=15;   //x与y是小鸟在窗口中的贴图坐标,vx与vy为小鸟在x与y轴运动的速度分量  
 
//全局函数声明  
ATOM                MyRegisterClass(HINSTANCE hInstance); 
BOOL                InitInstance(HINSTANCE, int); 
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM); 
void                MyPaint(HDC hdc); 
 
//****WinMain函数,程序入口点函数**************************************    
int APIENTRY WinMain(HINSTANCE hInstance, 
                     HINSTANCE hPrevInstance, 
                     LPSTR     lpCmdLine, 
                     int       nCmdShow) 

    MSG msg; 
 
    MyRegisterClass(hInstance); 
 
    //初始化  
    if (!InitInstance (hInstance, nCmdShow))  
    { 
        return FALSE; 
    } 
 
    //消息循环  
    GetMessage(&msg,NULL,NULL,NULL);            //初始化msg        
    while( msg.message!=WM_QUIT ) 
    { 
        if( PeekMessage( &msg, NULL, 0,0 ,PM_REMOVE) ) 
        { 
            TranslateMessage( &msg ); 
            DispatchMessage( &msg ); 
        } 
        else 
        { 
            tNow = GetTickCount(); 
            if(tNow-tPre >= 40) 
                MyPaint(hdc); 
        } 
    } 
 
    return msg.wParam; 

 
//****设计一个窗口类,类似填空题,使用窗口结构体*********************    
ATOM MyRegisterClass(HINSTANCE hInstance) 

    WNDCLASSEX wcex; 
 
    wcex.cbSize = sizeof(WNDCLASSEX);  
    wcex.style          = CS_HREDRAW | CS_VREDRAW; 
    wcex.lpfnWndProc    = (WNDPROC)WndProc; 
    wcex.cbClsExtra     = 0; 
    wcex.cbWndExtra     = 0; 
    wcex.hInstance      = hInstance; 
    wcex.hIcon          = NULL; 
    wcex.hCursor        = NULL; 
    wcex.hCursor        = LoadCursor(NULL, IDC_ARROW); 
    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1); 
    wcex.lpszMenuName   = NULL; 
    wcex.lpszClassName  = "canvas"; 
    wcex.hIconSm        = NULL; 
 
    return RegisterClassEx(&wcex); 

 
//****初始化函数*************************************  
// 加载位图资源并取得内部窗口区域信息  
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) 

    HBITMAP bmp; 
    hInst = hInstance; 
 
    hWnd = CreateWindow("canvas", "浅墨的窗口" , WS_OVERLAPPEDWINDOW, 
        CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL); 
 
    if (!hWnd) 
    { 
        return FALSE; 
    } 
 
    MoveWindow(hWnd,10,10,600,450,true); 
    ShowWindow(hWnd, nCmdShow); 
    UpdateWindow(hWnd); 
 
    hdc = GetDC(hWnd); 
    mdc = CreateCompatibleDC(hdc); 
    bufdc = CreateCompatibleDC(hdc); 
    bmp = CreateCompatibleBitmap(hdc,640,480); 
 
    SelectObject(mdc,bmp); 
 
    bg = (HBITMAP)LoadImage(NULL,"bg.bmp",IMAGE_BITMAP,640,480,LR_LOADFROMFILE); 
    bird = (HBITMAP)LoadImage(NULL,"angrybird.bmp",IMAGE_BITMAP,120,60,LR_LOADFROMFILE); 
     
    GetClientRect(hWnd,&rect);      //取得内部窗口区域的大小  
    MyPaint(hdc); 
 
    return TRUE; 

 
//****自定义绘图函数*********************************  
// 1.进行窗口贴图  
// 2.计算小鸟贴图坐标并判断小鸟是否碰到窗口边沿  
void MyPaint(HDC hdc) 

    SelectObject(bufdc,bg); 
    BitBlt(mdc,0,0,640,480,bufdc,0,0,SRCCOPY); 
 
    SelectObject(bufdc,bird); 
    BitBlt(mdc,x,y,60,60,bufdc,60,0,SRCAND); 
    BitBlt(mdc,x,y,60,60,bufdc,0,0,SRCPAINT); 
     
 
    BitBlt(hdc,0,0,640,480,mdc,0,0,SRCCOPY); 
 
    //计算X轴贴图坐标与速度  
    x += vx; 
    if(x <= 0) 
    { 
        x = 0; 
        vx = -vx; 
    } 
    else if(x >= rect.right-60) 
    { 
        x = rect.right - 60; 
        vx = -vx; 
    } 
 
    //计算Y轴贴图坐标与速度  
    y += vy;         
    if(y<=0) 
    { 
        y = 0; 
        vy = -vy; 
    } 
    else if(y >= rect.bottom-60) 
    { 
        y = rect.bottom - 60; 
        vy = -vy; 
    } 
 
    tPre = GetTickCount();     //记录此次绘图时间  

 
////****消息处理函数***********************************    
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) 

    switch (message) 
    { 
        case WM_KEYDOWN:                    //按键消息  
            if(wParam==VK_ESCAPE)           //按下【Esc】键  
                PostQuitMessage(0); 
            break; 
        case WM_DESTROY:                    //窗口结束消息    
            DeleteDC(mdc); 
            DeleteDC(bufdc); 
            DeleteObject(bg); 
            DeleteObject(bird); 
            ReleaseDC(hWnd,hdc); 
            PostQuitMessage(0); 
            break; 
        default:                            //其他消息  
            return DefWindowProc(hWnd, message, wParam, lParam); 
   } 
   return 0; 

#include "stdafx.h"
#include <stdio.h>


//全局变量声明
HINSTANCE hInst;
HBITMAP   bg,bird;
HDC    hdc,mdc,bufdc;
HWND   hWnd;
DWORD   tPre,tNow,tCheck;
RECT   rect;    //定义一个RECT结构体,用于储存内部窗口区域的坐标
int    x=50,y=50,vx=15,vy=15;   //x与y是小鸟在窗口中的贴图坐标,vx与vy为小鸟在x与y轴运动的速度分量

//全局函数声明
ATOM    MyRegisterClass(HINSTANCE hInstance);
BOOL    InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
void    MyPaint(HDC hdc);

//****WinMain函数,程序入口点函数************************************** 
int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{
 MSG msg;

 MyRegisterClass(hInstance);

 //初始化
 if (!InitInstance (hInstance, nCmdShow))
 {
  return FALSE;
 }

 //消息循环
 GetMessage(&msg,NULL,NULL,NULL);            //初始化msg     
    while( msg.message!=WM_QUIT )
    {
        if( PeekMessage( &msg, NULL, 0,0 ,PM_REMOVE) )
        {
            TranslateMessage( &msg );
            DispatchMessage( &msg );
        }
  else
  {
   tNow = GetTickCount();
   if(tNow-tPre >= 40)
    MyPaint(hdc);
  }
    }

 return msg.wParam;
}

//****设计一个窗口类,类似填空题,使用窗口结构体********************* 
ATOM MyRegisterClass(HINSTANCE hInstance)
{
 WNDCLASSEX wcex;

 wcex.cbSize = sizeof(WNDCLASSEX);
 wcex.style   = CS_HREDRAW | CS_VREDRAW;
 wcex.lpfnWndProc = (WNDPROC)WndProc;
 wcex.cbClsExtra  = 0;
 wcex.cbWndExtra  = 0;
 wcex.hInstance  = hInstance;
 wcex.hIcon   = NULL;
 wcex.hCursor  = NULL;
 wcex.hCursor  = LoadCursor(NULL, IDC_ARROW);
 wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
 wcex.lpszMenuName = NULL;
 wcex.lpszClassName = "canvas";
 wcex.hIconSm  = NULL;

 return RegisterClassEx(&wcex);
}

//****初始化函数*************************************
// 加载位图资源并取得内部窗口区域信息
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
 HBITMAP bmp;
 hInst = hInstance;

 hWnd = CreateWindow("canvas", "浅墨的窗口" , WS_OVERLAPPEDWINDOW,
  CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

 if (!hWnd)
 {
  return FALSE;
 }

 MoveWindow(hWnd,10,10,600,450,true);
 ShowWindow(hWnd, nCmdShow);
 UpdateWindow(hWnd);

 hdc = GetDC(hWnd);
 mdc = CreateCompatibleDC(hdc);
 bufdc = CreateCompatibleDC(hdc);
 bmp = CreateCompatibleBitmap(hdc,640,480);

 SelectObject(mdc,bmp);

 bg = (HBITMAP)LoadImage(NULL,"bg.bmp",IMAGE_BITMAP,640,480,LR_LOADFROMFILE);
 bird = (HBITMAP)LoadImage(NULL,"angrybird.bmp",IMAGE_BITMAP,120,60,LR_LOADFROMFILE);
 
 GetClientRect(hWnd,&rect);  //取得内部窗口区域的大小
 MyPaint(hdc);

 return TRUE;
}

//****自定义绘图函数*********************************
// 1.进行窗口贴图
// 2.计算小鸟贴图坐标并判断小鸟是否碰到窗口边沿
void MyPaint(HDC hdc)
{
 SelectObject(bufdc,bg);
 BitBlt(mdc,0,0,640,480,bufdc,0,0,SRCCOPY);

 SelectObject(bufdc,bird);
 BitBlt(mdc,x,y,60,60,bufdc,60,0,SRCAND);
 BitBlt(mdc,x,y,60,60,bufdc,0,0,SRCPAINT);
 

 BitBlt(hdc,0,0,640,480,mdc,0,0,SRCCOPY);

 //计算X轴贴图坐标与速度
 x += vx;
 if(x <= 0)
 {
  x = 0;
  vx = -vx;
 }
 else if(x >= rect.right-60)
 {
  x = rect.right - 60;
  vx = -vx;
 }

 //计算Y轴贴图坐标与速度
 y += vy;  
 if(y<=0)
 {
  y = 0;
  vy = -vy;
 }
 else if(y >= rect.bottom-60)
 {
  y = rect.bottom - 60;
  vy = -vy;
 }

 tPre = GetTickCount();     //记录此次绘图时间
}

////****消息处理函数*********************************** 
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
 switch (message)
 {
  case WM_KEYDOWN:     //按键消息
   if(wParam==VK_ESCAPE)   //按下【Esc】键
    PostQuitMessage(0);
   break;
  case WM_DESTROY:     //窗口结束消息 
   DeleteDC(mdc);
   DeleteDC(bufdc);
   DeleteObject(bg);
   DeleteObject(bird);
   ReleaseDC(hWnd,hdc);
   PostQuitMessage(0);
   break;
  default:       //其他消息
   return DefWindowProc(hWnd, message, wParam, lParam);
   }
   return 0;
}

运行时会带有幻影的错觉,实际上是因为这样的动画实现方式比较简单。

毕竟画面不是我们目前所追求的东西,目前我们主要学的是思想,关于华丽的游戏画面,这将是我们在后面的DirectX与游戏引擎中才需要讲究的东西。


下面是运行的截图:

/

 

/

 

/作者:zhmxy555