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