Working on a little side project to learn unity networking in depth here and while things seem to be running ok so far (knock knock), I have a couple questions some seasoned network folks might have some insights on. The server is authoritative, currently no client side prediction, just trying to get the core bits down.
I’m currently handling movement by just passing the two input vectors to the server, this works wonderfully and with it i’m hitting ~300-400 KBps. My camera is a TPS style camera however, so the issue is that I need to track rotation as well. I’m thinking that the only time rotation is really needed is the user is moving (or performing some other action that requires it). I’m OK with the persons rotation not being up to date on all clients for now visually, but i still need to track is because as the user rotates the camera and moves forward, it will change the direction of movement.
So using this scheme, I take the euler of the y-axis (float) and fire that across using an RPC. I’ve tried using a networkView for a second object and OnSerializeView(), but in all cases using my current algorithm its around ~2-3000 KBps (peak) useage. That’s a nasty cost to handle rotation when moving.
To get an idea of movement feel i’m looking for, heres a web player: http://logicwell.com/game/WebPlayer.html, choose “REMOTE” and WSAD + Mouse for moving around. Yes, I forgot to put directional lighting in there - been a little focused on the netcode ;).
So a few questions:
Assuming typical cable/wireless speeds, is a 3.5KBps SEND rate per client reasonable? Or do I need to do a few more passes on this and pare it down to something more reasonable?
Is there a better way to track a players bearing I’m missing (probably!) - if so, any thoughts on another approach that will reduce network overhead while keeping movement smooth would be appreciated, ideally I’d like to keep it down to ~1KBps (peak) load, assuming 3.5KBps is not optimal.
I would think passing a Matrix4, which is the universal storage type for all transform information, would be the most efficient. You can pass both rotation and position (and scale) in one struct, then unpack it and set position and rotation.
Sounds good, unfortunately i’m dealing with an observed NetworkView for the PC, and then i have a separate object on the server to track the camera rotations on the server. I wonder if i could calculate some kind of movement scale that takes into account velocities and just assume the player is facing that direction (until a gunshot etc).
So, right now I track Input.GetAxisRaw(“Horizontal”) and Input.GetAxisRaw(“Vertical”), then i fire an RPC to inform the server which direction my movement is in, somthing like:
But horiz and vertical are always, -1f, 0f or 1f … perhaps theres a way to take the camera’s rotation into account and calculate a more granular horiz/vertical pair that represents nt just the cardinal directions but in between as well?
This could not be further form the truth, a matrix is 4 * 4 * 4 = 64 bytes. You can easily compress rotation (along one axis) and position on three axes 1 + 2 + 2 + 2 = 7 bytes.
Right a float is 32 bits which would equal 4 bytes, so I think I have the euler thing right. Real question I guess is how to minimize overhead, I guess it just comes down to limiting the sends to X times per second instead of just blasting it across the wire every frame (potentially) … I’ll play with it and see what works best.
Twitch based games (counter-strike, etc.) usually do something like this:
Run the simulation at 60Hz (~16.66ms steps), read input every step and buffer it, when you have 2-3 commands, send them as one over to the server. Server runs simulation at the same speed (60Hz) and sends state to the client at 15-30Hz, so every 33-66ms, the rest you have to handle on the client using interpolation and extrapolation.
Using RPCs for sending this state is very bad, as it causes a significant overhead (both in processing on sender/receiver and in bandwidth). You also have to account for the 28 byte UDP overhead when sending state, so sending a 4 byte float/int will cost you 32 bytes, but sending 2 x 4 floats will only cost you 36 bytes.
You can compress data by turning a float angle into a byte, it limits you to 256 possible angles per axis, but it’s in 99.99% of the cases good enough.
You say you reached 300-400kbps for position data, this is insanely much. The position data for my game (sent every 50ms, so about 20Hz) consume about 600 bytes/second, or 0.6kbps. I send X and Z position, and only rotate on one axis Y, I compress the X and Z into halfs (2 bytes each) and the rotation into one byte, giving me a total packet size of 2 + 2 + 1 + 28 (UDP overhead) = 33 bytes * 20 (timer per second) = 660bytes per second, compare that to your 300,000-400,000 bytes/second.
Good point! I sorta troll here because I know I want to know this stuff for a future game. Eular/ axis angles make sense if you aren’t interpolating anyway. I should have thought of that. On a conceptual level, all 3D apps display Eular and process quats. This would be the same. Send an axis or three and process later. Cheers.
I did use words like “I would think”. I don’t think i posted misinformation. My post was accurate, it just depends on what kind of efficiency you mean: bits or data completeness. In this case, I missed the point and I thanked you for your insight. No need to reply here.
Negative. I’m not trying to be rude but it’s better if you just ask the questions you have instead of posting miss-information in hope of being corrected!.. I’m being cheeky here, but its true. You can interpolate between two rotations in Eulars but you won’t like the gimbol lock. I may not be a networking genus, but I have been in CG since fractal noise was how you made stuff better… You just never know when your knowlege will be incomplete. This is one of the reasons I have always participated in forums. Everyone knows something better than I do.
You can interpolate between euler angles, but if you try to interpolate on all three axises you will get gimbal lock yes, this however - is not related to the interpolation itself but just the nature of euler angles, you know all this of course, just clarifying for the next person reading!
I’m running at between 0.3KBps - 3.5KBps (or 300-3,500 bytes per second depending on circumstances), got my UoM mixed up on the first paragraph, probably was obvious when reading further down but perhaps not.
Not sure what the overhead for an RPC call in C#/Unity3D framework is - anyone have some metrics?
UDP costs are constant, i’m not worried about the constant overhead incurred from the transport layer, more about how to reduce my overhead a bit and more importantly, finding a way to optimize when I send data over. Following your example, I should be able to get way down on network traffic, if we assume the 20 sends per second and bringing the angle down to a byte there’s ~580 Bps - i’m also just rotating on the y-axis so that’s similar. I’m not sending movement for horiz/vert every pulse (or 20 times per second), instead my approach is to send delta’s so that only when the movement changes do we send any information at all. So typically I’m seeing ~1 send every 1-2s, or to put in terms of kbps, 16+28=44 Bps (assuming once per second).
One thing I’m curious about, it sounds like you’re using a udp library or c# networking primitives - is that what you mean instead of using an RPC? Or are you using a network view that observes a script to do this?
Cool, just sending over a new state every 20 frames made a huge difference, with some ‘slerping’ on the client side to smooth things out it looks pretty good network usage wise, tiny grab of net stats while running the game here: http://grab.by/aYfz . Guessing I could really slim that down going to raw udp library or something like that, but this seems pretty reasonable to me - captures full movement (network view sync’d PC) + y-axis rotations via RPC.
One thing that makes me a bit sad regarding Unity3D’s approach to OnSerializeView() is that it doesn’t seem to support bi-directional communication? Is there a way to enable this or does it always just assign owner as “writer” and clients as “readers”? Failing that, is there a way to get a raw socket stream just using unity3d/raknet to allow the clients to push data (i.e. networkView.GetStream().Write( … ) )?
Anyhow, thanks for the feedback all - looks WAY more reasonable on bandwidth usage now :)!
Ah, yes that is a bit less, even though peaking at 3500 bytes is quite a lot.
Depending on implementation the overhead is probably something around 4-30 bytes, a bit depending on if they send RPCs as strings or identifier.
Well, yes it’s constant. But it’s a lot more expensive to send 120 packages per second containing 4 bytes of data, then to group them up into clumps of three and send 40 packages per second of 12 bytes.
This is a very fragile approach, since lag is not constant this will cause your character to end up in different positions on the server and on the client, eventually.
I’ve built my own network server on-top of the UDP stack in .NET
Sending state on frame-based intervals will not work, what if my FPS drops to 5, it will take me 4 seconds to get a new state sent. You need to send it based on time.
I think this is how unitys networking is built, it’s simple and not suitable for everyone, live with it or drop it
Frame rate isn’t something I’ll be sticking with, just makes for an easy way to test changes to my send rate in a closed environment.
Since UDP is constant and for a given scenario i’m comparing apples to apples (i.e. i’m not comparing 7 byte payload over 10 sends per second vs 9 byte payload over 30 sends per second). Which means in the context of comparing different approaches UDP overhead is something to factor out of the equation.
I may do likewise in building a server, but so far things seem to be ok working around unitys framework. I can live with that, my last block of text was simply a question in case there are some aspects to the networking api in unity i’m missing. Going to tough it out with unity networking here for this first game and evaluate from there, i’m definitely leaning towards a homebrew udp server approach here myself though lol.