จากบทความที่แล้ว สร้างเกม Unity 3D แนว Space Shooting ตอนที่ 1 เราจะรู้วิธีสร้างเกมเบื้องต้นตั้งแต่ระบบ Spawn และ Enemy ไปจนถึงกระสุน รอบนี้เราจะสร้างเงื่อนไขของการทำเกมให้สมบูรณ์กัน
การสร้างเงื่อนไขของคะแนน และ คําสั่งสถานะ Boolean
จากบทเรียนก่อนหน้าจะได้เกมเกมยานอวกาศแบบง่าย หลังจากที่ระบบของยานอวกาศของเราสมบูรณ์ทั้ง ระบบกระสุน การเคลื่อนที่ของฉาก ระบบศัตรู และสุ่มตําแหน่งเกิดของศัตรู ระบบต่อไปที่จะทําให้เงื่อนไข ของเกมสมบูรณ์ที่สุดคือระบบ คะแนน และ การเปลี่ยนสถานะของเกมกรณีที่ตัวละครของเราชนศัตรู เริ่มต้นให้เราไปที่ Scene View หลังจากนั้นเปิดโหมดมุมมอง 2D
คลิกขวาที่ Hierarchy หลังจากนั้นให้ เลือก UI แล้วเลือก Legacy >Text ยังไม่ต้องเลือก TextMeshPro (ไว้จะสอนอีกที)
ระบบจะทําการสร้าง Canvas และ Text มาให้เราอัตโนมัติใน Hierarchy ให้เราดับเบิลคลิกที่ Canvas ใน Hierarchy หน้าจอ Scene View จะแสดง Ratio ของระบบ UI Canvas ให้กับเราว่าหน้าจอเราอยู่ตําแหน่ง ขอบเขตแค่ไหน
ให้เราทําการตั้งค่าใน Inspector Window ของ Canvas เล็กน้อยนั่นคือ ปรับ Canvas Scaler (Script) ตําแหน่ง UI Scale Mode จากเดิมคือ Constant Pixel Size ให้เลือกเป็น Scale with Screen Size เพื่อให้ มันทํางานแบบ Responsive
ใน Hierarchy จะมีText ที่เราต้องการใช้งานในการ แสดงผลว่าขณะนี้สถานะของเกมของเราอยู่ในสถานะ ไหน เช่นถ้าจบเกมก็ให้แสดงผลอักษรว่า “Game Over” แต่ถ้าเริ่มเกมใหม่ให้แสดงหน้าจอว่า Space Ship เป็นต้น ดัวนั้นให้เราปรับ Text ตัวนี้ให้เรียบร้อยคือปรับขนาดของมันเป็น 57 และวางในตําแหน่งตรงกลาง หน้าจอให้เรียบร้อย ปรับสีเป็นสีขาว
ให้เราเพิ่มปุ่ ม Button เข้าไปโดยการคลิกขวาที่ Hierarchy เลือก UI->Buttonวางตําแหน่งให้เหมาะสมโดย ใช้ เครื่องมือบน Toolbar ในการปรับขยาย และเคลื่อนย้ายมัน
เราสามารถแก้ไข Child ของปุ่ ม Button ได้โดยการคลิกที่ลูกศรสามเหลี่ยมขวาด้านหน้า Button เราจะพบ กับ Text เป็น Child ของมันซึ่งเราสามารถแก้ไขข้อความได้ เปลี่ยนเป็น Play Again! หรือ Click to Play ให้เราสร้าง Empty GameObject ขึ้นมาตั้งชื่อว่า GameSystem บน Hierarchy เพื่อเป็นส่วนของการ ควบคุมระบบการเล่นภายในเกม
Workflow การทํางาน
การทํางานคือเราจะซ่อน Player และ SpawnPoint ไว้ก่อนตอนเริ่มเกม เมื่อเรากดปุ่ ม Click to Play เราจึง จะเล่นเกมได้ ตัวยานอวกาศ Player และ SpawnPoint บน Hierarchy จะถูกเปิดใช้งานปรากฏขึ้น กระสุน ที่ยิงโดนศัตรูจะเพิ่มคะแนนให้กับเกม และเมื่อใดที่ยาน Player ของเราชนกับศัตรู เราจะเปลี่ยนสถานะของ เกมเป็น Game Over ซึ่งจะซ่อน Player และ SpawnoPoint แทน ดังนั้นเรามาออกแบบส่วนหน้าจอของ การคะแนนก่อน สร้าง UI->Text อีกตัวขึ้นมาใน Canvas ตั้งชื่อว่า TextScore ตกแต่งตําแหน่งในการ แสดงผล
ทําการเปลี่ยนชื่อ Canvas บน Hierarchy เป็น GameOverCanvas หลังจากนั้นกดปิด Activate บน Scene View ไว้บนส่วน Inspector โดยการเอาเครื่องหมายถูกออก
ทําการ DeActivate ตัว GameOverCanvas บน Inspector โดยการเอาเครื่องหมายถูกออก
หลังจากนั้นให้ทําการสร้าง Canvas ใหม่ขึ้นมาบน Hierarchy ทําตามขั้นตอนเดิมแต่แรกแต่ให้เป็นฉากเข้า เกมของเรา โดยอาจจะทํากราฟิก 2D มาใช้ในรูปแบบ Sprite ได้โดยการกด Windows->Package Manager แล้วเลือก Unity Registry หลังจากนั้นเลือก 2D Sprite ทําการติดตั้ง Install และ ดาวน์โหลด
ไปที่ Windows->Package Manager
เลือก Package Manager ให้เป็น หมวด Unity Registry
เลือก 2D Sprite หลังจากนั้นเลือก Install
เมื่อติดตั้งแล้วให้ทําการ ค้นหา Google Image หา Logo ของเกม Space Ship ที่เป็ นไฟล์ PNG (Transparent) หรือจะออกแบบเองได้ผ่านโปรแกรมเช่น Adobe Photoshop, Gimp หรือ PowerPoint เป็น ต้น
เมื่อได้ภาพที่ต้องการใช้งานแล้วให้นําเข้ารูปภาพใน Unity โดยการเลือกไฟล์รูปภาพแล้ววางลงบน Unity ส่วนของ Project Assets หลังจากนั้นคลิกที่รูปภาพแล้วทําการเลือกที่ Inspector ทําการเปลี่ยน Texture Type จาก “Default” เป็น “Sprite”
ทําการสร้าง Canvas ใหม่ขึ้นมา แล้วทําการปรับตั้งค่าส่วนของ Canvas Scaler ให้เป็ น Scale with
Screen Sizeเพื่อให้หน้าจอเกมเป็น Responsive หลังจากนั้นเปลี่ยนชื่อ Canvas เป็น StartGameCanvas
โดยมีส่วนประกอบต่อไปนี้:
- UI -> Image ใส่ Logo ไฟล์ Png เราเข้าไป
- ใส่ปุ่ม ที่ชื่อว่า “Start Game” เข้าไป
ตัวอย่างที่ต้องการตามภาพประกอบข้างล่าง
กลับไปที่ขั้นตอนที่เราสร้าง GameObject ที่ชื่อว่า GameControllerและมีTag Controller ให้คลิกที่ GameSystem ให้เราเพิ่ม Script เข้าไปตั้งชื่อ Class เดียวกันว่า “GameSystem” แล้วให้เราประกาศตัว แปรตามนี้:
public int score = 0;
public int hp = 5;
public bool isGameOver = false;
public bool isStartGame = false;
public GameObject GameTitleScene,GameOverScene;
//Call Player Class
Player player;
สร้าง GameObjet สําหรับเก็บ Canvas ของหน้าจอ TitleCanvas และ GameOverCanvas ขึ้นมาสําหรับ กําหนดการซ่อน หรือแสดง สร้างตัวแปร Boolean สําหรับเช็คค่า true หรือ false ตั้งชื่อว่า isGameOver และ isStartGame สําหรับบอกสถานะว่า Game Over หรือยังปรับให้เป็น False เมื่อเริ่มต้น ต่อมาให้เราเพิ่มคําสั่งใน ฟังก์ชัน Start() ทันทีตามนี้
void Start(){
if(player == null){
GameObject _player = GameObject.FindGameObjectWithTag("Player") as GameObject;
player = _player.GetComponent<Player>();
}
}
เป็นฟังก์ชันในการตรวจสอบว่าถ้ามีการเริ่มเกม จะมีการเรียกหาคลาสของ Player ที่เป็นไฟล์ C# อัตโนมัติ โดยหากมันเป็นค่าว่าง (null) ให้สร้าง GameObject ใหม่ขึ้นมาทันทีชื่อ _player หลังจากนั้นให้ไปค้นหาสิ่ง ที่อยู่บน Hierarchy ที่มี Tag ว่า “Player” (ซึ่งต้องตรวจสอบชื่อ Tag ให้ดีกว่าลืมหรือไม่) ให้เป็นรูปแบบ GameObject หลังจากนั้น ตัวแปร player ที่เราตั้งขึ้นมาเพื่อเก็บคลาส Player จะไปเรียกคุณสมบัติของ _player ที่เป็น GameObject ที่เราสร้างขึ้น โดยดึงคุณสมบัติส่วนของ component ที่ชื่อ Player นั้นคือชื่อ C# ไฟล์ หรือ Class ของ Player ที่ฝังเป็น Component ของ Player นั่นเอง
ไป Import ตัว Library ตัวหนึ่งที่จะใช้สําหรับให้เราทํางานร่วมกับ Text, Button ที่เป็นส่วนของ Component ที่เป็น UI ได้ที่ Header ดังนี้:
using UnityEngine.UI;
ทําการ Implement ส่วนของฟังก์ชันเมธอดของ Update() ดังนี้:
void Update()
{
if(isGameOver){
spawnPoint.SetActive(false);
GameOverScene.SetActive(true);
GameTitleScene.SetActive(false);
}
if(isStartGame){
spawnPoint.SetActive(true);
GameOverScene.SetActive(false);
GameTitleScene.SetActive(false);
}
}
อธิบาย: ถ้าเข้าเงื่อนไข Bool ตัวแปร isGameOver เป็นจริง (true) จะเกิดสถานะ GameOver ให้ออกจาก ฟังก์ชัน เงื่อนไขเริ่มต้น isGameOver จะเป็น false หรือเป็นเท็จ ดังนั้นจะเข้าเงื่อนไขแรกคือ Canvas ของ GameOverScene ที่จะรองรับ GameOverCanvas จะ SetActive เป็นเท็จหรือ false ก่อน และหน้าจอเริ่ม เกมจะ SetActive เป็น true แต่ถ้ามีการเกิดเหตุการณ์ isGameOver เป็นจริง จะเข้าเงื่อนไขบน คือ หน้าจอ GameOverCanvas จะปรากฏในเกมทันที
ข้อควรจํา: bool เป็นตัวแปร หรือ Datatype ที่รองรับแค่ จริง หรือ เท็จ true or false เท่านั้น
ย้าย Component ReSpawn ไปอยู่ใน GameObject ใหม่ตั้งชื่อว่า SpawnPoint ให้เรียบร้อย
เพิ่ม ตัวแปร spawnPoint เป็น GameObject เข้าไปในคลาสของ GameSystem
public GameObject spawnPoint;
เพิ่มการควบคุม โดยการปิดจุด SpawnPoint ก่อนเมื่อมีการเริ่มทํางานใน Start() เพื่อไม่ให้ปล่อยยานศัตรู ออกมาตอนเริ่มเกม
spawnPoint.SetActive(false);
ฟังก์ชัน Start() จะเป็นดังนี้:
void Start()
{
spawnPoint.SetActive(false);
if(player == null){
GameObject _player = GameObject.FindGameObjectWithTag("Player") as GameObject;
player = _player.GetComponent<Player>();
}
}
ทํา ก า รล า ก SpawnPoint ใน Hierarchy ไปวางใน spawnPoint บน GameSystem ที่ อ ยู่ ใ น GameController และเช่นกัน ลาก StartGameCanvasและ GameOverCanvas ไปวางใน GameSystem เช่นกัน
ทําการเขียนฟังก์ชันใหม่ใน คลาส GameSystem ชื่อว่า ClickStartGame():
public void ClickStartGame(){
isGameOver = false;
isStartGame = true;
}
กางส่วนของ StartGameCanvas ออกมาคลิกที่ Button บน Hierarchy แล้วไปที่ Inspector ดูส่วน OnClick() ของปุ่ม:
ทําการลาก GameController ไปวางที่ On Click() ของปุ่มในส่วน RunTime แล้วเลือก DropDownList ของ No Function ส่วนของ GameSystem เลือก ClickStartGame()
ทําการ Implement ฟังก์ชันใหม่ขึ้นมาชื่อว่า getEnemy() สําหรับลดค่า hp ทีละ 1 เมื่อชนยานศัตรู และ getPoint() สําหรับเพิ่ม score ทีละ 1 คะแนนเมื่อมีการยิงศัตรูได้โดยประกาศฟังก์ชันในคลาสของ GameSystem ต่อเนื่องดังนี้:
public void getEnemy(){
hp = hp - 1;
}
public void getPoint(){
score = score + 1;
}
ข้อสังเกต: การประกาศ public หน้า void เป็นการเปิดสาธารณะให้คลาสอื่นเรียกใช้ได้ ซึ่งนอกจาก ฟังก์ชันแล้วตัวแปรในคลาสนี้ก็สามารถทําได้เช่นกัน
แก้ไขศัตรูเมื่อถูกยิงเมื่อโดนกระสุนได้คะแนน
ขั้นตอนนี้ให้เปิดคลาส Enemy ขึ้นมาแล้วให้เพิ่มตัวแปรต่อไปนี้ลงไปที่ส่วนของตัวแปรทั้งหมด
public float SecondsUntilDestroy = 2.5f;
float startTime;
GameSystem gameSystem;
ให้ศัตรูมีตัวแปร SecondsUntilDestroy เป็น 2.5 วินาที เมื่อเกินเวลาจะทําลายตัวเองทิ้งจะได้เป็นการ เคลียร์หน่วยความจําในเกมของเรา ตามด้วยประกาศตัวแปร startTime ไว้ทําการเริ่มนับเวลาทําลายตัวเอง มีการประกาศคลาส GameSystem เก็บลงในตัวแปร gameSystem เพิ่มฟังก์ชัน Start() เข้าไปที่คลาสของ Enemy โดยให้เริ่มทํางานนับเวลาจาก Time.time(เวลา Interval, FPS) ในเกมของเราเก็บลงตัวแปร startTime
void Start()
{
startTime = Time.time;
if(gameSystem == null){
GameObject _gameSystem = GameObject.FindGameObjectWithTag("GameController") as GameObject;
gameSystem = _gameSystem.GetComponent<GameSystem>();
}
}
ในฟังก์ชัน Start() บ้างต้นจะมีการตรวจจับกระบวนการเดียวกันกับการตรวจหา Player หาก gameSystem เป็นค่าว่างเพื่อสร้าง GameObject ที่ handle ส่วนของ Component คลาส GameSystem ให้เชื่อมต่อกัน โดยอัตโนมัติหลังจากนั้นไปที่ Update() ให้ Implement ฟังก์ชันต่อไปนี้:
void Update()
{
transform.Translate(0, 0, objectSpeed);
if (Time.time - startTime >= SecondsUntilDestroy)
{
Destroy(this.gameObject);
}
}
ให้ตัวละครยานศัตรู เริ่มจับเวลาไปข้างหน้าเมื่อ startTime มากกว่า 2.5 วินาทีผ่านตัวแปร SecondsUntilDestroy ให้ทําลายตัวเองทิ้งด้วยคําสั่ง Destroy(this.gameObject);
แก้ไขหรือเพิ่มหากยังไม่ได้ Implement ฟังก์ชัน OnTriggerEnter() ของ Enemy เมื่อโดนกระสุนจะทําลายตัวเอง
จากเดิมคือ:
void OnTriggerEnter(Collider collision)
{
if (collision.gameObject.name == "bullet(Clone)")
{
Destroy(this.gameObject);
}
}
แก้ไขเป็น:
void OnTriggerEnter(Collider collision)
{
if (collision.gameObject.name == "bullet(Clone)")
{
gameSystem.getPoint();
Destroy(this.gameObject);
}
}
เพิ่มเงื่อนไขต่อไปนี้ลงใน OnTriggerEnter ของ Enemy
if (collision.gameObject.tag == "Player")
{
gameSystem.getEnemy();
Destroy(this.gameObject);
}
เป็นการตรวจสอบว่าหาก Enemy ชนกับ GameObject ที่มี Tag (อย่าลืมใส่หรือตั้ง) ว่า “Player” จะมีการ เสียคะแนนเกิดขึ้น เช่นกัน ตัว Enemy ก็จะทําลายตัวเอง
กลับไปที่คลาส GameSystem ให้เพิ่มเงื่อนไขต่อไปนี้ใน Update():
if(hp<=0){
hp = 0;
isGameOver=true;
isStartGame = false;
}
เป็นการตรวจสอบว่าหากพลังของ Player ตัวแปร hp มีการลดลงไปจนน้อยกว่าหรือเท่ากับ 0 ให้แสดงผล เป็น 0 เพราะหากติดลบจะดูไม่เหมาะสมกับเกม และให้ปรับเงื่อนไขแสดงหน้าจอ GameOverCanvas ออกมาโดยการเรียก Booleanของ isGameOver เป็น true และ ตั้งค่า isStartGame ให้เป็น false เพื่อให้ เกมมีการ Reset ค่าทั้งหมดใหม่
ทําการเพิ่ม Library ของการจัดการ Scene มาที่ Headerของคลาส GameSystem
using UnityEngine.SceneManagement;
เพิ่มฟังก์ชันใหม่ขึ้นมาชื่อว่า resetGame() ใน GameSystem ทําการ Implement ฟังก์ชันดังนี้:
public void resetGame(){
Scene scene = SceneManager.GetActiveScene();
SceneManager.LoadScene(scene.name, LoadSceneMode.Single);
}
ประกาศตัวแปร scene เป็นประเภท Scene (stricts) เรียก GetActiveSceneเพื่อดึงค่าว่าซีนที่กําลังเล่น อยู่นี้ชื่ออะไร (ซีน หรือ Scene คือ ด่าน Level ที่เรากําลังทํา) กลังจากนั้นให้ทําการ LoadScene() เป็นการ เรียก Scene ใหม่โดยเก็บค่า Scene ปัจจุบันคือ scene.name ไปทําการเรียกออกมา นั่นหมายความ บทเรียนนี้คือโหลด Scene เดิมซํ้านั่นเอง
ไปที่ GameOverCanvas บน Hierarchy ที่เรา Hide ไว้อยู่ให้กางออกมาแล้วเลือกที่ Button ให้เลือก GameController ไปวางใน On Click() แล้วเลือกฟังก์ชันคือ resetGame()
ทําการทดสอบเกมจะเห็นว่าตอนนี้เราได้เกมยานอวกาศปรากฏขึ้นมาแล้ว โดยการเล่นคือการควบคุมตัว ละครในเกมของเราด้วยแป้นพิมพ์ W,A,S,D เคลื่อนที่ไปมาในฉากมีการคลิกเมาส์เพื่อทําการยิงกระสุนไป ยังศัตรูเพื่อเก็บคะแนน ขั้นตอนต่อไปคือการจัดการ TextUI ใน Canvas แรกให้ปรากฏคะแนน HighScore และ Score ธรรมดา
สร้าง Canvas ใหม่ขึ้นมาชื่อว่า PlayGameCanvasแล้วย้าย Text จาก GameOverCanvas ไปวางใน PlayGameCanvas แทนโดยปรับตั้งค่า CanvasScaler ใหม่เป็นไปตามภาพด้านล่าง
ย้าย Text จาก GameOverCanvas มายัง PlayGameCanvas แล้วปรับ UI Scaler Mode
ปรับเป็น Scale With Screen Size
สร้าง Class ใหม่ใน Text ที่อยู่ภาพให้ PlayGameCanvas ใหม่ขึ้นมาชื่อว่า TextScore เป็น Component ใหม่ของ Text ที่เรากําหนดในภาพ
สร้าง คลาสชื่อ TextScore ใน Text ภายใต้ PlayGameCanvas
ทําการ Implement ระบบใหม่ของ Text ดังนี้:
using UnityEngine.UI;
เป็นการประกาศ UI ให้ตัว Text ของเราสามารถใช้งาน UI บน Unity ได้ ต่อมาให้ประกาศตัวแปร:
Text txt_score;
public GameSystem gameSystem;
ตัวแปรแรกคือ txt_score เป็นประเภทของ Text สําหรับแสดงผลใน UI
ตัวแปรที่สองคือ gameSystem เป็นการประกาศเพื่อใช้รองรับการเรียกคลาสข้ามไปยัง GameSystem
ทําการ Implement ฟังก์ชัน Start() ให้เรียก GameSystem และ ให้ txt_score ให้เรียกใช้งาน Component ของ Text โดยเขียนดังนี้:
void Start()
{
txt_score = GetComponent<Text>();
if(gameSystem == null){
GameObject _gameSystem = GameObject.FindGameObjectWithTag("GameController") as GameObject;
gameSystem = _gameSystem.GetComponent<GameSystem>();
}
}
เงื่อนไขต่อมาให้ไป Implement ส่วนของ Update() ดังนี้:
void Update()
{
txt_score.text = "Score: "+gameSystem.score;
}
เป็นการให้ txt_score ไปเรียกตัวแปร score จากคลาส GameSystem มาแสดงผลแบบ Text ตามเวลาจริง หรือที่เรียกว่า Real-Time Display ลาก GameController บน Hierarchy ไปวางใน Text ภายใต้ PlayGameCanvas ตามภาพ
ลาก GameController ไปวางใน TextScore ที่วางใน Text ของ PlayGameCanvas ทดสอบเกมและลองเล่น:
ทดสอบเล่นเกม โดยเริ่มจากฉากแรกของเกม ไปจนระบบพื้นฐานของเกม และคะแนน
Build PC Game การ Build Game สามารถกดที่ File -> Build Setting แล้วกด Build เกมของเราไปยัง Folder ที่ต้องการใน เครื่องคอมพิวเตอร์ของเราได้เลยตามสะดวก
Source Code: https://github.com/banyapon/UnitySpaceShip
One Comment