Part 5: Applying Scripts to the Scene

This post follows: Part 4: Using the State Management Script

We go back to the Mars Base scene and create similar UI Text objects and an Empty GameObject to hold the State Manager script from the test scene. Instead of sliders, we’ll create additional scripts for the modules on the base that will interact with the state management script.

Almost each module maps to either a generator or consumer role like the Greenhouse producing Food, AWProcessor for Air/Water and the Habitation consuming Air, Water, Food. There are additional steps required for handling Power since each module consumes power and our State Manager expects the total power consumption; to organize the setup, we’ll coordinate power needs in the Power script attached to the Power Generation Module of the base. We also introduce variable consumption levels based on number of occupants in the Habitation Module.

Let’s define a C# base class that handles the common properties and methods for the Mars Base. Right now, we know that each module has a defined Power Consumption Level. We’ll extend this class for the different capabilities of the other modules.

Module.cs

using UnityEngine;

public class Module : MonoBehaviour
{
    public float PowerConsumption;
}

The Greenhouse consumes power and generates food, the GreenhouseModule class is derived from Module and extends it with the FoodGeneration property that it will later pass to the State Manager.

GreenhouseModule.cs

using UnityEngine;

public class GreenhouseModule : Module
{
    public float FoodGeneration = 5f;    

    private StateManager mgr;

    void Start()
    {
        mgr = GameObject.FindObjectOfType<StateManager>();
        UpdateState();
    }

    void UpdateState()
    {
        mgr.FoodGeneration = FoodGeneration;
    }
}

We follow a similar approach with the AWPModule class for Air and Water Production setup.

AWPModule.cs

using UnityEngine;

public class AWPModule : Module
{
    public float AirGeneration = 5f;
    public float WaterGeneration = 5f;

    private StateManager mgr;

    void Start()
    {
        mgr = GameObject.FindObjectOfType<StateManager>();
        UpdateState();
    }

    void UpdateState()
    {
        mgr.AirGeneration = AirGeneration;
        mgr.WaterGeneration = WaterGeneration;
    }
}

The HabitationModule defines the resource consumers and introduces varying rates depending on the number of occupants.

HabitationModule.cs

using UnityEngine;

public class HabitationModule : Module
{
    public float OccupantAirConsumption = 1f;
    public float OccupantWaterConsumption = 1f;
    public float OccupantFoodConsumption = 1f;
    public int OccupantCount = 1;

    private StateManager mgr;

    void Start()
    {
        mgr = GameObject.FindObjectOfType<StateManager>();
        UpdateState();
    }

    void UpdateState()
    {
        mgr.AirConsumption = OccupantCount * OccupantAirConsumption;
        mgr.WaterConsumption = OccupantCount * OccupantWaterConsumption;
        mgr.FoodConsumption = OccupantCount * OccupantFoodConsumption;
    }
}

The PowerModule script is a bit more complicated than the earlier scripts as it is responsible for consolidating the total power consumption of the Mars Base and updating the State Manager. We use a List and setup the connections in the Editor at design time (this can also be done at runtime by searching for scene objects with a Module component – something we’ll do in later posts)

PowerModule.cs

using UnityEngine;
using System.Collections.Generic;

public class PowerModule : Module
{
    public float PowerGeneration;
    public List<Module> ConnectedModules;

    private StateManager mgr;

    void Start()
    {
        mgr = GameObject.FindObjectOfType<StateManager>();
        UpdateState();
    }

    void UpdateState()
    {
        float powerConsumption = PowerConsumption;
        foreach (var module in ConnectedModules)
        {
            powerConsumption += module.PowerConsumption;
        }

        mgr.PowerGeneration = PowerGeneration;
        mgr.PowerConsumption = powerConsumption;
    }
}

Part-05-PowerSetup

With all the scripts prepared, they need to be added as components to their corresponding GameObject (Greenhouse gets GreenhouseModule, Power gets PowerModule and so on). Let’s clean-up the scene and change the material color we’re using to something closer to light-gray/white. I also added a Plane object and position it at (0,0,0) and scale it to (100,1,100). We’re creating a Mars Base so let’s duplicate (CTRL-D) the existing material and change the Color of the duplicate to something close to red-orange. Assign the new material to the Plane and we’ll have a scene that looks similar to this scene.

Part-05-Scene

Let’s edit the UI Text output and value assignments in the State Manager so it’s easier to see what’s happening in the setup. We’ll display the current resource levels and the delta values that are configured in the scene.

StateManager.cs Snippet


   void UpdateDisplay()
    {
        AirText.text = string.Format("Air: {0}  ({1}{2})", AirLevel.ToString("N0"),
            (AirDelta > 0) ? "+" : (AirDelta < 0) ? "" : "=", AirDelta.ToString("N2"));
        WaterText.text = string.Format("Water: {0}  ({1}{2})", WaterLevel.ToString("N0"), 
            (WaterDelta > 0) ? "+" : (WaterDelta < 0) ? "" : "=", WaterDelta.ToString("N2"));
        FoodText.text = string.Format("Food: {0}  ({1}{2})", FoodLevel.ToString("N0"),
            (FoodDelta > 0) ? "+" : (FoodDelta < 0) ? "" : "=", FoodDelta.ToString("N2"));
        PowerText.text = string.Format("Power: {0}  ({1}{2})", PowerLevel.ToString("N0"),
            (PowerDelta > 0) ? "+" : (PowerDelta < 0) ? "" : "=", PowerDelta.ToString("N2"));
    }

   void UpdateValues()
    {
        AirLevel += ((AirDelta = (AirGeneration - AirConsumption)) * Time.deltaTime);
        AirLevel = Mathf.Clamp(AirLevel, 0f, AirMaxLevel);
        WaterLevel += ((WaterDelta = (WaterGeneration - WaterConsumption)) * Time.deltaTime);
        WaterLevel = Mathf.Clamp(WaterLevel, 0f, WaterMaxLevel);
        FoodLevel += ((FoodDelta = (FoodGeneration - FoodConsumption)) * Time.deltaTime);
        FoodLevel = Mathf.Clamp(FoodLevel, 0f, FoodMaxLevel);
        PowerLevel += ((PowerDelta = (PowerGeneration - PowerConsumption)) * Time.deltaTime);
        PowerLevel = Mathf.Clamp(PowerLevel, 0f, PowerMaxLevel);
    }

With everything in place, we should have something like this when the scene is running.

Part-05-RunScene

In the next post, I’ll add user input handling and interactions with the various modules.

For questions, comments or contact – follow me on Twitter @rlozada