• 游戏编程笔记--(一)游戏编程基础


    一 游戏编程基础


    1-概论


    1.游戏的组成

    游戏由剧情、图形图像、声音、文本等资源组成。


    2.游戏设计与制作

    设计与制作过程大致分为策划,美工,音效,程序,测试五部分。

    策划:负责设计游戏的剧情、类别、玩法等,是游戏最重要的部分,直接决定了游戏的成功与否。

    美工:负责绘制游戏中所需图形图像资源。

    音效:负责制作游戏中所需的声音资源。

    程序:负责将多媒体资源按照策划规定的方式组合起来,制作成最终产品-游戏。

    测试:负责测试程序的稳定性、游戏的难度等。

    我之前看过一本书,书中有这么一个比喻:如果拿游戏与人来类比的话,策划就是心脏,程序是骨骼,美工是皮肤,音效是衣服。

    游戏编程,就是游戏设计与制作的程序部分。在我的整个笔记中,所探讨的核心内容,就是游戏的程序实现。



    2-游戏程序组成


    1.组成

    主要由逻辑更新和画面渲染两部分组成,也可以说游戏程序就只干这两件事情。

    逻辑更新:接收玩家的输入,更新敌人、玩家、世界等数据。

    画面渲染:将游戏内容以图像的方式呈现出来。


    2.程序流程 

    初始化数据-更新-渲染-释放资源。




    3-Windows程序设计基础

            我所使用到的技术都是基于windows操作系统的,在2D游戏编程方面,使用GDI(图像开发接口)来处理图形图像,虽然GDI的执行效率较低,但是相对于其他的开发包来说,它比较容易学习和理解,在我们学习阶段使用它没问题。当我们对游戏编程思想有了更深的了解的时候,可以使用其他开发包来处理图形图像,如DirectX 3D,OpenGL等。


    1.程序入口WinMain

    一个简单的windows程序。

    #include <Windows.h>
    int WINAPI WinMain(HINSTANCE hInstance,
                       HINSTANCE hPrevInstance, 
                       LPSTR lpCmdLine,
                       int nShowCmd )
    {
        MessageBox(NULL,
            L"这是一个简单的windows应用程序!",
            L"这是标题",
            MB_OKCANCEL|MB_ICONINFORMATION);
        return 0;
    }


    WinMain和C语言的main函数类似,都是程序的入口函数,由系统调用。

    int WINAPI WinMain(
      HINSTANCE hInstance,     // handle to current instance
      HINSTANCE hPrevInstance, // handle to previous instance
      LPSTR lpCmdLine,         // command line
      int nCmdShow              // show state
    );


    详细参数可以参看MSDN或去百科上看,写程序时,保持这个结构不变即可。最重要的参数是hInstance,为应用程序实例句柄,标识了当前应用程序的资源地址。在游戏编程中,经常会用到它,所以,我们经常会将该值保存起来,方便后面使用。


    2.创建windows应用程序的流程

    主函数(WinMain)->注册窗口类(RegisterClassEx)->创建窗口(CreateWindowEx) ->消息循环(MainLoop),处理窗口过程(WinProc)。其中,窗口过程在消息循环中被反复调用。以下是算法伪代码:

    WinMain()
    {
        RegisterClassEx()
        CreateWindowEx()
        MainLoop()
    }
    MainLoop()
    {
        while(true)
        {
            WinProc()
        }
    }
    


    3.注册窗口类RegisterClassEx

    窗口类,即窗口的类型,它并不是指C++中的类(class)。告诉操作系统即将创建什么样的窗口。

    ATOM RegisterClassEx(
      CONST WNDCLASSEX *lpwcx // class data
    );
    typedef struct _WNDCLASSEX { //窗口类数据结构
        UINT       	 cbSize; 	 //本结构大小
        UINT       	 style; 	 //窗口类的样式
        WNDPROC    	lpfnWndProc; 	 //窗口过程函数指针
        int        	 cbClsExtra; 	 //附加参数
        int        	 cbWndExtra; 	 //附加参数
        HINSTANCE  	hInstance; 	 //应用程序实例句柄
        HICON      	hIcon; 	 //窗口图标
        HCURSOR    	hCursor; 	 //窗口光标
        HBRUSH     	hbrBackground; 	//背景画刷
        LPCTSTR    	lpszMenuName;	 //菜单名称
        LPCTSTR    	lpszClassName; 	//窗口类名称
        HICON      	hIconSm;	  //窗口小图标
    } WNDCLASSEX, *PWNDCLASSEX;



    4.创建窗口CreateWindowEx

    HWND CreateWindowEx(
      DWORD dwExStyle,     // extended window style扩展窗口样式
      LPCTSTR lpClassName, // registered class name已注册的窗口类名称
      LPCTSTR lpWindowName,// window name窗口标题
      DWORD dwStyle,       // window style窗口样式
      int x,               // horizontal position of window坐标x
      int y,               // vertical position of window坐标y
      int nWidth,          // window width窗口宽度
      int nHeight,         // window height高度
      HWND hWndParent,     // handle to parent or owner window父窗口
      HMENU hMenu,         // menu handle or child identifier菜单句柄
      HINSTANCE hInstance, // handle to application instance实例句柄
      LPVOID lpParam        // window-creation data附加参数
    );


    返回值是一个窗口句柄,句柄好比就是一个地址,标识了这个窗口的资源位置。


    5.消息循环MainLoop

    Windows程序都是基于消息机制的,所有的通信都是经过消息传递实现。

    1) 消息MSG:

    typedef struct tagMSG {
        HWND   hwnd; 	 //窗口句柄
        UINT   message; 	//消息
        WPARAM wParam; 	//参数
        LPARAM lParam; 	//附加参数
        DWORD  time; 	 //消息产生时间
        POINT  pt; 	 //消息产生时的鼠标坐标
    } MSG, *PMSG;


    2) 获取消息,GetMessage与PeekMessage:

    BOOL GetMessage(
      LPMSG lpMsg,        // message information
      HWND hWnd,          // handle to window
      UINT wMsgFilterMin, // first message
      UINT wMsgFilterMax   // last message
    );
    BOOL PeekMessage(
      LPMSG lpMsg,        // message information
      HWND hWnd,          // handle to window
      UINT wMsgFilterMin, // first message
      UINT wMsgFilterMax, // last message
      UINT wRemoveMsg      // removal options
    );


    两者都是从消息队列中取消息,不同的是当消息队列为空时,两者的处理方式不一样,前者是等待,后者是继续执行。由于我们的游戏需要不断的更新和重绘,而不能等待,所以我们要选择后者。

    3)翻译消息TranslateMessage

    将消息翻译成可处理的格式。

    BOOL TranslateMessage(
      CONST MSG *lpMsg   // message information
    );


    4)转发消息DispatchMessage

    将消息转发给窗口过程。

    LRESULT DispatchMessage(
      CONST MSG *lpmsg   // message information
    );



    6.窗口过程WinProc

    用户处理消息的函数,该函数在消息循环中被系统函数所调用。该函数的结构必须与下面这个函数类型相同!名称可以不同。

    LRESULT CALLBACK WindowProc(
      HWND hwnd,     // handle to window
      UINT uMsg,     // message identifier
      WPARAM wParam, // first message parameter
      LPARAM lParam   // second message parameter
    );



    7.例:创建窗口

    #include <Windows.h>
    //窗口过程
    LRESULT CALLBACK WndProc(
                             HWND hwnd,      // handle to window
                             UINT uMsg,      // message identifier
                             WPARAM wParam,  // first message parameter
                             LPARAM lParam   // second message parameter
                             )
    {
        switch(uMsg)
        {
        case WM_DESTROY://窗口销毁消息。按下窗口的叉时会产生。
            PostQuitMessage(0);//发送退出程序消息WM_QUIT。
            break;
        case WM_LBUTTONDOWN:
            MessageBox(hwnd,L"正处理鼠标左键单击消息",L"这是标题",MB_OK);
            break;
        default:
            return DefWindowProc(hwnd,uMsg,wParam,lParam);//调用默认窗口过程
        }
        return 0;
    }
    //主函数
    int WINAPI WinMain(
                       HINSTANCE hInstance,      // handle to current instance
                       HINSTANCE hPrevInstance,  // handle to previous instance
                       LPSTR lpCmdLine,          // command line
                       int nCmdShow              // show state
                       )
    {
        //MessageBox(NULL,L"这是消息框",L"这是标题",MB_OKCANCEL);
        WNDCLASSEX wcx;//窗口类
        memset(&wcx,0,sizeof(WNDCLASSEX));
        wcx.cbSize = sizeof(WNDCLASSEX);//窗口类大小
        wcx.style = CS_CLASSDC;//窗口类风格
        wcx.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);//获得系统画刷(白色)
        wcx.hCursor = LoadCursor(NULL,IDC_HAND);//加载系统光标
        wcx.hIconSm = wcx.hIcon = LoadIcon(NULL,IDI_APPLICATION);//加载系统图标
    wcx.hInstance = hInstance;//应用程序实例句柄
    /*字符串前面‘L’的意思是,该字符串为Unicode编码格式,
    不是默认的ASCII格式。如果要改成ASII格式,可以修改项目属性。*/
        wcx.lpszClassName = L"WndClass";//窗口类名称
        wcx.lpfnWndProc = (WNDPROC)WndProc;//窗口过程
        //注册窗口类
        RegisterClassEx( &wcx  );
        //窗口过程
    HWND hWnd = CreateWindowEx(0,L"WndClass",L"这是窗口标题",
    WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,
            0,0,640,480,NULL,NULL,hInstance,NULL);
        //显示窗口
        ShowWindow(hWnd,SW_SHOWNORMAL);
        //更新窗口,即发送重绘消息
        UpdateWindow(hWnd);
        MSG message;//消息结构
        while(true)
        {
            //从消息队列中取消息
            if(PeekMessage(&message,NULL,0,0,PM_REMOVE))
            {
                if (message.message == WM_QUIT)//跳出循环,退出程序。
                {
                    break;
                }
                //翻译消息
                TranslateMessage(&message);
                //发送给窗口过程
                DispatchMessage(&message);
            }
            Sleep(1);//暂停ms,即放弃CPU时间片ms,避免浪费CPU。
    }
    return 0;
    }


    8.使用VS2008-VC9创建Win32应用程序

    菜单:文件->新建->项目,在弹出的对话框中选Win32项目,截图如下:

    输入名称,选择好路径后,点确定,然后再点下一步,来到如下步骤,



    点选windows应用程序,勾选空项目,然后完成。这样一个空的Win32应用程序框架就建好了,接着给框架添加C++源文件(cpp文件)。写代码,调试,运行,OK。


            如果使用的是VC6.0,操作方法跟这个也很类似,但是VC6中的默认编码格式是ASCII,所以字符串前面不需要加‘L’。

            注意,不管是VS还是VC6,都要建立win32应用程序项目,不要创建控制台应用程序!否则无法通过链接。


            修改编码:在解决方案上点右键->属性,在弹出来的对话框中,将字符集改为“使用多字节字符集”,之后程序的编码将会变成ASCII格式。如果是初学者的话,建议将字符集改为“使用多字节字符集”。


    9.小节

            创建窗口的过程比较麻烦,但是大部分代码是固定不变的。所以,我们可以将不变的部分封装起来,今后使用到的时候,直接拿过来使用,而不必再写一遍,可以达到一劳永逸的效果!

            WindowsAPI比较多,对于这些API,我们只用知道它的用法就可以了,具体的参数我们可以去MSDN(微软软件开发文档)查,所以建议大家一定要装个MSDN不管哪个版本,都必须要有一个。

            刚开始学习的时候,新概念会有很多,如果不能理解的话,可以先忽略它,把它当一个黑盒子使用,用的时间长了,就可以理解了。

  • 相关阅读:
    动态规划例题
    c++ 进制转换函数
    约瑟夫问题
    set的基本使用
    stl中的二分查找
    1.生成的接口返回参数不包括系统自带的参数
    查看被锁定的表,并解锁
    添加、修改表中的字段
    NPOI简单示例2—合并表头
    NPOI简单示例
  • 原文地址:https://www.cnblogs.com/ygxsk/p/7694018.html
Copyright © 2020-2023  润新知