วิธีการออกแบบระบบเล่นซ้ำ


75

ดังนั้นฉันจะออกแบบระบบเล่นซ้ำได้อย่างไร

คุณอาจรู้จักเกมบางเกมเช่น Warcraft 3 หรือ Starcraft ที่คุณสามารถดูเกมได้อีกครั้งหลังจากเล่นไปแล้ว

คุณจบด้วยไฟล์รีเพลย์ที่ค่อนข้างเล็ก ดังนั้นคำถามของฉันคือ:

  • จะบันทึกข้อมูลได้อย่างไร? (รูปแบบกำหนดเองหรือไม่) (ขนาดเล็ก)
  • อะไรจะถูกบันทึกไว้?
  • จะทำให้เป็นเรื่องทั่วไปได้อย่างไรเพื่อให้สามารถใช้ในเกมอื่นเพื่อบันทึกช่วงเวลา (และไม่ใช่ตัวอย่างที่ตรงกันทั้งหมด)
  • ทำให้สามารถส่งต่อและย้อนกลับได้ (WC3 ไม่สามารถย้อนกลับได้เท่าที่ฉันจำได้)

3
แม้ว่าคำตอบด้านล่างจะให้ข้อมูลเชิงลึกที่มีค่ามากมาย แต่ฉันต้องการเน้นย้ำถึงความสำคัญของการพัฒนาเกม / เครื่องมือของคุณเพื่อกำหนดขึ้นสูง ( en.wikipedia.org/wiki/Deterministic_algorithm ) เนื่องจากเป็นสิ่งจำเป็นเพื่อให้บรรลุเป้าหมายของคุณ
Ari Patrick

2
โปรดทราบว่าเอ็นจิ้นฟิสิกส์ไม่ได้กำหนดไว้ล่วงหน้า (Havok อ้างว่าเป็น ... ) ดังนั้นวิธีแก้ปัญหาเพียงเก็บอินพุตและเวลาจะให้ผลลัพธ์ที่แตกต่างกันทุกครั้งหากเกมของคุณใช้ฟิสิกส์
Samaursa

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

4
กำหนดหมายถึงอินพุตเดียวกัน = เอาท์พุทเดียวกัน หากคุณลอยอยู่บนแพลตฟอร์มหนึ่งและเพิ่มเป็นสองเท่าในอีกแพลตฟอร์ม (หรือตัวอย่าง) หรือปิดใช้งานการใช้งานมาตรฐาน IEEE ของคุณโดยอัตโนมัตินั่นหมายความว่าคุณไม่ได้ทำงานด้วยอินพุตเดียวกันไม่ใช่ว่าไม่ได้กำหนดไว้

3
เป็นฉันหรือคำถามนี้ได้รับรางวัลทุกสัปดาห์หรือไม่
The Duck คอมมิวนิสต์

คำตอบ:


39

บทความที่ยอดเยี่ยมนี้ครอบคลุมประเด็นต่าง ๆ มากมาย: http://www.gamasutra.com/view/feature/2029/developing_your_own_replay_system.php

