Mapbox SDK en custom UI systeem

Ik heb in de week van 27 mei tot en met 31 juni gewerkt aan het uitwerken van mijn game concept.

Ik heb eerst wat tests gedaan met de Mapbox SDK. In Unity is de SDK goed te integreren, zoals hieronder te zien is. Nieuwe delen van de kaart worden automatisch geladen en ontladen en in Mapbox studio kan je de stijlen aanpassen.

Vervolgens ben ik begonnen aan een eigen UI systeem waar ik makkelijk een multipage app mee kan maken. Dit is iets wat ik niet kon vinden in de standaard Unity functionaliteit. Hieronder is een overzicht te zien van hoe het is opgebouwd:

Er is een scene genaamd UI, met daarin een canvas object met het script UIManager.

Daarnaast is er een Prefab UI Screen, die de basis vormt voor elk nieuw scherm wat ik maak voor de game. Deze heeft het script UIScreenManager erop zitten en bevat het legacy Animation-component.

Het script UIScreenManager heeft een referentie naar de UIManager en kan functies doorgeven naar de UIManager. Deze functies zijn:

  • SendDestroyScreen
  • SendNextScreen(UIScreenManager nextScreen)
  • SendPreviousScreen(UIScreenManager previousScreen)
  • SendDismissBottomOverlay()
  • SendPresentBottomOverlay(UIScreenManager overlayScreen)
  • SendActivateCurrentScreen()
  • SendActivateScreen()
  • SendDeactivateScreen()

Ook is de belangrijke functie Animate onderdeel:

Animate(Vector2 position, Vector2 direction, float duration, bool destroyOnAnimationEnd, bool activateCurrentScreenOnAnimationEnd)

destroyOnAnimationEnd is dat wanneer de UI Screen uit het scherm wordt geanimeerd het verwijderd wordt als het buiten het scherm is gekomen.

activateCurrentScreenOnAnimationEnd is zodat de popup ook geactiveerd wordt.

Tijdens het animeren kan de gebruiker namelijk niet op knoppen klikken.

Hier is een demo van hoe het werkt:

Ideevorming en concept

Ik heb in eerste instantie gekeken naar wat ik wil maken. Om mijn ontwikkeling met AR voort te zetten wilde ik daar iets mee doen, maar ik wilde deze keer ook iets nieuws uitproberen met Unity.

Toen kwam ik toch uit op de echte wereld gebruiken: door een kaart of 3d weergave van de wereld. Hier zou ik dan een racespel in kunnen maken.

Ik wilde eerst de Google Maps API gebruiken voor Unity maar dat is alleen voor grote bedrijven die het serieus willen gebruiken. Toen kwam ik Mapbox tegen, die ook Unity integratie heeft. Omdat deze een gratis versie heeft wil ik met de Mapbox Unity SDK een spel maken.

Het idee wat ik hierboven heb uitgewerkt heb ik samen met vrienden en mijn broertje vaak gespeeld, maar we liepen altijd tegen een aantal problemen aan. Deze problemen wil ik oplossen door een spelhulp te maken op de smartphone.

Het spel was als volgt:

  • Er zijn twee teams: de boeven en de politie.
  • De boeven krijgen tijd om weg te rennen en dan moet de politie ze zoeken.
  • De boef die het langst niet gepakt wordt wint.

Een aantal problemen die hierbij ontstonden waren:

  • Je weet niet waar je teamgenoten zijn waardoor plannen moeilijk is.
  • Je weet niet wanneer het spel afgelopen is, waardoor er geen spanning naar het einde van het spel is. Het is dan meer “ja laten we er maar mee stoppen, we kunnen je niet vinden”
  • Een boef kan zich verstoppen op een plek waar hij niet te vinden is.
  • Er zijn geen duidelijke grenzen van waar tot waar je mag komen.

Hieronder heb ik een ontwerp in Adobe XD gemaakt van het spel:

Adobe XD wireframe

Game jam eerste les

We hebben bij de eerste les in blok 4 van BPW tijdens de game jam gebruik gemaakt van de Leap Motion. Het team bestond naast mij nog uit Daniël Korssen, Kai Calis en Wouter Kamies.

