fortop 发表于 2024-7-15 18:23

游戏开发技术--LOD自动降档(Lua)

一、设计意图

在游戏开发过程中,凡是需要设计一些优化策略,以保证游戏的流畅体验。
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 = 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.array = 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)--封锁功能
页: [1]
查看完整版本: 游戏开发技术--LOD自动降档(Lua)