บางสิ่งที่บทความกล่าวถึงและทำได้ดี:

  • เกมของคุณจะต้องถูกกำหนดไว้ล่วงหน้า
  • มันบันทึกสถานะเริ่มต้นของระบบเกมในเฟรมแรกและมีเพียงผู้เล่นที่ป้อนข้อมูลในระหว่างการเล่นเกม
  • quantize inputs เพื่อลดจำนวนของบิต กล่าวคือ เป็นตัวแทนของลอยอยู่ในช่วงต่าง ๆ (เช่น. [0, 1] หรือช่วง [-1, 1] ภายในบิตน้อยต้องป้อนปริมาณ Quantized ต้องได้รับในระหว่างการเล่นเกมจริงเกินไป
  • ใช้บิตเดียวเพื่อตรวจสอบว่ากระแสอินพุตมีข้อมูลใหม่ เนื่องจากกระแสบางส่วนจะไม่เปลี่ยนแปลงบ่อยครั้งการใช้ประโยชน์นี้จึงเป็นการเชื่อมโยงทางชั่วคราวในอินพุต

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

ในแง่ของการทำให้ระบบสามารถนำกลับมาใช้ใหม่ได้ฉันได้สร้างระบบการเล่นซ้ำกับสตรีมอินพุตทั่วไป แต่ยังให้ตะขอเพื่ออนุญาตให้ตรรกะเฉพาะเกมเพื่อป้อนคีย์บอร์ดจอมพล / gamepad / เมาส์ไปยังสตรีมเหล่านี้

หากคุณต้องการกรอย้อนกลับหรือการค้นหาแบบสุ่มคุณสามารถบันทึกจุดตรวจ (เกมเต็มของคุณ) ทุกเฟรม N ควรเลือก N เพื่อลดขนาดไฟล์เล่นซ้ำและตรวจสอบให้แน่ใจว่าเวลาที่เครื่องเล่นต้องรอนั้นสมเหตุสมผลในขณะที่สถานะเล่นซ้ำจนถึงจุดที่เลือก วิธีหนึ่งในการหลีกเลี่ยงปัญหานี้คือเพื่อให้แน่ใจว่าการค้นหาแบบสุ่มสามารถทำได้เฉพาะกับจุดตรวจที่แน่นอนเหล่านี้เท่านั้น การกรอกลับเป็นเรื่องของการตั้งค่าสถานะของเกมเป็นจุดตรวจสอบทันทีก่อนเฟรมที่มีปัญหาจากนั้นเล่นซ้ำอินพุตจนกว่าคุณจะไปที่เฟรมปัจจุบัน อย่างไรก็ตามหาก N มีขนาดใหญ่เกินไปคุณสามารถเอาเฟรมออกมาทุกๆสองสามภาพ วิธีหนึ่งในการทำให้ข้อต่อเหล่านี้ราบรื่นขึ้นคือการแคชเฟรมล่วงหน้าระหว่างจุดตรวจ 2 จุดก่อนหน้านี้แบบอะซิงโครนัสในขณะที่คุณกำลังเล่นเฟรมแบบแคชจากภูมิภาคจุดตรวจสอบปัจจุบัน


หากมี RNG ที่เกี่ยวข้องให้รวมผลลัพธ์ของ RNG ที่กล่าวไว้ในสตรีมด้วย
ratchet freak

1
@ ratchet freak: ด้วยการใช้ PRNG ที่กำหนดไว้ล่วงหน้าคุณจะได้รับโดยการเก็บเฉพาะเมล็ดของมันในระหว่างจุดตรวจ
ไม่ใช่ตัวเลข

22

นอกจากวิธี "ให้แน่ใจว่าการกดแป้นพิมพ์สามารถเล่นซ้ำได้" ซึ่งอาจเป็นเรื่องยากอย่างน่าประหลาดใจคุณสามารถบันทึกสถานะเกมทั้งหมดในทุก ๆ เฟรม ด้วยการบีบอัดที่ชาญฉลาดเพียงเล็กน้อยคุณสามารถบีบลงได้อย่างมีนัยสำคัญ นี่คือวิธีที่ Braid จัดการกับโค้ดย้อนเวลาและทำงานได้ค่อนข้างดี

เนื่องจากคุณจะต้องใช้จุดตรวจสอบสำหรับการกรอกลับใหม่คุณอาจต้องการลองใช้วิธีการง่ายๆก่อนที่จะทำให้สิ่งต่าง ๆ ซับซ้อน


2
+1 ด้วยการบีบอัดที่ชาญฉลาดคุณสามารถลดจำนวนข้อมูลที่คุณต้องการจัดเก็บลงได้ (ตัวอย่างเช่นอย่าเก็บสถานะหากมันไม่ได้เปลี่ยนไปเมื่อเทียบกับสถานะล่าสุดที่คุณเก็บไว้สำหรับวัตถุปัจจุบัน) . ฉันได้ลองกับฟิสิกส์แล้วและมันก็ใช้ได้ดีจริงๆ หากคุณไม่มีฟิสิกส์และไม่ต้องการกรอเกมที่สมบูรณ์ฉันจะใช้วิธีแก้ปัญหาของโจเพียงเพราะมันจะสร้างไฟล์ที่เล็กที่สุดเท่าที่เป็นไปได้ในกรณีนี้หากคุณต้องการย้อนกลับด้วยคุณสามารถเก็บได้เพียงnวินาทีสุดท้ายของ เกม.
Samaursa

@Samaursa - ถ้าคุณใช้ไลบรารีการบีบอัดแบบมาตรฐาน (เช่น gzip) คุณจะได้รับการบีบอัดแบบเดียวกัน (น่าจะดีกว่า) โดยไม่จำเป็นต้องทำสิ่งต่าง ๆ เช่นตรวจสอบด้วยตนเองเพื่อดูว่าสถานะเปลี่ยนแปลงหรือไม่
Justin

2
@ Kragen: ไม่จริงเลย ไลบรารีการบีบอัดมาตรฐานนั้นค่อนข้างดี แต่มักจะไม่สามารถใช้ประโยชน์จากความรู้เฉพาะโดเมนได้ หากคุณสามารถช่วยพวกเขาออกมาได้เล็กน้อยโดยการใส่ข้อมูลที่คล้ายกันและแยกแยะสิ่งที่ไม่ได้เปลี่ยนแปลงจริงๆคุณสามารถกระทืบสิ่งต่าง ๆ ลงได้อย่างมาก
ZorbaTHut

1
@ZorbaTHut ในทางทฤษฎีแล้วใช่ แต่ในทางปฏิบัติแล้วมันคุ้มค่ากับความพยายามจริงๆเหรอ?
Justin

4
ความพยายามนั้นขึ้นอยู่กับว่าคุณมีข้อมูลมากน้อยเพียงใด หากคุณมี RTS ที่มีหน่วยเป็นร้อยเป็นพันอาจเป็นเรื่องสำคัญ หากคุณต้องการจัดเก็บรีเพลย์ในหน่วยความจำอย่าง Braid อาจเป็นเรื่องสำคัญ

21

คุณสามารถดูระบบของคุณราวกับว่ามันประกอบด้วยชุดของรัฐและฟังก์ชั่นที่ฟังก์ชั่นที่f[j]มีการป้อนข้อมูลx[j]เปลี่ยนสถานะของระบบs[j]เข้าสู่สถานะs[j+1]เช่น:

s[j+1] = f[j](s[j], x[j])

สถานะคือคำอธิบายของโลกทั้งใบของคุณ ตำแหน่งของผู้เล่นตำแหน่งของศัตรูคะแนนกระสุนที่เหลือ ฯลฯ ทุกสิ่งที่คุณต้องการในการวาดเฟรมเกมของคุณ

ฟังก์ชั่นคือทุกสิ่งที่อาจส่งผลกระทบต่อโลก การเปลี่ยนเฟรม, ปุ่มกด, แพ็กเก็ตเครือข่าย

อินพุตคือข้อมูลที่ฟังก์ชั่นใช้งาน การเปลี่ยนเฟรมอาจใช้เวลานานนับตั้งแต่เฟรมสุดท้ายผ่านไปการกดปุ่มอาจรวมถึงการกดปุ่มจริงรวมถึงการกดปุ่ม Shift หรือไม่

เพื่อประโยชน์ในการอธิบายนี้ฉันจะทำให้สมมติฐานต่อไปนี้:

ข้อสันนิษฐาน 1:

จำนวนรัฐสำหรับการวิ่งของเกมนั้นมีขนาดใหญ่กว่าจำนวนฟังก์ชั่น คุณอาจมีสถานะเป็นแสน แต่มีฟังก์ชั่นหลายโหลเท่านั้น (การเปลี่ยนเฟรม, ปุ่มกด, แพ็คเก็ตเครือข่าย ฯลฯ ) แน่นอนปริมาณของอินพุตจะต้องเท่ากับจำนวนของรัฐลบหนึ่ง

ข้อสมมติฐาน 2:

ราคาพิเศษ (หน่วยความจำ, ดิสก์) ของการจัดเก็บในสถานะเดียวจะยิ่งใหญ่กว่าการจัดเก็บฟังก์ชั่นและอินพุตของมัน

ข้อสันนิษฐาน 3:

ค่าใช้จ่ายชั่วคราว (เวลา) ของการนำเสนอรัฐจะคล้ายกันหรือเพียงหนึ่งหรือสองคำสั่งของขนาดยาวกว่าการคำนวณฟังก์ชั่นมากกว่ารัฐ

ขึ้นอยู่กับข้อกำหนดของระบบการเล่นซ้ำของคุณมีหลายวิธีในการนำระบบการเล่นซ้ำมาใช้ดังนั้นเราจึงสามารถเริ่มต้นด้วยวิธีที่ง่ายที่สุดได้ ฉันจะทำตัวอย่างเล็ก ๆ โดยใช้เกมหมากรุกบันทึกไว้ในแผ่นกระดาษ

วิธีที่ 1:

s[0]...s[n]ร้านค้า ง่ายมากตรงไปตรงมามาก เนื่องจากข้อสันนิษฐานที่ 2 ทำให้ราคาพิเศษของสิ่งนี้ค่อนข้างสูง

สำหรับหมากรุกสิ่งนี้จะสำเร็จได้โดยการวาดกระดานทั้งหมดสำหรับแต่ละท่า

วิธีที่ 2:

หากคุณต้องการเล่นซ้ำไปข้างหน้าคุณสามารถจัดเก็บs[0]แล้วเก็บf[0]...f[n-1](จำไว้ว่านี่เป็นเพียงชื่อของฟังก์ชั่น id) และx[0]...x[n-1](สิ่งที่ป้อนเข้าสำหรับแต่ละฟังก์ชั่นเหล่านี้) ในการเล่นซ้ำคุณเพียงเริ่มต้นด้วยs[0]และคำนวณ

s[1] = f[0](s[0], x[0])
s[2] = f[1](s[1], x[1])

และอื่น ๆ ...

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

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

หากคุณใช้ตัวเลขสุ่มหลอกคุณสามารถเก็บหมายเลขที่สร้างขึ้นเป็นส่วนหนึ่งของการป้อนข้อมูลของคุณxหรือเก็บสถานะของฟังก์ชั่น PRNG เป็นส่วนหนึ่งของรัฐของคุณและการดำเนินการในฐานะที่เป็นส่วนหนึ่งของฟังก์ชั่นsf

สำหรับหมากรุกสิ่งนี้จะสำเร็จได้โดยการวาดกระดานเริ่มต้น (ซึ่งเป็นที่รู้จัก) แล้วอธิบายแต่ละท่าว่าชิ้นส่วนไหนไปไหน นี่คือวิธีที่พวกเขาทำได้จริงโดยวิธี

วิธีที่ 3:

ตอนนี้คุณน่าจะต้องการค้นหาการเล่นซ้ำของคุณอีกครั้ง นั่นคือการคำนวณการโดยพลการs[n] nโดยใช้วิธีที่ 2 คุณจะต้องคำนวณs[0]...s[n-1]ก่อนจึงจะสามารถคำนวณs[n]ซึ่งตามสมมติฐานที่ 2 อาจช้า

ในการดำเนินการนี้วิธีที่ 3 เป็นลักษณะทั่วไปของวิธีการที่ 1 และที่ 2: การจัดเก็บf[0]...f[n-1]และx[0]...x[n-1]เช่นเดียวกับวิธีที่ 2 แต่ยังร้านค้าs[j]สำหรับทุกสำหรับให้คงที่j % Q == 0 กล่าวง่ายๆคือQคุณเก็บที่คั่นหนังสือไว้ที่ทุกQรัฐ ตัวอย่างเช่นสำหรับQ == 100คุณจัดเก็บs[0], s[100], s[200]...

ในการคำนวณs[n]สำหรับพลnแรกคุณโหลดเก็บไว้ก่อนหน้าs[floor(n/Q)]แล้วคำนวณฟังก์ชั่นทั้งหมดจากการfloor(n/Q) nอย่างมากคุณจะคำนวณQฟังก์ชัน ค่าที่น้อยกว่าของQการคำนวณได้เร็วขึ้น แต่ใช้พื้นที่มากขึ้นในขณะที่ค่าที่มากขึ้นของการQใช้พื้นที่น้อยลง แต่ใช้เวลาในการคำนวณนานกว่า

วิธีที่ 3 ด้วยQ==1จะเหมือนกับวิธีที่ 1 ในขณะที่วิธีที่ 3 ด้วยQ==infจะเหมือนกับวิธีที่ 2

สำหรับหมากรุกสิ่งนี้จะสำเร็จได้โดยการวาดทุกการเคลื่อนไหวและหนึ่งในทุก ๆ 10 บอร์ด (สำหรับQ==10)

วิธีที่ 4:

หากคุณต้องการที่จะกลับ replay คุณสามารถทำให้การเปลี่ยนแปลงเล็ก ๆ ของวิธีที่ 3 สมมติQ==100และคุณต้องการที่จะคำนวณs[150]ผ่านs[90]ในสิ่งที่ตรงกันข้าม ด้วยวิธีที่ไม่มีการแก้ไข 3 คุณจะต้องทำการคำนวณ 50 ครั้งเพื่อรับs[150]และอีก 49 การคำนวณเพื่อให้ได้รับs[149]และต่อไป แต่เนื่องจากคุณคำนวณแล้วว่าs[149]จะได้รับs[150]คุณสามารถสร้างแคชs[100]...s[150]เมื่อคุณคำนวณs[150]เป็นครั้งแรกและจากนั้นคุณก็อยู่s[149]ในแคชเมื่อคุณต้องการที่จะแสดง

คุณจำเป็นที่จะงอกใหม่แคชทุกครั้งที่คุณจำเป็นต้องคำนวณs[j]สำหรับสำหรับการใดก็ตามj==(k*Q)-1 kเวลานี้การเพิ่มQจะส่งผลให้มีขนาดเล็กลง (สำหรับแคชเท่านั้น) แต่เวลาจะนานขึ้น (สำหรับการสร้างแคชใหม่) Qสามารถคำนวณค่าที่เหมาะสมที่สุดได้หากคุณทราบขนาดและเวลาที่ต้องใช้ในการคำนวณสถานะและฟังก์ชัน

สำหรับหมากรุกสิ่งนี้จะสำเร็จได้โดยการวาดทุกการเคลื่อนไหวรวมถึงหนึ่งในทุก ๆ 10 บอร์ด (สำหรับQ==10) แต่ก็ต้องวาดในกระดาษอีกแผ่นหนึ่งซึ่งเป็นกระดาน 10 อันดับสุดท้ายที่คุณคำนวณ

วิธีที่ 5:

หากสถานะนั้นใช้พื้นที่มากเกินไปหรือฟังก์ชั่นใช้เวลามากเกินไปคุณสามารถสร้างโซลูชันที่ใช้การเล่นซ้ำแบบย้อนกลับ (ไม่ใช่ของปลอม) ในการทำสิ่งนี้คุณต้องสร้างฟังก์ชั่นย้อนกลับสำหรับแต่ละฟังก์ชั่นที่คุณมี อย่างไรก็ตามสิ่งนี้ต้องการให้แต่ละฟังก์ชั่นของคุณเป็นการฉีด หากสิ่งนี้เป็นไปได้ดังนั้นสำหรับการf'แสดงการกลับกันของฟังก์ชันการfคำนวณs[j-1]นั้นง่ายมาก

s[j-1] = f'[j-1](s[j], x[j-1])

โปรดทราบว่าในที่นี่ฟังก์ชั่นและใส่ทั้งสองไม่ได้j-1 jฟังก์ชันและอินพุตเดียวกันนี้จะเป็นฟังก์ชันที่คุณจะใช้หากคุณกำลังคำนวณ

s[j] = f[j-1](s[j-1], x[j-1])

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

วิธีนี้ตามที่เป็นอยู่สามารถย้อนกลับคำนวณแต่ถ้าคุณมีs[j-1] s[j]ซึ่งหมายความว่าคุณสามารถดูการเล่นซ้ำย้อนหลังได้โดยเริ่มจากจุดที่คุณตัดสินใจเล่นซ้ำย้อนหลัง หากคุณต้องการเล่นซ้ำย้อนหลังจากจุดที่กำหนดคุณต้องผสมสิ่งนี้กับวิธีที่ 4

สำหรับหมากรุกสิ่งนี้ไม่สามารถนำไปใช้ได้เนื่องจากมีบอร์ดที่กำหนดและการย้ายครั้งก่อนคุณสามารถทราบได้ว่าชิ้นส่วนใดถูกย้าย แต่ไม่ใช่ตำแหน่งที่ย้ายมา

วิธีที่ 6:

สุดท้ายถ้าคุณไม่สามารถรับประกันได้ว่าการทำงานทั้งหมดของคุณเป็นการฉีดคุณสามารถทำเคล็ดลับเล็ก ๆ ได้ แทนที่จะให้แต่ละฟังก์ชั่นคืนค่าสถานะใหม่คุณยังสามารถให้คืนค่าข้อมูลที่ถูกทิ้งเช่น:

s[j+1], r[j] = f[j](s[j], x[j])

r[j]ข้อมูลที่ถูกทิ้งอยู่ที่ไหน จากนั้นสร้างฟังก์ชันผกผันของคุณเพื่อให้พวกเขานำข้อมูลที่ถูกทิ้งไปเช่น:

s[j] = f'[j](s[j+1], x[j], r[j])

นอกจากนี้f[j]และx[j]คุณยังต้องจัดเก็บr[j]สำหรับแต่ละฟังก์ชั่น อีกครั้งหากคุณต้องการที่จะหาคุณจะต้องเก็บที่คั่นหนังสือเช่นด้วยวิธีที่ 4

สำหรับหมากรุกสิ่งนี้จะเหมือนกับวิธีที่ 2 แต่ต่างจากวิธีที่ 2 ซึ่งบอกได้ว่าชิ้นส่วนไหนจะไปที่ไหนคุณต้องเก็บว่าแต่ละชิ้นมาจากไหน

การดำเนินงาน:

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

แทนที่จะเก็บสถานะเกมทั้งหมดคุณสามารถเก็บขั้นต่ำเปล่าที่คุณต้องการวาดสถานะที่กำหนดและจัดลำดับข้อมูลนี้เป็นระยะเวลาคงที่ สถานะของคุณจะเป็นอนุกรมเหล่านี้และการป้อนข้อมูลของคุณจะแตกต่างระหว่างสองอนุกรม สิ่งสำคัญสำหรับการทำงานนี้คือการทำให้เป็นอันดับควรเปลี่ยนเพียงเล็กน้อยหากรัฐโลกเปลี่ยนไปเล็กน้อยเช่นกัน ความแตกต่างนี้สามารถเปลี่ยนแปลงได้อย่างสมบูรณ์ดังนั้นการใช้วิธีที่ 5 กับบุ๊กมาร์กจึงเป็นไปได้มาก

ฉันได้เห็นสิ่งนี้ถูกนำไปใช้ในเกมสำคัญบางเกมซึ่งส่วนใหญ่ใช้สำหรับเล่นซ้ำข้อมูลล่าสุดเมื่อมีเหตุการณ์ (เกิดขึ้นในเฟรมต่อวินาทีหรือมีคะแนนในเกมกีฬา)

ฉันหวังว่าคำอธิบายนี้จะไม่น่าเบื่อเกินไป

¹นี่ไม่ได้หมายความว่าบางโปรแกรมทำหน้าที่เหมือนไม่ได้กำหนดไว้ (เช่น MS Windows ^^) ตอนนี้อย่างจริงจังหากคุณสามารถสร้างโปรแกรมที่ไม่ได้กำหนดค่าไว้ในคอมพิวเตอร์ที่กำหนดขึ้นมาได้คุณมั่นใจได้เลยว่าคุณจะได้รับรางวัลเหรียญฟิลด์รางวัลทัวริงและอาจเป็นรางวัลออสการ์และแกรมมี่สำหรับทุกสิ่งที่คุ้มค่า


ใน "โปรแกรมคอมพิวเตอร์ทั้งหมดเป็นแบบ DETERMINISTIC" คุณไม่สนใจที่จะพิจารณาโปรแกรมที่ต้องใช้เธรด ในขณะที่เธรดส่วนใหญ่จะใช้สำหรับการโหลดทรัพยากรหรือเพื่อแยกการวนรอบการเรนเดอร์มีข้อยกเว้นและในตอนนั้นคุณอาจไม่สามารถอ้างสิทธิ์ในระดับที่แท้จริงได้อีกเว้นแต่คุณจะเข้มงวดเกี่ยวกับการกำหนดระดับ กลไกการล็อคเพียงอย่างเดียวนั้นไม่เพียงพอ คุณจะไม่สามารถแบ่งปันข้อมูลที่ไม่แน่นอนใด ๆ โดยไม่ต้องทำงานพิเศษเพิ่มเติม ในหลาย ๆ สถานการณ์เกมไม่ต้องการความเข้มงวดในระดับนั้นเพื่อตัวของมันเอง แต่ทำได้สำหรับสิ่งต่าง ๆ เช่นรีเพลย์
krdluzni

1
@krdluzni การทำเกลียวการขนานและการสุ่มตัวเลขจากแหล่งสุ่มที่แท้จริงไม่ได้ทำให้โปรแกรมไม่ได้ถูกกำหนดไว้ การกำหนดเวลาของเธรดการหยุดชะงักหน่วยความจำที่ไม่ได้กำหนดค่าเริ่มต้นและแม้แต่สภาวะการแข่งขันเป็นเพียงอินพุตเพิ่มเติมที่โปรแกรมของคุณใช้ คุณเลือกที่จะทิ้งอินพุตเหล่านี้หรือไม่ได้พิจารณาสิ่งเหล่านั้นเลย (ไม่ว่าจะด้วยเหตุผลใดก็ตาม) จะไม่ส่งผลกระทบต่อความจริงที่ว่าโปรแกรมของคุณจะทำงานเหมือนกันทุกประการ "non-deterministic" เป็นคำศัพท์ทางวิทยาศาสตร์คอมพิวเตอร์ที่แม่นยำมากดังนั้นโปรดหลีกเลี่ยงการใช้ถ้าคุณไม่รู้ว่ามันหมายถึงอะไร

@ ออสการ์ (อาจค่อนข้างสั้น, ยุ่ง, อาจแก้ไขในภายหลัง): แม้ว่าในบางแง่มุมทางทฤษฎีที่เข้มงวดคุณสามารถอ้างการกำหนดเวลาของเธรดและอื่น ๆ เป็นอินพุตนี่ไม่ได้มีประโยชน์ในแง่ปฏิบัติใด ๆ เนื่องจากพวกเขาไม่สามารถสังเกตได้ ตั้งโปรแกรมเองหรือควบคุมโดยผู้พัฒนา นอกจากนี้โปรแกรมที่ไม่ได้ถูกกำหนดอย่างมีนัยสำคัญจะแตกต่างกันอย่างมีนัยสำคัญมันเป็นที่ไม่ได้กำหนดไว้ (ในแง่ของเครื่องรัฐ) ฉันเข้าใจความหมายของคำ ฉันหวังว่าพวกเขาจะเลือกอย่างอื่นแทนที่จะโหลดคำที่มีอยู่มากเกินไป
krdluzni

@krdluzni จุดของฉันในการออกแบบระบบการเล่นซ้ำที่มีองค์ประกอบที่คาดเดาไม่ได้เช่นการกำหนดเวลาของเธรด (หากพวกเขามีผลต่อความสามารถในการคำนวณการเล่นซ้ำอย่างถูกต้อง) คือการปฏิบัติต่อพวกเขาเหมือนกับแหล่งอินพุตอื่น ๆ ฉันไม่เห็นใครก็ตามที่บ่นว่าโปรแกรมนั้นเป็น "ที่ไม่สามารถกำหนดได้" เพราะมันใช้อินพุตของผู้ใช้ที่คาดเดาไม่ได้อย่างสมบูรณ์ สำหรับเทอมนั้นมันไม่ถูกต้องและสับสน ฉันอยากให้พวกเขาใช้บางอย่างเช่น "คาดเดาไม่ได้ในทางปฏิบัติ" หรืออะไรทำนองนั้น และไม่เป็นไปไม่ได้เลยตรวจสอบการดีบักการเล่นซ้ำของ VMWare

9

สิ่งหนึ่งที่คำตอบอื่น ๆ ยังไม่ครอบคลุมถึงอันตรายของการลอยตัว คุณไม่สามารถสร้างแอปพลิเคชันที่กำหนดอย่างสมบูรณ์โดยใช้โฟลท

การใช้ลอยคุณสามารถมีระบบกำหนดอย่างสมบูรณ์ แต่เฉพาะในกรณีที่:

  • ใช้ไบนารีเดียวกัน
  • ใช้ CPU เดียวกันทั้งหมด

นี่เป็นเพราะการเป็นตัวแทนภายในของการลอยตัวนั้นแตกต่างกันไปจากซีพียูหนึ่งไปยังอีกซีพียู - มากที่สุดระหว่าง AMD และ Intel CPU ตราบใดที่ค่าเหล่านี้อยู่ในการลงทะเบียน FPU พวกเขาจะมีความแม่นยำมากกว่าที่พวกเขาดูเหมือนจากด้าน C ดังนั้นการคำนวณระดับกลางใด ๆ จะดำเนินการด้วยความแม่นยำสูงกว่า

มันค่อนข้างชัดเจนว่าสิ่งนี้จะส่งผลกระทบต่อ AMD vs intel อย่างไร - ตัวอย่างหนึ่งใช้ 80 บิตลอยและ 64 อื่น ๆ - แต่ทำไมความต้องการไบนารีเดียวกัน

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

คุณสามารถช่วยได้โดยการตั้งค่า_control87 () / _ controlfp ()เพื่อใช้ความแม่นยำที่ต่ำที่สุด อย่างไรก็ตามห้องสมุดบางแห่งอาจสัมผัสเช่นนี้ (อย่างน้อยก็มีเวอร์ชัน d3d บางส่วน)


3
ด้วย GCC คุณสามารถใช้ -ffloat-store เพื่อบังคับค่าออกจากการลงทะเบียนและตัดให้เหลือความแม่นยำ 32/64 บิตโดยไม่ต้องกังวลเกี่ยวกับไลบรารีอื่น ๆ ที่ยุ่งกับแฟล็กควบคุมของคุณ เห็นได้ชัดว่าสิ่งนี้จะส่งผลเสียต่อความเร็วของคุณ (แต่จะให้ปริมาณอื่น ๆ )

8

บันทึกสถานะเริ่มต้นของตัวสร้างหมายเลขสุ่มของคุณ จากนั้นบันทึก, ประทับเวลา, อินพุตแต่ละรายการ (เมาส์, คีย์บอร์ด, เครือข่าย, อะไรก็ตาม) หากคุณมีเกมเครือข่ายคุณอาจมีสิ่งนี้อยู่แล้ว

ตั้งค่า RNG อีกครั้งและเล่นอินพุต แค่นั้นแหละ.

สิ่งนี้ไม่ได้แก้ไขซ้ำซึ่งไม่มีวิธีแก้ปัญหาทั่วไปอื่น ๆ นอกเหนือจากการเล่นตั้งแต่ต้นโดยเร็วที่สุดเท่าที่จะทำได้ คุณสามารถปรับปรุงประสิทธิภาพสำหรับสิ่งนี้ได้ด้วยการตรวจสอบสถานะเกมทั้งหมดทุก ๆ X วินาทีจากนั้นคุณจะต้องเล่นซ้ำหลาย ๆ ครั้ง แต่สถานะเกมทั้งหมดอาจมีราคาแพงเกินไปที่จะคว้า

รูปแบบไฟล์ไม่สำคัญ แต่เอ็นจิ้นส่วนใหญ่มีวิธีการเรียงลำดับคำสั่งและสถานะไว้แล้ว - สำหรับเครือข่ายการบันทึกหรืออะไรก็ตาม เพียงแค่ใช้มัน


4

ฉันจะโหวตให้กับการเล่นซ้ำแบบกำหนดขึ้นใหม่ มันง่ายกว่าและมีข้อผิดพลาดน้อยกว่าในการประหยัดสถานะของทุกเอนทิตีทุก 1 / Nth ของวินาที

บันทึกสิ่งที่คุณต้องการแสดงในการเล่น - ถ้าเป็นเพียงตำแหน่งและส่วนหัวก็ดีถ้าคุณต้องการแสดงสถิติบันทึกด้วยเช่นกัน แต่โดยทั่วไปแล้วประหยัดให้น้อยที่สุด

ปรับแต่งการเข้ารหัส ใช้ให้น้อยที่สุดสำหรับทุกสิ่ง การเล่นซ้ำไม่จำเป็นต้องสมบูรณ์แบบตราบใดที่ดูดีพอ แม้ว่าคุณจะใช้ float สำหรับพูดหัวเรื่องคุณสามารถบันทึกเป็นไบต์และรับ 256 ค่าที่เป็นไปได้ (ความแม่นยำ1.4º) นั่นอาจเพียงพอหรือมากเกินไปสำหรับปัญหาเฉพาะของคุณ

ใช้การเข้ารหัสเดลต้า ยกเว้นกรณีที่หน่วยงานของคุณส่งผ่านทางไกล (และถ้าเป็นเช่นนั้นให้ทำกรณีแยกต่างหาก) เข้ารหัสตำแหน่งเป็นความแตกต่างระหว่างตำแหน่งใหม่และตำแหน่งเก่า - สำหรับการเคลื่อนไหวระยะสั้นคุณสามารถออกไปได้ด้วยบิตน้อยกว่าที่คุณต้องการสำหรับตำแหน่งเต็ม .

หากคุณต้องการกรอย้อนกลับง่าย ๆ ให้เพิ่มคีย์เฟรม (ข้อมูลเต็มไม่มีเดลตา) ทุก ๆ เฟรม N ด้วยวิธีนี้คุณจะได้รับความแม่นยำที่ลดลงสำหรับเดลตาและค่าอื่น ๆ ข้อผิดพลาดในการปัดเศษจะไม่เป็นปัญหาหากคุณรีเซ็ตเป็นค่า "จริง" เป็นระยะ

ในที่สุด gzip ทุกสิ่ง :)


