I think we’ve all experienced trying to play a game and encountering varied amounts of lag, leading sometimes to un-responsiveness of controls and/or interactions. As an EU Player, my latency in some cases is horrendous, and I shouldn’t need to wait for 3 seconds for an animation to play.
Sometimes from a game design aspect, lag visualization is important, the player should know when their client is behind, especially in FPSes and other fast-paced PvP games. The issue is however, developers usually don’t think about this, and just leave it in as they either didn’t think of fixing it, or didn’t know how to.
All reactions should be instant, whether it’s an animation starting, a button turning a certain color, or anything related to PvE, or even in a PvP game, sure I know that the server has to fact-check my attack, but that doesn’t mean it should hinder the response, if the server detects hacking just don’t deal the damage, don’t let it hinder gameplay.
local function reload()
-- Check whether reload is legal LOCALLY
-- Play Animations
local newAmmo = reloader:InvokeServer(); -- Server Checks: Whether Reload is legal, which gun the player has and how much ammo to grant,
-- updates server-based ammo count
-- Wait for animations finished playing if not done yet
-- set ammo indicators to newAmmo (The delay incurred for the ammo request isn't so astronomically restricting unless in a fast-paced shooter setting, in which case even initial ammo refunding could be handled locally).
end
local cas = game:GetService("ContextActionService");
cas:BindAction("Reload", reload, Enum.KeyCode.R)
This avoids the hindrance of animations playing and as such flow and responsiveness, it still checks rules for game law abiding players, only breaking for people who do with your game what you do not expect (They shouldn’t expect a good experience anyways). The only issue dragged along by this is replication, simply Fire all the other clients or simply fire all the clients during server verification, including a name parameter for clients to filter through and decide whether to play the animation on given person.
The common alternative would’ve been:
local function reload()
local newAmmo, legal = reloader:InvokeServer()
if legal then
-- Play Animations
-- Update Counters
-- /shrug/
end
end
This is the wrong way to do it.
local function reload()
-- Do everything on the server
end
This is an even worse way of doing it.
Let’s get back on topic, so far we’ve spoken about control responsiveness, but how about your environment? A common misconception that I hear is that clients are evil, and that would be true in a Filtering Disabled setting, but in this case, having a client control it’s own environment is no biggie. To have a more responsive environment:
- If environmental effects are going on, don’t waste the user’s time trying to show the event unfolding in server-time. Tell the client what to do and it will start doing it without any objections. So instead of having a nuke play on the server, let the client play your beautiful effect work with all its intended value.
- A gunshot is a quick motion of a bullet, not a jittery motion of a poor internet connection, make sure your audience knows that fact.
- Passive Environment Effects are even worse, a tree swaying in the wind should not be misteriously driven by some avatar-esque forces, rather smoothly moved locally (Having a tree move globally would be quite weird? haha, joke.).
Additionally, it’s not a myth that the server isn’t actually so powerful, it wouldn’t be able to handle as much as you might expect. So moral of the story:
The server is not your best friend, it’s like your ex, only use it for things you’re unsure about.
(And for real, I hope this rant has inspired you to create a good and responsive user experience)
Have fun developing!