Trường hợp nổi bật nhất của coroutines là điểm đồ họa cũ bằng đồ họa & nhấp vào trò chơi phiêu lưu, nơi chúng được sử dụng để cắt cảnh và đoạn hoạt hình khác trong trò chơi. Một mã ví dụ đơn giản sẽ trông như thế này:
# script_file.scr
bob.walkto(jane)
bob.lookat(jane)
bob.say("How are you?")
wait(2)
jane.say("Fine")
...
toàn bộ chuỗi này không thể được viết dưới dạng mã bình thường, khi bạn muốn xem bob làm phim hoạt hình đi bộ của mình sau khi bạn đã làm bob.walkto(jane)
thay vì nhảy ngay đến dòng kế tiếp . Đối với các hoạt hình đi bộ để chơi bạn tuy nhiên cần phải cung cấp cho kiểm soát trở lại với các công cụ trò chơi và đó là nơi mà coroutines đi vào chơi.
Toàn bộ chuỗi này được thực hiện dưới dạng coroutine, nghĩa là bạn có khả năng tạm ngưng và tiếp tục nó theo ý muốn.Một lệnh như bob.walkto(jane)
do đó nói với đối tượng bob bên động cơ mục tiêu của nó và sau đó đình chỉ coroutine, chờ đợi một cuộc gọi wakeup khi bob đã đạt được mục tiêu của mình.
Trên mọi thứ bên cơ thể trông như thế này (pseudo code):
class Script:
def __init__(self, filename):
self.coroutine = Coroutine(filename)
self.is_wokenup = True
def wakeup(self):
self.is_wokenup = False;
def update():
if self.is_wokenup:
coroutine.run()
class Character:
def walkto(self, target, script):
self.script = script
self.target = target
def update(self):
if target:
move_one_step_closer_to(self.target)
if close_enough_to(self.target):
self.script.wakeup()
self.target = None
self.script = None
objects = [Character("bob"), Character("jane")]
scripts = [Script("script_file.scr")]
while True:
for obj in objects:
obj.update()
for scr in scripts:
scr.update()
Một từ litte cảnh báo tuy nhiên, trong khi coroutines làm cho mã hóa các chuỗi rất đơn giản, không phải mọi hiện thực bạn sẽ tìm thấy trong số họ sẽ được xây dựng với serialization trong tâm trí, do đó, tiết kiệm trò chơi sẽ trở thành một vấn đề rắc rối nếu bạn sử dụng nhiều coroutines.
Ví dụ này cũng chỉ là trường hợp cơ bản nhất của coroutine trong trò chơi, bản thân coroutine cũng có thể được sử dụng cho nhiều tác vụ và thuật toán khác.
Coroutines có một số vấn đề về đồng bộ hóa, nhưng chỉ khi trạng thái của chúng không thể nhất quán giữa các lệnh gọi yield(). Có một khóa ẩn xung quanh tất cả các mã giữa các cuộc gọi yield() có thể đơn giản hóa rất nhiều thứ. Bíp lớn nhất là bất kỳ khi nào một thói quen được tạo ra, người ta nên quyết định sau đó và ở đó liệu thường trình sẽ luôn có thể hoàn thành trước khi nó có năng suất(). Nếu người gọi thường xuyên không mong đợi một thói quen sinh lợi, nó có thể không có những thứ trong trạng thái phù hợp khi thói quen được gọi. Thêm một lợi nhuận() sẽ là một thay đổi phá vỡ. – supercat
Vấn đề là bạn thường muốn có khả năng mang lại tất cả các nơi cho các thói quen như vậy (như trong UnrealScript, nơi mà 'hàm tiềm năng' tiềm ẩn), nhưng điều này dẫn đến sự kết hợp của coroutines tương tác theo những cách không thể đoán trước. ví dụ. Nếu một là một coroutine tuần tra nói "đi về phía bắc; nhìn; đi về phía nam; nhìn; lặp lại;" và người kia là một thói quen phản ứng nói "nếu (nghe thấy tiếng tây) {đi về phía tây; nhìn; đi về phía đông;}" thì xen kẽ cả hai có thể dẫn đến việc diễn viên di chuyển khá khó lường. Đó là lý do tại sao UnrealScript chỉ cho phép các chức năng như vậy trong chính xác một 'coroutine'. – Kylotan
Và ngay cả khi bạn chỉ có 1 coroutine cho mỗi diễn viên, điều đó vẫn chưa đủ để đảm bảo logic luôn hoạt động ở cấp độ ngôn ngữ: nếu coroutine của bạn nói: "rút gươm; chờ 1 giây; tấn công đối thủ bằng kiếm" sẽ xảy ra nếu thanh kiếm biến mất do bạn bị người chơi khác giải giáp? Đặc biệt, nếu ngôn ngữ của bạn thực hiện coroutine đó bằng cách sử dụng một biến cục bộ cho một thanh kiếm mà bây giờ đề cập đến một đối tượng null? Rất khó để xem xét tất cả các trường hợp này nếu bạn đang sử dụng một ngôn ngữ mục đích chung theo cách thông thường. – Kylotan