View Categories

World Builder – Runtime Scripting Tutorials

6 min read

1. MyHoverComponent Tutorial #

The Hover Component creates a simple up-and-down floating motion – perfect for highlighting objects or creating ambient movement in educational scenes!

Purpose #

This component makes objects float up and down using a sine wave function, creating a smooth, natural-looking hover effect.

Step-by-Step Tutorial #

1. Create the Component Script #

using System.Collections.Generic;
using UnityEngine;
using Newtonsoft.Json;
using WorldBuilder.Core.Components;

namespace MyComponents
{
    public class MyHoverComponent: DataComponentBase
    {
        [JsonProperty("Speed")]
        public float Speed = 1.0f;

        [JsonProperty("Height")]
        public float Height = 2.0f;

        private Vector3 m_InitialPosition;

        void Start()
        {
            m_InitialPosition = transform.position;
        }

        void Update()
        {
            float newY = Mathf.Sin(Time.time * Speed) * Height + m_InitialPosition.y;
            transform.position = new Vector3(transform.position.x, newY, transform.position.z);
        }

        public override List<string> GetAssetReferences() { return null; }
        public override void AfterDeserialization() { }
    }
}

2. Create the Inspector View #

using UnityEngine;
using UnityEngine.UIElements;
using WorldBuilder.UI;
using WorldBuilder.Plugins;
using WorldBuilder.Plugins.UI;
using System;
using WorldBuilder.Core.Components.Inspectors;

namespace MyComponents
{
    public class MyHoverComponentInspectorView : DataComponentInspectorViewBase
    {
        public override Type TypeOfDataComponent { get => typeof(MyHoverComponent); }

        public MyHoverComponentInspectorView(Texture2D icon) : base(icon) { }

        public override void CreateInspectorEntries(VisualElement window, IReferenceProvider referenceProvider, IUIElementFactory elementFactory, object instance)
        {
            var hoverComponent = instance as MyHoverComponent;

            elementFactory.CreateInspectorFloatField(window, "Speed", hoverComponent.Speed, (oldValue, newValue) =>
            {
                hoverComponent.Speed = newValue;
            });

            elementFactory.CreateInspectorFloatField(window, "Height", hoverComponent.Height, (oldValue, newValue) =>
            {
                hoverComponent.Height = newValue;
            });
        }
    }
}

3. How It Works #

  1. Component Properties:

    • Speed: Controls how quickly the object moves up and down (oscillation frequency)
    • Height: Determines the maximum distance the object moves from its starting position
  2. The Math Behind It:

    • The sine function creates a smooth wave pattern between -1 and 1
    • Multiplying by Height scales the wave to the desired amplitude
    • Adding the initial Y position centers the wave around the object’s starting point
  3. Runtime Behavior:

    • Start(): Captures the initial position when the component is initialized
    • Update(): Recalculates the position every frame using the sine wave
    • Only the Y-axis changes; X and Z remain constant

4. Using the Component #

  1. Add the component to any object you want to hover
  2. Adjust the Speed parameter to control how quickly it moves (lower = slower)
  3. Adjust the Height parameter to control how far it moves from its starting position

2. MyInteractivePulseComponent Tutorial #

The Pulse Component creates a rhythmic scaling effect – perfect for drawing attention to objects or creating breathing-like animations!

Purpose #

This component repeatedly scales an object up and down, creating a pulsing visual effect.

Step-by-Step Tutorial #

1. Create the Component Script #

using System.Collections.Generic;
using UnityEngine;
using Newtonsoft.Json;
using WorldBuilder.Core.Components;

namespace MyComponents
{
    public class MyInteractivePulseComponent : DataComponentBase
    {
        [JsonProperty("PulseSpeed")]
        public float PulseSpeed = 2.0f;

        [JsonProperty("PulseScale")]
        public float PulseScale = 1.5f;

        private Vector3 m_OriginalScale;

        void Start()
        {
            m_OriginalScale = transform.localScale;
        }

        void Update()
        {
            float scale = Mathf.Abs(Mathf.Sin(Time.time * PulseSpeed)) * PulseScale;
            transform.localScale = m_OriginalScale * (1 + scale);
        }

        public override List<string> GetAssetReferences() => null;
        public override void AfterDeserialization() { }
    }
}

2. Create the Inspector View #

