12/08/2018, 14:04

Coroutines

Khi ta gọi một hàm, nó sẽ chạy đến khi hoàn thành hết các câu lệnh rồi mới return. Điều này có nghĩa là bất kì 1 hành động nào được đặt trong hàm sẽ phải được thực hiện chỉ trong 1 lần update frame. 1 lần gọi hàm không thể được sử dụng để thể hiện các bước của 1 animation hay 1 chuỗi các sự kiện. ...

Khi ta gọi một hàm, nó sẽ chạy đến khi hoàn thành hết các câu lệnh rồi mới return. Điều này có nghĩa là bất kì 1 hành động nào được đặt trong hàm sẽ phải được thực hiện chỉ trong 1 lần update frame. 1 lần gọi hàm không thể được sử dụng để thể hiện các bước của 1 animation hay 1 chuỗi các sự kiện. Như ví dụ sau đây, hãy xem xét việc giảm dần opacity của object cho đến khi nó biến mất ( opacity = 0 )

void Fade()
{
	for ( float f = 1f; f >= 0; f -= 0.1f)
	{
		Color c = renderer.material.color;
		c.a = f;
		renderer.material.color = c;
	}
}

Tuy nhiên, hàm Fade này sẽ không có hiệu ứng mờ dần như ta mong đợi. Để có thể có hiệu ứng mờ dần, opacity phải được giảm sau 1 chuỗi các frame để hiện các thành phần trung gian đang được tạo ra. Tuy nhiên, 1 hàm sẽ thực thi tất cả những lệnh chứa trong nó chỉ trong 1 frame. Những thành phần trung gian sẽ không bao giờ được nhìn thấy và object sẽ biến mất ngay lập tức.

Ta có thể giải quyết được những trường hợp này bằng việc đưa đoạn code trên vào trong hàm Update, hàm này có thể thực thi việc làm mờ object trong từng frame theo cách cơ bản. Tuy nhiên, thuận tiện hơn, ta thường sử dụng 1 coroutine trong những công việc như này.

Một coroutine giống như 1 hàm có khả năng tạm ngưng việc thực thi lệnh và trả về 1 control cho Unity nhưng sau đó sẽ tiếp tục từ điểm mà nó tạm ngưng trước đó trên frame tiếp theo. Trong C#, 1 coroutine được khai báo như sau:

IEnumerator Fade()
{
	for ( float f = 1f; f >= 0; f-= 0.1)
	{
		Color c = renderer.material.color;
		c.a = f;
		renderer.material.color = c;
		yeild return null;
	}
}

Bản chất của hàm trên là 1 hàm được khai báo trả về giá trị kiểu IEnumerator với câu lệnh yeild return được đặt ở đâu đó trong phần thân hàm. Dòng yeild return là điểm mà tại đó hàm sẽ tạm ngưng thực thi và sẽ được tiếp tục tại các frame tiếp theo. Để cho 1 coroutine được chạy, ta cần sử dụng hàm StartCoroutine.

void Update()
{
	if ( Input.GetKeyDown("f") )
		StartCoroutine("Fade");
}

Theo mặc định, 1 coroutine được tiếp tục trong frame tiếp theo sau khi chạy đến lệnh yield nhưng ta có thể đặt thời gian delay sử dụng WaitForSecond:

IEnumerator Fade()
{
	for ( float f = 1f; f >= 0; f-= 0.1)
	{
		Color c = renderer.material.color;
		c.a = f;
		renderer.material.color = c;
		yeild return new WaitForSeconds(.1f);
	}
}

Cách này sử dụng để tạo ra hiệu ứng qua từng giai đoạn nhưng nó cũng hữu ích cho việc tối ưu. Rất nhiều công việc trong game cần được thực hiện định kì và cách rõ ràng nhất là đặt chúng trong hàm Update. Tuy nhiên, hàm này sẽ được gọi rất nhiều lần trong mỗi giây. Khi 1 công việc không cần phải lặp lại 1 cách khá đều đặn, ta có thể đặt chúng trong 1 coroutine để thực hiện cập nhật đều đặn nhưng không phải trong mọi frame. Một ví dụ của việc này, chẳng hạn ta thực hiện 1 chức năng báo hiệu, cảnh báo cho người chơi khi địch đang ở gần. Code của nó sẽ giống như sau:

function ProximityCheck()
{
	for ( int i = 0; i < enemies.Length; i++)
	{
		if ( Vector3.Distance(transform.position, enemies[i].transform.position) < dangerDistance)
			return true;
	}
	return false;
}

Nếu có rất nhiều địch, sau đó ta gọi hàm này trong mỗi frame thì có thể dẫn đến sự quá tải. Tuy nhiên, trong trường hợp này ta cần sử dụng 1 coroutine để gọi hàm này 0.1 giây 1 lần (100 mili giây).

IEnumerator DoCheck()
{
	for ( ; ; )
	{
		ProximityCheck();
		yield return new WaitForSeconds(.1f);
	}
}

Điều này sẽ giảm 1 lượng đáng kể số lần thực thi hàm ProximityCheck() mà không có bất kì một ảnh hưởng đáng chú ý nào trong gameplay.

Tham khảo

Unity Documentation

0