FeaturedGame DevelopmentGame DevelopmentUnity 3D

เขียนเกมฝึกพิมพ์ภาษาอังกฤษ Typing Game ด้วย Unity ตอนที่ 1

ตัวอย่าง Workshop แบบฝึกหัดเชิงปฏิบัติการพัฒนาเกมแนว Typing Game หรือเกมฝึกพิมพ์ภาษาอังกฤษด้วย Unity 3D ประกอบไปด้วย UI, EventSystem สำหรับผู้เริ่มต้น

เปิด Unity 5 ขึ้นมาครับ ทำการ New Project มาใหม่ (ผมจะละการเอาตัวละคร และ Animator ออกไปก่อนนะครับ คิดว่าคงทำกันได้)

Screen Shot 2558-07-12 at 9.01.27 PM

 

ทำการวาง Plane ลงไปในฉากก่อนเลยครับ 2 ตัว โดย Plane ตัวแรกนั้นตั้งชื่อว่า GameOver อีกตัวนั้นหา Material มาใส่เล็กน้อยตั้งชื่อว่า Water ครับโดยการวางจะเป็นดังนี้

Screen Shot 2558-07-12 at 9.03.34 PM

โดยการวาง GameOver ไว้ตำแหน่งข้างล่าง และ นำ Water วางเหนือ GameOver ครับ โดย Inspector ตัวอย่างนั้นเป็นดังนี้ครับ

Inspector ของ GameOver
Inspector ของ GameOver

และอีกตัวคือ Inspector  ของ “Water” ครับ

Inspector ของ Water
Inspector ของ Water

ทำการสร้าง Script C# ใหม่ของ Water ครับชื่อวา “Water.cs” โดยใส่ Script การเลื่อน Material เช่นเคย

using UnityEngine;
using System.Collections;

public class Water : MonoBehaviour {
	float speed = 0.04f;
	float offset;
	void Start () {
		
	}
	void Update () {
		offset = Time.time * speed;
		GetComponent<Renderer>().material.mainTextureOffset= new Vector2(0,-offset);
	}
}

ต่อมาให้เราสร้าง Box ขึ้นมาเป็นแท่น 3 แท่นลอยเหนือ Water ครับ จุดประสงค์คือเป็น Box ให้ตัวละครของเรากระโดดข้ามจากซ้ายสุดไปขวาสุด

(วิธีการคือหา Material ของต้นหญ้า ไปใส่ใน Box แล้วทำการ Clone Prefab มาใช้ซ้ำๆ ครับ)

Screen Shot 2558-07-12 at 9.09.45 PM

กำหนด Inspector ของ Box ให้เรียบร้อยครับ

Inspector ของ "Box"
Inspector ของ “Box”

กำหนดค่าต่างๆ ตามที่สร้างไว้ตามตัวอย่างก็ได้ครับ ต่อมาเราจะสร้างตัวละครไปวางใน Scene ครับ Asset ที่ใช้ก็ QueryChan นำมา Re-Skin ใหม่แค่นั้นครับ ลางไปวางที่ตำแหน่ง Box ตัวแรกซ้ายสุด กำหนดชื่อว่า Player ครับ

Screen Shot 2558-07-12 at 9.13.32 PM

วางตัวละครลงไปในฉากครับ แล้วทำการใส่ Physics -> RigidBody (กำหนด Gravity), Physics -> Character Controller และ Physics -> Capsule Collider ครับ

Screen Shot 2558-07-12 at 9.15.20 PM

ส่วน Animator นั้นก็ทำง่ายก็พอครับสร้าง Parameter Bool ว่า IsJumping และ Trigger ว่า IsWinner พอ

ส่วนค่า Inspector ของ Player นั้น กำหนดตามตัวอย่างก็ได้ครับ

Inspector ของ Player
Inspector ของ Player

