Tutoriel Endless Runner pour Unity

Dans les jeux vidéo, quelle que soit la taille du monde, il a toujours une fin. Mais certains jeux tentent d'imiter le monde infini, ces jeux relèvent de la catégorie appelée Endless Runner.

Endless Runner est un type de jeu dans lequel le joueur avance constamment tout en collectant des points et en évitant les obstacles. L'objectif principal est d'atteindre la fin du niveau sans tomber ni entrer en collision avec les obstacles, mais souvent, le niveau se répète à l'infini, augmentant progressivement la difficulté, jusqu'à ce que le joueur entre en collision avec l'obstacle.

Gameplay des surfeurs de métro

Étant donné que même les ordinateurs et appareils de jeu modernes ont une puissance de traitement limitée, il est impossible de créer un monde véritablement infini.

Alors, comment certains jeux créent-ils l’illusion d’un monde infini ? La réponse est de réutiliser les blocs de construction (c'est-à-dire le regroupement d'objets), en d'autres termes, dès que le bloc passe derrière ou en dehors de la vue de la caméra, il est déplacé vers l'avant.

Pour créer un jeu de coureurs sans fin en Unity, nous devrons créer une plate-forme avec des obstacles et un contrôleur de joueur.

Étape 1: Créer la plateforme

Nous commençons par créer une plateforme en mosaïque qui sera ensuite stockée dans le Prefab:

  • Créez un nouveau GameObject et appelez-le "TilePrefab"
  • Créer un nouveau cube (GameObject -> Objet 3D -> Cube)
  • Déplacez le cube à l'intérieur de l'objet "TilePrefab", changez sa position en (0, 0, 0) et mettez-le à l'échelle (8, 0,4, 20).

  • Vous pouvez éventuellement ajouter des Rails sur les côtés en créant des Cubes supplémentaires, comme ceci:

