การสร้างสคริปต์ Unity Player Controller 3D พร้อมทิศทางที่ตัวละครกำลังหันหน้าอยู่

การทำให้การหมุนเป็นไปอย่างธรรมชาติยิ่งขึ้นตามทิศทางที่ผู้เล่นกำลังเคลื่อนที่ เราจะต้องปรับ logic ในการคำนวณ targetRotation และการเคลื่อนที่


สาเหตุที่ยังหัน 90 องศา

C#

// คำนวณทิศทางการเคลื่อนที่ตาม Input
Vector3 inputDirection = new Vector3(horizontalInput, 0f, verticalInput).normalized;

// ...

Quaternion targetRotation = Quaternion.LookRotation(inputDirection);
transform.rotation = Quaternion.RotateTowards(transform.rotation, targetRotation, rotationSpeed * Time.deltaTime);

inputDirection เป็นทิศทางใน World Space เช่น ถ้ากด ‘W’ (verticalInput = 1) inputDirection จะเป็น (0, 0, 1) เสมอ (ทิศ Z บวกของโลก) ไม่ว่าตัวละครจะหันไปทางไหน นั่นหมายความว่า targetRotation จะพยายามทำให้ตัวละครหันไปทาง Z บวกของโลกทันที ทำให้ดูเหมือนหัน 90 องศา หากทิศทางปัจจุบันไม่ตรงกัน


ปรับปรุงโค้ดให้การหมุนเป็นธรรมชาติและสัมพันธ์กับการเคลื่อนที่

เราจะปรับให้ตัวละครหมุนตาม ทิศทางการเคลื่อนที่จริง ที่ผู้เล่นต้องการ

C#

using UnityEngine;

public class PlayerController3D : MonoBehaviour
{
    public float moveSpeed = 5f;       // ความเร็วในการเดิน/วิ่ง
    public float jumpForce = 8f;       // แรงกระโดด
    public float rotationSpeed = 720f; // ความเร็วในการหมุน (องศาต่อวินาที)

    private Rigidbody rb;
    private bool isGrounded;           // สถานะว่าตัวละครอยู่บนพื้นหรือไม่

    void Start()
    {
        rb = GetComponent<Rigidbody>();
        rb.freezeRotation = true; // ตรึงการหมุนของ Rigidbody
    }

    void Update()
    {
        HandleMovementAndRotationInput(); // รวมการจัดการการเคลื่อนที่และการหมุนไว้ด้วยกัน
        HandleJumpInput();
    }

    void HandleMovementAndRotationInput()
    {
        float horizontalInput = Input.GetAxis("Horizontal");
        float verticalInput = Input.GetAxis("Vertical");

        // สร้างเวกเตอร์ทิศทางการเคลื่อนที่จาก Input (ใน Local Space ของ Input)
        // ถ้าใช้ Input.GetAxis("Horizontal") และ Input.GetAxis("Vertical")
        // ค่าจะสัมพันธ์กับแกน X, Z ของ World Space (หรือ Input Manager)
        Vector3 inputMovement = new Vector3(horizontalInput, 0f, verticalInput);

        // ตรวจสอบว่ามี Input การเคลื่อนที่จริงหรือไม่ (magnitude มากกว่าค่าเล็กน้อย)
        // เพื่อป้องกันการหมุนเมื่อผู้เล่นไม่ได้กดปุ่ม
        if (inputMovement.magnitude > 0.1f) // 0.1f เป็นค่าเผื่อสำหรับจอยสติ๊กหรือการกดค้าง
        {
            // ทำให้เวกเตอร์มีขนาด 1 เพื่อให้ทิศทางถูกต้อง
            inputMovement.Normalize();

            // คำนวณทิศทางเป้าหมายที่ตัวละครควรหันไป (ใน World Space)
            // Quaternion.LookRotation จะสร้างการหมุนให้แกน Z ของตัวละครหันไปทาง inputMovement
            Quaternion targetRotation = Quaternion.LookRotation(inputMovement);

            // ค่อยๆ หมุนตัวละครไปสู่ทิศทางที่ต้องการอย่างเป็นธรรมชาติ
            // ใช้ Quaternion.Slerp เพื่อให้การหมุนนุ่มนวลกว่า Quaternion.RotateTowards
            transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, rotationSpeed * Time.deltaTime);
        }

        // คำนวณความเร็วในการเคลื่อนที่ตามทิศทางที่ตัวละครกำลังหันหน้าอยู่ (transform.forward)
        // คูณด้วย inputMovement.magnitude เพื่อให้ความเร็วเป็น 0 เมื่อไม่มี input และเป็นค่าเต็มเมื่อมี input
        Vector3 currentMoveDirection = transform.forward * moveSpeed * inputMovement.magnitude;

        // กำหนดความเร็วของ Rigidbody โดยรักษาความเร็วในแกน Y เดิมไว้
        rb.velocity = new Vector3(currentMoveDirection.x, rb.velocity.y, currentMoveDirection.z);
    }

    void HandleJumpInput()
    {
        if (Input.GetButtonDown("Jump") && isGrounded)
        {
            rb.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);
            isGrounded = false;
        }
    }

    void OnCollisionEnter(Collision collision)
    {
        if (collision.gameObject.CompareTag("Ground"))
        {
            isGrounded = true;
        }
    }

    void OnCollisionExit(Collision collision)
    {
        if (collision.gameObject.CompareTag("Ground"))
        {
            isGrounded = false;
        }
    }
}

