กลไกพื้นฐานใน 'การคืนผลตอบแทน www' ของ Unity3D Game Engine


14

ในเอ็นจิ้นเกม Unity3D ลำดับรหัสทั่วไปสำหรับการรับข้อมูลระยะไกลคือ:

WWW www = new WWW("http://remote.com/data/location/with/texture.png");
yield return www;

กลไกพื้นฐานที่นี่คืออะไร?

ฉันรู้ว่าเราใช้กลไกผลตอบแทนเพื่ออนุญาตให้เฟรมถัดไปประมวลผลในขณะที่การดาวน์โหลดเสร็จสมบูรณ์ แต่เกิดอะไรขึ้นภายใต้ประทุนเมื่อเราทำyield return www?

มีวิธีใดบ้างที่ถูกเรียกใช้ (ถ้ามีในคลาส WWW) ความสามัคคีใช้เธรดหรือไม่ เลเยอร์ Unity "ส่วนบน" กำลังจับอินสแตนซ์ www แล้วทำอะไรบางอย่าง

แก้ไข:

  • คำถามนี้เกี่ยวกับ Unity3D internals โดยเฉพาะ ฉันไม่สนใจคำอธิบายเกี่ยวกับวิธีการyieldทำงานของคำสั่งใน C # แต่ฉันกำลังมองหามุมมองภายในเกี่ยวกับวิธีที่ Unity จัดการกับสิ่งปลูกสร้างเหล่านี้เพื่ออนุญาตให้ WWW สามารถดาวน์โหลดข้อมูลในลักษณะกระจายข้ามเฟรมต่างๆ

1
โปรดทราบว่าการใช้yield returnสำหรับการดำเนินการแบบอะซิงโครนัสคือแฮ็ค ในโปรแกรม "ของจริง" C # คุณต้องใช้โปรแกรมTaskนี้ ความสามัคคีอาจไม่ได้ใช้เพราะมันถูกสร้างขึ้นก่อน. Net 4.0 เมื่อTaskถูกนำมาใช้
BlueRaja - Danny Pflughoeft

คำตอบ:


8

นี่คือคำหลัก C # ให้ผลตอบแทนในการดำเนินการ - มันไม่ได้ทำอะไรเป็นพิเศษกับwwwวัตถุ แต่มันหมายถึงสิ่งที่พิเศษสำหรับวิธีการที่มีอยู่โดยเฉพาะคำหลักนี้สามารถใช้ได้เฉพาะในวิธีการที่ส่งกลับIEnumerable(หรือIEnumerator) และใช้ เพื่อระบุว่าวัตถุใดที่จะ "คืนค่า" โดยตัวแจงนับเมื่อมีการเรียกใช้MoveNext

มันทำงานได้เพราะคอมไพเลอร์แปลงวิธีทั้งหมดเป็นคลาสแยกซึ่งใช้IEnumerable(หรือIEnumerator) โดยใช้เครื่องสถานะ - ผลสุทธิคือว่าร่างกายของวิธีการที่ตัวเองไม่ได้ดำเนินการจนกว่าจะมีคนระบุผ่านค่าตอบแทน มันจะใช้งานได้กับทุกประเภทไม่มีอะไรพิเศษเกี่ยวกับWWWวิธีการบรรจุที่พิเศษ

ดูเบื้องหลังของคำสำคัญผลตอบแทน C #สำหรับข้อมูลเชิงลึกเพิ่มเติมเกี่ยวกับประเภทของโค้ดคอมไพเลอร์ C # ที่สร้างขึ้นหรือเพียงแค่ทดสอบและตรวจสอบโค้ดด้วยตัวคุณเองโดยใช้IL Spy


