ความพยายามของฉันในการเรียนรู้วิธีใช้ Finite State Machines เป็นอย่างไร

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

หนึ่งในวิธีการเหล่านี้เรียกว่า "รูปแบบของรัฐ" และเป็นวิธีที่ฉันใช้อยู่

มีปัญหาอะไร?

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

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

วิธีหนึ่งที่เราสามารถนำระบบดังกล่าวไปใช้มีดังนี้:

เรามีสถานะสี่สถานะจากตัวแปร enum ซึ่งได้รับการประเมินในชุดคำสั่ง if ในลูป Update โค้ดอาจดูสั่นคลอนเล็กน้อย แต่สำหรับสิ่งที่เราตั้งใจจะทำ มันก็ใช้ได้ดี

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

เราจะแก้ไขปัญหานี้ได้อย่างไร

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

แผนภาพอาจช่วยได้

นี่คือแผนภาพ UML ของรูปแบบสถานะ อาจจะดูน่ากลัว แต่ผมจะอธิบายให้ดีที่สุด

เรามีเครื่องสถานะ ในกรณีนี้เรียกว่า "บริบท" ซึ่งกำลังป้อนข้อมูลในรูปแบบของ "สถานะ" และฟังก์ชันตามสถานะนั้น รัฐทำหน้าที่เป็นผู้ปกครองของ "รัฐที่เป็นรูปธรรม" ซึ่งเป็นรัฐที่แท้จริง ระดับบนสุดมีฟังก์ชันการทำงานที่คงที่ตลอดรายการย่อยทั้งหมด ในกรณีนี้คือ “handle()”

ความพยายามในการดำเนินการของฉัน

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

ในกรณีของเรา เรามีคลาส “PlayerMovement” ซึ่งทำหน้าที่เป็น “บริบท” จากนั้นเราก็มีคลาสนามธรรมที่เรียกว่า “สถานะ” จากนั้นในที่สุดเราก็มีสถานะการเคลื่อนไหวในรูปแบบของ “WalkingState”, “IdlingState” “RunningState” และ “JumpingState”; ทั้งหมดนี้สืบทอดมาจาก "รัฐ"

เรามาดูการดำเนินการนี้กัน

นี่คือคลาส “PlayerMovement”

แล้วชั้นเรียน "รัฐ"

และนี่คือคลาส “IdlingState” ซึ่งมีโครงสร้างเหมือนกับสถานะอื่นๆ

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

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

เราสามารถปรับปรุงได้หรือไม่?

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

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

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

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

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

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

กรณีการใช้งานอื่น ๆ ?

รูปแบบของรัฐมีความหลากหลายมาก กรณีการใช้งานที่พบบ่อยที่สุดอย่างหนึ่งคือในเกมปัญญาประดิษฐ์

คุณสามารถให้ศัตรูย้ายสถานะได้ ตั้งแต่ "โจมตี" ไปจนถึง "ลาดตระเวน" "ไล่ล่า" ไปจนถึง "สืบสวน" ฯลฯ คุณยังสามารถมีสถานะสุขภาพของตัวละคร เช่น "เดินกะโผลกกะเผลก" "สุขภาพดี" หรือ "คริติคอล" ได้ ". กรณีการใช้งานแทบจะไม่มีที่สิ้นสุด ดังนั้นการทำความเข้าใจ Finite State Machines จะทำให้คุณมีความได้เปรียบในอาชีพการเขียนโปรแกรมของคุณได้อย่างแน่นอน

ขอขอบคุณที่อ่านบทความของฉัน ฉันหวังว่าคุณจะสนุกกับการอ่านในขณะที่ฉันเขียนมัน ฉันหวังว่าคุณจะคอยติดตามโพสต์ของฉันต่อไป

แล้วเจอกัน!