Samouczek dotyczący kontrolera odtwarzacza z góry na dół dla Unity

Wiele osób zna takie gatunki gier, jak FPS (strzelanka pierwszoosobowa) i RTS (strategia czasu rzeczywistego), ale istnieje również wiele gier, które nie mieszczą się w jednej konkretnej kategorii. Jedną z takich gier jest Top-Down Shooter.

Top-Down Shooter to gra, w której graczem sterujemy z perspektywy widoku z góry.

Przykładami strzelanek z widokiem z góry są Hotline Miami, Hotline Miami 2, Original Grand Theft Auto itp.

Ocaleni z wampirów 2

Aby utworzyć kontroler postaci z góry na dół w Unity, wykonaj poniższe kroki.

Krok 1: Utwórz skrypty

W tym samouczku będziemy potrzebować tylko jednego skryptu.

  • Utwórz nowy skrypt, nadaj mu nazwę SC_TopDownController, usuń z niego wszystko i wklej do niego poniższy kod:

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

Krok 2: Utwórz moduł cieniujący

Ten samouczek wymaga również niestandardowego modułu cieniującego, który jest potrzebny, aby cel Celowania nałożył się na resztę obiektów (zawsze na wierzchu).

  • Kliknij prawym przyciskiem myszy widok projektu -> Utwórz -> Shader -> Standardowy moduł cieniujący powierzchni
  • Nazwij moduł cieniujący "Cursor"

  • Otwórz moduł cieniujący, usuń wszystko z niego, a następnie wklej poniższy kod:

Cieniowanie kursora

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
}

Krok 3: Skonfiguruj odgórny kontroler postaci

Skonfigurujmy kontroler postaci z góry na dół:

  • Utwórz nowy obiekt GameObject i wywołaj go "Player"
  • Utwórz nową kostkę i przeskaluj ją (w moim przypadku skala to (1, 2, 1))
  • Utwórz drugą kostkę, skaluj ją znacznie mniejszą i przesuń ją do wyższego obszaru (chodzi po prostu o to, w którą stronę patrzy gracz)
  • Przesuń obie kostki do obiektu "Player" i usuń ich komponenty BoxCollider

Teraz, zanim przejdziemy dalej, utwórzmy prefabrykat celu Aim:

  • Utwórz nowy obiekt GameObject i wywołaj go "AimTarget"
  • Utwórz nowy Quad (GameObject -> Obiekt 3D -> Quad) i przesuń go wewnątrz obiektu "AimTarget"
  • Przypisz poniższą teksturę do quada i zmień Material Shader na 'Custom/FX/Cursor'

celownik z czerwoną kropką

  • Zapisz "AimTarget" w Prefabrze i usuń go ze Sceny

Powrót do instancji Player:

  • Dołącz skrypt SC_TopDownController do obiektu "Player" (zauważysz, że dodał on kilka dodatkowych komponentów, takich jak Rigidbody i CapsuleCollider)
  • Skaluj CapsuleCollider, aż będzie pasował do modelu odtwarzacza (w moim przypadku wysokość jest ustawiona na 2, a środek jest ustawiony na (0, 1, 0)

  • Na koniec przypisz zmienne "Player Camera" i "Target Indicator Prefab" w SC_TopDownController

Instancja Player jest już gotowa, przetestujmy ją:

Sharp Coder Odtwarzacz wideo

Wszystko działa zgodnie z oczekiwaniami.

Sugerowane artykuły
Tworzenie ruchu gracza w jedności
Samouczek skakania po ścianie w 3D i 2D dla odtwarzacza Unity
Samouczek dotyczący kontrolera robaków 3D dla Unity
Poradnik dotyczący latarki dla Unity
Unity Jak tworzyć mobilne elementy sterujące dotykiem
Planetarny kontroler odtwarzacza oparty na Rigidbody dla Unity
Kontroler odtwarzacza RTS i MOBA dla Unity