สวัสดีครับ ในบทความนี้ผมจะพามาแนะนำให้รู้จักกับ "Raycast" มันคือการที่เรายิงเส้นตรงออกมาจากกล้องของ Player เพื่อให้ไปตกกระทบ Objects ต่าง ๆ ใน Scene ตัวอย่างการใช้ Raycast ที่เกมต่างๆ ใช้ได้แก่
เกมแนวยิง (Shooter) ในการยิงปืนในเกมนั้น ส่วนใหญ่แล้วจะเป็นการยิงเส้นตรงออกจาก Player ไปกระทบกับ Objects ต่าง ๆ หาก Raycast กระทบกับศัตรูภายในฉาก ก็แสดงว่า Player ยิงโดนศัตรูตัวนั้น นั่นเอง
เกมที่สามารถ Interact กับ Objects ภายในฉากได้ การที่เราจะรู้ว่า เป้า (Cursor) ของผู้เล่น เล็งไปที่ Object ไหนอยู่ ก็ต้องยิง Raycast ออกมาจาก Player เพื่อตรวจสอบการตกกระทบ
หรือแม้แต่การที่นำเมาส์ไปคลิกที่จุดต่าง ๆ ภายในเกม เพื่อ Interact กับสิ่งต่าง ๆ ก็ใช้ Raycast เช่นกัน
ในการสร้าง Raycast ก็ต้องยิงออกมาจากตำแหน่งของกล้อง เพราะ ฉนั้น ผมจะเพิ่มไฟล์ที่ชื่อ RaycastSystem.cs เข้าไปในกล้องของผู้เล่น ซึ่งผมต้องการจะยิง Raycast ออกไป ตรวจสอบว่าไปกระทบกับ Object ที่มี Tag เป็น "Item" หรือไม่ ถ้าใช่ให้แสดงใน Console ว่า "Item Object." หรือถ้าไม่ ให้แสดงว่า "Not a Item Object."
เนื่องจากเราอยากให้มันยิง Raycast ออกไปตลอดในทุก ๆ เฟรม ก็เลยต้องเขียนลงไปใน Update()
โดนผมจะสร้าง Object ที่เป็น Class "Ray" ขึ้นมา โดย Parameter แรกผมจะใส่ transform.position ซึ่งเป็นข้อมูลตำแหน่งแบบ Vector3 (x,y,z) ของ Object ปัจจุบันที่ Script ทำงานอยู่ และ Parameter ที่สองคือ transform.forward เป็นการกำหนดทิศทางของ Raycast โดยกำหนดไปที่ตำแหน่งปัจจุบัน ในทิศทาง Forward (ข้างหน้า)
เวลาที่เราจะอ้างอิงไปหา GameObject ที่ Raycast ไปตกกระทบเราจะเรียกผ่าน hit.transform.gameObject โดยที่ Object นั้น ต้องมี Collision ไม่ว่าจะเป็น Box Collision หรือ Mesh Collision ก็ได้ Raycast จึงจะสามารถตกกระทบได้
using UnityEngine;
using System.Collections;
public class RaycastSystem : MonoBehaviour {
void Start () {
}
void Update () {
Ray ray = new Ray(transform.position, transform.forward);
if(Physics.Raycast(ray, out RaycastHit hit))
{
switch(hit.transform.gameObject.tag)
{
case "Item":
Debug.Log("Item Object.");
break;
default:
Debug.Log("Not a Item Object.");
break;
}
}
}
}
จากโค้ดข้างบน การทำงานของ Raycast ก็คือจะยิงเส้นออกไปแบบไม่จำกัดระยะทาง ไม่ว่าจะอยู่ใกล้หรือไกล หากเล็งให้ cursor ไปตรงกับ Item ก็จะแสดง "Item Object." วิธีแก้คือ เราจะกำหนดเงื่อนไขตามระยะทาง โดยคำนวนจากฟังก์ชั่น Vector3.Distance ซึ่งเราจะใส่ hit.transform.position ซึ่งคือตำแหน่งที่ Raycast ไปตกกระทบ ลงใน Paremeter แรก และใส่ transform.position ซึ่งเป็นตำแหน่งปัจจุบันของ Script ผลลัพท์จะเก็บไว้ในตัวแปร distance จากนั้นเราจะใช้ if กำหนดเงื่อนไขว่า หากระยะทางน้อยกว่า 5 unit จึงจะให้ switch/case ทำงาน
using UnityEngine;
using System.Collections;
public class RaycastSystem : MonoBehaviour {
void Start () {
}
void Update () {
Ray ray = new Ray(transform.position, transform.forward);
if(Physics.Raycast(ray, out RaycastHit hit))
{
var distance = Vector3.Distance(hit.transform.position, transform.position);
if(distance < 5) {
switch(hit.transform.gameObject.tag)
{
case "Item":
Debug.Log("Item Object.");
break;
default:
Debug.Log("Not a Item Object.");
break;
}
}
}
}
}
