Scraps: Modular Vehicle Combat cover
Scraps: Modular Vehicle Combat screenshot
Linux PC Mac Steam
Genre: Shooter, Simulator, Indie

Scraps: Modular Vehicle Combat

Gauntlet mode in development: Level building

Weekly update time. This week I've continued working on level content and generation. I added some more content to the Earth level, and added the remaining features to the level gen that I needed:
[LIST]
<*>Since Gauntlet levels can be played in forward or reverse direction, I added an option to include level content in one direction only (so I can do stuff like have walls that are always behind something no matter whether you're playing in "forward" or "reverse").
<*>Added an automatic rotation option to mirror the direction of things in reverse mode. So I can do stuff like have turrets automatically flip to face the other way.
<*>Added a specifier to have things not spawn until a certain level. Since each world (each map) will have 2-3 levels played within it, I can say something like "OK, don't have drones appear until level 2" and they won't spawn in World 1-1.
[/LIST]



Hopefully that's now every feature it needs. I'll really try to stick to that because I know in gamedev it's all too easy to get stuck working on the "engine" forever and never actually make the game. I also think the level gen is all working correctly now (it's a pretty serious probability-processing machine at this point), it just needs some adjustments.

I added some giant arrows in the sky for myself in the Unity editor so I actually remember which way is the world's forward direction...



Oh and I modelled some of those "taller barriers" that I mentioned were needed last time.



Gauntlet mode release TODO status:


Bold = currently working on.
[LIST]
<*>Finish up the level generation system - All features now in. Some adjustments still to do.
<*>Populate the three maps with lots more content for the level generator to choose from - Working on map 1.
<*>Finish up the desert and snow terrains
<*>Write the framework around the new mode – handling game start, game end, scoring etc
<*>Make some more vehicles for the AI to use
<*>Add leaderboards? Or initial release might just give you a local score, and add online leaderboards later
[/LIST]

Gauntlet mode in development: Thinking about level design

This week I fixed some bugs with the Gauntlet mode level generation and I've been testing my level generation out.

I looked at two things that I noticed were an issue:

Issue: A need for cover in the early game


In Gauntlet you start with a small and cheap vehicle, and I found that you'd end up shooting a machine-gun turret with a machine-gun and it lacked much room for strategy. You can sneak up to some extent but often you crest a hill or something and you both have line-of-sight and you might as well shoot or get shot. I can beat the levels easily enough but I've been playing Scraps for years - you shouldn't have to take x amount of damage every time.

Solution: I'm just going to add some taller barriers around the place basically, so there'll be more opportunities to approach from a strategic angle.

Issue: Searching for that last crate


You want to shoot crates to collect scrap, and if crates might be out in the wilderness somewhere, you're going to want to scour the wilderness to make sure you find them all.

I knew that would be a bit of a chore, so I made most things spawn along the road that goes through every Gauntlet level. But it doesn't fix the problem because there's still some chance that things will spawn out in the middle of nowhere, so you end up going looking anyway. And removing that chance entirely would mean no reward ever for exploration, so I wouldn't do that.

RPGs have had this problem for a long time. The theory is of course that when there's somewhere out-of-the-way to explore, you get something cool to reward your exploration. And having something there is probably better than exploring and finding nothing. But it also means now you have to explore every boring side-passage because you might miss something cool.



Image credit: Possibly Adrian Chmielarz.

Solution: I've added a radar mini-map so you can automatically know there's something out there from a good distance. I don't think I'd add a radar in melee mode/multiplayer because having a radar map would remove a player's ability to surprise the enemy. But in singleplayer I think it'll be fine.



Enemy units are yellow crosses, non-threatening objects that contain scrap are blue squares. Trying to think of the colour-blind in these design choices.

 

Scraps v0.5.5.0: The Plasmanator Update

The first week after I made the Gauntlet announcement post was a bit of a mess and I didn't have a lot of time for Scraps work. It was getting near time for an update post and I was left wondering what I could make the next one about. What could I do in a couple of days or so? I didn't really want to do another small Gauntlet mode work post because that's all I've been doing for a while.

So my decision was, you know what, it's time to take a short break from the new game mode work and put something new in the game, for the players.

I looked through my figurative bag of potential parts to add and the Plasmanator came out.



Changelog for v0.5.5.0


- Added Plasmanator weapon
- Replaced uLink closed-source version with uLink source code
- Reduced network bandwidth use a little

Plasmanator facts


