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