Currently developing GearBlocks. I guess you could say I'm an industry veteran (in other words, I'm old ;)). After having worked as a programmer in "AAA" games for around 15 years, I decided to try an experiment, could I develop and release my own game? Jury's still out on that, but things are going pretty well so far.

RSS My Blogs  (0 - 10 of 46)

Player physics

dangersam Blog

Over the past few weeks I fixed a bunch of bugs, and then turned my attention to player physics and movement control.

Problems with using CharacterController

Since the very earliest days of the game's development I've been using a CharacterController for the player along with a "character motor" script (adapted from a Unity sample) to control it using the CharacterController's Move() function. The character motor would apply forward / back, strafing, and jumping movement, acceleration due to gravity, and if the player was standing on a moving object it would make them move with it.

This setup worked reasonably well, but the CharacterController doesn't provide proper interactions with Rigidbodies in the world. The player is treated as a static immovable object when things collide with them. The player can't push objects around, and they can't be pushed by objects either. It just looks wrong when for example a vehicle hits the player at high speed and they don't move at all.

Also, the character motor implementation had a bug where, if the player collided with a Rigidbody, they would occasionally get thrown across to the other side of the map. Most often this happened when exiting a seat, very annoying! I suspect this issue was related to the code that moved the player when standing on something, but I'm not 100% sure.

Despite all these problems, I didn't particularly want to add more work to my plate by throwing out this character motor code and starting again from scratch. So I tried playing around in the OnControllerColliderHit() message to try and get the player to push stuff around, and in OnCollisionStay() I tried to make the player be pushed by objects that collide with them. I couldn't get this to work nicely though, I think it really is necessary for the player to have a Rigidbody to give proper physical interactions, anything else is always going to be a bit of a hack at best.

At this point I realised I needed to ditch the CharacterController and start from scratch after all, using a Rigidbody instead.

A physics based player

So now instead of a CharacterController, the player has a CapsuleCollider and a Rigidbody. I implemented new code that replicates the original forward / back, strafing, and jumping behaviour, by applying a force to the player's Rigidbody. It uses a Physics.SphereCast() to detect contact with the ground and to find the Rigidbody that the player is standing on, if any. If they are standing on a Rigidbody, its velocity is transferred to the player. Also, the force used to move the player is applied in the opposite direction to the Rigidbody that they're standing on, thus obeying Newton's third law.

To sum up the features and benefits of this new approach:-

  • Correct player collisions with other Rigidbodies.
  • Player can push other Rigidbodies around.
  • Player can be pushed by other Rigidbodies.
  • Player can stand on a moving Rigidbody.
  • Obeys Newton's third law when player accelerates while standing on a Rigidbody, this results in really cool "conservation of momentum" behaviour.
  • Player falls due to gravity automatically from having a Rigidbody, but even better, the player's weight now pushes down on the Rigidbody that they're standing on.
  • Eliminates the old "player thrown across to the other side of the map" bug.
  • Code is simpler and cleaner than the old character motor implementation.

Here are some examples of all this in action:-

As you can see this provides a level of interaction between the player and the constructions they build that simply wasn't possible before, so I'm happy I bit the bullet and re-implemented things this way!

However it's by no means perfect, and there are a few issues / missing features that will need addressing at some point, for example:-

  • I'd like to implement code to handle climbing steps, I think the player needs an extra force to push them up steps, as they tend to get stuck at the moment.
  • I also want to add something to make the player slide off ledges, as right now the player can stand more than half way off a ledge with their feet in the air, which looks weird.
  • The old character motor had code to make the player slide down steep slopes, not especially crucial but it would be good to get this working in the new implementation.

Over the past few weeks I've been finishing up the player character work I started a while back, along with some code refactoring and other things.

Building a player character

I've now implemented functionality so that a construction built out of body parts can be treated as a player character model. Such a "character construction" differs from a normal construction as follows:-

  • It doesn't get serialized out to saved games, because the player's character construction gets spawned separately (based on what was selected in the character screen, see below).
  • It doesn't collide with other non-character constructions or objects in the world (the player's CharacterController takes care of collisions).
  • It can't be selected, frozen, or destroyed.
  • Body parts in it don't move via physics, instead they are locked to the appropriate animated bone (e.g. head, upper arm, lower leg, etc.)

