设计意图
在游戏开发过程中,通常需要设计一些优化策略,以保证游戏的流畅体验。
LOD自动降档策略就是其中之一,当检测到帧率不达标的时候,可以自动触发画质档位的下降(或者其他优化方案),以保证帧率可以恢复达标的状态。
在游戏中,玩家可以根据需求选择是否开启此项优化功能。
设计思路
帧率检测方法:检测当前帧之前一定数量(CheckTotalCount)的游戏帧中,deltaTime超过某一阈值的帧数(DeltaTimeThreshold),若此帧数过高,超过设定数量(CheckFrameCount),则记为一次不达标。
例如,设定CheckTotalCount = 40,CheckFrameCount = 30,DeltaTimeThreshold = 0.05。
则根据策略,在某一检测帧之前的40帧内,如果有75%的帧与前一帧的间隔时间(dt)超过0.05秒,则可以把这40帧标记为一次不达标的段。
若不达标的次数在监测范围内超过可容忍的次数, 则进行档位自适应调整。
实现方式
注意:代码中使用了自己实现的Update模块,可以对指定的Tick函数进行注册和注销,以实现游戏中的更新逻辑。有机会我会对此模块进行介绍,读者可以根据自身的情况实现自己的更新函数。
--[[引入Update模块]]--
local LuaUpdater = require "Main/LuaUpdater"
--[[相关配置]]--
local DeltaTimeThreshold = 0.05 --检测达成降档的DT阈值
local CheckTotalCount = 40; -- 当前帧之前,需要统计DT状态的帧数
local CheckFrameCount = 30; -- 需要统计的帧数中,DT高于阈值的数量
local FRFailTolerateTimes = 3; -- 帧率检测到不达标的容忍次数
local TotalCheckTick = 3000; -- 连续检测帧率的最大次数
--[[定义变量]]--
local OptimizeController = {}
local private =
{
--bool队列,统计当前帧前CheckTotalCount个帧是否为不达标的帧
reachQueue = { head = 1, tail = 1, size = 0, capacity = CheckTotalCount, array = {}, },
--计时队列,统计不达标段的记录时间(帧号)
frFailTickQueue = { head = 1, tail = 1, size = 0, capacity = FRFailTolerateTimes, array = {}, },
reachCount = 0, --统计不达标的帧数
frameCheckTick = 0, --更新函数的计时(帧号)
}
--[[外部接口,开启或关闭]]--
function OptimizeController.EnableAdaptiveLOD(enable)
if enable then
private.StartFrameCheck()
else
private.StopFrameCheck()
end
end
--[[内部实现]]--
function private.StartFrameCheck()
--开启更新
LuaUpdater.StartRealTimeUpdate(private.Tick)
--重置变量
private.ClearQueue(private.reachQueue)
private.ClearQueue(private.frFailTickQueue)
private.reachCount = 0
private.frameCheckTick = 0
end
function private.StopFrameCheck()
--关闭更新
LuaUpdater.StopRealTimeUpdate(private.Tick)
end
function private.Tick(deltaTime)
private.frameCheckTick = private.frameCheckTick + 1
--如果队列满了,需要出队
if private.reachQueue.size == CheckTotalCount then
if private.Dequeue(private.reachQueue) then
private.reachCount = private.reachCount - 1
end
end
local reach = deltaTime >= DeltaTimeThreshold
private.Enqueue(private.reachQueue, reach) --记录是否达标的状态,reach == true 代表不达标
if not reach then
--当前帧如果没有出现未达标的情况,可以跳出
return
end
private.reachCount = private.reachCount + 1
if private.reachCount < CheckFrameCount then
--还未达到帧率不达标的情况,可以跳出
return
end
--已经触发不达标段的计数,可以重新进行统计了
private.ClearQueue(private.reachQueue)
private.reachCount = 0
--判断此次触发是否咋在统计的时间范围内
if private.frFailTickQueue.size == FRFailTolerateTimes
and private.frameCheckTick - private.Dequeue(private.frFailTickQueue) < TotalCheckTick then
private.ClearQueue(private.frFailTickQueue)
private.frameCheckTick = 0
--[[
此处调用自己项目内实现的挡位调节方法
]]--
else
private.Enqueue(private.frFailTickQueue, private.frameCheckTick)
end
end
--[[队列操作]]--
function private.Enqueue(queue,item)
if queue.size == queue.capacity then
return
end
queue.array[queue.tail] = item
queue.tail = queue.tail % queue.capacity + 1
queue.size = queue.size + 1
end
function private.Dequeue(queue)
if queue.size <= 0 then
return nil
end
local item = queue.array[queue.head]
queue.array[queue.head] = nil
queue.head = queue.head % queue.capacity + 1
queue.size = queue.size - 1
return item
end
function private.ClearQueue(queue)
table.clearArray(queue.array)
queue.head = 1
queue.tail = 1
queue.size = 0
end
return OptimizeController
可以直接Require此模块进行使用
local ctrl = require "OptimizeController"
ctrl.EnableAdaptiveLOD(true) --开启功能
ctrl.EnableAdaptiveLOD(false) --关闭功能