Unite 2016 - MonoBehaviour Vs Scriptable Object by Richard Fine

You might also like

Download as pdf or txt
Download as pdf or txt
You are on page 1of 99

Unite 2016

-
Overthrowing the MonoBehaviour Tyranny in a Glorious
ScriptableObject Revolution

Richard Fine
Sustain Enginering @ Unity

This Presentation transcript is organized by Aucer in 2023.01


In this talk

● Problems with MonoBehaviour (as a class)

● ScriptableObjects as an alternative (to Monobehaviour )


Another way of doing things,
another important element in your architectural toolbox
when you’re working in unity.
● Examples (abstract class, live demo, one of the project)

2
THE MONOBEHAVIOUR TYRANY (Why is it bad)

● Scripts written as MonoBehaviours on GameObjects

● Live in scenes or prefabs

● Store all your state

● Get callbacks from Unity

3
THE MONOBEHAVIOUR TYRANY - the first problem(1)

● Shared vs non-shared state

● In most projects you don’t have a


unique maximum health for every
single enemy.
We want a sort of a consistent
shared bit of information
for a bunch of objects and
some per instance information.

4
THE MONOBEHAVIOUR TYRANY - the first problem(2)

● Shared vs non-shared state

● The way that most people write


monobehaviors
combines all this information together
into one place.

5
THE MONOBEHAVIOUR TYRANY - the first problem(3)

● Shared
Information that you want to
using across multiple objects,
multiple instance.

● Non-shared
Information that you don't.

6
THE MONOBEHAVIOUR TYRANY - the first problem(3)

● Current health for enemy is definitely


information that you want to be
on that enemy only
using across multiple objects,
multiple instance.

7
THE MONOBEHAVIOUR TYRANY - the second problem

● Reset when exiting playmode


Any changes you've made in play mode get lost

8
THE MONOBEHAVIOUR TYRANY - the third problem

● Bad in collaboration
● Sub-file granularity (bad in collaboration)
When you have a monobehavior
in a file.
● Scene file
A ton of other objects
● Prefab file
transforms
components
potential information

9
THE MONOBEHAVIOUR TYRANY - the third problem (Why)

● (All write in monobehavior)This is pretty bad in collaboration


● If you want to edit a monobehavior in one scene file,
You have to potentially lock the whole scene file
or other people in your team may be changing other objects in the same file
then you’re having to kind of risk having merge conflicts
when all comes together afterwards.

10
THE MONOBEHAVIOUR TYRANY - the third problem (Solution)

● Using ScriptableObject

● It will be much nicer from a collaboration point of view


if one object meant one file
so you could lock things
and operate at the same level that the VCS expects you to to operate.

11
THE MONOBEHAVIOUR TYRANY - the third problem (Solution)

Version control systems (VCS)


Allow teams to back up and archive the source code of their projects.
This makes it easier to review and make edits to the repository
or restore previous versions in the event of a build-breaking error.

12
THE MONOBEHAVIOUR TYRANY - the fourth problem

● Callback chaos
When you have severals monobehaviors on different objects,
you can’t make sure which “Start()” function run first.

● Accident-prone

● Still have sub-file granularity

13
Uninstantiated prefabs help a bit, but…

● Kind of abuse of the Prefab concept

● Accident-prone (1)
It's quite easy to accidentally drag and drop
one of these things into the scene
and then suddenly you've got the shared object in the project
and also the instance in the scene
and that can become very confusing

14
Using prefabs might cause these problems

● Accident-prone (2)
It allows this sort of accumulation of mistakes
and things that it'd be better to just avoid having at the first place

15
Using prefabs might cause these problems

● Still have sub-file granularity(1)


Even if you have a prefab that just has a single monobehavior on it
you keep in the project
It still has a game object and a transform in that file as well,
and maybe you never really touched those
but it still means that
when you're looking in the file there's extra data in there
there's the extra overhead when you're loading it
it's extra stuff

16
Using prefabs might cause these problems

● Still have sub-file granularity(2)


As game programmers
we like to be efficient with things
we don't like having redundant extra objects
hanging around for no reason

17
C# statics are very DIY - 1

● Still have sub-file granularity(3)