ต่อมานำส่วนของจุดที่ตัวละครเราไปชนแล้วจะชนะ หรือผ่านเกมซะ อาจจะเป็นรางวัลอะไรสักอย่าง ในตัวอย่างใช้ ทาโกะยากิ ยักษ์ครับตั้งชื่อว่า “Reward” วางลงไปใน Scene ที่  Box สุดท้ายครับ

Screen Shot 2558-07-12 at 9.18.33 PM

กำหนด Inspector ของ Reward ดังนี้ครับ

Inspector ของ Rewards
Inspector ของ Rewards

ปรับ Main Camera ของเราที่ Inspector ส่วนของ Position X เป็น 9 ส่วนของ Position Y เป็น 3 และ ส่วนของ Position Z เป็น -3 ครับ

และปรับ Rotation ส่วนของ X เป็น 7 แกน Rotation Y เป็น 270 และ Z เป็น 0 ในหน้า Game Scene สำหรับ Preview เกมของเราจะเป็นดังภาพด้านล่างครับ

Screen Shot 2558-07-12 at 9.18.45 PM

ต่อมาให้เราสร้าง UI มาไว้ในเกมครับ ไปที่เมนู GameObject->UI เลือก Raw Image  และ Text มาวางในเกม ทั้ง 2 ตัว

Screen Shot 2558-07-12 at 9.25.33 PM

ที่หน้า Hierarchy จะมีการสร้าง Canvas และ EventSystem ให้อัตโนมัติ

Screen Shot 2558-07-12 at 9.27.18 PM

คลิกที่ Canvas ครับ แล้วตั้งค่า Inspector เป็น Scale With ScreenSize เพื่อให้เป็นการออกแบบ Layout แบบ Responsive ขยายและ Match Size ตามขนาดหน้าจอครับ

Screen Shot 2558-07-12 at 9.29.08 PM

เปลี่ยนชื่อ Raw Image ใน Canvas เปลี่ยนชื่อเป็น “ClockIcon” หา texture รูปนาฬิกาสวยๆ มาใส่ และ เปลี่ยนชื่อ Text ใน Canvas เป็น TimerText และวางตำแหน่งของทั้งสองให้เหมาะสม

กดปุ่ม 2D ใน Scene View ดูก่อนครับ แล้วค่อยวางตำแหน่ง

Screen Shot 2558-07-12 at 9.32.05 PM

สังเกตจากตัวอย่างนั่นคือการวางที่ตำแหน่งซ้ายบนของ Canvas หรือหน้าจอนั่นเองครับ

ต่อมาเราจะสร้างระบบ จับเวลา Count Down ครับ ให้เราสร้าง C# Script ชื่อว่า “TimeCounter.cs” ขึ้นมาครับ ใส่ไว้เป็น Component หนึ่งของ TimerText ใน Canvas ของเราครับ

using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using UnityEngine.EventSystems;

public class TimeCounter : MonoBehaviour {
	Text counterText;
	float startTime = 11;
	public string countValue= "";
	void Start () {
		counterText = GetComponent<Text>();
		Time.timeScale = 1;
	}

	void Update () {
		startTime -= Time.deltaTime;
		int iValue = (int)startTime;
		iValue = Mathf.FloorToInt(startTime);
		if (iValue < 0) {
			iValue = 0;
		}
		countValue = iValue.ToString ();
		counterText.text = ""+countValue;
	}
}

สังเกตครับจะเห็นว่าถ้ามีการทำงานเขียน Script ร่วมกับ UI อย่าง text ต้องมีการประกาศ

using UnityEngine.UI;
using UnityEngine.EventSystems;

 

ทดสอบจะเห็นว่า เวลามันจะ count down จาก 11 ไปเรื่อยๆ จนถึง 0 ครับ

Screen Shot 2558-07-12 at 9.39.17 PM

ต่อมาเราจะทำ Game Logic การเล่นที่เป็นหัวใจสำคัญของเกมเราครับให้เราทำการสร้าง Empty GameObject ขึ้นมา ตั้งชื่อว่า “GameSystem” วางตรงไหนก็ได้ครับใน Scene

