Table of Contents

This tutorial covers the basics of what spaces are, how to create them, and how to use them with levels.

Learning Objectives

  • Creating new spaces
  • Basic HUD
  • ClearColor

Level Setup

What is a Space?

There have probably been a few times while learning Zilch that you typed this.Space but didn't really know what exactly a Space was. A Space is a special type of object which represents the space occupied by objects in a Level resource. Another way to think about it is as an object that represents the dimensions of whichever level is loaded into it, or as 3D area with infinite bounds that contains a level.

Space Basics

  • Each game can have multiple spaces
  • Objects in different spaces cannot interact graphically or physically
  • Each Space (usually) has its own renderer(s) that draws the loaded level to the viewport
  • Each Component and each Cog contains a member variable that is a reference to the Space it exists in

Creating a Space

A very common reason for creating a second space is to implement a HUD that overlays the game. This approach takes advantage of the fact that objects in one space will not directly interact with objects in another. Let's make the level for our first HUD.

  • Command : Add Resource
  • Create a Level resource using the {nav icon=clone, name=2D Level} template and name it HUDLevel
  • Command : CreateSpriteText
  • In the Property Window
  • Under SpriteText
  • Set Text to HUD Level SpriteText
  • Select : GameCamera object
  • In the Properties Window
  • Under Camera
  • Set Size to 5
  • Open the Level resource named Level
  • Select : LevelSettings object
  • In the Properties Window
  • Add Component : HUDManager
  • In HUDManager
  • Update the HUDManager script to the following:
class HUDManager : NadaComponent
{
  [Property]
  var HUDLevel : Level;
  
  [Property]
  var HUDSpaceArchetype : Archetype = Archetype.Space;
  
  var HUDSpace : Space;
  
  function Initialize(init : CogInitializer)
  {
    this.HUDSpace = this.GameSession.CreateSpace(this.HUDSpaceArchetype);
    this.HUDSpace.LoadLevel(this.HUDLevel);
  }
}

image

Notice how the text from the HUDLevel Level resource is visible, but not the text from the game Level resource. This is because by default spaces are rendered as opaque.

Overlaying the HUD

  • Open the Level resource named HUDLevel
  • Select : Renderer object
  • In the Properties Window
  • Under ForwardRenderer
  • Set ClearColor to [R:0, G:0, B:0, A:0.00]
  • Select : GameCamera object
  • In the Properties Window
  • Under CameraViewport
  • Set RenderOrder to 1
  • Select : SpriteText object
  • In the Properties Window
  • Under Transform
  • Set Translation to [0,1,0]
  • Open the Level resource named Level
  • Command : PlayGame

image

Notice that both text objects are now visible. This is because we reduced the alpha value of the HUD level clear color to 0. The clear color is what is rendered behind everything in a level. We've also set the RenderOrder of the HUD GameCamera object's CameraViewport to 1, while the main space's RenderOrder remains at its default value of 0. This is important for layering purposes: no two overlaid CameraViewports should have the same RenderOrder .

Now that we know how to make a space transparent, it is fairly simple to extrapolate constructing a HUD within a dedicated level and rendering it on top of a game.

Communicating Between Spaces

Having multiple spaces can increase a project's complexity. It is important to know how to allow components which exists in seperate spaces to communicate with each other. Information is often sent between spaces via events. Let's make a basic meter in our HUD.

  • Command : Add Resource
  • Create a NadaScript resource using the Component template template and name it Meter
  • Update the Meter script to the following:
class Meter : NadaComponent
{
  [Dependency] var Transform : Transform;
  [Dependency] var Area : Area;
  
  [Property]
  var MaxValue : Real = 10;
  
  var CurrentValue : Real;
  
  var MeterScaleAction : Action;
  
  var OriginalSize : Real2;
  
  var ScaleRate : Real = 1.0;
  
  function Initialize(init : CogInitializer)
  {
    this.CurrentValue = this.MaxValue;
    this.OriginalSize = this.Area.Size;
    Zilch.Connect(this.Space, Events.MeterUpdate, this.OnMeterUpdate);
  }

  function OnMeterUpdate(event : MeterUpdate)
  {
    var val = this.CurrentValue + event.Value;
    this.CurrentValue = Math.Clamp(val, 0.0, this.MaxValue);
    this.ScaleMeter();
  }
  
  function ScaleMeter()
  {
    if(this.MeterScaleAction != null)
      this.MeterScaleAction.Cancel();
    
    //calculate and constrain the new meter size
    var perc = this.CurrentValue / this.MaxValue;
    var targetSize = perc * this.OriginalSize;
    targetSize.Y = this.OriginalSize.Y;
    
    //calculate the duration of the scale action
    var sizeDifference = Math.Abs(this.Area.Size.X - targetSize.X);
    var duration = sizeDifference / this.ScaleRate;
    
    this.MeterScaleAction = Action.Property(this.Owner.Actions, @this.Area.Size, targetSize, duration, Ease.Linear);
  }
}

class MeterUpdate : NadaEvent
{
  sends MeterUpdate : MeterUpdate;
  var Value : Real;
}

This Meter component will be be communicated// with via events sent from the //GameSpace// to the //HUDSpace by the following Input component.

  • Command : Add Resource
  • Create a NadaScript resource using the Component template template and name it Input
  • Update the Input script to the following:
class Input : NadaComponent
{
  [Dependency] var HUDManager : HUDManager;
  
  [Property]
  var IncreaseKey : Keys = Keys.Up;
  
  [Property]
  var DecreaseKey : Keys = Keys.Down;
  
  [Property]
  var InputValue : Real = 1.0;
  
  function Initialize(init : CogInitializer)
  {
    Zilch.Connect(Zilch.Keyboard, Events.KeyDown, this.OnKeyDown);
  }

  function OnKeyDown(event : KeyboardEvent)
  {
    if(event.Key == this.IncreaseKey)
    {
      Console.WriteLine("Increase");
      this.UpdateMeter(this.InputValue);
    }
    else if(event.Key == this.DecreaseKey)
    {
      Console.WriteLine("Decrease");
      this.UpdateMeter(-this.InputValue);
    }
  }
  
  function UpdateMeter(val : Real)
  {
    var meterUpdateEvent = MeterUpdate();
    meterUpdateEvent.Value = val;
    this.HUDManager.HUDSpace.DispatchEvent(Events.MeterUpdate, meterUpdateEvent);
  }
}

This simple input component will allow us to use the meter to simulate the game behavior of losing and gaining health without having to actually implement a game for this tutorial.

MeterScale

Each time Up or Down is pressed, the MeterUpdate event is dispatched on the HUDSpace. The Meter component is listening to its space (this HUDSpace) for that event. So when it receives the event, the ScaleMeter function is invoked.

(NOTE)Notice how the meter renders over the SpriteText object in the GameLevel. As mentioned earlier, spaces can not interact graphically or physically (forces, collision, clipping, etc.), and this is a prime example of that behavior.

Related Materials

Manual

  • Command
  • Add Resource
  • Select
  • {icon university}[[zilch_engine_documentation/zilch_editor_documentation/zilchmanual/editor/addremovecomponent/|Add/Remove Component]]

Tutorial

Reference

Classes

Enums

Commands

Tasks

  • T1175