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

โครงการนี้มุ่งเน้นไปที่กรณีการใช้งานทั่วไปที่คุณอาจพบเมื่อพัฒนาเกมบน Roblox ซึ่งเมื่อมีความเหมาะสม คุณจะพบหมายเหตุเกี่ยวกับการแลกเปลี่ยน ข้อตกลง และเหตุผลของการเลือกการดำเนินการต่างๆ เพื่อให้คุณสามารถตัดสินใจได้ดีที่สุดสำหรับเกมของคุณเอง
รับไฟล์
- ไปที่ หน้าเกม Plant
- คลิกที่ปุ่ม ⋯ และ แก้ไขใน Studio
กรณีการใช้งาน
Plant ครอบคลุมกรณีการใช้งานดังต่อไปนี้:
- การเก็บข้อมูลเซสชันและข้อมูลผู้เล่น
- การจัดการมุมมอง UI
- การเชื่อมต่อระหว่างไคลเอนต์และเซิร์ฟเวอร์
- ประสบการณ์ผู้ใช้ครั้งแรก (FTUE)
- การซื้อสกุลเงินแข็งและอ่อน
นอกจากนี้ โครงการนี้ยังแก้ปัญหาชุดปัญหาที่แคบกว่าซึ่งใช้ได้กับหลายเกม รวมถึง:
- การปรับแต่งพื้นที่ในสถานที่ที่เกี่ยวข้องกับผู้เล่น
- การจัดการความเร็วในการเคลื่อนที่ของตัวละครผู้เล่น
- การสร้างวัตถุที่ติดตามตัวละครไปรอบๆ
- การตรวจจับว่าส่วนใดของโลกที่ตัวละครอยู่
โปรดทราบว่ามีกระบวนการใช้งานหลายอย่างในเกมนี้ที่เล็กเกินไป แคบเกินไป หรือไม่แสดงให้เห็นถึงการแก้ปัญหาที่น่าสนใจ; เหล่านี้จะไม่ได้รับการกล่าวถึง
โครงสร้างโครงการ
การตัดสินใจครั้งแรกเมื่อสร้างเกมคือการตัดสินใจว่าจะจัดโครงสร้าง โครงการ อย่างไร ซึ่งหลักๆ จะรวมถึงการวางตำแหน่งของอินสแตนซ์เฉพาะใน โมเดลข้อมูล และวิธีการจัดระเบียบและโครงสร้างจุดเริ่มต้นสำหรับทั้งโค้ดไคลเอนต์และเซิร์ฟเวอร์
โมเดลข้อมูล
ตารางต่อไปนี้อธิบายว่าบริการคอนเทนเนอร์ใดในโมเดลข้อมูลที่อินสแตนซ์ถูกวางไว้
| บริการ | ประเภทของอินสแตนซ์ |
|---|---|
| Workspace | ประกอบด้วยโมเดลสถิตที่แสดงถึงโลก 3D โดยเฉพาะส่วนของโลกที่ไม่เป็นของผู้เล่นใดๆ คุณไม่จำเป็นต้องสร้าง แก้ไข หรือทำลายอินสแตนซ์เหล่านี้ในระหว่างการทำงาน ดังนั้นจึงสามารถทิ้งไว้ที่นี่ได้ นอกจากนี้ยังมี Folder ที่ว่างเปล่า ซึ่งจะมีโมเดลฟาร์มของผู้เล่นเพิ่มเข้ามาในระหว่างการทำงาน |
| Lighting | เอฟเฟกต์บรรยากาศและแสง |
| ReplicatedFirst | ประกอบด้วยชุดอินสแตนซ์ที่เล็กที่สุดที่จำเป็นในการแสดงหน้าจอโหลดและเริ่มเกม ยิ่งมีอินสแตนซ์มากขึ้นใน ReplicatedFirst เวลาที่รอให้พวกเขาทำซ้ำก่อนที่โค้ดใน ReplicatedFirst จะทำงานก็จะยิ่งนานขึ้น
|
| ReplicatedStorage | ทำหน้าที่เป็นคอนเทนเนอร์เก็บข้อมูลสำหรับอินสแตนซ์ทั้งหมดที่ต้องการการเข้าถึงทั้งจากไคลเอนต์และเซิร์ฟเวอร์
|
| ServerScriptService | ประกอบด้วย Script ที่ทำหน้าที่เป็นจุดเริ่มต้นสำหรับโค้ดฝั่งเซิร์ฟเวอร์ทั้งหมดในโครงการ |
| ServerStorage | ทำหน้าที่เป็นคอนเทนเนอร์เก็บข้อมูลสำหรับอินสแตนซ์ทั้งหมดที่ไม่จำเป็นต้องทำซ้ำไปยังไคลเอนต์
|
| SoundService | ประกอบด้วยวัตถุ Sound ที่ใช้สำหรับเอฟเฟกต์เสียงในเกม ภายใต้ SoundService วัตถุ Sound เหล่านี้ไม่มีตำแหน่งและไม่ได้จำลองในพื้นที่ 3D |
จุดเริ่มต้น
โครงการส่วนใหญ่จะจัดระเบียบโค้ดภายใน ModuleScripts ที่สามารถนำเข้าได้ทั่วทั้งฐานข้อมูลโค้ด ModuleScripts เป็นแบบใช้ซ้ำได้ แต่จะไม่ทำงานด้วยตัวเอง; ต้องนำเข้าโดย Script หรือ LocalScript โครงการ Roblox หลายโครงการจะมีจำนวน Script และ LocalScript ที่มาก ซึ่งแต่ละอันเกี่ยวข้องกับพฤติกรรมหรือระบบเฉพาะในเกม สร้างจุดเริ่มต้นหลายจุด
สำหรับไมโครเกม Plant จะมีการใช้แนวทางที่แตกต่างออกไปผ่าน LocalScript เดียวที่เป็นจุดเริ่มต้นสำหรับโค้ดไคลเอนต์ทั้งหมด และ Script เดียวที่เป็นจุดเริ่มต้นสำหรับโค้ดเซิร์ฟเวอร์ทั้งหมด แนวทางที่ถูกต้องสำหรับโครงการของคุณขึ้นอยู่กับความต้องการของคุณ แต่จุดเริ่มต้นเดียวจะให้การควบคุมที่มากขึ้นเกี่ยวกับลำดับที่ระบบต่างๆ ถูกดำเนินการ
รายการต่อไปนี้อธิบายถึงการแลกเปลี่ยนของทั้งสองแนวทาง:
- Script เดียวและ LocalScript เดียวครอบคลุมโค้ดเซิร์ฟเวอร์และไคลเอนต์ตามลำดับ
- การควบคุมที่มากขึ้นเกี่ยวกับลำดับที่ระบบต่างๆ เริ่มต้นเพราะโค้ดทั้งหมดถูกเริ่มต้นจากสคริปต์เดียว
- สามารถส่งผ่านวัตถุโดยอ้างอิงระหว่างระบบได้
สถาปัตยกรรมระบบระดับสูง
ระบบระดับสูงในโครงการมีรายละเอียดด้านล่าง บางระบบเหล่านี้ซับซ้อนกว่าระบบอื่นๆ และในหลายกรณีฟังก์ชันการทำงานของพวกมันถูกทำให้เป็นนามธรรมข้ามลำดับชั้นของคลาสอื่นๆ

