บทเรียนการเขียนเกมแนว Temple Run ด้วย Unity 3D ตอนที่ 4 การใส่ Game Logic เข้าไปในเกมของเราให้เกิดเงื่อนไขเวลาที่ ชน เหรียญได้คะแนน หรือชนศัตรูแล้วเสียแต้ม
ก่อนจะศึกษาบทเรียนนี้แนะนำให้ศึกษาบทเรียนก่อนหน้านี้
- Workshop เขียนเกมแนว Temple Run ด้วย Unity 3D ตอนที่ 1
- Workshop เขียนเกมแนว Temple Run ด้วย Unity 3D ตอนที่ 2
- Workshop เขียนเกมแนว Temple Run ด้วย Unity 3D ตอนที่ 3
ในบทเรียนนี้จะเป็นการ ใส่ Game Logic หรือ Game Play ลงไปในเกมของเรา ซึ่งถ้าเราได้มีการออกแบบ Game Worlds และ Storyboard ไว้แล้ว
Game Logic ที่คิดไว้กับ Workshop ตัวนี้จะเป็นดังนี้
เมื่อเริ่มเกม
- คะแนนจะเป็น 0 เวลาเริ่มต้นของเกมจะเริ่มที่ 10 วินาที และลดลงทีละ 1 วินาที
- หากเวลาเป็น 0 จะแสดงหน้าจอ Game Over พร้อมประกาศผลคะแนน และปุ่ม Restart เพื่อเล่นเกมใหม่
ตัวละคร
- คะแนนจะเพิ่มเมื่อเก็บเหรียญได้ ทีละ 50 แต้มโดยนำไปคูณกับจำนวนเวลาที่เหลือ
- เวลาในเกมจะมีผลกับตัวละครเมื่อเก็บเหรียญได้ เวลาจะเพิ่มขึ้น
- เมื่อชนตัวศัตรู เวลาจะลดลง ทำให้เวลาในเกมนับถอยหลังเหลือเวลาน้อยลง มีผลต่อคะแนน
หน้าจอของเกม
- มี GUI บอกเวลานับถอยหลัง
- มี GUI บอกจำนวน Score ของเรา
มาเริ่มกันดีกว่า
ในบทเรียนก่อนหน้านี้จะมี GameObject ที่เป็น Empty Object ที่เราสร้างไว้เพื่อกำหนดในบทความ
เราจะทำการเพิ่ม Component ส่วนของ Code C# มาอีกตัว ก่อนอื่นให้เราสร้างไฟล์ C# มาใหม่ชื่อว่า GameLogic.cs
ประกาศ Public ไว้
float timeRemaining = 10; float timeExtension = 3f; float timeDeduction = 2f; float totalTimeElapsed = 0; float score=0f; public bool isGameOver = false;
เวลาเริ่มต้นในเกมคือ 10 วินาที จะมีการเพิ่มและลดคือ 2f คะแนนเป็น Score เก็บไว้เริ่มต้นที่ 0 สร้างตัวแปร Boolean ว่า isGameOver สำหรับบอกสถานะว่า Game Over หรือยังปรับให้เป็น False เมื่อเริ่มต้น
เพิ่มคำสั่งใน ฟังก์ชัน Update() ทันทีตามนี้
if(isGameOver) return; totalTimeElapsed += Time.deltaTime; score = totalTimeElapsed*50; timeRemaining -= Time.deltaTime; if(timeRemaining <= 0){ isGameOver = true; }
ถ้า GameOver ให้ออกจากฟังก์ชัน คะแนน Score จะเพิ่มจากเวลาที่เหลือ จากลดหรือเพิ่ม คูณ 50 คะแนน ถ้า timeRemaining หรือเวลาในเกมเหลือ 0 ให้เปลี่ยนสถานะของ isGameOver เป็น True นั่นคือ Game Over ครับ
สร้างฟังก์ชันใหม่ขึ้นมาคือ OnGUI() เขียนคำสั่งเกี่ยวกับ GUI หน้าจอเมนูเมื่อเกม Game Over ลงไปดังนี้
void OnGUI() { if(!isGameOver) { GUI.Label(new Rect(10, 10, Screen.width/5, Screen.height/6), "TIME LEFT: "+((int)timeRemaining).ToString()); GUI.Label(new Rect(Screen.width-(Screen.width/6), 10, Screen.width/6, Screen.height/6), "SCORE: "+((int)score).ToString()); } else { Time.timeScale = 0; //Show Total Score GUI.Box(new Rect(Screen.width/4, Screen.height/4, Screen.width/2, Screen.height/2), "GAME OVER\nYOUR SCORE: "+(int)score); //Restart Game if (GUI.Button(new Rect(Screen.width/4+10, Screen.height/4+Screen.height/10+10, Screen.width/2-20, Screen.height/10), "RESTART")){ Application.LoadLevel(Application.loadedLevel); } } }
ถ้ายังไม่อยู่ในสถานะ Game Over ให้สร้าง Label ขึ้นมาอยู่มุมซ้าย และ ขวา บนของเกม เป็นการแสดงผลตัวแปร Score และ Time Remaining ของเรา
หากอยู่ในสถานะของ Game Over แล้วให้เข้าเงื่อนไขการสร้าง GUI ใหม่คือ
//Show Total Score GUI.Box(new Rect(Screen.width/4, Screen.height/4, Screen.width/2, Screen.height/2), "GAME OVER\nYOUR SCORE: "+(int)score); //Restart Game if (GUI.Button(new Rect(Screen.width/4+10, Screen.height/4+Screen.height/10+10, Screen.width/2-20, Screen.height/10), "RESTART")){ Application.LoadLevel(Application.loadedLevel); }
เพื่อบอก คะแนน Total Score ของเรา และแสดงปุ่ม Restart เกมเพื่อเริ่มเกมใหม่ ใช้คำสั่งโหลดเกมเข้า Scene เดิมตามนี้
Application.LoadLevel(Application.loadedLevel);
ไปที่ Hierarchy เลือก GameObject ที่เราสร้างไว้ชื่อ “GameController”
ทำการ Add Component ส่วนของ Script GameLogic.cs เข้าไป
แก้ไข GameLogic.cs อีกครั้งใน Mono Develop ให้เพิ่มฟังก์ชันชื่อ GetItems() และ CrashMonster() เข้าไปเพื่อทำเงื่อนไข เพิ่มเวลาเมื่อได้เหรียญ และ ลดเวลาเมื่อชนศัตรู
public void GetItems() { timeRemaining += timeExtension; } public void CrashMonster() { timeRemaining -= timeDeduction; }
ภาพรวมของไฟล์ GameLogic.cs จะเป็นดังนี้
using UnityEngine; using System.Collections; public class GameLogic : MonoBehaviour { float timeRemaining = 10; float timeExtension = 3f; float timeDeduction = 2f; float totalTimeElapsed = 0; float score=0f; public bool isGameOver = false; void Start () { Time.timeScale = 1; } void Update () { if(isGameOver) return; //move out of the function totalTimeElapsed += Time.deltaTime; score = totalTimeElapsed*50; timeRemaining -= Time.deltaTime; if(timeRemaining <= 0){ isGameOver = true; } } public void GetItems() { timeRemaining += timeExtension; } public void CrashMonster() { timeRemaining -= timeDeduction; } void OnGUI() { if(!isGameOver) { GUI.Label(new Rect(10, 10, Screen.width/5, Screen.height/6), "TIME LEFT: "+((int)timeRemaining).ToString()); GUI.Label(new Rect(Screen.width-(Screen.width/6), 10, Screen.width/6, Screen.height/6), "SCORE: "+((int)score).ToString()); } else { Time.timeScale = 0; //Show Total Score GUI.Box(new Rect(Screen.width/4, Screen.height/4, Screen.width/2, Screen.height/2), "GAME OVER\nYOUR SCORE: "+(int)score); //Restart Game if (GUI.Button(new Rect(Screen.width/4+10, Screen.height/4+Screen.height/10+10, Screen.width/2-20, Screen.height/10), "RESTART")){ Application.LoadLevel(Application.loadedLevel); } } } }
กลับมาที่ ตัวละครของเราให้คลิกที่ตัว player แก้ไขไฟล์ Player.cs อีกครั้ง ทำการ Include ไฟล์ GameLogic.cs ให้ใช้ตัวแปรร่วมใน Player.cs ได้
using UnityEngine; using System.Collections; public class Player : MonoBehaviour { public GameLogic control; //เพิ่มเข้าไป
แล้วนำฟังก์ชันจาก GameLogic.cs ไปใส่ในเงื่อนไข OnTrigger ใน Player ได้เลย
void OnTriggerEnter(Collider other) { if(other.gameObject.name == "Items(Clone)") { control.GetItems(); } else if(other.gameObject.name == "Monster(Clone)") { control.CrashMonster(); } Destroy(other.gameObject); //destroy the snag or powerup if colllected by the player }
ภาพรวมไฟล์ Player.cs จะเป็นดังนี้
using UnityEngine; using System.Collections; public class Player : MonoBehaviour { public GameLogic control; CharacterController controller; bool isGrounded= false; public float PlayerSpeed = 6.0f; public float PlayerJumpSpeed = 8.0f; public float gravity = 20.0f; private Vector3 PlayerMoveDirection = Vector3.zero; void Start () { controller = GetComponent<CharacterController>(); } void Update () { if (controller.isGrounded) { animation.Play("Walk"); PlayerMoveDirection = new Vector3(Input.GetAxis("Horizontal"), 0, 0); PlayerMoveDirection = transform.TransformDirection(PlayerMoveDirection); PlayerMoveDirection *= PlayerSpeed; if (Input.GetButton ("Jump")) { animation.Stop("run"); animation.Play("Attack"); PlayerMoveDirection.y = PlayerJumpSpeed; } if(controller.isGrounded) isGrounded = true; } PlayerMoveDirection.y -= gravity * Time.deltaTime; controller.Move(PlayerMoveDirection * Time.deltaTime); } void OnTriggerEnter(Collider other) { if(other.gameObject.name == "Items(Clone)") { //Items control.GetItems(); } else if(other.gameObject.name == "Monster(Clone)") { //Monster control.CrashMonster(); } Destroy(other.gameObject); } }
ไปที่ ส่วนของ Component ใน Inspector เราจะพบว่า Control ยังเป็นค่าว่าง
ให้ลาก GameController จาก Hierarchy ไปวางในส่วน Control ทันที
เมื่อเสร็จแล้วจะเป็นดังนี้
ทดสอบการ Run ตัว Game ของเราทันที
ส่วนของวีดีโอ ผลลัพธ์ เป็นดังนี้