● Don’t always use “Static”, “Singleton” (can be more efficient)
The other approach that people will sometimes take to dealing with
shared state in particular is to use c-sharp statics.
Have a bunch of static variables and you store your shared information in that
and that works but it's very very DIY

18
C# statics are very DIY - 2

● No serialization for statics in Unity


● Serialization is the process of converting an object into a stream of bytes to
store the object or transmit it to memory, a database, or a file. Its main purpose
is to save the state of an object in order to be able to recreate it when needed.
The reverse process is called deserialization.

19
C# statics are very DIY - 3

● No serialization for statics in Unity


● If you want to store maximum enemy health
like static int max enemy health variable
It’s completely up to you
how you fill that variable out
and loading and savig that information.

20
C# statics are very DIY - 4

● There's also no way to visualize that(static variable not showing by default) in the
inspector
● You have to write custom editors that will read your statics
and they(static variables) all get lost whenever you reload the domain
● So if you try and do support for recompiling in play mode all your static
data gets wiped out.
● You can do these things but you have to roll a lot of support around it yourself.

21
C# statics are very DIY - 5

● You're using an engine like unity


because you don't want to roll your own
support for all of these basic fundamental things.
● So let's talk about ScriptableObject.

22
ScriptableObject

● A ScriptableObject isn't attached to a game object.

● Can not be attached to GameObjects/Prefabs

● Does not get (most) callbacks

● Can be serialized and inspected just like Monobehavior

23
ScriptableObject

● A ScriptableObject isn't attached to a game object.


That's literally all it is.
So you can have your own custom class
your own variables
your own functions
make an instance of it
It doesn't have to live in the scene.

24
ScriptableObject

● Can be serialized and inspected just like Monobehavior


that includes writing custom editors
using property drawers etc.
It's a fully understood object within the unityecosystem

25
PropertyDrawer

● Base class to derive custom property drawers from.


● Use this to create custom drawers for your own Serializable classes or for script
variables with custom PropertyAttributes.
● https://docs.unity3d.com/ScriptReference/PropertyDrawer.html

26
How ScriptableObject saves us pain

● Shared vs non-shared state


● Encourages strong separation
Usually people end up using it for shared state only
you have a monobehavior that stores your non shared state
your per instance state that might reference ScriptableObject that
holds all the shared state.
This way of working as a pattern so it helps guide you into a best practice.
● Reset when exiting playmode.
Doesn’t live in scene, so doesn’t get reset.
Only the things in scene that get reset.

27
How ScriptableObject saves us pain

● sub-file granularity
● Total control over granularity
How scriptableobject live in files
You have total control over that
you can have a dot asset file in your project
that contains exactly one object - a ScriptableObject
you don't have to have extra game objects and transforms hanging around.

28
How ScriptableObject saves us pain

● You can pack several ScriptableObjects into one asset file


if you want to
it's up to you
we have the api's to let you do all of that.
I'll show you how some of that works later on.
and lastly callback chaos.

29
How ScriptableObject saves us pain

● sub-file granularity
● Total control over granularity

● Callback chaos
SO get basically no callbacks
SO have
1. OnEnable()
2. OnDisable()
3. OnDestory()

30
How ScriptableObject saves us pain

● Callback chaos

Scriptableobject(SO) get basically no callbacks


SO don't do anything
It's a much lower cognitive load for you when you're trying to debug
when you're trying to understand what is happening (While in unity testing your game)
you can just ignore these(SO) things.
They(SO) don't do stuff unless you're making them do stuff.

31
How to declare + reference ScriptableObject

public class MySO : UnityEngione.ScriptableObject


{
public int someVar;
}
You're declaring variables exactly the same way
as you would with a Monobehavior.
public MySO mySOInstance;
Referencing one. It will show up in the inspector.
It will let you drag and drop in instances of ScriptableObject.
It serializes it deserializes it does the work, it does everything great.

32
Which part can use ScriptableObject

● objects and assets


● textures or materials
● monobehaviors or gameobjects

Working with objects and assets that you're used


to when dealing with textures or materials and
monobehaviors or gameobjects or anything else.

33
How to create ScriptableObject(Create instances in-memory with)

● Create instances in-memory with


ScriptableObject.CreateInstance<MySO>()
● There's a couple of ways
firstly you can create just in memory
using Create instance on ScriptableObject as a static method
so this is kind of like your add component
but there's nothing to add it
so you just create one of these things it's free-floating in memory
it's an object that exists it's the same as doing new material
or something like that.
In theory we could have done this as a constructor (I don't know why we didn't
historical reasons I guess)