แต่ละระบบเหล่านี้เป็น "ซิงเกิลตัน" ซึ่งหมายความว่าเป็นคลาสที่ไม่สามารถสร้างอินสแตนซ์ได้ซึ่งจะถูกเริ่มต้นโดยสคริปต์ start ที่เกี่ยวข้องของไคลเอนต์หรือเซิร์ฟเวอร์ คุณสามารถอ่านเพิ่มเติมเกี่ยวกับ รูปแบบซิงเกิลตัน ได้ในภายหลังในคู่มือนี้
เซิร์ฟเวอร์
ระบบต่อไปนี้เกี่ยวข้องกับเซิร์ฟเวอร์
| ระบบ | คำอธิบาย |
|---|---|
| เครือข่าย |
|
| PlayerDataServer |
|
| ตลาด |
|
| CollisionGroupManager |
|
| FarmManagerServer |
|
| PlayerObjectsContainer |
|
| TagPlayers |
|
| FtueManagerServer |
|
| CharacterSpawner |
|
ไคลเอนต์
ระบบต่อไปนี้เกี่ยวข้องกับไคลเอนต์
| ระบบ | คำอธิบาย |
|---|---|
| เครือข่าย |
|
| PlayerDataClient |
|
| MarketClient |
|
| LocalWalkJumpManager |
|
| FarmManagerClient |
|
| UISetup |
|
| FtueManagerClient |
|
| CharacterSprint |
|
การสื่อสารระหว่างไคลเอนต์และเซิร์ฟเวอร์
เกม Roblox ส่วนใหญ่เกี่ยวข้องกับการสื่อสารระหว่างไคลเอนต์และเซิร์ฟเวอร์ ซึ่งอาจรวมถึงการที่ไคลเอนต์ขอให้เซิร์ฟเวอร์ดำเนินการบางอย่างและเซิร์ฟเวอร์ทำซ้ำการอัปเดตไปยังไคลเอนต์
ในโครงการนี้ การสื่อสารระหว่างไคลเอนต์และเซิร์ฟเวอร์จะถูกเก็บให้เป็นทั่วไปที่สุดเท่าที่จะเป็นไปได้โดยการจำกัดการใช้วัตถุ RemoteEvent และ RemoteFunction เพื่อลดจำนวนกฎพิเศษที่ต้องติดตาม โครงการนี้ใช้วิธีการต่อไปนี้ตามลำดับความชอบ:
- การทำซ้ำผ่าน ระบบข้อมูลผู้เล่น
- การทำซ้ำผ่าน แอตทริบิวต์
- การทำซ้ำผ่าน แท็ก
- การส่งข้อความ โดยตรง ผ่านโมดูล เครือข่าย
การทำซ้ำผ่านระบบข้อมูลผู้เล่น
ระบบข้อมูลผู้เล่น ช่วยให้ข้อมูลสามารถเชื่อมโยงกับผู้เล่นซึ่งคงอยู่ระหว่างเซสชันการบันทึก ระบบนี้ให้การทำซ้ำจากไคลเอนต์ไปยังเซิร์ฟเวอร์และชุด API ที่สามารถใช้ในการสอบถามข้อมูลและสมัครสมาชิกการเปลี่ยนแปลง ทำให้เหมาะสำหรับการทำซ้ำการเปลี่ยนแปลงสถานะของผู้เล่นจากเซิร์ฟเวอร์ไปยังไคลเอนต์
ตัวอย่างเช่น แทนที่จะเรียกใช้ UpdateCoins RemoteEvent ที่กำหนดเองเพื่อบอกไคลเอนต์ว่ามีเหรียญกี่เหรียญ คุณสามารถเรียกใช้สิ่งต่อไปนี้และให้ไคลเอนต์สมัครสมาชิกผ่านเหตุการณ์ PlayerDataClient.updated
PlayerDataServer.setValue(player, "coins", 5)แน่นอนว่านี่มีประโยชน์เฉพาะสำหรับการทำซ้ำจากเซิร์ฟเวอร์ไปยังไคลเอนต์และสำหรับค่าที่คุณต้องการให้คงอยู่ระหว่างเซสชัน แต่สิ่งนี้ใช้ได้กับกรณีจำนวนมากในโครงการ รวมถึง:
- ขั้นตอน FTUE ปัจจุบัน
- สินค้าคงคลังของผู้เล่น
- จำนวนเหรียญที่ผู้เล่นมี
- สถานะของฟาร์มของผู้เล่น
การทำซ้ำผ่านแอตทริบิวต์
ในสถานการณ์ที่เซิร์ฟเวอร์ต้องทำซ้ำค่าที่กำหนดเองไปยังไคลเอนต์ซึ่งเฉพาะเจาะจงกับ Instance ที่กำหนด คุณสามารถใช้ แอตทริบิวต์ Roblox จะทำการทำซ้ำค่าแอตทริบิวต์โดยอัตโนมัติ ดังนั้นคุณไม่จำเป็นต้องรักษาเส้นทางโค้ดใดๆ เพื่อทำซ้ำสถานะที่เกี่ยวข้องกับวัตถุ ข้อดีอีกประการหนึ่งคือการทำซ้ำนี้เกิดขึ้นพร้อมกับอินสแตนซ์เอง
สิ่งนี้มีประโยชน์โดยเฉพาะสำหรับอินสแตนซ์ที่สร้างขึ้นในระหว่างการทำงาน เนื่องจากแอตทริบิวต์ที่ตั้งค่าในอินสแตนซ์ใหม่ก่อนที่จะถูกจัดวางในโมเดลข้อมูลจะทำการทำซ้ำพร้อมกันกับอินสแตนซ์เอง สิ่งนี้หลีกเลี่ยงความจำเป็นในการเขียนโค้ดเพื่อ "รอ" ข้อมูลเพิ่มเติมที่จะถูกทำซ้ำผ่าน RemoteEvent หรือ StringValue
คุณยังสามารถอ่านแอตทริบิวต์โดยตรงจากโมเดลข้อมูลได้จากทั้งไคลเอนต์หรือเซิร์ฟเวอร์ โดยใช้วิธี GetAttribute() และสมัครสมาชิกการเปลี่ยนแปลงด้วยวิธี GetAttributeChangedSignal() ในโครงการ Plant วิธีการนี้ถูกใช้สำหรับการทำซ้ำสถานะปัจจุบันของพืชไปยังไคลเอนต์
การทำซ้ำผ่านแท็ก
CollectionService ช่วยให้คุณสามารถใช้แท็กสตริงกับ Instance ได้ สิ่งนี้มีประโยชน์สำหรับการจัดประเภทอินสแตนซ์และทำซ้ำการจัดประเภทนั้นไปยังไคลเอนต์
ตัวอย่างเช่น แท็ก CanPlant ถูกใช้ในเซิร์ฟเวอร์เพื่อบ่งบอกไปยังไคลเอนต์ว่ากระถางที่กำหนดสามารถรับพืชได้
การส่งข้อความโดยตรงผ่านโมดูลเครือข่าย
ในสถานการณ์ที่ไม่มีตัวเลือกก่อนหน้านี้ใช้ได้ คุณสามารถใช้การเรียกเครือข่ายที่กำหนดเองผ่านโมดูล เครือข่าย นี่เป็นตัวเลือกเดียวในโครงการที่อนุญาตให้มีการสื่อสารจากไคลเอนต์ไปยังเซิร์ฟเวอร์และจึงมีประโยชน์ที่สุดสำหรับการส่งคำขอจากไคลเอนต์และรับการตอบสนองจากเซิร์ฟเวอร์
Plant ใช้การเรียกเครือข่ายโดยตรงสำหรับคำขอจากไคลเอนต์หลายประเภท รวมถึง:
- รดน้ำพืช
- ปลูกเมล็ด
- ซื้อรายการ
ข้อเสียของวิธีนี้คือข้อความแต่ละข้อความต้องการการกำหนดค่าที่กำหนดเองซึ่งอาจเพิ่มความซับซ้อนของโครงการ แม้ว่าจะหลีกเลี่ยงได้ทุกที่ที่เป็นไปได้ โดยเฉพาะสำหรับการสื่อสารจากเซิร์ฟเวอร์ไปยังไคลเอนต์
คลาสและซิงเกิลตัน
คลาสในโครงการ Plant เช่นเดียวกับอินสแตนซ์ใน Roblox สามารถสร้างและทำลายได้ ไวยากรณ์คลาสของมันได้รับแรงบันดาลใจจากแนวทาง Lua ที่เป็นอัตลักษณ์ในการ โปรแกรมเชิงวัตถุ โดยมีการเปลี่ยนแปลงหลายอย่างเพื่อเปิดใช้งานการสนับสนุน การตรวจสอบประเภทที่เข้มงวด
การสร้างอินสแตนซ์
คลาสหลายคลาสในโครงการนี้เกี่ยวข้องกับหนึ่งหรือมากกว่า Instances วัตถุของคลาสที่กำหนดจะถูกสร้างขึ้นโดยใช้วิธี new() ซึ่งสอดคล้องกับวิธีการที่อินสแตนซ์ถูกสร้างขึ้นใน Roblox โดยใช้ Instance.new()
รูปแบบนี้มักใช้สำหรับวัตถุที่คลาสมีการแสดงผลทางกายภาพในโมเดลข้อมูล และคลาสขยายฟังก์ชันการทำงานของมัน ตัวอย่างที่ดีคือ BeamBetween ซึ่งสร้างวัตถุ Beam ระหว่าง Attachment สองตัวที่กำหนดและทำให้การยึดเหล่านั้นอยู่ในแนวตั้งเพื่อให้ลำแสงหันขึ้นเสมอ อินสแตนซ์เหล่านี้สามารถถูกโคลนจากเวอร์ชันที่สร้างไว้ล่วงหน้าใน ReplicatedStorage หรือส่งผ่านไปยัง new() เป็นอาร์กิวเมนต์และเก็บไว้ภายในวัตถุภายใต้ self
อินสแตนซ์ที่เกี่ยวข้อง
ตามที่กล่าวไว้ข้างต้น คลาสหลายคลาสในโครงการนี้มีการแสดงผลในโมเดลข้อมูล อินสแตนซ์ที่สอดคล้องกับคลาสและถูกจัดการโดยมัน
แทนที่จะสร้างอินสแตนซ์เหล่านี้เมื่อวัตถุคลาสถูกสร้างขึ้น โค้ดมักจะเลือกที่จะ Clone() เวอร์ชันที่สร้างไว้ล่วงหน้าของ Instance ที่เก็บไว้ภายใต้ ReplicatedStorage หรือ ServerStorage แม้ว่าจะเป็นไปได้ที่จะทำให้คุณสมบัติของอินสแตนซ์เหล่านี้เป็นแบบอนุกรมและสร้างพวกมันจากศูนย์ในฟังก์ชัน new() ของคลาส การทำเช่นนี้จะทำให้การแก้ไขวัตถุเป็นเรื่องที่ยุ่งยากมากและทำให้ยากต่อการอ่าน นอกจากนี้ การโคลนอินสแตนซ์มักจะเป็นการดำเนินการที่รวดเร็วกว่าในการสร้างอินสแตนซ์ใหม่และปรับแต่งคุณสมบัติของมันในระหว่างการทำงาน
การประกอบ
แม้ว่าการสืบทอดจะเป็นไปได้ใน Luau โดยใช้ เมตาเทเบิล โครงการนี้เลือกที่จะอนุญาตให้คลาสขยายซึ่งกันและกันผ่าน การประกอบ เมื่อรวมคลาสผ่านการประกอบ วัตถุ "ลูก" จะถูกสร้างขึ้นในวิธี new() ของคลาสและรวมอยู่เป็นสมาชิกภายใต้ self
สำหรับตัวอย่างของสิ่งนี้ในทางปฏิบัติ ดูที่คลาส CloseButton ซึ่งห่อหุ้มคลาส Button
การทำความสะอาด
คล้ายกับวิธีที่ Instance สามารถถูกทำลายด้วยวิธี Destroy() คลาสที่สามารถสร้างอินสแตนซ์ได้ก็สามารถถูกทำลายได้เช่นกัน วิธีการทำลายสำหรับคลาสในโครงการคือ destroy() โดยมีตัวพิมพ์เล็ก d เพื่อความสอดคล้องใน camelCase ทั่วทั้งวิธีการของฐานข้อมูลโค้ด รวมถึงเพื่อแยกความแตกต่างระหว่างคลาสของโครงการและอินสแตนซ์ของ Roblox
บทบาทของวิธี destroy() คือการทำลายอินสแตนซ์ใดๆ ที่สร้างโดยวัตถุ ตัดการเชื่อมต่อการเชื่อมต่อใดๆ และเรียก destroy() บนวัตถุลูกใดๆ สิ่งนี้มีความสำคัญโดยเฉพาะสำหรับการเชื่อมต่อ เนื่องจากอินสแตนซ์ที่มีการเชื่อมต่อที่ใช้งานอยู่จะไม่ถูกทำความสะอาดโดยตัวเก็บขยะ Luau แม้ว่าจะไม่มีการอ้างอิงใดๆ ต่ออินสแตนซ์หรือการเชื่อมต่อกับอินสแตนซ์ก็ตาม
ซิงเกิลตัน
ซิงเกิลตัน ตามชื่อที่บอกไว้ เป็นคลาสที่สามารถมีวัตถุได้เพียงหนึ่งเดียวเท่านั้น พวกเขาคือสิ่งที่เทียบเท่ากับ บริการ ของ Roblox ในโครงการ Plant ใช้ประโยชน์จากความจริงที่ว่าการเรียกใช้ ModuleScript จะเก็บค่าที่ส่งกลับไว้ในแคช ซึ่งหมายความว่าการเรียกใช้ ModuleScript ซิงเกิลตันเดียวกันจากสถานที่ต่างๆ จะให้วัตถุที่ส่งกลับเดียวกันอย่างสม่ำเสมอ ข้อยกเว้นเพียงอย่างเดียวสำหรับกฎนี้คือถ้าสภาพแวดล้อมที่แตกต่างกัน (ไคลเอนต์หรือเซิร์ฟเวอร์) เข้าถึง ModuleScript
ซิงเกิลตันจะแยกออกจากคลาสที่สามารถสร้างอินสแตนซ์ได้โดยข้อเท็จจริงที่ว่าพวกเขาไม่มีวิธี new() แทนที่จะเป็นเช่นนั้น วัตถุพร้อมกับวิธีการและสถานะของมันจะถูกส่งกลับโดยตรงผ่าน ModuleScript เนื่องจากซิงเกิลตันไม่ได้ถูกสร้างขึ้น จึงไม่มีการใช้ไวยากรณ์ self และวิธีการจะถูกเรียกด้วยจุด (.) แทนที่จะเป็นโคลอน (:)
การตรวจสอบประเภทที่เข้มงวด
Luau สนับสนุนการพิมพ์แบบค่อยเป็นค่อยไป ซึ่งหมายความว่าคุณสามารถเพิ่มการกำหนดประเภทที่เลือกได้ในบางส่วนหรือทั้งหมดของโค้ดของคุณ ในโครงการนี้ การตรวจสอบประเภทที่เข้มงวดจะถูกใช้สำหรับทุกสคริปต์ นี่เป็นตัวเลือกที่น้อยที่สุดสำหรับเครื่องมือ Script Analysis ของ Roblox และดังนั้นจึงมีแนวโน้มที่จะจับข้อผิดพลาดประเภทก่อนการทำงาน
ไวยากรณ์คลาสที่มีประเภท
แนวทางที่จัดตั้งขึ้นในการสร้างคลาสใน Lua ได้รับการ บันทึกไว้อย่างดี อย่างไรก็ตามมันไม่เหมาะสมกับการพิมพ์ที่เข้มงวดของ Luau ใน Luau วิธีที่ง่ายที่สุดในการรับประเภทของคลาสคือวิธี typeof():
type ClassType = typeof(Class.new())วิธีนี้ใช้ได้ แต่ไม่ค่อยมีประโยชน์เมื่อคลาสของคุณถูกเริ่มต้นด้วยค่าที่มีอยู่เฉพาะในระหว่างการทำงาน เช่น Player นอกจากนี้ สมมติฐานที่ทำในไวยากรณ์คลาส Lua ที่เป็นอัตลักษณ์คือการประกาศวิธีการในคลาส self จะต้องเป็นอินสแตนซ์ของคลาสนั้นเสมอ; นี่ไม่ใช่สมมติฐานที่เครื่องมือการตรวจสอบประเภทสามารถทำได้
เพื่อสนับสนุนการตรวจสอบประเภทที่เข้มงวด โครงการ Plant ใช้โซลูชันที่แตกต่างจากไวยากรณ์คลาส Lua ที่เป็นอัตลักษณ์ในหลายๆ ด้าน ซึ่งบางอย่างอาจรู้สึกไม่เป็นธรรมชาติ:
- การกำหนด self จะถูกทำซ้ำทั้งในประกาศประเภทและในตัวสร้าง สิ่งนี้ทำให้เกิดภาระในการบำรุงรักษา แต่จะมีการแจ้งเตือนหากการกำหนดทั้งสองไม่ตรงกัน
- วิธีการของคลาสจะถูกประกาศด้วยจุด ดังนั้น self สามารถประกาศให้ชัดเจนว่าเป็นประเภท ClassType วิธีการยังสามารถเรียกใช้ด้วยโคลอนตามที่คาดหวัง
--!strict
local MyClass = {}
MyClass.__index = MyClass
export type ClassType = typeof(setmetatable(
{} :: {
property: number,
},
MyClass
))
function MyClass.new(property: number): ClassType
local self = {
property = property,
}
setmetatable(self, MyClass)
return self
end
function MyClass.addOne(self: ClassType)
self.property += 1
end
return MyClassการแคสต์ประเภทหลังจากการป้องกันทางตรรกะ
ณ เวลาที่เขียน ประเภทของค่าจะไม่ถูกลดลงหลังจากคำสั่งเงื่อนไขการป้องกัน ตัวอย่างเช่น หลังจากการป้องกันด้านล่าง ประเภทของ optionalParameter จะไม่ถูกลดลงเป็น number
--!strict
local function foo(optionalParameter: number?)
if not optionalParameter then
return
end
print(optionalParameter + 1)
endเพื่อบรรเทานี้ ตัวแปรใหม่จะถูกสร้างขึ้นหลังจากการป้องกันเหล่านี้โดยมีการแคสต์ประเภทอย่างชัดเจน
--!strict
local function foo(optionalParameter: number?)
if not optionalParameter then
return
end
local parameter = optionalParameter :: number
print(parameter + 1)
endการเดินทางในลำดับชั้นของ DataModel
ในบางกรณี ฐานข้อมูลโค้ดจำเป็นต้องเดินทางในลำดับชั้นของโมเดลข้อมูลของต้นไม้ของวัตถุที่ถูกสร้างขึ้นในระหว่างการทำงาน สิ่งนี้นำเสนอความท้าทายที่น่าสนใจสำหรับการตรวจสอบประเภท ณ เวลาที่เขียน ไม่สามารถกำหนดลำดับชั้นของโมเดลข้อมูลทั่วไปเป็นประเภทได้ ดังนั้นจึงมีกรณีที่ข้อมูลประเภทเดียวที่มีอยู่สำหรับโครงสร้างโมเดลข้อมูลคือประเภทของอินสแตนซ์ระดับสูงสุด
แนวทางหนึ่งในการแก้ปัญหานี้คือการแคสต์เป็น any และจากนั้นปรับปรุง ตัวอย่างเช่น:
local function enableVendor(vendor: Model)
local zonePart: BasePart = (vendor :: any).ZonePart
endปัญหาของแนวทางนี้คือมันส่งผลกระทบต่อความสามารถในการอ่าน แทนที่จะเป็นเช่นนั้น โครงการนี้ใช้โมดูลทั่วไปที่เรียกว่า getInstance สำหรับการเดินทางในลำดับชั้นของโมเดลข้อมูลที่แคสต์เป็น any ภายใน
local function enableVendor(vendor: Model)
local zonePart: BasePart = getInstance(vendor, "ZonePart")
endเมื่อความเข้าใจของเครื่องมือประเภทเกี่ยวกับโมเดลข้อมูลพัฒนาไป อาจเป็นไปได้ว่ารูปแบบเช่นนี้จะไม่จำเป็นอีกต่อไป
ส่วนติดต่อผู้ใช้
Plant รวมถึงส่วนติดต่อผู้ใช้ 2D ที่ซับซ้อนและเรียบง่ายหลายประเภท ซึ่งรวมถึงรายการแสดงผลที่ไม่โต้ตอบ เช่น ตัวนับเหรียญ และเมนูที่ซับซ้อนและโต้ตอบได้ เช่น ร้านค้า
แนวทาง UI
คุณสามารถเปรียบเทียบ Roblox UI กับ HTML DOM ได้อย่างหลวมๆ เพราะมันเป็นลำดับชั้นของวัตถุที่อธิบายว่าสิ่งที่ผู้ใช้ควรเห็นคืออะไร แนวทางในการสร้างและอัปเดต UI ของ Roblox จะแบ่งออกเป็น แนวทางเชิงบังคับ และ แนวทางเชิงประกาศ
| แนวทาง | ข้อดีและข้อเสีย |
|---|---|
| เชิงบังคับ | ในแนวทางเชิงบังคับ UI จะถูกจัดการเหมือนกับลำดับชั้นของอินสแตนซ์อื่นๆ บน Roblox โครงสร้าง UI จะถูกสร้างขึ้นก่อนการทำงานใน Studio และเพิ่มลงในโมเดลข้อมูล โดยทั่วไปจะอยู่ใน StarterGui โดยตรง จากนั้นในระหว่างการทำงาน โค้ดจะจัดการชิ้นส่วนเฉพาะของ UI เพื่อสะท้อนสถานะที่ผู้สร้างต้องการ แนวทางนี้มีข้อดีบางประการ คุณสามารถสร้าง UI จากศูนย์ใน Studio และเก็บไว้ในโมเดลข้อมูล นี่เป็นประสบการณ์การแก้ไขที่ง่ายและมองเห็นได้ซึ่งสามารถเร่งการสร้าง UI ได้ เนื่องจากโค้ด UI เชิงบังคับจะเกี่ยวข้องเฉพาะกับสิ่งที่ต้องเปลี่ยนแปลง ทำให้การเปลี่ยนแปลง UI ที่ง่ายทำได้ง่าย ข้อเสียที่น่าสังเกตคือ เนื่องจากแนวทาง UI เชิงบังคับต้องการให้สถานะถูกนำไปใช้ด้วยตนเองในรูปแบบของการเปลี่ยนแปลง การแสดงผลที่ซับซ้อนของสถานะอาจกลายเป็นเรื่องยากที่จะค้นหาและแก้ไขข้อบกพร่อง มักจะเกิดข้อผิดพลาดเมื่อพัฒนาโค้ด UI เชิงบังคับ โดยเฉพาะเมื่อสถานะและ UI กลายเป็นไม่ตรงกันเนื่องจากการอัปเดตหลายครั้งที่มีปฏิสัมพันธ์ในลำดับที่ไม่คาดคิด อีกความท้าทายหนึ่งของแนวทางเชิงบังคับคือมันยากที่จะแบ่ง UI ออกเป็นส่วนประกอบที่มีความหมายซึ่งสามารถประกาศได้ครั้งเดียวและนำกลับมาใช้ใหม่ได้ เนื่องจากต้นไม้ UI ทั้งหมดถูกประกาศในเวลาการแก้ไข รูปแบบทั่วไปอาจถูกทำซ้ำในหลายส่วนของโมเดลข้อมูล |
| เชิงประกาศ | ในแนวทางเชิงประกาศ สถานะที่ต้องการของอินสแตนซ์ UI จะถูกประกาศอย่างชัดเจน และการนำไปใช้สถานะนี้อย่างมีประสิทธิภาพจะถูกทำให้เป็นนามธรรมโดยไลบรารี เช่น Roact หรือ Fusion ข้อดีของแนวทางนี้คือการนำไปใช้สถานะกลายเป็นเรื่องง่าย และคุณเพียงแค่ต้องอธิบายว่าสิ่งที่คุณต้องการให้ UI ของคุณมีลักษณะอย่างไร สิ่งนี้ทำให้การระบุและแก้ไขข้อบกพร่องง่ายขึ้นอย่างมาก ข้อเสียหลักคือการต้องประกาศต้นไม้ UI ทั้งหมดในโค้ด ไลบรารีอย่าง Roact และ Fusion มีไวยากรณ์เพื่อทำให้สิ่งนี้ง่ายขึ้น แต่ก็ยังเป็นกระบวนการที่ใช้เวลานานและประสบการณ์การแก้ไขที่ไม่เป็นธรรมชาติเมื่อประกอบ UI |
Plant ใช้แนวทาง เชิงบังคับ โดยมีแนวคิดว่าการแสดงการเปลี่ยนแปลงโดยตรงจะให้ภาพรวมที่มีประสิทธิภาพมากขึ้นเกี่ยวกับวิธีการสร้างและจัดการ UI บน Roblox ซึ่งจะไม่สามารถทำได้ด้วยแนวทางเชิงประกาศ โครงสร้างและตรรกะ UI ที่ซ้ำซ้อนบางส่วนยังถูกทำให้เป็นนามธรรมใน ส่วนประกอบ ที่ใช้ซ้ำได้เพื่อหลีกเลี่ยงกับดักทั่วไปในรูปแบบ UI เชิงบังคับ
สถาปัตยกรรมระดับสูง

