Search…

Hiện Thực Game Zero Với Unity - Phần 2 - Hiện Thực Gameplay

30/09/20205 min read
Hướng Dẫn Hiện Thực Game Zero Với Unity. Giới thiệu và hướng dẫn xây dựng gameplay cơ bản của game.

Bài viết nằm trong chuỗi Hướng Dẫn Hiện Thực Game Zero Với Unity, cung cấp kiến thức trong việc xây dựng 1 game trên Unity, đồng thời biết cách tổ chức project hiệu quả trong Unity.

Ở phần 2 sẽ giới thiệu và hướng dẫn xây dựng gameplay cơ bản của game.

Giới thiệu gameplay

Gameplay của Zero khá đơn giản: Mỗi lượt chơi là 1 phép tính được biểu thị bằng hình ảnh ngẫu nhiên 4 chất của bộ bài Tây (gọi là suit). Người chơi sẽ phải tính số lượng suit có ở 2 bên, kết hợp với các toán tử (>, <, =) và quyết định nhanh chóng.

Sau 1 khoảng thời gian ngắn, nếu trả lời đúng sẽ được chuyển sang mức độ tiếp theo với độ khó tăng dần, trả lời sai sẽ thua. Người chơi có thể cạnh tranh bằng cách chia sẻ điểm số đạt được với nhau. Với yếu tố về thời gian và các hiệu ứng, Zero là 1 trong những game thú vị thuộc thể loại endless.

Bài viết này sẽ hướng dẫn hiện thực lại gameplay bao gồm:

  • Random phép tính.
  • Kiểm tra tính đúng sai của phép tính. 

GameManager

GameManager là nơi quản lý tập trung các sự kiện xảy ra trong game.

Toàn bộ phần Gameplay sẽ được quản lý bởi script GameManager.cs. Script này sẽ được gắn vào 1 đối tượng được gọi là GameManager.

Giao diện và cấu trúc của các GameObject trong project như sau:

ss_1

Các thuộc tính và phương thức cần thiết của script GameManager.cs:

  • Left, Right, Operator: lưu trữ ảnh hiển thị mô phỏng của phép tính hiện tại.
  • Calculation: lưu trữ phép tính được ngẫu nhiên sau mỗi lần trả lời đúng. Calculation là cơ sở để vẽ các đối tượng lên màn hình.
  • Hàm GenerateNextCalculation: tạo phép tính mới. Hàm sẽ được gọi khi người chơi vượt qua được 1 câu hỏi.
  • Hàm IsRightAnswer: kiểm tra tính đúng sai của phép tính. Hàm này được sử dụng trong button khi click chuột.

Đối với các suit đại diện cho phép tính, sử dụng các container (đối tượng chứa) và thành phần Transform của container để tìm và lưu trữ các đối tượng con. Cách hiện thực như sau (dòng 23-27, GameManager.cs):

for(int i = 0; i < 4; i++)
{
     m_lefts[i] = m_leftContainer.transform.GetChild(i).gameObject;
     m_rights[i] = m_rightContainer.transform.GetChild(i).gameObject;
}

Random phép tính

Việc random phép tính phụ thuộc vào số lượng suit mỗi bên và số lượng các toán tử. Đối với Zero, số lượng tối đa ở 1 phía là 4, đồng thời sử dụng 3 toán tử >, <=.

Hiện thực hàm GenerateNextCalcultion như sau (dòng 55-65, GameManager.cs):

public void GenerateNextCalculation()
{
	m_calculation[0] = Random.Range(1, 5);     // Suits left
	m_calculation[1] = Random.Range(1, 5);     // Suits right
	m_calculation[2] = Random.Range(0, 3);     // Operator
	
	GetSuitsPosition(m_lefts, m_calculation[0]);
	GetSuitsPosition(m_rights, m_calculation[1]);
	
	m_operator.GetComponent<SpriteRenderer>().sprite = m_sprOperators[m_calculation[2]];
}

Sau khi thực hiện ngẫu hiên phép tính, cập nhật lại các suit 2 bên bằng cách cho ẩn những suit không cần thiết, đồng thời toán tử cũng được cập nhật lại. Việc cập nhật vị trí các suits được hiện thực trong hàm GetSuitsPosition. Nguyên mẫu hàm như sau:

void GameManager.GetSuitsPosition(GameObject[] _suits, int _count);

Hàm nhận vào 2 giá trị là danh sách đối tượng cùng với số lượng. Thiết kế hàm như thế để sử dụng chung cho cả các suits ở 2 bên. Hiện thực hàm khá đơn giản do Zero chỉ có tối đa 4 suit mỗi bên (dòng 67-130, GameManager.cs):