34
How to create ScriptableObject

● Create instances in-memory with


ScriptableObject.CreateInstance<MySO>()

● Bind them to .asset files with


AssetDatabase.CreateAsset() or
AssetDatabase.AddObjectToAsset()

You'll been able to kind of pack these things in


so this is how you have the control over how these things are put together
you can just have one object in a file
or you can kind of keep on packing extra objects into one asset file
whatever suits this kind of situation you're working
whatever suits the way of thinking about the problem that most make sense to you,
and you can automate all this as well.

35
How to create ScriptableObject

● Create instances in-memory with >... It will make an instance of the object bind
ScriptableObject.CreateInstance<MySO>()
it to a whole new asset file and then that
you're done simple as that.
● Bind them to .asset files with
AssetDatabase.CreateAsset() or and you can customize the file name and
AssetDatabase.AddObjectToAsset() the menu item name and so on
with parameters on the attribute
● Use [CraeteAssetMenu] to automate this
but simple as adding that one string
This makes it super easy
and you've got instant kind of custom asset
you stick this on your scriptableObject class
support in your project.
and then it will show up in “ Assets ” .>. “ Create”>...

36
ScriptableObject Callbacks (1)

● OnEnable()
we get this when the scripableobject is first created
before create instance returns or
we get it when the object is loaded
we also get it after a domain reload
when script to recompile in the editor at the end of that process when we finish reloading
everything any scriptableobjects that exist in memory get OnEnable( ) to kind of warm
them back up again

● OnDisable()

37
ScriptableObject Callbacks (2)

● OnEnable()
● OnDisable()
we get OnDisable() which is called when a scriptobject
is being destroyed or
before it's being destroyed and also
before we are shutting down the app domain for assembly reload
● OnDestory()
which happens when it's destroyed
and that's it that's all you actually get on a scriptableobjet by default.

38
ScriptableObject Callbacks (3)

● To sum up :
There's not a lot to learn and you can see that
most of the these things that might do on their own
is going to happen around
1. startup time
2. reloading the app domains and so on
3. rest of the time per frame bases frame to frame they just don't do anything
unless you tell them, you can add your own functions to a scriptableobject
just as you can add your own functions to a monobehavior
(No Update() in scriptableObject )

39
ScriptableObject Life Cycle (1)

● Let's look a little bit about the life cycle of these things.
I've shown you how they are created what about after that
just pretty much the same as any other asset.
You know once you've made a texture or a material or something like that
the life cycle is the same as those.
As long as you're as long as you still have it ,you're continuing to use it,
it's going to stay alive.
The point at which it usually will go away is when you call
“Resources.UnloadUnusedAssets() ”

40
ScriptableObject Life Cycle (2)

● Resources.UnloadUnusedAssets()
this is where we're going to scan through
all of your monobehaviors and objects in the scene
and see which things are referencing
which other things don't work out.
This scriptableobject that you created it's no longer being referenced by anything
so we're going to get rid of it
you have some control over that
you can stop that from happening using hide flags and
we'll talk a little bit about a pattern that you can create with

41
A note on Destory() / DestoryImmediate() [1]

Just a little note then OnDestroy() and


DestroyImmediate().

Ma Because this is the other thing that


na
g
Na
ed
sid people will somtimes get rid of them
tiv e
es
ide just as you can do with a material or
texture or whatever.

42
A note on Destory() / DestoryImmediate() [2]

C# part It's important to remember unity is not


instance of your own class
a dotnet engine.
It's a C++ engine with a dotnet layer.
This means all the objects in unity they
Ma
na
Na
ge
ds
ide
have dual personality thing going on
tiv
es
ide
they have a C++ representation and they
C++ part
instance of standard engine have a dotnet representation
object for SOs
43
A note on Destory() / DestoryImmediate() [3]

C# part So I’ve got my ScriptableObject that exists in


instance of your own class memory right now.
And there's part of it that exists in managed land.
That's the c-sharp class that you declared.
The C++ part takes care of the identity of the object
Ma
na
Na
ge
ds it provides things like the instance ID, referencing,
tiv ide
es serilization - making it available in the inspector.
ide
C++ part
instance of standard engine
object for SOs
44
A note on Destory() / DestoryImmediate() [4]