เลเยอร์และส่วนประกอบ
ใน Plant โครงสร้าง UI ทั้งหมดจะเป็น Layer หรือ Component
- Layer ถูกกำหนดให้เป็นซิงเกิลตันการจัดกลุ่มระดับสูงที่ห่อหุ้มโครงสร้าง UI ที่สร้างไว้ล่วงหน้าใน ReplicatedStorage เลเยอร์อาจมีส่วนประกอบจำนวนหนึ่งหรืออาจห่อหุ้มตรรกะของตนเองทั้งหมด ตัวอย่างของเลเยอร์คือเมนูสินค้าคงคลังหรือจำนวนเหรียญในหน้าจอแสดงผล
- Component เป็นองค์ประกอบ UI ที่ใช้ซ้ำได้ เมื่อวัตถุส่วนประกอบใหม่ถูกสร้างขึ้น มันจะโคลนแม่แบบที่สร้างไว้ล่วงหน้าจาก ReplicatedStorage ส่วนประกอบอาจมีส่วนประกอบอื่นๆ อยู่ภายใน ตัวอย่างของส่วนประกอบคือคลาสปุ่มทั่วไปหรือแนวคิดของรายการสินค้า
การจัดการมุมมอง
ปัญหาการจัดการ UI ที่พบบ่อยคือการจัดการมุมมอง โครงการนี้มีเมนูและรายการ HUD ที่หลากหลาย ซึ่งบางรายการฟังการป้อนข้อมูลจากผู้ใช้ และการจัดการอย่างระมัดระวังว่าเมื่อใดที่พวกเขาจะมองเห็นหรือเปิดใช้งานนั้นจำเป็น
Plant แก้ปัญหานี้ด้วยระบบ UIHandler ซึ่งจัดการว่าเลเยอร์ UI ควรจะมองเห็นได้หรือไม่ เลเยอร์ UI ทั้งหมดในเกมจะถูกจัดประเภทเป็น HUD หรือ Menu และการมองเห็นของพวกเขาจะถูกจัดการโดยกฎต่อไปนี้:
- สถานะเปิดใช้งานของเลเยอร์ Menu และ HUD สามารถสลับได้
- เลเยอร์ HUD ที่เปิดใช้งานจะแสดงเฉพาะเมื่อไม่มีเลเยอร์ Menu ที่เปิดใช้งาน
- เลเยอร์ Menu ที่เปิดใช้งานจะถูกเก็บไว้ในสแตก และมีเพียงเลเยอร์ Menu หนึ่งเดียวที่มองเห็นได้ในเวลาเดียวกัน เมื่อเลเยอร์ Menu ถูกเปิดใช้งาน มันจะถูกแทรกไปข้างหน้าของสแตกและแสดง เมื่อเลเยอร์ Menu ถูกปิดใช้งาน มันจะถูกลบออกจากสแตกและเลเยอร์ Menu ที่เปิดใช้งานถัดไปในคิวจะถูกแสดง
แนวทางนี้เป็นที่เข้าใจได้เพราะมันอนุญาตให้เมนูถูกนำทางด้วยประวัติ หากเมนูหนึ่งถูกเปิดจากเมนูอื่น การปิดเมนูใหม่จะทำให้เมนูเก่าแสดงอีกครั้ง
ซิงเกิลตันเลเยอร์ UI ลงทะเบียนตัวเองกับ UIHandler และได้รับสัญญาณที่ถูกเรียกเมื่อความสามารถในการมองเห็นของมันควรเปลี่ยนแปลง
การอ่านเพิ่มเติม
จากภาพรวมที่ละเอียดของโครงการ Plant คุณอาจต้องการสำรวจคู่มือดังต่อไปนี้ซึ่งเจาะลึกในแนวคิดและหัวข้อที่เกี่ยวข้อง
- โมเดลไคลเอนต์-เซิร์ฟเวอร์ — ภาพรวมของโมเดลไคลเอนต์-เซิร์ฟเวอร์ใน Roblox
- เหตุการณ์ระยะไกลและการเรียกกลับ — เกี่ยวกับเหตุการณ์เครือข่ายระยะไกลและการเรียกกลับสำหรับการสื่อสารข้ามพรมแดนไคลเอนต์-เซิร์ฟเวอร์
- UI — รายละเอียดเกี่ยวกับวัตถุและการออกแบบส่วนติดต่อผู้ใช้ใน Roblox