We hebben de taken als volgt verdeeld: Wouter en Kai maken assets, Daniël probeert de Leap Motion aan de praat te krijgen en ik maakte het spel zelf.

Voor de Leap Motion werkend te krijgen moesten we, en vooral Daniël, veel moeite doen. Daarom heb ik me vooral gefocust op de game mechanics maken zodat we dat in ieder geval hebben. Deze game mechanics zijn heel simpel: er komen noten aan en je moet op het juiste moment op de bongo slaan (in het geval van deze demo een toets indrukken) zodat je in het ritme blijft. Er zijn drie sporen van noten waardoor het moeilijker is.

Uiteindelijk heeft Daniël de Leap Motion aan de praat gekregen, waardoor dat stuk prioriteit heeft gekregen. Zo hebben we een leuke bongo scene neergezet waar mensen met de Leap Motion op konden slaan.

We hebben geen tijd meer gehad om de gameplay te combineren met de scene, waardoor we twee losse demo’s hebben. Dit is te zien in onderstaande filmpjes. De code is tevens te vinden op:

https://github.com/WouterKamies/BPW-GameJam-poging-2

Unity AR Game eindsprint

De deadline van vrijdag 29 maart kwam steeds dichter bij en ik had nog alsmaar geen idee hoe ik het idee kon uitbouwen tot een echte puzzel.

Op 28 maart heb ik een laatste update gegeven waarin ik laat zien dat ik een puzzel kan maken. Nou is alleen een puzzel niet voldoende. Er moet meer omheen. Daarom ben ik aan de slag gegaan met het verhalende aspect, een gebruikersinterface en het laden en ontladen van puzzels. Ook heb ik de puzzelelementen die de speler volgen een model gegeven zodat het lijkt op een bewakingscamera.

Hieronder ga ik een uitgebreide uitleg geven over de werking van het spel. Het complete Unity project staat op GitHub voor als je het zelf in werking wilt zien: https://github.com/ArjoNagelhout/ARGame

Plaatsing en herpositionering van het spel

Met augmented reality is het belangrijkste aspect dat de virtuele wereld in de echte wereld komt. Omdat mijn puzzel één vlak nodig heeft om op te staan, wil ik dat de speler deze uit kan kiezen. Wanneer het spel begint heeft de speler de mogelijkheid om het bord ergens te plaatsen. Deze plek kan dan bevestigd worden door op de knop te drukken. Ook moet de speler het spel mee kunnen nemen naar een andere plek. Dit herpositioneren heeft ook een knop gekregen.

Tijdens het plaatsen en herpositioneren wordt in het midden van het scherm gekeken of er een vlak gedetecteerd is en als dat het geval is wordt de positie van het te plaatsen object geüpdatet. Om ervoor te zorgen dat bij de eerste keer zonder bord het duidelijk is waar je het plaatst is er een plaatsingsindicator toegevoegd.

In onderstaande video is te zien hoe het plaatsen en herpositioneren werkt.

Onderstaande code zorgt ervoor dat het plaatsen en herpositioneren werkt:

Het laden van levels

Omdat ik wil dat de ervaring speler zo vloeiend mogelijk is. Wil ik dat wanneer de speler het level haalt, deze direct naar het volgende level gaat. Omdat in dat geval er niet gewisseld kan worden van scenes, heb ik ervoor gekozen om de levels te maken van prefabs. Deze prefabs worden vervolgens verwisseld wanneer de speler het level haalt.

Er is een object genaamd BoardLoader. Deze heeft een array met GameBoard prefabs. Hij heeft tevens de functie LoadLevel(id) zodat hij een nieuw GameBoard kan laden. Bij deze functie wordt het oude bord weggedaan en komt er een nieuwe voor in de plaats. Zie link voor de code: https://github.com/ArjoNagelhout/ARGame/blob/master/ARGame-project/Assets/Scripts/BoardLoader.cs

Door middel van een Animator wordt het bord omhoog en omlaag verplaatst om ze te wisselen.

Het halen van een level

