Skip to content

Latest commit

 

History

History
185 lines (127 loc) · 10.4 KB

File metadata and controls

185 lines (127 loc) · 10.4 KB

Scripting

Scripts unlock a lot of the potential of the Unity engine because they enable you to perform actions such as create graphical effects, control physical gameplay behaviour or react to user input.

Scripting

Unity Scripts

Below is the basic script created by Unity whenever you Create, C# Script. It demonstrates a couple of essential features.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class NewBehaviourScript : MonoBehaviour
{

    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        
    }
}

C# is an object-oriented language featuring classes that you instantiate as objects (as and when required). The class NewBehaviourScript, above, inherits from Unity's MonoBehaviour, which is a base class from which every Unity script inherits its functionality. NewBehaviourScript overrides the MonoBehaviour methods Start and Update - Start is called when the object is first created (and before the first frame - it is common practice to put any initialisation of member variables here), and Update is called once every frame render.

MonoBehaviour defines other behaviour, too - the one you will likely see most frequently is FixedUpdate, which Unity's physics system calls at a rate independent from the main thread's (rendering) frame rate. For example, imagine the physics engine is running at fifty calls per second, and the frame rate is running at twenty-five frames per second (FPS) - then FixedUpdate will be called twice per frame. Therefore, it is bad practice to put physics calculations in Update - they should go in FixedUpdate, instead (similarly, user input should go in Update, and not FixedUpdate.). Figure 1, below, shows a flowchart for MonoBehaviour.

Figure 1: MonoBehaviour Flow Chart

Unity defaults to a Time Fixed Timestep of 0.02, which translates to FixedUpdate getting called fifty times a second, and a VFX Fixed Timestep of 0.001666667, which translates to 60 FPS.

A Simple Script

You can write some very simple scripts and still do interesting things in Unity (so don't worry if you do not have much/any programming experience). For example, here's a simple script that spawns objects into a scene:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SpawnObjects : MonoBehaviour
{
    [SerializeField] private GameObject spawnObject;
    [SerializeField] private Transform spawnPoint;

    [Range(0, 1000)]
    [SerializeField] private int maxObjects = 1;

    private int numObjects; 

    // Update is called once per frame
    void Update()
    {
        if (numObjects < maxObjects)
        {
            Spawn(numObjects);
            numObjects++;
        }  

    }

    void Spawn(int num)
    {
        GameObject mObjectClone = Instantiate(spawnObject, spawnPoint.position, Quaternion.identity) as GameObject;
        mObjectClone.SetActive(true);
    }
}

Drag the script onto a GameObject and call that object SpawnManager. Next, create a Cube GameObject and drag that into the script's Spawn Object field. Finally, add an empty GameObject Spawn Point into the scene and add that to the script's Spawn Point field. Set the Max Objects slider to 10. Press Play - you should have ten more cubes at the Spawn Point - congratulations! You have created a scripted scene!

Design Patterns

However, to explore the full power of Unity's scripting, you will probably have to write something more extensive, requiring some planning and design. The Gang of Four's classic programming book, Design Patterns: Elements of Reusable Object-Oriented Software by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides describes various ways of organising software systems. The patterns described in that book are the inspiration for the site Game Programming Patterns, which was created by Bob Nystrom, a former employee at Electronic Arts, the company responsible for many popular games, including EA Sports FC (FiFa) and The Sims.

Game Programming Patterns describes six of the Gang of Four's patterns that a programmer might apply to a game:

  1. Command
  2. Flyweight
  3. Observer
  4. Prototype
  5. Singleton
  6. State

Below is a brief discussion of two of those patterns (Singleton and Observer), but you should visit Bob Nystrom's site and have a read about all the patterns for yourself (especially if you want to enter the game's world as a programmer).

Singleton

The Singleton pattern is pervasive in the game's world (and beyond) - it's common to see various all-powerful single Manager classes that govern the behaviour of various parts of a game (Bob Nystrom's site has an interesting discussion on whether that is a good thing or not).

Figure 2 below is a class design showing examples of object-oriented composition and inheritance. The GameManager is a Singleton that has-a UI, has-a SpawnManager (which might also be a Singleton), has-a Player and has-a list of Places. Addititionally, SinkHole is-a EntryPoint, as are VideoController and Platform.

UML Singleton

Figure 2: Singleton

Observer

The Observer pattern is an event-driven Model-View-Controller (MVC) architecture that's as pervasive (if not more so) as the Singleton pattern. It lets one piece of code announce an event without caring who receives that event or what actions they take. That leads to software designs featuring low coupling and high cohesion, which, in general, is considered a good thing.

Scriptable Objects as Observers

Unity's Scriptable Objects allows you to create and edit GameObjects independently of scenes - they're super-useful for preserving data across levels or subscribing behaviour to different instances of similar objects.

To use a ScriptableObject, create a script and make it inherit from the ScriptableObject class. You can also use the CreateAssetMenu attribute to make it easy to create custom assets using your ScriptableObject class.

using UnityEngine;

[CreateAssetMenu(fileName = "Data", menuName = "ScriptableObjects/SpawnManagerScriptableObject", order = 1)]
public class SpawnManagerScriptableObject : ScriptableObject
{
    public string prefabName;

    public int numberOfPrefabsToCreate;
    public Vector3[] spawnPoints;
}

With the above script in your Assets folder, you can create an instance of your ScriptableObject by navigating to Assets > Create > ScriptableObjects > SpawnManagerScriptableObject. Give your new ScriptableObject instance a meaningful name and alter the values. To use these values, you need to create a new script that references your ScriptableObject, in this case, a SpawnManagerScriptableObject:

using UnityEngine;

public class Spawner : MonoBehaviour
{
    // The GameObject to instantiate.
    public GameObject entityToSpawn;

    // An instance of the ScriptableObject defined above.
    public SpawnManagerScriptableObject spawnManagerValues;

    // This will be appended to the name of the created entities and increment when each is created.
    int instanceNumber = 1;

    void Start()
    {
            SpawnEntities();
    }

    void SpawnEntities()
    {
        int currentSpawnPointIndex = 0;

        for (int i = 0; i < spawnManagerValues.numberOfPrefabsToCreate; i++)
        {
            // Creates an instance of the prefab at the current spawn point.
            GameObject currentEntity = Instantiate(entityToSpawn, spawnManagerValues.spawnPoints[currentSpawnPointIndex], Quaternion.identity);

            // Sets the name of the instantiated entity as the string defined in the ScriptableObject and then appends it with a unique number. 
            currentEntity.name = spawnManagerValues.prefabName + instanceNumber;

            // Moves to the next spawn point index. If it goes out of range, it wraps back to the start.
            currentSpawnPointIndex = (currentSpawnPointIndex + 1) % spawnManagerValues.spawnPoints.Length;

            instanceNumber++;
        }
    }
}

Attach the above script to a GameObject in a Scene, and in the Inspector, set the Spawn Manager Values field to the new SpawnManagerScriptableObject. Set the Entity To Spawn field to any Prefab in your Assets folder, then click Play in the Editor. The Prefab you referenced in the Spawner instantiates using the values you set in the SpawnManagerScriptableObject instance.

However, ScriptableObjects can do many more things, including implementing the Observer pattern by managing in-game interactions through Events and event channels.

External Links