大部分前期工作都已经完成了,可以正式开始敲游戏代码了。之前显示的都是死循环的窗口,这次终于可以来解决这个问题了,先看看今天的结果。


       这次C++中有部分修改的代码,但有了之前的基础,就不花费很多精力在这上面,代码会放到最后面。首先将之前的Game.lua修改下名字,改成GameBase.lua。

GameBase.lua

require "Module.Enum.SDL"local GameBase =
{m_bIsRunning = false, --是否在运行m_pSDLWindow = nil,     --SDL_Window指针m_pSDLRenderer = nil,    --SDL_Renderer指针m_title = "Game",        --窗口标题m_width = 0,         --窗口宽度m_height = 0,            --窗口高度m_bFullscreen = false,   --是否是全屏运行m_lastTime = 0,           --上次运行时间
}--初始化
function GameBase:Init()self.m_bIsRunning = false--SDL初始化local flags = SDL_INIT_TYPE.SDL_INIT_VIDEOlocal bResult = Renderer.SDLInit(flags)if not bResult thenLogger.LogError("Renderer.SDLInit(%d) failed, %s", flags, Renderer.GetError())return falseend--读取Window.ini配置local filePath = "Config/Window.ini"local windowConfig = io.open(filePath, "r")if not windowConfig thenLogger.LogError("Error: Can't find %s", filePath)return falseendself.m_title = windowConfig:read()self.m_width = tonumber(windowConfig:read())self.m_height = tonumber(windowConfig:read())self.m_bFullscreen = tonumber(windowConfig:read()) ~= 0windowConfig:close()--根据Window.ini配置创建SDL窗口flags = 0if self.m_bFullscreen thenflags = flags | SDL_WINDOW_TYPE.SDL_WINDOW_FULLSCREENendself.m_pSDLWindow = Renderer.CreateWindow(self.m_title, 100, 100, self.m_width, self.m_height, flags)--创建SDLRendererflags = SDL_RENDERER_TYPE.SDL_RENDERER_ACCELERATED | SDL_RENDERER_TYPE.SDL_RENDERER_PRESENTVSYNCself.m_pSDLRenderer = Renderer.CreateRenderer(self.m_pSDLWindow, -1, flags)--SDL_Image初始化flags = SDL_IMAGE_INIT_TYPE.IMG_INIT_PNGbResult = Renderer.SDLImageInit(flags)if not bResult thenLogger.LogError("Renderer.SDLImageInit(%d) failed, %s", flags, Renderer.GetError())return falseendif self.OnInit thenbResult = self:OnInit()if not bResult thenreturn falseendendself.m_bIsRunning = truereturn true
end--运行
function GameBase:Run()while self:IsRunning() doself:HandleEvents()self:Update()self:Render()end
end--释放
function GameBase:Release()if self.OnRelease thenself:OnRelease()endif self.m_pSDLRenderer thenRenderer.DestroyRenderer(self.m_pSDLRenderer)endif self.m_pSDLWindow thenRenderer.DestroyWindow(self.m_pSDLWindow)endRenderer.Quit()
end--是否在运行中
function GameBase:IsRunning()return self.m_bIsRunning
end--退出游戏
function GameBase:QuitGame()self.m_bIsRunning = false
end--事件处理
function GameBase:HandleEvents()local bResult, eventType = Renderer.PollEvent()while bResult doif eventType == SDL_EVENT_TYPE.SDL_QUIT thenself:QuitGame()else--other eventendbResult, eventType = Renderer.PollEvent()end--按下Escape键退出游戏bResult = Renderer.GetKeyboardState(SDL_KEYCODE.SDL_SCANCODE_ESCAPE)if bResult thenself:QuitGame()endif self.OnHandleInput thenself:OnHandleInput()end
end--更新
function GameBase:Update()--限制一帧至少16mswhile not Renderer.TICKS_PASSED(Renderer.GetTicks(), self.m_lastTime + 16) doendlocal currentTime = Renderer.GetTicks()local deltaTime = (currentTime - self.m_lastTime) / 1000self.m_lastTime = currentTime--调试会导致deltaTime变大,限制一下if deltaTime > 0.05 thendeltaTime = 0.05endif self.OnUpdate thenself:OnUpdate(deltaTime)end
end--渲染
function GameBase:Render()Renderer.SetRenderDrawColor(self.m_pSDLRenderer, 0, 0, 255, 255)Renderer.RenderClear(self.m_pSDLRenderer)if self.OnRender thenself:OnRender()endRenderer.RenderPresent(self.m_pSDLRenderer)
endreturn GameBase

