Devblog 1.4: Monkey Fight - Scoreboard, Sounds, Lots of Polishing

Apologies for taking so long on this update, all you loyal Monkey Fight fans.
Since the last post, I've added a scoreboard, sound effects synced across the network, username tags above players' heads, and did I mention ragdolls last post? Don't think so.

Firstly, username tags were a little tough to set up. I kept running into problems since the username was stored in static file clientside, and not only did I have to figure out how to send that to the server, but I had to sync all non-local nametags on the clientside so they always faced the local player's camera. This proved especially difficult because when a player respawned or a new player joined, that  new nametag would have to be updated to look at the local player. (Now that I think about it, I probably could have done that with some kind of local player singleton...) It ended up working fine, though. Usernames float above the players' heads and are slightly transparent. I had to jump through a couple hoops to get the usernames to be hidden behind walls; by default, Unity's 3D text objects render on top of everything else (even though they're classified as 3D objects), and I ended up having to find a shader online that hides the text behind walls. And, since that shader couldn't be applied to the default font material, I had to download a new font for the game and make a font texture for that.

As you can see in the title screen, I changed all the fonts to this new font. I quite like it. 
The title screen with the new font. Notice I also gave the buttons a nice banana gradient.
I also made the player ragdoll on death. How, you ask? I used the ragdoll wizard in unity - you just click 3D object -> ragdoll, and drag the bones into the right places. I kinda had to estimate with the bones, since the monkey asset doesn't really seem to follow humanoid bone standards. The resultant ragdoll definitely works, but certain parts of the model jut out and stick and drag. Better than nothing, I suppose; given that I used a non-humanoid model, I guess I lucked out getting a somewhat-stabled ragdoll.

I added little green text when you pick up a health or ammo pickup (oh, I also added health and ammo pickups), and damage text that shows up clientside when you hit an enemy with a bullet. I also added a red vignette that shows up on the edges of the screen when you take damage. Hit and on-hit feedback really add a lot to the shooter experience.

You know what else adds a lot to the shooter experience? Sounds! I set up a sound system that tries its damnedest to sync sounds over the network, and that is something I never want to experience again but probably will anyway. I originally tried to sync sound via the animator, i.e. sounds were called locally by the animations, since the animators are already synced. This was a terrible idea, since it turns out the networkanimator component really just doesn't sync most of the time. If you fire too fast, or interrupt an animation with another animation (interruption is necessary for a lot of animations, so this is a big issue...) it just doesn't sync to the client. I ended up hard-coding the sounds into the player controller script, which works, though it doesn't totally sync up to the network. That makes me wonder whether I could hard-code the animations into the playercontroller...? Hmmmm...

Last but (certainly) not least was the scoreboard. If you hit tab, you can see all the connected players along with their kills and deaths. This was a nightmare to implement as well. Want to know something funny? Something hilarious? If you have a synclist (a list that, when changed from the server, changes for all the clients), and you change it, and in the next line you call a clientRPC function (a function that, when called from the server, calls on each of the clients), the clientRPC function will call on the clients BEFORE the synclist is updated for the clients. What I was doing originally was updated the synclist with update information  (new player connection, incrementing a player's kills or deaths, etc.) then calling a clientRPC function to update the local scoreboard for every player. After spending an eternity trying to figure out the problem, I spent a second eternity devising the Worst Workaround Ever, which was so shamefully complicated I don't even want to explain it. Fortunately, around the time I'd finished the workaround, I thought "hey, can't you put a hook on sync variables?" (a hook a function that is automatically called on every client whenever a syncvar is changed, it's pretty handy.) but you literally can't put a hook on a synclist. Then I thought, "couldn't I just make a dummy syncvar, sets its hook to the scoreboard update function, and change it whenever I change the synclist?" and boom, it sort of worked. Sometimes it just didn't, though, and it took a third eternity to discover that, because the dummy variable I was using was a boolean and I was toggling it every time I wanted to update, if I toggled it in two separate server calls in one frame (which happened often enough to be a problem) it would keep the same value and wouldn't register any change, thus not calling the hook. I changed it to an int and it works like a charm.

Oh yeah, regarding bullets, I just used raycasts. Seems to be the best option since I am by no means going for realism in this game.

Here's some test footage:


That's pretty much it for now, nothing super exciting. I will soon be working on adding other weapons, other gamemodes (...at least one other gamemode - and hopefully teams) an options menu, and a "chat" (more of a server message relay that tells you who killed who and stuff.) Until next time.

Comments