using UnityEngine;
using UnityEngine.UIElements;
using WorldBuilder.UI;
using WorldBuilder.Plugins;
using WorldBuilder.Plugins.UI;
using System;
using WorldBuilder.Core.Components.Inspectors;

namespace MyComponents
{
    public class MyInteractivePulseInspectorView : DataComponentInspectorViewBase
    {
        public override Type TypeOfDataComponent => typeof(MyInteractivePulseComponent);

        public MyInteractivePulseInspectorView(Texture2D icon) : base(icon) { }

        public override void CreateInspectorEntries(VisualElement window, IReferenceProvider referenceProvider, IUIElementFactory elementFactory, object instance)
        {
            var pulseComponent = instance as MyInteractivePulseComponent;

            elementFactory.CreateInspectorFloatField(window, "Pulse Speed", pulseComponent.PulseSpeed, (oldValue, newValue) =>
            {
                pulseComponent.PulseSpeed = newValue;
            }, "Hz");

            elementFactory.CreateInspectorFloatField(window, "Pulse Scale", pulseComponent.PulseScale, (oldValue, newValue) =>
            {
                pulseComponent.PulseScale = newValue;
            }, "x");
        }
    }
}

3. How It Works #

  1. Component Properties:

    • PulseSpeed: Controls how quickly the object pulses (in cycles per second)
    • PulseScale: Determines how much the object’s size changes during pulsing
  2. The Math Behind It:

    • Uses a sine wave like the Hover component, but takes the absolute value
    • This creates a “bouncing” effect between 0 and 1 instead of -1 and 1
    • Multiplies by PulseScale to determine maximum size increase
    • Adds 1 to maintain the original size as the minimum
  3. Runtime Behavior:

    • Start(): Captures the initial scale when the component is initialized
    • Update(): Recalculates the scale every frame using the sine wave
    • All axes scale uniformly (the object grows/shrinks in all dimensions)

4. Using the Component #

  1. Add the component to any object you want to pulse
  2. Adjust the PulseSpeed parameter (higher values make it pulse faster)
  3. Adjust the PulseScale parameter (higher values make it grow larger)

3. MyLookAtComponent Tutorial #

The LookAt Component makes objects track and face a target – perfect for creating attention-directing elements in educational scenes!

Purpose #

This component makes an object continuously rotate to face another specified object in the scene.

Step-by-Step Tutorial #

1. Create the Component Script #

using System.Collections.Generic;
using UnityEngine;
using Newtonsoft.Json;
using WorldBuilder.Core.Components;

namespace MyComponents
{
    public class MyLookAtComponent : DataComponentBase
    {
        [JsonProperty("TargetObjectName")]
        public string TargetObjectName;

        private Transform m_TargetTransform;

        void Start()
        {
            var target = GameObject.Find(TargetObjectName);
            if (target)
                m_TargetTransform = target.transform;
        }

        void Update()
        {
            if (m_TargetTransform)
                transform.LookAt(m_TargetTransform);
        }

        public override List<string> GetAssetReferences() => null;
        public override void AfterDeserialization() { }
    }
}

2. Creating the Inspector View #

For this component, we could create an inspector view similar to the others. Here’s how it might look:

using UnityEngine;
using UnityEngine.UIElements;
using WorldBuilder.UI;
using WorldBuilder.Plugins;
using WorldBuilder.Plugins.UI;
using System;
using WorldBuilder.Core.Components.Inspectors;

namespace MyComponents
{
    public class MyLookAtComponentInspectorView : DataComponentInspectorViewBase
    {
        public override Type TypeOfDataComponent => typeof(MyLookAtComponent);

        public MyLookAtComponentInspectorView(Texture2D icon) : base(icon) { }

        public override void CreateInspectorEntries(VisualElement window, IReferenceProvider referenceProvider, IUIElementFactory elementFactory, object instance)
        {
            var lookAtComponent = instance as MyLookAtComponent;

            elementFactory.CreateInspectorTextField(window, "Target Object", lookAtComponent.TargetObjectName, (oldValue, newValue) =>
            {
                lookAtComponent.TargetObjectName = newValue;
            });
        }
    }
}

3. How It Works #

  1. Component Properties:

    • TargetObjectName: The name of the object this component should face toward
  2. Implementation Details:

    • Uses Unity’s built-in LookAt() method, which rotates an object to face a target
    • Finds the target by name at startup using GameObject.Find()
    • Caches the target’s transform for efficiency
    • Only rotates if a valid target is found
  3. Runtime Behavior:

    • Start(): Finds the target object by name and caches its transform
    • Update(): Continuously updates rotation to face the target every frame