I added a new character screen accessible from the main menu. This allows the user to select a character construction, which will then be spawned as the player character when entering a game.

To create a custom character, first the user assembles a construction as normal out of body parts, and then they save it via the save construction screen. As shown below, I added a tab to this screen to allow a construction to be saved as a character so that it'll then appear in the character screen and be available for selection.

To switch to the new character, the user then has to exit back to the main menu, go into the character screen and select the one they just saved, before re-entering a game.

In game, the character construction can be seen in the world tool UI just like any other construction. However there are some operations that can't be performed on it as mentioned before: select, freeze, or destroy. This can be seen below, note that the character construction has an icon to identify it as such.

This pretty much completes the player character system although, as always, there are a few loose ends to tie up:-

  • I need to add checks to ensure that the construction is valid to be a character (e.g. has the correct body parts, and they are connected together appropriately).
  • Currently, to change the player character construction the user has to exit back to the main menu, I'd like to implement a way to swap it during game play.
  • The body part meshes need a bit more modelling work.
  • I'd like to add more attachment points to the body parts to allow for more customisation options.
  • I also want to add more body part variants and accessories.

Free flight mode

I've also finished working on a "free flight" mode for the player. This turns off gravity for the player and changes the controls slightly so that the user can fly up and hover in the air, handy for when building large constructions!

Singleton squashing

Finally, I also did another code refactoring pass to eliminate the remaining singleton MonoBehaviours in the game (well nearly). This primarily involved converting them to ScriptableObject assets, and replacing any public method calls with events and handlers. I covered this topic in the ScriptableObjects For Fun and Profit blog post a while back, so I won't go into detail here. Suffice to say I'm done with this refactoring process now, there are only a couple of singleton MonoBehaviours left, which are for my networking abstraction layer (something I also blogged about a while ago), and I think I'm just gonna leave these as they are.

Well, seems like another two months has gone by in a flash! I was away visiting family for some of this time, and the work I've been doing on the game has resulted in a frustrating lack of progress to show for it, but here's an update on what I've been up to anyway.

Saved game serialization

Up until now, saved games and constructions have been serialized via a binary stream, with no formatting, just using BinaryReader and BinaryWriter directly. This is fast and results in a compact file size, but has one huge disadvantage, a lack of version tolerance. In other words, if I add or remove any variables to be saved, or reorder them, then old saved game files will no longer load correctly. To work around this I wrote code to check a version number in the saved file, and then convert things over for each added or removed variable. This is a hack really, and has resulted in rather messy and hard to maintain code.

This situation is bad enough with just the demo version of the game out there, with a cut down feature set. Maintaining saved game backwards compatibility will only get harder once the full version is released.

Ideally, I need a properly structured save file format, with some kind of key value pairing that would allow for version tolerance, but wouldn't bloat the file size too much.

BinaryFormatter

First I investigated using BinaryFormatter, because it allows for version tolerance via optional fields, but I couldn't get it to work when deserializing MonoBehaviour classes. I need to be able to instantiate the MonoBehaviour and then populate serialized values into it, not have the deserialization process itself try and allocate a new MonoBehaviour (which is not allowed by Unity). I thought maybe using a serialization surrogate might allow for this, but couldn't figure out a way to make it work. The other downside of BinaryFormatter is all the assembly and type data it saves out adds quite a bit to the file size.

Json serialization

So after looking around for other possible solutions, I decided to try Json. This provides the key value pairs I need in a fairly compact structured format. I used Json.NET from Newtonsoft (provided via a Unity asset store package for ease of integration) which seemed really promising, it's very easy to use and highly configurable. In most cases there's very little additional code to write, you can just use the JsonProperty attribute to specify which class properties to serialize, and configure how they're serialized. Also, it allows for populating properties of a MonoBehaviour that has already been allocated, by using JsonSerializer.Populate() inside a JsonConverter.

Still, it took me several weeks to get Json serialization working for both saved constructions and full saved games, there were a few stumbling blocks along the way which took time to work around, as did just learning how to best use the Json.NET API. The end result seemed great though, it solved the version tolerance problem, and the code was so much simpler and cleaner.

