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

Scraps: Modular Vehicle Combat

Gauntlet mode: Free repair time

Gauntlet tasks to do before initial release


Get local games fully working
Object for holding game state in Gauntlet mode
Auto-repair system + vehicle swap ability
– Gauntlet game flow from menu to end
– Score system
– Level generation tweaks
– Update scrap drop system
– Item unlock system
– Update How To Play screens
– Test and balance gameplay
– Music?

Free repairs


At the end of each Gauntlet level I'm giving the player some amount of free repair and rebuild time.



This is measured in "minutes" because I don't want people to think it's using up some of their scrap, but there's a straight conversion between "minutes" and "scrap". For instance one minute might give 50 scrap worth of repairs. The implementation of all of this could change, but at the moment I'm giving the player some amount of free repairs (which can include rebuilding lost parts) at the end of each Gauntlet level, and each level gives a bit more time than the last (covering the fact that that player's vehicle is probably getting bigger).



The free repairs are currently automatic, but they'll try to be not too dumb, and always repair cockpit and chassis first.

Why


The thing is, Gauntlet mode will be a series of levels where you keep the same vehicle, so if I don't provide any free repair time then there'll be a huge spread in potential vehicle value once the player has completed a few levels, which becomes hard to manage.

Say I want to have the player be able to, on average, increase their vehicle value by 3000 at the end of a certain level, and I also decide that I'd like to let them recover from about 50% damage. I calculate what I think their vehicle will be worth at that point following the formula - say it comes out that their vehicle might be worth 4000 scrap. 50% damage = 2000... So I'd put 5000 scrap in the level to cover 2000 repair/rebuild + 3000 upgrade. But of course they might take more or less damage than that, and compounded across several levels you have potentially HUGE ranges in vehicle value. Someone who takes no damage can increase their vehicle's value by 5000. Eventually, just by dropping enough scrap for an "average" player to repair 50% damage, the player who cleverly takes no damage could have a vehicle worth even more than I really want to allow for performance reasons. Or just totally streamroll through levels that are balanced for lesser vehicles.

The more free repairs I give, the more levelled the playing field becomes. With unlimited free repairs, all scrap collected could go to vehicle upgrades. I'm still experimenting with how much free repair time I actually provide. I don't want everything to be totally balanced and boring either.

How


That big repair/rebuild button that you see on the Build screen in Melee mode, I've actually updated that (in my dev version, not live) to use a GUI more like the above screenshot except with scrap showing instead of minutes. Then the end-of-level repairs are actually a special case of the same system. Internally instead of repairing using the player's banked scrap, a special temporary wallet is created with some scrap in it, and that's used to do the repairs. So the code path is exactly the same except the "money" for it comes from a different place. Keeps things simple vs. having two different systems to maintain.

Vehicle Swap


This just means you can swap your vehicle out somehow, so if you start with e.g. a Small chassis you can upgrade to a Medium or Large. I won't say much about this now because how I handle it might change.

Game Flow


The next task on the list is the flow between levels. I've already been working on that as well, with a major issue being level loading times. Loadnig a Gauntlet map was taking about 15 seconds on my relatively decent PC, but I've now got it down to around 5½ seconds. I'll talk about that next time.

***

I know there's been a bit of a gap between the last update and this one. I've actually been fixing a few other issues around the game as well. Nothing really worth talking about, although if you've ever done a repair/rebuild on a vehicle and had it error out saying a part can't be attached, I discovered that issue too and it'll be fixed when Gauntlet releases.

Gauntlet mode: Game state object (and some technical rambling)

Gauntlet tasks to do before initial release


Get local games fully working
Object for holding game state in Gauntlet mode
– Auto-repair system + vehicle swap ability
– Gauntlet game flow from menu to end
– Score system
– Level generation tweaks
– Update scrap drop system
– Item unlock system
– Update How To Play screens
– Test and balance gameplay
– Music?

"Object for holding game state in Gauntlet mode" is probably the most boring entry on the task list (wow what a great way to open a blog post), but let's talk technical stuff.

Scraps and persistent state


The Unity engine that Scraps is written in has this concept of "Scenes." You can put stuff in a scene, like make a level for your game. You can attach stuff to stuff in your scenes, like lights and physics and scripts that run code. A nice thing about Scenes is that when you go to a different one, everything you put in the first scene is unloaded, so you know you didn't accidentally leave anything hanging around taking up memory[1].

But sometimes obviously you want stuff that doesn't go away on scene change. Scraps Melee games need to remember who's playing when the map changes, Gauntlet games need to remember the player's scrap amount/score/etc when they move between levels, everything needs to remember which vehicle the player has selected and so on. You can create code that doesn't get destroyed on scene changes, either by making it totally separate from the scene system, or by marking it in the scene with a special DontDestroyOnLoad flag.

Anyway, in the Scraps system:

(click for big)

Everything there except ServerStartupInit (which just gets things going on the server) is persistent between scenes. Server is an object on the server, sort of a master controller. Client (for multiplayer) or Local (for singleplayer) are its client-side equivalents. There's a ScrapsPlayer for each player in a game - both AI or human. VehicleBase is a script on the vehicles themselves. It's subclassed into Client and Server variants. Having a ScrapsPlayer rather than having a player be their vehicle means that a player can continue to exist and run code when they don't have a vehicle, like between being destroyed and respawning. Whereas having VehicleBase on the vehicle means that vehicle-specific actions can still be done on the vehicle itself.

For instance say a ScrapsPlayer decided to set the health level on a vehicle in 1 second's time, and within that time the player's vehicle changed to a different one, it could end up setting that health level on the wrong vehicle. If the vehicle script decided to do the same thing and the vehicle got destroyed within that second, the command would get destroyed as well and never happen. OK that's a pretty contrived example, but I do remember something somewhat like that being a real issue at one point. Network latency in particular makes everything need a bit more careful protection.

ANYWAY, I haven't needed to update any of that very much, that's just some random info about the whole system. What I have needed to do is add a new type of GameData, because GameDataServer/GameDataClient/GameDataLocal were more like MeleeModeGameData.

I mean look at this stuff in GameData:

(SUPER SECRET SCRAPS CODE DO NOT STEAL™)

Number of players, networking stuff, starting scrap and other melee game settings. None of that's needed for a new singleplayer mode and adding Gauntlet mode stuff to it would just make it a big mess.

So anyway GameData is now MeleeGameData and there's a new GauntletGameData too. It stores the player's score, what level they're on, etc. When a new game is started the appropriate GameData object is created based on the game type.

I'm not sure if the next item on the list completed will be "Auto-repair system + vehicle swap ability" or "Gauntlet game flow from menu to end". The former is part of the latter but I might be able to do the game flow without having the new build screen systems totally ready.



[1] As with many things code-related, this is sadly true only in the most naïve, optimistic theoretical way. If you have anything that doesn't get destroyed on a scene change (like a static class or a class with DontDestroyOnLoad), and it references something that should, the thing will still stay in memory until the reference is cleared.

Gauntlet mode: Singleplayer without networking

I'm going to try and post news about what I'm working on a bit more often again. Here's my list of what I need to do for an initial Gauntlet mode release:

Gauntlet tasks to do before initial release


- Get local games fully working
- Object for holding game state in Gauntlet mode
- Auto-repair system + vehicle swap ability
- Gauntlet game flow from menu to end
- Score system
- Level generation tweaks
- Update scrap drop system
- Item unlock system
- Update How To Play screens
- Test and balance gameplay
- Music?

Looks a bit long and scary, and yes it is rather a lot, but not all of those things are major. I thought the first one was pretty major and I've already got it done. What I'm going to do is, each time I complete one of those, I'll make a post and talk about that line item in more detail. When they're all done there'll be a real game update.

Let's talk about "Get local games fully working".

Local Singleplayer


I'd like to start with Minecraft as an example here. When Notch originally made Minecraft, the singleplayer and multiplayer components of the survival mode were separate. Bugs would often appear in one but not the other - usually multiplayer because networking is hard, man. A lot of work had to be done twice, in the singleplayer game and also in the multiplayer one. I read an interview somewhere where Notch mentioned that one thing he'd have liked to have done differently was going fully multiplayer from the start, and having the singleplayer just run on a local server. Eventually that's exactly what happened: The game was changed to be always multiplayer behind the scenes, and that's why anyone can easily join your singleplayer game on LAN now if you choose to open it up.

When I built Scraps I decided that likewise, having one networked system that everything used was the way to do. In the interests of simplicity, the Melee game mode always runs a separate server. That's why it was easy to add the "Allow other players to join" option to singleplayer games. Whether you host a multiplayer game or whether you start a singleplayer game, a local server starts up and you silently connect to it.

I don't think I made a mistake in doing it that way because it works well, but there is an effect on CPU performance because some calculations have to be done twice (the graphics card gets away free here because I run the server with no graphics). For Gauntlet mode I want to have good performance on moderate PCs even on later levels with big vehicles, and getting rid of the need for the server is an obvious win there. Plus I'd done a lot of the work already as I'd needed it in the past for testing. The changes basically entailed writing new paths for things when there was no network present, so what usually had to wait for server confirmation etc would do its own thing.

Now, converting a singleplayer game to multiplayer is always a big job, sometimes such a major change it's just about impossible. But converting a multiplayer game to singleplayer has been a lot easier! Not least because things always get simpler rather than more complex. This class structure in Scraps:



becomes this:



I've actually still got a couple of minor issues to fix but it's basically all working. As a bonus, when the Gauntlet update comes out you'll also be able to play the Melee game type in a true local mode - I've got that working as well - if you have "Allow other players" unticked:



That should give a bit of a performance boost for people with low-spec CPUs (to be clear, you won't see this performance boost now, it'll come with the Gauntlet update).

 

Gauntlet mode update: Worlds 1 and 2 done, framework next

Thank you for your extreme patience as I continue to bumble along with this game in what spare time I have. Last update, I suggested that I'd get Gauntlet mode's World 1 finished, and then work on the framework for Gauntlet mode iteself. In the end I decided to get the first two worlds done first, since both will be needed for a playable release of Gauntlet anyway. I've now got the content for the first two worlds complete.

The Gauntlet mode worlds will be loosely themed on the classical Greek elements: Earth, Water, Wind, Fire. Earth and Water now have all their content, and if I give them three levels each, that'll be enough for an initial release of the new game mode that you can try out. As in an actual game update, not just more progress news like this. What I still need to do first is write the framework around the new mode: How you enter levels, progress through them, repair between levels etc. It's stuff  that's mostly in the game already but it needs to be strung together correctly and have some new concepts added. At the moment to test Gauntlet mode, I use dev tools to select a vehicle and drop into a level manually. I'm afraid I don't have a release time estimate because the amount of time I have for Scraps currently varies a lot week by week. It'll get done.

I've also been making a few other changes to the game as I go, that'll make it in when Gauntlet launches. One I can show off is a new primary firing mode:

https://www.youtube.com/watch?v=mJVNoWncFjQ

It's something that was brought up in the forums a while ago. People were carefully building vehicles that didn't have quite enough power supply for their weapons, so the weapons would end up dropping down into a sequential firing mode instead of all firing at once - meaning less recoil, and more spread out power and heat. Now that mode can be automatic, but the old mode is still there as well. There isn't really any additional complexity to worry about for the player - left-mouse will be the new mode, or right-mouse will fire using the all-at-once mode that's in the game now.

Some new screens of the Gauntlet worlds I've been making


EARTH is sort of the Green Hill zone of the thing. It's grassy hills and rocks and trees, and combat is against lewer-level enemies: Basic machine gun turrets, mostly micro vehicles, hover drones.



WATER is snow and ice. AI vehicles are more expensive and more frequent, turrets are more powerful, and drones are less common. But your vehicle will be more powerful too.

Still Alive

I haven't posted for a while as I haven't had anything particularly interesting to show, so I just want to check in to show that I'm still here.

When I get some time I've mostly been working on content in Gauntlet mode World 1. Once I've done everything in that world I'll be writing the framework that surrounds Gauntlet mode itself - that is, the process that you go through from clicking to select Gauntlet, choosing a starting vehicle, entering a level, going to the next level etc. The different parts of that are basically features the game already, but they need to be strung together and bolstered in the right ways. Luckily Gauntlet maps past World 1 will also be a lot faster to create because 90% of the time I've spent on World 1 has been in making tools and systems rather than the content itself - both creation tools, and code for the semi-random generation of the map's content when it's played.

I'll check in again when I've next got something to show.

Adding some trees

The Unity engine has a built-in terrain system that's been around for ages. It's honestly pretty great actually, but it has a few quirks. The tree system is maybe the quirkiest part.

There's a special system for trees, separate from the one for placing glass and other detail stuff. Your trees must use two specific built-in shaders. Your trees must reside in a folder with the text "Ambient-Occlusion" in the name. They may have colliders, but other components on them will be ignored.

I tried the tree system out, I haven't used it before. It's got some nice features - for instance it'll automatically turn your trees into billboards at long distances to save rendering work (much like Jurassic Park: Tresspasser did way back in 1998). Although even that has its quirk - the billboards have a slightly different perspective to the meshes so they do some weird bending as they switch over.

I had it all working actually quite well, but the killer was the lack of support for custom components. The trees would have to be permanent; immovable. Scraps with indestructible trees? It wouldn't do.



Look at that realistic tree-smashing action (pls don't destroy trees IRL).

So anyway I added some trees.



(not to SandyBridge - I was just testing them there)

They're not using the special tree system. They're just meshes with a couple of LODs. LODs being levels of detail - showing the lower LOD at far distances. Works fine.

The trees I have actually came with two LODs already, but I wasn't exactly impressed. Does this look like a seamless transition to you?



The branches are fine. The leaves not so much. So I made my own low LOD versions that aren't particularly great either but everything looks OK.



I'm still adding level content. Each "world" in Gauntlet mode is big - but at least it does get reused for three levels. My new job is taking up a lot of time as I knew it would, but I'm still managing to get a little Scraps work done. See you next time.

Weekly update and some semi-bad news for Scraps

Let's get the bad news out of the way first. Last week I got a nice job offer that I've accepted. And it had to be a full-time position, so it's not going to leave much remaining time for Scraps. It's an offer that I would be silly to ignore in favour of my obscure little game that I've already been working on for years without much success (income-wise anyway). Of course Scraps could take off in success after Gauntlet mode is added, or after it leaves Early Access, but historically that's very rare: Almost always if a game's sales are slow at first during Early Access, they remain a small-time thing at full release.

So I apologise to anyone reading this because development is gonna get real slow on Scraps. I don't like to let the community down even though it's small. You can still rest assured that I'm not going to abandon this project - Scraps is still gonna get done! But here's what I'm going to do:

- I'm going to stop the current weekly news updates since I expect there won't be much for each one. I'll post whenever I have interesting new stuff to share instead.
- I'm going to add information to the game's description on the Steam store page saying that it's a hobby/side project at this point and development will probably be slow. I don't want to deceive anyone here.
- I'm not going to put the game in sales anymore (25% off etc) until either Gauntlet mode is in, or I go back to having more time to work on it.

The Scraps community is so nice; I want to deliver you an awesome game. But as I said earlier I'd be silly not to take this job, which is exactly the sort of job I want. Here's what I did in my last week of freedom:

I made some more level content. Yeah, I'm still on world 1, because I keep making things like tools for level building or things to put in the levels rather than actually putting things in levels. Subsequent maps should be way faster to populate, like how the terrains for worlds 2 and 3 were done much much faster than the terrain for world 1.



A thing I did this week in that vein was make a new type of vehicle spawner. I already had vehicle spawners that I could place on the map with a list of vehicles they could spawn. Looked like this in the editor:



When playing, they'd randomly select a vehicle to spawn from the list, and that worked OK. Gauntlet mode is sort of half procedural, half hand-placed, and it's important to be careful with how procedural it is. Fully hand placed would make it exactly the same every time, which would get boring fast. But fully procedural almost always ends up boring as well - a million variations on the same thing. No Man's Sky is the procedural game du jour to complain about, but there are many.

However, in this particular case I realised that there were many scenarios where I really just wanted a vehicle spawner that would spawn any vehicle I had that was appropriate for the current level. Same potential set for every spawner. On top of that, really the vehicles should get harder as you progressed to another level within the same world, but to do that currently I'd have to place multiple spawners and limit them to just the level they were intended for.

So I made a vehicle library that loads up all the vehicles I've put in a folder, and lets me select them with filters. For instance if I wanted a vehicle in a certain price range with only cheap weapons on it I could do:

vehicleLibrary.GetVehicle(VehicleFilters.TotalCost(minValue, maxValue), VehicleFilters.EachWeaponCost(weaponCostLimit));


Or for a vehicle in a certain price range with a small chassis:

vehicleLibrary.GetVehicle(VehicleFilters.TotalCost(minValue, maxValue), VehicleFilters.ChassisType(VehicleData.ChassisOptions.Small));


"But writing code every time? That sounds like even more work than placing two spawners." Well yeah, it would be, but I made a new vehicle spawner that automatically detects which Gauntlet level the player is on and sets the appropriate filters. So there's no vehicle info to set in the interface at all:



And I can still use the old spawner type if I want specific vehicle options.

In other news, at one point I also accidentally broke everything in a fun way:

Gauntlet mode in development: Still level building

This week I've been mostly placing content in Gauntlet mode levels. I did make an SMG drone as a less deadly version of the the existing MMG drone:



I also made a little Unity editor prefab placer script which I'll share for any Unity devs out there.

In Unity, if you want to add things to a scene you usually drag prefabs from the Project window, which is really just a folder view. It works pretty well but it means that you need to switch around in folders to find things, and you also can't see the object while you're positioning it. Unity provides pretty good tools to customise the editor so I made a little custom window instead:



It lets you spawn arbitrary prefabs with the buttons, and it automatically raycasts to the ground and places things there. You can show a visual indicator for placing invisible prefabs, and there's an option to give things a random starting rotation. You could also rotate the selected object with the keyboard like in the gif above, but for some reason it often ran at like 1fps and was horrible to use, so I've removed that feature.

The code isn't anything special because it's just meant for my personal use in the editor, but I'll share it here as I think it could be useful to other unity devs. If you're using it, read the code comments as there are some changeable things within, but it should work as-is as long as you replace the paths in LoadPrefabList with your own.

Here's the code: http://pastebin.com/E3xmmUWd

Scraps v0.5.6.1

Changelog


0.5.6.1
- Revamped the code that shows other player's vehicles in multiplayer. Their movement is now smoother, while not being any more delayed
- Much improved vehicle collisions between laggy players as well. No more flying across the map!
- Reduced some unnecessary network traffic
- Added option to have a radar in melee games
- Minor GUI adjustments
Bug Fixes:
- Hover effect no longer shows on Melee mode button while an overlay is up
- Fixed real-time shadows not matching up with baked shadows at low graphics settings on the Test Map
- Fixed a timing bug on joining multiplayer games that could get you disconnected with a vehicle build error


Radar in Melee games


I said in a previous update that I wouldn't add the radar I designed for Gauntlet mode into melee mode, because you should be able to hide from other players. But I figured it may as well be a custom option. Tick it at the lobby to enable it for everyone in the game (except AI - they still need line-of-sight to see you).



No more inadvertently flying across the map


This week I managed to fix an issue that's been plaguing Scraps multiplayer since it was first released, and that I actually I thought might be impossible to fix. Something that might not seem very important at the moment while Internet multiplayer is quiet, but still a relief to conquer.

Scraps has always had an issue where sometimes, if you collided with another player and the latency between you was high, you could end up flying across the map. Like at 1:43 in this video where I'm playing from New Zealand on a US-based server:

https://www.youtube.com/watch?v=ZGKtSYu71t0

It was... "fun", sometimes, with quotes as necessary around "fun". It was definitely not how things should work, particularly since you could end up taking mega collision damage as well.

To set the scene:
[LIST]
<*>In Scraps, each client PC is in charge of the physics for their own vehicle.
<*>The server also runs its own physics, but for movement it's just checking what the client said (for most other things it's the authority).
<*>Other vehicles you see are sending you their positions - they're not running their own movement physics on your machine.
<*>Having said that, they still need collision boxes and momentum so that you can collide with them.
[/LIST]
So each vehicle is doing their movement physics on their own machine, and getting sent the positions of everyone else. Since the "fly across the map" problem got worse as your latency got worse, I theorised that the problem was as follows:
[LIST]
<*>1. You and an enemy vehicle collide.
<*>2. Latency means that although your vehicle bounces back right away, they keep moving forward for a moment because both of you are seeing each other a little in the past, and you're only doing physics locally for yourself.
<*>3. They therefore clip their vehicle's collision boxes into your vehicle further than they should, sending you flying across the map due to the physics engine's repulsion force that tries to keep things apart.
[/LIST]
Basically their ghost invades your personal space and your vehicle tries to get away. And that would be potentially impossible to fix.

But it turned out that wasn't the problem!

The Real Problem part 1: How games show other players


Let's look at how a game shows other players' positions first.



The above applies to any real-time multiplayer game. You simply cannot know where other players are right now, because there's always some latency between your computers. There are two typical ways to handle this: Extrapolation and Interpolation.



With extrapolation, you say OK they were here half a second ago and going this fast in this direction, so they're probably here now. But extrapolation sucks: In Scraps and especially in FPS games where players can change direction even faster. If players always ran in a straight line at the same speed extrapolation would be great, but it's just a guess and often it turns out to be a bad one -meaning once the next real position update comes through, you'll see things rapidly correct themselves to a new position.

So usually interpolation is the way to go, in Scraps and in other games. You show the other player a little in the past (1s in the diagram above is a big exaggeration just for easy numbers. Quake 3 used 0.1s for instance), so that you can have real data on both their past and future positions, and then show a smooth movement between them. No more guessing. There can still be some issues like the "bouncing ball problem", but that's getting picky.

Fun fact: Scraps actually dynamically modifies the interpolation delay to compensate for the latency of each player, so I can keep things smooth while also having as little delay on other players as possible. Scraps can also extrapolate a little bit, but only if it has to.

The Real Problem part 2: The real problem


Anyway, that's all fine and good. But in your average FPS game the other players are just a set of colliders that have no actual velocity or momentum. I mean, they "move" around, but it's more like they teleport each frame to a new position. Whereas in Scraps if an enemy vehicle collides with you they should be able to knock your vehicle away - not just keeping you out of their collision boxes but really pushing you with force based on their current velocity. They need to be actual physics objects working with the physics engine, while also just being data fed in from other PCs.

Scraps uses a networking library called uLink, and along with uLink came an example script for just this sort of thing. They had an interesting system, and I used the same sort of system for Scraps. It worked like this:



It only looked at the most recent data coming in from the other player, and it set their vehicle up with a velocity that would get it to that point at the right time. Therefore you had a vehicle with real physics that nevertheless always moved exactly where it was told to.

But what I noticed last week, and what I should really have thought about in the past, was that unreliable data could cause big velocity variations (of course this is also a lesson in trusting other people's code...). For instance if you didn't get any data from the other player for a bit, their vehicle would keep moving wherever it had been going for a bit and then suddenly have to do a big correction, giving it a big velocity - whether or not it actually had a big velocity on the other player's PC (although really big corrections would just teleport instead, it wasn't that dumb).

I noticed that the velocity actually fluctuated all the time, and I wondered why I hadn't just tried doing what a normal FPS would do, but interpolating the velocity as well as the position, like so:



So I did that - and it works really well! Why didn't I try that in the first place? Velocity is now interpolated along with position, rotation etc. The vehicles have real physics but still go where they are told to go, and the flying-across-the-map problem ended up fixed as well. And there's no additional delay in where you see the enemy - the interpolation is set the same amount in the past as it was before. Turns out it must have been velocity fluctuations that were causing it, and what I thought was a problem with higher latency was actually a problem with less reliable connections.

You will still see a delay on high-latency connections in the other player getting pushed back in a collision. You hit them, you bounce back, they seem to stay still for a moment, then they bounce back too. That's just because you're only doing physics for yourself, so you're waiting for the new collision to get sent out and then some back to you. The good thing is, everyone gets pushed back the right amount!

The bad thing is... no more exciting unplanned flights into orbit?

Scraps v0.5.6.0

2016-8 - 0.5.6.0
- Reduced XP required to unlock some later parts a little. Final unlock is at 4000XP versus the previous 4700XP
- Added eight stationary turrets to the test map to facilitate testing armour etc
Bug Fixes:
- Fixed several null reference exceptions that could occur when vehicle parts were destroyed
- Players with laggy connections could still get incorrectly kicked for "cheating". Threw out that code for now: It won't kick you anymore but still needs some work later
- Various small fixes