Hit "Story". There's a rudimentary tutorial. :)
Recent community posts
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. :)
A ton of features have gone in recently to bring the game closer to a multiplayer demo. Previously, players chose teams before starting a match, but that didn't work for networked multiplayer. Also, there was no way to switch teams in the middle of a match. Until now!
Plus if you're a server admin, you can move other players to different teams as well.
This is a little subtle and hard to see, but sniper bullets now work the same way as player movement: if the target survives the impact, the bullet ricochets. Sorry, I didn't have time to compile a trick-shot montage set to Breaking Benjamin.
The master server and game server now listen on IPv6 as well as IPv4. If a client connects to the master server over IPv6, the master will return IPv6 addresses for all game servers. I suspect this may be a brittle solution that somehow breaks at some point, but it's good to have the plumbing done and know the code is future-proof. In related news, here's a great article I just read about what IPv6 could have been.
Minions use a simple heuristic to determine which target to attack: whichever is closest. Now take a look at this map:
It has two floors stacked on top of each other. There's a turret on the upper floor. A minion who spawns on the floor directly beneath the turret will assume that the turret is the closest target, and kick off a pathfinding request to make its way there.
Unfortunately, the path to the turret turns out to be a roundabout excursion up a series of ramps, and will likely take the minion past other, closer targets. This is a problem. There's no way of knowing which target is actually closest without calculating a pathfinding solution for each and every one, which would be prohibitively expensive.
Instead, I decided to mark up the map with some extra pathfinding information. I added some special Blender objects which allow me to say "if you're inside area X, and you want to reach target Y, then you'll have to pass through point Z to get there". This metadata does not impact the actual pathfinding, it only improves the accuracy of the heuristic for choosing targets.
While I was messing around with minions, I also implemented a basic obstacle avoidance algorithm to keep them from running into each other and clumping together. The algorithm is this: if a minion is in front of you, turn to the right.
Another improvement I made is that if a minion attacks a player and then loses sight of them, they will advance to the last known position of the player and search for them before moving on to a new target.
In-game UI notifications
One piece of feedback I've received recently is that players have a hard time keeping track of the game state. How many batteries do I have? How many turrets do I have? Did the enemy just capture something of mine?
So now I'm highlighting batteries and turrets outside the player's range with colored icons. There are also icons for "under attack" and "lost", so you can easily see which things require your attention. If you happen to be looking away from the object that's under attack at the time, there's also a text notification.
I finally added settings for screen resolution, fullscreen/windowed, and v-sync. The settings menu is finally done.
All of this is coming together to culminate in a multiplayer demo hopefully by the end of September. Prepare your bodies
I made a robot doggie. His name is Samsa. He plays a fairly critical role in the story. Don't worry, nothing bad ever happens to dogs in video games and movies and literature.
I didn't like how he turned out first. He has to unfold into a sort of command center, which dictates certain design limitations:
At this point, the vibe he gave was more Stegosaurus than Golden Retriever. I asked Twitter how to make him cuter, and they told me to shorten and speed up the footsteps, add some Z-axis roll, and add an antenna. A lot of them also suggested "googly eyes", but that was a bit much for this game. I implemented everything else:
The antenna idea was brilliant because it's so easy and fun to animate and adds a ton of visual interest:
One of the abilities in Deceiver is called "active armor". You hit a button, and any incoming damage gets reflected back at the attacker. Like so:
If you watch that gif closely, you can see that I react well in advance of the actual bolt impact. But what if it was really close? If you look in the top left of the gif, you can see the ping is over 200ms. If I reacted just in time from my perspective, I would be 200ms too late from the server's perspective.
To solve this problem, I created a buffer for all player damage. That means the server acknowledges incoming damage, but waits for a split second before actually applying it. And by a split second, I mean whatever the player's ping is, plus one frame for good measure.
Here's what that looks like. The bolt disappears and spawns a particle effect instantly, but the damage doesn't actually register until 200ms later:
Now I can react to the bolt right up to the moment the bolt impacts. Note that damage buffering is unnecessary and in fact detrimental between two players playing splitscreen on the same client!
Yes, this skews the game against attackers, but I think it's much more frustrating to get killed cheaply due to lag, as opposed to the minor annoyance of having your hits not register. Especially in a game where a lot of damage comes from non-player characters.
While developing this feature, I used a tool Ryan Evans recommended to me called clumsy, which simulates bad network conditions. Brain-dead simple to operate.
Lastly, I added pings to the scoreboard, so you can rage at players who have Comcast.
P.S. - Sound is coming. This is me being completely hyped out of my mind.
Upgrades to the upgrade system
Upgrades in this game have always had a certain risk/reward mechanic. They take a few seconds to purchase, during which your drone remains immobile and vulnerable. Most of the time this was fine because you had to be at a friendly base in order to upgrade, but sometimes the base got captured while you were upgrading, which kicked you out of the upgrade menu.
Someone at Indy Pop Con suggested an idea that would both add some visual interest and make players invulnerable while upgrading:
At first, I was stressing out about the edge cases and physics of flipping a drone around - what happens if two drones are attached to the base when it flips? What if an enemy drone flies through the gap into the void while the base is flipping?
Then I realized I had to fake the whole thing. Nothing actually moves at all; I disable the drone's collision and animate the model, but otherwise, the drone just sits there. No physics objects move at all. About half of this feature was implemented live on stream.
I spent a day getting cURL building on Mac, Windows, and Linux, which allowed me to make HTTP requests. Now I'm using itch.io's API for login and authentication. Launch the game from the itch app, and my game receives a JWT via an environment variable. The game talks to the master server, which talks to itch, et voilà, it magically knows your name. Scary. Steam authentication will work basically the same way.
Now that we have actual user profiles, it's finally time to let people connect to each other. Until now, I had half-assed plans for a matchmaking system similar to the one I use in campaign mode, but then I decided to try an idea I've been wanting to do since at least 2011. It goes like this.
I really miss community-run servers in games. It's hard to build a community for, say, Overwatch, without a dedicated place for people to meet. Even if it's not a community per se, some of my favorite memories happened in dedicated servers with custom game rules. That's gone now. These days it's a lot easier to pay some cloud provider and not have to worry about community-run servers falling out of date or distributing malware.
So the idea is: virtual dedicated servers. Anyone can create a dedicated server for free. It's just a database row that holds a name, a set of game rules, and a list of admins. When someone wants to play on this "server", a real dedicated server is allocated from the pool and set up with the custom game rules. It's like giving gamers their own little version of Amazon Web Services. Here it is in action.
Still a lot of work to do, especially on the server browser queries. I'd like to use a Reddit-like algorithm to keep popular servers at the top while still surfacing new configurations.
I'm using the fantastic SQLite for the database. Their build process combines all their source files into a single 6.8 MB C file. Super easy to set up.
Title screen redesign
The title screen also doubles as the first level in story mode, so it's super important. Here's how it used to look:
I didn't like how the dark colors contrasted with the white outlines, and the nighttime setting didn't mesh very well with the start of a journey. Also, the level had you moving to the left to progress, which felt surprisingly jarring since our brains associate left-to-right movement with progress. Finally, there wasn't much room to explore and mess around, and the tutorial was very linear.
So I flipped everything around to progress toward the right, changed the colors, opened up the layout a bit, and integrated the tutorial more seamlessly with the environment. Here's how it looks now:
The cinematic I've been working on is done, for now at least. It's a little over a minute long.
Unfortunately I realized it needs to happen in the third level rather than the second, which means I still have another cinematic to do for level 2 before this vertical slice is done. I'm excited for it though, along with all the other story stuff. I recently found a way to cut the story down to 9 levels, 2 of which are already done.
Kill cam highlighting
This is a minor but important quality of life improvement. The kill cam now highlights your killer when they're not directly visible. I basically just change the depth test to pass when the depth is greater, and then render the mesh with transparency. Easy peasy.
This was a lot of fun, but unfortunately I won't be going back next year. There were maybe ten games total; most of the con focused on unlicensed pop culture stuff and YouTube personalities. Most people were not interested in my game at all, but those who did sit down and play almost always went away with a huge grin on their face, so that was encouraging!
I recorded almost 400 MB of gameplay. This works by blasting all network packets from the server directly into a file, then playing them back in order. I can record an 8 minute play session in under 10 MB.
The only downside is that the network protocol does not capture camera angles, so I can only watch replays in this weird top-down view.
Here's some highlights:
Right player stalks left player from the top of a pillar
Left player captures a battery but immediately gets smashed
Right player gets overwhelmed by left player's minion army
Left player does a good job of staying behind their minions
Right player holds their own against minions, left player not so much
I got a ton of player feedback from Indy Popcon, which means I have a huge list of cool stuff to work on. A few notable changes already implemented:
Yes I'm still improving this. Previously I used a cone shape to cull geometry between the camera and the player's drone. The issue was that, if you backed up close to a wall, it would intersect with the narrow part of the cone, leaving you a tiny circle to see through. I switched from a cone to a paraboloid, which slightly improved the situation:
There was another issue as well. When transitioning from one surface to another, the player's rotation lerps smoothly, which is nice. The problem was that, all the culling planes were based on this lerped rotation, which resulted in a lot of popping and graphical artifacts during the lerp. Now the culling transitions immediately from one surface to the next, while the player model still lerps smoothly.
Previously, after a cooldown, all three of your jumps recharged instantly. Now they recharge individually, similar to Overwatch's Tracer, which was the original inspiration for this system. The difference with this new system is, the first jump takes a long time to recharge, while the other two recharge much faster. I want to give players an interesting choice here: do you immediately use the first jump because it's an emergency, or do you wait a split second longer to gain more future mobility?
That's all for now. Big changes in progress. Next milestone is GDEX in September.
Been working on this cinematic where the player gets shot and drugged:
The player's model and Meursault's model are in separate blend files, so I created a "workspace" blend file with all the models linked in, so I can animate them together. Then I save the animations back out to their respective blend files, then line everything up in-game.
Finally fleshed out the settings menu. Almost every graphical effect can be turned off. All keys can be rebound, and the tutorial prompts and UI instantly update to reflect the change. In my last game, there was one menu for both keyboard and gamepad controls; if you plugged in a gamepad, you could only change the gamepad controls. One person was confused because they forgot they had a gamepad plugged in. Anyway, for this game I'm doing two separate menus. The gamepad menu is only visible if you plug in a gamepad. Also, other local players can change their own gamepad settings, but not the keyboard controls or any other settings.
I made a few changes in an attempt to move away from twitchy Call of Duty-esque gameplay. First, I halved the gamepad acceleration so that it takes 0.4 seconds to reach full speed. Then I slowed down the ADS zoom speed, and the speed at which drones and bolts fly. I also tweaked the energy rewards to encourage players to capture and hold batteries rather than aggressively attack all the time.
Also, it's a minor detail, but I'm experimenting with analog zoom; the degree to which you pull the trigger affects how far the camera zooms in. We'll see if people like it or not. Probably doesn't make too much of a difference.
Almost ready for Indy Popcon. Tweaked the promo art a bit more and had it printed out:
Along with this one:
Also put up a quick website: http://deceivergame.com
I ended up moving the main character into a separate render layer which allowed me to thicken the edges around her. I also added mist to give a sense of distance to the background, and a barrier in front of the character to give her some grounding. Also messed with the composition. Here's the new version:
In the most recent redesign, I realized it would be prohibitively difficult to design maps that function equally well for both parkour/exploration and PvP drone combat. Now I'm designing different maps for different purposes. This is the first real map I've designed specifically for parkour:
Since I don't have to worry about things like balance and spawn points, I can focus on the spectacle and fun of just running around in the environment. Likewise, I can make smaller and tighter PvP maps without worrying about visuals too much.
This guy has been planned for a long time, and now he finally has a model.
His name is Meursault, and he's a little nuts. I'm going to try animating and scripting the first encounter with him this upcoming week. Excited to see how it turns out.
I had a bug in my AI, so I turned on my nav mesh debug renderer to see what was going on.
Sure enough, there was a bug where faces were not rasterized if they were almost horizontal but not quite. You can see a conspicuous gap in the screenshot above.
After fixing the bug, I thought the nav mesh looked cool enough to stay enabled in release mode. I cleaned it up by adding transparency and antialiasing:
Finally, I wrote a shader to make the dots fade out over distance:
Last week I tried to use in-engine screenshots to create high-res promotional artwork. I realized that wasn't going to work, so I set about making Blender imitate my rendering style:
Of course Blender is so amazing that it was pretty easy. Freestyle edge rendering let me choose which edges to highlight, and a halo material worked for the stars.
I imported a few more models from the game, threw in a quick particle system, arranged them into a scene, and slapped the logo on it:
The colors seemed a little garish, so I tried another color scheme:
Everyone on Twitter liked the pink version better, but I really wanted to get rid of the black background behind the logo. I also enabled contour rendering to get a nice silhouette around the main character. Unfortunately, this also added a silhouette around each individual spark particle, which made them too distracting. I ended up putting the sparks on a separate render layer with no freestyle edge rendering, and then compositing the two layers together. Here's my final composite setup including an extremely simple bloom effect:
After a few more hours of posing and tweaking colors, I ended up with this:
Hopefully this will make for a nice banner at IndyPopCon.
As usual, I'm making tons of small but significant tweaks. Gameplay has changed a lot in response to a decent amount of playtesting recently. Weapons no longer take energy to fire; only building things incurs an energy cost. Energy also accrues more slowly, and upgrades cost more.
Here's 30 seconds of footage showing what the game is like now:
Until now, I've been designing each level to function equally well for first-person running/jumping/climbing, and third-person spider bot PvP combat. I realized it would be much better to design separate levels for parkour and PvP, so now the plan is to have 9 parkour levels, each with a PvP map attached to it. While I was revamping the level structure I decided to redesign the overworld a bit as well. Still very WIP.
The PvP game has changed so much recently, and I kept shoehorning the new gameplay into a tutorial map originally designed for a different type of game. Finally I decided to start over from scratch.
Someone asked me to design a retro cabinet for the game, so they could put it in their VR arcade. I jumped at the opportunity because I'll be showing the game at a few expos this summer and fall, and I need promotional artwork. The image above is all rendered in-engine, which I now realize is not going to work for a high-resolution physical sign. The new plan is to render something in Blender and trace over it with vector art.
Most of my work is on small details right now. Above is a development stream where I added animation to all the menus in the game, then added a footstep animation to the wallrun tutorial, then moved some blocking IO calls to the AI thread. All important changes that noticeably improve the game, but not exactly revolutionary.
Rockets and decoys cut
Decoys were never really fun, and sensors accomplish basically the same thing (take aggro off the player). Rockets were really cool but not useful. Then I added grenades, which fill a similar role to rockets since they wait for enemies to approach before exploding.
New core design
Destroying six separate modules turned out to be tedious, so I lowered the number to three. People were also confused why they the core was invulnerable until the turrets were destroyed, so I put a force field around it which disappears once the core becomes vulnerable.
Force field changes
Previously, force fields had a short battery life. You could place one near a battery to increase its life, but it would still last less than a minute. The spherical shield itself was invulnerable, but there was a "generator" object inside the field that could be destroyed. If you happened to be inside the field when it was created, you could easily destroy the generator. Minions could also walk through the field to get inside and destroy the generator.
The problem is, now there are a lot more minions in a typical game, since they spawn automatically. I wanted force fields to be big, expensive, and important, but they don't last long with so many minions around. Plus, making them exclusively vulnerable to minions felt a little too rock-paper-scissory.
Force fields now sport an indefinite lifetime and a large amount of health, second only to the turrets. They can now be worn down from the inside or outside via minions, bolts, sniper bullets, grenades, or plain old drone attacks. And they're much more expensive.
The most CPU-intensive task in the game right now is actually ragdoll simulation. If more than 5 minions die at the same time, framerate drops from 300 to 60 or less. Ragdolls are strictly cosmetic, so I disabled them on the server to avoid CPU spikes. During normal gameplay, the server now puts one CPU core at 20-40% utilization, although AI might drive that number higher.
Project status and future
I decided not to teach this summer and instead subsist on money left over from last year. That means there will have to be a Kickstarter and/or Early Access release sometime around fall.
This is the biggest thing missing for any kind of release. Jack has been excited to work on this project for years now, but is currently crunching on LawBreakers. I started filling out Wwise audio events and spreadsheets in preparation for him to join the project this summer / fall.
Had the opportunity to speak and show the game at Vector conference at Eastern Kentucky University. Got some playtesters and some great feedback.
I had the privilege of meeting Nathan Fouts from Mommy's Best Games. His was one of the blogs that convinced me of the viability of going indie back in 2009-2010. He played the game and enjoyed it but listed a lot of stuff he hated, which is exactly the kind of feedback I need. One problem he mentioned has plagued the game for years...
Up until now, I applied a shader that darkened everything behind the surface you're currently attached to, like this:
Notice the sharp line across the structure in the top left. This indicates that you can't shoot yourself anywhere in the darkened area, because you'd have to clip through the surface you're currently attached to. This always confuses everyone. I have to explain it to every new player.
Nathan suggested instead to automatically zip the player to the edge of the surface where the targeting line is clear, then launch them toward the intended target. So I got rid of the darkening shader and implemented his suggestion. Here's how it looks in slow motion:
That battery hangs below the floor level, but the game still lets me hit it by automatically dashing forward to the edge before launching at the target.
It seems asinine and nitpicky, but tweaks like this add up and affect players' subconscious impression of the game.
Since October 2016, the main focus of the game has been Rush mode, where the attacking team must capture two control points by setting off a timed hacking process.
There were a few problems with this mode:
- The game is designed for 1v1, possibly 2v2 or 3v3 at the most. Rush games work best with large teams. In a 1v1 Rush game, the players often just take turns capturing one control point, then the other.
- Sitting still and holding X to hack a control point is not very exciting.
- The control points don't really interact with any other systems in the game. At best, they encourage you to camp in a single spot and wait.
The core consists of a number of modules which must be individually destroyed:
All turrets must be destroyed before the core becomes vulnerable. Turrets work like this:
I also realized that minions are important enough that they should be a part of every match, rather than being available as an optional ability. So now, they spawn automatically over time.
Where do they spawn from? Well, I wanted to give more reason for players to capture batteries other than just gaining more energy. So now, they also function as spawn points. You can choose to spawn from any battery you own.
This mode takes the game back toward the earlier MOBA experiments, but without some of the failed aspects (large bullet-sponge health bars, last-hitting).
Your shield now goes down while sniping. High risk, high reward!
Active armor tweaks
You can now reflect incoming bolts, increasing their speed and damage. Useful for fighting turrets.
There are tons of other changes to talk about. One example is the "toggle zoom" option. Instead of holding RMB to zoom in, you can click it to toggle the zoom. I think unsexy accessibility options like this are super important. The new AI system is also still under construction. I'll be showing the game at Vector conference next weekend, so hopefully I'll get a lot of feedback to move forward with.
After posting last week's devlog, I realized the HUD was a bit of a trainwreck, so I redesigned it.
Camera clipping effect
Last week I mentioned a few more changes I wanted to make to the camera clipping effect. These are now done, and after over a year of tweaking, I finally consider this effect good enough to ship.
The effect works by filling all the backfaces with a special G-buffer tag that causes later post-processing effects to block out any glowing edges and scan lines with a pure black color. The only downside is that the effect requires all environment geometry to be convex. I think it's a small price to pay for perfectly correct results with almost no performance penalty.
I added an "active armor" ability that temporarily grants you invincibility and reflects any incoming damage onto the attacker.
I had an opportunity to show the game as part of the Playcrafting booth at PAX East. It was fun getting to meet a lot of cool people, but I realized that you get back what you put in when it comes to expos and shows. It's difficult to attract interest when you're part of a larger booth and have no signage.
To help attract attention at PAX, I wrote up a quick and dirty "attract mode". The game can now record matches and automatically play them back after 60 seconds of inactivity. As soon as someone touches a controller, the game goes back to the main menu. It was very straightforward to write; I just record the network packets and play them back in order. Unfortunately I couldn't get anyone to play a match with me before PAX, so the feature went unused. But it will come in handy in the future!
This was a lot easier than I expected. Essentially the only code that needs to think about multi-byte codepoints is the text rendering code.
This was necessary because I wanted...
Playstation button prompts
The game now displays different button prompts for each player, depending on what kind of controller they plugged in, if any. It also instantly switches to keyboard prompts if you touch the mouse. My last game always showed controller prompts as long as a controller was plugged in, and that proved to be confusing to some players. Besides, this is a feature that impressed me in The Witcher 3, so I decided to steal it.
Clipping v4 (?)
Still experimenting with new camera clipping techniques. If anyone knows any of the graphics programmers who worked on For Honor, I would love to know how they do their effect.
In this version, I'm rendering backfaces the same as camera-facing tris, but in pure black. I'm working on removing the white outlines inside the black areas. If I can also fix a few weird clipping situations that block the camera view, I think the effect will finally be complete.
The sudoku hacking game had one flaw, which is that sometimes it gave you a number that could belong in a currently unsolvable portion of the puzzle. To fix this, I made it calculate the number of possibilities for each cell and give you the one with the least number of possible values.
I scrapped the old behavior tree system a while back, so the AI players have been sitting idle since then. I'm finally making significant progress on a new system which will be able to seamlessly playback recordings of player moves, mixed with ad-libbed AI actions. The recording system is done, and now I'm working on the AI actions. After that, I'll work on the playback system which will select which recordings to use in each situation.
Having AI players in the game again revealed to me a major issue with the gameplay...
I realized that the core shooting mechanic was a bit rock-paper-scissorsy, because it always takes the same number of hits to kill. Three hits is also too many; it feels tiresome, especially if the other player is trying to get away. The possibility of getting one-shotted as constant fear adds some thrill to the game. At the same time, I don't want a single dumb mistake to result in death, especially at the hands of a non-player character.
With that in mind, I'm bringing back an old concept I tried a while back: skill shots. Your shot can take away 1 hit point or all 3, depending on how good it is. I think it's especially satisfying to get a 1-hit kill because you don't bounce off at all.
This is the last time, I promise. The game is called Deceiver.
Easier to remember, and a surprisingly uncontested name within video games at least.
Some people know the reasoning behind the name, but unfortunately it's extremely spoilery.
This system does raycasts to determine how far each raindrop should fall. Unfortunately, my particle system (and most particle systems I believe) requires that particles be removed in the same order they were added, meaning all particles must have the same lifetime. To get around this, I store each particle's individual lifetime in a vertex attribute and clip the particle out of existence in the pixel shader if the particle is too old.
The system keeps a cache of raycasts around each camera, updating the cache over time at a rate of about 1000 raycasts per second. As an improvement over my last rain system, it can also "fill in" missing particles when the camera moves or teleports too quickly for the normal spawn rate to keep up with.
- Added a network lag simulator and made more changes to further harden the netcode. One example: the server was using the client's running average RTT to rewind the world when doing collision detection for that client. Problem is, if the player sends a "jump" command that gets dropped by the network and resent later, the timing of that command is out of sync on the server and client, and it will only be resolved when the player lands. So now the server uses the sequence ID of the command to calculate and store an RTT value for rewinding purposes, which remains constant until the player lands.
- For the longest time I was bothered by the game's performance on my laptop, which has a GTX 950M capable of running Rocket League on decent settings at over 60 FPS. For a while this made me doubt a little bit my ability to write performant shaders. Turns out, my game was being relegated to the Intel integrated graphics chip. Tweaking the nVidia settings brought performance to over 150 FPS. Yay!
- Upgraded to VS 2017. I think it's an improvement from 2015 overall. Certainly the install experience was much better.
- The local/online multiplayer menu system is maybe half done. Multiplayer might be completely done soon. I am considering an Early Access release to stress test the netcode and collect gameplay feedback.
I added an extra shield hitpoint, so it takes three total hits to kill a player. I'm hoping this will encourage players to use abilities rather than only attacking each other directly.
I have the beginnings of a master server, which facilitates connections between players and servers. It's pretty basic at this point, but it works. I'll continue to expand this in the future with authentication and matchmaking.
I threw out the behavior tree system, which badly needed refactoring anyway. Now I'm working on a system which will record player inputs into a database, then search through the database for specific scenarios and play back the correct actions. It's maybe halfway to a rough draft working prototype.
Just another cool thing I wanted to pull in from my last game. They're animated and actually cast shadows on the environment.
GDC and PAX East
I'll be present for the first two days of GDC, plus out of nowhere I got an opportunity to show at a booth at PAX East! Beyond excited :)
Projectile client-side prediction
All moving projectiles in the game are normal entities tracked via the usual interpolated transform sync system. This is fine for AI characters shooting at you, but it's incredibly frustrating when you are shooting projectiles. You have to wait for a network round-trip before the projectile shows up.
I often test netcode on localhost, where there is no lag. Since this feature is heavily dependent on lag, I took some time during the stream this past Friday to implement a buffer that simulates network lag.
I cranked the lag up to 300ms total round-trip time and fired some projectiles. The first problem was that, since the server took 150ms to register my "fire projectile" command, my target might have moved by the time the projectile got to it.
The solution works like this on the server:
- Rewind the world 150ms to the point where the player fired
- Step forward in increments of 1/60th of a second until we reach the present, checking for obstacles along the way
- Spawn the projectile at the final position
- If a target was hit during this process, delete the projectile and apply any damage effects
To the player, there is still a 300ms delay before anything happens, but the projectile will pop into existence 20 feet out, where it would have been if there were no lag. This makes it easier to aim, but it's still annoying to have no immediate visual feedback when you fire.
I thought about spawning the projectile on the client. The problem is, projectiles are entities, and the entity system is controlled by the server. If I spawned projectiles on the client, IDs would get out of sync and things would explode.
So instead, I made a new system for fake projectiles, totally separate from the entities. Actually "system" is too strong a word, it's just an array of structs. These fake projectiles live for up to half a second, and the client removes them in order as soon as the server spawns a real projectile.
Here's the end result running with over 300ms of lag:
You can now buy stuff at these special locations known as "shops".
Locke has a number of greetings he can give, which will have accompanying animations. I'm really starting to enjoy animation work! Actually had a blast making this:
Also, this thing is now over 50,000 lines of code
I had my first anxiety attack on Tuesday! Feels like I've completed a gamedev rite of passage. I've been relaxing and hanging out with my family this week to try and get healthy again. Feeling much better now. Here's what got done before the break:
This guy was supposed to look ragged, but his outfit was based on the ridiculously photogenic homeless man so it ended up very stylish actually.
He's one of the first NPCs you'll meet. He just talks to himself.
You can now kill minions from above. I haven't done anything to align the animation yet.
Lots of bug fixes and small changes. I refactored the scripting system so that scripts can be executed on both the client and server in networked games. But the biggest time sinks (and of course the biggest overall challenges for this project) are the AI and netcode. I'll still be doing fun story stuff and character models through the end of February for a vertical slice to show at GDC. After that, it's time to dive in to network infrastructure and a completely new AI system.
I keep adding animations one by one. At one point, Assimp decided to optimize the root bone of the player model out of existence. This obviously caused some problems.
Friends! I need your feedback.
I am considering renaming the game once again. "The Yearning" is still a good fit, but it's confusing, pretentious, and easy to forget. It's also impossible to infer anything about the game from the name alone.
I'm considering renaming it to "Skirr".
"Skirr" is the name of the city in which the game takes place. Reasons I like this name:
- It means "to flee". The game is about fleeing the earth to escape an apocalypse.
- It sounds like "scurry", which evokes the creepy-crawly nature of the spider-bots.
- It doesn't have much competition on Google.
- To me it sounds like an action/adventure title.
- Taking inspiration from Astroneer, "SKIRR" in all caps looks acceptable and could help draw attention.
Reasons I don't like it:
- Confusing spelling. People who hear the title spoken might think it's spelled something like "Scur".
- Confusing pronunciation. People who read the title might think it's pronounced something like "Skeer".
What say you? Too confusing still?
Another title I thought about for a while is "Caligula", after a play by Albert Camus. "Caligula" is the name of the refuge planet in the game. Unfortunately there's already a "Caligula" game in development.
Apologies for so many rebrands, but I would rather nail the title than stay shackled to a bad one in order to minimize confusion. Besides, No One Knows About Your Game.
In other news, the dock is finally finished:
This is the third map you'll discover, if you count the title screen. Which I do.
These work exactly the way they did in my last game. While climbing a ledge, the player physics body moves straight up, and then straight forward in a jerky fashion. While this is happening, I offset the model and camera so that the climbing animation stays rooted at the same position even though the player entity is moving. After the animation is done, I blend everything back together so the model and physics body are in the same position again. I believe this is similar to something in UE4 called "root motion".
I started out thinking this game would work the same as my last in terms of story. Branching dialogue choices in a simple text-based system, plus random notes scattered throughout the levels.
This week I finally realized a few things:
- I mainly play action games. This is an action game. Things happen in action games. Reading text is not a great fit.
- None of my favorite games have branching dialogue. You choose a story branch by performing an action, not selecting it from a menu.
- Games are most compelling when gameplay and story coexist and complement each other. That's difficult to do when they're totally separate. Pre-rendered cutscenes, or worse, animatics, foster a clear delineation between gameplay and story. The best games do everything in-engine, preferably without taking control away from the player. In an action game, heavy amounts of text lead to the same problem.
- I now have enough modeling and animation experience to pull off fully animated characters if I take a lot of artistic license and stick to a stylized look.