Screen Shot 2558-07-12 at 9.34.17 PM

ทำการสร้าง C# Script ขึ้นมาชื่อว่า “GameSystem.cs” ใส่ฟังก์ชันต่อไปนี้

using UnityEngine;
using System.Collections;

public class GameSystem : MonoBehaviour {
	public string randomResult ="";
	public bool winnerGame = false;
	public bool isBox = false;
	public bool isPassed = false;
	
	public void RandomString(){
		string[] myDataStrings = new string[] {"cat", "student", "dog","elephant","bangkok","thailand","monkey","teacher","doctor","world","car","boat"};
		string myRandomString  = myDataStrings[Random.Range(0, myDataStrings.Length)];

		randomResult = myRandomString.ToUpper ();
			
	}
	public void IsWinner(){
		winnerGame = true;
		Debug.Log (winnerGame);
	}
	public void boxReset(){
		isBox = true;
	}
	public void CorrectAnswer(){
		isPassed = true;
	}
}

ส่วนของ Random String คือสุ่มเอาศัพท์ที่เรากำหนดไว้ให้พิมพ์ตามจากในระบบที่เก็บอยู่ในรูปของ Array เอาออกมาเก็บลงตัวแปร randonResult พักไว้ให้ระบบเรียกไปใช้ครับที่ฟังก์ชัน

public void RandomString(){
		string[] myDataStrings = new string[] {"cat", "student", "dog","elephant","bangkok","thailand","monkey","teacher","doctor","world","car","boat"};
		string myRandomString  = myDataStrings[Random.Range(0, myDataStrings.Length)];

		randomResult = myRandomString.ToUpper ();
			
	}

ส่วน

public void boxReset(){
  isBox = true;
}
public void CorrectAnswer(){
  isPassed = true;
}

ฟังก์ชัน boxReset() มีไว้สำหรับ แจ้ง reset ค่าเมื่อตัวละครยืนบน “Box” และ CorrectAnswer() มีไว้เช็คเพื่อทำการ เปลี่ยนศัพท์ใหม่เมื่อพิมพ์ถูกครับ

ดังนั้นเราจะเขียนโปรแกรมแบบ OOP กันแล้ว โดยทุก Element หรือทุก Model ที่มี C# อยู่จะมีการอ้างถึง GameSystem.cs ทุกตัวครับ

ไปที่ Player ทำการสร้าง C# ชื่อ “Player.cs” ขึ้นมาควบคุมมันครับ

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEngine.UI;
using UnityEngine.EventSystems;

public class Player : MonoBehaviour {
	Animator anim;
	public float speed = 4.0F;
	public float jumpSpeed = 7.0F;
	public float gravity = 10.0F;
	private Vector3 moveDirection = Vector3.zero;
	public bool Winner = false;
	public bool wordshow = false;

	public GameSystem system;
	void Start(){
		anim = GetComponent <Animator> ();
		Time.timeScale = 1;
		if(system ==null){
			GameObject _system = GameObject.FindGameObjectWithTag("GameSystem") as GameObject;
			system = _system.GetComponent<GameSystem>();
		}
	}
	void Update() {
		anim.SetBool ("IsJumping", false);
		
		CharacterController controller = GetComponent<CharacterController>();
		if (controller.isGrounded) {
				moveDirection = new Vector3(0, 0, 0);
				moveDirection = transform.TransformDirection(moveDirection);
				moveDirection *= speed;
		}
		if (system.isPassed == true) {
			Character_jump();
			system.boxReset();
			system.isPassed = false;
		}

		moveDirection.y -= gravity * Time.deltaTime;
		controller.Move(moveDirection * Time.deltaTime);
	}

	public void Character_jump(){
		moveDirection.y = jumpSpeed;
		moveDirection.z = speed;
		anim.SetBool ("IsJumping", true);
	}

