Awesome, glad you were able to get a new computer! And thanks, development is progressing really well, I'm super excited about where it's been going lately.
Recent community posts
Awesome, glad you were able to get a new computer! And thanks, development is progressing really well, I'm super excited about where it's been going lately.
New map WIP
I decided the maximum number of players I want to support is 12, so the game can support 4 teams of 3 or 3 teams of 4. In light of that, I started working on a new map with enough room for so many players. This will be the largest map yet.
It's still a work in progress, but sometimes I'm tempted to leave parts purposely unfinished.
Some friends were kind enough to play a few local matches of Deceiver with me, and even though most of them were familiar with shooters, they were still confused. It didn't help that the game was in the middle of some of the design overhaul changes described below.
What I should have done was set the game to unlock all the abilities and disable the advanced stuff so we could all just shoot each other. The game does offer those settings, but I didn't want to sit around tweaking settings while they waited.
This is related to another problem I have with the servers online. Playtesters have created 77 different servers with different settings. It's impossible to see at a glance what the rules are for each server. Also, when creating a server, it's overwhelming to see all these options, and it encourages you to tweak all of them, which is usually not a great idea.
Presets are the solution I came up with. When you create a server, you can choose "Standard", "Arcade", or "Custom". Now you can see at a glance what to expect from a server. Arcade unlocks all abilities, and Custom lets you go crazy with any setting you want. The nice part is, these presets work with all three game modes. I can easily envision more presets like "Snipers Only", "No Shields", "Fast Cooldowns", etc.
Ever played a game where the cursor felt laggy and unresponsive? That was my game until just recently. Turns out, computers use a separate low-latency hardware path to render the cursor. I switched from my custom OpenGL cursor to a hardware cursor. Unfortunately, that meant my vector-based anti-aliased cursor mesh wouldn't work anymore.
I spent WAY too much time trying to pixel art a cool looking cursor, and ended up settling for a plain ol' crosshair. I may switch to the default system cursor, but I think this helps everything fit together better.
I made a number of sweeping design changes. Below is a list, with the accompanying reasoning behind each change.
- Remove ticket system from Assault. Assault is an attack/defend mode, and previously the defenders won by either running the clock out or exhausting the attackers' tickets (respawns). Now the ticket limit is gone. Reasoning: Originally, both teams actually had limited tickets, which made the game type devolve into just regular ol' team deathmatch. With only the attackers limited by tickets, it still meant the defenders were basically playing deathmatch. Receiving energy and deterring the enemy is enough of a reward for killing an enemy player, there's no need to attach a win condition to it. Also, the ticket system had to scale based on the number of players, so if a player joined or left mid-match, the number of tickets would change. Confusing UX.
- Remove spawn selection. Previously you could spawn from any battery you captured, and by default you spawned at the battery nearest to where you last died. This is all gone now. You only have one spawn point. Reasoning: it's important to have downtime with lower intensity gameplay. Also, the decision of where to spawn was never interesting, and mostly just served to confuse newer players.
- Make turrets auto-heal and decrease player damage against them. Reasoning: Once again, over the past year or so I forgot that the Assault game mode is basically a MOBA. Players are not supposed to fight turrets themselves. It's not interesting or fun. So now, it's mostly worthless to fight turrets.
- Make ability re-purchases cost nothing. Once you've purchased an ability, you can now replace it with another one and later switch back to it free of charge. Previously you had to purchase it all over again. Reasoning: I want to encourage more diverse usage of different abilities. Also it feels more friendly to the player.
- Make all abilities free to use. Previously, you would pay energy to buy an ability, and then some abilities also required energy to use. Now, energy is only used to buy abilities. Reasoning: it was always impossible to balance prices for moment-to-moment combat purchases against prices for longer time-frame purchases. If a high level ability costs 1000 energy to unlock, and a low-level ability costs 20 energy to use, that means you can spam the low-level ability 50 times. Also, you never got a sense of progression because you're always spending your progression currency on combat. The number never really goes up. Now the abilities are easy to balance: I just put separate cooldowns on them. And your progression is much easier to see, since your energy always goes up until you spend it on an ability.
- Change the minion ability to a passive spawn rate boost. Previously you could use it to spawn minions at will. Now it just takes up an ability slot and increases the rate at which minions spawn from your captured batteries. Reasoning: the minion ability previously required no strategy. You could just show up at an enemy base and plop down 10 minions in as many seconds. Now you are more encouraged to capture and protect batteries, since they spawn minions.
- Change DM and CTF modes to no longer spawn minions by default. Reasoning: the DM and CTF modes, which should be straightforward for anyone familiar with shooters, confused my friends who are familiar with shooters. These are the modes I use to introduce the game to new players, since there's much less to explain. But people are still confused, and one of the major confusion points is "who are all these people walking around?" It just makes the game feel unfocused and chaotic. I still think minions are a blast, and people love shooting them, so I'm keeping the minion boost ability in for these game modes. Instead of boosting the minion spawn rate, it enables them spawning at all.
Rectifiers do two things: they heal stuff, and they create a field which provides stealth for your drone and alerts you to the presence of enemy drones.
Previously, rectifiers were pretty ineffective because they were easily destroyed. Now they're tougher because they're stealthed by default, so enemies can't see them unless they plop down a rectifier of their own.
Unfortunately, the healing particle effect I was using completely gave away the rectifier's position. It showed a trail of particles going from the rectifier to the object being healed. I changed the effect to use a sphere mesh instead. Had to modify the instancing system to allow per-instance colors.
You can now launch a grenade at a friendly minion and it will attach to the minion's back, waiting for a hapless victim to wander near. While implementing this feature, I accidentally turned the minions into giant walking grenades:
Initial Discord integration is done. You can now see what people are doing in-game.
And if you're in a game, you can invite a person or a whole channel to play with you, even if you're in a private server.
The invite updates the number of players in the server in real-time.
I also upgraded the bot to place LFG players in a special role and mention them specifically when people are playing.
I'm really impressed by the quality of Discord's software. Their APIs are well-designed. Everything they do is a pleasure to work with.
I got an opportunity to talk to Michael Cox, marketing guy from Crows Crows Crows. He's @DevMicco on Twitter, he has DMs open. Great guy with super helpful advice.
He told me to release the trailer, release the demo, push PR, everything, but don't launch Kickstarter. Instead funnel people into Discord and mailing lists. then launch Kickstarter when there's enough people to make it succeed on the first day.
That makes a lot of sense to me, so that's tentatively the plan I'm going with now.
In the meantime, we had another playtest session on Thursday and I came away with a bunch of balance tweaks and bug fixes, which I was able to knock out yesterday.
The most interesting news is, the game now has a Discord bot.
This thing allows you to indicate when you are available to play, and if anyone else is online or marked available during that time, you'll get a
notification. It also periodically blasts out statistics about who's playing.
I've been using vyte.in to organize playtest sessions, and it hasn't been great, so hopefully this will work better.
Still working on the trailer. Here's a new shot from it:
It's coming together quicker than anticipated. Most of the footage is pretty close to final, and now it's just a matter of getting the audio together.
In the mean time, I realized while watching gameplay footage that the weapons were in desperate need of camera recoil. So I added some.
It's just velocity and acceleration. I overwrite the velocity when the gun fires, then the acceleration (or "gravity") brings it back down. Once the angle gets close to zero, I limit the velocity by a multiple of the current angle, so it slows down instead of slamming back to zero. Relevant code here.
Unfortunately this new feature means I now have to re-shoot several scenes in the trailer.
Working on some scenes for the trailer. Here's Seven and Samsa in "holographic cyberpunk space command center" mode:
He folds up into cyberdoggo form:
And Seven packs up to leave:
The backpack animation was incredibly tricky. It's not perfect, but I'm okay with it. I did it by duplicating the bones that affect the
backpack, then creating constraints that glue the new bones to the original ones. To put the backpack on, I just animate the influence of
these constraints from zero to one.
The visuals were originally inspired by TRON. Ten years ago I started experimenting with that art style, but gave up on it pretty quick. Here's how it looked back then:
Things have changed a bit since then :)
Improved map view
The map view before was too zoomed in to show the whole map at once, and it kept panning the camera around all over the place. Also, the UI was
unclear. I switched to a static camera that overlooks the whole map, and cleaned up the UI:
The cooldown netcode has been a problem for a while. Previously it worked like this. The first time the player performed an action, the server
would add the necessary heat to the cooldown meter, but then instantly subtract the amount of heat that would be dissipated during the network
round-trip time (RTT) between the server and client, to ensure they stayed in sync.
When the client needs to add heat to the cooldown meter, it does so instantly and ignores the cooldown value coming from the server, simulating it locally until enough time has passed for the server to receive a packet and send one back. Then the client overwrites its local heat value with the one from the server. Most of the time, they're perfectly in sync and the handoff isn't noticeable.
However, the server only needs to compensate for lag one time. If you have multiple cooldowns stacking on top of each other, and you subtract the
RTT from each one, you'll get incorrect results. So the server only performed this lag compensation if the heat value was at zero.
The tricky part is, the bolter adds a very small amount of heat to the cooldown meter for each shot fired. It takes less than a third of a
second to dissipate the heat. Turned out, if your ping was high enough (i.e. the RTT was more than 150ms), the server would subtract enough
from the cooldown meter that by the time you fired the bolter again, the heat would be down to zero already, and the cycle would continue. You
could fire the bolter indefinitely.
Long story short, I made the client do lag compensation instead of the server. It takes the current heat value coming in from the server and subtracts the RTT no matter what. Much more simple and robust.
As I've said before, the singleplayer portion of the game is basically Dishonored with no enemies and all you do is climb. Well, I wasn't
joking. The game now has Dishonored's Blink ability as a movement upgrade:
It uses the same cooldown system as the rest of the game, which I like because the cooldown is pretty quick the first two times, and slows down
Triangular minion head
A viewer on Twitch said I should try making the minion's head more triangular. I had no argument against it, since everything else in the game is
triangular. Now all my brand new promotional art is outdated :(
Force field sound obstruction
This was an easy win. I already have audio obstruction/occlusion code, it just wasn't taking force fields into account. It's pretty cool when
someone fires a weapon inside a force field and it sounds muffled and muted.
I switched the UI from teal and pink to yellow and pink. Here's how the main menu looks now.
Seven is the character with the most screen-time in the game. And she's a no-nonsense badass. I knew I needed to dedicate
some time to her.
I started out last week by playing hair stylist. I wanted to take Overwatch's wavy hair and bring it into Deceiver's neon aesthetic.
Next I modeled some cyberpunk combat boots.
Then the rest of her outfit.
The last piece was a military ALICE pack, but I'll get to that later!
Logan whipped up a looping rain sound for me. Previously I've just ramped the volume up and down based on a system that calculates how much rain is falling near you. But this time I wanted to control not only the volume, but also the positioning. So if you're standing under an overhang, the
rain will sound like it's coming from outside.
I already had a grid of raycast results surrounding the camera to drive the particle system, so I just ran some weighted averages on that to figure out where to place the rain sound. I used a Wwise RTPC to set the spread to 100% when the sound is close to the camera, so if there's rain all around you, you'll hear it in both ears.
I spent a day and a half on this crazy voxel-based reverb system. When my level importer builds a level, it first chops the level up into chunks.
Then at the center of each chunk, it queries the AI navigation mesh and collects all the points that are "visible" from that chunk.
Then it rasterizes those points into three "shells", for close range, middle range, and far range geometry. Each shell is sort of like a spherical
black and white bitmap image. If the close range bitmap is 100% black, then that means we're in a pretty tight space.
The importer spits out three coefficients for each voxel chunk, representing the amount of close range, middle range, and far range reverb. After this it runs the whole voxel through a smoothing kernel and writes the result out to a file.
At runtime, whenever a sound plays or a sound emitter moves, it's just a quick lookup to figure out what reverb values to use.
I'd like to write more about this soon. Hopefully there will be another Poor Man's article on it. :)
Adaptive client-side interpolation delay
I watched a talk on Overwatch netcode:
Pretty much the only thing they mentioned that I wasn't doing already was the adaptive interpolation delay. Basically, if you have a reliable
connection (regardless of ping), the game will buffer incoming packets for a shorter amount of time, because it trusts that new packets will
come in on time. This reduces latency.
If your connection is dropping packets left and right, the game will start to jitter. Eventually it will decide to increase the interpolation buffer to smooth over those missing packets. I use a simple scoring algorithm with some basic hysteresis to prevent the client from switching back and forth all the time.
Of course, algorithms often do the wrong thing, so it's important to give people control whenever possible. Hence this menu option:
The character in the store art I've been using dates back to 2014... and it shows.
With Seven's model finally done, I decided to redesign the store art and focus it around her, since she's the most important character.
First I tried just adding her to the existing scene:
This worked okay, but I was never quite satisfied with this color scheme. I tried messing with the lighting.
At this point I started soliciting feedback from some fellow developers, and they all thought it looked confusing and muddy. The foreground and
background bled into each other too much.
I kept messing with the lighting.
Everyone wanted me to switch to cold background, warm foreground, so I tried that:
Eventually I realized the old 2014 model had to go. It wasn't up to par. I also had to spend some time in the Blender compositor to get ambient
occlusion to behave the way I wanted to. By the end I had seven render layers, some of which spit out multiple passes.
At this point I slapped the logo on top and realized it would never work. The logo is surrounded by a dark border, and it didn't stand out against
the dark background at all. So I switched back to a pink background, and also threw in some geometry from a level I've already completed.
You can see I also removed the scan lines from the logo. On their own, they were okay-ish, but I realized they were just too complex and I couldn't make them work with anything else.
I'm still not 100% happy with the colors on Seven; I think she might not stand out enough. But after two whole days of tweaking I think my time is better spent elsewhere. I've never had this much trouble with store art before.
The capsule art stands out pretty well, but I'm still worried about it.
The problem I've had with this game from the beginning is that it's essentially two different games in one, complete with two different art
styles. There's the exploration game with vibrant, feminine colors, and then there's the drone combat game with deep, masculine colors. Which
style do I use for marketing materials?
A fellow dev suggested doing a simple A/B test with Facebook ads, which I actually tried once a few years ago. The sample size for a $50 ad spend (on the order of 50 clicks) seemed to small for me to take action on. I dunno. It might be a good idea.
Hello! It's been a while since the last update. I was out of the loop for a few days recovering from wisdom teeth removal. Also, while the past few weeks have been very productive, it's more like a thousand small improvements rather than two or three big ones. I'm writing a lot of commit messages like this:
Story mode changes
I was able to watch some brand new players get a little lost and confused in the Docks level I posted last time, and have since refined the level design to the point where my sister had no problem finding her way around. Some super dedicated playtesters in our Discord server also found a ton of bugs and exploits which I was happy to patch.
I also decided to include audio logs in the game, to give more time to explore the characters without having to build expensive cutscenes. Other small touches to add life to the Docks include this fire effect which didn't turn out quite right on the first try:
Drone combat tweaks
The active armor effect has been a problem for a while. It was too subtle. Just some sparkles around your drone. I finally did what I should have done months ago:
Back in August I posted about "drone repulsion". Basically, if two drones are crawling around and they bump into each other, the slower one takes damage and goes flying. The intent was to avoid the problem of shooting at a player, just barely missing, and landing anti-climatically right next to them, so close that your shields overlap. Drone repulsion ensures no two drones ever overlap.
The problem is when you're crawling along, suddenly bump into an enemy drone, and your drone goes flying 20 meters without any input on your part. It's jarring, disorienting, and not fun.
So I got rid of it and instead solved the problem of near-misses in a much more obvious way: use a sphere-cast rather than a raycast. Now if you're flying toward an enemy and any part of your shield touches their shield, that counts as a hit for you. Once you land and start crawling, no more drone repulsion.
Can't believe it took me this long to figure this out.
This is Treatment Plant. Actually it's an old map by Ian Cuslidge, but it's been revamped, cleaned up and welded together to accommodate the new rendering system and game types.
Special thanks to Ryan Evans for pointing me in the right direction to get my game recognized by graphics drivers so that it runs on discrete GPUs rather than integrated Intel HD garbage. Here's the code in question:
_declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
__declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
Also, look for a new entry in the "Poor Man's" series of game programming articles this coming Tuesday.
In the course of poring over old screenshots and gifs for said article, I dug up a couple neat images from the past 2.5 years of development:
Hey fyfyone! I'm not sure how you got a copy of the game without "purchasing" it (in this case just claiming a free key). At any rate, send me an email at evan (at) helveticascenar.io and I'll get you a key so you can keep playing.
And yes, the final full game release will definitely be on itch as well as Steam!
CTF turned out to be a bit too easy to score in. There was no incentive to capture the batteries, which takes a lot away from the game. So I added force fields around the flags. Now you have to beat down the force field before capturing the flag, which encourages you to earn more energy to improve your damage output and capture flags quicker.
Started working on story mode again, beginning with the most important features, like shopping cart physics:
The story mode is basically Dishonored, except there are no people and all you do is climb.
For a long time, the first level has had a problem of not feeling like a real place with a purpose. It feels like everything was put there just for you, the player. I spent some time fleshing out the level and making it feel more like a city with a life of its own:
This is like, version 10 of this level. There's still a lot of work to do on it. I would like to add more NPCs.
Ah, I see. Yeah you needed to grab a key before they ran out. Weird that the app is giving you an option to install the game at all, since the alpha is closed at the moment. Hang on for just a bit longer and there will be a public free demo when the Kickstarter comes out!
I've had a ton of incredibly useful playtest feedback the past two weeks. Here are some of the gameplay changes and additions I've made in response to feedback:
Team bases are now surrounded with indestructible force fields to prevent spawn camping.
My friend Zac Fierce gave some great feedback about abilities. Previously, you could carry three abilities, which were permanent. Abilities got mapped to X, Y, and B in the order they were purchased. You could "toggle" them on and off by pressing X/Y/B to select the ability, and then right trigger "fired" the ability. The ability automatically toggled itself off after firing.
One of the problems with this system was that it changed the behavior of the right trigger without telling you. If you switched to the shotgun and attemped to fire, you'd expect it to fire and then toggle back to the normal movement/jump ability. But if the shotgun wasn't ready to fire, you none of that would happen, which might leave you confused as to which mode you're in.
Zac suggested putting the movement ability on a separate button, but I didn't want to add complexity to the controls. There was also the problem that the reticle gives tons of feedback about where you can and can't jump, and of course if you have a weapon equipped, you can fire it anywhere you want. So I would need two reticle indicators, one to show whether you can jump, and one to show whether you can fire. I might still do this, but it will take some thought to pull it off without complicating things too much.
Here's what I ended up doing. Rather than toggling abilities on and off, I made it so that when you press Y, it switches to the Y ability and stays there until you select a different ability. I also cut the number of abilities down to two, so that I could map the movement/jump ability to X. I also now allow abilities to be replaced, so while you can only carry two at a time, you can buy as many of them as you can afford.
The upgrade menu also now lets you choose which slot you want the ability to go in, which solves another issue. When someone always buys the shotgun first, they build up muscle memory that says "Y is shotgun". If they change the order of buying, the old system would change their control bindings so that Y is something else. The new system lets the player choose their control bindings regardless of purchase order.
Here it is in action:
Another feature request I heard a lot was for a "capture the flag" game mode. So that's in now:
Another common complaint dealt with the camera. I've explained previously how I use a cone to nudge the camera away from whatever surface the player lands on, so that they're more likely to be aiming in a direction they can shoot toward next.
Unfortunately I've always kept this cone active, even after the player has landed on the surface. This prevented the player from looking directly back at the surface they were attached to. They would have to swing the camera around the cone. This was an easy fix. After landing on a surface, there's a half second where the cone does its thing, and then I disable it. Now you can swing the camera around any way you like without regard for level geometry.
Spotting was another requested feature. I already have text emotes in the game that allow players to shout out specific turrets or batteries, but it's much more intuitive to just aim at something, hit a button, and have everyone see it.
Assault needed a bit of balancing. It was weighted pretty heavily in favor of the defenders. I'm balancing this by giving the attackers a pretty hefty chunk of energy to start with, and also by adding time to the clock and giving the attackers extra lives for every turret they destroy.
Also, players on the attacker team used to have an individual pool of lives, which meant one player could run out of lives and get stuck spectating. I switched to a ticket system so that lives are shared across all players.
That takes care of gameplay changes for the most part. Of course I've been tweaking cooldowns, damage values, and other numbers like crazy. But that's not very interesting.
One thing everyone agrees is very interesting is authentication! I asked itch.io to add third-party OAuth support, and they did it! Now you can log in to the game using your web browser even if you don't have the itch app installed.
I also integrated the Steam SDK, which will also log you in automatically.
Some players were having issues initiating and maintaining a connection, so I made another pass at the netcode and added some redundancy and improved latency handling. Now I'm sending every message at least twice as a pre-emptive measure. Also, when packets arrive delayed or out of order, the game does a better job of fast-forwarding to catch up with the server.
Another common feature request was mouse support in the menus. This is mostly done now.
Tons of other minor improvements have also been made. Players can edit server settings in-game. The game now gives more feedback when you kill another player. There's now an FOV slider. The input system now respects your operating system's keyboard layout. The crash reporter system has allowed me to fix a number of crash bugs.
I'm also putting together some assets for the upcoming Kickstarter. Progress continues!
Thanks for playing and leaving such in-depth feedback. :)
You make some good points about the ability controls, which yeah, they've been an issue from the start. This is an old issue I should revisit now.
Gamepad controls are one barrier here. Spacebar sounds great for desktop, but the best thing I can think of on gamepad is right bumper, which isn't great.
The second problem is that the sniper actually takes your shield down completely while it's activated. It would be too powerful otherwise. The assumption there is that you only have the sniper activated while sniping.
I'll see if I can come up with anything. On the bright side, the ability ordering actually works exactly how you described, or at least it should work that way. If you buy the shotgun and map it to 1, it should stay mapped to 1 throughout the match. :)
Thanks again man!
The latest patch includes a fix that will hopefully solve this issue. Last Laugh or FreakOrama, could either of you try deleting your settings file (or maybe just move it to your desktop) and boot the game again to see if it still starts with the wrong resolution? I'd really appreciate it!
Do you have multiple monitors? That's been an issue for some people. It tries to fill the screen on boot. Although I've got two monitors and it only fills one of them. Not sure what causes it.
What graphics card do you have? And do you use any configuration stuff that combines your monitors into one?
Anyway if you check the config file you can change the "width" and "height" properties manually. Check the readme to find the config file: https://github.com/etodd/deceiver/blob/master/shipme.txt
I shopped around for a while but ended up back where I started: Digital Ocean. So, the reason you're not supposed to use VMs is because performance is unpredictable; you never know when someone else on the host is going to saturate the CPU with a massive cron job or something. Theoretically you should be shielded from that, but in reality, it's not great for realtime applications.
I personally can't yet say whether it's an issue or not. But I did purchase special "high CPU" instances, which guarantee that you get two dedicated physical cores. One instance each for US East, US West, and Europe. I'm running four server processes per core for now.
Also set up an extremely simple alerting system that sends me an email when a server process crashes, and when a client uploads a crash dump.
I added a simple dashboard to the master server that allows me to execute SQL commands and keep an eye on connected servers and clients.
The bulk of this feature was done on stream:
Someone on stream suggested adding a "spot" feature which would allow players to call out targets for their team. I thought it was a pretty good idea. This feature was also done on stream:
Needed some new gifs for the website, so I recorded a few.
The alpha is basically ready. I might have to launch without itch's OAuth support. We'll see.
My friend Charles Dickens loaned me some massive TVs that made this booth a sight to behold:
Deceiver got two honorable mentions for something something art-related, and best of show. The weekend was a lot of fun, and there were a ton of great games. BOMBFEST absolutely stole the show!
One interesting insight was that the game played really really fast, much faster than previous builds. One one hand, that's great, people love the action, but on the other hand, I could tell people were getting tired playing the game. It's very demanding. Players would become completely absorbed for 10 minutes, then snap out of it and say "okay that's enough of that".
That was one thing I noticed about the best of show winner MageQuit. Despite offering a lot of PvP action, it actually plays pretty slowly and gives players a ton of downtime. I realized this design works great for local multiplayer games where you're mainly trying to hang out with friends anyway.
This ties in with something I read a while back about the success of PUBG. That game also has a ton of downtime, which is great for streamers because they need time to interact with their chat.
For now, I tempered the pacing a bit by increasing cooldown times. It's a stop-gap measure. I would like to find another way to facilitate those all-important peaks and valleys in pacing. I think larger maps and larger teams would be a great way to do that.
I made some major improvements to the website on the advice of the illustrious Jessie Kooner.
I also submitted a Steam store page for approval from Valve so people can wishlist the game.
This leaves two items on the alpha release to-do list. First, I'm waiting on itch.io to enable OAuth support. Second, I need to buy and deploy some dedicated servers.
I wanted to release tomorrow, but it's looking like I'll have to push it to next week. At any rate, it's getting close!
I spent a few weeks animating, capturing, and editing a trailer. I started out working on some story scenes:
Animation can be really tricky. Here's a problem I encountered with Blender's "Child Of" constraint:
I export the animation at 24 fps, but play it back at 60 fps. That causes the cigarette to shake around a little. Fortunately you don't really see the cigarette too closely in the shot I had planned.
At any rate, I talked to the wonderful M. Joshua who advised me to hold off on the story elements. The trailer isn't for the full release, only for a multiplayer alpha.
I did finish the trailer, and even managed to cram a teensy bit of story into it. OBS Studio wasn't able to record even 720p video at 60 fps without hitching, so I picked up an HDMI pass-through recording box and hooked it up to my laptop. Worked beautifully at 1080p 60 fps. This is the first time I've been able to make a 1080p trailer.
The Assault mode is a bit like League of Legends. Team A tries to destroy Team B's turrets. When people try this mode, they immediately want to know if they can repair their turrets. A repair tool has been on my to-do list for some time, but I wasn't excited about adding a whole new ability that accomplishes only one purpose.
Then I started thinking about Sensors. These have been in the game almost from day one, but they've never felt overpowered like everything else does. They detect and track enemy drones, and they create a stealth zone where you become invisible. That's it.
I decided to combine the repair tool idea with Sensors and ended up with Generators. In addition to the old sensor functionality, these also passively generate energy at a fairly good clip. They pay for themselves within 30 seconds or so. They also slowly heal any minions and turrets in range. I also buffed them up and gave them more health and a shield. You can spawn a ton of these near a turret to get a whole bunch of healing power.
Prepare for alpha
The multiplayer alpha is really close to release. Just need to polish a few things and deploy the servers. I tested my ability to run multiple instances on the same server. It looks like I could run about 6 games with 4 players each on a single t1.micro.
I added support for GameJolt login. Also added a crash reporter on Windows:
It also opens the GitHub issues page after uploading the crash dump.
I'm showing the game at GDEX this weekend, then at LexPlay next month.
Cooldowns have gone through the most iteration to date. Aside from the health system, movement system, reticle code, and story. :P
People were having a hard time equating the three pips below the reticle with the amount of "charges" they had left. The bolter made things more confusing both for the player and for the code, because it let your fire three bolts per charge. Then the shotgun came along, which required two whole charges to fire. Things were getting crazy.
I did away with all that and replaced it with an actual heat-based cooldown system. Looks like this:
You can also see the new muzzle flash effect in that gif.
I finally re-installed linux on my desktop, and wonder of wonders, the graphics drivers worked out of the box. That never happens. I was shocked. Here's the built-in open source AMD drivers running the game faster than Windows 10:
Jack made a pretty awesome spawn sound which had basically zero accompanying visuals, so I added some:
Adding controllers on the fly
I never realized what a huge win this feature is. Especially at expos, it's great to be able to add and remove players on the fly as people come and go. Took maybe 50 lines of code. Totally worth it.
I finally added some rudimentary friend functionality. You can create a server and mark it as "private", which means only friends are allowed to join. I also added the ability to mark others as server admins, which means they can change the server settings, switch maps on the fly, mess with teams, and kick people.
Tons of other stuff is going on in the background, including work on the first official trailer (woah)
The Last of the Jaggies
It took me two and a half years, but I freakin' FINALLY murdered the last surviving jaggies.
I started out using a typical edge detection post-process effect for the glowing edges. Then for a while I was supersampling the edges. Then I switched to a new system based on GL_LINES, which finally gave me some nice anti-aliasing. As a refresher, here's the comparison:
This has worked pretty well for the most part, except for a few cases where I got Z-fighting between the solid geometry and the lines. It looked like this:
I tried to mitigate this by restoring the depth buffer with a shader that sampled multiple pixels, taking the farthest one, but it wasn't enough.
A lot of times it was more subtle than the above example. The anti-aliasing would get cut off by the depth buffer:
Long story short, turns out drawing lines on top of solid objects is a common problem in 3D modelling software, and thus OpenGL has a built-in solution called glPolygonOffset. You render the solid geometry with a specially calculated depth offset that takes the depth derivative into account. So polygons that have a larger depth differential get offset more.
Rendering the whole scene with this offset messed things up for my culling system, so I settled on drawing the scene once normally, then instead of restoring the depth buffer later for edge rendering, I render the whole scene again in a depth-only pass with the polygon offset enabled.
It works beautifully:
I'll leave you with this gif of a feature I threw in yesterday. The shotgun has a teensy bit of kick now.
It's a minor thing in the grand scheme, but it makes me happy.
After adding text emotes last week, I realized how useful it is to be able to reference points on the map by name. To facilitate this, I added labels for batteries and turrets. Batteries are numbered, turrets have letters. These labels also appear in the event feed in the top right, so there are multiple ways to find out which objective requires your attention.
This was a no-brainer. The bolter, shotgun, and sniper now eject spent cartridges. They're purely aesthetic. The cartridges are sized differently for the different weapons.
eeeeeeeeeeeee i love it
Jack has been summoning incredible sounds out of the ether despite being one of the busier human beings I know. I spent most of this week hooking them up and experimenting with spatial audio. I was kindly given a trial copy of Wwise Reflect, which I immediately integrated. Unfortunately, the plugin didn't quite work for our use case. We're switching to a more standard reverb setup where the game scales different reverb presets from 0-1 depending on the results of a few raycasts.
Reverb is nice, but obstruction and occlusion is essential for gameplay. Obstruction is easy enough - one raycast is enough to determine whether a sound is blocked or not.
Occlusion is trickier. Let's say a sound is just barely obstructed by the edge of a wall. The sound would not be affected much, because the occlusion wouldn't be very high. In some sense, the sound just takes a slight detour around the obstruction, arriving at the listener barely distorted.
I'm approximating occlusion by estimating how far the sound would have to travel to reach the listener without penetrating any walls. If the sound is nearby but behind a large wall that we can't get around easily, the occlusion should be 100%.
To calculate this, I'm leveraging the AI nav mesh. When a sound source is obstructed, the audio system does a pathfind to determine how occluded it is. Here's a visualization:
Sound doesn't actually work this way, but the total path distance minus the straight-line distance is a good rough estimate of how far "out of its way" a sound will have to travel to reach the listener.
Server admins can now select which map will load next, even if the map is outside the server's normal rotation. They can also force the server to instantly switch maps.
This week has been INSANE. I don't even know why. When productivity strikes, you don't ask questions. You just go with it.
We coded up a shotgun live on stream:
Full auto bolter
The bolter is now full auto:
Speed changes, 20% map shrinkage
I increased the drone crawl speed a bit, and then decreased some other numbers to make it even faster in comparison. I slowed down the fly speed, decreased the drone's maximum range, and shrunk all the maps by 20%. This makes it more viable to tactically change your position by crawling, where before, everything was so spread out and the crawl speed was so slow that you felt essentially rooted in place. It feels like a whole different game now.
Camera rotation clamping
I clamp the camera's rotation so that the player is never stuck aiming straight into the wall they are attached to. At first, I clamped it against the wall's plane, but then I realized it was okay to let the player aim into the wall a little bit, so I switched to a cone. If the player tries to rotate the camera so that the look vector is inside the cone, the game pushes the camera back.
There were a few problems with this system. First, I didn't realize the algorithm needed multiple iterations to keep the camera out of the cone. So sometimes you would see the camera jitter a bit over the course of a few frames. I corrected this by running multiple iterations in one frame.
Second and more importantly, the cone sometimes pushed the camera out in a way that changed the player's aim unexpectedly. When landing on a new surface, I would start the cone perpendicular to the camera, then slerp its quaternion toward the final rotation of the surface. Unfortunately, slerp doesn't always rotate in the most direct path, and that caused it to move the cone in weird ways.
The new system immediately sets the cone orthogonal to the surface. Instead of tweening the cone's rotation over time, it tweens the cone's size. The cone starts out at 0 degrees and slowly expands to 45 degrees. This pushes the camera in a very smooth and predictable way.
I still have to smooth out the cone's rotation when crawling between surfaces, so I switched from a quaternion slerp to a linear vector lerp which takes a more direct path to the desired rotation. The end result is much more smooth and never disrupts the player's aim.
Here you can see the system nudging the camera away from the wall after landing:
I've had a pretty major problem until now, which is that you could shoot at a drone, barely miss, and anti-climactically land right next to them, with your character models uselessly clipping through each other. Worse, it's incredibly difficult to hit someone once you're that close; the physics just don't line up.
I corrected this by adding "repulsion". If two drones are just crawling around and their shields bump into each other, one drone will take a hit and the other will go flying. Drones that just landed take precedence. If both drones are just crawling around, the one that's moving faster takes precedence.
This change helps introduce more space and movement between players. Interestingly, this extra flight does not take one of your three cooldowns like it normally would.
Extra drone upgrade
I had a hard time keeping track of how many drones I had left while playing. It was always a surprise when I lost a match by using up my drone stock. So now I display your remaining drones at all times in the UI. I also added a new upgrade which allows you to purchase extra drones for a relatively high price. These changes are designed to make people play more carefully.
noclip in replays
I've had a replay system for a while, but the camera has always been a bit weird. I added support for noclip, which will hopefully allow me to film some neat scenes for a trailer.
Servers now know which region they are in, and the client asks you which region you're in, and the master server matches the two together.
Text emotes and chat
The UI for this is still a bit rough, but chat messages and text emotes (a la Rocket League) are in.
Players are now rewarded for damaging other players, even if they didn't get a kill. Servers will now kick you after a certain period of inactivity to prevent AFK players from clogging up matches. I've also made a ton of improvements to the netcode, making it more robust and improving the client-side prediction.
Thanks to Twitch viewer RayMarch for this one. They suggested adding some lag and springiness to the camera so you can better see when your drone hits something.
This makes things much less confusing when you bounce off an enemy:
The camera locks up again when you're done dashing or flying, so you can still do precision aiming.
After seeing the first gif above, someone pointed out the "danger" sign blocking your view of the spider drone. The UI has a number of status indicators that turn on and off depending on the player state. These were scattered along the central vertical axis. I standardized their size and position and made the stack up nicely to the right of the reticle.
Also visible in the second gif above is the newly tweaked turret design. Previously the base of the turret was dark, meaning spider drones could not shoot or climb on it. This caused some issues, as you're trying to aim at the turret and suddenly get a big confusing X on the screen indicating you can't shoot there. The new turret base allows you to crawl on it while avoiding other gameplay issues by having the actual turret body hover a foot above the base.
Build ID overlay
You can also see the new build ID overlay in the screenshot above. This will help me debug issues when people submit screenshots, and hopefully prevent people from taking pre-alpha screenshots as final quality.
Staggered grid experiment
I tried staggering the grid points to create a triangular pattern rather than rectangular:
I found the rectangular pattern emphasized the level geometry more clearly, while the triangular pattern distracted from it. I ended up undoing this change.
Server admins can now kick people. You can even kick someone playing on the same screen as you.
Progressive upgrade pricing
Each upgrade you purchase now increases the price of all future upgrades. This makes the order of purchase more important, and also allows me to price things relatively cheaply at the beginning, which gives the player more options. Previously you really could only choose between the four cheapest upgrades at first.
Cleaned up and updated Refinery:
Cleaned up and updated Plaza:
Also, I tested out how Samsa looks in-engine. :)