Wat zijn buildingblocks? 2018-06-15 11:06

Het woord 'buildingblocks' is vrijwel overal in dit portfolio te vinden, maar wat betekend het nu eigenlijk? Kortom, de buildingblocks zijn een verzameling assets die gebruikt kunnen worden in een Unity project om bepaalde functionaliteiten toe te voegen. Door de buildingblocks te gebruiken hoeft er niet elke keer voor dezelfde functionaliteit nieuwe code geschreven te worden.

Services, Actions & Steps

Er zijn op het moment 25 buildingblocks, het aantal groeit nog steeds. 6 hiervan worden gebruikt in het dit project. Voordat er uitgelegd kan worden wat deze buildingblocks doen, moet er eerst beschreven worden wat 'Services' en 'Actions' zijn.

Services

Een service is een object dat bepaalde functionaliteiten in de scene uitvoert. Bijvoorbeeld: de AudioService heeft methodes waarmee geluiden afgespeeld kunnen worden; De InputService vangt input events op in een object en maakt het makkelijker naar een bepaald event te luisteren.
De services zijn dus een soort van managers die helpen algemene functionaliteiten uit te voeren.

Actions

In Virtual Studio worden verschillende stappen opgesteld. De stappen beschrijven de acties die de speler moet doorlopen. Bijvoorbeeld, 'Zorg ervoor dat de omstander 112 belt'. Een stap kan voltooid worden door verschillende acties uit te voeren. Bijvoorbeeld, 'Selecteer de bel 112 optie uit het menu' > 'Speel bel animatie af op omstander' > 'Speel feedback geluid af zodra animatie is afgelopen'.
Deze acties worden in Unity opgesteld als klassen. De stappen worden dan in Virtual Studio opgebouwd uit verschillende van deze acties (wellicht dezelfde meerdere keren met verschillende instellingen).
Hieronder is bijvoorbeeld een Action die checkt of de camera (dus het hoofd van de speler) in de buurt is van een object voor een bepaalde tijd:


using System.Collections;
using BlueTea.AEDMR.BTVS.ActionStrategies;
using BlueTea.AEDMR.BTVS.WorldObjects;
using BlueTea.BuildingBlocks.HoloLens;
using BlueTea.BuildingBlocks.VirtualStudio;
using UnityEngine;

namespace BlueTea.AEDMR.BTVS.Actions
{
    [ActionDescription("Make the camera be near another object for a certain amount of seconds")]
    [ActionTypes(ActionType.UserInteractive)]
    [Action(typeof(FindWorldObjectByIDStrategy))]
    public class CameraProximityAction : Action
    {
        #region Properties

        protected PropertyInformation DistanceInfo = new PropertyInformation
        {
            Description = "How far may the camera be from the target object for it to count as being in proximity",
            DefaultValue = new DefaultValue(1, null)
        };

        public float Distance { get; set; }

        protected PropertyInformation DurationInfo = new PropertyInformation
        {
            Description = "The amount of seconds the camera should be in proximity of the target",
            DefaultValue = new DefaultValue(2, null)
        };

        public float Duration { get; set; }

        protected PropertyInformation InProximityInfo = new PropertyInformation
        {
            Description = "Should the camera be in proximity (our out of proximity)",
            DefaultValue = new DefaultValue(true, null)
        };

        public bool InProximity { get; set; }

        protected PropertyInformation PivotPointInfo = new PropertyInformation
        {
            Description = "The pivotpoint that should be used",
            DefaultValue = new DefaultValue("Root", null),
            Constraint = new Choice(CustomPivot.BoneTypes)
        };

        public string PivotPoint { get; set; }

        #endregion

        #region EventHandlers

        public override void Execute()
        {
            base.Execute();

            // Get strategy
            FindWorldObjectByIDStrategy strat = GetStrategyOfType<FindWorldObjectByIDStrategy>();
            if (strat == null)
            {
                ActionLogger.LogError("Unable to get strategy");
                OnActionFinished();
                return;
            }

            // Get worldobject
            WorldObjectID target = strat.Target;
            if (target == null)
            {
                OnActionFinished();
                return;
            }

            Transform targetTransform = target.transform;
            if (PivotPoint != "Root")
            {
                CustomPivot[] pivots = target.GetComponentsInChildren<CustomPivot>(true);
                foreach (CustomPivot pivot in pivots)
                    if (pivot.Bone.ToString().GetHashCode() == PivotPoint.GetHashCode())
                    {
                        targetTransform = pivot.transform;
                        break;
                    }
            }

            // Start waiting for proximity
            GameObject waitObj = new GameObject("[WaitObject]");
            WaitBehaviour beh = waitObj.AddComponent<WaitBehaviour>();
            beh.StartCoroutine(AwaitProximity(beh, targetTransform));
        }

        #endregion

        #region Private