1
ขึ้นอยู่กับประเภทของเกม
Jari Komppa

ฉันจะระมัดระวังอย่างมากกับคำสั่งนี้ โดยเฉพาะอย่างยิ่งสำหรับโครงการขนาดใหญ่ที่มีการพึ่งพาของบุคคลที่สามที่ช่วยประหยัดรัฐเป็นไปไม่ได้ ในขณะที่การรีเซ็ตและเล่นซ้ำอินพุตเป็นไปได้เสมอ
TomSmartBishop

2

มันเป็นเรื่องยาก. ก่อนอื่นให้อ่าน Jari Komppa ก่อนอื่นเลย

การเล่นซ้ำบนคอมพิวเตอร์ของฉันอาจไม่ทำงานบนคอมพิวเตอร์ของคุณเนื่องจากผลลัพธ์ลอยนั้นแตกต่างกันเล็กน้อย มันเป็นเรื่องใหญ่

แต่หลังจากนั้นถ้าคุณมีตัวเลขสุ่มคือการเก็บค่าเมล็ดในการเล่นซ้ำ จากนั้นโหลดสถานะเริ่มต้นทั้งหมดและตั้งหมายเลขสุ่มเป็นเมล็ดนั้น จากที่นั่นคุณสามารถบันทึกสถานะคีย์ / เมาส์ปัจจุบันและระยะเวลาที่เคยเป็นมา จากนั้นเรียกใช้เหตุการณ์ทั้งหมดโดยใช้สิ่งนั้นเป็นอินพุต