	void OnTriggerEnter(Collider other) {
		switch (other.gameObject.name)
		{
		case "Reward":
			system.IsWinner();
			break;
		case "GameOver":
			print ("Game Over");
			break;
		default:
			break;
		}
	}
}

สังเกต

public GameSystem system;

เราจะอ้างถึงไฟล์ GameSystem.cs โดยอ้างไว้ในตัวแปล system ครับ

ถ้าเป็นบทเรียนตอนก่อนๆ เราจะต้องลาก เจ้า GameObject ในหน้า Hierarchy ไปวางที่ช่องวางของ Player ของ Inspector แต่ในรอบนี้เราจะเขียน Code ให้มันเรียกค่าตัวนี้ไปวางอัตโนมัติครับ โดยการอ้าง Tag ของ Inspector GameObject

ให้ไปสร้าง Tag ใหม่ขึ้นมาครับ คลิกที่ Gamesystem บน Heirarchy

กด Add Tag...
กด Add Tag…

ทำการเพิ่ม Tag ใหม่เข้าไป 3 อย่างไว้เลยคือ GameSystem และเตรียม TypeCorrect และ TypeWrong ไว้ก่อนล่วงหน้าครับ

Screen Shot 2558-07-12 at 9.49.31 PM

กลับมาที่ Code ของ Player.cs จะเห็นว่าเราจะทำการค้นหา Tag ที่ชื่อ “GameSystem” จาก Heirarchy ไปใส่ในตัวแปล system ทันทีด้วยตัวมันเองโดยที่เราไม่ต้องลากวางเลยครับ

if(system ==null){
	GameObject _system = GameObject.FindGameObjectWithTag("GameSystem") as GameObject;
	system = _system.GetComponent<GameSystem>();
}

โดยเงื่อนไขการชน Trigger ของตัวละครนั้น

void OnTriggerEnter(Collider other) {
		switch (other.gameObject.name)
		{
		case "Reward":
			system.IsWinner();
			break;
		case "GameOver":
			print ("Game Over");
			break;
		default:
			break;
		}
	}

ถ้าชนกับ GameObject ที่ชื่อ “Reward” ให้เรียกฟังก์ชัน IsWinner() ที่อยู่ใน GameSystem.cs ได้เลยครับ นั่นคือ ชนะเกม

ส่วนของ GameSystem.cs นั้นจะมีการเช็คว่าพิมพ์ถูกหรือไม่ ไว้เทียบกับคำตอบที่ปรากฏถ้าถูกจะมีการ ใช้ตัวแปร isPassed มาเช็ค ร่วมกับ ตัวละครใน Player.cs ถ้าพิมพ์ถูกตัวละครจะกระโดดไปข้างหน้า

if (system.isPassed == true) {
	Character_jump();
	system.boxReset();
	system.isPassed = false;
}

กลับมาที่ไฟล์ TimeCounter.cs ครับเพิ่ม GameSystem.cs เข้าไปเช่นกันโดยการประกาศ ตัวแปรเพิ่ม และแก้ไข ฟังก์ชัน Start() ตามนี้

public GameSystem system;

	void Start () {
		counterText = GetComponent<Text>();
		Time.timeScale = 1;
		if(system ==null){
			GameObject _system = GameObject.FindGameObjectWithTag("GameSystem") as GameObject;
			system = _system.GetComponent<GameSystem>();
		}
	}

แล้วไปเพิ่มเงื่อนไขสำหรับ จับเวลาใหม่เมื่อตัวละครแตะโดน Box ให้ reset เวลาเป็น 11 ใหม่ตามนี้ที่ Update() ครับ

void Update () {
		startTime -= Time.deltaTime;
		int iValue = (int)startTime;
		iValue = Mathf.FloorToInt(startTime);
		if (iValue < 0) {
			iValue = 0;
		}
		countValue = iValue.ToString ();
		counterText.text = ""+countValue;

		if (system.isBox == true) {
			startTime = 11;
			system.isBox=false;
		}
	}

