Featured image of post Creare un gioco in Realtà Aumentata con Unity e ARCore

Creare un gioco in Realtà Aumentata con Unity e ARCore

Step-by-step su come costruire un piccolo gioco in Realtà Auementata con Unity, AR Foundation e ARCore

In questo articolo, creo un semplice gioco usando AR Foundation. L’obiettivo del gioco è quello di sfuggire per più tempo possibile agli zombie utilizzando un player virtuale che si muove utilizzando il movimento dello smartphone e un target visivo di riferimento.


Introduzione

ARCore è il framework di Google per la creazione di esperienze di realtà aumentata su smartphone. Con Unity è possibile utilizzare AR Foundation per creare applicazioni AR multipiattaforma.

In questo articolo, costruisco un semplice gioco usando AR Foundation. L’obiettivo del gioco è quello di sfuggire per più tempo possibile agli zombie utilizzando un player virtuale che si sposta utilizzando il movimento dello smartphone e un target visivo di riferimento.

L’applcazione però non sarà solo virtuale poiché ci sarà la coesistenza di ambiente virutale con ambiente reale quindi la luce del gioco sarà fortemente influenzata dalla luce ambientale.

Crea il Gioco in Realtà Aumentata

Game Design

Per questo gioco utilizzo due scene:

  • GameAR Scene
  • Menu Scene

Nella Menu Scene ci sarà il main menu della scena con tutte le possibili opzioni mentre nel GameAR Scene sarà presente l’intero gioco in AR.

Per il menu sono necessarie due schermate principali dove l’utente potrà navigare. Nella schermata principale del menu potrà:

  1. Avviare il gioco
  2. Accedere al menu opzioni/info
  3. Uscire dal gioco

Dalla schermata opzioni/info è possibile aggiungere delle opzioni o come nell’immagine sottostante dare solo delle informazioni all’utente e avere un tasto di back per tornare alla schermata principale del menu.

Schermata principale del menuSchermata Info del menu

La scena AR del gioco si occupa di:

  • Rilevare i plane del mondo reale e creare un recinto virtuale su di essi;
  • Aggiungere un player che si muove mediante il movimento dello smartphone mediante un reticle pointer.
  • Spawnare dei nemici che si dirigono verso il player.

Crea il progetto e configurare l’ambiente

Alcuni di questi step sono presi dalla documentazione ufficiale AR Core di Google disponibile su https://codelabs.developers.google.com/arcore-unity-ar-foundation

  1. Crea un progetto Unity di tipo 3D.

Unity Hub, creazione progetto

  1. Installa i framework ARCore e AR Foundation dal package manger di Unity -> Click su Window > Package Manager.
  • ARCore XR Plugin
  • AR Foundation

Package Manager per ARCore e AR Foundation

  1. Installa lo starter package fornito da Google per l’AR
  1. Build Settings
  • Click su File > Build Settings;
  • Nella sezione Platform
  1. Modificare le impostazioni di progetto

AR Foundation deve essere configurato per inizializzare i sistemi XR all’avvio.

  • Open Edit > Project Settings e click sulla sezione XR Plug-in Management .
  • Nella scheda Android, abilitare ARCore con la spunta.

ARCore non funziona con l’API grafica Vulkan (se vuoi approfondire il perché ho scritto un articolo qui), per questa ragione è necessario rimuoverla e lasciare solo *Open GLES:

  • *Dal pannello Project Settings click su Player,
  • rimuovere la spunta da Auto Graphics API e
  • rimuovere con il - l’API Vulkan.

API Grafiche

Dal medesimo pannello, cerca Minimum API Level e selezione come Android 7.0 Nougat (API level 24), questo perché ARCore funzionanano a partire dal API level 24.

Minimum API Level 24

Configura la scena AR

A partire dalla scena base SampleScene:

  1. Cancella tutti gli elementi nella scena.

