Skip to content

Quick Start

Daniel Everland edited this page Dec 27, 2019 · 17 revisions

Table of Contents

Requirement

The first thing you should do, is ensure your Unity editor version is compatible with SO-A. You currently need Unity version 2017.3 or higher

Installation Instructions

There are three ways you can install SO-A. Here's a short guide that explains the pros and cons of each method

+ Get updates as soon as they're ready.

+ Has an in-editor manager for updates.

+ Assets are kept in a separate section, which reduces clutter in project-view

+/- Assets will be locked, so you can't modify the source code in any way. Whether this is a good or bad thing is up to you

- Not properly supported until 2018.1

+ Has an in-editor manager for updates.

- Updates can take several days to be published, due to manual verification step by Unity.

- Installation and updating can be tedious

- No in-editor way to check for updates.

+ Install using a single command line command

- Requires Node.js and openupm-cli

Unity Package Manager Instructions

  1. Ensure you're using Unity 2018.1 or higher
  2. Open your asset folder

  1. Open the "Packages" folder

  1. Open the "manifest.json" file in a text editor

  1. Add the following to your dependencies

"com.danieleverland.scriptableobjectarchitecture": "https://github.com/DanielEverland/ScriptableObject-Architecture.git#release/stable",

  1. Save changes. Unity will automatically import the package.

Unity Asset Store Instructions

  1. Open the Unity Asset Store window

  1. Search for "ScriptableObject-Architecture"

  1. Select the ScriptableObject-Architecture package and click on "IMPORT"

  1. Import all assets

Manual Installation

  1. Navigate to the GitHub Release Page

  2. Open the newest release and click on the .unitypackage file

  1. In Unity, click on Assets > Import Package > Custom Package...

  1. Select the downloaded .unitypackage file

  1. Import all assets

OpenUPM Installation

  1. Ensure you've installed OpenUPM
  2. Open the command line on your OS

  1. Navigate to the root of your Unity project cd C:\YourUnityProjectRoot

  1. Run installation command openupm add com.danieleverland.scriptableobjectarchitecture

  1. All done! Open Unity and it'll take care of the rest.

Tutorial

Throughout this tutorial I'll take you through the step-by-step instructions for using the following features of ScriptableObject-Architecture

  • Variables
  • References
  • Events
  • Typed events
  • Runtime sets

This will also give you a fundamental understanding of how using scriptable objects can improve your architecture in terms of decoupling systems and debugging them.

In order to keep the length of this tutorial manageable, I will only describe code and concepts relating to SO-A specifically. Everything else, like how to register collisions in Unity, code snippets for code that makes the player move etc. will not be covered. The entire repository is available in the branch called "tutorial", though, in case you want to check it out.

Creating a Player

The first thing we're going to do is create two assets. These assets will define the maximum- and current amount of health the player has. These are both what SO-A calls 'variables', as they store data, and both will be of the type float.

First, we'll create the max health variable. To do so, simply right-click somewhere in your project and select Create > Variables > float.

We're going to name this asset "PlayerMaxHealth" and set its value to 100

We're going to do the exact same thing for our players current health, but leave it blank, since it'll be managed by code.

Great! Now we need to tie this together with some code. For now the only responsibility of this piece of code is to set the current health to maximum when the game starts, but we'll expand it later on to add more complex behaviour.

This leads us to SO-A's next concept: references. References are what allows us to reference some value from our code without knowing where it comes from - all we care about is reading and writing to it, the rest is handled entirely from Unity's inspector. We're dealing with data of the type float, so we want to use the reference type called FloatReference.

We simply create a component that includes the following snippet

[SerializeField]
private FloatReference currentHealth = null;
[SerializeField]
private FloatReference maxHealth = null;

private void Awake()
{
    currentHealth.Value = maxHealth.Value;
}

In order to couple our assets to these values, we simply drag them into the inspector