อัปเดต:เพื่อชี้แจง

  • เมื่อ Unity เรียก coroutine ที่มีyield returnคำสั่งทั้งหมดที่เกิดขึ้นคือ enumerator ถูกส่งกลับ - ไม่มีเนื้อความของเมธอดใดถูกดำเนินการ ณ จุดนี้
  • ในการรับส่วนของเมธอดเพื่อเรียกใช้ Unity ต้องเรียกMoveNextใช้ตัววนซ้ำเพื่อรับค่าแรกในลำดับ สิ่งนี้ทำให้วิธีการดำเนินการถึงyeild returnคำสั่งแรกที่จุดที่ผู้โทรกลับมาดำเนินการต่อ (และ Unity สันนิษฐานไปเพื่อแสดงส่วนที่เหลือของเฟรม)
  • ตามที่ฉันเข้าใจว่า Unity ปกติแล้วจะเรียกMoveNextวิธีการในตัววนซ้ำแต่ละเฟรมที่ตามมาทำให้วิธีการดำเนินการอีกครั้งถึงyield returnคำสั่งถัดไปหนึ่งครั้งในแต่ละเฟรมจนกว่าจะถึงจุดสิ้นสุดของวิธีการหรือyield breakคำสั่งถึง (ระบุ จุดสิ้นสุดของลำดับ)

บิตเท่านั้นพิเศษที่นี่ (และในคู่ของกรณีอื่น ๆ ) ก็คือความสามัคคีไม่ก้าวหน้า iterator นี้โดยเฉพาะเฟรมถัดไปแทนก็เพียงความก้าวหน้า iterator (ที่ก่อให้เกิดวิธีการที่จะยังคงดำเนินการ) เมื่อการดาวน์โหลดเสร็จสมบูรณ์ แม้ว่าดูเหมือนจะเป็นคลาสYieldInstructionพื้นฐานซึ่งน่าจะมีกลไกทั่วไปสำหรับการส่งสัญญาณไปยัง Unity เมื่อ iterator ควรสูงขึ้นWWWคลาสไม่ปรากฏว่าสืบทอดมาจากคลาสนี้ดังนั้นฉันสามารถสันนิษฐานได้ว่ามีกรณีพิเศษสำหรับ คลาสนี้ในเอ็นจิน Unity

เพื่อให้ชัดเจน - yieldคำหลักไม่ได้ทำอะไรเป็นพิเศษในWWWชั้นเรียน แต่เป็นการจัดการพิเศษที่ Unity มอบให้กับสมาชิกของการแจงนับที่ส่งคืนซึ่งทำให้เกิดลักษณะการทำงานนี้


อัปเดตที่สอง:สำหรับกลไกที่WWWใช้ในการดาวน์โหลดหน้าเว็บแบบอะซิงโครนัสมันอาจใช้วิธีการ HttpWebRequest.BeginGetResponseซึ่งจะใช้ภายในแบบอะซิงโครนัส IOหรืออาจใช้เธรด (โดยเฉพาะการสร้างเธรดเฉพาะ


5
ที่จริงในความสามัคคีบางสิ่งที่พิเศษที่จะเกิดขึ้นกับWWWวัตถุเมื่อมีการให้ผลดูWWW's อ้างอิง
เอริค

9

yieldดูเหมือนว่าส่วนใหญ่จะใช้ใน Unity ในบริบท coroutine อ่านเพิ่มเติมเกี่ยวกับ coroutines และทำไมพวกเขาใช้ C # 's yieldผมขอแนะนำบทความบล็อกนี้: coroutines Unity3D ในรายละเอียด การวิจัยส่วนใหญ่ในคำตอบนี้มาจากบทความนั้น

Coroutines in Unity ใช้สำหรับห่อหุ้มงานที่:

  1. อาจใช้เวลานานกว่าเฟรมที่จะเรนเดอร์ (ดังนั้นจึงทำให้เกิดการชะลอตัว) และ
  2. สามารถแยกจากลูปเกมได้ (เนื่องจากผลลัพธ์ไม่จำเป็นต้องมีในเฟรมปัจจุบัน)

ตัวอย่างของงานประเภทนี้คือการคำนวณเส้นทาง (อีกครั้ง) หรือในกรณีของคำถามของคุณการรับข้อมูลจากเว็บไซต์

ในการตอบคำถามย่อย (ตามลำดับที่แก้ไขเล็กน้อย):

มีวิธีใดบ้างที่ถูกเรียกใช้ (ถ้ามีในคลาส WWW) เลเยอร์ Unity "ส่วนบน" กำลังจับอินสแตนซ์ www และกำลังทำอะไรอยู่หรือไม่

WWWคลาสของ Unity ออกแบบมาเพื่อให้ได้ผลจาก coroutine ตามความคิดเห็นในบทความบล็อกที่ลิงก์ด้านบนบล็อกเก็งกำไรของรหัส (เลเยอร์ "" ส่วนบน ") เกี่ยวกับYieldInstructions จริง ๆ แล้วประกอบด้วยสวิตช์ซึ่งยังตรวจสอบหาWWWs ที่ให้ผล รหัสนี้จะทำให้แน่ใจว่า coroutine จะเสร็จสิ้นโดยอัตโนมัติเมื่อดาวน์โหลดเสร็จตามที่อธิบายในWWWการอ้างอิงของ

ความสามัคคีใช้เธรดหรือไม่

ในกรณีนี้หากต้องการดาวน์โหลดข้อมูล "โดยไม่ปิดกั้นส่วนที่เหลือของเกม": ใช่เป็นไปได้มากที่สุด (และการใช้เธรดจะใช้ในการคลายการบีบอัดข้อมูลที่ดาวน์โหลดตามที่เห็นWWW.threadPriority)


ดี! ฉันเห็นความคิดเห็นaltdevblogaday.com/2011/07/07/unity3d-coroutines-in-detail/และดูเหมือนว่า WWW จะได้รับการดูแลเป็นพิเศษ ดังนั้นฉันต้องการทราบว่าวิธีนี้สำเร็จได้อย่างไรเพื่ออนุญาตให้ดาวน์โหลดในหลาย ๆ เฟรมโดยไม่ต้องใช้เธรด
thyandrecardoso

จุดดี! ฉันคิดว่าจะต้องมีการทำเกลียวในระดับนั้นหลังจากทั้งหมดฉันจะแก้ไขคำตอบของฉันเพื่อสะท้อนให้เห็นว่า
เอริค

1
@thyandardardoso ฉันคิดว่ามันใช้HttpWebRequest.BeginGetResponseหรือสิ่งที่คล้ายกัน แต่คุณสามารถคอมไพล์แอสเซมบลีเพื่อยืนยันสิ่งนี้ถ้ามันสำคัญกับคุณจริงๆ
Justin

สำหรับตอนนี้ฉันแค่รอ "คำอธิบายที่ดีที่สุด" ... จะดีถ้าใครจากทีม Unity dev ให้ "คำตอบที่ถูกต้อง" 8 -) ... ในที่สุดฉันคิดว่าการใช้งานจริงจะไม่เกิดขึ้น ห่างไกลจากที่ให้ไว้ที่นี่แล้ว ... ฉันไม่จำเป็นต้องถอดชุดประกอบและรู้ทั้งหมดนี้อย่างแน่นอน แต่ฉันอาจลองใหม่ในภายหลัง :)
thyandrecardoso

7

น่าเสียดายที่ WWW มีการใช้งานภายในเป็นรหัสเนทีฟซึ่งหมายความว่าเราไม่สามารถดูรหัสได้ จากการทดลองฉันบอกได้เลยว่า

  1. WWWจะไม่ได้มาจากYieldInstructionดังนั้นสิ่งที่เกิดขึ้นเมื่อคุณyieldจะต้องมีการจัดการโดยรหัสกรณีพิเศษ
  2. ฉันไม่เคยหมกมุ่นกับความแตกต่างระหว่าง

    yield return www;

    และ

    while(!www.isDone)
        yield return null;

    ฉันคิดว่านั่นเป็นวิธีที่สมเหตุสมผลที่สุดในการติดตั้งและอาจเป็นสิ่งที่เกิดขึ้นภายใต้ประทุน แต่ฉันไม่รู้แน่

  3. Unity ไม่ได้เริ่มเธรดใหม่สำหรับการดาวน์โหลดอย่างน้อยในบางแพลตฟอร์ม (iOS, webplayer) หรือถ้าเป็นเช่นนั้นมันตั้งWWW.isDoneอยู่บนเธรดหลัก ฉันรู้สิ่งนี้เพราะรหัสนี้:

    while(!www.isDone)
        Thread.Sleep(500);

    ไม่ทำงาน

ฉันไม่คิดว่าคุณจะมีคำตอบที่เฉพาะเจาะจงมากขึ้นได้นอกจากมีคนที่เข้าถึงซอร์สโค้ดของ Unity3d มาที่นี่


ได้. ข้อมูลเชิงลึกที่ดีจริงๆ! ขอบคุณ! แม้ว่าจะไม่เปิดตัวเธรดต่อสิ่งที่เป็นไปได้มากที่สุดที่อาจเกิดขึ้นคือ WWW (หรือเลเยอร์ของเครื่องยนต์) โดยใช้ HttpWebRequest.BeginGetResponse (หรืออะไรทำนองนั้น) ... ใช่ไหม? บางสิ่งบางอย่างไม่สมบูรณ์แบบ async จะต้องเกิดขึ้นไม่ว่าด้วยวิธีใด ... การดาวน์โหลดไม่สามารถ "หยุด" ได้
thyandrecardoso

** ไม่สามารถ "หยุด" ระหว่างเฟรมฉันหมายถึง
thyandrecardoso

2

เนื่องจาก Unity3D ใช้ C # เป็นเครื่องมือการเขียนสคริปต์ของพวกเขาฉันคิดว่ามันเป็นคำหลักที่ได้มาตรฐานซึ่งสร้างไว้ใน C # โดยทั่วไปแล้วมันหมายถึงว่ามันส่งคืนค่าของ www เรียบร้อยแล้วเพื่อให้คุณสามารถดำเนินการต่อในขณะที่การทำซ้ำครั้งต่อไปมันจะคืนค่าต่อไป ฯลฯ ... ผลตอบแทนโดยทั่วไปสร้างเครื่องรัฐและ iterator ในพื้นหลัง


ใช่ฉันคิดว่าฉันมีแนวคิดพื้นฐานเกี่ยวกับคำหลักที่ให้ผลตอบแทน อย่างไรก็ตามสิ่งที่เกิดขึ้นในระหว่างตัวสร้างของ WWW ที่อนุญาตสำหรับ "เครื่องรัฐ" ฉันหมายความว่า "ผลตอบแทนที่ได้รับ www" ดูเหมือนจะไม่เรียกอะไรในชั้น WWW ... เมื่อคุณพูดว่า "มันหมายความว่ามันจะส่งกลับค่าของ www แล้ว" ค่าอินสแตนซ์ www คืออะไร? อินสแตนซ์นั้นจะทำอะไรใน "การทำซ้ำ" ครั้งถัดไป
thyandrecardoso

1
ใน Coroutines Unity การให้ WWW เป็นกรณีพิเศษให้ดูWWWการอ้างอิงของ นอกจากนี้ฉันไม่แน่ใจว่าyieldจะสร้างอะไร บริบทตัววนซ้ำถูกสร้างขึ้นโดยการนำไปใช้IEnumerableหรือใช้เป็นชนิดส่งคืน "กลไกของรัฐ" ก็ปิดเช่นกัน แน่นอนว่ามีรัฐ แต่เพียงอย่างเดียวนั้นไม่ใช่ทรัพย์สินที่เพียงพอใช่ไหม บางทีคุณอาจอธิบายรายละเอียดเกี่ยวกับเรื่องนี้
Eric

1
นี่คือการอ้างอิงที่ดีเกี่ยวกับพฤติกรรม C # ปกติ shadowcoding.blogspot.nl/2009/01/yield-and-c-state-machine.html Yield สร้างโค้ดจำนวนมากเพื่อติดตามตำแหน่งที่อยู่ในตัววนซ้ำ เกี่ยวกับกรณีพิเศษของ Unity ที่ให้ WWW ฉันไม่รู้ แต่ตามเอกสารแล้วมันไม่มีอะไรที่ต้องทำกับคำหลัก C # ปกติมันสับสนมากพวกเขาไม่สามารถทำให้มันเป็นวิธีแบบซิงโครนัสได้
รอยต.
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.