Tutoriel de contrôleur de lecteur descendant pour Unity

De nombreuses personnes connaissent des genres de jeux tels que le FPS (First-Person Shooter) et le RTS (Real-Time Strategy), mais il existe également de nombreux jeux qui n'entrent pas dans une seule catégorie spécifique. L'un de ces jeux est Top-Down Shooter.

Top-Down Shooter est un jeu dans lequel le joueur est contrôlé depuis une perspective de dessus.

Des exemples de jeux de tir descendants sont Hotline Miami, Hotline Miami 2, Original Grand Theft Auto, etc.

Survivants des vampires 2

Pour créer un contrôleur de personnage descendant dans Unity, suivez les étapes ci-dessous.

Étape 1: Créer les scripts

Pour ce tutoriel, nous n’aurons besoin que d’un seul script.

  • Créez un nouveau script, nommez-le SC_TopDownController, supprimez tout et collez-y le code ci-dessous:

SC_TopDownController.cs

using UnityEngine;

[RequireComponent(typeof(Rigidbody))]
[RequireComponent(typeof(CapsuleCollider))]

public class SC_TopDownController : MonoBehaviour
{
    //Player Camera variables
    public enum CameraDirection { x, z }
    public CameraDirection cameraDirection = CameraDirection.x;
    public float cameraHeight = 20f;
    public float cameraDistance = 7f;
    public Camera playerCamera;
    public GameObject targetIndicatorPrefab;
    //Player Controller variables
    public float speed = 5.0f;
    public float gravity = 14.0f;
    public float maxVelocityChange = 10.0f;
    public bool canJump = true;
    public float jumpHeight = 2.0f;
    //Private variables
    bool grounded = false;
    Rigidbody r;
    GameObject targetObject;
    //Mouse cursor Camera offset effect
    Vector2 playerPosOnScreen;
    Vector2 cursorPosition;
    Vector2 offsetVector;
    //Plane that represents imaginary floor that will be used to calculate Aim target position
    Plane surfacePlane = new Plane();

    void Awake()
    {
        r = GetComponent<Rigidbody>();
        r.freezeRotation = true;
        r.useGravity = false;

        //Instantiate aim target prefab
        if (targetIndicatorPrefab)
        {
            targetObject = Instantiate(targetIndicatorPrefab, Vector3.zero, Quaternion.identity) as GameObject;
        }

        //Hide the cursor
        Cursor.visible = false;
    }

    void FixedUpdate()
    {
        //Setup camera offset
        Vector3 cameraOffset = Vector3.zero;
        if (cameraDirection == CameraDirection.x)
        {
            cameraOffset = new Vector3(cameraDistance, cameraHeight, 0);
        }
        else if (cameraDirection == CameraDirection.z)
        {
            cameraOffset = new Vector3(0, cameraHeight, cameraDistance);
        }

        if (grounded)
        {
            Vector3 targetVelocity = Vector3.zero;
            // Calculate how fast we should be moving
            if (cameraDirection == CameraDirection.x)
            {
                targetVelocity = new Vector3(Input.GetAxis("Vertical") * (cameraDistance >= 0 ? -1 : 1), 0, Input.GetAxis("Horizontal") * (cameraDistance >= 0 ? 1 : -1));
            }
            else if (cameraDirection == CameraDirection.z)
            {
                targetVelocity = new Vector3(Input.GetAxis("Horizontal") * (cameraDistance >= 0 ? -1 : 1), 0, Input.GetAxis("Vertical") * (cameraDistance >= 0 ? -1 : 1));
            }
            targetVelocity *= speed;

            // Apply a force that attempts to reach our target velocity
            Vector3 velocity = r.velocity;
            Vector3 velocityChange = (targetVelocity - velocity);
            velocityChange.x = Mathf.Clamp(velocityChange.x, -maxVelocityChange, maxVelocityChange);
            velocityChange.z = Mathf.Clamp(velocityChange.z, -maxVelocityChange, maxVelocityChange);
            velocityChange.y = 0;
            r.AddForce(velocityChange, ForceMode.VelocityChange);

            // Jump
            if (canJump && Input.GetButton("Jump"))
            {
                r.velocity = new Vector3(velocity.x, CalculateJumpVerticalSpeed(), velocity.z);
            }
        }

        // We apply gravity manually for more tuning control
        r.AddForce(new Vector3(0, -gravity * r.mass, 0));

        grounded = false;

        //Mouse cursor offset effect
        playerPosOnScreen = playerCamera.WorldToViewportPoint(transform.position);
        cursorPosition = playerCamera.ScreenToViewportPoint(Input.mousePosition);
        offsetVector = cursorPosition - playerPosOnScreen;

        //Camera follow
        playerCamera.transform.position = Vector3.Lerp(playerCamera.transform.position, transform.position + cameraOffset, Time.deltaTime * 7.4f);
        playerCamera.transform.LookAt(transform.position + new Vector3(-offsetVector.y * 2, 0, offsetVector.x * 2));

        //Aim target position and rotation
        targetObject.transform.position = GetAimTargetPos();
        targetObject.transform.LookAt(new Vector3(transform.position.x, targetObject.transform.position.y, transform.position.z));

        //Player rotation
        transform.LookAt(new Vector3(targetObject.transform.position.x, transform.position.y, targetObject.transform.position.z));
    }

    Vector3 GetAimTargetPos()
    {
        //Update surface plane
        surfacePlane.SetNormalAndPosition(Vector3.up, transform.position);

        //Create a ray from the Mouse click position
        Ray ray = playerCamera.ScreenPointToRay(Input.mousePosition);

        //Initialise the enter variable
        float enter = 0.0f;

        if (surfacePlane.Raycast(ray, out enter))
        {
            //Get the point that is clicked
            Vector3 hitPoint = ray.GetPoint(enter);

            //Move your cube GameObject to the point where you clicked
            return hitPoint;
        }

        //No raycast hit, hide the aim target by moving it far away
        return new Vector3(-5000, -5000, -5000);
    }

    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);
    }
}

Étape 2: Créer le shader

Ce didacticiel nécessite également un shader personnalisé, nécessaire pour que la cible de visée recouvre le reste des objets (toujours au-dessus).

  • Faites un clic droit sur la vue Projet -> Créer -> Shader -> Standart Surface Shader
  • Nommer le shader "Cursor"

  • Ouvrez le shader, supprimez tout ce qu'il contient puis collez le code ci-dessous:

Curseur.shader

Shader "Custom/FX/Cursor" {
	Properties {
		_MainTex ("Base", 2D) = "white" {}
	}
	
	CGINCLUDE

		#include "UnityCG.cginc"

		sampler2D _MainTex;
		
		half4 _MainTex_ST;
						
		struct v2f {
			half4 pos : SV_POSITION;
			half2 uv : TEXCOORD0;
		};

		v2f vert(appdata_full v) {
			v2f o;
			
			o.pos = UnityObjectToClipPos (v.vertex);	
			o.uv.xy = TRANSFORM_TEX(v.texcoord, _MainTex);
					
			return o; 
		}
		
		fixed4 frag( v2f i ) : COLOR {	
			return tex2D (_MainTex, i.uv.xy);
		}
	
	ENDCG
	
	SubShader {
		Tags { "RenderType" = "Transparent" "Queue" = "Transparent+100"}
		Cull Off
		Lighting Off
		ZWrite Off
		ZTest Always
		Fog { Mode Off }
		Blend SrcAlpha OneMinusSrcAlpha
		
	Pass {
	
		CGPROGRAM
		
		#pragma vertex vert
		#pragma fragment frag
		#pragma fragmentoption ARB_precision_hint_fastest 
		
		ENDCG
		 
		}
				
	} 
	FallBack Off
}

Étape 3: Configurer le contrôleur de personnage descendant

Configurons un contrôleur de personnage descendant:

  • Créez un nouveau GameObject et appelez-le "Player"
  • Créez un nouveau cube et mettez-le à l'échelle (dans mon cas, l'échelle est (1, 2, 1))
  • Créez un deuxième cube, redimensionnez-le beaucoup plus petit et déplacez-le vers une zone supérieure (il s'agit simplement de savoir dans quelle direction le joueur regarde)
  • Déplacez les deux cubes à l'intérieur de l'objet "Player" et supprimez leurs composants BoxCollider

Maintenant, avant d'aller plus loin, créons le préfabriqué Aim Target:

  • Créez un nouveau GameObject et appelez-le "AimTarget"
  • Créez un nouveau Quad (GameObject -> Objet 3D -> Quad) et déplacez-le à l'intérieur de l'objet "AimTarget"
  • Attribuez la texture ci-dessous à un quad et modifiez le Material Shader en 'Custom/FX/Cursor'

réticule point rouge

  • Enregistrez le "AimTarget" dans Prefab et supprimez-le de la scène

Retour à l'instance Player:

  • Attachez le script SC_TopDownController à l'objet "Player" (vous remarquerez qu'il a ajouté des composants supplémentaires tels que Rigidbody et CapsuleCollider)
  • Mettez à l'échelle le CapsuleCollider jusqu'à ce qu'il corresponde au modèle du joueur (dans mon cas, la hauteur est définie sur 2 et le centre est défini sur (0, 1, 0)

  • Et enfin, attribuez les variables "Player Camera" et "Target Indicator Prefab" dans SC_TopDownController

L'instance Player est maintenant prête, testons-la:

Sharp Coder Lecteur vidéo

Tout fonctionne comme prévu.

Articles suggérés
Créer le mouvement des joueurs dans Unity
Contrôleur de lecteur RTS et MOBA pour Unity
Tutoriel Player 3D et 2D Wall Jump pour Unity
Tutoriel sur le contrôleur de ver 3D pour Unity
Contrôleur de lecteur planétaire basé sur Rigidbody pour Unity
Ajout de la prise en charge du double saut à un contrôleur de personnage de plateforme 2D dans Unity
Contrôleur d'avion pour Unity