ทำการเพิ่ม Text ขึ้นมาอีกตัวใน Canvas ครับตั้งชื่อว่า “RandomText”

Screen Shot 2558-07-12 at 10.01.24 PM

ทำการเขียน Code C# ลงไปใน RandomText ครับ โดยมีฟังก์ชันดังนี้

using UnityEngine.UI;
using UnityEngine.EventSystems;

public class RandomText : MonoBehaviour {
	Text ShowText;
	string blankText = "";
	bool isBlinking = true;
	public GameSystem system;
	public bool isRandom = false;
	public string word_merge ="";

	void Start () {
		ShowText = GetComponent<Text>();

		if(system ==null){
			GameObject _system = GameObject.FindGameObjectWithTag("GameSystem") as GameObject;
			system = _system.GetComponent<GameSystem>();
		}
	}

	public void Random_String(){
		StartCoroutine(BlinkText());
		system.RandomString();

	}

	void Update(){
		if (isRandom == false) {
			Random_String ();
			isRandom = true;
		} 

		if (system.isBox == true) {
			isRandom = false;
		}

		if (system.winnerGame == true) {
			ShowText.text = "Stage Clear!";
			isBlinking = false;
			StartCoroutine(stopPlay());
		}
		word_merge = system.randomResult;

	}

	public IEnumerator BlinkText(){
		while(isBlinking){
			ShowText.text = blankText;
			yield return new WaitForSeconds(.5f);
			ShowText.text = word_merge;
			yield return new WaitForSeconds(.5f); 
		}
	}

	public IEnumerator stopPlay(){
		yield return new WaitForSeconds(5f);
		Time.timeScale = 0;
	}
}

เรียกการสุ่ม Array ศัพท์ของเราจากไฟล์ GameSystem.cs ในฟังก์ชัน Start() ครับ

public void Random_String(){
		StartCoroutine(BlinkText());
		system.RandomString();

	}

และมีการเช็คว่าถ้ามีการสุ่มไปแล้วจะไม่สุ่มอีกผ่าน Boolean ของ IsRandom ครับ

void Update(){
		if (isRandom == false) {
			Random_String ();
			isRandom = true;
		} 

		if (system.isBox == true) {
			isRandom = false;
		}

		if (system.winnerGame == true) {
			ShowText.text = "Stage Clear!";
			isBlinking = false;
			StartCoroutine(stopPlay());
		}
		word_merge = system.randomResult;

	}

ใส่ code ส่วนของ effect text กระพริบสักหน่อย

public IEnumerator BlinkText(){
		while(isBlinking){
			ShowText.text = blankText;
			yield return new WaitForSeconds(.5f);
			ShowText.text = word_merge;
			yield return new WaitForSeconds(.5f); 
		}
	}

ทำการทดสอบครับ ศัพท์จะมาแล้ว

Screen Shot 2558-07-12 at 10.11.46 PM

บทเรียนต่อไป เขียนเกมฝึกพิมพ์ภาษาอังกฤษ Typing Game ด้วย Unity ตอนที่ 2

Asst. Prof. Banyapon Poolsawas

อาจารย์ประจำสาขาวิชาการออกแบบเชิงโต้ตอบ และการพัฒนาเกม วิทยาลัยครีเอทีฟดีไซน์ & เอ็นเตอร์เทนเมนต์เทคโนโลยี มหาวิทยาลัยธุรกิจบัณฑิตย์ ผู้ก่อตั้ง บริษัท Daydev Co., Ltd, (เดย์เดฟ จำกัด)

Related Articles

Back to top button

Adblock Detected

เราตรวจพบว่าคุณใช้ Adblock บนบราวเซอร์ของคุณ,กรุณาปิดระบบ Adblock ก่อนเข้าอ่าน Content ของเรานะครับ, ถือว่าช่วยเหลือกัน