C# part When you call destroy or destroy mediate


instance of your own class people assume that kills off the whole thing
but that's not what happens.
You've got C sharp part it has a reference to it .
When you destroy the ScriptableObject
Ma
na
Na
ge
ds what actually happens is we kill the c++ part
tiv ide
es we can not kill the C sharp part
ide
C++ part there is no mechanism for immediately destroy an
instance of standard engine
object for SOs object in C sharp
45
A note on Destory() / DestoryImmediate() [5]

When you destroy the ScriptableObject


C# part
instance of your own class What actually happens is we kill the c++ part.
We can not kill the C sharp part . There is no mechanism for
immediately destroy an object in C sharp. Closest you get is
something like Idisposable but even that doesn't actually destroy
the C sharp object. It tells it they're going to clean up its stuff but the
Ma object itself is still kept around in disposed state until all the
na
ge references to it are gone.
Na ds
tiv ide
es So this is a common problem people will run into sometimes not
ide
just with ScriptableObject but with any kind of asset that has this
C++ part
instance of standard engine scripting component and native component which is all of them.
object for SOs
46
A note on Destory() / DestoryImmediate() [6]

C# part Having destroyed ScriptableObject


instance of your own class
The scripting wrapper - the managed part can
be still kept around by that reference from
some other field.
Ma The only way to actually get rid of that c-sharp
na
ge
Na
tiv
ds
ide part is to make sure nothing is referencing it
es
ide
anymore and then let the GC run and the GC
C++ part
instance of standard engine will eventually clean it up at that point.
object for SOs
47
A note on Destory() / DestoryImmediate() [7]

C# part So when you're destroying things it's a good


instance of your own class
idea to also null out the references that
you've got.

Ma
na
ge
Na ds
tiv ide
es
ide
C++ part
instance of standard engine
object for SOs
48
Patterns

49
Patterns(1)

That's all about ScriptableObject.


It's a pretty simple class.
There's one page about it in the documentation(unity odfficial)
and this is why a lot of people don't know about it.
They kind of just scan through and miss it.

50
Patterns(2)

It's so simple but simplicity is a good thing in architectures.


We(programmers) want simplicity
We want to use really basic objects
Don't do very much so there's less to think about
Let's look at some patterns that we can do with this stuff(SO)
The first most common one is to use these things as completely pure inert data
objects.

51
Patterns(3) Data Objects and Tables

● Plain data-holding class bound to a .asset file


Holds shared state usually
● Example here is
“A drop table for killing a goblin”
Three potential objects it could drop
and the chances of dropping.

52
Patterns(3) Data Objects and Tables

● Plain data-holding class bound to a .asset file

● PropertyDrawers and Custom Editorrs for


even nicer UX
you can save what sliders for
chance that's sticking the arranged
attribute on a float because actually
what it's storing is a float between 0 & 1

53
Patterns(3) Data Objects and Tables

● Plain data-holding class bound to a .asset file

● PropertyDrawers and Custom Editorrs for


even nicer UX

● Less overhead + smoother UX than a “real”


database

54
Patterns(3) Data Objects and Tables Less overhead + smoother UX than a “real” database(1)

One of the approaches people could use to use this as well is to use
like a real database - to go and use sequel Lite or something like that.
That's a valid approach but using scriptableobject instead it's got a bit
less overhead than that you know you don't have to deal with kind of
setting up the sequel Lite database and loading it and kind of getting
it all in place.

55
Patterns(3) Data Objects and Tables Less overhead + smoother UX than a “real” database(2)

Smoother UX as well you know your designers your artists they can edit
all this stuff and exactly the same ways to edit any other object in unity
and it's using familiar UX paradigms it's you know completely standard. And
referencing these things works very smoothly as well.
With a standard database you have to get some way of identifying kind of
which row in the database to work from.
Just drag and drop scriptableobjects then you can reference them.
56
Extendable Enums
Empty SOs bound to asset files
Check equality use as dictionary key
This is very similar to what you do
with an enum.
In principle, when you have any
enum, you declare your different
values.
You're not supposed to do
arithmetic on them.

57
Enums
The only things you can really do with enum values