可以看出来GameBase是作为元表的,我把很多框架上的函数都写在GameBase上,是不想被人重载掉这些接口。按顺序逐个说下API:GameBase:Init()应该是最简单的,只是将之前的代码完善而已。
       SDL初始化应该没有多少问题,跳过。在创建SDL窗口之前,添加了一堆读取Window.ini的代码,实际上只是把创建窗口需要的参数提取出来,放到Window.ini中,利用IO操作来获取数据,减少在代码中频繁修改窗口名,窗口宽度等数据的次数。可以来看下Window.ini的配置:


       创建SDLRenderer也没有多少变化,跳过。新增加的SDLImageInit其实跟SDL_Init一样(我避免歧义,重命名了Renderer.Init函数名)。接下来代码就比较核心了,过会还会看到很多OnXXX的函数,这些才是我们真正关心的函数。
       没有用过lua的估计会有点懵逼,这是啥意思,这里简单解释下,因为GameBase本身是没有OnInit字段(key),所以会跳过这段代码,但是不要忘记GameBase是作为元表的,也就是父类,而我们后面将使用的也是子类(也就是后面的RyuujinnGame)来创建游戏的。因此如果是子类(RyuujinnGame表)在调用Init函数的时候,执行到同样的地方时候,self.OnInit就会从子类(RyuujinnGame表)查找这个OnInit字段(key),找到后,就调用OnInit函数,这样就使得子类(RyuujinnGame表)只关心游戏的逻辑,不去关系SDL窗口的创建,SDL事件的处理等。
       接下来的GameBase:Run()就是之前的死循环修改后的版本,主要负责3个工作,处理事件,更新游戏,渲染游戏。再接着的GameBase:Release()也是之前提取出来的代码,只是加了OnRelease调用而已,这个跟之前的OnInit是一致的,这边不再重复。GameBase:IsRunning()和GameBase:QuitGame()的代码就一句,没啥可说的。
       GameBase:HandleEvents()主要功能就是事件处理,比如处理点击关闭按钮事件,按下Escape按键等。lua这边代码也很简单,但这里有一个特殊的地方就是,C++函数只能有一个返回值(不算把结果按地址传递,类似c#的out),lua这边直接返回了2个值,来看C++这边的代码,int PollEvent(lua_State* L)就实现了lua这边可以接受2个返回值的功能,因为事件处理只关心类型,也做一步处理,不传SDL_Event,而是传SDL_Event的type字段。


       接下来的GameBase:Update()和GameBase:Render()代码很简单,可以修改Renderer.SetRenderDrawColor函数后面4个参数来修改SDL窗口的颜色。SDL一些枚举值,也有部分提取到lua中,在Script文件夹中,创建Module文件夹,再在Module文件夹下创建Enum文件夹,再在Enum文件夹下创建SDL.lua文件,工作也很简单,就是把大部分ctrl+c,ctrl+v就ok了。需要注意的就是16进制要转成10进制。

SDL.lua

--SDL初始化类型
SDL_INIT_TYPE =
{SDL_INIT_VIDEO = tonumber("00000020", 16),--将16进制字符串转换成10进制数字
}--SDL窗口类型
SDL_WINDOW_TYPE =
{SDL_WINDOW_FULLSCREEN = tonumber("00000001", 16),
}SDL_RENDERER_TYPE =
{SDL_RENDERER_ACCELERATED = tonumber("00000002", 16),SDL_RENDERER_PRESENTVSYNC = tonumber("00000004", 16),
}--SDL_Image初始化类型
SDL_IMAGE_INIT_TYPE =
{IMG_INIT_PNG = tonumber("00000002", 16),
}--事件类型
SDL_EVENT_TYPE =
{SDL_QUIT = tonumber("100", 16),
}--按键
SDL_KEYCODE =
{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,
}

大部分工作已经在GameBase上完成了,接下来就是来实现子类,添加RyuujinnGame.lua,代码也很简单,关键就是通过__newindex元方法来进制重载GameBase上的函数。
RyuujinnGame.lua

RyuujinnGame = setmetatable({},
{ __index = require "GameBase",__newindex = function(t, key, newValue)--禁止重载GameBase函数local oldValue = t[key]if oldValue == nil or type(oldValue) ~= "function" thenrawset(t, key, newValue)elseLogger.LogError("This action overrides GameBase's function is not allowed\n"..debug.traceback())endend
})function RyuujinnGame:OnInit()return true
endfunction RyuujinnGame:OnRelease()endfunction RyuujinnGame:OnHandleInput()endfunction RyuujinnGame:OnUpdate(deltaTime)endfunction RyuujinnGame:OnRender()end

Main.lua脚本估计后面也不会用太大的改动,这应该算是最终版吧。
Main.lua

require "Logger"require "RyuujinnGame"function Main()Logger.Log("Main")local bResult = RyuujinnGame:Init()if bResult thenRyuujinnGame:Run()endRyuujinnGame:Release()
end

运行后就会得到一个蓝色的窗口,点击关闭按钮和按下Escape按键都能关闭掉SDL窗口。

是时候在窗口上显示一张图片啥的,接下来就只要修改RyuujinnGame.lua的脚本,就会在窗口上显示一张图片,并且可以通过WSAD按键控制图片的移动。Renderer.Test只是测试代码,后面会删除的。

RyuujinnGame.lua

RyuujinnGame = setmetatable({},
{ __index = require "GameBase",__newindex = function(t, key, newValue)--禁止重载GameBase函数local oldValue = t[key]if oldValue == nil or type(oldValue) ~= "function" thenrawset(t, key, newValue)elseLogger.LogError("This action overrides GameBase's function is not allowed\n"..debug.traceback())endend
})function RyuujinnGame:OnInit()self.texture = Renderer.GetTexture(self.m_pSDLRenderer, "Resource/body.png")self.posX = 0self.posY = 0return true
endfunction RyuujinnGame:OnRelease()endfunction RyuujinnGame:OnHandleInput()if Renderer.GetKeyboardState(SDL_KEYCODE.SDL_SCANCODE_W) thenself.posY = self.posY - 10endif Renderer.GetKeyboardState(SDL_KEYCODE.SDL_SCANCODE_S) thenself.posY = self.posY + 10endif Renderer.GetKeyboardState(SDL_KEYCODE.SDL_SCANCODE_A) thenself.posX = self.posX - 10endif Renderer.GetKeyboardState(SDL_KEYCODE.SDL_SCANCODE_D) thenself.posX = self.posX + 10end
endfunction RyuujinnGame:OnUpdate(deltaTime)endfunction RyuujinnGame:OnRender()local destWidth = 100local destHeight = 100Renderer.Test(self.m_pSDLRenderer, self.texture, 100, 100, self.posX, self.posY)
end

Renderer.h

#pragma once#define SDL_MAIN_HANDLED// 引入SDL需要的头文件
#include <SDL.h>
#include <SDL_image.h>// 链接SDL静态库
#pragma comment(lib, "SDL2.lib")
#pragma comment(lib, "SDL2main.lib")
#pragma comment(lib, "SDL2_image.lib")class Renderer
{
public:
#pragma region 初始化static bool SDLInit(Uint32 flags);static bool SDLImageInit(int flags);static SDL_Window* CreateWindow(const char* title, int posX, int posY, int width, int hegiht, Uint32 flags);static SDL_Renderer* CreateRenderer(SDL_Window* pWindow, int index, Uint32 flags);
#pragma endregion#pragma region 释放static void DestroyWindow(SDL_Window* pWindow);static void DestroyRenderer(SDL_Renderer* pRenderer);static void Quit();
#pragma endregion#pragma region 报错static const char* GetError();
#pragma endregion#pragma region 事件static bool PollEvent(SDL_Event& event);
#pragma endregion#pragma region 时间static Uint32 GetTicks();static bool TICKS_PASSED(Uint32 currentTime, Uint32 targetTime);
#pragma endregion#pragma region 渲染static void SetRenderDrawColor(SDL_Renderer* pRenderer, Uint8 r, Uint8 g, Uint8 b,Uint8 a);static void RenderClear(SDL_Renderer* pRenderer);static void RenderPresent(SDL_Renderer* pRenderer);static SDL_Texture* GetTexture(SDL_Renderer* pRenderer, const char* fileName);static void Test(SDL_Renderer* pRenderer, SDL_Texture* pTexture, int width, int height, int x, int y);
#pragma endregion#pragma region 按钮static bool GetKeyboardState(int keycode);
#pragma endregion
};struct lua_State;
namespace LuaWrap
{void RegisterRenderer(lua_State* L);
}

Renderer.cpp

#include "Renderer.h"
#include "Logger.h"bool Renderer::SDLInit(Uint32 flags)
{return 0 == SDL_Init(flags);
}bool Renderer::SDLImageInit(int flags)
{return 0 != IMG_Init(IMG_INIT_PNG);
}SDL_Window* Renderer::CreateWindow(const char* title, int posX, int posY, int width, int hegiht, Uint32 flags)
{return SDL_CreateWindow(title, posX, posY, width, hegiht, flags);
}SDL_Renderer* Renderer::CreateRenderer(SDL_Window* pWindow, int index, Uint32 flags)
{return SDL_CreateRenderer(pWindow, index, flags);
}void Renderer::DestroyWindow(SDL_Window* pWindow)
{if (pWindow)SDL_DestroyWindow(pWindow);
}void Renderer::DestroyRenderer(SDL_Renderer* pRenderer)
{if (pRenderer)SDL_DestroyRenderer(pRenderer);
}void Renderer::Quit()
{SDL_Quit();
}const char* Renderer::GetError()
{return SDL_GetError();
}bool Renderer::PollEvent(SDL_Event& event)
{return SDL_PollEvent(&event);
}Uint32 Renderer::GetTicks()
{return SDL_GetTicks();
}bool Renderer::TICKS_PASSED(Uint32 currentTime, Uint32 targetTime)
{return SDL_TICKS_PASSED(currentTime, targetTime);
}void Renderer::SetRenderDrawColor(SDL_Renderer* pRenderer, Uint8 r, Uint8 g, Uint8 b, Uint8 a)
{SDL_SetRenderDrawColor(pRenderer, r, g, b, a);
}void Renderer::RenderClear(SDL_Renderer* pRenderer)
{SDL_RenderClear(pRenderer);
}void Renderer::RenderPresent(SDL_Renderer* pRenderer)
{SDL_RenderPresent(pRenderer);
}SDL_Texture* Renderer::GetTexture(SDL_Renderer* pRenderer, const char* fileName)
{SDL_Texture* pTexture = nullptr;SDL_Surface* pSurface = IMG_Load(fileName);if (!pSurface){Logger::LogError("IMG_Load() failed in Renderer::GetTexture(): %s", fileName);return nullptr;}pTexture = SDL_CreateTextureFromSurface(pRenderer, pSurface);SDL_FreeSurface(pSurface);if (!pTexture){SDL_Log("SDL_CreateTextureFromSurface() failed in GetTexture(): %s", SDL_GetError());return nullptr;}return pTexture;
}void Renderer::Test(SDL_Renderer* pRenderer, SDL_Texture* pTexture, int width, int height, int x, int y)
{SDL_Rect rect;rect.w = width;rect.h = height;rect.x = x;rect.y = y;SDL_RenderCopy(pRenderer, pTexture, nullptr, &rect);
}bool Renderer::GetKeyboardState(int keycode)
{bool bResult = false;const Uint8* pState = SDL_GetKeyboardState(nullptr);if (pState)bResult = pState[keycode];return bResult;
}

RendererWrap.cpp

#include "Renderer.h"
#include <LuaClient.h>
#include "Logger.h"int SDLInit(lua_State* L)
{Uint32 flag = (Uint32)lua_tointeger(L, 1);lua_pushboolean(L, Renderer::SDLInit(flag));return 1;
}int SDLImageInit(lua_State* L)
{Uint32 flag = (Uint32)lua_tointeger(L, 1);lua_pushboolean(L, Renderer::SDLImageInit(flag));return 1;
}int CreateWindow(lua_State* L)
{const char* title = lua_tostring(L, 1);int posX = (int)lua_tointeger(L, 2);int posY = (int)lua_tointeger(L, 3);int width = (int)lua_tointeger(L, 4);int hegiht = (int)lua_tointeger(L, 5);Uint32 flag = (Uint32)lua_tointeger(L, 6);lua_pushlightuserdata(L, Renderer::CreateWindow(title, posX, posY, width, hegiht, flag));return 1;
}int CreateRenderer(lua_State* L)
{SDL_Window* pWindow = (SDL_Window*)lua_touserdata(L, 1);int index = (int)lua_tointeger(L, 2);Uint32 flag = (Uint32)lua_tointeger(L, 3);lua_pushlightuserdata(L, Renderer::CreateRenderer(pWindow, index, flag));return 1;
}int DestroyWindow(lua_State* L)
{SDL_Window* pWindow = (SDL_Window*)lua_touserdata(L, 1);Renderer::DestroyWindow(pWindow);return 0;
}int DestroyRenderer(lua_State* L)
{SDL_Renderer* pRenderer = (SDL_Renderer*)lua_touserdata(L, 1);Renderer::DestroyRenderer(pRenderer);return 0;
}int Quit(lua_State* L)
{Renderer::Quit();return 0;
}int GetError(lua_State* L)
{auto ss = Renderer::GetError();lua_pushstring(L, Renderer::GetError());return 1;
}int PollEvent(lua_State* L)
{SDL_Event event;bool bResult = 1 == SDL_PollEvent(&event);long type = -1;if (bResult)type = event.type;lua_pushboolean(L, bResult);lua_pushnumber(L, type);return 2;
}int GetTicks(lua_State* L)
{lua_pushnumber(L, Renderer::GetTicks());return 1;
}int TICKS_PASSED(lua_State* L)
{Uint32 currentTime = (Uint32)lua_tonumber(L, 1);Uint32 targetTime = (Uint32)lua_tonumber(L, 2);lua_pushboolean(L, Renderer::TICKS_PASSED(currentTime, targetTime));return 1;
}int SetRenderDrawColor(lua_State* L)
{SDL_Renderer* pRenderer = (SDL_Renderer*)lua_touserdata(L, 1);Uint8 r = (Uint8)lua_tonumber(L, 2);Uint8 g = (Uint8)lua_tonumber(L, 3);Uint8 b = (Uint8)lua_tonumber(L, 4);Uint8 a = (Uint8)lua_tonumber(L, 5);Renderer::SetRenderDrawColor(pRenderer, r, g, b, a);return 0;
}int RenderClear(lua_State* L)
{SDL_Renderer* pRenderer = (SDL_Renderer*)lua_touserdata(L, 1);Renderer::RenderClear(pRenderer);return 0;
}int RenderPresent(lua_State* L)
{SDL_Renderer* pRenderer = (SDL_Renderer*)lua_touserdata(L, 1);Renderer::RenderPresent(pRenderer);return 0;
}int GetTexture(lua_State* L)
{SDL_Renderer* pRenderer = (SDL_Renderer*)lua_touserdata(L, 1);const char* fileName = lua_tostring(L, 2);lua_pushlightuserdata(L, Renderer::GetTexture(pRenderer, fileName));return 1;
}int Test(lua_State* L)
{SDL_Renderer* pRenderer = (SDL_Renderer*)lua_touserdata(L, 1);SDL_Texture* pTexture = (SDL_Texture*)lua_touserdata(L, 2);int width = (int)lua_tonumber(L, 3);int height = (int)lua_tonumber(L, 4);int x = (int)lua_tonumber(L, 5);int y = (int)lua_tonumber(L, 6);Renderer::Test(pRenderer, pTexture, width, height, x, y);return 0;
}int GetKeyboardState(lua_State* L)
{int keycode = (int)lua_tonumber(L, 1);lua_pushboolean(L, Renderer::GetKeyboardState(keycode));return 1;
}int RendererGet(lua_State* L)
{const char* strKey = lua_tostring(L, 2);luaL_getmetatable(L, "RendererMetaTable");lua_pushvalue(L, 2);lua_rawget(L, -2);if (lua_isnil(L, -1))Logger::LogError("Renderer don't have the field: %s.\n%s:%d: in function '%s'", strKey, __FILE__, __LINE__, __FUNCTION__);return 1;
}int RendererSet(lua_State* L)
{luaL_getmetatable(L, "RendererMetaTable");lua_pushvalue(L, 2);lua_rawget(L, -2);if (lua_isnil(L, -1)){lua_pop(L, 1);lua_pushvalue(L, 2);lua_pushvalue(L, 3);lua_rawset(L, -3);}else{if(LUA_TFUNCTION == lua_type(L, -1))Logger::LogError("The action is not allowed.\n%s:%d: in function '%s'", __FILE__, __LINE__, __FUNCTION__);else{lua_pop(L, 1);lua_pushvalue(L, 2);lua_pushvalue(L, 3);lua_rawset(L, -3);}}return 0;
}void LuaWrap::RegisterRenderer(lua_State* L)
{if (!L) return;lua_newtable(L);luaL_newmetatable(L, "RendererMetaTable");lua_pushstring(L, "__index");lua_pushcfunction(L, RendererGet);lua_rawset(L, -3);lua_pushstring(L, "__newindex");lua_pushcfunction(L, RendererSet);lua_rawset(L, -3);lua_pushstring(L, "SDLInit");lua_pushcfunction(L, SDLInit);lua_rawset(L, -3);lua_pushstring(L, "SDLImageInit");lua_pushcfunction(L, SDLImageInit);lua_rawset(L, -3);lua_pushstring(L, "CreateWindow");lua_pushcfunction(L, CreateWindow);lua_rawset(L, -3);lua_pushstring(L, "CreateRenderer");lua_pushcfunction(L, CreateRenderer);lua_rawset(L, -3);lua_pushstring(L, "GetTexture");lua_pushcfunction(L, GetTexture);lua_rawset(L, -3);lua_pushstring(L, "Test");lua_pushcfunction(L, Test);lua_rawset(L, -3);lua_pushstring(L, "DestroyWindow");lua_pushcfunction(L, DestroyWindow);lua_rawset(L, -3);lua_pushstring(L, "DestroyRenderer");lua_pushcfunction(L, DestroyRenderer);lua_rawset(L, -3);lua_pushstring(L, "Quit");lua_pushcfunction(L, Quit);lua_rawset(L, -3);lua_pushstring(L, "GetError");lua_pushcfunction(L, GetError);lua_rawset(L, -3);lua_pushstring(L, "PollEvent");lua_pushcfunction(L, PollEvent);lua_rawset(L, -3);lua_pushstring(L, "SetRenderDrawColor");lua_pushcfunction(L, SetRenderDrawColor);lua_rawset(L, -3);lua_pushstring(L, "RenderClear");lua_pushcfunction(L, RenderClear);lua_rawset(L, -3);lua_pushstring(L, "RenderPresent");lua_pushcfunction(L, RenderPresent);lua_rawset(L, -3);lua_pushstring(L, "GetTicks");lua_pushcfunction(L, GetTicks);lua_rawset(L, -3);lua_pushstring(L, "TICKS_PASSED");lua_pushcfunction(L, TICKS_PASSED);lua_rawset(L, -3);lua_pushstring(L, "GetKeyboardState");lua_pushcfunction(L, GetKeyboardState);lua_rawset(L, -3);lua_setmetatable(L, -2);lua_setglobal(L, "Renderer");
}

源码下载地址

[实战]C++加Lua加SDL来重写龙神录弹幕游戏(4):完善Game类相关推荐

  1. [实战]C++加Lua加SDL来重写龙神录弹幕游戏(6):只读表

    这次就不急着往下讲解游戏功能了,先来说下lua的功能,因为Unity热更新的问题,导致很多手游都会使用c#加lua来开发,因此有很多新手,或者用lua开发了一两年的程序员,还不是很了解lua,在使用中 ...

  2. [实战]C++加Lua加SDL来重写龙神录弹幕游戏(1):准备

    自学程序加工作好几年了,是时候来练个手了(不算用引擎开发游戏).之前Google学习资料的时候,找到一个弹幕射击游戏的教程,稍微瞄了眼,还挺不错的,但用的是封装的DxLib库,这就不开心了,后面就没看 ...

  3. [实战]C++加Lua加SDL来重写龙神录弹幕游戏(5):添加背景

    敲代码很快,写博客很慢,如果写详细一点,就有点太长了,跟写策划文档没区别了,╮(╯_╰)╭.之前虽然在SDL窗口中显示出了一张人物图片,但也只是为了测试SDL_Image而已.现在就正式来完成这次的工 ...

  4. [实战]C++加Lua加SDL来重写龙神录弹幕游戏(2):Lua创建SDL窗口

    完成了准备工作之后,就可以开始撸代码了.因为项目也不是很大,就打算大部分都用lua来开发.上一篇已经写了一部分测试代码,但都是塞到一个Main.cpp之中,主要是为了测试配置是否成功.这次的工作就要把 ...

  5. [实战]C++加Lua加SDL来重写龙神录弹幕游戏(3):添加Logger类

    上一篇已经完成了Lua调用SDL创建窗口的功能了,めでたしめでたし.但在开始敲游戏代码之前,继续做一个准备工作,添加C++类logger.        在之前的测试中,控制台上全是白色的输出字,一眼 ...

  6. 【Python_PySide2学习笔记(一)】PySide2动态加载UI方式,重写关闭窗体事件

    ** PySide2设置关闭提示窗口:动态加载UI方式,重写关闭窗体事件 ** ** 前言 ** 转载:https://blog.csdn.net/qq_44703282/article/detail ...

  7. R语言时间序列(time series)分析实战:时序数据加载、绘制时间序列图

    R语言时间序列(time series)分析实战:时序数据加载.绘制时间序列图 目录

  8. matlab dwt 多层分解,利用matlab对图片进行多层小波分解 会的加QQ511607771 加过作图之后积分加倍送...

    利用matlab对图片进行多层小波分解 会的加QQ511607771 加过作图之后积分加倍送 mip版  关注:96  答案:2  悬赏:0 解决时间 2021-01-23 13:02 已解决 202 ...

  9. openlayers学习——3、openlayers加点加圆加图标图片

    openlayers加点加圆加图标图片 前言:基于Vue,学习openlayers,根据官网demo,记录常用功能写法.本人不是专业GIS开发,只是记录,方便后续查找. 参考资料: openlayer ...

最新文章

  1. 2022-2028年中国塑料绳的制造行业市场现状调查及投资商机预测报告
  2. gis 空间分析 鸟类栖息地选取_动物栖息地选择评估的常用统计方法
  3. KMP模版 KMP求子串在主串出现的次数模版
  4. socket编程实现回声客户端
  5. OD的 CC断点,内存访问断点,硬件断点 解析
  6. 一个程序员的职业规划
  7. .Net环境下的缓存技术介绍 (转)
  8. linux内核定时器使用
  9. 01-操作数组的方法
  10. Aqua data studio 19 汉化方法
  11. 计算机鼠标滚轮不流畅,鼠标滚轮不灵敏怎么办_鼠标中间的滚轮不灵敏了如何解决...
  12. Android | 判断App处于前台还是后台的方案
  13. matplotlib.pyplot如何绘制多张子图
  14. python pandas缺失值处理_pandas缺失值的处理
  15. 01-线性回归sklearn实现
  16. 实验7-3-5 输出大写英文字母(15 分)
  17. shell脚本下载小猪佩奇中文版全集mp3
  18. Win10下Docker使用阿里专属加速器
  19. 微信机器人的构建与使用
  20. 提笔,再回忆~落笔,成悔,一切皆已随风:伤感日志

热门文章

  1. java基础巩固-宇宙第一AiYWM:为了维持生计,Redis基础Part6(Redis的应用场景、Redis是单线程的速度还快、Redis线程模型:Reactor模式、事件、发布订阅、管道)~整起
  2. ARM base instruction -- ldm
  3. 为什么用scrum_为什么Scrum赢了
  4. 超神学院计算机排名,超神学院电脑版
  5. 华为鸿蒙内核linux算国产,都是号称国产操作系统,阿里的YunOS跟华为“鸿蒙”有何不同之处...
  6. 为什么要用Excel做量化投资
  7. 带你一起来摸鱼(快看摸鱼方法了)
  8. Python爬虫:常见的反爬
  9. 创建Vue插件(手动展示vue组件)
  10. python 3d渲染_python带你玩玩3D渲染