One issue was that the resulting file sizes of the text based Json format were huge. Given that the game uses the same serialization code path to send construction data between networked players, this was a problem. So, I switched over to using Bson (the binary equivalent to Json), and also compressed the data via a DeflateStream. This worked well, the resulting file sizes actually ending up smaller than my original binary stream format.

Performance and memory problems

At this point I thought I was good to go, but then I started profiling the Json serialization with large saved game files (more than a thousand parts), and realized I was in trouble. Firstly, deserializing the saved game was more than twice as slow using Json vs. the old binary stream method. This wasn't a complete disaster as the load times weren't terribly long in the first place. The other more serious issue was that the Json deserialization did an enormous number of tiny GC allocations (as in millions of allocs, totalling hundreds of MB!).

I found that reducing the JsonProperty name lengths helped slightly with this but not to any significant degree. I spent quite a lot of time restructuring the code that loads the various modules in the game (player, constructions, time of day, etc.) to try and deserialize from the file stream more efficiently, but this made very little difference to performance or memory usage unfortunately (the resulting code was cleaner than before though so I guess the refactoring was worth doing anyway).

I'm annoyed with myself that I didn't do enough tests to pick up on these problems before putting all the effort in to convert the game over to use Json. If I'd known ahead of time, I probably wouldn't have bothered.

So now I'm not sure what to do. If I stick with the old binary stream solution, then all the Json serialization effort will have been wasted and I'm still stuck with difficult to maintain code for backwards compatibility. But the Json serialization option as it stands isn't acceptable, I'd need to do something to resolve the memory and performance issues. One possibility would be to manually serialize everything (i.e. use JsonReader / JsonWriter directly rather than JsonSerializer), supposedly this is the fastest way as it avoids overhead from reflection and so on.

I've decided for now to put all this to one side, think about it some more, and come back to it later. In the meantime I really need to get back to trying to make some positive progress with the rest of the game!

Player animations

dangersam Blog

The janky placeholder player animations that have been in the game for ages were long overdue for replacement with something better. Over the past couple of months I've been spent working on sourcing better animations, setting up the animation blending and transitions, and then driving that based on game state.