Pour les obstacles, j'aurai 3 variantes d'obstacles, mais vous pourrez en faire autant que nécessaire:

  • Créez 3 GameObjects à l'intérieur de l'objet "TilePrefab" et nommez-les "Obstacle1", "Obstacle2" et "Obstacle3"
  • Pour le premier obstacle, créez un nouveau Cube et déplacez-le à l'intérieur de l'objet "Obstacle1"
  • Adaptez le nouveau cube à environ la même largeur que la plate-forme et réduisez sa hauteur (le joueur devra sauter pour éviter cet obstacle)
  • Créez un nouveau matériau, nommez-le "RedMaterial" et changez sa couleur en Rouge, puis attribuez-le au Cube (c'est juste pour que l'obstacle se distingue de la plate-forme principale)

  • Pour le "Obstacle2", créez quelques cubes et placez-les en forme triangulaire, en laissant un espace ouvert en bas (le joueur devra s'accroupir pour éviter cet obstacle)

  • Et enfin, "Obstacle3" sera un double de "Obstacle1" et "Obstacle2", combinés ensemble

  • Sélectionnez maintenant tous les objets à l'intérieur des obstacles et changez leur balise en "Finish", cela sera nécessaire plus tard pour détecter la collision entre le joueur et l'obstacle.

Pour générer une plate-forme infinie, nous aurons besoin de quelques scripts qui géreront le pool d'objets et l'activation des obstacles:

  • Créez un nouveau script, appelez-le "SC_PlatformTile" et collez-y le code ci-dessous:

SC_PlatformTile.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SC_PlatformTile : MonoBehaviour
{
    public Transform startPoint;
    public Transform endPoint;
    public GameObject[] obstacles; //Objects that contains different obstacle types which will be randomly activated

    public void ActivateRandomObstacle()
    {
        DeactivateAllObstacles();

        System.Random random = new System.Random();
        int randomNumber = random.Next(0, obstacles.Length);
        obstacles[randomNumber].SetActive(true);
    }

    public void DeactivateAllObstacles()
    {
        for (int i = 0; i < obstacles.Length; i++)
        {
            obstacles[i].SetActive(false);
        }
    }
}
  • Créez un nouveau script, appelez-le "SC_GroundGenerator" et collez-y le code ci-dessous:

SC_GroundGenerator.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;

public class SC_GroundGenerator : MonoBehaviour
{
    public Camera mainCamera;
    public Transform startPoint; //Point from where ground tiles will start
    public SC_PlatformTile tilePrefab;
    public float movingSpeed = 12;
    public int tilesToPreSpawn = 15; //How many tiles should be pre-spawned
    public int tilesWithoutObstacles = 3; //How many tiles at the beginning should not have obstacles, good for warm-up

    List<SC_PlatformTile> spawnedTiles = new List<SC_PlatformTile>();
    int nextTileToActivate = -1;
    [HideInInspector]
    public bool gameOver = false;
    static bool gameStarted = false;
    float score = 0;

    public static SC_GroundGenerator instance;

    // Start is called before the first frame update
    void Start()
    {
        instance = this;

        Vector3 spawnPosition = startPoint.position;
        int tilesWithNoObstaclesTmp = tilesWithoutObstacles;
        for (int i = 0; i < tilesToPreSpawn; i++)
        {
            spawnPosition -= tilePrefab.startPoint.localPosition;
            SC_PlatformTile spawnedTile = Instantiate(tilePrefab, spawnPosition, Quaternion.identity) as SC_PlatformTile;
            if(tilesWithNoObstaclesTmp > 0)
            {
                spawnedTile.DeactivateAllObstacles();
                tilesWithNoObstaclesTmp--;
            }
            else
            {
                spawnedTile.ActivateRandomObstacle();
            }
            
            spawnPosition = spawnedTile.endPoint.position;
            spawnedTile.transform.SetParent(transform);
            spawnedTiles.Add(spawnedTile);
        }
    }

    // Update is called once per frame
    void Update()
    {
        // Move the object upward in world space x unit/second.
        //Increase speed the higher score we get
        if (!gameOver && gameStarted)
        {
            transform.Translate(-spawnedTiles[0].transform.forward * Time.deltaTime * (movingSpeed + (score/500)), Space.World);
            score += Time.deltaTime * movingSpeed;
        }

        if (mainCamera.WorldToViewportPoint(spawnedTiles[0].endPoint.position).z < 0)
        {
            //Move the tile to the front if it's behind the Camera
            SC_PlatformTile tileTmp = spawnedTiles[0];
            spawnedTiles.RemoveAt(0);
            tileTmp.transform.position = spawnedTiles[spawnedTiles.Count - 1].endPoint.position - tileTmp.startPoint.localPosition;
            tileTmp.ActivateRandomObstacle();
            spawnedTiles.Add(tileTmp);
        }

        if (gameOver || !gameStarted)
        {
            if (Input.GetKeyDown(KeyCode.Space))
            {
                if (gameOver)
                {
                    //Restart current scene
                    Scene scene = SceneManager.GetActiveScene();
                    SceneManager.LoadScene(scene.name);
                }
                else
                {
                    //Start the game
                    gameStarted = true;
                }
            }
        }
    }

    void OnGUI()
    {
        if (gameOver)
        {
            GUI.color = Color.red;
            GUI.Label(new Rect(Screen.width / 2 - 100, Screen.height / 2 - 100, 200, 200), "Game Over\nYour score is: " + ((int)score) + "\nPress 'Space' to restart");
        }
        else
        {
            if (!gameStarted)
            {
                GUI.color = Color.red;
                GUI.Label(new Rect(Screen.width / 2 - 100, Screen.height / 2 - 100, 200, 200), "Press 'Space' to start");
            }
        }


        GUI.color = Color.green;
        GUI.Label(new Rect(5, 5, 200, 25), "Score: " + ((int)score));
    }
}
  • Attachez le script SC_PlatformTile à l'objet "TilePrefab"
  • Attribuer les objets "Obstacle1", "Obstacle2" et "Obstacle3" au tableau Obstacles

Pour le point de départ et le point final, nous devons créer 2 GameObjects qui doivent être placés respectivement au début et à la fin de la plateforme:

  • Attribuer des variables de point de départ et de point de fin dans SC_PlatformTile

  • Enregistrez l'objet "TilePrefab" dans Prefab et supprimez-le de la scène
  • Créez un nouveau GameObject et appelez-le "_GroundGenerator"
  • Attachez le script SC_GroundGenerator à l'objet "_GroundGenerator"
  • Changez la position de la caméra principale en (10, 1, -9) et changez sa rotation en (0, -55, 0)
  • Créez un nouveau GameObject, appelez-le "StartPoint" et changez sa position en (0, -2, -15)
  • Sélectionnez l'objet "_GroundGenerator" et dans SC_GroundGenerator, attribuez les variables Main Camera, Start Point et Tile Prefab.

Appuyez maintenant sur Play et observez comment la plate-forme bouge. Dès que la tuile plate-forme sort de la vue de la caméra, elle est reculée jusqu'à la fin avec l'activation d'un obstacle aléatoire, créant l'illusion d'un niveau infini (Passer à 0:11).

La caméra doit être placée de la même manière que la vidéo, de sorte que les plates-formes se dirigent vers la caméra et derrière elle, sinon les plates-formes ne se répéteront pas.

Sharp Coder Lecteur vidéo

Étape 2: Créer le lecteur

L'instance du joueur sera une simple sphère utilisant un contrôleur avec la possibilité de sauter et de s'accroupir.

  • Créez une nouvelle sphère (GameObject -> Objet 3D -> Sphère) et supprimez son composant Sphere Collider
  • Attribuez-lui "RedMaterial" créé précédemment
  • Créez un nouveau GameObject et appelez-le "Player"
  • Déplacez la sphère à l'intérieur de l'objet "Player" et changez sa position en (0, 0, 0)
  • Créez un nouveau script, appelez-le "SC_IRPlayer" et collez-y le code ci-dessous:

SC_IRPlayer.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[RequireComponent(typeof(Rigidbody))]

public class SC_IRPlayer : MonoBehaviour
{
    public float gravity = 20.0f;
    public float jumpHeight = 2.5f;

    Rigidbody r;
    bool grounded = false;
    Vector3 defaultScale;
    bool crouch = false;

    // Start is called before the first frame update
    void Start()
    {
        r = GetComponent<Rigidbody>();
        r.constraints = RigidbodyConstraints.FreezePositionX | RigidbodyConstraints.FreezePositionZ;
        r.freezeRotation = true;
        r.useGravity = false;
        defaultScale = transform.localScale;
    }

    void Update()
    {
        // Jump
        if (Input.GetKeyDown(KeyCode.W) && grounded)
        {
            r.velocity = new Vector3(r.velocity.x, CalculateJumpVerticalSpeed(), r.velocity.z);
        }

        //Crouch
        crouch = Input.GetKey(KeyCode.S);
        if (crouch)
        {
            transform.localScale = Vector3.Lerp(transform.localScale, new Vector3(defaultScale.x, defaultScale.y * 0.4f, defaultScale.z), Time.deltaTime * 7);
        }
        else
        {
            transform.localScale = Vector3.Lerp(transform.localScale, defaultScale, Time.deltaTime * 7);
        }
    }

    // Update is called once per frame
    void FixedUpdate()
    {
        // We apply gravity manually for more tuning control
        r.AddForce(new Vector3(0, -gravity * r.mass, 0));

        grounded = false;
    }

    void OnCollisionStay()
    {
        grounded = true;
    }

    float CalculateJumpVerticalSpeed()
    {
        // From the jump height and gravity we deduce the upwards speed 
        // for the character to reach at the apex.
        return Mathf.Sqrt(2 * jumpHeight * gravity);
    }

    void OnCollisionEnter(Collision collision)
    {
        if(collision.gameObject.tag == "Finish")
        {
            //print("GameOver!");
            SC_GroundGenerator.instance.gameOver = true;
        }
    }
}
  • Attachez le script SC_IRPlayer à l'objet "Player" (vous remarquerez qu'il a ajouté un autre composant appelé Rigidbody)
  • Ajoutez le composant BoxCollider à l'objet "Player"

  • Placez l'objet "Player" légèrement au-dessus de l'objet "StartPoint", juste devant la caméra.

Appuyez sur Play et utilisez la touche W pour sauter et la touche S pour vous accroupir. L'objectif est d'éviter les obstacles rouges:

Sharp Coder Lecteur vidéo

Cochez ceci Horizon Bending Shader.

Source
📁EndlessRunner.unitypackage26.68 KB
Articles suggérés
Tutoriel pour le jeu de puzzle Match-3 dans Unity
Comment créer un jeu de serpent dans Unity
Créer un jeu de casse-briques 2D dans Unity
Créer un jeu de puzzle coulissant dans Unity
Comment créer un jeu inspiré de Flappy Bird dans Unity
Mini-jeu dans Unity | CUBEavoid
Zombies de la ferme | Making d'un jeu de plateforme 2D dans Unity