are compare them and say is this equal to this enum value I'm expecting or
is it something else.

and you could do exactly the same thing

with these(SOs).

But the key advantage here is that (by using SO)your designers can
add new values to the enum without changing any code.

58
Extendable Enums
Netural progression to data objects

Once you decide that actually it


would be helpful to have some
information associated with these
things there's also very natural
extension to go from these empty
ScriptableObject to a non-empty
ScriptableObject.

59
Extendable Enums Example(1)
Example:

Create different damage types


exist in game from different
weapons and different attacks.

Add a bull for kind of which ones


the player is vulnerable to. >>

60
Extendable Enums Example(1)
>> So I could add that in one place, and then just go through all
these damage types and just check the bull on or off instead of
heavy to have a separate lookup somewhere from each of these
to corresponding bull values.

It's usually faster to kind of put the data on these things directly.

That's something you can't do with enum(origin way).

You can't really add extra data to each enum entry very easily.

61
Extendable Enums
Empty SOs bound to asset files

Check equality use as dictionary key

Netural progression to data objects

Supports : null
>>

62
Supports : null
>> With an enum you might need to

create a state for nothing entry at the beginning.

With this(using SO as enum)

nothing is just no reference

and that ends out working really nicely for a lot of situations.

63
Dual Serialisation
● ScriptableObjects are supported by JasonUtility
● Mix in-bult SOs with deserialised JSON SOs.

You can make a ScriptableObject and use


JsonUtility.ToJson() to get a string with all serializable
data in that ScriptableObject and save to a file.

You can deserialize them as well of course.


64
Dual Serialisation Example - User Made Level

Using SO & JSON Example - Level creator(user can edit level in game) [1]
I think you have to use FromJsonOverwrite() rather than FromJson() but you
can make an object and then use over write to fill it with data that you got from
Json.

What this means is that having built a system that works with
ScriptableObjects you can mix objects that you created a design time that you
saved into asset files in your project with objects that you've created by
deserializing Json.

What might you do with that a pretty good example would be something like :

“User Made Levels”

So given a ScriptableObject that stores all the information.


65
Dual Serialisation Example - User Made Level

Dual Serialisation Example - User Made Level


class LevelLayout : ScriptableObject

public Vector2[] platformPositions;

public Vector2[] enemyPositions;

public Vector2[] coinPositions;

public Vector2 startPosition;

66
Dual Serialisation - User Made Level

Using SO & JSON Example - Level creator(user can edit level in game) [2]
For example I have a simple platformer.
I've got some platforms, enemies , coins to collect and I've got to play a
start position. The game is to avoid the enemies and once you've got all
the coins then the level is done. You go to the next one now.
I don't want to ship my game with no levels.
I want to make a bunch of levels in the editor ideally using the scene
view using the standard tools I'm used to.

67
Dual Serialisation - User Made Level

Using SO & JSON Example - Level creator(user can edit level in game) [3]
So I write a very simple editor script to collect up all the things in the
scene and pack them into a ScriptableObject for me, so I can have a
bunch of object assets just shatter my project(Divided into modules)
that I'm going to ship in the game, built in.

But I also want to ship a level editor. I just want to ship something on
device that you can go to level edit mode, drag drop replay your
platforms in, serialize the Json and share it online.

68
Dual Serialisation Example - User Made Level
// Load built-in level from an AssetBundle

level = lvlsBundle.LoadAsset<LevelLayout>(“lvl1.asset”);

When it comes to loading these levels in,

I can load in the built-in ones maybe for an asset bundle.

69
Dual Serialisation Example - User Made Level
// Load level from JSON
level = CreateInstance<LevelLayout>();
var json = File.ReadAllText(“customlevel.json”);
JsonUtility.FromJsonOverWrite(json, level);
I make an empty level layout object.

I read all my json from a file somewhere maybe downloaded from a server.

I overwrite the object with the json data.

After that i can use that object i don't have to care whether it's a level that i ship built in came from asset bundle or level
came from JSON, over even mix of two(Load from AssetBundle and JSON ,mix use)

70
Dual Serialisation
// Load built-in level from an AssetBundle
level = lvlsBundle.LoadAsset<LevelLayout>(“lvl1.asset”);

// Load level from JSON


