Bingkai sesaat

Berapa yang mencukupi?

Lebih pantas tidak semestinya lebih baik

Berapa yang mencukupi?

Jenis permainan yang akan saya buat tidak memerlukan banyak FPS. Tetapi mari kembali ke permulaan: apa itu? Ia ialah bilangan bingkai (imej skrin) yang dipaparkan berturut-turut dalam 1 saat.

Jika anda sebut 30 FPS, maknanya 30 imej dipaparkan berturut-turut dalam 1 saat. Jika FPS terlalu rendah, imej akan tersekat-sekat dan tidak lancar (tidak semestinya begitu).

Buat masa ini sasaran saya ialah sekurang-kurangnya 60 FPS dan, seperti yang anda lihat, memang begitu 😄

FPS dalam permainan

FPS dalam permainan

Kekal terkawal

Saya telah membaca beberapa artikel tentang cara yang terbaik dan paling mudah. Ia bergantung pada jenis permainan dan platform. Oleh sebab saya memilih kebolehpercayaan dan ini permainan 2D yang ringkas, saya memilih untuk menetapkan framerate pada 60.

Untuk itu saya menggunakan https://github.com/bjornbytes/tick dengan sedikit pengubahsuaian saya sendiri, jadi buat masa ini ia kelihatan begini.

local tick = {
  rate = 1 / 60
}

local timeAccumulator = 0

function love.run()
  love.load(love.arg.parseGameArguments(arg), arg)
  love.update(0)
  love.timer.step()

  return function()
    -- Handle events.
    love.event.pump()
    for name, a, b, c, d, e, f in love.event.poll() do
      if name == "quit" and not (love.quit and love.quit()) then
        return a or 0
      end
      love.handlers[name](a, b, c, d, e, f)
    end

    -- Get delta time.
    local _, _, flags = love.window.getMode()
    local dt = love.timer.step()

    if flags.vsync > 0 and flags.refreshrate > 0 then
      local perfectDt = 1 / flags.refreshrate

      if dt < perfectDt * 1.3 and dt > perfectDt / 1.3 then -- If dt is somewhat near perfect, force it to be actually perfect.
        dt = perfectDt
      else
        dt = math.min(dt, 1 / 20) -- In case the game is lagging, let it lag (but let the game think it's at least 20 FPS). Not clamping dt may put us in a situation where we can't do all required ticks in time and the game will more or less freeze.
      end
    end

    -- Update game.
    timeAccumulator = timeAccumulator + dt
    local didUpdate = false

    while timeAccumulator >= tick.rate do
      timeAccumulator = timeAccumulator - tick.rate
      didUpdate = true

      -- local updateStartTime = love.timer.getTime()
      love.update(tick.rate)
      -- love.timer.sleep(20*(love.timer.getTime()-updateStartTime)) -- DEBUG: Emulate a slower computer.
    end

    -- Draw game.
    if didUpdate then
      love.graphics.origin()
      love.graphics.clear(love.graphics.getBackgroundColor())
      love.draw()
      love.graphics.present()
    end

    if not (flags.vsync > 0 and didUpdate) then
      love.timer.sleep(.001) -- Not needed if vsync is on and we called present(), as present() will have slept for us.
    end
  end
end

Sebab saya melakukan ini ialah supaya saya boleh menganggap ia sentiasa berjalan pada 60 FPS, dan tiada perkara pelik berlaku jika seseorang mempunyai kad grafik yang mencapai 10000 FPS. Dengan teknik ini ia kekal stabil ‘sentiasa’.

Deltatime

Dalam hampir semua yang bergerak, saya menggunakan deltatime supaya pergerakan tidak lari daripada penyegerakan.

---Move point to destination
---@param dt number DeltaTime
---@return integer? x,integer? y,boolean? stopped
function Move:updateToDestination(dt)
    if self.vDestination == nil then
        return
    end

    local dist = self.vPosition:dist(self.vDestination)
    local step = self.speed * dt

Vsync

Kebanyakan monitor juga mempunyai pilihan vsync, yang bagaimanapun telah saya hidupkan dalam Love2d.

t.window.vsync = 1 -- Vertical sync mode (number)

Akhir kata

Saya belum 100% pasti sama ada apa yang saya fikirkan di sini sepadan dengan praktiknya. Saya hanya akan tahu selepas beberapa ujian dibuat pada pelbagai komputer dan peranti. Saya rasa ia tidak akan pernah betul sepenuhnya untuk semua sistem. Sudah tentu ada juga komputer yang terlalu perlahan sehingga permainan tidak berjalan dengan optimum. Buat masa ini saya sedang bereksperimen untuk memaparkan amaran dalam keadaan begitu.

    local checkFpsSeconds = .1
    local minimalFpsNeeded = 55
    local warningAfterSeconds = 4.0
    local startLoadingTime = love.timer.getTime()
    self.tick =
        Tick.recur(
        function()
            local fps = love.timer.getFPS()

            if tonumber(love.timer.getTime() - startLoadingTime) > warningAfterSeconds then
                loadingScreen.textObj:setText("You need " .. tostring(minimalFpsNeeded - fps) .. " FPS more.")
            end

            if fps > minimalFpsNeeded then
                self.tick:stop()

                -- everything is fast
            end
        end,
        checkFpsSeconds
    )