void GetSuitsPosition(GameObject[] _suits, int _count)
{
	switch (_count)
	{
		case 1:
			_suits[0].SetActive(true);
			_suits[1].SetActive(false);
			_suits[2].SetActive(false);
			_suits[3].SetActive(false);

			_suits[0].transform.localPosition = Vector3.zero;
			_suits[0].GetComponent<SpriteRenderer>().sprite = m_sprSuits[Random.Range(0, 4)];

			break;
		case 2:
			_suits[0].SetActive(true);
			_suits[1].SetActive(true);
			_suits[2].SetActive(false);
			_suits[3].SetActive(false);

			_suits[0].transform.localPosition = new Vector3(-0.6f, 0, 0);
			_suits[0].GetComponent<SpriteRenderer>().sprite = m_sprSuits[Random.Range(0, 4)];

			_suits[1].transform.localPosition = new Vector3(0.6f, 0, 0);
			_suits[1].GetComponent<SpriteRenderer>().sprite = m_sprSuits[Random.Range(0, 4)];

			break;
		case 3:
			_suits[0].SetActive(true);
			_suits[1].SetActive(true);
			_suits[2].SetActive(true);
			_suits[3].SetActive(false);

			_suits[0].transform.localPosition = new Vector3(-0.6f, -0.5f, 0);
			_suits[0].GetComponent<SpriteRenderer>().sprite = m_sprSuits[Random.Range(0, 4)];

			_suits[1].transform.localPosition = new Vector3(0.6f, -0.5f, 0);
			_suits[1].GetComponent<SpriteRenderer>().sprite = m_sprSuits[Random.Range(0, 4)];

			_suits[2].transform.localPosition = new Vector3(0, 0.5f, 0);
			_suits[2].GetComponent<SpriteRenderer>().sprite = m_sprSuits[Random.Range(0, 4)];

			break;
		case 4:
			_suits[0].SetActive(true);
			_suits[1].SetActive(true);
			_suits[2].SetActive(true);
			_suits[3].SetActive(true);

			_suits[0].transform.localPosition = new Vector3(-0.6f, 0, 0);
			_suits[0].GetComponent<SpriteRenderer>().sprite = m_sprSuits[Random.Range(0, 4)];

			_suits[1].transform.localPosition = new Vector3(0.6f, 0, 0);
			_suits[1].GetComponent<SpriteRenderer>().sprite = m_sprSuits[Random.Range(0, 4)];

			_suits[2].transform.localPosition = new Vector3(0, 1.0f, 0);
			_suits[2].GetComponent<SpriteRenderer>().sprite = m_sprSuits[Random.Range(0, 4)];

			_suits[3].transform.localPosition = new Vector3(0, -1.0f, 0);
			_suits[3].GetComponent<SpriteRenderer>().sprite = m_sprSuits[Random.Range(0, 4)];

			break;
	}
}

Kiểm tra tính đúng sai bằng button

2 button được hiện thực trong bài viết trước có nhiệm vụ kiểm tra tính đúng đắn của phép tính. Khi người chơi trả lời sai thì lượt chơi sẽ kết thúc. Ở bài viết trước, hiện thực đơn giản bằng cách xuất 1 chuỗi tương ứng ra màn hình Console. Chỉnh sửa script ButtonManager.cs cho phù hợp với mục tiêu của gameplay như sau (dòng 6-24, GameManager.cs):

public void CheckAnswer()
{
	GameManager manager = GameObject.FindGameObjectWithTag("GameManager").GetComponent<GameManager>();
	
	if(gameObject.name == "BtnRight")
	{
		if (manager.IsRightAnswer())
			manager.GenerateNextCalculation();
		else
	        Static.S_Debug("You're wrong!");
	}
	else
	{
		if (!manager.IsRightAnswer())
			manager.GenerateNextCalculation();
		else
	        Static.S_Debug("You're wrong!");
	}
}

Script GameManager.cs được gắn vào đối tượng GameManager có tag là “GameManager”. Do đó sử dụng hàm FindGameObjectWithTag để tìm và lấy ra scipt này.

Tại đây là lúc hàm GenerateNextCalculation được gọi khi người chơi chọn đúng. Hàm kiểm tra tính đúng đắn của phép tính được đặt trong script GameManager.cs với thuộc tính public (dòng 132-138, GameManager.cs):

public bool IsRightAnswer()
{
	return
			(m_calculation[2] == 0 && m_calculation[0] == m_calculation[1]) ||
			(m_calculation[2] == 1 && m_calculation[0] > m_calculation[1]) ||
			(m_calculation[2] == 2 && m_calculation[0] < m_calculation[1]);
}

Ngoài ra, trong hàm Update gắn chức năng của 2 button này vào 2 phím mũi tên trái phải để tiện theo dõi trong Unity Editor (dòng 35-52, GameManager.cs):

#if UNITY_EDITOR
		{
			if(Input.GetKeyDown(KeyCode.LeftArrow))
			{
				if (IsRightAnswer())
					GenerateNextCalculation();
				else
					Static.S_Debug("You're wrong!");
			}
			else if (Input.GetKeyDown(KeyCode.RightArrow))
			{
				if (!IsRightAnswer())
					GenerateNextCalculation();
				else
					Static.S_Debug("You're wrong!");
			}
		}
#endif

Download Project

STDIO_ZeroUnity-2.

Bài chung series

IO Stream

IO Stream Co., Ltd

30 Trinh Dinh Thao, Hoa Thanh ward, Tan Phu district, Ho Chi Minh city, Vietnam
+84 28 22 00 11 12
developer@iostream.co

383/1 Quang Trung, ward 10, Go Vap district, Ho Chi Minh city
Business license number: 0311563559 issued by the Department of Planning and Investment of Ho Chi Minh City on February 23, 2012

©IO Stream, 2013 - 2024