上一篇博客,我们讲了SDL环境的配置和基本程序框架。这篇博客,我本打算讲一下Game类的代码,但突然想到很多API都没有介绍,所以这篇博客先来介绍一下SDL的基础知识和游戏运行框架,Game类的实现下篇博客再讲。
基本的SDL 类型、API和宏 SDL基本结构SDL提供了一些结构体或类型,用来保存游戏的相关信息。下面我们就来看一下常用的结构体:
SDL_Window
窗口结构体。它保存一个窗口的信息。这个结构体的具体实现并不重要,我们不会用到里面的成员,只会通过函数对它的指针进行操作。
SDL_Renderer
渲染器结构体。它保存一个渲染器的信息。这个结构体的具体实现并不重要,我们不会用到里面的成员,只会通过函数对它的指针进行操作。
SDL_Rect
描述一个矩形。x,y为左上角坐标,w,h为宽和高。
typedef struct SDL_Rect
{
int x, y;
int w, h;
} SDL_Rect;
SDL_Event
结构体定义:
typedef union SDL_Event
{
Uint32 type;
SDL_CommonEvent common;
SDL_WindowEvent window;
SDL_KeyboardEvent key;
SDL_TextEditingEvent edit;
SDL_TextInputEvent text;
SDL_MouseMotionEvent motion;
SDL_MouseButtonEvent button;
SDL_MouseWheelEvent wheel;
SDL_JoyAxisEvent jaxis;
SDL_JoyBallEvent jball;
SDL_JoyHatEvent jhat;
SDL_JoyButtonEvent jbutton;
SDL_JoyDeviceEvent jdevice;
SDL_ControllerAxisEvent caxis;
SDL_ControllerButtonEvent cbutton;
SDL_ControllerDeviceEvent cdevice;
SDL_QuitEvent quit;
SDL_UserEvent user;
SDL_SysWMEvent syswm;
SDL_TouchFingerEvent tfinger;
SDL_MultiGestureEvent mgesture;
SDL_DollarGestureEvent dgesture;
SDL_DropEvent drop;
Uint8 padding[56];
} SDL_Event;
这个共用体非常重要,它保存了一个消息的信息。其中type为消息类型,其它都是消息的一些信息,这里不作具体介绍。下面是type的取值:
typedef enum
{
SDL_FIRSTEVENT = 0,
SDL_QUIT = 0x100,
SDL_APP_TERMINATING,
SDL_APP_LOWMEMORY,
SDL_APP_WILLENTERBACKGROUND,
SDL_APP_DIDENTERBACKGROUND,
SDL_APP_WILLENTERFOREGROUND,
SDL_APP_DIDENTERFOREGROUND,
SDL_WINDOWEVENT = 0x200,
SDL_SYSWMEVENT,
SDL_KEYDOWN = 0x300,
SDL_KEYUP,
SDL_TEXTEDITING,
SDL_TEXTINPUT,
SDL_MOUSEMOTION = 0x400,
SDL_MOUSEBUTTONDOWN,
SDL_MOUSEBUTTONUP,
SDL_MOUSEWHEEL,
SDL_JOYAXISMOTION = 0x600,
SDL_JOYBALLMOTION,
SDL_JOYHATMOTION,
SDL_JOYBUTTONDOWN,
SDL_JOYBUTTONUP,
SDL_JOYDEVICEADDED,
SDL_JOYDEVICEREMOVED,
SDL_ConTROLLERAXISMOTION = 0x650,
SDL_CONTROLLERBUTTONDOWN,
SDL_CONTROLLERBUTTONUP,
SDL_CONTROLLERDEVICEADDED,
SDL_CONTROLLERDEVICEREMOVED,
SDL_CONTROLLERDEVICEREMAPPED,
SDL_FINGERDOWN = 0x700,
SDL_FINGERUP,
SDL_FINGERMOTION,
SDL_DOLLARGESTURE = 0x800,
SDL_DOLLARRECORD,
SDL_MULTIGESTURE,
SDL_CLIPBOARDUPDATE = 0x900,
SDL_DROPFILE = 0x1000,
SDL_RENDER_TARGETS_RESET = 0x2000,
SDL_USEREVENT = 0x8000,
SDL_LASTEVENT = 0xFFFF
} SDL_EventType;
SDL基本API
现在,我们需要了解一下SDL的一些常用函数的用法。
基本函数SDL_Init
extern DECLSPEC int SDLCALL SDL_Init(Uint32 flags);
作用:初始化SDL。
参数:flags指定初始化类型。它可以是以下值(如有多个用"|"连接):
#define SDL_INIT_TIMER 0x00000001
#define SDL_INIT_AUDIO 0x00000010
#define SDL_INIT_VIDEO 0x00000020
#define SDL_INIT_JOYSTICK 0x00000200
#define SDL_INIT_HAPTIC 0x00001000
#define SDL_INIT_GAMECONTROLLER 0x00002000
#define SDL_INIT_EVENTS 0x00004000
#define SDL_INIT_NOPARACHUTE 0x00100000
#define SDL_INIT_EVERYTHING (
SDL_INIT_TIMER | SDL_INIT_AUDIO | SDL_INIT_VIDEO | SDL_INIT_EVENTS |
SDL_INIT_JOYSTICK | SDL_INIT_HAPTIC | SDL_INIT_GAMEConTROLLER
)
返回值:成功返回0,失败返回非0。
SDL_GetTicks
extern DECLSPEC Uint32 SDLCALL SDL_GetTicks(void);
作用:获取自SDL_Init以来的毫秒数,如果超过49天,会从头开始。
参数:无。
返回值:自SDL_Init以来的毫秒数。
SDL_GetError
extern DECLSPEC const char *SDLCALL SDL_GetError(void);
作用:获取上一次的错误。
参数:无。
返回值:描述错误的字符串。
SDL_Quit
extern DECLSPEC void SDLCALL SDL_Quit(void);
作用:退出由SDL_Init加载的库。这个函数用在游戏退出时。
参数:无。
返回值:无。
SDL_CreateWindow
extern DECLSPEC SDL_Window * SDLCALL SDL_CreateWindow(const char *title,
int x, int y, int w,
int h, Uint32 flags);
作用:创建一个SDL游戏窗口。
参数:title为窗口标题,x,y为窗口相对于左上角的位置,w,h为窗口宽度和高度,flags为窗口风格,详见此处。
返回值:创建的窗口的指针。通过这个指针,可以对窗口进行相应操作。失败返回nullptr。
SDL_DestroyWindow
extern DECLSPEC void SDLCALL SDL_DestroyWindow(SDL_Window * window);
作用:销毁一个窗口。
参数:window为要销毁的窗口指针。
返回值:无。
SDL_CreateRenderer
extern DECLSPEC SDL_Renderer * SDLCALL SDL_CreateRenderer(SDL_Window * window,
int index, Uint32 flags);
作用:创建一个2D渲染器。
参数:window为要创建渲染器的窗口,index一般为-1,flags一般为0。详见此处。
返回值:创建的渲染器的指针。通过这个指针,可以进行绘图。失败返回nullptr。
SDL_SetRenderDrawColor
extern DECLSPEC int SDLCALL SDL_SetRenderDrawColor(SDL_Renderer * renderer,
Uint8 r, Uint8 g, Uint8 b,
Uint8 a);
作用:设置渲染器绘制颜色。
参数:renderer为要设置的渲染器,r,g,b,a为颜色的rgba分量。
返回值:成功返回0,失败返回非0。
SDL_RenderClear
extern DECLSPEC int SDLCALL SDL_RenderClear(SDL_Renderer * renderer);
作用:将渲染器清空为设置的颜色。
参数:renderer为要清空的渲染器。
返回值:成功返回0,失败返回非0。
SDL_RenderPresent
extern DECLSPEC void SDLCALL SDL_RenderPresent(SDL_Renderer * renderer);
作用:把绘制的元素显示到屏幕上。
参数:renderer为要刷新的渲染器。
返回值:无。
SDL_RenderFillRect
extern DECLSPEC int SDLCALL SDL_RenderFillRect(SDL_Renderer * renderer,
const SDL_Rect * rect);
作用:使用指定的颜色填充一个矩形。
参数:renderer为渲染器,rect指定矩形的坐标。
返回值:成功返回0,失败返回非0。
SDL_DestroyRenderer
extern DECLSPEC void SDLCALL SDL_DestroyRenderer(SDL_Renderer * renderer);
作用:销毁一个渲染器。
参数:renderer为要销毁的渲染器。
返回值:无。
SDL_PollEvent
extern DECLSPEC int SDLCALL SDL_PollEvent(SDL_Event * event);
作用:从消息队列中取出一个消息。
参数:event为消息结构体的指针,用来存储取出的消息。
返回值:如果有消息返回非0,否则返回0。
SDL_GetKeyboardState
extern DECLSPEC const Uint8 *SDLCALL SDL_GetKeyboardState(int *numkeys);
作用:获取键盘状态。
参数:numkey一般为nullptr。
返回值:一个数组,表示键盘状态,其中1为该键被按下,0为没被按下。
示例:
const Uint8* keyState = SDL_GetKeyboardState(NULL); if (keyState[SDL_SCANCODE_RIGHT])//右箭头被按下 mPlaneDir += 1; if (keyState[SDL_SCANCODE_LEFT])//左箭头被按下 mPlaneDir -= 1;
其中数组下标取值如下:
typedef enum
{
SDL_SCANCODE_UNKNOWN = 0,
SDL_SCANCODE_A = 4,
SDL_SCANCODE_B = 5,
SDL_SCANCODE_C = 6,
SDL_SCANCODE_D = 7,
SDL_SCANCODE_E = 8,
SDL_SCANCODE_F = 9,
SDL_SCANCODE_G = 10,
SDL_SCANCODE_H = 11,
SDL_SCANCODE_I = 12,
SDL_SCANCODE_J = 13,
SDL_SCANCODE_K = 14,
SDL_SCANCODE_L = 15,
SDL_SCANCODE_M = 16,
SDL_SCANCODE_N = 17,
SDL_SCANCODE_O = 18,
SDL_SCANCODE_P = 19,
SDL_SCANCODE_Q = 20,
SDL_SCANCODE_R = 21,
SDL_SCANCODE_S = 22,
SDL_SCANCODE_T = 23,
SDL_SCANCODE_U = 24,
SDL_SCANCODE_V = 25,
SDL_SCANCODE_W = 26,
SDL_SCANCODE_X = 27,
SDL_SCANCODE_Y = 28,
SDL_SCANCODE_Z = 29,
SDL_SCANCODE_1 = 30,
SDL_SCANCODE_2 = 31,
SDL_SCANCODE_3 = 32,
SDL_SCANCODE_4 = 33,
SDL_SCANCODE_5 = 34,
SDL_SCANCODE_6 = 35,
SDL_SCANCODE_7 = 36,
SDL_SCANCODE_8 = 37,
SDL_SCANCODE_9 = 38,
SDL_SCANCODE_0 = 39,
SDL_SCANCODE_RETURN = 40,
SDL_SCANCODE_ESCAPE = 41,
SDL_SCANCODE_BACKSPACE = 42,
SDL_SCANCODE_TAB = 43,
SDL_SCANCODE_SPACE = 44,
SDL_SCANCODE_MINUS = 45,
SDL_SCANCODE_EQUALS = 46,
SDL_SCANCODE_LEFTBRACKET = 47,
SDL_SCANCODE_RIGHTBRACKET = 48,
SDL_SCANCODE_BACKSLASH = 49,
SDL_SCANCODE_NonUSHASH = 50,
SDL_SCANCODE_SEMIcolon = 51,
SDL_SCANCODE_APOSTROPHE = 52,
SDL_SCANCODE_GRAVE = 53,
SDL_SCANCODE_COMMA = 54,
SDL_SCANCODE_PERIOD = 55,
SDL_SCANCODE_SLASH = 56,
SDL_SCANCODE_CAPSLOCK = 57,
SDL_SCANCODE_F1 = 58,
SDL_SCANCODE_F2 = 59,
SDL_SCANCODE_F3 = 60,
SDL_SCANCODE_F4 = 61,
SDL_SCANCODE_F5 = 62,
SDL_SCANCODE_F6 = 63,
SDL_SCANCODE_F7 = 64,
SDL_SCANCODE_F8 = 65,
SDL_SCANCODE_F9 = 66,
SDL_SCANCODE_F10 = 67,
SDL_SCANCODE_F11 = 68,
SDL_SCANCODE_F12 = 69,
SDL_SCANCODE_PRINTSCREEN = 70,
SDL_SCANCODE_SCROLLLOCK = 71,
SDL_SCANCODE_PAUSE = 72,
SDL_SCANCODE_INSERT = 73,
SDL_SCANCODE_HOME = 74,
SDL_SCANCODE_PAGEUP = 75,
SDL_SCANCODE_DELETE = 76,
SDL_SCANCODE_END = 77,
SDL_SCANCODE_PAGEDOWN = 78,
SDL_SCANCODE_RIGHT = 79,
SDL_SCANCODE_LEFT = 80,
SDL_SCANCODE_DOWN = 81,
SDL_SCANCODE_UP = 82,
SDL_SCANCODE_NUMLOCKCLEAR = 83,
SDL_SCANCODE_KP_DIVIDE = 84,
SDL_SCANCODE_KP_MULTIPLY = 85,
SDL_SCANCODE_KP_MINUS = 86,
SDL_SCANCODE_KP_PLUS = 87,
SDL_SCANCODE_KP_ENTER = 88,
SDL_SCANCODE_KP_1 = 89,
SDL_SCANCODE_KP_2 = 90,
SDL_SCANCODE_KP_3 = 91,
SDL_SCANCODE_KP_4 = 92,
SDL_SCANCODE_KP_5 = 93,
SDL_SCANCODE_KP_6 = 94,
SDL_SCANCODE_KP_7 = 95,
SDL_SCANCODE_KP_8 = 96,
SDL_SCANCODE_KP_9 = 97,
SDL_SCANCODE_KP_0 = 98,
SDL_SCANCODE_KP_PERIOD = 99,
SDL_SCANCODE_NonUSBACKSLASH = 100,
SDL_SCANCODE_APPLICATION = 101,
SDL_SCANCODE_POWER = 102,
SDL_SCANCODE_KP_EQUALS = 103,
SDL_SCANCODE_F13 = 104,
SDL_SCANCODE_F14 = 105,
SDL_SCANCODE_F15 = 106,
SDL_SCANCODE_F16 = 107,
SDL_SCANCODE_F17 = 108,
SDL_SCANCODE_F18 = 109,
SDL_SCANCODE_F19 = 110,
SDL_SCANCODE_F20 = 111,
SDL_SCANCODE_F21 = 112,
SDL_SCANCODE_F22 = 113,
SDL_SCANCODE_F23 = 114,
SDL_SCANCODE_F24 = 115,
SDL_SCANCODE_EXECUTE = 116,
SDL_SCANCODE_HELP = 117,
SDL_SCANCODE_MENU = 118,
SDL_SCANCODE_SELECT = 119,
SDL_SCANCODE_STOP = 120,
SDL_SCANCODE_AGAIN = 121,
SDL_SCANCODE_UNDO = 122,
SDL_SCANCODE_CUT = 123,
SDL_SCANCODE_COPY = 124,
SDL_SCANCODE_PASTE = 125,
SDL_SCANCODE_FIND = 126,
SDL_SCANCODE_MUTE = 127,
SDL_SCANCODE_VOLUMEUP = 128,
SDL_SCANCODE_VOLUMEDOWN = 129,
SDL_SCANCODE_KP_COMMA = 133,
SDL_SCANCODE_KP_EQUALSAS400 = 134,
SDL_SCANCODE_INTERNATIONAL1 = 135,
SDL_SCANCODE_INTERNATIONAL2 = 136,
SDL_SCANCODE_INTERNATIONAL3 = 137,
SDL_SCANCODE_INTERNATIONAL4 = 138,
SDL_SCANCODE_INTERNATIONAL5 = 139,
SDL_SCANCODE_INTERNATIONAL6 = 140,
SDL_SCANCODE_INTERNATIONAL7 = 141,
SDL_SCANCODE_INTERNATIONAL8 = 142,
SDL_SCANCODE_INTERNATIONAL9 = 143,
SDL_SCANCODE_LANG1 = 144,
SDL_SCANCODE_LANG2 = 145,
SDL_SCANCODE_LANG3 = 146,
SDL_SCANCODE_LANG4 = 147,
SDL_SCANCODE_LANG5 = 148,
SDL_SCANCODE_LANG6 = 149,
SDL_SCANCODE_LANG7 = 150,
SDL_SCANCODE_LANG8 = 151,
SDL_SCANCODE_LANG9 = 152,
SDL_SCANCODE_ALTERASE = 153,
SDL_SCANCODE_SYSREQ = 154,
SDL_SCANCODE_CANCEL = 155,
SDL_SCANCODE_CLEAR = 156,
SDL_SCANCODE_PRIOR = 157,
SDL_SCANCODE_RETURN2 = 158,
SDL_SCANCODE_SEPARATOR = 159,
SDL_SCANCODE_OUT = 160,
SDL_SCANCODE_OPER = 161,
SDL_SCANCODE_CLEARAGAIN = 162,
SDL_SCANCODE_CRSEL = 163,
SDL_SCANCODE_EXSEL = 164,
SDL_SCANCODE_KP_00 = 176,
SDL_SCANCODE_KP_000 = 177,
SDL_SCANCODE_THOUSANDSSEPARATOR = 178,
SDL_SCANCODE_DECIMALSEPARATOR = 179,
SDL_SCANCODE_CURRENCYUNIT = 180,
SDL_SCANCODE_CURRENCYSUBUNIT = 181,
SDL_SCANCODE_KP_LEFTPAREN = 182,
SDL_SCANCODE_KP_RIGHTPAREN = 183,
SDL_SCANCODE_KP_LEFTBRACE = 184,
SDL_SCANCODE_KP_RIGHTBRACE = 185,
SDL_SCANCODE_KP_TAB = 186,
SDL_SCANCODE_KP_BACKSPACE = 187,
SDL_SCANCODE_KP_A = 188,
SDL_SCANCODE_KP_B = 189,
SDL_SCANCODE_KP_C = 190,
SDL_SCANCODE_KP_D = 191,
SDL_SCANCODE_KP_E = 192,
SDL_SCANCODE_KP_F = 193,
SDL_SCANCODE_KP_XOR = 194,
SDL_SCANCODE_KP_POWER = 195,
SDL_SCANCODE_KP_PERCENT = 196,
SDL_SCANCODE_KP_LESS = 197,
SDL_SCANCODE_KP_GREATER = 198,
SDL_SCANCODE_KP_AMPERSAND = 199,
SDL_SCANCODE_KP_DBLAMPERSAND = 200,
SDL_SCANCODE_KP_VERTICALBAR = 201,
SDL_SCANCODE_KP_DBLVERTICALBAR = 202,
SDL_SCANCODE_KP_colon = 203,
SDL_SCANCODE_KP_HASH = 204,
SDL_SCANCODE_KP_SPACE = 205,
SDL_SCANCODE_KP_AT = 206,
SDL_SCANCODE_KP_EXCLAM = 207,
SDL_SCANCODE_KP_MEMSTORE = 208,
SDL_SCANCODE_KP_MEMRECALL = 209,
SDL_SCANCODE_KP_MEMCLEAR = 210,
SDL_SCANCODE_KP_MEMADD = 211,
SDL_SCANCODE_KP_MEMSUBTRACT = 212,
SDL_SCANCODE_KP_MEMMULTIPLY = 213,
SDL_SCANCODE_KP_MEMDIVIDE = 214,
SDL_SCANCODE_KP_PLUSMINUS = 215,
SDL_SCANCODE_KP_CLEAR = 216,
SDL_SCANCODE_KP_CLEARENTRY = 217,
SDL_SCANCODE_KP_BINARY = 218,
SDL_SCANCODE_KP_OCTAL = 219,
SDL_SCANCODE_KP_DECIMAL = 220,
SDL_SCANCODE_KP_HEXADECIMAL = 221,
SDL_SCANCODE_LCTRL = 224,
SDL_SCANCODE_LSHIFT = 225,
SDL_SCANCODE_LALT = 226,
SDL_SCANCODE_LGUI = 227,
SDL_SCANCODE_RCTRL = 228,
SDL_SCANCODE_RSHIFT = 229,
SDL_SCANCODE_RALT = 230,
SDL_SCANCODE_RGUI = 231,
SDL_SCANCODE_MODE = 257,
SDL_SCANCODE_AUDIonEXT = 258,
SDL_SCANCODE_AUDIOPREV = 259,
SDL_SCANCODE_AUDIOSTOP = 260,
SDL_SCANCODE_AUDIOPLAY = 261,
SDL_SCANCODE_AUDIOMUTE = 262,
SDL_SCANCODE_MEDIASELECT = 263,
SDL_SCANCODE_WWW = 264,
SDL_SCANCODE_MAIL = 265,
SDL_SCANCODE_CALCULATOR = 266,
SDL_SCANCODE_COMPUTER = 267,
SDL_SCANCODE_AC_SEARCH = 268,
SDL_SCANCODE_AC_HOME = 269,
SDL_SCANCODE_AC_BACK = 270,
SDL_SCANCODE_AC_FORWARD = 271,
SDL_SCANCODE_AC_STOP = 272,
SDL_SCANCODE_AC_REFRESH = 273,
SDL_SCANCODE_AC_BOOKMARKS = 274,
SDL_SCANCODE_BRIGHTNESSDOWN = 275,
SDL_SCANCODE_BRIGHTNESSUP = 276,
SDL_SCANCODE_DISPLAYSWITCH = 277,
SDL_SCANCODE_KBDILLUMTOGGLE = 278,
SDL_SCANCODE_KBDILLUMDOWN = 279,
SDL_SCANCODE_KBDILLUMUP = 280,
SDL_SCANCODE_EJECT = 281,
SDL_SCANCODE_SLEEP = 282,
SDL_SCANCODE_APP1 = 283,
SDL_SCANCODE_APP2 = 284,
SDL_NUM_SCANCODES = 512
} SDL_Scancode;
介绍了这些函数,就足够做一个简单的小游戏了。
SDL基本宏SDL_TICKS_PASSED
#define SDL_TICKS_PASSED(A, B) ((Sint32)((B) - (A)) <= 0)
作用:判断时间A是否经过了时间B。
游戏运行框架在这里,我先简单介绍一下游戏的更新机制。游戏进行主要由三部分组成:
- 处理输入;
- 更新游戏;
- 生成输出。
在代码中,这三个功能分别由三个函数实现,而在一个主函数里,要循环执行这三个函数。这也是游戏与其他程序不同的一点:需要频繁更新。处理输入就是对输入做出处理,这里的输入包括多方面,如键盘、鼠标、控制杆的操作,还有联机游戏服务器发来的信息等;更新游戏则是对游戏的各种数据做出更新,但并不显示到屏幕上;生成输出时,需要输出当前的信息,如绘制游戏界面,向服务器发送信息等等。
今天就说到这里,谢谢大家支持,下期再见