4. Using the Component #

  1. Add the component to any object that should face another object
  2. Set the TargetObjectName to match the exact name of the target object
  3. The object will now always rotate to face the target

4. MyOrbitComponent Tutorial #

The Orbit Component creates circular movement around a target – perfect for simulating planetary motion or creating dynamic, circling elements!

Purpose #

This component makes an object orbit around another specified object (or its initial position if no target is found).

Step-by-Step Tutorial #

1. Create the Component Script #

using System.Collections.Generic;
using UnityEngine;
using Newtonsoft.Json;
using WorldBuilder.Core.Components;

namespace MyComponents
{
    public class MyOrbitComponent : DataComponentBase
    {
        [JsonProperty("OrbitSpeed")]
        public float OrbitSpeed = 6.0f;

        [JsonProperty("OrbitRadius")]
        public float OrbitRadius = 10.0f;

        [JsonProperty("TargetObjectName")]
        public string TargetObjectName;

        private Transform m_TargetTransform;
        private Vector3 m_CenterPoint;

        void Start()
        {
            var target = GameObject.Find(TargetObjectName);
            if (target)
                m_TargetTransform = target.transform;
            else
                m_CenterPoint = transform.position;
        }

        void Update()
        {
            Vector3 center = m_TargetTransform ? m_TargetTransform.position : m_CenterPoint;
            transform.position = center + new Vector3(
                Mathf.Cos(Time.time * OrbitSpeed) * OrbitRadius, 
                transform.position.y, 
                Mathf.Sin(Time.time * OrbitSpeed) * OrbitRadius
            );
        }

        public override List<string> GetAssetReferences() => null;
        public override void AfterDeserialization() { }
    }
}

2. Create the Inspector View #

using UnityEngine;
using UnityEngine.UIElements;
using WorldBuilder.UI;
using WorldBuilder.Plugins;
using WorldBuilder.Plugins.UI;
using System;
using WorldBuilder.Core.Components.Inspectors;

namespace MyComponents
{
    public class MyOrbitComponentInspectorView : DataComponentInspectorViewBase
    {
        public override Type TypeOfDataComponent => typeof(MyOrbitComponent);

        public MyOrbitComponentInspectorView(Texture2D icon) : base(icon) {}

        public override void CreateInspectorEntries(VisualElement window, IReferenceProvider referenceProvider, IUIElementFactory elementFactory, object instance)
        {
            var orbitComponent = instance as MyOrbitComponent;

            elementFactory.CreateInspectorFloatField(window, "Orbit Speed", orbitComponent.OrbitSpeed, (oldValue, newValue) =>
            {
                orbitComponent.OrbitSpeed = newValue;
            }, "°/s", tooltip: "Speed at which object orbits around center");

            elementFactory.CreateInspectorFloatField(window, "Orbit Radius", orbitComponent.OrbitRadius, (oldValue, newValue) =>
            {
                orbitComponent.OrbitRadius = newValue;
            }, "m");

            elementFactory.CreateInspectorTextField(window, "Target Object", orbitComponent.TargetObjectName, (oldValue, newValue) =>
            {
                orbitComponent.TargetObjectName = newValue;
            });
        }
    }
}

3. How It Works #

  1. Component Properties:

    • OrbitSpeed: Controls how quickly the object moves around its target (angular velocity)
    • OrbitRadius: Determines the distance from the center point
    • TargetObjectName: The name of the object to orbit around
  2. The Math Behind It:

    • Uses sine and cosine functions to create circular motion
    • Cosine controls X position, Sine controls Z position
    • Multiplying by OrbitRadius sets the circle size
    • Multiplying by Time.time * OrbitSpeed creates continuous movement
  3. Runtime Behavior:

    • Start(): Finds the target object or defaults to orbiting the initial position
    • Update(): Recalculates position every frame to create circular movement
    • Maintains the original Y position (orbits in the XZ plane)

4. Using the Component #

  1. Add the component to any object that should orbit
  2. Set the TargetObjectName to the name of the center object (or leave empty to orbit initial position)
  3. Adjust OrbitSpeed to control how quickly it circles (higher = faster)
  4. Adjust OrbitRadius to control how far from the center it orbits