Een GameBoard prefab heeft in het script de functie CheckProgress. Deze functie checkt of alle Target objecten in aanraking zijn met hun corresponderende FollowingCamera object.

Github Link: https://github.com/ArjoNagelhout/ARGame/blob/master/ARGame-project/Assets/Scripts/GameBoard.cs

De levels zijn opgebouwd uit de volgende onderdelen:

FollowingCamera

Een FollowingCamera heeft twee functies: Het beweegt naar de speler toe met de functie MoveTowardsPosition en met LookAtPosition wordt CameraHead gedraaid naar de speler.

Gebruikersinterface

De gebruikersinterface bestaat uit een startscherm, een eindscherm en een UI overlay. De UI overlay bestaat op zijn beurt uit drie elementen: De LevelDescription, RepositionButton en PlacementPopup. Hieronder staan de mockups:

Animaties zijn ook toegevoegd voor de UI op dezelfde manier als bij het GameBoard.

Reflectie

Ik heb erg genoten van werken met Unity. Vanaf 2014 heb ik gewerkt met GameMaker voor het maken van games. Ik heb altijd de stap naar Unity uitgesteld omdat ik dan een terugval zou maken in complexiteit van wat ik kan maken. Doordat ik met de module Building Playful Worlds ben begonnen aan Unity vind ik het nu heel fijn om mee te werken. Dit heeft voornamelijk te maken met het feit dat met Unity een stuk gestructureerder te werken is dan met GameMaker. Dingen als parenting, scripting, componenten en prefabs zijn een grote upgrade van wat in GameMaker is. Daarnaast biedt C# meer mogelijkheden dan GML.

In de toekomst ga ik voor het maken van prototypes zeker vaker Unity gebruiken. Voor mijn interesse in augmented reality is het heel belangrijk geweest om te leren werken met Unity. Nu ben ik in staat om mijn ideeën voor de technologie naast visualiseren ook interactief te maken.

Unity AR Puzzle Game progress

Ik heb de afgelopen week nog eens goed nagedacht over hoe ik het thema kan combineren met daadwerkelijke gameplay. Het idee met camera’s die je volgen heb ik in onderstaande afbeeldingen geëxploreerd:

De camera’s die je volgen hebben het volgende script:

public class FollowingCamera : MonoBehaviour
{
    public float maxSpeed;

    public Rigidbody rb;
    public Transform toFollow;

    void FixedUpdate()
    {
        MoveTowardsPosition(toFollow);

    }

    private void MoveTowardsPosition(Transform target)
    {
        Vector3 targetDirection = (target.position - transform.position).normalized;
        targetDirection = Vector3.Scale(targetDirection, new Vector3(1, 0, 1)); // Verticale rotatie telt niet mee

        rb.AddForce(targetDirection * maxSpeed);
    }
}

Het gameboard gebruikt de volgende code:

public class GameBoard : MonoBehaviour
{
    [System.NonSerialized]
    public Target[] targets;

    [System.NonSerialized]
    public bool completed;

    void Start()
    {
        targets = GetComponentsInChildren<Target>();
    }

    public void CheckProgress()
    {
        int amountReached = 0;

        foreach (Target target in targets)
        {
            if (target.reached == true)
            {
                amountReached += 1;
            }
        }

        if (amountReached == targets.Length)
        {
            if (completed == false)
            {
                completed = true;
                Debug.Log("Completed");
            }
        }
    }
}

De targets, waar de camera’s heen moeten gebruiken de volgende code:

public class Target : MonoBehaviour
{
    public GameObject followingCamera;
    private GameBoard gameBoard;

    [System.NonSerialized]
    public bool reached;

    void Start()
    {
        gameBoard = GetComponentInParent<GameBoard>();
    }

    void OnTriggerEnter(Collider other)
    {
        if (other.gameObject == followingCamera)
        {
            reached = true;
            gameBoard.CheckProgress();
        }
    }

    void OnTriggerExit(Collider other)
    {
        if (other.gameObject == followingCamera)
        {
            reached = false;
        }
    }
}

Ik heb dus voornamelijk op de hiërarchie gefocust. Dit zorgt ervoor dat ik makkelijk nieuwe levels kan maken.