Elementi base della scena da cancellare

  1. Aggiungi gli oggetti relativi all’AR Foundation. Tasto destro del mouse sul pannello Hierarchy e aggiungi:
  • XR > AR Session: Questo game object gestisce il ciclo di vita dell’esperienza AR;
  • XR > AR Session Origin: Questo game object applica le trasformazioni XYZ da elementi tracciabili (come punti o piani) a oggetti ancorati nella scena Unity;
  • Light > Directional Light: Definisce la sorgente che illumina i vari Game Object.
  1. Click su AR Session Origin nella hierarchy, e seleziona l’AR Camera object. Nell’inspector, cambia il tag e seleziona MainCamera.

Elementi AR, MainCamera Tag

Plane Detection

Una volta preparata la scena base puoi procedere allo sviluppo del gioco a partire dal plane detaction.

ARPlaneManager

ARPlaneManager rileva gli ARPlane e crea, aggiorna e rimuove i GameObject quando l’ambiente circostante cambia in base al rilevamento dell’ARCamera.

  1. Dalla scheda Hierarchy > Create Empty per creare un Game Object Vuoto.
  2. Rinomina il GameObject appena creato come Driving Surface Manager. Questo componente visualizzerà i plane rilevati fino a quando non ne viene selezionato uno dal Player.
  3. Popoliamo il nuovo Game Object: dall’Inspector , click su Add Component e aggiungi il componente AR Plane Manager.

Driving Surface Manager

  1. A questo punto allo script AR Plane Manager sulla scheda Plane Prefab seleziona dalla Scene il GameObject Driving Surface Plane (Attenzione! Assicurati di selezionare il Driving Surface Plane della scena e non dall’Asset).

Questo prefab presente nel pacchetto iniziale fornisce una trama sabbiosa per il pavimento che verrà utilizzato creando un plane che si sovrappone alla scena reale.

  • Puoi cambiare anche la Detection Mode in Horizontal. Questo permette all’ARPlaneManager di rilevare solo Plane orizzontali, il che semplifica molto il rilevamento del plane per il gioco.

Driving Surface Manager con Plane

ARRaycastManager

Un ARRaycastManager espone la funzionalità raycast. Nel passaggio successivo, utilizzeremo questo oggetto per fornire i controlli per l’utente.

  1. Seleziona il GameObject Driving Surface Manager.
  2. Nell’Inspector, click Add Component e aggiungere il componente ARRaycastManager al Driving Surface Manager.

DrivingSurfaceManager

Il DrivingSurfaceManager è uno script di supporto presente nello Starter Package di ARCore che consente di selezionare un ARPlane. Una volta selezionato un piano AR, tutti gli altri piani saranno nascosti e la rilevazione di nuovi plane verrà disabilitata.

  1. Assicurarsi che l’oggetto denominato Driving Surface Manager sia selezionato nel riquadro Hierarchy.
  2. Nell’Inspector, fai clic su Add Component e aggiungi lo script DrivingSurfaceManager. Non è necessaria alcuna configurazione per questo script.
Modifica Dust Material

Uno degli elementi forniti nello Starter Pack è il Dust Material non viene visualizzato correttamente nelle ultime versioni di Unity.

Driving Surface Manager, Dust Material

  1. Da Unity apri la cartella Assets -> Starter Package qui click su Dust Material.
  2. Dall’Inspector click su Shader e seleziona Standard, assicurati che il Rendering Mode sia Transparent.

Dust Material

Test Build & Run

Click su File > Build And Run e prova che il rilevamento del plane avvenga correttamente, come nell’immagine sottostante.

Test Build - Plane Detection

Reticle & Game Elements

A questo punto, dopo aver programmato l’applicazione per rilevare i plane, dobbiamo gestire l’interattivià degli elementi all’interno del recinto virtuale rilevato e per far questo ci servirà un aiming reticle (ovvero una sorta di mirino). L’obiettivo è che il giocatore punti il ​​telefono su una superficie o plane rilevato. Il mirino è necessario a fornire un chiaro feedback visivo per la posizione designata.

