How to provide a responsive user experience

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!

9 Likes

Something I’m confused about here.

To my understanding, this code could easily be exploited by exploiters by modifying or straight-up removing the if-statement, no? I always operate on the principle that the player has absolute control the modify everything on the client. Am I mistaken here?

Otherwise, why is this code snippet better than the server code? The code snippet on the server can’t be abused under any circumstances, correct?

1 Like

Good question, I had that as an example of a bad way to do it. My fear is not the client managing itself, I know that exploiters ultimately have control there, that can happen as much as you’d like. InvokeServer is there to notify the server of everything that’s happening on the client, because otherwise you wouldn’t be able to deal damage even if you shot, due to:

   -- updates server-based ammo count

Whenever a player tries to shoot, their server-based ammo count is checked.

The server-only code is terrible due to what I’ve stated in the article, the player experiences slow, unresponsive controls while waiting for (Also valid for #2):

  • The RemoteFunction to be Fired.
  • The server to Process the request.
  • The Data being returned to the RemoteFunction.

On the client, you shouldn’t care what the potential exploiter is doing to the game, let them. Just provide a good experience to everyone else.

This article should clear things up: Documentation - Roblox Creator Hub

Any client code is accessible to the client, but it’s only the effects and visuals that we’re passing on to the client, (animations, updating UI) not any logic. This will not affect other players, only passing on the right tasks to the best places.

I don’t see why an exploiter would want to ruin their own experience by changing up the visuals, which is why visuals are comepletely okay to pass on to the client.

That brings us to the second point. If you’ve seen a weapon system where the client waits for the server to render the visuals, there’s a noticable delay between the firing and the bullet appearing. For a fast-paced shooter game, you’d want immediate visual feedback. That’s the bad user experience OP posted this to handle.

Again, the solution would be to let the client render the visuals immediately while the server handles the gameplay (kills, deaths, ammo decrease).


tl;dr
As server-client communication will inevitably have a delay, which is horrible on the user experience:

  • Let clients render the effects, and
  • Let the server handle the logic.

The client can safely determine how their own effects are rendered without affecting the rest of the game, so this is safe.

2 Likes

@g_captain
My apologies, looks like I got confused. I had read the code snippet I quoted and thought “He’s processing damage on the client?”, thinking it would reflect onto the server. Upon further hindsight, even if that were the case, it wouldn’t cause any issue for the gameplay since it’s on the client side. Don’t know exactly what I was thinking when I posted it, but I was clearly mistaken.

I understand your post well and it’s something that I’d recommend new developers look at, but I was just misinterpreting the code snippet and became confused.

My apologies.

1 Like