level = CreateInstance<LevelLayout>();
var json = File.ReadAllText(“customlevel.json”);
JsonUtility.FromJsonOverWrite(json, level);

71
Reload-Proof Singletons [1]

C# part
Let's look at some reload proof
Singletons.

Ma Coming back to the domainn's reload


na
g
Na
ed
sid issue. Whenever you recompile code in
tiv e
es
ide unity, we serialize everything throw away
all the manage code, loading the new
C++ part assemblies that you built and bring it all
back again. 72
Reload-Proof Singletons [2]

C# part
I've got my ScriptableObject with a C#
part and C++part.

Ma And I have got a static reference to C#


na
g Static field
Na
ed
sid part. I'm using this as a singleton.
tiv e
es
ide To store current scores of all the players
in the game.
C++ part >>
73
Reload-Proof Singletons [3]
Data

C# part
>> If I reload domain in the editor
If I recompile during play mode,

Ma I’m going to lose all the data, the static


na
g Static field
Na
ed
sid field reference, C# object.
tiv e
es
ide All the manage stuff is gone.
So how do we get that back static field ?
C++ part use : FindObjectOfType() >>
Data 74
Reload-Proof Singletons [4]
Data
>> We can use FindObjectOfType() to
C# part
locate that singleton object.
Just like searching for a material or a
Ma FindObjectOfType()
na
g ed Static field texture or anything else.
Na sid
tiv e
es
ide

C++ part

Data 75
Reload-Proof Singletons example
class MySingleton : ScriptableObject{
private static MySingleton _inst;

}
We've got our singleton class deriving from ScriptableObject.
We have our static field that points to the instanc of
singleton that we want to work with.
I’ve made it private because what we’re going to do is we’re
gonna acces it via a property and the property is going to
make sure that all of this stuff is kind of implemented.
So we have the actual instance property bear with a getter.

76
Reload-Proof Singletons example
class MySingleton : ScriptableObject{
private static MySingleton _inst;
public static MySingleton Instance{get{
if(!_inst)
_inst = Resources.FindObjectOfType<MySIngleton>();
if(!_inst)
_inst = CreateInstance<MySingleton>();
}}
}
First, if we don't already have that instance field populated then we'll go and try
and find it(an instance of our singleton type).
If that fails then we'll make one.