Aggiungi il Reticle

  1. Nel pannello dove sono gli Assets, vai su Assets -> Starter Package
  2. Posiziona l’oggetto Reticle Prefab nella scena trascinandolo nella scheda Hierarchy e selezionalo.
  3. Nell’Inspector, *click su Add Component e aggiungi lo script ReticleBehaviour (Questo script contiene alcuni metodi per il controllo del mirino).
  4. Nello scipt Reticle Behaviour aggiungi il Driving Surface Manager che hai nella scena, e nel campo Chield il Reticle Prefab che hai aggiunto nella scena.

Ora nell’inspector del Reticle Prefab aggiungere il componente AR Raycast Manager

A questo punto va modificato lo script nello Starter Package ReticleBehaviour.cs come segue:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
using System.Collections;
using System.Collections.Generic;
using System.Linq;

using Unity.Collections;
using UnityEngine;
using UnityEngine.XR.ARFoundation;
using UnityEngine.XR.ARSubsystems;

[RequireComponent(typeof(ARRaycastManager))]
public class ReticleBehaviour : MonoBehaviour
{
    public GameObject Child;
    public DrivingSurfaceManager DrivingSurfaceManager;

    public ARPlane CurrentPlane;

    // Start is called before the first frame update
    private void Start()
    {
        Child = transform.GetChild(0).gameObject;
    }

    void Update()
    {
        // TODO: Conduct a ray cast to position this object.
        var screenCenter = Camera.main.ViewportToScreenPoint(new Vector3(0.5f, 0.5f));

        var hits = new List<ARRaycastHit>();
        DrivingSurfaceManager.RaycastManager.Raycast(screenCenter, hits, TrackableType.PlaneWithinBounds);

        CurrentPlane = null;
        ARRaycastHit? hit = null;
        if (hits.Count > 0)
        {
            // If you don't have a locked plane already...
            var lockedPlane = DrivingSurfaceManager.LockedPlane;
            hit = lockedPlane == null
                // ... use the first hit in `hits`.
                ? hits[0]
                // Otherwise use the locked plane, if it's there.
                : hits.SingleOrDefault(x => x.trackableId == lockedPlane.trackableId);
        }

        if (hit.HasValue)
        {
            CurrentPlane = DrivingSurfaceManager.PlaneManager.GetPlane(hit.Value.trackableId);
            // Move this reticle to the location of the hit.
            transform.position = hit.Value.pose.position;
        }
        Child.SetActive(CurrentPlane != null);
    }
}

Il risultato di questa attività dovrebbe essere come quello nell’immagine sottostante:

Test Build - Reticle

Player

Il giocatore controllerà un avatar virtuale che guiderà verso varie posizioni con il mirino. Nello starter pack ci sono un auto usato come player e gli script che già di serie permettono al player di seguire il mirino.

Come prima attività aggiungiamo l’auto presente nello Starter Package all’interno della Scena.

  1. Creiamo un Empty Game Object, click con il tasto destro nella scheda Hierarchy e seleziona Create Empty.
  2. Per comodità rinomina il Game Object appena creato come Car Spawner.
  3. Dall’inspector dell’oggetto Car Spawner, click su Add Component e seleziona lo script Car Manager (presente nello starter pack con il nome di CarManager.cs).
  4. Aggiungi allo script Car Manager i seguenti object presenti:
    • Dall’Asset seleziona il modello Car Prefab e inseriscilo nella label Car Prefab dell’inspector.
    • Dalla scena seleziona il Reticle Prefab e importalo nella label Reticle Prefab dell’inspector.
    • Dalla scena seleziona il Driving Surface Manager e importalo nella label Driving Surface Manager dell’inspector.

Il risultato dovrebbe essere come quello dell’immagine sottostante.

Car Spawner

