ในการพัฒนาเกม คุณมักจะต้องผูกบางสิ่งเข้ากับการสุ่ม: Unity มีการสุ่มของตัวเองสำหรับสิ่งนี้ และในทางกลับกันก็มี System.Random กาลครั้งหนึ่ง ในโครงการหนึ่ง ฉันรู้สึกว่าทั้งสองสามารถทำงานได้แตกต่างกัน (แม้ว่าทั้งสองควรจะมีการกระจายที่เท่ากันก็ตาม)
จากนั้นพวกเขาไม่ได้ลงรายละเอียด - แค่การเปลี่ยนไปใช้ System.Random ก็เพียงพอแล้วที่จะแก้ไขปัญหาทั้งหมด ตอนนี้เราตัดสินใจที่จะดูรายละเอียดเพิ่มเติมและดำเนินการวิจัยเล็กๆ น้อยๆ ว่า RNG “ลำเอียง” หรือคาดการณ์ได้เป็นอย่างไร และอันไหนที่จะเลือก ยิ่งกว่านั้นฉันเคยได้ยินความคิดเห็นที่ขัดแย้งกันเกี่ยวกับ "ความซื่อสัตย์" ของพวกเขามากกว่าหนึ่งครั้ง - ลองพิจารณาว่าผลลัพธ์ที่แท้จริงเปรียบเทียบกับที่ประกาศไว้อย่างไร
โปรแกรมการศึกษาแบบย่อหรือ RNG จริงๆ แล้วคือ RNG
หากคุณคุ้นเคยกับเครื่องสร้างตัวเลขสุ่มอยู่แล้ว คุณสามารถข้ามไปที่ส่วน "การทดสอบ" ได้ทันที
ตัวเลขสุ่ม (RN) คือลำดับของตัวเลขที่สร้างขึ้นโดยใช้กระบวนการสุ่ม (วุ่นวาย) ซึ่งเป็นแหล่งที่มาของเอนโทรปี นั่นคือ นี่คือลำดับที่องค์ประกอบต่างๆ ไม่ได้เชื่อมโยงกันตามกฎทางคณิตศาสตร์ใดๆ - ไม่มีความสัมพันธ์ระหว่างเหตุและผล
สิ่งที่สร้างตัวเลขสุ่มเรียกว่าเครื่องสร้างตัวเลขสุ่ม (RNG) ดูเหมือนว่าทุกอย่างจะเป็นเรื่องพื้นฐาน แต่ถ้าเราเปลี่ยนจากทฤษฎีไปสู่การปฏิบัติจริง ๆ แล้วมันไม่ง่ายเลยที่จะใช้อัลกอริธึมซอฟต์แวร์เพื่อสร้างลำดับดังกล่าว
เหตุผลก็คือในกรณีที่ไม่มีความสับสนวุ่นวายแบบเดียวกันในอุปกรณ์อิเล็กทรอนิกส์สำหรับผู้บริโภคยุคใหม่ หากไม่มีมัน ตัวเลขสุ่มก็จะไม่เป็นแบบสุ่ม และเครื่องกำเนิดของพวกมันจะกลายเป็นฟังก์ชันปกติของอาร์กิวเมนต์ที่กำหนดไว้อย่างชัดเจน สำหรับความเชี่ยวชาญเฉพาะด้านในสาขาไอที นี่เป็นปัญหาร้ายแรง (เช่น การเข้ารหัส) แต่สำหรับสาขาอื่นๆ ก็มีวิธีแก้ปัญหาที่ยอมรับได้อย่างสมบูรณ์
จำเป็นต้องเขียนอัลกอริทึมที่จะส่งคืนแม้ว่าจะไม่ใช่ตัวเลขสุ่มอย่างแท้จริง แต่ให้ใกล้เคียงที่สุดเท่าที่จะเป็นไปได้ - สิ่งที่เรียกว่าตัวเลขสุ่มหลอก (PRN) อัลกอริทึมในกรณีนี้เรียกว่าเครื่องกำเนิดตัวเลขสุ่มเทียม (PRNG)
มีหลายตัวเลือกในการสร้าง PRNG แต่สิ่งต่อไปนี้จะเกี่ยวข้องกับทุกคน:
- ความจำเป็นในการเริ่มต้นเบื้องต้น
PRNG ไม่มีแหล่งที่มาของเอนโทรปี ดังนั้นจึงต้องได้รับสถานะเริ่มต้นก่อนใช้งาน โดยระบุเป็นตัวเลข (หรือเวกเตอร์) และเรียกว่าเมล็ด (เมล็ดสุ่ม) บ่อยครั้งที่ตัวนับนาฬิกาของโปรเซสเซอร์หรือตัวเลขที่เทียบเท่ากับเวลาของระบบถูกใช้เป็นข้อมูลเริ่มต้น
- ความสามารถในการทำซ้ำของลำดับ
PRNG ถูกกำหนดไว้อย่างสมบูรณ์ ดังนั้นข้อมูลเริ่มต้นที่ระบุระหว่างการกำหนดค่าเริ่มต้นจะกำหนดลำดับตัวเลขในอนาคตทั้งหมดโดยไม่ซ้ำกัน ซึ่งหมายความว่า PRNG ที่แยกกันซึ่งเริ่มต้นด้วย seed เดียวกัน (ในเวลาที่ต่างกัน ในโปรแกรมที่ต่างกัน บนอุปกรณ์ที่แตกต่างกัน) จะสร้างลำดับเดียวกัน
คุณต้องรู้การแจกแจงความน่าจะเป็นที่กำหนดลักษณะของ PRNG ด้วย - มันจะสร้างตัวเลขอะไรและความน่าจะเป็นเท่าใด ส่วนใหญ่แล้วนี่คือการแจกแจงแบบปกติหรือการแจกแจงแบบสม่ำเสมอ
การแจกแจงแบบปกติ (ซ้าย) และการกระจายแบบสม่ำเสมอ (ขวา)
สมมุติว่าเราตายอย่างยุติธรรมซึ่งมี 24 ด้าน หากคุณทอย ความน่าจะเป็นที่จะได้เลขหนึ่งจะเท่ากับ 1/24 (เช่นเดียวกับความน่าจะเป็นที่จะได้เลขอื่น) หากคุณโยนหลายครั้งและบันทึกผลลัพธ์ คุณจะสังเกตเห็นว่าขอบทั้งหมดหลุดออกมาด้วยความถี่เดียวกันโดยประมาณ โดยพื้นฐานแล้ว แม่พิมพ์นี้ถือได้ว่าเป็น RNG ที่มีการกระจายสม่ำเสมอ
จะเป็นอย่างไรถ้าคุณโยนลูกเต๋าเหล่านี้ 10 ลูกพร้อมกันแล้วนับคะแนนรวมล่ะ? จะรักษาความสม่ำเสมอเอาไว้หรือไม่? เลขที่ ส่วนใหญ่แล้วจำนวนเงินจะอยู่ใกล้ 125 จุดนั่นคือเป็นค่าเฉลี่ย และด้วยเหตุนี้ แม้กระทั่งก่อนที่จะทำการขว้าง คุณก็สามารถประมาณผลลัพธ์ในอนาคตได้อย่างคร่าว ๆ
เหตุผลก็คือ มีจำนวนชุดค่าผสมมากที่สุดเพื่อให้ได้คะแนนเฉลี่ย ยิ่งอยู่ห่างจากมันมากเท่าไร การรวมกันก็จะน้อยลงเท่านั้น และด้วยเหตุนี้ ความน่าจะเป็นที่จะสูญเสียก็จะยิ่งน้อยลงเท่านั้น หากมองเห็นข้อมูลนี้ จะมีลักษณะคล้ายกับรูปร่างระฆังอย่างคลุมเครือ ดังนั้นหากยืดออกไปอีกหน่อย ระบบลูกเต๋า 10 ลูกจึงสามารถเรียกว่า RNG ด้วยการแจกแจงแบบปกติ
อีกตัวอย่างหนึ่ง เฉพาะครั้งนี้บนเครื่องบิน - ยิงไปที่เป้าหมาย เกมยิงจะเป็น RNG ที่สร้างคู่ตัวเลข (x, y) ที่แสดงบนกราฟ
ยอมรับว่าตัวเลือกทางด้านซ้ายนั้นใกล้เคียงกับชีวิตจริงมากขึ้น - นี่คือ RNG ที่มีการแจกแจงแบบปกติ แต่ถ้าคุณต้องการกระจายดาวในท้องฟ้าที่มืดมิด ตัวเลือกที่เหมาะสมซึ่งได้รับโดยใช้ RNG ที่มีการกระจายแบบสม่ำเสมอจะเหมาะสมกว่า โดยทั่วไป ให้เลือกเครื่องกำเนิดไฟฟ้าขึ้นอยู่กับงานที่ทำอยู่
ทีนี้เรามาพูดถึงเอนโทรปีของลำดับ PNG กัน ตัวอย่างเช่น มีลำดับที่เริ่มต้นดังนี้:
89, 93, 33, 32, 82, 21, 4, 42, 11, 8, 60, 95, 53, 30, 42, 19, 34, 35, 62, 23, 44, 38, 74, 36, 52, 18, 58, 79, 65, 45, 99, 90, 82, 20, 41, 13, 88, 76, 82, 24, 5, 54, 72, 19, 80, 2, 74, 36, 71, 9, ...
ตัวเลขเหล่านี้สุ่มแค่ไหนเมื่อมองแวบแรก? เริ่มต้นด้วยการตรวจสอบการกระจายตัว
มันดูใกล้เคียงกัน แต่ถ้าคุณอ่านลำดับของตัวเลขสองตัวแล้วตีความว่าเป็นพิกัดบนระนาบ คุณจะได้สิ่งนี้:
ลวดลายจะมองเห็นได้ชัดเจน และเนื่องจากข้อมูลในลำดับได้รับการเรียงลำดับในลักษณะใดลักษณะหนึ่ง (นั่นคือ มีเอนโทรปีต่ำ) สิ่งนี้จึงสามารถทำให้เกิด "อคติ" เช่นนั้นได้ อย่างน้อยที่สุด PRNG ดังกล่าวไม่เหมาะนักสำหรับการสร้างพิกัดบนเครื่องบิน
ลำดับอื่น:
42, 72, 17, 0, 30, 0, 15, 9, 47, 19, 35, 86, 40, 54, 97, 42, 69, 19, 20, 88, 4, 3, 67, 27, 42, 56, 17, 14, 20, 40, 80, 97, 1, 31, 69, 13, 88, 89, 76, 9, 4, 85, 17, 88, 70, 10, 42, 98, 96, 53, ...
ดูเหมือนทุกอย่างจะเรียบร้อยดีที่นี่แม้แต่บนเครื่องบิน:
มาดูกันในปริมาณ (อ่านครั้งละสามตัวเลข):
และรูปแบบอีกครั้ง การสร้างภาพข้อมูลแบบสี่มิติเป็นไปไม่ได้อีกต่อไป แต่รูปแบบสามารถมีอยู่ในมิตินี้และในมิติที่ใหญ่กว่าได้
ในการเข้ารหัสซึ่งมีการกำหนดข้อกำหนดที่เข้มงวดที่สุดกับ PRNG สถานการณ์ดังกล่าวเป็นสิ่งที่ยอมรับไม่ได้อย่างเด็ดขาด ดังนั้นจึงมีการพัฒนาอัลกอริธึมพิเศษเพื่อประเมินคุณภาพซึ่งเราจะไม่พูดถึงในตอนนี้ หัวข้อนี้กว้างขวางและสมควรได้รับบทความแยกต่างหาก
การทดสอบ
ถ้าเราไม่รู้บางสิ่งบางอย่างอย่างแน่นอน แล้วจะทำอย่างไรกับมัน? คุ้มไหมที่จะข้ามถนนถ้าคุณไม่รู้ว่าสัญญาณไฟจราจรไหนอนุญาตให้ข้ามถนน? ผลที่ตามมาอาจแตกต่างกัน
เช่นเดียวกับการสุ่มที่ฉาวโฉ่ใน Unity เป็นการดีถ้าเอกสารเปิดเผยรายละเอียดที่จำเป็น แต่เรื่องราวที่กล่าวถึงในตอนต้นของบทความเกิดขึ้นอย่างแม่นยำเนื่องจากขาดข้อมูลเฉพาะที่ต้องการ
และหากคุณไม่รู้ว่าเครื่องมือทำงานอย่างไร คุณจะใช้งานไม่ถูกต้องได้ โดยทั่วไป ถึงเวลาแล้วที่จะต้องตรวจสอบและดำเนินการทดลองเพื่อให้แน่ใจว่าอย่างน้อยที่สุดเกี่ยวกับการกระจายตัว
วิธีแก้ปัญหานั้นง่ายและมีประสิทธิภาพ - รวบรวมสถิติ รับข้อมูลวัตถุประสงค์ และดูผลลัพธ์
สาขาวิชา
มีหลายวิธีในการสร้างตัวเลขสุ่มใน Unity - เราทดสอบห้าวิธี
- System.Random.Next() สร้างจำนวนเต็มในช่วงค่าที่กำหนด
- System.Random.NextDouble() สร้างตัวเลขความแม่นยำสองเท่าในช่วงตั้งแต่ [0; 1)
- UnityEngine.Random.Range() สร้างตัวเลขความแม่นยำเดี่ยว (ลอยตัว) ในช่วงของค่าที่กำหนด
- UnityEngine.Random.value สร้างตัวเลขความแม่นยำเดี่ยว (ลอยตัว) ในช่วงตั้งแต่ [0; 1)
- Unity.Mathematics.Random.NextFloat() ส่วนหนึ่งของไลบรารี Unity.Mathematics ใหม่ สร้างตัวเลขความแม่นยำเดี่ยว (ลอยตัว) ในช่วงของค่าที่กำหนด
เกือบทุกที่ในเอกสารมีการระบุการกระจายแบบสม่ำเสมอ ยกเว้น UnityEngine.Random.value (โดยที่ไม่ได้ระบุการกระจาย แต่โดยการเปรียบเทียบกับ UnityEngine.Random.Range() คาดว่าจะมีเครื่องแบบเหมือนกัน) และ Unity.Mathematics.Random .NextFloat() (โดยที่ The พื้นฐานคืออัลกอริธึม xorshift ซึ่งหมายความว่าคุณต้องรอการกระจายแบบสม่ำเสมออีกครั้ง)
ตามค่าเริ่มต้น ผลลัพธ์ที่คาดหวังจะถูกนำไปใช้ตามที่ระบุไว้ในเอกสารประกอบ
ระเบียบวิธี
เราเขียนแอปพลิเคชันขนาดเล็กที่สร้างลำดับตัวเลขสุ่มโดยใช้แต่ละวิธีที่นำเสนอและบันทึกผลลัพธ์เพื่อการประมวลผลต่อไป
ความยาวของแต่ละลำดับคือ 100 หมายเลข
ช่วงของตัวเลขสุ่มคือ [0, 100)
ข้อมูลถูกรวบรวมจากหลายแพลตฟอร์มเป้าหมาย:
- Windows
— Unity v2018.3.14f1, โหมดตัวแก้ไข, โมโน, .NET Standard 2.0 - MacOS
— Unity v2018.3.14f1, โหมดตัวแก้ไข, โมโน, .NET Standard 2.0
— Unity v5.6.4p4, โหมดตัวแก้ไข, โมโน, .NET Standard 2.0 - Android
— Unity v2018.3.14f1, รุ่นต่ออุปกรณ์, Mono, .NET Standard 2.0 - iOS
— Unity v2018.3.14f1, สร้างต่ออุปกรณ์, il2cpp, .NET Standard 2.0
การดำเนินงาน
เรามีหลายวิธีในการสร้างตัวเลขสุ่ม สำหรับแต่ละคลาส เราจะเขียนคลาส wrapper แยกกัน ซึ่งควรมี:
- ความเป็นไปได้ในการตั้งค่าช่วงของค่า [ต่ำสุด/สูงสุด) จะถูกตั้งค่าผ่านตัวสร้าง
- วิธีการคืน MF เรามาเลือกประเภท float กันดีกว่า เนื่องจากเป็นแบบทั่วไปมากกว่า
- ชื่อของวิธีการสร้างสำหรับการทำเครื่องหมายผลลัพธ์ เพื่อความสะดวก เราจะส่งคืนเป็นค่าชื่อเต็มของคลาส + ชื่อของวิธีที่ใช้ในการสร้าง MF
ขั้นแรก เรามาประกาศนามธรรมที่จะแสดงโดยอินเทอร์เฟซ IRandomGenerator:
namespace RandomDistribution
{
public interface IRandomGenerator
{
string Name { get; }
float Generate();
}
}
การใช้งาน System.Random.Next()
วิธีนี้ช่วยให้คุณสามารถตั้งค่าช่วงของค่าได้ แต่จะส่งคืนจำนวนเต็ม แต่จำเป็นต้องมีการลอยตัว คุณสามารถตีความจำนวนเต็มเป็นการลอยตัวหรือขยายช่วงของค่าตามลำดับความสำคัญหลายระดับโดยชดเชยค่าเหล่านั้นด้วยระดับกลางแต่ละรุ่น ผลลัพธ์จะเหมือนกับจุดคงที่ซึ่งมีลำดับความแม่นยำที่กำหนด เราจะใช้ตัวเลือกนี้เนื่องจากใกล้กับค่าทศนิยมที่แท้จริงมากขึ้น
using System;
namespace RandomDistribution
{
public class SystemIntegerRandomGenerator : IRandomGenerator
{
private const int DefaultFactor = 100000;
private readonly Random _generator = new Random();
private readonly int _min;
private readonly int _max;
private readonly int _factor;
public string Name => "System.Random.Next()";
public SystemIntegerRandomGenerator(float min, float max, int factor = DefaultFactor)
{
_min = (int)min * factor;
_max = (int)max * factor;
_factor = factor;
}
public float Generate() => (float)_generator.Next(_min, _max) / _factor;
}
}
การใช้งาน System.Random.NextDouble()
นี่คือช่วงของค่าคงที่ [0; 1) ในการฉายภาพลงบนค่าที่ระบุในตัวสร้าง เราใช้เลขคณิตอย่างง่าย: X * (สูงสุด − นาที) + นาที
using System;
namespace RandomDistribution
{
public class SystemDoubleRandomGenerator : IRandomGenerator
{
private readonly Random _generator = new Random();
private readonly double _factor;
private readonly float _min;
public string Name => "System.Random.NextDouble()";
public SystemDoubleRandomGenerator(float min, float max)
{
_factor = max - min;
_min = min;
}
public float Generate() => (float)(_generator.NextDouble() * _factor) + _min;
}
}
การใช้งาน UnityEngine.Random.Range()
วิธีการของคลาสคงที่ UnityEngine.Random นี้ช่วยให้คุณสามารถตั้งค่าช่วงของค่าและส่งกลับประเภทโฟลต คุณไม่จำเป็นต้องทำการแปลงเพิ่มเติมใดๆ
using UnityEngine;
namespace RandomDistribution
{
public class UnityRandomRangeGenerator : IRandomGenerator
{
private readonly float _min;
private readonly float _max;
public string Name => "UnityEngine.Random.Range()";
public UnityRandomRangeGenerator(float min, float max)
{
_min = min;
_max = max;
}
public float Generate() => Random.Range(_min, _max);
}
}
การใช้งาน UnityEngine.Random.value
คุณสมบัติค่าของคลาสคงที่ UnityEngine.Random ส่งคืนประเภท float จากช่วงค่าคงที่ [0; 1) เรามาฉายภาพบนช่วงที่กำหนดในลักษณะเดียวกับเมื่อใช้ System.Random.NextDouble()
using UnityEngine;
namespace RandomDistribution
{
public class UnityRandomValueGenerator : IRandomGenerator
{
private readonly float _factor;
private readonly float _min;
public string Name => "UnityEngine.Random.value";
public UnityRandomValueGenerator(float min, float max)
{
_factor = max - min;
_min = min;
}
public float Generate() => (float)(Random.value * _factor) + _min;
}
}
การใช้งาน Unity.Mathematics.Random.NextFloat()
เมธอด NextFloat() ของคลาส Unity.Mathematics.Random ส่งคืนจุดลอยตัวของประเภท float และอนุญาตให้คุณระบุช่วงของค่า ความแตกต่างเพียงอย่างเดียวคือแต่ละอินสแตนซ์ของ Unity.Mathematics.Random จะต้องเริ่มต้นด้วยเมล็ดบางส่วน - วิธีนี้เราจะหลีกเลี่ยงการสร้างลำดับที่ซ้ำกัน
using Unity.Mathematics;
namespace RandomDistribution
{
public class UnityMathematicsRandomValueGenerator : IRandomGenerator
{
private Random _generator;
private readonly float _min;
private readonly float _max;
public string Name => "Unity.Mathematics.Random.NextFloat()";
public UnityMathematicsRandomValueGenerator(float min, float max)
{
_min = min;
_max = max;
_generator = new Random();
_generator.InitState(unchecked((uint)System.DateTime.Now.Ticks));
}
public float Generate() => _generator.NextFloat(_min, _max);
}
}
การใช้งาน MainController
การใช้งาน IRandomGenerator หลายอย่างพร้อมแล้ว ถัดไป คุณต้องสร้างลำดับและบันทึกชุดข้อมูลผลลัพธ์สำหรับการประมวลผล ในการดำเนินการนี้ เราจะสร้างฉากและสคริปต์ MainController ขนาดเล็กใน Unity ซึ่งจะทำงานที่จำเป็นทั้งหมดและในขณะเดียวกันก็รับผิดชอบในการโต้ตอบกับ UI
มากำหนดขนาดของชุดข้อมูลและช่วงของค่า MF และรับวิธีที่ส่งคืนอาร์เรย์ของเครื่องกำเนิดไฟฟ้าที่กำหนดค่าและพร้อมใช้งาน
namespace RandomDistribution
{
public class MainController : MonoBehaviour
{
private const int DefaultDatasetSize = 100000;
public float MinValue = 0f;
public float MaxValue = 100f;
...
private IRandomGenerator[] CreateRandomGenerators()
{
return new IRandomGenerator[]
{
new SystemIntegerRandomGenerator(MinValue, MaxValue),
new SystemDoubleRandomGenerator(MinValue, MaxValue),
new UnityRandomRangeGenerator(MinValue, MaxValue),
new UnityRandomValueGenerator(MinValue, MaxValue),
new UnityMathematicsRandomValueGenerator(MinValue, MaxValue)
};
}
...
}
}
ตอนนี้เรามาสร้างชุดข้อมูลกันดีกว่า ในกรณีนี้ การสร้างข้อมูลจะรวมกับการบันทึกผลลัพธ์ลงในสตรีมข้อความ (ในรูปแบบ CSV) ในการจัดเก็บค่าของ IRandomGenerator แต่ละตัว คอลัมน์แยกของตัวเองจะถูกจัดสรร และบรรทัดแรกจะมีชื่อของตัวสร้าง
namespace RandomDistribution
{
public class MainController : MonoBehaviour
{
...
private void GenerateCsvDataSet(TextWriter writer, int dataSetSize, params IRandomGenerator[] generators)
{
const char separator = ',';
int lastIdx = generators.Length - 1;
// write header
for (int j = 0; j <= lastIdx; j++)
{
writer.Write(generators[j].Name);
if (j != lastIdx)
writer.Write(separator);
}
writer.WriteLine();
// write data
for (int i = 0; i <= dataSetSize; i++)
{
for (int j = 0; j <= lastIdx; j++)
{
writer.Write(generators[j].Generate());
if (j != lastIdx)
writer.Write(separator);
}
if (i != dataSetSize)
writer.WriteLine();
}
}
...
}
}
สิ่งที่เหลืออยู่คือการเรียกเมธอด GenerateCsvDataSet และบันทึกผลลัพธ์ลงในไฟล์หรือถ่ายโอนข้อมูลผ่านเครือข่ายทันทีจากอุปกรณ์ปลายทางไปยังเซิร์ฟเวอร์ที่รับ
namespace RandomDistribution
{
public class MainController : MonoBehaviour
{
...
public void GenerateCsvDataSet(string path, int dataSetSize, params IRandomGenerator[] generators)
{
using (var writer = File.CreateText(path))
{
GenerateCsvDataSet(writer, dataSetSize, generators);
}
}
public string GenerateCsvDataSet(int dataSetSize, params IRandomGenerator[] generators)
{
using (StringWriter writer = new StringWriter(CultureInfo.InvariantCulture))
{
GenerateCsvDataSet(writer, dataSetSize, generators);
return writer.ToString();
}
}
...
}
}
แหล่งที่มาของโครงการอยู่ที่
ผลการวิจัย
ไม่มีปาฏิหาริย์เกิดขึ้น สิ่งที่พวกเขาคาดหวังคือสิ่งที่พวกเขาได้รับ - ในทุกกรณี การกระจายที่เท่าเทียมกันโดยไม่มีการสมรู้ร่วมคิด ฉันไม่เห็นประเด็นในการแยกกราฟสำหรับแพลตฟอร์ม - กราฟทั้งหมดแสดงผลลัพธ์ที่เหมือนกันโดยประมาณ
ความจริงคือ:
การสร้างภาพลำดับบนระนาบจากวิธีการสร้างทั้งห้าวิธี:
และการแสดงภาพในรูปแบบ 3 มิติ ฉันจะเหลือเพียงผลลัพธ์ของ System.Random.Next() เพื่อไม่ให้สร้างเนื้อหาที่เหมือนกันจำนวนมาก
เรื่องราวที่เล่าในบทนำเกี่ยวกับการกระจาย UnityEngine แบบปกติการสุ่มไม่ได้เกิดขึ้นซ้ำ: ในตอนแรกมีข้อผิดพลาดหรือมีบางอย่างเปลี่ยนแปลงไปในเครื่องยนต์ แต่ตอนนี้เราแน่ใจแล้ว
ที่มา: will.com