Let's move on to something more complex. We want to be able to know when the player dies, but it doesn't really make sense the variable's value every frame. Instead we're going to introduce a new concept: event listeners. Event listeners automatically subscribe to an event (or a variable, in this case), and will let you know when said event is raised. If you subscribe to a variable, the event is raised whenever the value of the variable changes.

First step is to add a GameEventListener component to our player, which is our event listener, obviously.

Next, we drag our current health variable into the field called "Event"

Alright, now we want to couple this event listener to our script. First, we create a function in our player health component the event listener can call - the important part here is that the protection level of the function is public, otherwise the event listener component won't be able to see it.

public void HealthChanged()
{
}

Next step is to couple the event listener with this function. This step uses a Unity feature called UnityEvents, so I'll skip the details, but you basically drag the component that contains the function, and then select the function from the dropdown.

Last step is to add functionality to our function. If the current health is zero or below, we destroy it.

public void HealthChanged()
{
    if (currentHealth.Value < 0)
        Destroy(gameObject);
}

Alrighty, everything's setup and done. If you did this the conventional way you would already at this point encounter an issue - there's nothing in our game that damages the player, so we're not able to test if this works. This is where SO-A's strength shows, since we're already able to test this, simply by using the inspector.

This is a very important aspect of SO-A; the ability to debug systems by manipulating them directly. We're able to test that this system works, simply by changing the values which it composes of - we're not reliant on different systems, like enemy AI, in order to make sure that player death works.

Next up: adding a healthbar.

Adding a healthbar

I've added a healthbar and made sure that it works like you'd expect

Time to couple this with our health value.

To do this we actually only need a very simple component. The component will, every frame, calculate its fill value by dividing the current health with the maximum health.

We can improve this system by only doing this calculation when the player's health changes. I'll leave that up to the reader as an assignment.

[SerializeField]
private Image fillImage = null;
[SerializeField]
private FloatReference currentHealth = null;
[SerializeField]
private FloatReference maxHealth = null;

private void Update()
{
    fillImage.fillAmount = currentHealth.Value / maxHealth.Value;
}

That's it! We can now test the healthbar again, but this time instead of manipulating the fill image directly, we simply manipulate the player's health in the inspector.

Note how versatile this is - the player doesn't even have to be present in the scene for this to work, you can test this in a scene that only contains the healthbar.

Placing cubes

Next thing we're going to do is to allow our player to place and delete cubes, and have a UI element count how many cubes there are in the game. This sounds kind of silly, but I couldn't come up with a better example ¯\(ツ)/¯. It teaches you the concept of runtime sets, so let's do it!

Once again, we're leaving out the details, you can check the repo if you're curious, but we place cubes using left mouse button and delete them using right mouse button.

Alrighty, it works! Time to add the UI functionality. First thing first, we need to introduce the concept of collections. A collection is essentially like a C# list, but which exists in the inspector. This, once again, allows you to debug systems and have them exist independently of each other.

In this case we're working with GameObjects, so we want a GameObjectCollection. To add one, simply right click in your project view, and select Create > Collections > GameObject. We'll call this "CubesInScene"

Now we need to cubes to add themselves to the collection when created, and remove themselves when destroyed. Doing this is super simple, we just need to add the following short script to our cubes

[SerializeField]
private GameObjectCollection collection;

private void OnEnable()
{
    collection.Add(gameObject);
}
private void OnDisable()
{
    collection.Remove(gameObject);
}

Next, we need to drag the collection onto the component that's been added to our cube.

Finally, we need to add our UI element. It should simply display the amount of items in our collection. To do so, we use the following script.

[SerializeField]
private Text textField;
[SerializeField]
private GameObjectCollection gameObjectCollection;

private void Update()
{
    textField.text = $"There are {gameObjectCollection.Count} cubes in the scene.";
}

We add this to our text element and, once again, simply drag the collection into the serialize field.

Great, let's test this!

Awesome!

At this point you should have a good understand of how to use SO-A, and in which cases it's beneficial to use. For even more details, check out the wiki :)