ในการข้ามไฟล์ต่างๆ (ซึ่งยากกว่ามาก) คุณจะต้องทิ้ง THE MEMORY เช่นเดียวกับที่ทุกหน่วยเงินเงินระยะเวลาผ่านไปทุกสถานะของเกม จากนั้นส่งต่ออย่างรวดเร็ว แต่เล่นซ้ำทุกอย่างยกเว้นการเรนเดอร์เสียง ฯลฯ จนกว่าคุณจะไปถึงปลายทางเวลาที่คุณต้องการ สิ่งนี้อาจเกิดขึ้นทุกนาทีหรือ 5 นาทีขึ้นอยู่กับความเร็วในการส่งต่อ

ประเด็นหลักคือ - การจัดการกับตัวเลขสุ่ม - การคัดลอกอินพุต (ผู้เล่น (s) และผู้เล่นระยะไกล) - สถานะการทุ่มตลาดสำหรับการกระโดดไปรอบ ๆ ไฟล์และ ... - มีชั้นลอยไม่ทำลายสิ่ง (ใช่ฉันต้องตะโกน)


2

ฉันค่อนข้างแปลกใจที่ไม่มีใครพูดถึงตัวเลือกนี้ แต่ถ้าเกมของคุณมีองค์ประกอบแบบผู้เล่นหลายคนคุณอาจทำงานหนักมากมายที่จำเป็นสำหรับคุณลักษณะนี้แล้ว ท้ายที่สุดสิ่งที่หลายคน แต่ความพยายามที่จะเล่นการเคลื่อนไหวของคนอื่นในเวลาที่แตกต่างกัน (เล็กน้อย) บนคอมพิวเตอร์ของคุณเอง?

สิ่งนี้ยังช่วยให้คุณได้รับประโยชน์จากขนาดไฟล์เล็กลงเป็นผลข้างเคียงอีกครั้งโดยสมมติว่าคุณทำงานกับรหัสเครือข่ายที่ใช้แบนด์วิดท์ได้ง่าย

ในหลาย ๆ ทางมันรวมทั้งตัวเลือก "เป็นอันตรายอย่างยิ่ง" และ "เก็บบันทึกทุกอย่าง" คุณจะยังคงต้องมีระดับ - ถ้าการเล่นซ้ำของคุณเป็นบอทที่เล่นเกมอีกครั้งในลักษณะที่คุณเล่นในตอนแรกไม่ว่าการกระทำใดที่พวกเขาทำที่สามารถมีผลลัพธ์แบบสุ่มจำเป็นต้องได้ผลลัพธ์เดียวกัน

รูปแบบข้อมูลอาจง่ายพอ ๆ กับการถ่ายโอนข้อมูลของเครือข่าย แต่ฉันคิดว่ามันจะไม่เจ็บปวดในการทำความสะอาดสักเล็กน้อย (คุณไม่ต้องกังวลกับความล่าช้าในการเล่นซ้ำอีกครั้งหลังจากทั้งหมด) คุณสามารถเล่นซ้ำได้เพียงบางส่วนของเกมโดยใช้กลไกจุดตรวจสอบที่คนอื่นพูดถึง - โดยทั่วไปเกมแบบผู้เล่นหลายคนจะส่งสถานะการอัพเดทเกมแบบเต็มทุกครั้งดังนั้นคุณอาจได้ทำงานนี้อีกครั้ง


0

ในการรับไฟล์รีเพลย์ที่เล็กที่สุดที่เป็นไปได้คุณจะต้องตรวจสอบให้แน่ใจว่าเกมของคุณถูกกำหนดไว้แล้ว โดยปกติแล้วสิ่งนี้จะเกี่ยวข้องกับการดูตัวสร้างตัวเลขสุ่มของคุณและดูว่ามันถูกใช้ที่ไหนในตรรกะของเกม

คุณจะต้องมีตรรกะของเกม RNG และ RNG อย่างอื่นสำหรับสิ่งต่างๆเช่น GUI, เอฟเฟกต์ของอนุภาค, เสียง เมื่อคุณทำสิ่งนี้เสร็จแล้วคุณต้องบันทึกสถานะเริ่มต้นของตรรกะเกม RNG จากนั้นคำสั่งเกมของผู้เล่นทุกคนในทุกเฟรม

สำหรับหลาย ๆ เกมจะมีระดับของ abstraction ระหว่างอินพุตและตรรกะของเกมที่อินพุตถูกเปลี่ยนเป็นคำสั่ง ตัวอย่างเช่นการกดปุ่ม A บนตัวควบคุมจะส่งผลให้คำสั่งดิจิตอล "กระโดด" ถูกตั้งค่าเป็นจริงและตรรกะของเกมจะตอบสนองต่อคำสั่งโดยไม่ตรวจสอบคอนโทรลเลอร์โดยตรง ด้วยการทำเช่นนี้คุณจะต้องบันทึกคำสั่งที่ส่งผลต่อตรรกะของเกม (ไม่จำเป็นต้องบันทึกคำสั่ง "หยุดชั่วคราว") และส่วนใหญ่ข้อมูลนี้จะมีขนาดเล็กกว่าการบันทึกข้อมูลคอนโทรลเลอร์ คุณไม่ต้องกังวลเกี่ยวกับการบันทึกสถานะของรูปแบบการควบคุมในกรณีที่ผู้เล่นตัดสินใจที่จะทำการแมปปุ่ม

การกรอกลับเป็นปัญหาที่ยากโดยใช้วิธีการกำหนดค่าและนอกเหนือจากการใช้สแนปชอตของสถานะเกมและการส่งต่ออย่างรวดเร็วไปยังจุดที่คุณต้องการดูมีไม่มากที่คุณสามารถทำได้นอกเหนือจากการบันทึกสถานะเกมทั้งหมดในแต่ละเฟรม

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

อาจเป็นส่วนที่สำคัญที่สุดเพียงอย่างเดียวของการเขียนระบบการเล่นซ้ำซึ่งขึ้นอยู่กับระดับคือการบันทึกสตรีมข้อมูลดีบั๊ก สตรีมการดีบักนี้มีสแน็ปช็อตของข้อมูลให้มากที่สุดเท่าที่เป็นไปได้ในแต่ละเฟรม (เมล็ด RNG, การแปลงเอนทิตี, ภาพเคลื่อนไหวและอื่น ๆ ) และสามารถทดสอบกระแสการดีบักที่บันทึกไว้กับสถานะของเกมในระหว่างการรีเพลย์ สิ่งนี้จะช่วยให้คุณทราบได้อย่างรวดเร็วว่าไม่ตรงกันในตอนท้ายของเฟรมใด ๆ วิธีนี้จะช่วยประหยัดเวลานับไม่ถ้วนที่ต้องการถอนผมออกจากข้อบกพร่องที่ไม่ทราบสาเหตุ บางสิ่งที่ง่ายเหมือนตัวแปรที่ไม่มีการกำหนดค่าเริ่มต้นจะทำให้ทุกอย่างสับสนในชั่วโมงที่ 11

หมายเหตุ: หากเกมของคุณเกี่ยวข้องกับการสตรีมเนื้อหาแบบไดนามิกหรือคุณมีตรรกะของเกมในหลายเธรดหรือหลายคอร์ ... ขอให้โชคดี


0

ในการเปิดใช้งานการบันทึกและการกรอกลับให้บันทึกกิจกรรมทั้งหมด (สร้างโดยผู้ใช้สร้างตัวจับเวลาสร้างการสื่อสาร, ... )

สำหรับเวลาบันทึกเหตุการณ์แต่ละเหตุการณ์ของสิ่งที่เปลี่ยนแปลงค่าก่อนหน้าค่าใหม่

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

ข้อมูลที่บันทึกไว้เป็นรายการการเปลี่ยนแปลง
สามารถบันทึกการเปลี่ยนแปลงในรูปแบบต่าง ๆ (ไบนารี, xml, ... )
การเปลี่ยนแปลงประกอบด้วยรหัสเอนทิตีชื่อคุณสมบัติค่าเก่าค่าใหม่

ตรวจสอบให้แน่ใจว่าระบบของคุณสามารถเล่นการเปลี่ยนแปลงเหล่านี้ได้ (เข้าถึงเอนทิตีที่ต้องการเปลี่ยนคุณสมบัติที่ต้องการส่งต่อไปยังสถานะใหม่หรือย้อนกลับสู่สถานะเดิม)

ตัวอย่าง:

  • เวลาจาก start = t1, เอนทิตี = ผู้เล่น 1, คุณสมบัติ = ตำแหน่ง, เปลี่ยนจาก a เป็น b
  • เวลาจาก start = t1, เอนทิตี = ระบบ, คุณสมบัติ = โหมดเกม, เปลี่ยนจาก c เป็น d
  • เวลาจาก start = t2, เอนทิตี = ผู้เล่น 2, คุณสมบัติ = สถานะ, เปลี่ยนจาก e เป็น f
  • ในการเปิดใช้งานกรอถอยหลัง / กรอไปข้างหน้าเร็วขึ้นหรือบันทึกเฉพาะช่วงเวลาที่แน่นอน
    เฟรมคีย์จำเป็น - ถ้าบันทึกตลอดเวลาทุกคราวจากนั้นบันทึกสถานะเกมทั้งหมด
    หากบันทึกเฉพาะช่วงเวลาที่แน่นอนที่จุดเริ่มต้นให้บันทึกสถานะเริ่มต้น


    -1

    หากคุณต้องการแนวคิดเกี่ยวกับวิธีการนำระบบการเล่นซ้ำกลับมาใช้ใหม่ให้ค้นหา google เพื่อหาวิธีใช้การเลิกทำ / ทำซ้ำในแอปพลิเคชัน อาจเห็นได้ชัดสำหรับบางคน แต่อาจจะไม่ได้ทั้งหมดว่าการเลิกทำ / ทำซ้ำเป็นแนวคิดเหมือนกับการเล่นเกมซ้ำ เป็นเพียงกรณีพิเศษที่คุณสามารถย้อนกลับได้และขึ้นอยู่กับแอปพลิเคชันโดยค้นหาจากเวลาที่กำหนด

    คุณจะเห็นว่าไม่มีใครใช้งานการเลิกทำ / ทำซ้ำบ่นเกี่ยวกับตัวแปรที่กำหนด / ไม่ได้กำหนดตัวแปรลอยหรือซีพียูเฉพาะ


    การเลิกทำ / ทำซ้ำเกิดขึ้นในแอปพลิเคชั่นที่เป็นตัวกำหนดพื้นฐาน, เป็นตัวขับเคลื่อนเหตุการณ์, และสถานะของแสง (เช่นสถานะของเอกสารเวิร์ดโปรเซสเซอร์เป็นเพียงข้อความและการเลือกไม่ใช่โครงร่างทั้งหมดซึ่งสามารถคำนวณใหม่ได้)

    เห็นได้ชัดว่าคุณไม่เคยใช้แอปพลิเคชัน CAD / CAM ซอฟต์แวร์การออกแบบวงจรซอฟต์แวร์ติดตามการเคลื่อนไหวหรือแอปพลิเคชันใด ๆ ที่มีการเลิกทำ / ทำซ้ำที่ซับซ้อนกว่าตัวประมวลผลคำ ฉันไม่ได้บอกว่ารหัสสำหรับการเลิกทำ / ทำซ้ำสามารถคัดลอกเพื่อเล่นซ้ำในเกมเพียงแค่ว่ามันเป็นแนวคิดเดียวกัน (บันทึกรัฐและเล่นซ้ำในภายหลัง) อย่างไรก็ตามโครงสร้างข้อมูลหลักไม่ใช่คิว แต่เป็นสแต็ก
    โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
    Licensed under cc by-sa 3.0 with attribution required.