Zombie

Per creare i nemici della nostra macchina, dobbiamo scaricare un package da Unity in modo da avere degli zombie che inseguiranno la nostra auto e dai quali dobbiamo fuggire.

Scarica e importa il seguente package dall’asset store: Car Spawner

  1. Creiamo un Empty Game Object, click con il tasto destro nella scheda Hierarchy e seleziona Create Empty.
  2. Per comodità rinomina il Game Object appena creato come Zombie Spawner.
  3. Dall’inspector dell’oggetto Zombie Spawner, click su Add Component e seleziona lo script Car Manager (presente nello starter pack con il nome di PackageSpawner.cs).
  4. Aggiungi allo script Zombie Spawner i seguenti object presenti:
    • Dalla scena seleziona il Driving Surface Manager e importalo nella label Driving Surface Manager dell’inspector.
    • Dall’Asset seleziona il modello Zombie 3 prefab e inseriscilo nella label Package Prefab dell’inspector.

Dato che il prefab dello zombie è in proporzione molto grande rispetto al piano e all’auto, dobbiamo ridurre il rapporto in scala. Va inoltre aggiunto uno script per la gestione del comportamento degli zombie.

  1. Dal Project Pane naviga su Asset->Zombie->Prefabs e click sul file Zombie3 (lo stesso precedentemente inserito nella label Package Prefab del Game Object Zombie Spawner) e modifica lo scale su 0.2 per ogni asse.
  2. Dall’Inspector del prefab Zombie3 click su Add Component e aggiungi un Box Collider, spunta la label Is Trigger.
  3. Dall’Inspector del prefab Zombie3 click su Add Component e aggiungi un Box Collider, spunta la label Is Trigger.

Zombie prefab modificato

Se tutto è stato fatto correttamente dovreste avere un risultato simile a quello nell’immagine sottostante.

Interfaccia

Per rendere il gioco più realistico, creiamo una piccola interfaccia dove sono presenti le vite del Player e la predisposizione per fermare il gioco e tornare al menù principale.

  1. Dal menù Hierarchy, click con il tasto destro del mouse e aggiungiamo l’elemento UI->Canvas.
  2. Poi crea 3 elementi UI->Image dove posizionare un’immagine a forma di cuore (puoi trovare in rete diversi png con il cuore).
  3. Crea un button con UI-> Button - TextMeshPro per ritornare al menù principale.
  4. Creaiamo un immagine per simulare il rosso quando il player è colpito da uno zombie, UI -> Image
  5. Creiamo un sotto menù per il Game Over, dotato di un UI->Image per il backgroud e un UI->Button - TextMeshPro.

Interfaccia per la Scena

Esempio di uno dei cuori dell’interfacciaButton per tornare al menù

Game Over Image + Button Menù

Scena Finale

Custom Script & Game Logic

Zombie Script

L’obiettivo del gioco è quello di far abbassare la vita del player ogni volta che lo Zombie colpisce l’auto, per far questo dobbiamo fare in modo che lo zombie si avvicini al player; per far questo crea un semplice script ZombieARControl.cs

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ZombieARControl : MonoBehaviour
{
    private Transform target;
    public float speed;

    void FixedUpdate()
    {
        target = GameObject.FindGameObjectWithTag("Player").transform;
        Vector3 a = transform.position;
        Vector3 b = target.position;
        transform.position = Vector3.MoveTowards(a, b, speed);
        transform.LookAt(target);
        Debug.Log("zombie Rotation: " + transform.rotation + " zombie Positon: " + transform.position);
    }
}

Dopo aver creato lo script, seleziona nuovamente dal Project Pane il file Zombie3 (vai su Asset->Zombie->Prefabs e click sul file Zombie3), dall’inspector click su Add Componenent e aggiugi lo script Zombie AR Control creato in precedenza. Sulla label speed seleziona 0.0018, questo permetterà allo zombie di muoversi ad una ragionevole velocità.

