Finally, a new build (and up on GameJolt)

Work’s been draining my enthusiasm lately, but I’ve finally created a new build of Spamocalypse. The main changes are:

  • Lifts and ladders. I am planning to rebuild the remaining levels to use more ladders, just to give the player some more routes.
  • I rebuilt the tutorial level from scratch. It now has more buildings to hide in or look inside, and a few ladders and a lift. Most of the buildings are empty, but I’ll think of more props later.
  • Some extra effects: the Moderators have green clouds flying around them, the spammers will actually vomit particles at you, and the bots now have a chimney that spews smoke
  • Some graphics fixes: there were a few parts in levels where the player can see through gaps in the scenery. With my old pitch-black sky, this wasn’t a problem. However, with the skyboxes I have, this becomes more noticeable.
  • Fixed a bug in the lighting check, where the coroutine only ran once, and then that light would never the player until they exited and entered again. The sun/moon light wasn’t affected by this.

So, I think I have the mechanics pretty much finished. At this point, it’s now just extra props and bug fixes. I’ve also finally uploaded it to Gamejolt as a mostly-complete work-in-progress (albeit as version 0.1.0). I’m going to keep Dropbox as a mirror.

Advertisements

18 months of work down the drain

Tags

, , ,

This is a massive, rage-inducing WTF moment. I kept having problems with the pathfinding code marking corridors as impassable even though there was enough room to walk along them, and having issues with checking underneath objects, particularly in buildsings with more than one floor. I decided to try updating my pathfinding code in Spamocalypse to use the latest version in my repository, mainly because that allows me to store the navigation data in a ScriptableObject. I thought of using separate meshes for separate floors, or possibly separate rooms, but I then ran into an issue where the dictionaries weren’t being set up on load, resulting in NPCs not being able to find a path and thus trying to walk through obstacles.

I’ve been using this code for around 18 months now. The reason I started using it is because I first started the project on Unity 4.6, when pathfinding was only available to Pro users, and for my thesis project I built/adapted a pathfinding system to track where units died in a team deathmatch game. The way it works is that I cast rays downwards at regular intervals to check if an NPC can walk at that position, and store the result. The only problem is that casting at regular intervals doesn’t really work with arbitrary shapes like those in a real-world city, especially if there are floors below it. So, a few weeks ago I started considering just using the built-in pathfinding system, but I kept putting it off because I thought that refactoring it would take too long…and I didn’t want my code to go to waste. The sunk cost fallacy rears its ugly head!

*headdesk*

FFFFUUUUUUUUUU!

Continue reading

More voice lines

Tags

, , ,

I generated some more voice lines for Spamocalypse. This time, I chose a Scottish accent instead of the North English I used one for the Mods. I also wrote a Bash script to autogenerate the lines, because I’m lazy and hate manually running commands over and over again. To do that, I had to browse StackOverflow teach myself how to prompt for input, read from a file and split a line into two parts. So, I’m reasonably happy with that.

Here’s the video:

There are still some lines I’m missing. Firstly, their respawn lines. Having them announce that they’re back up would be quite helpful, especially since they’re supposed to become suspicious after dying too many times. I also didn’t add the lines they sprout while patrolling or idle, but I figured 90 seconds was enough for a demo.

Spamocalypse Voice demo

Tags

, , ,

I spent the weekend messing around with Audacity to create some voice lines for the Moderators in Spamocalypse. Here’s the interim results:

Here’s how I did this.

  1. Type the rough version of what I wanted to say into this online Shakespearean translator, and copy the results.
  2. On my terminal, run the following command to instruct eSpeak to create a sound file, using a (somewhat robotic) North English accent, and replacing $RESULTS with the results of the last step:
    espeak -w $FILENAME.wav -v en-uk-north "$RESULTS"
  3. Import the file into Audacity, and convert it to a stereo track.
  4. Slow it down by 20%.
  5. Add a reverb effect.
  6. Duplicate the track, and add the Wahwah effect
  7. Export as an OGG file

I was trying to go for something like the Hammer Haunts in Thief, and I don’t think I got there. It still sounds a bit robotic to me, but it’ll do as a placeholder for now.

UnityList: Where to find open-source Unity projects

Tags

, , ,

This is not my work, but it’s something I want to spread the word about. Somebody has created a site to list all the Unity-related projects on GitHub.

It is in beta, so there’s still a few things missing. Firstly, it doesn’t allow you to filter by licence yet, but that’s coming. Secondly, it doesn’t seem to preserve the formatting in the documentation/README. However, those are the only issues I have with it, and it’s an awesome tool. So, another place to look for snippets that might be useful when I get around to doing a new project.

NullHunter: An Editor script that flags potential Null References

Tags

, ,

When giving Spamocalypse a test recently, I ran into an issue where I hadn’t initialised a variable, resulting in my console being filled with NullReferenceExceptions. This annoyed me enough that I went and wrote an Editor extension that looks for null public variables in a particular MonoBehaviour.

It’s based around reflection, which is the ability of a computer programme to examine itself. This allows you to do things like create mock objects for unit testing, examine the fields of a class, or even change a private variable or method to public! This is something I’ve never done before, but it only took me a few hours of reading the MSDN docs and a quick check on the Unity forums when I ran into an ArgumentException before I was able to make a basic version.

You can find it on GitHub. I’ve decided to release it under an MIT licence; so you just need to attribute it to me if you use it yourself, but there are no restrictions on how you use it.

Update: I’ve created <a href=”https://www.youtube.com/watch?v=8FrS6jHotsg”>a video demo</a>.

 

New level and lighting update

Tags

, ,

So, Spamocalypse finally has a third level, albeit one that isn’t complete. However, I wanted to check the overall layout and lighting, before I start adding more props.

This one has only two main routes to your goal: the first is to turn left from where you start, and the other is to go straight. Going straight brings you along what I call “Amrachan Road”, which has no enemies at all (spoilers!). However, I will add more, and there’s lots of side paths here that I want to use. There is also very little loot, but I’ll add more.

I also redid the lighting so that street lamps are baked, which makes it easier to see where you’re going. However, I think I may have made them too bright…

Crouching in the shadows

Crouching in the shadows


Standing in the same spot, but now the sun can "see" the player

Standing in the same spot, but now the sun can “see” the player


Screenshot from 2016-06-28 20:16:19
Yes, definitely too bright! And the light doesn't seem to affect the player.

Yes, definitely too bright! And the light doesn’t seem to affect the player.

It definitely looks too bright, although this doesn’t affect the NPC’s ability to spot the player – that is inversely proportional to the distance from that light, with an additional factor for being in the sun. However, this needs a lot of adjustments. Just when I thought I had the mechanics done…

How NOT to calculate lighting for stealth games

Tags

, , ,

My original stealth system for Spamocalypse was based around the navigation system. Each Node has a variable for illumination, which is calculated using raycasts and then baked in. For point, area and spot lights, this is inversely proportional to the distance from that light; for the sun/moon, the value of the sun’s intensity is just added without any processing. When an NPC or the player is trying to check if they can be seen at a player, they query the NavigationMesh to get the closest Node, and then check the illumination of that Node.

What’s the problem?
This is expensive! As I covered in previous post, this causes a performance impact. However, the fix I made for that causes another issue while trying to calculate the effect that street lamps would have on the lighting. They had absolutely no effect – it was possible to stand underneath one, and the NPCs would simply ignore you if there were no other lights around. I suppose I could have rewritten that as a “Refuge In Audacity” thing, where the spammers wouldn’t see you because they wouldn’t believe someone would stand under a street lamp in full view of them…but that’s another game, I think.

The street lamps I have are essentially a long, narrow box which acts as a parent for a point light. When the light was casting rays to check a line of sight to different nodes, it just hit the inside of the collider. So, the next thing I tried was disabling the parent collider for those lights, but I ended up accidentally disabling other scenery colliders. When some other point lights were casting rays around themselves, they ended up hitting points that were outside the navigation mesh, which caused the BuildNavMesh class to throw an exception.

WTF was I thinking?
I just didn’t know of any better way to do this. My first thought was to try using Unity’s LightProbes, but I started Spamocalypse the free version of Unity 4.6, and they weren’t available to free users at the time. I also don’t know if the API for LightProbes allows the intensity to be retrieved as a float, which is what I was looking for. I also had some experience at trying to generate a pathfinding system for my Master’s thesis, so I decided to give that a try, as I knew the general concept would work.

The actual solution
After looking around the Unity forums, I’ve created a physics-based solution that uses trigger colliders and raycasts. Each spot, area and point light has a trigger collider attached, which tracks instances of a CalculateLight component that are inside the collider. If the light has a “line of sight” to that CalculateLight, the light contributes to it’s total intensity. The amount contributed is the intensity of the light, divided by the distance between the light and the object it’s tracking, and at the moment is clamped to a maximum of 1.
There is only ever 1 directional light in a scene, so the Sun component handles this. It has a list of CalculateLight components, and tracks each of them using FixedUpdate. It basically casts a ray to check if the object can be seen, and toggles whether or not it “visible” to the sun/moon. If a CalculateLight component suddenly is in the sun, it’s total intensity is immediately increased by the intensity of the sun, and stays that way until the character can no longer be seen.

Does it work?
Yes, it does. I haven’t done a full test over the City Inbound level yet, but it doesn’t take as long to calculate, and it actually works in real-time – so, I could possibly use this for things like spotlights or torches in future projects. I will update this with the full test results.

New build! (Finally)

Tags

, ,

I finally have a new build. Most of these are performance fixes that I covered in my last post, but I also added a lot of extra stuff to the City Inbound level.

Using Coroutine for line-of-sight detection
While trying to improve the performance of the line-of-sight detection, I started using a Coroutine instead of OnTriggerStay. OnTriggerStay is called on the physics timestep, which by default is 0.02 milliseconds. I set my Coroutine to run every 0.25 seconds, which spreads out the relatively expensive conversion of a position to a Node. It’s not perfect, but it will do for now.

Spicrab Storage and Spicrab Square
This is a former storage warehouse. At the moment, it’s mostly empty, but there are a few notes to find inside the office. Two of the other buildings around the square are accessible, but the rest are not. One of the buildings contains a lift…which I haven’t tested yet.

WMG
A shop that sells washing machines…in a medieval city. This is a reference to a particularly obnoxious spammer that used to clog up the Unity forums with threads about fixing washing machines, fridges or air conditioning. I originally planned to add this arsehole in as a particularly persistent boss, but I ditched that early on.

Sewers
You now start in the sewers, and must find your way to the sewer entrance next to the Museum. That will lead into the next part of the City, which I am hoping to finish within a month or so.

Screenshots
Have some screenshots! These are all from the Editor, rather than the build.
Barry's Diner/Tea Shop
Museum Square, with the firewalls casting an orange glow over the entrance.
Looking down Loam Street

Optimising Unity

Tags

, ,

For the last three or four weeks, I’ve been trying to fix several performance issues I found while testing. Some of these are graphics issues, which I can’t really fix myself, but the scripting ones are things I can work on. Some of these come from an interesting thread on the Unity forums – which is definitely worth a read. Anyway, here’s the changes.

Removing concave colliders
Concave colliders in Unity are Mesh Colliders that don’t have the “Convex” button checked, and they’re discouraged because they’re quite expensive. In quite a few cases, it is possible to decompose the mesh into primitives, and Unity themselves recommend using compound colliders where possible. I admit, I’ve been guilty of doing this for Spamocalypse – a lot of the scenery is built using Mesh Colliders, though I have been careful to mark these as static to try and offset this, and I’ve been breaking some of them down into separate parts. As it stands, I had to replace the octagonal sound detection meshes with capsule colliders to get the physics cost down when I had at least ten active NPCs in the scene.

Splitting the second level
I ran into a massive problem building the lighting with the second level, mainly due to Unity’s global illumination having a problem with large scenes. There is a way around that, which is to temporarily scale the level down to about 1% of its original size, but I also had a problem with generating the navigation mesh. So, I’ve split part of the second level into a third, which the player will enter via a sewer pipe. This means I’ve had to add more scenery and things to find…which I had planned anyway! So, win-win.

Profiling the build
Normally, you’d test the game in the Editor. The problem is, testing in the Editor includes some extra overhead, so the profiler won’t necessarily be accurate. So, the best thing to do is to profile the final build. The documentation is a great starting point, especially the part about manually profiling segments of code using “Profiler.BeginSample” and “Profiler.EndSample”. Here’s a quick example:


float calculateDistance(Vector3 firstPosition, Vector3 secondPosition)
{
Profiler.BeginSample("getSum");
float distance = Vector3.Distance(firstPosition, secondPosition);
Profiler.EndSample();
return distance;
}

Doing so has uncovered several time sinks in my pathfinding code: the method to translate a position into a node was taking about 5 milliseconds. This is due to my (over-)reliance on Linq to search for nodes: using an Aggregate query on a nav mesh of about 10000 nodes is going to impact performance! So, what I did was change the following:

Node theNode;
if (nodes.ContainsKey(requestPos))
{
theNode = nodes[requestPos];
}
else
{
theNode = nodes.Values.Aggregate((d, n) => Vector3.Distance(d.position, requestPos) <
Vector3.Distance(n.position, requestPos) ? d : n );
}

to round the position to one decimal place before checking if the position exists. If not, it retrieves a limited list of nodes that are less than e.g. 1 metre from the specified position, and then finds whichever fits closest. This alone cut the request time from about 5 ms to 3ms.

Node theNode;

// Round to 1 decimal place
requestPos.x = Mathf.Round(requestPos.x * 10) / 10;
requestPos.y = Mathf.Round(requestPos.y * 10) / 10;
requestPos.z = Mathf.Round(requestPos.z * 10) / 10;

if (nodes.ContainsKey(requestPos))
{
theNode = nodes[requestPos];
}
else
{
// first, prune any nodes that are too far away
// then find whichever is closest to the requested position
List tmpNodes = nodes.Values.Where(d=> Vector3.Distance(d.position, requestPos) Vector3.Distance(d.position, requestPos) <
Vector3.Distance(n.position, requestPos) ? d : n );
}

However, this is still slower than I’d like. I really need to find a better way to calculate light intensity; baking it into the navigation mesh wasn’t as good as I thought!