        private IEnumerator AwaitProximity(WaitBehaviour beh, Transform target)
        {
            // Wait till the camera is in proximity
            bool distanceSufficient = InProximity
                ? Vector3.Distance(Camera.main.transform.position, target.position) <= Distance
                : Vector3.Distance(Camera.main.transform.position, target.position) >= Distance;
            while (!distanceSufficient)
            {
                distanceSufficient = InProximity
                    ? Vector3.Distance(Camera.main.transform.position, target.position) <= Distance
                    : Vector3.Distance(Camera.main.transform.position, target.position) >= Distance;
                yield return null;
            }

            // Wait till either the camera is out of proximity or enough time has passed
            float timePassed = 0;
            distanceSufficient = InProximity
                ? Vector3.Distance(Camera.main.transform.position, target.position) <= Distance
                : Vector3.Distance(Camera.main.transform.position, target.position) >= Distance;
            while (distanceSufficient && timePassed < Duration)
            {
                timePassed  = Time.deltaTime;

                distanceSufficient = InProximity
                    ? Vector3.Distance(Camera.main.transform.position, target.position) <= Distance
                    : Vector3.Distance(Camera.main.transform.position, target.position) >= Distance;
                yield return null;
            }

            // If enough time has passed end the action
            if (timePassed >= Duration)
            {
                ActionLogger.LogFormat("Camera was in proximity of {0} for {1} seconds", target.name, timePassed);
                Object.Destroy(beh.gameObject);
                OnActionFinished();
                yield break;
            }

            ActionLogger.LogErrorFormat("Camera was not in proximity for long enough (time passed: {0} seconds)", timePassed);

            // Start new coroutine like this so we won't be stuck in this method forever.
            // If we'd used 'yield return AwaitProximity(...)' then the amount of coroutines running would stack up
            beh.StartCoroutine(AwaitProximity(beh, target));
        }

        #endregion
    }
}

Ook is er nog een verschil tussen 'Actions' en 'ActionStrategies'. Een ActionStrategy is een stuk functionaliteit dat in meerdere actions kan voorkomen. Bijvoorbeeld het ophalen van een object in de wereld aan de hand van de naam van het object. Of het afspelen van een geluid.

BuildingBlocks

Nu dat het duidelijk is wat services, actions en actionstrategies zijn, kan gekeken worden naar een aantal buildingblocks, zodat het duidelijk worden op welke manier ze worden toegepast in projecten.

General

Een collectie van services en andere assets die door alle projecten gebruikt worden. Vrijwel alle andere buildingblocks zijn afhankelijk van deze buildingblock. In deze buildingblock zijn onder andere de audio-, input- en canvasservices te vinden. Daarnaast is de definitie van de services (dus de abstracte Service klasse) in deze buildingblock te vinden.

Game

Een diagram die alle mogelijke transities tussen scenes laat zien in een Virtual Studio app.

Algemene services die gebruikt worden in een spel. Bijvoorbeeld de GameService die de verschillende scenes kan laden volgens de standaard scene flow die gebruikt wordt in Virtual Studio apps (zie afbeelding). Naast de GameService is er ook de ScoreService. In dit project is er niks mee gedaan, maar in andere projecten wordt deze service gebruikt om bij te houden wat de score is van een speler tijdens het doorlopen van de lessen. In Virtual Studio wordt deze score gebruikt door de docent om de prestaties van de leerlingen te evalueren.

Rest

Er wordt met Virtual Studio gecommuniceerd via een rest API. Deze buildingblock bevat alle nodige onderdelen om deze connectie te maken, maar maakt de daadwerkelijke connectie niet. De buildingblock zou dus ook gebruikt kunnen worden in een project dat niet met Virtual Studio hoeft te verbinden, maar een andere rest API moet aanspreken.

Virtual Studio

In deze buildingblock worden alle onderdelen die specifiek te maken hebben met Virtual Studio afgehandeld. Deze buildingblock is dan ook afhankelijk van alle bovengenoemde buildingblocks. Dingen zoals het inloggen op Virtual Studio en ophalen van lessen wordt hierin gedaan. Ook bevat deze buildingblock de definitie van onder andere de 'Action' klasse en de 'Lesson' klasse.

Localization

Zoals de titel al zegt bevat deze buildingblock de nodige assets voor het ondersteunen van meerdere talen in een app. Deze buildingblock is gebaseerd op de I2Loc plugin, maar is flink aangepast om te voldoen aan de eisen van BlueTea. 

HoloLens

Deze buildingblock is essentieel voor elk project dat support wilt toevoegen voor de HoloLens. Deze buildingblock heeft assets voor het fixen van problemen die zich voorden bij het builden naar de HoloLens. Bijvoorbeeld een InputService die inhaakt op de inputevents van de MRToolkit zonder dat er iets aan de code veranderd hoeft te worden van klasses die gebruik maken van de InputService. Daarnaast bevat deze buildingblock een aantal prefabs en scripts voor unieke HoloLens interactie mogelijkheden, zoals spatial understanding en de login UI.


Dit zijn de buildingblocks die gebruikt worden in het AED-MR project. Elke buildingblock brengt zijn eigen functionaliteit in het project en heeft zijn eigen afhankelijkheden.

Comments