77
Reload-Proof Singletons example
class MySingleton : ScriptableObject{
private static MySingleton _inst;
public static MySingleton Instance{get{
if(!_inst)
_inst = Resources.FindObjectOfType<MySIngleton>();
if(!_inst)
_inst = CreateInstance<MySingleton>();
Sometimes, we didn't have to do it because we want to doing this by laoding an
asset or ready-made for an asset bundle.
We'd also want to set the hide flags on it to make sure it shouldn't be picked up
by the garbage collector.
We want this thing live for the whole duration of the session work in Unity.

78
Reload-Proof Singletons example
class MySingleton : ScriptableObject{
private static MySingleton _inst;
public static MySingleton Instance{get{
if(!_inst)
_inst = Resources.FindObjectOfType<MySIngleton>();
if(!_inst)
_inst = CreateInstance<MySingleton>();
return _inst;
))
}
Once that's done you know,
either we found the existing one or
we made one at that point
it should exist we should have one
so we can just return it at that point

79
Reload-Proof Singletons Conclusion
This is a super simple pattern that will get you these
objects that will survive a domain reload that you can
keep just referring to.
And then after the main reload you go to access the
instance and it's as if it never went away.

80
Delegate objects
Another pattern that might be familiar to anyone who's
done Mac OS or iOS development is the use of delegates.
● ScriptableObjects can contain methods, not only data
This means that you can use them as sort of pluggable
implementation or a strategy pattern .

81
Delegate objects
● ScriptableObjects can contain methods, not only data
● Pass in scene objects to work on
Because if you're storing a scriptable object in an asset it
can't reference things in the scene it has receive those
dynamically.

82
Delegate objects
● ScriptableObjects can contain methods, not only data
● Pass in scene objects to work on
This is true again with you know with anything else you store in the project
a prefab that that you have in a project it can't reference things in a scene.
You can't guarantee that scene is loaded it has to be shared so you can
have a scriptableobject that operates on things in scenes.
As long as they're passed in on the fly.
So the same as implements of strategy pattern.

83
Delegate objects
● ScriptableObjects can contain methods, not only data
● Pass in scene objects to work on
● Strategy pattern

84
Delegate objects example
abstract class PowerupEffect{
public abstract void Apply(GameObject collector);
}
So an example of this suppose I have a game bullet-hell scrolling
shooter and I'm picking up power-ups as I as I fly through.
I have a abstarct class for my powerup effect
that should have been scriptable object.

85
Abstract class
abstract class PowerupEffect{
public abstract void Apply(GameObject collector);
}
It(Abstract class) doesn't do anything and it has abstract void apply
method.
The idea is that whenever anything picks up a powerup it's going to call
Apply()

86
Delegate objects example [1]
abstract class PowerupEffect{
public abstract void Apply(GameObject collector);
}
public void OnTriggerEnter(GameObject toucher)
{
Destory(gameobject);
powerupEffect.Apply(toucher);
}

87
Delegate objects example [2]
Look at an actual example thenhow we would derive from that.
Here's something like a like a health powerup.
So we're deriving from powerup effect.
We're implementing this apply method and we have a tunable
amount value so I can have two or three different health
powerup effects in my project.

88
Delegate objects example [3]
You know a small buff, big buff, ultra buff, and oh and even
something that removes health as well.
I could just have a negative amount value so I pass in the
player object or the NPC or let me picked up the powerup as
the collector parameter and I been inside the scriptable
object I'm getting the health component I'm adjusting the
value accordingly.

89
Delegate objects example [4]
So I'm delegating the behavior of what should actually
happen in that powerup after the scriptableobject on the
side.
Again, you could do this using a prefab as well,
But this(delagate in SO) lets you separate out the actual
gameplay consequences of this powerup from all the other
details like collider, visual aspects and so on.

90
Delegate objects example- code - Pull out Audio properties [5]
public class HealthBuff : PowerupEffect{
public float amount;
public override void Apply(GameObject collector){
collector.GetComponent<Health>().value += amount;
}
}
A lot of the value in all of this stuff is in separation of concerns.
Like Pull out Audio properties.

91
Delegate objects example - Pull out Audio properties [6]
I want to pull out the audio properties of the gameobjects.
People please to be able to keep them somewhere
where my designer or my audio guy is just going to work on those
without having to kind of go and trawl(find) through this prefab and
find one component somewhere in the middle of it that they want to
work on.

92
Delegate objects example- code - InstaKill [7]
public class InstaKill : PowerupEffect{
public GameObject explosionFX;
public override void Apply(GameObject collector){
Instantiate(explosionFX, collector.position, collector.rotation;
Destory(collector);
}
}
Here's a second one, insta-kill.
It's kind of a harsh powerup.
You get this one and will make an explosion and will destroy whatever picks it up.

93
Delegate objects example - Pull out Audio properties [8]
InstaKill (A collectable object but will destroy whatever picks it up. )
So yeah you don't want to get this one but again you know we can
have a whole bunch of different instantKill of different explosions.
You want a nice burst, fireball etc.
It's the same code, same object type.
Just different instances configured in different ways, and then plugged
into different power-ups.

94
Delegate objects example- code - HealthBuffn & InstaKill [9]
public class HealthBuff : PowerupEffect{
public float amount;
public override void Apply(GameObject collector){
collector.GetComponent<Health>().value += amount;
}
}
public class InstaKill : PowerupEffect{
public GameObject explosionFX;
public override void Apply(GameObject collector){
Instantiate(explosionFX, collector.position, collector.rotation;
Destory(collector);
}
}

95
Practice

Let’s move on the live demo.


This is based on the demo project that
was used at the Boston training day in
2015

96
TANKS! Demo
● Unite 2015 Training Day project
● Audio events
● Destructable buildings
● Pluggable AIs
● Configurable game settings with load/save

97
TANKS! Demo
It’s a pretty simple game.
You drive a tank around, fire shells to the other player and try to be the last
man standing.
There's a few things I'd like to do to that project.
Firstly I'm going to see if I can improve the audio a little bit because
they've got a few audio clips playing repeatedly.
But there's some stuff we can do to very quickly
actually make the audio a little bit more impressive.

98
TANKS! Demo
Second thing is I'm going to add destructible buildings,
because who doesn't love destructible buildings.

99

You might also like