Học một chương trình sẽ không để thiết kế phần mềm tương tác nhiều hơn so với học thể loại bút pháp như viết thơ. Kyle Woodbury
STDIO Tiếp tục chuỗi bài viết Hướng Dẫn Hiện Thực Game Zero Với Unity, trong bài viết này, tôi sẽ hướng dẫn bạn đọc thêm các hiệu ứng âm thanh và hiệu ứng của gameplay. Đây là bài viết thứ 5, đồng thời là bài viết kết thúc chuỗi bài hướng dẫn.
Nội dung bài viết

Giới thiệu

Tiếp tục chuỗi bài viết Hướng Dẫn Hiện Thực Game Zero Với Unity, trong bài viết này, tôi sẽ hướng dẫn bạn đọc thêm các hiệu ứng âm thanh và hiệu ứng của gameplay. Đây là bài viết thứ 5, đồng thời là bài viết kết thúc chuỗi bài hướng dẫn.

Các bài viết trong series

Các hiệu ứng âm thanh trong Zero

Trong Zero sử dụng ba hiệu ứng âm thanh chính:

sfx_answer_right.wav: Sử dụng khi người chơi vượt qua một level.

sfx_answer_wrong.wav: Sử dụng khi GameOver.

sfx_button_press.wav: Sử dụng khi người chơi bấm chọn một button (trừ hai button Right và Wrong được xử lý riêng).

Để thêm một file âm thanh vào button, các bạn thực hiện thao tác kéo thả trực tiếp hoặc chọn Add Component → Audio → Audio Source, sau đó kéo thả file âm thanh vào thuộc tính Clip của Audio Source. Tham khảo thêm bài viết Âm Thanh Trong Unity.

Hai button Right Wrong quan trọng nên tôi sẽ ưu tiên hiện thực trước. Về mặt logic, sfx_answer_right sfx_answer_wrong không gắn vào hai button tương ứng. Câu trả lời của người chơi ở mỗi level là yếu tố quyết định âm thanh nào sẽ được phát ra. Do đó, tôi đưa phần xử lý hai âm thanh này vào GameManager.cs như sau:

Xử lý âm thanh Right (dòng 174-175, GameManager.cs):

m_audioController.clip = m_sfxAnswerRight;
m_audioController.Play();

Xử lý âm thanh Wrong (dòng 253-254, GameManager.cs):

m_audioController.clip = m_sfxAnswerWrong;
m_audioController.Play();

Trước đó, tôi định nghĩa các thuộc tính cần thiết và khởi tạo chúng trước khi sử dụng. Thao tác như sau:

Khai báo (dòng 33-35, GameManager.cs):

private AudioClip m_sfxAnswerRight;
private AudioClip m_sfxAnswerWrong;
private AudioSource m_audioController;

Khởi tạo (dòng 52-54, GameManager.cs):

m_sfxAnswerRight = Resources.Load<AudioClip>("Audios/sfx_answer_right");
m_sfxAnswerWrong = Resources.Load<AudioClip>("Audios/sfx_answer_wrong");
m_audioController = GetComponent<AudioSource>();

Tôi sử dụng Resources.Load để load trưc tiếp các file âm thanh trong thư mục tài nguyên. Bạn đọc tham khảo thêm bài viết Khởi Tạo Đối Tượng Trong Runtime.

Đối với các button còn lại, việc phát âm thanh có thể xử lý trong code hoặc cách khác tiện dụng hơn là sử dụng event OnClick của button. Thao tác như sau:

  • Thêm component Audio Source và gắn âm thanh sfx_button_press.wav vào thuộc tính Clip của Audio Source.
  • Tạo thêm một event OnClick bằng cách click chọn dấu "+" ở Inspector của button.
  • Chọn Audio Source của chính button đó và chọn Audio Source → Play() ở menu bên cạnh.

ss_1

Thêm effect cho gameplay

Trong gameplay của Zero, chúng tôi đã thống nhất và tạo ra các hiệu ứng Rotate Flash cho các suit để tăng tính hấp dẫn cho game. Hai hiệu ứng này đã được tôi hiện thực lại trong script EffectController.cs, bạn đọc có thể tham khảo thêm để hiểu về giải thuật mà tôi sử dụng trong các hiệu ứng.

Nguyên mẫu của hai hàm như sau:

public void EffectsController.DoRotateEffect(GameObject suit);
public void EffectsController.DoFlashEffect(GameObject suit);

Tham số truyền vào là suit cần tạo hiệu ứng. Mỗi khi người chơi vượt qua một level, Effect state (trạng thái hiệu ứng) của mỗi suit sẽ được cập nhật lại. Chỉ những suit đang được kích hoạt mới có hiệu ứng.