จุดที่ปรับปรุงและทำความเข้าใจ

  1. Vector3 inputMovement = new Vector3(horizontalInput, 0f, verticalInput);
    • เรายังคงสร้างเวกเตอร์จาก Input เหมือนเดิม แต่ตอนนี้เราจะเรียกมันว่า inputMovement เพื่อให้ชัดเจนว่าเป็น “ทิศทางที่ผู้เล่นต้องการจะไป”
  2. if (inputMovement.magnitude > 0.1f)
    • การตรวจสอบนี้สำคัญมาก เพื่อให้แน่ใจว่าเราจะหมุนตัวละครก็ต่อเมื่อ ผู้เล่นมีการกดปุ่มเคลื่อนที่จริง ๆ หากไม่มีการกดปุ่ม inputMovement.magnitude จะเป็น 0 และตัวละครจะไม่พยายามหมุนเมื่อหยุดนิ่ง
    • ค่า 0.1f เป็นค่าเผื่อเล็กน้อยเพื่อรองรับการทำงานของจอยสติ๊ก หรือการกดปุ่มค้างที่อาจมีค่า Error เล็กน้อย
  3. inputMovement.Normalize();
    • ทำให้เวกเตอร์ inputMovement มีขนาดเป็น 1 เพื่อให้เราได้ “ทิศทาง” อย่างเดียว ไม่ได้สนใจ “ความแรง” ของการกดปุ่ม (ซึ่งจะใช้ในการคูณกับ moveSpeed ทีหลัง)
  4. Quaternion targetRotation = Quaternion.LookRotation(inputMovement);
    • นี่คือหัวใจสำคัญ! เราสร้าง การหมุนเป้าหมาย (targetRotation) ที่จะทำให้ แกน Z ของตัวละคร (ซึ่งคือด้านหน้าของตัวละคร) หันไปทาง inputMovement ที่ผู้เล่นกำลังกดอยู่
  5. transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, rotationSpeed * Time.deltaTime);
    • เราใช้ Quaternion.Slerp (Spherical Linear Interpolation) เพื่อค่อย ๆ หมุนตัวละครจาก transform.rotation (การหมุนปัจจุบัน) ไปยัง targetRotation (การหมุนที่ต้องการ)
    • rotationSpeed * Time.deltaTime จะควบคุมว่าในแต่ละเฟรม ตัวละครจะหมุนไปได้มากน้อยแค่ไหน ยิ่ง rotationSpeed สูง ตัวละครก็จะหมุนเร็วขึ้น
    • Slerp จะให้การหมุนที่ นุ่มนวลและเป็นธรรมชาติ กว่า RotateTowards โดยเฉพาะเมื่อใกล้ถึงทิศทางเป้าหมาย มันจะค่อย ๆ ช้าลง
  6. Vector3 currentMoveDirection = transform.forward * moveSpeed * inputMovement.magnitude;
    • สำหรับการเคลื่อนที่ เราใช้ transform.forward ของตัวละคร (ซึ่งคือทิศทางที่ตัวละครกำลังหันหน้าไป หลังจากที่มันหมุนแล้ว)
    • เราคูณด้วย inputMovement.magnitude อีกครั้ง เพื่อให้:
      • ถ้า inputMovement.magnitude เป็น 0 (ไม่มีการกดปุ่ม) ตัวละครก็จะหยุดเคลื่อนที่
      • ถ้า inputMovement.magnitude เป็น 1 (กดปุ่มสุด) ตัวละครก็จะเคลื่อนที่ด้วย moveSpeed เต็มที่
    • นี่จะทำให้ตัวละคร เดินไปในทิศทางที่กำลังหันหน้าอยู่ และเปลี่ยนทิศทางเมื่อตัวละครหมุน
  7. rb.velocity = new Vector3(currentMoveDirection.x, rb.velocity.y, currentMoveDirection.z);
    • กำหนดความเร็วให้กับ Rigidbody โดยยังคงรักษาความเร็วในแกน Y เดิมไว้ เพื่อให้การกระโดดและแรงโน้มถ่วงยังทำงานได้ปกติ

วิธีทดสอบและปรับจูน

  1. นำโค้ดไปใช้: คัดลอกโค้ดที่แก้ไขแล้วไปวางในสคริปต์ PlayerController3D ของคุณ
  2. ปรับ rotationSpeed: ใน Inspector ของ Unity ให้ลองปรับค่า Rotation Speed บนสคริปต์ของตัวละคร ยิ่งค่าน้อย ตัวละครก็จะหมุนช้าลงและดูเป็นธรรมชาติมากขึ้น แต่ถ้าช้าเกินไปอาจรู้สึกหน่วง
  3. ทดลองเคลื่อนที่: กดปุ่ม W, A, S, D ค้างไว้สลับกัน คุณจะเห็นว่าตัวละครค่อยๆ หันไปในทิศทางที่คุณกดอย่างราบรื่น แทนที่จะหัน 90 องศาทันที

เราใช้คุกกี้เพื่อพัฒนาประสิทธิภาพ และประสบการณ์ที่ดีในการใช้เว็บไซต์ของคุณ คุณสามารถศึกษารายละเอียดได้ที่ นโยบายความเป็นส่วนตัว และสามารถจัดการความเป็นส่วนตัวเองได้ของคุณได้เองโดยคลิกที่ ตั้งค่า

ตั้งค่าความเป็นส่วนตัว

คุณสามารถเลือกการตั้งค่าคุกกี้โดยเปิด/ปิด คุกกี้ในแต่ละประเภทได้ตามความต้องการ ยกเว้น คุกกี้ที่จำเป็น

ยอมรับทั้งหมด
จัดการความเป็นส่วนตัว
  • เปิดใช้งานตลอด

บันทึกการตั้งค่า