Player Controller Script

Crea lo script **Player Controller che si occuperà della logica di gioco e dell’interfaccia grafica.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;


public class PlayerControl : MonoBehaviour
{
    public GameObject[] hearts;
    public GameObject gameOver;
    public static int life = 3;
    private bool dead;
    public static GameObject hitPlayer;

    private void Start()
    {
        hitPlayer= GameObject.Find("/Canvas/HitImage");
    }

    public void Restart()
    {
        gameOver.SetActive(false);
    }

    public static void ReduceLife()
    {
        life -= 1;
        var color = hitPlayer.GetComponent<Image>().color;
        color.a = 0.8f;
        hitPlayer.GetComponent<Image>().color = color;
    }

    void FixedUpdate()
    {
        if (life < 1)
        {
            Destroy(hearts[0].gameObject);
            //Player Dead, Load Menu Scene With loose message
            Debug.Log("Game: You Lost!");
            gameOver.SetActive(true);
        }
        else if (life < 2)
        {
            Destroy(hearts[1].gameObject);
        }
        else if (life < 3)
        {
            Destroy(hearts[2].gameObject);
        }

        if(hitPlayer != null)
        {
            if(hitPlayer.GetComponent<Image>().color.a > 0)
            {
                var color = hitPlayer.GetComponent<Image>().color;
                color.a -= 0.01f;
                hitPlayer.GetComponent<Image>().color = color;
            }
        }
    }

    public void GameOver()
    {
        Debug.Log("Play");
        life = 3;
        Destroy(gameOver);
        SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex - 1);
    }
}

Questo script va aggiunto al GameObject Car Spawner. Per gli elementi li riempiremo nella prossima parte in cui costruisco l’interfaccia.

Creaiamo la scena per il menu

In questa ultima parte è necessario creare un menù di gioco semplice sotto forma di scena che parta appena si avvia l’applicazione.

Scena Menu

Come dall’immagine sopra va creata un immagine di background che fa da sfondo al menu BackgroundMenu, poi va creato il menù vero e proprio che chiameremo MainMenu. All’interno ci saranno 3 button, uno per il play del gioco, uno per il sottomenù info ed uno per uscire dal gioco, i tasti 1 e 3 verranno gestiti tramite uno script apposito.

Scena Menu

Lo script MainMenu.cs gestisce i collegamenti 1 e 3 del MainMenu all’interno della scena menu.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;

public class MainMenu : MonoBehaviour
{

    public static GameObject gameOver;
    public void PlayGame()
    {
        Debug.Log("Play");
        SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex + 1);
    }

    public void QuitGame()
    {
        Debug.Log("Quit");
        Application.Quit();
    }
}

A questo punto inserisci lo script nell’UI GameObject MainMenu e collega i bottoni con i metodi dello script sopra. PlayGame al bottone PlayButton e il metodo QuitGame al bottone QuitButton.

Scena MenuScena Menu

Per il menù di info puoi creare un semplice sottomenù che fa sparire quello principale.

Info Button

Scena Menu

Build e Demo

A questo punto puoi fare il build del gioco inserendo le scene nel seguente ordine:

  1. Scena Menu
  2. Scena GameAR

Build

Se tutto è stato correttamente configurato potrai giocare al mini game.

Youtube Video Guida

Conclusioni

Il progetto originale, fornito dalla documentazione di Google, è disponibile a questo link, permetteva di catturare i pacchetti attraverso l’auto e tutto questo è inserito nello starter pack fornito da Google.

La documentazione fornita da Google però presenta delle limitazioni e alla data del 11/03/2023 non è aggiornata alle ultime versioni di ARCore e di Unity e per questo richiede alcune modifiche che sono presenti in questo articolo.

Fonti

Grazie ☺

Condividi:
Views
Create with Hugo, Theme Stack;
Sito creato e gestito da Francesco Garofalo;