[LIST]
<*>Area effect weapon like a super Plasma Artillery.
<*>Charges up. Holding down the Fire button charges it, and releasing actually fires it.
<*>Drains lots of power while charging, and some power while sitting at full charge.
<*>White smoke and a change in audio indicate full charge.
<*>More charge = more damage and force on hit.
<*>Unlocks at 4700XP.
[/LIST]
More charge also used to mean a faster bullet, which was nice conceptually but made it horrible to aim because you could never really get a feel for it unless you always waited for full charge. So the firing force is constant, but the hit force does increase with charge. There are bigger bullet FX and hit FX for the weapon for bigger charges.




I wasn't sure how a charged weapon should handle the "R" key aiming mode where weapons only fire if they're aiming inside the circle area. It's just not a great weapon to use in that mode really - and you can of course turn it off. Currently it works the same as any other weapon, which means that the firing state cannot be active for it when it aims outside the circle. Since it charges while "firing", it will charge while inside the circle if you're holding fire, but if it goes outside the circle while being charged it'll fire off a shot since the "firing" state is turning off.

I also wasn't sure how the AI should use it. Well, conceptually I was, but algorithmically... well, I wish I could just tell it to "behave like a human." I'd given Dave an assignment to look at it sometime but then I ended up doing it myself today. Basically I told the AI that if it's firing a charged weapon, it should release Fire momentarily to let off a shot IF:
[LIST]
<*>Its aiming is going off-target OR
<*>The weapon is fully charged OR
<*>Power levels are very low (which would suggest that it won't be able to charge any higher)
[/LIST]
What I sometimes find is that in my code I'll go maybe a little overboard trying to find the perfect solution that covers every possible case, whereas Dave will code something super fast that's simple and yet more often than not turns out to work fine. Sometimes it'll need some tweaks and changes later, but usually the fact that it's simple at first means it's also easy to change. I feel like I might have managed a bit of a Dave-style solution here for once where I was worrying about how the AI was going to handle all these complex weapon scenarios, I ended up just adding a few simple rules, and actually it seems to do well enough to fool me that maybe the AI could even be somewhat intelligent.

Of course as always if you find any bugs with things, please report them.

This video was in my sneak preview update but here it is again. If you want to see something new with it, just load up the game - the Plasmanator is in the game right now!

https://www.youtube.com/watch?v=9hKUO4t5y3M

P.S. Take note of where your shots hit. If you don't seem to be doing much damage, you may be hitting in front of the enemy instead of directly on them. The bullets drop fairly fast so aiming a little above the target is key.

Weapon preview

Since I’m being slow with the next update post, here’s a sneak preview.

https://www.youtube.com/watch?v=9hKUO4t5y3M

Not-so-secret new content preview #10/10: Gauntlet

The new singleplayer Scraps game mode I'm working on is called Gauntlet.



You know how games are prone to having some sort of epic conflict you've got to resolve? Yeah well, here that conflict happened, and now it's long over, and it left only ruins behind.

Here, we fight over the scraps.

And there's one place with the biggest stockpile of scrap of all. And it's well defended. But you're already on the road.

Overview


OK enough cliché. So how will it play? Well, it's sort of a Scraps Roguelike, or more like what's being referred to these days as a Roguelite or (and I realise this is the dumbest term ever) a Roguelike-like.

I usually avoid comparing Scraps directly to other games in official posts as it always seems a little sad when someone can only describe their game in terms of other games. But to get a basic idea, take Vlambeer's popular Nuclear Throne for instance.

Nuclear throne isn't much like Scraps in terms of how the game actually plays, but imagine the character is your Scraps vehicle, the Rads that you pick up are scrap, the mutations you buy are vehicle parts, and the end-of-level portal is an evac pad at the end of a road, and you might sort of have a picture of what Gauntlet mode is.

Gameplay Breakdown


Everything here is subject to change since a lot of this hasn't actually been tested yet. It has to play well. But the idea is:

1. Player starts with around 3000 scrap to create a basic starter vehicle.


2. They enter a semi-procedurally generated level in World 1. I talked a bit about level gen in preview#7. A piece of a larger map is selected, and the player and evac pad are placed:





Then out of a set of hand-placed content, a subset of that content is selected to use in the level:


(top-down view showing active stuff highlighted in green - you might need to click for full size to see it well)

3. The player traverses the level
4. The player reaches the evac point at the end of a level
5. The can spend scrap they've collected on their vehicle
6. Next level begins...

After completing the three levels of World 1, they go onto World 2. If they manage to complete all the levels in all the worlds, they loop back around to World 1, but now they can no longer collect any more Scrap. The goal then will be to destroy as many enemy units as possible before their vehicle is destroyed.

Final score will be based on:
[LIST]
<*>Total scrap value of enemy units destroyed (i.e. scrap in crates doesn't count)
<*>As a lesser factor, time to complete the initial run (i.e. before looping back to World 1)
[/LIST]
Looping back should be difficult - it's not meant to be easy to complete. And if the player dies, that's it - they start again.



Some more details:
[LIST]
<*>Every level has a road going through it, with you spawning on the road at one end and the evac pad spawning at the other end.
<*>Every time you enter world 1-1 or whatever specific level, it will be generated with roughly the same total scrap value, divided in the same way between enemy content that shoots you and inanimate stuff like crates. As the level increases the total scrap in that level also increases.
<*>You do not have to destroy the enemies in a level to activate the evac point. You can sneak around (most content will be on or near the road) and just go straight there if you like. But you won't have collected scrap from destroying things, which will likely harm your chances eventually, and you won't increase your score.
<*>At the end of a level, you'll get some "free" repair time on top of the scrap you've collected - an equivalent scrap value of vehicle repairs that will be performed for free (including destroyed parts), but that can't be spent on additional parts. If you have more damage than your free repair time covers, you can still spend scrap on repairs to cover the rest if you have it. This means that more of your scrap collection can count towards actual upgrades - it levels out the results a bit and encourages actually going after dangerous enemy units.
<*>I'm thinking of three levels per world because well, I have to get a certain amount of use out of the content I'm creating, but it may become two.
[/LIST]

ETA


I'd love to also be saying "and the Gauntlet update will be released on {date}!" here as well, but the fact is I'm not sure. I still have a fair amount to do on it especially with having other part-time work on now. What I can say is that I will keep writing updates on progress every week. I was writing updates every two weeks before I started doing the Secret New Content Previews; I'm going to continue that as weekly now. It's good motivation for me as well to get significant things done and not get bogged down in the small details and less important tasks. Here's what's done and what's not on Gauntlet:

DONE
[LIST]
<*>Created all enemy and inanimate unit types for the new mode - turrets, drones, crates, walls etc
<*>Created three maps for the new mode, which is enough for the initial release
<*>Wrote the AI for all the new unit types and updated (with Dave's help) existing vehicle AI
<*>Got some AI vehicle designs from people on the Scraps forum
<*>Got my semi-procedural level generation system working
<*>Wrote some tools to make creating new terrains super fast
[/LIST]
TODO FOR INITIAL RELEASE
[LIST]
<*>Populate the three maps with lots more content for the level generator to choose from
<*>Write the framework around the new mode - handling game start, game end, scoring etc
<*>Finish up the level generation system
<*>Finish up the desert and snow terrains
<*>Make some more vehicles for the AI to use
<*>Add leaderboards? Or initial release might just give you a local score, and add online leaderboards later
[/LIST]
TODO LATER
[LIST]
<*>Token system: Along with scrap wreckage, I want vehicles to be able to drop tokens that let you unlock new parts - either just within the current game, or usable anywhere. For example, a super big cannon you can use in the current game, or a cosmetic piece you can use anywhere that you can only get by getting so far in Gauntlet.
<*>More worlds: Ideally I want five worlds rather than three. Some level of extra content might depend a bit on how much people actually enjoy the game mode.
[/LIST]


Thanks for reading!

P.S. I suddenly thought I might have accidentally mentioned the game mode name back at preview #5 when I got the comment on it. Well predicted.

Scraps v0.5.4.5

Just a small update mostly fixing a couple of issues with the previous release.

2016-6 - 0.5.4.5
- Improved in-game camera collision avoidance
- Made damage label colour more obvious
Bug Fixes:
- Fixed power management bug in previous release
- Fixed false cheat flags

Super secret new content preview #9/10: Cold water

I've been working on the third and final terrain type I want for the initial release of the new singleplayer mode. To complement the existing Earth/Fire worlds, the next element is Water. Cold water.





Slippery ice.





Tune in next weekend and I'll properly explain what the new game mode actually is!

Scraps v0.5.4.4

No special preview this week I'm afraid, but there is a game update.

I didn't intend to do an update right at this moment but I've finally, finally fixed a memory leak on the Scraps server that I've been trying to track down for months. I've been looking at it on and off in the evenings so that it didn't impact development on the game, but I've also really wanted to get it fixed. If you ran a game, even a single-player one, for a long time with lots of vehicles being spawned and destroyed, it'd eventually mess up and weird things would start to happen - like all damage would stop registering. It also meant that dedicated servers needed much closer supervision than they should have needed.

In the meantime while I've been working on the new game mode I've also been fixing other things, so there are some other bonus changes here as well.

Changelog


2016-6 - 0.5.4.4
- Added partial Polish translation
- Adjusted collision damage down a little
- Adjusted AI targeting a little. More shots at parts, less direct chassis shots
- AI aiming calculation now takes the shooter's velocity into account as well as the target's. Be afraid
- Made the in-game chassis colliders more detailed. Previously there was a box that could end up "protecting" some low components like small engines, with the chassis taking damage instead of the hit part
- Adjusted some sounds and volumes of various things
- Removed wreckage size scaling as it spawned since it looked dumb and was dumb
- Had to exclude screen resolutions that don't match your monitor's aspect ratio due to a bug in the Unity engine. On Linux resolution reporting is too fickle to limit: If a chosen res doesn't look right, just try another one
- Added visual indicator to health bars to help show when damage is done
- Wreckage pickup is far more performance-efficient. No more slowdown when complex vehicles drive over wreckage.
Bug Fixes:
- FINALLY tracked down a memory leak on the server that was causing trouble when running the game for a long time
- Fixed a couple of bugs with bullet spread. Machine-guns were more accurate than they looked visually (they match the visuals now)
- Projectile trail FX angle fix

2016-4 - 0.5.4.3
- Improved AI pathing quite a bit
- If AI gets stuck persuing in a circle, it'll eventually break out
- Updated the vehicle/environment shader look a little
Bug Fixes:
- ATI cards that didn't support DirectX 11 had some issues with the new terrain shader. Disabled some features if ATI+DX9 is detected to help the look

Info on the bug in the form of a technical description


The Scraps server was slowly using up more and more RAM, until eventually the whole thing would crash with a stack overflow exception. But the stack wasn't the problem - it was the heap.

I knew that it was the heap because looking at the Unity profiler, it appeared that all objects were being garbage collected just as they should be, but the thing that grew was Other->ManagedHeap.UsedSize. In other words things on the heap were getting allocated and not deallocated, so the heap had to keep growing when new things were made.

Unfortunately in Unity it's impossible to inspect the heap, although it is now possible on platforms that use IL2CPP. Being able to inspect the contents of the heap would have solved this much faster.

I fairly quickly worked out that creating and destroying vehicles was the problem, but the profiler reported that everything on the vehicles was successfully destroyed. Yet the leak implied something big was being held onto. There had to be a reference somewhere to something on a vehicle from something outside of the vehicle, that never got freed.

My eventual discovery after slowly hacking pieces of Scraps apart was that it was an event subscription to an event on a static class.

Scraps parts have a Health class that's a MonoBehaviour, and Health has a non-MonoBehaviour class that acts as a sub-component to do different things depending on whether this is a client or server machine (remember that this leak only happened on the server).

When the health script was created, the member class subscribed to a static tick event on a static clock class that the server has. When the health script was destroyed, it run its explosion effects and all that, and also told the member class to unsubscribe from its event... most of the time.

The problem was that the Health script's call to the member class wasn't in its OnDestroy, it was in another method that did all the destroying actions for the part and then called Destroy() itself - and that method didn't get called in every situation. Thus sometimes the event never got unsubscribed, and the reference to the member class was held by the static clock class forever, preventing it from being destroyed.

Much more was being retained on the heap than just that one class, so my theory is that Unity was managing to remove the vehicle's data on the C# side, but not in the C++ back-end. Thus the profiler would show that every object was destroyed successfully, but in fact the static clock class' reference held onto the non-MonoBehaviour class which in turn was holding onto the whole vehicle part with its memory-consuming mesh and textures.

To fix the issue for certain I basically just moved the cleanup code into the OnDestroy of the Health script, guaranteeing that if the script is destroyed, its member class gets cleaned up as well.

TL;DR: If your heap is growing forever and you aren't leaking any objects, one thing to look for is events that mightn't be being unsubscribed. And don't think that a GameObject being gone means it's necessarily really gone.

Info on the bug in the form of a story


Guests come to a party at your place and they want to use your dinnerware, but you want to keep track of who's taken some out of the cupboard so when a guest takes a plate, you also get them to tie a rope around their wrist with the other end tied to the cupboard (you're a particularly annoying host).

When a guest leaves the party, they'll put back their plate, untie their rope, and leave.

But sometimes, due to an oversight in your party planning, guests can leave without returning their plate - with their rope still tied to the cupboard!

Later the party is over and it looks like everyone's left, but you find that you can't fully close the door because there are some ropes still tied on to a bunch of people outside. Over time and several parties, you accumulate more and more guests who are still tied on with ropes.

You find that you cannot get all guests to return their plates before they leave (as a terrible host I assume you've also attracted terrible quests). But you can fix the problem. You revise your party rules so that instead of guests untying their ropes when they return their plate, they must untie their rope when they leave the party, no matter what. Now your house and yard are properly cleared out every time a party is over. And you buy some new plates I guess.

Super secret new content preview #8/10: Desert landscape

For the new singleplayer game mode I need three maps to start off with.

You've seen the initial Gorge map:


Desert Map


Now I've started on a desert map. In my mind I have this basic idea of the four classical elements - Earth, Wind, Fire, Water - where the gorge map is Earth and is sort of the starting Green Hill Zone of the thing, and the desert is Fire. Artistically though I was inspired by the real Desert Road here in New Zealand:



Photo credit: Nicolas Leroy.

So not a sandy desert, but more of a tussocky one left barren by volcanic activity.



I'm still working on the look of this but here are a couple more shots:




Other Part-Time Work


Something else I want to let you know about in case it affects development times (I do realise I'm not churning out updates fast as it is - I really try, but doing everything yourself always takes a while). I've got some other part-time work starting this week. I've been working on Scraps full-time for years now but it's never made the sort of money one could live off. That's fine, I'm making this game because I've wanted to play it since the 90s and not because I want to be rich, but I'm going to have some other work to do on the side.

Please don't let this worry you about Scraps ever being left unfinished. I've been wanting to play this game forever, it's taken longer than I hoped but it's really starting to look like the game I've always wanted, and unless I die suddenly I'll be working on it and it's getting made.

Super secret new content preview #7/10: Procedural and hand-crafted

Super secret new content preview #7/10: "Procedural and hand-crafted"

This update is going to end up giving away a bit about the new game mode but that's OK, I'll explain everything at preview #10 and that's not too far off anyway.

So, in the new game mode I wanted levels that are a bit different every time. Each one short but always something new. The standard solution to that is to use procedural generation, and have the code create something that's different every time. But procedural can also mean bland, generating the same things with the same rules in different places, and for every game where it works well there's one where it doesn't.

I'm taking a sort of a hybrid approach here which with some luck will almost give you the best of both worlds. Here's a complete map for one "world":



That's big - it's over 7km long. But when a level generates it just takes a small chunk of that complete terrain, starting anywhere along it, and putting travel in a forward or reverse direction. I'm still tuning the exact length that'll be, but it's in the region of 1.5 or two kilometres.



Then within that area, things get more complicated.

Throughout the whole level the road is your guide.



Spawn on the road, leave on the road.

Within the level I can hand-place scenery, walls, turrets, enemy vehicles etc. A lot more stuff than the level will actually need. Then I tell the level generator OK, spawn some stuff in the level for a total of about x amount of scrap, and about 60% of it (or whatever) should be in stuff that attacks you, and the rest should be harmless (like crates). It then looks at all the groups of things and probabilities within the selected section of the map and decides what to spawn.

I've set it up so I can pack the entire contents of the level up so instead of having a bajillion things spawned in the world and then deleting most of them (which would waste a bunch of time and RAM), everything in the level is stored only as information about that thing, and then only what's selected is spawned. Everything can be unpacked (basically, spawn everything) so I can edit it, and then packed again for in-game use. In Unity engine terms it basically stores the prefab used, the position and rotation, and then any other custom information that particular thing needs to configure it.

But the key feature is that I've also set it up so I can create everything in groups with custom spawn probabilities. Here's an example of one group of things that could be selected to spawn:



This whole thing - the roadblocks, the turret, and the two crates - has a "normal" chance of spawning so it has about as much chance as anything else to be selected. If it's too close to something else that's already selected, or its scrap values don't work out with what the level needs, then it probably won't be chosen.

But within the group itself, it can choose different subsets of things to spawn as well. This subset will be chosen as soon as the level generator looks at it, so that it can know exactly what scrap value it'll have.



The roadblocks in the middle are set to always spawn the concrete barriers OR the wooden walls, but but never both. So it'll never actually look like the above in-game.

Neither the crates nor the turret are guaranteed to spawn at all, even if the group is chosen - you could get just the roadblocks. But somewhere between zero and all three will be chosen, with one of the crates in this case being a rarer spawn than the other. I could also set it to only spawn one thing out of three, or 1-3, or 0-2 or whatever. And the spawners can be nested so one of the choices could be another spawner with its own mix of guaranteed things and sub-choices.

But even as it is there are 16 possible combinations of just that one small set.

For AI vehicle spawns, I can now define patrol paths so they can patrol around, or just sit and wait.



Right now most of the code for level generation is done, as is most of the stuff to put in the levels, but I still have a lot of work to do in actually populating levels and in the actual framework of the new game mode. Sometimes it feels like things are progressing at a decent speed, sometimes it doesn't, but rest assured that I'm working on it.

See you next weekend.