Wat ik nog moet maken is een level laad systeem in het main script.

Vervolgens moet ik het implementeren in AR, maar dat werkt al, zoals hieronder te zien is:

Unity AR Game update 1

Ik heb een begin gemaakt aan mijn AR game. De puzzelcamera draait nu naar de speler toe en deze gaat dan ook omhoog en omlaag. Het volgende is dat hij stopt met draaien als hij het einde heeft bereikt.

Daarna moet ik nog de code schrijven die checkt voor elke puzzelcamera of de speler in het zichtveld staat.

Omdat de camera voor de puzzel vier rotatiestanden heeft en hij bij moet houden hoe vaak hij is omgedraaid heb ik bovenstaande code geschreven. Deze kan nog een stuk eleganter maar dat ga ik later doen.

Wat in de code gebeurt is:

1. Kijk in welk kwadrant de speler stond volgens het getal totalRotation

2. Kijk in welk kwadrant de speler nu staat

3. Als het vorige kwadrant anders is dan het huidige kwadrant, voeg dan een toe of haal een af van totalRotation

Unity Self Driving Car test

Samen met een vriend heb ik een begin gemaakt aan een evolutie-systeem. Hij heeft de code voor het DNA geschreven en ik heb het geïmplementeerd in Unity.

Ik heb hier zelf geleerd hoe je prefabs als objecten spawnt en die dan variabelen meegeeft. Ook weet ik nu hoe je raycasts moet uitvoeren. Dit vanwege de “voelsprieten” die de “auto’s” hebben.

Ook moeten de auto’s elkaar niet beïnvloeden tijdens de simulatie, dus ik weet nu ook hoe lagen werken en de functie ignoreCollision.

De voelsprieten code is als volgt:

private float GetDistanceToObject(Quaternion angle, float maxDistance)
    {
    Vector3 dir = angle * transform.forward;

    Ray ray = new Ray(transform.position, dir);
    Debug.DrawRay(transform.position, dir * 10);

    if (Physics.Raycast(ray, out RaycastHit hit, maxDistance))
    {
        return hit.distance;
    }
    else
    {
        return maxDistance;
    }
}

De movement werkt op de volgende manier:

void Update()
{
    List<float> distances = new List<float>
    {
        GetDistanceToObject(Quaternion.Euler(0, 45, 0), 10),
        GetDistanceToObject(Quaternion.Euler(0, -45, 0), 10)
    };
    int action = (int)DNA.GetAction(dna, distances);
    int vert = 0;
    switch (action)
    {
        case 1:
            vert = 1;
            break;
        case 2:
            vert = -1;
            break;
        case 3:
            rotationAngle += turnSpeed;
            vert = 0;
            break;
        case 4:
            rotationAngle -= turnSpeed;
            vert = 0;
            break;
    }

    transform.rotation = Quaternion.Euler(0, rotationAngle, 0);
    velocity = (transform.forward * vert) * maxSpeed;
}

private void FixedUpdate() 
{
    rigidBody.AddForce(velocity);
}
De auto prefab


ARFoundation first test

Als eerste stap naar het maken van een game in augmented reality heb ik onderstaande tutorial gevolgd:

De code die in het interaction object zit is als volgt:

Er wordt gekeken of er een plane gedetecteerd is waar de gebruiker naar kijkt en als dat zo is wordt de plaatsingsindicator daarnaartoe gezet.

Als de gebruiker klikt op het scherm wordt een nieuw object in de scene neergezet waar op dat moment de plaatsingsindicator is.

In de scene zit een AR Session object, een AR Session Origin object, de plaatsingsindicator en het interactie-object waar het bovenstaande script zich in bevindt.

Ook moeten bovenstaande packages geïnstalleerd zijn. Omdat de ARCore XR Plugin ook is geïnstalleerd zou ik net zo makkelijk voor Android kunnen exporteren.

Bij het exporteren naar iOS wordt door Unity het volgende Xcode project gemaakt, wat vervolgens uitgevoerd kan worden door een iPhone:

Het eindresultaat is hieronder te zien: