Tutorial Pengontrol Pemain Top-Down untuk Unity

Banyak orang yang familiar dengan genre game seperti FPS (First-Person Shooter) dan RTS (Real-Time Strategy), namun banyak juga game yang tidak termasuk dalam satu kategori tertentu saja. Salah satu permainan tersebut adalah Top-Down Shooter.

Top-Down Shooter adalah game yang pemainnya dikontrol dari sudut pandang atas.

Contoh penembak top-down adalah Hotline Miami, Hotline Miami 2, Original Grand Theft Auto, dll.

Korban Vampir 2

Untuk membuat Pengontrol Karakter Top-Down di Unity, ikuti langkah-langkah di bawah ini.

Langkah 1: Buat Skrip

Untuk tutorial ini, kita hanya membutuhkan satu skrip.

  • Buat skrip baru, beri nama SC_TopDownController, hapus semuanya, dan tempel kode di bawah ini ke dalamnya:

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

Langkah 2: Buat Shadernya

Tutorial ini juga memerlukan shader khusus, yang diperlukan untuk membuat target Aim melapisi seluruh Objek (selalu di atas).

  • Klik kanan pada tampilan Proyek -> Buat -> Shader -> Standart Surface Shader
  • Beri nama shadernya "Cursor"

  • Buka shader, hapus semua yang ada di dalamnya lalu paste kode di bawah ini:

Kursor.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
}

Langkah 3: Siapkan Pengontrol Karakter Top-Down

Mari kita siapkan Pengontrol Karakter Top-Down:

  • Buat GameObject baru dan beri nama "Player"
  • Buat Kubus baru dan skalakan (Dalam kasus saya skalanya adalah (1, 2, 1))
  • Buat Kubus kedua, skalakan menjadi lebih kecil, dan pindahkan ke area atas (Ini hanya untuk mengetahui arah mana yang dilihat pemain)
  • Pindahkan kedua Kubus di dalam Objek "Player" dan hapus komponen BoxCollidernya

Sekarang, sebelum melangkah lebih jauh, mari buat prefab target Aim:

  • Buat GameObject baru dan beri nama "AimTarget"
  • Buat Quad baru (GameObject -> 3D Object -> Quad) dan pindahkan ke dalam Objek "AimTarget"
  • Tetapkan Tekstur di bawah ini menjadi Quad dan ubah Material Shader menjadi 'Custom/FX/Cursor'

garis bidik titik merah

  • Simpan "AimTarget" ke Prefab dan hapus dari Scene

Kembali ke contoh Player:

  • Lampirkan skrip SC_TopDownController ke Objek "Player" (Anda akan melihat bahwa ia menambahkan beberapa komponen tambahan seperti Rigidbody dan CapsuleCollider)
  • Skala CapsuleCollider hingga cocok dengan model pemain (dalam kasus saya, Tinggi diatur ke 2 dan Pusat diatur ke (0, 1, 0)

  • Dan terakhir, tetapkan variabel "Player Camera" dan "Target Indicator Prefab" di SC_TopDownController

Instance Player sekarang sudah siap, mari kita uji:

Sharp Coder Pemutar video

Semuanya berfungsi seperti yang diharapkan.

Artikel yang Disarankan
Menciptakan Pergerakan Pemain dalam Kesatuan
Tutorial Lompat Dinding 3D dan 2D Pemain untuk Unity
Tutorial Senter untuk Unity
Pengontrol Pemain RTS dan MOBA untuk Unity
Pengontrol Karakter Cara Menambahkan Kemampuan untuk Mendorong Benda Kaku di Unity
Tutorial Pengontrol Worm 3D untuk Unity
Pengontrol Pemain Planetary berbasis benda kaku untuk Unity