CharacterControl in networked game

I’m trying to make a simple networked game where each player has an avatar, can move around, and drive a car.

Currently, it works great when I run it “offline” (not connected to server). Now, I’m trying to make it work with several remote players, and I’ve hit a wall.

The plan is for the game to have a classic client/server architecture, with the clients sending information about attempted movement to the server, which executes the movement and sends back correct values for positioning et cetera. The server then has all the correct data, and the clients are fed this information.

I am using CharacterControl (yes, I know it’s deprecated but bear with me) and CarControl for moving the avatars/cars. While developing the networked side, I am currently not controlling the avatar locally at all, instead, I am sending orders to the server to adjust my walking/facing direction (which CharacterControl then uses).

Information about all avatars’ position and facing direction is continuously sent to clients, and while correcting the positioning of the avatars works fine, correcting the facing direction is not.

When a packet is received by a client, I am using CharacterControl’s warp (yes I know that continuous snap convergence is not optimal, but it’s just to start out with) to adjust to correct position, and simply changing CharacterControl’s setViewDirection to adjust the facing direction.

The results of this is that the movement is somewhat smooth, but the facing direction updates super rarely! These are just two separate lines of code that are executed right after eachother. Calling warp() instantly changes the position, but calling setViewDirection does not change facing direction until 1-5 seconds after.

After trying to make this work for a long time - I am stuck. I feel like using CharacterControl and CarControl works really well for how I want the game to look and feel, but it seems really hard to use them in a networked game.

If anyone has any tips on how to approach this problem or alternative solutions for a networked game of the type I’m describing, I would be very happy to hear from you!

Have you tried using the ‘BetterCharacterControl’ object instead of the regular ‘CharacterControl’? I had trouble with the regular one a long time ago and only have experience using BetterCharacterControl in a networking scenario. It sounds like what you are attempting should work from the information you’ve provided, so maybe switching to the BetterCharacterControl could help, otherwise you could post the code where you send and receive the view direction

I just finished doing something very similar for my project. I continuously send the movement input as well as the view direction 20-30 times per second in an unreliable message, and then call .setViewDirection() with the BetterCharacterControl when received

I didn’t actually try it thoroughly with BetterCharacterControl as I assumed it would have the same issues. If it actually worked well for you then I will definitely try it!

Thanks a ton for your response!

Tried it with BetterCharacterControl and was faced with the same exact problem! The actual facing direction of the avatar does not get changed until several seconds after setViewDirection() is called.

1 Like

Are you calling warp as frequently as you set the view direction? If so, it could help to add a distance threshold that needs met before each warp to make sure that’s not causing any issues. I only call warp on a character control if a Player’s location on the server compared to the client is over a certain distance.

Otherwise it could help to do a System.out.println(viewDirection) in the update loop to compare and see if the problem’s on the server or the client. From my recent experience adding networking to a game that started as single player project, I had to make a lot of changes on the clientside to dumb down certain functions so they can listen to the server properly. And since you only see a visual output on the client, it can take some troubleshooting to determine if the server is sending wrong information, or if the clients are using it incorrectly once received.

I did actually do that prior to writing this post! I printed out all view direction values received at the client, and they change instantly when I attempt to change the direction of my character. So the viewdirection values I assign with setViewDirection are accurate, but the visual effect of changing the direction happens once every 5 seconds or so, even though I assign different view directions with setViewDirections about 20 times per second.

If the view direction value being received on the client is correct, then it sounds like something else in your client’s code could be changing the view direction after you’ve called .setViewDir() with the correct value from the server.

Why are you using CharacterControl on the client if the server is handling your movement for you?

Or do you let the clients “cheat” and just send you their position?

Note: there is already a full functional “server authoritative” example using SimEthereal which already handles interpolation, etc…

One that doesn’t use an ES:

And one that does:

SimEthereal can save you lots of time as it already does zone management, tight packing of data, etc… It can pack a LOT of object updates into a packet that will be smaller than a standard MTU and does all of this using a reliable UDP protocol.

Good question. The real answer is that I started out with using CharacterControl before I started to make the game networked, and the camera I use followed the character perfectly when turning. When i try to rotate the character and camera to the viewDirection sent by the server, I couldn’t get it to work.

Currently I have a character class that “has a” CharacterControl field, and this character class has a CameraNode as it’s child node. The setup of the CameraNode looks as following:

      camNode = new CameraNode("CamNode", sapp.getCamera());
      camNode.lookAt(model.getLocalTranslation(), Vector3f.UNIT_Y);

The character object is attached to the root node, and if I simply try to rotate the character node, then seemingly nothing happens. If i try to rotate the character model (the visual avatar), then it rotates, but the camera attached to the character object does not change.

I don’t know how to rotate the character model and have the camera follow it perfectly without using character control.

I am very new to JME and I’m making this project for a course in networked virtual environments, so forgive my complete lack of knowledge!

Well, CameraNode should work without character control if you’ve attached it right. The two are totally separate and unrelated things.

Edit: is what you really want a chase camera? Like:

You could cast the characterModel to a Node and try to attach the camNode to that to see if rotating the visual avatar still works that way. That won’t solve the reason that the characterNode isn’t rotating, but it might help to figure out what’s going on.

After your suggestions I removed characterControl from the client side, and I’m trying to update the characters position by calling setLocalTranslation, but it does not update it. I can still change the location of the model, but If i try to change the position of the character (which extends Node), it gets reset immediately. I even tried adding the following code to the update loop:

    System.out.println("BEFORE: " + game.characters.get(0).getLocalTranslation());
    game.characters.get(0).setLocalTranslation(-10f, 5f, 3f);
    System.out.println("AFTER: " + game.characters.get(0).getLocalTranslation());

The output is

BEFORE: (-5.0329814, 1.4399343, -0.04748598)
AFTER: (-10.0, 5.0, 3.0)

on every single iteration!

And after each iteration of the loop, the local translation of the character is reset back to where it started (the -10, 5, 3 does not stick). One might think I have some code somewhere resetting the position, but I cannot find it whatsoever. This might seem like the most ridiculous problem ever, but I am very bad at this. I’m very thankful for your help!

Based on what you’re describing, it sounds like the client player is being warped back to it’s server location.

Are you using .setWorldTranslation() to move any spatials that have a control attached? If you try to set the location of a Node/Spatial that has a character control attached to it, then the character control’s location will override any changes to the assocaited Spatial, and calling .move() or .setLocalTranslation() won’t do anything.

I could be wrong, but that would explain why you are able to get things working if you use the child Spatial, as opposed to the parent Node that could potentially be associated with a control still.

Is “Character” your class?

It sounds like it’s resetting the position whatever the case is. Calling setLocalTranslation() will 1000000% move the node unless you have something else moving it back.

You can verify this by adding some System.out.println() calls around and see what’s what. Or run in a debugger.

I was about to say the same, I guess OP still has a rigid body or some such on the client side and physics overrides position.