Trước hết, các hiệu ứng được định nghĩa trong enum Effect (dòng 29-34, Statics.cs):

public enum Effect
{
	EFFECT_FLASH,
	EFFECT_ROTATE,
	EFFECT_NONE
}

Hai hằng số về tỉ lệ xuất hiện hiệu ứng (dòng 19-20, Statics.cs):

public static readonly int s_effectFlashRate = 10;
public static readonly int s_effectRotateRate = 20;

Mỗi suit sẽ có một biến tương ứng quyết định effect của suit trong một level. Tôi sử dụng mảng một chiều để lưu trữ chung trạng thái effect của các suit như sau (dòng 37, 56, GameManager.cs):

private Effect[] m_suitEffects;
m_suitEffects = new Effect[Constant.s_numSuit * 2];

Tại hàm Update, các suit có effect sẽ được cập nhật liên tục từng frame. Do trong Zero chỉ có hai effect chính là Rotate và Flash nên tôi hiện thực đơn giản như sau (dòng 106-120, GameManager.cs):

/* Do Effect */
for (int i = 0; i < Constant.s_numSuit; i++)
{
	/* Effect for left side */
	if (m_suitEffects[i] == Effect.EFFECT_ROTATE)
		GetComponent<EffectsController>().DoRotateEffect(m_lefts[i]);
	else if (m_suitEffects[i] == Effect.EFFECT_FLASH)
		GetComponent<EffectsController>().DoFlashEffect(m_lefts[i]);

	/* Effect for right side */
	if (m_suitEffects[i + Constant.s_numSuit] == Effect.EFFECT_ROTATE)
		GetComponent<EffectsController>().DoRotateEffect(m_rights[i]);
	else if (m_suitEffects[i + Constant.s_numSuit] == Effect.EFFECT_FLASH)
		GetComponent<EffectsController>().DoFlashEffect(m_rights[i]);
}

Tại hàm GenerateNextCalculation, tôi loại bỏ effect của tất cả các suit, sau đó tiến hành random effect cho các suit đang được kích hoạt. Các hiệu ứng chỉ tồn tại trong một level duy nhất nên việc cập nhật lại tại GenerateNextCalculation là hợp lý và hiệu quả nhất. Code như sau (dòng 136-165, GameManager.cs):

/* Reset effect to none */
for(int i = 0; i < Constant.s_numSuit; i++)
{
	m_suitEffects[i] = Effect.EFFECT_NONE;
	m_suitEffects[i + Constant.s_numSuit] = Effect.EFFECT_NONE;

	m_lefts[i].transform.rotation = Quaternion.identity;
	m_rights[i].transform.rotation = Quaternion.identity;
}

/* Get new effect */
for(int i = 0; i < m_calculation[0]; i++)
{
	int randValue = Random.Range(0, 100);

	if (randValue <= Constant.s_effectFlashRate)
		m_suitEffects[i] = Effect.EFFECT_FLASH;
	else if (randValue <= Constant.s_effectRotateRate)
		m_suitEffects[i] = Effect.EFFECT_ROTATE;
}

for (int i = 0; i < m_calculation[1]; i++)
{
	int randValue = Random.Range(0, 100);

	if (randValue <= Constant.s_effectFlashRate)
		m_suitEffects[i + Constant.s_numSuit] = Effect.EFFECT_FLASH;
	else if (randValue <= Constant.s_effectRotateRate)
		m_suitEffects[i + Constant.s_numSuit] = Effect.EFFECT_ROTATE;
}

Sau khi hiệu ứng kết thúc, cụ thể là hiệu ứng xoay, suit có thể sẽ không còn góc xoay mặc định ban đầu. Do đó tôi gán lại góc xoay Quaternion.identity cho các suit khi hiệu ứng kết thúc (dòng 142-143).

Đến đây game đã hoàn thiện, bạn có thể chọn nút Play để xem thành quả đạt được.

ss_2

Download Project

STDIO_ZeroUnity-5

Bạn cần hỗ trợ các dự án kết nối không dây?

Quí doanh nghiệp, cá nhân cần hỗ trợ, hợp tác các dự án IoT, kết nối không dây. Vui lòng liên hệ, hoặc gọi trực tiếp 0942.111912.

  • TỪ KHÓA
  • Arduino
  • ESP32
  • ESP8266
  • Wifi
  • Bluetooth
  • Zigbee
  • Raspberry Pi
THẢO LUẬN
ĐÓNG