I worked out that I needed animations for:-

  • Standing idle.
  • Standing jump.
  • Turn left / right in place.
  • Locomotion (forward, backward, strafe left, strafe right) at various speeds from walking to sprinting.
  • Walking and running forward jumps (for jumping while moving backwards or strafing I just use the standing jump animation, as I couldn't find anything more suitable).
  • Seated.
  • Tool holding (right arm held out) in up, forward, and down directions, blended based on the camera view direction.

I don't have the budget to pay for an animator, so it was quite challenging to find suitable animation content to meet these requirements. In the end I was able to get by with a combination of a paid animation pack from the Unity asset store, some free animations from Mixamo.com, and creating some animations from scratch.

It was a fiddly process to set up the animation state machine in Unity to get all these motions blending and transitioning nicely, but I'm happy with the final outcome:-

First person mode

I've now enabled the animated player character in first person mode, the downside being that this can cause problems with bits of the body poking through the camera near clip plane, not the easiest thing to avoid especially if the camera is set to have a wide field of view.

To solve this I had to do a few tricks to offset the character and camera based on the view direction. It also imposed restrictions on the range of motion of the upper torso and arms and therefore what animations I could use. The end result is well worth it though:-

In first person mode the camera is now attached to the character's animated head position, this adds a lot of life to the camera movement (e.g. "dipping" after landing from a jump), and makes a big difference to the feel of the game:-

Tools

Once I had the player character animations working well in both first and third person modes, I moved on to the task of adding some tool models and attaching them to the player's hand. Now, as the player selects between the different tools (e.g. builder, painter, etc.), the transition between them is animated, and the models are swapped. Right now I just have simple placeholder models for the tools, and I plan on adding some transition sound effects, but here's what it looks like so far:-

Body parts

dangersam Blog

Time for a long overdue update! I've done a lot over the past two months so I'll have to split this into a couple of blog posts. In this first one we'll look at the character models I've been working on.

Crash test dummy

A while ago I added knuckle joint connectors which, along with the ball and socket connectors, can be used to construct simple rag-doll characters. This got me thinking, it would be cool to have some proper body parts with the connectors built in to them, which could be assembled in the game to make more convincing humanoid rag-dolls. I figured a crash test dummy would best fit with the idea of being made up of rigid body parts with exposed joints.

The first thing I had to decide was what body parts would be needed. I wanted to allow for flexibility of movement, but not have so many parts that building a character in the game would be unmanageable. I settled on the following 11 parts:-

  • Upper and lower torso (initially I had a single torso part, but in the end I had to split it to allow for animation - see "Player character" below).
  • Head.
  • Upper and lower left arm.
  • Upper and lower right arm.
  • Upper and lower left leg.
  • Upper and lower right leg.

The second problem to solve was the joint connector locations and body proportions, this was tricky as I wanted to keep all the connectors on unit boundaries (or failing that, half unit) relative to one another, while keeping the dimensions of all the body parts in proportion.

After these decisions were resolved it was time to fire up Blender and make some parts, here's what I ended up with:-

...and once assembled together:-

I added attachment points on the hands so that other parts such as axles can be connected to them:-

...and because the body parts are compatible with everything else in the game, you can create all sorts of nightmarish stuff!

Player character

One of the longest unresolved design problems in GearBlocks has been the player character model, the skinned models in the game at the moment are just placeholders and were well overdue to be replaced. The trouble is, I couldn't decide what the player model should look like, or how to have it fit in aesthetically with the rest of the "GearBlocks world".

Then I thought, now that a humanoid character can be built in the game from body parts, why not utilise this for the player too? This will require implementing a way for a character built in game to be selected and used as the player model.

To explore this idea, I decided a crash test dummy wasn't good enough, I wanted to have a proper human. Inspired by action figurines, I tried to make the body parts so that their joints, although exposed, are not overly obvious. I went with a stylised appearance for the face and hair, this was partly due to the limitations of my modelling skills, but I also think this look fits the visual style of the game quite well. Here's what I have so far:-

Note that this is all still a work in progress, in particular the clothing and feet need some more refinement, as do the skin and hair shading. At some point I'd also like to add a female variant, and other skin tones. Plus it would be cool to have accessories (such as crash helmets). I did make the clothing paintable however, which allows for creating somewhat unique looking characters:-

...and of course you can mix and match with the crash test dummy parts!

I haven't yet implemented the system to make a built character "become the player", this will need to hook up the body parts to the appropriate animated bones (replacing the old skinned mesh altogether). For now I've hacked something together so I can test these character models out with the player animation system to make sure they work properly:-

The animations you see here are all new, in fact most of my time over last two months has actually been spent on the player animations, and this will be the topic of the next post...

I've been wiped out with a nasty bug over the last couple of weeks which slowed productivity somewhat, still got quite a few things done though!

Text decals

I implemented a decal shader that can modify material properties in the g-buffer independently of normals. This allows for rendering over the top of objects already rendered in the scene, modifying albedo, smoothness, etc. while leaving the underlying normals as they are, which gives a "painted over" look. I wrote this shader specifically to work with rendering font text from the Unity UI.

Then, I added a Canvas in World Space mode and a UI Text component to the resizable plate part, and implemented a new text label part behaviour to configure the text string to be shown.

With a “painterly” font, it's quite a nice effect, here's an example of what it looks like:-

And here's the part behaviour UI the player can use to configure the text string:-

This will be handy for labelling things on constructions, and I’m thinking I might make use of it in future challenge game mode scenarios / tutorials.

Data links

I refactored the part behaviour linking code using various interfaces to better decouple the code and make it easier to add new link types. I also made some minor fixes and improvements to the linker tool.

I then implemented a new data link type, which allows part behaviours to expose "data channels" that can be accessed by other linked part behaviours. Where appropriate, I added data channels to all the part behaviours already in the game, for example the electric motor now exposes its current RPM, torque, and power values.

Electronics

Now the dawn of a whole new category of parts, electronics! I hope to expand on this a lot more in the future but here’s what I’ve done so far.

Putting the text decal shader and the data links together, along with a LCD display font, I implemented some text display screen parts that show the data channels of the part they’re linked to.

Here you can see the new data link type being used as we link a display to a motor:-

The display screen showing the motor’s data channel values while it runs:-

These displays have a part behaviour that lets the player assign a data channel to each text line on the display. Here's the UI for this, it needs some improvement but is functional for now at least:-

I also added some "sensor" parts which are little modules that calculate their own speed, acceleration, or attitude (i.e. orientation in the world) and report these values via data channels. These can then be linked to from the LCD displays to show their current values.

The speed sensor in action, as well as speed it also reports its altitude and rate of climb (RoC):-

The accelerometer sensor, reporting longitudinal, lateral, and vertical acceleration separately, as well as overall acceleration:-

Finally, the attitude sensor, which reports heading, pitch, and roll in degrees:-

I have loads of ideas for more sensor modules I could add in the future, e.g. for reporting angle, angular velocity, proximity (think radar / lidar) and so on. Let me know if you have any ideas too! Combining these with the control links I have planned will really open things up to some very cool possibilities I think.

Anyway, for now I’ve just been enjoying messing around with these parts, like adding some sensors and LCD screens to the Mosquito flyer for flight instruments!

Here's a quick update on what I've been working on over the last month or so!

Linker tool

After a long hiatus from working on it, I finally went back and completed the linker tool. The first attempt at this tool that I did a while back was hard-coded to work with the pulleys, so I had to generalise the implementation to support different link types. A part can now have one or more link nodes that are associated with its part behaviour(s). When the linker tool is active these link nodes can be highlighted by the player, which they can then click on and drag to another link node to create a link between them.

These are the different link types that I've done, or am planning to do:-

  • Pulley - used to route belt between pulleys, implementation now complete.
  • Power - used to link motors, batteries, solar panels, this is also now done (see "Electric Power" below).
  • Data - allows one part behaviour to query data from another linked part behaviour, an example could be a screen that can be linked to a motor to display its current RPM.
  • Control - allows one part behaviour to control another part behaviour, an simple example being a switch that can be linked to several motors, lights, etc. to turn them on / off together.

In this example you can see some pulley links and a power link being added between a motor and battery:-

As there are now different link types, I've made a few tweaks to make it easier to manipulate them. The indicators for each link type are colour coded so that they can easily be distinguished from one another, and there is now a linker tool UI that allows links of different types to be shown or hidden.

Electric power

As I mentioned, I've now implemented "power" links so that motors, lights, and so on can be linked to batteries. I implemented code in the motor and light part behaviours to calculate a power value that is then used to discharge energy from the linked battery / batteries. Also, when braking, a motor will now regenerate energy back into its linked battery / batteries.

The battery part behaviour itself does very little other than tracking the battery's current state of charge.

I've also added solar panels, these have a simple part behaviour that calculates a power value based on the panel's surface area and its current angle relative to the sun. This is then used to charge energy into its linked battery / batteries.

It's fun to play around with this stuff in conjunction with the day / night cycle in the game, using solar panels to charge a battery during the day which then powers your lights at night:-

Knuckle joints

I've also just added a new type of hinge connector, what I'm calling a "knuckle joint", where the parts connect in line with one another like this:-

I made two versions of the knuckle joint connector, one that can rotate through 180 degrees and another that is limited to 90 degrees. Here's an example of using the 90 degree version, as well as the ball and socket connectors I added ages ago, to make a ragdoll in the game:-

Most of my time over the past few weeks has been spent on a major reworking of the code for all of the tools in the game (builder, material, painter, etc.) in order to improve the building experience for networked client players in multi-player games.

Previously the tool code that responded to player inputs in order to perform various actions (such as attaching a part, breaking an attachment, applying paint, and so on) would always run on the server for all players, with each client simply sending their inputs to the server for it to deal with. This setup made it relatively easy for the server to handle things like arbitration between players, seeing as they were effectively all running locally as far it was concerned.

However this approach was flawed, the most significant problem being the latency between client player input and an action happening, which made building really awkward to say the least.

So I've now re-implemented every tool so that player input is processed locally on each client. In some cases the client must still request the server to complete certain actions, so that the server can validate and arbitrate where needed (for example, while one player is attaching one part to a second one, at the same time another player could be deleting that second part; the server needs to have the final say on the outcome in this scenario). In many situations however, it is not necessary for the client to check with the server first (e.g. moving or resizing a selected part, painting a part, etc.), instead the client just informs the server of the change made so that it can be broadcast out to the rest of the clients.

The result is that the building experience for networked clients is now basically the same as it is in single player games. The other benefit of these changes is that because input is processed locally for each player, it simplifies the code and makes it a bit easier to modify and improve the tools.

Builder tool tweaks

On which subject, I've made a couple of minor improvements to the builder tool thanks to some suggestions I've received.

  • Remove actions (delete part, destroy construction, etc.) are now delayed, during which time the action key has to be held down. This should hopefully prevent annoying accidental deletions from mis-clicks!
  • I changed the default key binding for duplication from "Q" to "Left Alt + LMB", by default "Q" is now dedicated just to opening the tool menu.

The usability of the builder tool controls is still an ongoing concern, and something I'll have to keep plugging away at over time, but at least now it'll be easier to implement any further improvements I need to make.

New demo coming soon

Finally, I'm getting close to releasing another demo build, and I've been fixing a bunch of bugs in preparation for this. There have been a ton of changes to the code since the last demo, so I'm slightly paranoid that some as yet undiscovered bugs might have been introduced. Time to do some more testing I think, hopefully I don't encounter any last minute issues!

Well, it's been a while, so time for a progress update I think! The material tool is now done, and I'll show it in action very soon, so watch out for that. Most of my time however has been occupied with a massive code re-architecture effort, and that's what I'm going to go over in this update.

From a high level perspective the GearBlocks game code is quite well laid out in terms of separating various subsystems (e.g audio, graphics, player, UI, etc.) via namespaces and so on. However, there was still a lot of code coupling (i.e. direct dependencies) between areas of the game code that should really be completely independent. This made it impossible to reuse or test parts of the code independently, and it was only going to get worse as development progressed.

ScriptableObjects to the Rescue

I'd been using ScriptableObjects in Unity for a long time, but only in a select few cases as data containers, I certainly hadn't been using them to their full potential.

I watched these two excellent presentations a while back:-

Ever since, I'd been wanting to adapt the ideas presented in these talks to the game to improve the code architecture, and so I finally decided to take the plunge. This was a huge endeavour, but well worth it I think.

ScriptableObject Events

Previously I was using Unity's ExecuteEvents system as the basis for events in the game. This was helpful for code decoupling, however it still had some disadvantages:-

  • In order to add a new event, a new interface has to be written (derived from IEventSystemHandler), and then implemented in all the MonoBehaviours that need to receive the event.
  • It's necessary to explicitly call ExecuteEvents.Execute() on every GameObject with MonoBehaviours that need to receive the event. To me, this makes ExecuteEvents more like messages than true events, but perhaps that's just semantics.
  • Only MonoBehaviours on GameObjects can receive these events, ScriptableObjects can not.

So I replaced these with a new system, where each event is now a ScriptableObject asset. Here's a simplified version of the code:-

public class EventAsset : ScriptableObject
{
    public delegate void EventHandler();
    public event EventHandler Handler = null;

    public void Raise()
    {
        if( Handler != null )
        {
            Handler();
        }
    }
}

The real implementation is slightly more complex, but follows the same principle. It's implemented using C# generics to allow for different event argument types, and has support for logging and listing the current event subscribers. This is used by a custom editor I wrote to display this info while the game is running in the Unity editor, here's an example of it in action:-

To use an event it can simply be assigned to a variable in the Unity inspector, then to receive it, just subscribe to Handler:-

public class Receiver : MonoBehaviour
{
    [SerializeField] EventAsset somethingHappened;

    EventAsset.EventHandler onSomethingHappened;

    void OnEnable()
    {
        onSomethingHappened = () => { Debug.Log( "I hear that something happened!" ); };
        somethingHappened.Handler += onSomethingHappened;
    }

    void OnDisable()
    {
        somethingHappened.Handler -= onSomethingHappened;
    }
}

Or to raise the event, just call Raise() on the event:-

public class Sender : MonoBehaviour
{
    [SerializeField] EventAsset somethingHappened;

    void SomethingHappened()
    {
        Debug.Log( "Something happened, telling everyone!" );
        somethingHappened.Raise();
    }
}

This setup has some useful advantages over the old ExecuteEvents system:-

  • No need to write any code to add a new event, just create a new event asset and assigned it in the inspector where needed.
  • No need to explicitly refer to specific GameObjects to send the event.
    Don't even need to be using GameObjects, these events can be used by ScriptableObjects as well as MonoBehaviours.
  • The events are more easily debuggable via the custom editor.

ScriptableObject Variables

Events aren't always the most appropriate pattern for sharing data between subsystems, for example sometimes it's necessary to store a value somewhere and allow it to be read a later point, perhaps continuously polling it to watch as it changes.

Previously I was doing this by having my subsystems be singletons, and then directly reading / writing properties in them where needed, thereby tightly coupling different areas of the code together, not good! To solve this I made a new "variable" system, where each variable is a ScriptableObject asset. Whereas events can be thought of as radio broadcasts, the variable system is conceptually more like a noticeboard (with each variable being a notice pinned to the board).

Here's a simplified version of the code, it's implemented as a generic class to allow for different variable types:-

public abstract class VariableAssetBase<T> : ScriptableObject
{
    [SerializeField] T value;

    public T Value { set { this.value = value; } }

    public static implicit operator T( VariableAssetBase<T> variableAsset )
    {
        return variableAsset.value;
    }
}

For example, a bool variable type:-

public class BoolVariableAsset : VariableAssetBase<bool>
{
}

Again, the real code has a bit more going on. It has an event delegate that code can subscribe to, in order to be notified when the variable value is assigned to (this saves having to use a separate event for this). It also has support for serialisation so that I can use these variables for things like game settings (e.g. controls, gameplay, video) and allow the player to save / load them. Plus I made a custom editor that allows variable values to be viewed or even modified while the game is running in the Unity editor. At some point I might implement a debug console that would allow this to be done even in standalone builds, which would be super cool!

To use a variable it can be assigned in the inspector, then written to / read from. Notice that Assigner and Watcher in this example are completely independent of one another:-

public class Assigner : MonoBehaviour
{
    [SerializeField] BoolVariableAsset isThingTrueVar;

    void ThingBecomesTrue()
    {
        isThingTrueVar.Value = true;
    }
}

public class Watcher : MonoBehaviour
{
    [SerializeField] BoolVariableAsset isThingTrueVar;

    void Update()
    {
        PollThingTruthiness();
    }
	
    void PollThingTruthiness()
    {
        Debug.Log( "Thing is currently " + isThingTrueVar );
    }
}

I replaced data in my subsystems that needed to be shared with these new ScriptableObject variables. This allowed me to remove a lot of code dependencies, and eliminate the need for singleton references in most cases.

One example being the UI overlay that displays the player's speed, acceleration, and altitude. It now just reads variables for these values and displays them, completely independently of the player code that updates them.

ScriptableObject Dictionaries

There's one slight wrinkle with the ScriptableObject variable system, in that there is only one global instance of each variable. For example, sometimes I need one instance of a variable per player (in multi-player games). To solve this I implemented a simple ScriptableObject dictionary, here's the implementation pretty much in full:-

public abstract class DictionaryAssetBase<TKey, TValue> : ScriptableObject
{
    Dictionary<TKey, TValue> dictionary = null;

    void OnDisable()
    {
        if( dictionary != null )
        {
            dictionary.Clear();
        }
    }

    public TValue this[TKey key]
    {
        get
        {
            if( dictionary != null )
            {
                TValue value;
                if( dictionary.TryGetValue( key, out value ) )
                {
                    return value;
                }
            }

            return default(TValue);
        }

        set
        {
            if( dictionary == null )
            {
                dictionary = new Dictionary<TKey, TValue>();
            }

            dictionary[key] = value;
        }
    }
}

Then for example, a dictionary with byte keys and bool values:-

public class ByteBoolDictionaryAsset : DictionaryAssetBase<byte, bool>
{
}

The only part I left out here is some code for listing the entries currently in the dictionary, used by another custom editor I added for debugging while the game is running in the Unity editor.

A dictionary is used in much the same way as a ScriptableObject variable:-

public class Assigner : MonoBehaviour
{
    [SerializeField] byte thisPlayersID;
    [SerializeField] ByteBoolDictionaryAsset isThingAboutPlayerTrueVar;

    void PlayerThingBecomesTrue()
    {
        isThingAboutPlayerTrueVar[thisPlayersID] = true;
    }
}

public class Watcher : MonoBehaviour
{
    [SerializeField] byte thisPlayersID;
    [SerializeField] ByteBoolDictionaryAsset isThingAboutPlayerTrueVar;

    void Update()
    {
        PollPlayerThingTruthiness();
    }

    void PollPlayerThingTruthiness()
    {
        Debug.Log( "Thing is currently " + isThingAboutPlayerTrueVar[thisPlayersID] + ", about player with ID: " + thisPlayersID );
    }
}

Replacing Singletons

The game has many self contained code modules providing utilities and functionality used by other parts of the code. Previously these were either static classes or singleton MonoBehaviours, both having their disadvantages:-

  • Static classes can't have variables serialized by Unity or edited in the inspector.
  • Singleton MonoBehaviours need to live on a GameObject somewhere in the scene (or at least in a prefab).

So now I've re-implemented most of these as ScriptableObjects which have neither of these downsides. They work well with the new ScriptableObject events too, these modules being able subscribe to or raise events, which helps with code decoupling.

Other Uses of ScriptableObjects

I found many more places to use ScriptableObjects, far too many to go over in detail now, but here's a brief summary of a few of them:-

  • Added ScriptableObject "delegate objects", making use of the strategy pattern where different variations on a theme implement a common interface. For example I use this for the procedural generation code for the various different re-sizable parts in the game.
  • Replaced some enums with ScriptableObject assets.
  • Implemented ScriptableObject data assets with built in functionality for better separation of concerns. For example, I implemented a "sound asset" ScriptableObject that handles random AudioClip selection and playback, and then created a whole bunch of these assets for all the sounds in the game.

Hey all, hope everyone has had a good holiday break. I thought I'd give a quick update on what I've been working on over the past few weeks.

Toolbox code refactoring

Up until now, the code for the various tools (builder, linker, painter, etc.) was pretty much all in one (very large) source file. This was driving me crazy as made it a real pain to to fix bugs, or add new features. So I finally took some time to do something I'd been wanting to do for ages, which was to refactor this monolithic beast into separate source files for each tool.

There are still things I'd like to improve and clean up (further code decoupling, mainly), but it's much better than it was, and makes it easier to add new tools, on which subject...

Material swapper tool

After the refactoring, I started work on a new tool that allows you to swap the material on certain parts (such as beams and plates) after they've been spawned, and even after they're already part of a construction.

The first step was to add a material definition that encapsulates all the various part material properties (i.e. the rendering & physics materials, density, strength, and "is it paintable"). Next I had to refactor the part descriptor code to allow parts to use this new material definition (seems like I've been doing a lot of code refactoring lately!)

Then on to the material tool itself, which I'm still in the middle of building. Right now I have a first pass implementation working, with the basic UI done, and the ability to change material on the highlighted part. There's still more to do however; for example, save / load (including converting old save files to the new material swappable parts), and probably more refactoring as I'm not quite happy with how the part descriptor code is structured just yet.

Anyway, it shouldn't take too much longer to finish up, once it's done I'll reveal more about how it works. After that I'll probably get back to finishing up the linker tool, as that's been on the back burner for way too long now.

In the meantime, I'd like to say a big thank you for following my progress, particularly to those of you that have been following for a long time, and who continue to play the demo and give me feedback. I know development of the game is frustratingly slow, but I will get it done eventually, I hope!

Happy New Year, and all the best for 2019.

Level
Avatar
Avatar
Last Online
Country
Canada 🇨🇦
Gender
Male
Friends
Become friends
Member watch
Follow
Blog Statistics
Articles
46
Views
2,859
Views Today
0
Feed
RSS