大部分前期工作都已經完成了,可以正式開始敲遊戲代碼了。之前顯示的都是死循環的窗口,這次終於可以來解決這個問題了,先看看今天的結果。
這次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_VIDEO
local bResult = Renderer.SDLInit(flags)
if not bResult then
Logger.LogError("Renderer.SDLInit(%d) failed, %s", flags, Renderer.GetError())
return false
end
--讀取Window.ini配置
local filePath = "Config/Window.ini"
local windowConfig = io.open(filePath, "r")
if not windowConfig then
Logger.LogError("Error: Can't find %s", filePath)
return false
end
self.m_title = windowConfig:read()
self.m_width = tonumber(windowConfig:read())
self.m_height = tonumber(windowConfig:read())
self.m_bFullscreen = tonumber(windowConfig:read()) ~= 0
windowConfig:close()
--根據Window.ini配置創建SDL窗口
flags = 0
if self.m_bFullscreen then
flags = flags | SDL_WINDOW_TYPE.SDL_WINDOW_FULLSCREEN
end
self.m_pSDLWindow = Renderer.CreateWindow(self.m_title, 100, 100, self.m_width, self.m_height, flags)
--創建SDLRenderer
flags = SDL_RENDERER_TYPE.SDL_RENDERER_ACCELERATED | SDL_RENDERER_TYPE.SDL_RENDERER_PRESENTVSYNC
self.m_pSDLRenderer = Renderer.CreateRenderer(self.m_pSDLWindow, -1, flags)
--SDL_Image初始化
flags = SDL_IMAGE_INIT_TYPE.IMG_INIT_PNG
bResult = Renderer.SDLImageInit(flags)
if not bResult then
Logger.LogError("Renderer.SDLImageInit(%d) failed, %s", flags, Renderer.GetError())
return false
end
if self.OnInit then
bResult = self:OnInit()
if not bResult then
return false
end
end
self.m_bIsRunning = true
return true
end
--運行
function GameBase:Run()
while self:IsRunning() do
self:HandleEvents()
self:Update()
self:Render()
end
end
--釋放
function GameBase:Release()
if self.OnRelease then
self:OnRelease()
end
if self.m_pSDLRenderer then
Renderer.DestroyRenderer(self.m_pSDLRenderer)
end
if self.m_pSDLWindow then
Renderer.DestroyWindow(self.m_pSDLWindow)
end
Renderer.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 do
if eventType == SDL_EVENT_TYPE.SDL_QUIT then
self:QuitGame()
else--other event
end
bResult, eventType = Renderer.PollEvent()
end
--按下Escape鍵退出遊戲
bResult = Renderer.GetKeyboardState(SDL_KEYCODE.SDL_SCANCODE_ESCAPE)
if bResult then
self:QuitGame()
end
if self.OnHandleInput then
self:OnHandleInput()
end
end
--更新
function GameBase:Update()
--限制一幀至少16ms
while not Renderer.TICKS_PASSED(Renderer.GetTicks(), self.m_lastTime + 16) do
end
local currentTime = Renderer.GetTicks()
local deltaTime = (currentTime - self.m_lastTime) / 1000
self.m_lastTime = currentTime
--調試會導致deltaTime變大,限制一下
if deltaTime > 0.05 then
deltaTime = 0.05
end
if self.OnUpdate then
self:OnUpdate(deltaTime)
end
end
--渲染
function GameBase:Render()
Renderer.SetRenderDrawColor(self.m_pSDLRenderer, 0, 0, 255, 255)
Renderer.RenderClear(self.m_pSDLRenderer)
if self.OnRender then
self:OnRender()
end
Renderer.RenderPresent(self.m_pSDLRenderer)
end
return 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" then
rawset(t, key, newValue)
else
Logger.LogError("This action overrides GameBase's function is not allowed\n"..debug.traceback())
end
end
})
function RyuujinnGame:OnInit()
return true
end
function RyuujinnGame:OnRelease()
end
function RyuujinnGame:OnHandleInput()
end
function RyuujinnGame:OnUpdate(deltaTime)
end
function RyuujinnGame:OnRender()
end
Main.lua腳本估計後面也不會用太大的改動,這應該算是最終版吧。
Main.lua
require "Logger"
require "RyuujinnGame"
function Main()
Logger.Log("Main")
local bResult = RyuujinnGame:Init()
if bResult then
RyuujinnGame:Run()
end
RyuujinnGame: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" then
rawset(t, key, newValue)
else
Logger.LogError("This action overrides GameBase's function is not allowed\n"..debug.traceback())
end
end
})
function RyuujinnGame:OnInit()
self.texture = Renderer.GetTexture(self.m_pSDLRenderer, "Resource/body.png")
self.posX = 0
self.posY = 0
return true
end
function RyuujinnGame:OnRelease()
end
function RyuujinnGame:OnHandleInput()
if Renderer.GetKeyboardState(SDL_KEYCODE.SDL_SCANCODE_W) then
self.posY = self.posY - 10
end
if Renderer.GetKeyboardState(SDL_KEYCODE.SDL_SCANCODE_S) then
self.posY = self.posY + 10
end
if Renderer.GetKeyboardState(SDL_KEYCODE.SDL_SCANCODE_A) then
self.posX = self.posX - 10
end
if Renderer.GetKeyboardState(SDL_KEYCODE.SDL_SCANCODE_D) then
self.posX = self.posX + 10
end
end
function RyuujinnGame:OnUpdate(deltaTime)
end
function RyuujinnGame:OnRender()
local destWidth = 100
local destHeight = 100
Renderer.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");
}