Skip to content

Instantly share code, notes, and snippets.

@wolf81
Last active May 8, 2021 15:10
Show Gist options
  • Select an option

  • Save wolf81/1d8bd82acf088a8820751d0c2dc4cdb9 to your computer and use it in GitHub Desktop.

Select an option

Save wolf81/1d8bd82acf088a8820751d0c2dc4cdb9 to your computer and use it in GitHub Desktop.
fixed timestep, state interpolation, client-side prediction, apply player input without lag
local game = Class {}
-- the index of the current player, which is always 1, regardless of how many clients are in array
local CURRENT_CLIENT_INDEX = 1
local TICK_RATE = 1/60
local function exitTo(game, nextState) --[[ exit to other scenes ]] end
function game:init()
Scene.init(self, nil, textures['background-game'])
self.hud = Hud(0, HUD_Y)
self.time = 0
end
--[[
The following cases are possible when entering this state:
- human vs cpu => 1 server; 2 clients; index 1 is current player; index 2 is CPU player
- human (server) vs human (client) => 1 server; 1 client; index 1 is current player
- human (client) vs human (server) => no server; 1 client; index 1 is current player
]]
function game:enter(previous, server, clients)
self.server = server
self.clients = clients
self.state = self.clients[CURRENT_CLIENT_INDEX].state
self.clients[CURRENT_CLIENT_INDEX].disconnected = function()
if self.state.sc == nil then exitTo(self, Disconnect {}) end
end
end
function game:update(dt)
Scene.update(self, dt)
-- FIX TIMESTEP TO 60 FPS
self.time = self.time + dt
while self.time >= TICK_RATE do
self.time = self.time - TICK_RATE
-- GET STATE FROM CLIENT
local state = self.clients[CURRENT_CLIENT_INDEX].state
-- APPLY PLAYER INPUT IMMEDIATELY TO STATE, DON'T WAIT FOR SERVER TO PROCESS
local paddleId = self.clients[CURRENT_CLIENT_INDEX].paddleId
if paddleId ~= nil then
local paddle = state.paddles[paddleId]
local direction = self.clients[CURRENT_CLIENT_INDEX].control.direction
state:paddleMove(paddleId, direction)
end
-- UPDATE STATE (CLIENT-SIDE PREDICTION)
state:update(dt)
--[[
STATE INTERPOLATION (disabled)
-- if the time difference between current and next state is small,
interpolate position of entities
local stateDt = math.abs(self.state.time - state.time)
if stateDt < (TICK_RATE * 2) then
for id, lastEntity in pairs(self.state.entities) do
local entity = state.entities[id]
if entity ~= nil and entity:getType() ~= ENTITY_TYPE_BLOCK then
entity.position.x = lume.lerp(lastEntity.position.x, entity.position.x, stateDt / TICK_RATE)
entity.position.y = lume.lerp(lastEntity.position.y, entity.position.y, stateDt / TICK_RATE)
end
end
end
]]
if self.server ~= nil then self.server:update(TICK_RATE) end
for _, client in ipairs(self.clients) do client:update(TICK_RATE) end
self.state = state
end
end
function game:draw()
Scene.draw(self)
-- render code here
end
function game:keypressed(key, code)
self.clients[CURRENT_CLIENT_INDEX]:keypressed(key, code)
end
function game:keyreleased(key, code)
self.clients[CURRENT_CLIENT_INDEX]:keyreleased(key, code)
if key == 'escape' then exitTo(self, Menu {}) end
end
function game:quit()
if self.server ~= nil then self.server:stop() end
self.clients[CURRENT_CLIENT_INDEX]:disconnect()
end
return game
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment