Unity Cách tạo điều khiển cảm ứng trên thiết bị di động

Điều khiển là một trong những phần quan trọng nhất của trò chơi điện tử và không có gì ngạc nhiên khi nó cho phép người chơi tương tác với thế giới trò chơi.

Điều khiển trò chơi là các tín hiệu được gửi thông qua tương tác phần cứng (chuột/bàn phím, bộ điều khiển, màn hình cảm ứng, v.v.), sau đó được mã trò chơi xử lý, áp dụng một số hành động nhất định.

PCMáy chơi game có các nút vật lý có thể nhấn được, tuy nhiên, các thiết bị di động hiện đại chỉ có một vài nút vật lý, phần tương tác còn lại được thực hiện thông qua cử chỉ chạm, có nghĩa là các nút trò chơi cần phải được hiển thị trên màn hình. Đó là lý do tại sao khi tạo trò chơi dành cho thiết bị di động, điều quan trọng là phải tìm được sự cân bằng giữa việc có tất cả các nút trên màn hình trong khi vẫn giữ cho màn hình thân thiện với người dùng và không lộn xộn.

Điều khiển di động Unity

Trong hướng dẫn này, tôi sẽ trình bày cách tạo điều khiển di động (Cần điều khiển và Nút) đầy đủ tính năng trong Unity bằng UI Canvas.

Bước 1: Tạo tất cả các tập lệnh cần thiết

Hướng dẫn này có 2 tập lệnh, SC_ClickTracker.cs và SC_MobileControls.cs. Tập lệnh đầu tiên sẽ lắng nghe các sự kiện nhấp chuột và tập lệnh thứ hai sẽ đọc các giá trị được tạo từ các sự kiện.

SC_ClickTracker.cs

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

#if UNITY_EDITOR
using UnityEditor;
#endif

public class SC_ClickTracker : MonoBehaviour, IPointerDownHandler, IDragHandler, IPointerUpHandler
{
    public string buttonName = ""; //This should be an unique name of the button
    public bool isJoystick = false;
    public float movementLimit = 1; //How far the joystick can be moved (n x Joystick Width)
    public float movementThreshold = 0.1f; //Minimum distance (n x Joystick Width) that the Joystick need to be moved to trigger inputAxis (Must be less than movementLimit)

    //Reference variables
    RectTransform rt;
    Vector3 startPos;
    Vector2 clickPos;

    //Input variables
    Vector2 inputAxis = Vector2.zero;
    bool holding = false;
    bool clicked = false;

    void Start()
    {
        //Add this button to the list
        SC_MobileControls.instance.AddButton(this);

        rt = GetComponent<RectTransform>();
        startPos = rt.anchoredPosition3D;
    }

    //Do this when the mouse is clicked over the selectable object this script is attached to.
    public void OnPointerDown(PointerEventData eventData)
    {
        //Debug.Log(this.gameObject.name + " Was Clicked.");

        holding = true;

        if (!isJoystick)
        {
            clicked = true;
            StartCoroutine(StopClickEvent());
        }
        else
        {
            //Initialize Joystick movement
            clickPos = eventData.pressPosition;
        }
    }

    WaitForEndOfFrame waitForEndOfFrame = new WaitForEndOfFrame();

    //Wait for next update then release the click event
    IEnumerator StopClickEvent()
    {
        yield return waitForEndOfFrame;

        clicked = false;
    }

    //Joystick movement
    public void OnDrag(PointerEventData eventData)
    {
        //Debug.Log(this.gameObject.name + " The element is being dragged");

        if (isJoystick)
        {
            Vector3 movementVector = Vector3.ClampMagnitude((eventData.position - clickPos) / SC_MobileControls.instance.canvas.scaleFactor, (rt.sizeDelta.x * movementLimit) + (rt.sizeDelta.x * movementThreshold));
            Vector3 movePos = startPos + movementVector;
            rt.anchoredPosition = movePos;

            //Update inputAxis
            float inputX = 0;
            float inputY = 0;
            if (Mathf.Abs(movementVector.x) > rt.sizeDelta.x * movementThreshold)
            {
                inputX = (movementVector.x - (rt.sizeDelta.x * movementThreshold * (movementVector.x > 0 ? 1 : -1))) / (rt.sizeDelta.x * movementLimit);
            }
            if (Mathf.Abs(movementVector.y) > rt.sizeDelta.x * movementThreshold)
            {
                inputY = (movementVector.y - (rt.sizeDelta.x * movementThreshold * (movementVector.y > 0 ? 1 : -1))) / (rt.sizeDelta.x * movementLimit);
            }
            inputAxis = new Vector2(inputX, inputY);
        }
    }

    //Do this when the mouse click on this selectable UI object is released.
    public void OnPointerUp(PointerEventData eventData)
    {
        //Debug.Log(this.gameObject.name + " The mouse click was released");

        holding = false;

        if (isJoystick)
        {
            //Reset Joystick position
            rt.anchoredPosition = startPos;
            inputAxis = Vector2.zero;
        }
    }

    public Vector2 GetInputAxis()
    {
        return inputAxis;
    }

    public bool GetClickedStatus()
    {
        return clicked;
    }

    public bool GetHoldStatus()
    {
        return holding;
    }
}

#if UNITY_EDITOR
//Custom Editor
[CustomEditor(typeof(SC_ClickTracker))]
public class SC_ClickTracker_Editor : Editor
{
    public override void OnInspectorGUI()
    {
        SC_ClickTracker script = (SC_ClickTracker)target;

        script.buttonName = EditorGUILayout.TextField("Button Name", script.buttonName);
        script.isJoystick = EditorGUILayout.Toggle("Is Joystick", script.isJoystick);
        if (script.isJoystick)
        {
            script.movementLimit = EditorGUILayout.FloatField("Movement Limit", script.movementLimit);
            script.movementThreshold = EditorGUILayout.FloatField("Movement Threshold", script.movementThreshold);
        }
    }
}
#endif

SC_MobileControls.cs

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

public class SC_MobileControls : MonoBehaviour
{
    [HideInInspector]
    public Canvas canvas;
    List<SC_ClickTracker> buttons = new List<SC_ClickTracker>();

    public static SC_MobileControls instance;

    void Awake()
    {
        //Assign this script to static variable, so it can be accessed from other scripts. Make sure there is only one SC_MobileControls in the Scene.
        instance = this;

        canvas = GetComponent<Canvas>();
    }

    public int AddButton(SC_ClickTracker button)
    {
        buttons.Add(button);

        return buttons.Count - 1;
    }

    public Vector2 GetJoystick(string joystickName)
    {
        for(int i = 0; i < buttons.Count; i++)
        {
            if(buttons[i].buttonName == joystickName)
            {
                return buttons[i].GetInputAxis();
            }
        }

        Debug.LogError("Joystick with a name '" + joystickName + "' not found. Make sure SC_ClickTracker is assigned to the button and the name is matching.");

        return Vector2.zero;
    }

    public bool GetMobileButton(string buttonName)
    {
        for (int i = 0; i < buttons.Count; i++)
        {
            if (buttons[i].buttonName == buttonName)
            {
                return buttons[i].GetHoldStatus();
            }
        }

        Debug.LogError("Button with a name '" + buttonName + "' not found. Make sure SC_ClickTracker is assigned to the button and the name is matching.");

        return false;
    }

    public bool GetMobileButtonDown(string buttonName)
    {
        for (int i = 0; i < buttons.Count; i++)
        {
            if (buttons[i].buttonName == buttonName)
            {
                return buttons[i].GetClickedStatus();
            }
        }

        Debug.LogError("Button with a name '" + buttonName + "' not found. Make sure SC_ClickTracker is assigned to the button and the name is matching.");

        return false;
    }
}

Bước 2: Thiết lập điều khiển di động

  • Tạo Canvas mới (GameObject -> UI -> Canvas)
  • Thay đổi 'UI Scale Mode' trong Canvas Scaler thành 'Scale With Screen Size' và thay đổi Độ phân giải tham chiếu thành độ phân giải bạn đang làm việc (trong trường hợp của tôi là 1000 x 600)
  • Đính kèm tập lệnh SC_MobileControls vào Đối tượng Canvas
  • Nhấp chuột phải vào Đối tượng Canvas -> Giao diện người dùng -> Hình ảnh
  • Đổi tên Hình ảnh vừa tạo thành "JoystickLeft"
  • Thay đổi "JoystickLeft" Sprite thành một vòng tròn trống (đừng quên thay đổi Loại họa tiết thành 'Sprite (2D and UI)' sau khi nhập nó vào Unity)

  • Đặt các giá trị "JoystickLeft" Rect Transform giống như trong screenshot bên dưới:

  • Trong thành phần Hình ảnh, đặt Màu alpha thành 0,5 để làm cho hình ảnh trong suốt một chút:

  • Nhân đôi đối tượng "JoystickLeft" và đổi tên thành "JoystickLeftButton"
  • Di chuyển "JoystickLeftButton" bên trong đối tượng "JoystickLeft"
  • Thay đổi "JoystickLeftButton" Sprite thành một vòng tròn đầy:

  • Đặt các giá trị "JoystickLeftButton" Rect Transform giống như trong ảnh chụp màn hình bên dưới:

  • Thêm thành phần Nút vào "JoystickLeftButton"
  • Trong thành phần Nút thay đổi Chuyển sang 'None'
  • Đính kèm tập lệnh SC_ClickTracker vào "JoystickLeftButton"
  • Trong SC_ClickTracker, đặt Tên nút thành bất kỳ tên duy nhất nào (trong trường hợp của tôi, tôi đặt nó thành 'JoystickLeft') và bật hộp kiểm 'Is Joystick'.

Nút Joystick đã sẵn sàng. Bạn có thể có số lượng Cần điều khiển bất kỳ (Trong trường hợp của tôi, tôi sẽ có 2, một ở bên trái để điều khiển chuyển động và một ở bên phải để điều khiển chuyển động).

  • Sao chép "JoystickLeft" và đổi tên thành "JoystickRight"
  • Mở rộng "JoystickRight" và đổi tên "JoystickLeftButton" thành "JoystickRightButton"
  • Đặt các giá trị "JoystickRight" Rect Transform giống như trong ảnh chụp màn hình bên dưới:

  • Chọn đối tượng "JoystickRightButton" và trong SC_ClickTracker thay đổi Tên nút thành 'JoystickRight'

Cần điều khiển thứ hai đã sẵn sàng.

Bây giờ hãy tạo một nút thông thường:

  • Nhấp chuột phải vào Đối tượng Canvas -> Giao diện người dùng -> Nút
  • Đổi tên đối tượng nút thành "SprintButton"
  • Thay đổi "SprintButton" Sprite thành Vòng tròn với hiệu ứng vát:

  • Đặt các giá trị "SprintButton" Rect Transform giống như trong ảnh chụp màn hình bên dưới:

  • Thay đổi màu sắc hình ảnh "SprintButton" alpha thành 0,5
  • Đính kèm tập lệnh SC_ClickTracker vào đối tượng "SprintButton"
  • Trong SC_ClickTracker thay đổi Tên nút thành 'Sprinting'
  • Chọn Đối tượng văn bản bên trong "SprintButton" và thay đổi văn bản của nó thành 'Sprint', đồng thời thay đổi Kích thước phông chữ thành 'Bold'

Nút di động Unity

Nút đã sẵn sàng.

Chúng ta sẽ tạo một nút khác có tên "Jump":

  • Nhân đôi đối tượng "SprintButton" và đổi tên nó thành "JumpButton"
  • Thay đổi giá trị "JumpButton" Pos Y thành 250
  • Trong SC_ClickTracker thay đổi Tên nút thành 'Jumping'
  • Thay đổi văn bản bên trong "JumpButton" thành 'Jump'

Và nút cuối cùng là "Action":

  • Nhân đôi đối tượng "JumpButton" và đổi tên thành "ActionButton"
  • Thay đổi giá trị "ActionButton" Pos X thành -185
  • Trong SC_ClickTracker thay đổi Tên nút thành 'Action'
  • Thay đổi văn bản bên trong "ActionButton" thành 'Action'

Bước 3: Thực hiện kiểm soát di động

Nếu bạn đã làm theo các bước ở trên thì giờ đây bạn có thể sử dụng các hàm này để triển khai các điều khiển dành cho thiết bị di động trong tập lệnh của mình:

if(SC_MobileControls.instance.GetMobileButtonDown("BUTTON_NAME")){
	//Mobile button has been pressed one time, equivalent to if(Input.GetKeyDown(KeyCode...))
}

if(SC_MobileControls.instance.GetMobileButton("BUTTON_NAME")){
	//Mobile button is being held pressed, equivalent to if(Input.GetKey(KeyCode...))
}

//Get normalized direction of a on-screen Joystick
//Could be compared to: new Vector2(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical")) or new Vector2(Input.GetAxis("Mouse X"), Input.GetAxis("Mouse Y"))
Vector2 inputAxis = SC_MobileControls.instance.GetJoystick("JOYSTICK_NAME");

Ví dụ: tôi sẽ triển khai các điều khiển trên thiết bị di động bằng Bộ điều khiển FPS từ hướng dẫn này. Hãy làm theo hướng dẫn đó trước, nó khá đơn giản.

Nếu bạn làm theo hướng dẫn đó thì bây giờ bạn sẽ có Đối tượng "FPSPlayer" cùng với Canvas với các điều khiển trên thiết bị di động.

Chúng tôi sẽ duy trì các điều khiển trên Máy tính để bàn đồng thời triển khai các điều khiển trên thiết bị di động, làm cho nó trở nên đa nền tảng:

  • Mở tập lệnh SC_FPSController, cuộn đến dòng 28 và xóa phần này (xóa phần đó sẽ ngăn con trỏ bị khóa và sẽ cho phép nhấp vào điều khiển di động trong Trình chỉnh sửa.):
        // Lock cursor
        Cursor.lockState = CursorLockMode.Locked;
        Cursor.visible = false;
  • Cuộn đến dòng 39 và thay thế:
        bool isRunning = Input.GetKey(KeyCode.LeftShift);
        float curSpeedX = canMove ? (isRunning ? runningSpeed : walkingSpeed) * Input.GetAxis("Vertical") : 0;
        float curSpeedY = canMove ? (isRunning ? runningSpeed : walkingSpeed) * Input.GetAxis("Horizontal") : 0;
  • Với:
        bool isRunning = Input.GetKey(KeyCode.LeftShift) || SC_MobileControls.instance.GetMobileButton("Sprinting");
        float curSpeedX = canMove ? (isRunning ? runningSpeed : walkingSpeed) * (Input.GetAxis("Vertical") + SC_MobileControls.instance.GetJoystick("JoystickLeft").y) : 0;
        float curSpeedY = canMove ? (isRunning ? runningSpeed : walkingSpeed) * (Input.GetAxis("Horizontal") + SC_MobileControls.instance.GetJoystick("JoystickLeft").x) : 0;
  • Cuộn xuống dòng 45 và thay thế:
        if (Input.GetButton("Jump") && canMove && characterController.isGrounded)
  • Với:
        if ((Input.GetButton("Jump") || SC_MobileControls.instance.GetMobileButtonDown("Jumping")) && canMove && characterController.isGrounded)
  • Cuộn xuống dòng 68 và thay thế:
            rotationX += -Input.GetAxis("Mouse Y") * lookSpeed;
            rotationX = Mathf.Clamp(rotationX, -lookXLimit, lookXLimit);
            playerCamera.transform.localRotation = Quaternion.Euler(rotationX, 0, 0);
            transform.rotation *= Quaternion.Euler(0, Input.GetAxis("Mouse X") * lookSpeed, 0);
  • Với:
#if UNITY_IPHONE || UNITY_ANDROID || UNITY_EDITOR
            rotationX += -(SC_MobileControls.instance.GetJoystick("JoystickRight").y) * lookSpeed;
#else
            rotationX += -Input.GetAxis("Mouse Y") * lookSpeed;
#endif
            rotationX = Mathf.Clamp(rotationX, -lookXLimit, lookXLimit);
            playerCamera.transform.localRotation = Quaternion.Euler(rotationX, 0, 0);
#if UNITY_IPHONE || UNITY_ANDROID || UNITY_EDITOR
            transform.rotation *= Quaternion.Euler(0, SC_MobileControls.instance.GetJoystick("JoystickRight").x * lookSpeed, 0);
#else
            transform.rotation *= Quaternion.Euler(0, Input.GetAxis("Mouse X") * lookSpeed, 0);
#endif

Vì chuyển động của giao diện sẽ cản trở quá trình kiểm tra cần điều khiển trong Trình chỉnh sửa nên chúng tôi đang sử dụng #if cho biên dịch dành riêng cho nền tảng để tách logic di động khỏi phần còn lại của nền tảng.

Bộ điều khiển FPS di động hiện đã sẵn sàng, hãy kiểm tra nó:

Sharp Coder Trình phát video

Như bạn có thể thấy, tất cả Cần điều khiển và Nút đều hoạt động (ngoại trừ nút "Action", chưa được triển khai do không có tính năng phù hợp cho nó).

Nguồn
📁MobileControls.unitypackage272.33 KB
Bài viết được đề xuất
Cần điều khiển nhập cảm ứng di động trong Unity
Tạo chuyển động của người chơi trong Unity
Bộ điều khiển ô tô cho Unity
Bộ điều khiển nhân vật Cách thêm khả năng đẩy các vật cứng trong Unity
Bộ điều khiển máy bay cho Unity
Hướng dẫn sử dụng bộ điều khiển Worm 3D cho Unity
Bộ điều khiển trực thăng cho Unity