ความพยายามของฉันในการเรียนรู้วิธีใช้ Finite State Machines เป็นอย่างไร
สิ่งหนึ่งที่ฉันพยายามทำให้สำเร็จในช่วงไม่กี่เดือนที่ผ่านมาคือการจัดระเบียบโค้ดและรูปแบบการออกแบบที่เหมาะสม การจัดการโค้ดที่เหมาะสมสำหรับฉันถือเป็นแนวคิดสำคัญที่ต้องทำความเข้าใจ เนื่องจากมันจะบังคับให้คุณสร้างโค้ดในลักษณะที่สามารถจัดการและเข้าใจได้ง่าย ความล้มเหลวในการจัดระเบียบฐานโค้ดอาจทำให้โค้ดของคุณเป็น "สิ่งนี้" หรือ "สิ่งนี้" ซึ่งจะนำไปสู่โค้ดสปาเก็ตตี้ในรูปแบบฝันร้าย
หนึ่งในวิธีการเหล่านี้เรียกว่า "รูปแบบของรัฐ" และเป็นวิธีที่ฉันใช้อยู่
มีปัญหาอะไร?
ลองจินตนาการว่าคุณกำลังพัฒนาระบบการเคลื่อนไหวของผู้เล่นสำหรับเกม คุณต้องการที่จะอยู่เฉยๆ เดินวิ่งและกระโดด แต่ละสถานะเหล่านี้มีการตั้งค่าเฉพาะที่ฝังอยู่ในนั้นเพื่อกำหนดว่าผู้เล่นจะเคลื่อนไหวอย่างไร หากทำได้ การเดินจะทำให้คุณเคลื่อนที่ด้วยความเร็วปานกลาง วิ่งเร็วขึ้น ไม่มีการเคลื่อนไหวใดๆ เลย และการกระโดดจะทำให้คุณเคลื่อนที่บนแกน Y
สิ่งหนึ่งที่เราต้องทำให้แน่ใจในระบบเช่นนี้ก็คือ รัฐควรจะแยกจากกัน คุณไม่สามารถวิ่งและเดินในเวลาเดียวกันได้
วิธีหนึ่งที่เราสามารถนำระบบดังกล่าวไปใช้มีดังนี้:
เรามีสถานะสี่สถานะจากตัวแปร enum ซึ่งได้รับการประเมินในชุดคำสั่ง if ในลูป Update โค้ดอาจดูสั่นคลอนเล็กน้อย แต่สำหรับสิ่งที่เราตั้งใจจะทำ มันก็ใช้ได้ดี
แต่ปัญหาก็เกิดขึ้นเมื่อคุณต้องการขยายระบบโดยการเพิ่มสถานะหรือเพิ่มฟังก์ชันการทำงานที่ซับซ้อนให้กับทุกรัฐ หากเราเพิ่มสถานะอื่น เราจะต้องเขียนฟังก์ชันการทำงานในไฟล์เดียวกัน รวมถึงคำสั่ง if อีกอันหนึ่งเพื่อตรวจสอบว่าเราสามารถเปลี่ยนไปสู่สถานะนั้นได้หรือไม่ นี่เป็นฝันร้ายเมื่อพูดถึงแนวคิดเรื่อง "ความรับผิดชอบเดี่ยว" สำหรับชั้นเรียนนี้เช่นกัน เนื่องจากไม่เพียงแต่รับผิดชอบต่อแต่ละรัฐและฟังก์ชันการทำงานเท่านั้น แต่ยังรวมถึงการเปลี่ยนแปลงด้วย
เราจะแก้ไขปัญหานี้ได้อย่างไร
นี่คือจุดที่รูปแบบของรัฐเข้ามาช่วยเหลือ แนวคิดก็คือเราควรสร้างคลาสสำหรับทุกสถานะที่ไม่ซ้ำกันในการเคลื่อนไหวของผู้เล่นของเรา จากนั้นป้อนมันผ่านเครื่องสถานะที่มีขอบเขตจำกัด
แผนภาพอาจช่วยได้
นี่คือแผนภาพ UML ของรูปแบบสถานะ อาจจะดูน่ากลัว แต่ผมจะอธิบายให้ดีที่สุด
เรามีเครื่องสถานะ ในกรณีนี้เรียกว่า "บริบท" ซึ่งกำลังป้อนข้อมูลในรูปแบบของ "สถานะ" และฟังก์ชันตามสถานะนั้น รัฐทำหน้าที่เป็นผู้ปกครองของ "รัฐที่เป็นรูปธรรม" ซึ่งเป็นรัฐที่แท้จริง ระดับบนสุดมีฟังก์ชันการทำงานที่คงที่ตลอดรายการย่อยทั้งหมด ในกรณีนี้คือ “handle()”
ความพยายามในการดำเนินการของฉัน
ตอนนี้ฉันจะให้การดำเนินการ โปรดจำไว้ว่าไม่มีคำตอบที่ผิดที่นี่ และหากคุณเชื่อว่าโค้ดของฉันสามารถปรับปรุงได้ โปรดแจ้งให้เราทราบ
ในกรณีของเรา เรามีคลาส “PlayerMovement” ซึ่งทำหน้าที่เป็น “บริบท” จากนั้นเราก็มีคลาสนามธรรมที่เรียกว่า “สถานะ” จากนั้นในที่สุดเราก็มีสถานะการเคลื่อนไหวในรูปแบบของ “WalkingState”, “IdlingState” “RunningState” และ “JumpingState”; ทั้งหมดนี้สืบทอดมาจาก "รัฐ"
เรามาดูการดำเนินการนี้กัน
นี่คือคลาส “PlayerMovement”
แล้วชั้นเรียน "รัฐ"
และนี่คือคลาส “IdlingState” ซึ่งมีโครงสร้างเหมือนกับสถานะอื่นๆ
อย่างที่คุณเห็นเราได้แยกฟังก์ชันการทำงานในคลาสใหญ่หนึ่งคลาสออกเป็นคลาสเล็ก ๆ หลายคลาส แต่ละรัฐมีหน้าที่รับผิดชอบต่อการเคลื่อนไหวของผู้เล่นแต่ในการตีความของตนเอง พวกเขายังต้องรับผิดชอบในการตัดสินใจว่าจะเปลี่ยนไปสู่รัฐอื่นเมื่อใด
วิธีการแยกสถานะออกเป็นคลาสของตัวเองช่วยให้เราสามารถสร้างฟังก์ชันการทำงานแบบกำหนดเองในลักษณะที่เหนียวแน่นและสะอาดตา
เราสามารถปรับปรุงได้หรือไม่?
รูปแบบสถานะนี้ดีมากสำหรับการแยกฟังก์ชันการทำงานของแต่ละสถานะ และช่วยให้มั่นใจว่าระบบใช้งานง่าย อ่าน และขยาย ปัญหาเดียวคือแม้สิ่งนี้จะกลายเป็นเรื่องยากในการจัดการเมื่อมีการแนะนำรัฐจำนวนมาก
จดจำ; สำหรับแต่ละรัฐ เรายังต้องกำหนดค่าการเปลี่ยนผ่านไปยังสถานะอื่นๆ ที่เกี่ยวข้อง และในบางกรณี เราอาจต้องใช้วิธีทำซ้ำตัวเองเพื่อให้บรรลุเป้าหมายนี้
นี่คือตัวอย่าง; พูดได้อย่างยุติธรรมว่าจากสภาวะการเดิน การวิ่ง และการเกียจคร้าน เราควรจะเปลี่ยนไปสู่ภาวะการกระโดดได้ ปัญหาคือเรากำลังเขียนโค้ดเดียวกันเพื่อตรวจสอบว่าเราสามารถเปลี่ยนไปใช้ Jump ได้หรือไม่
วิธีแก้ปัญหาคือการใช้ เครื่องสถานะแบบลำดับชั้นโดยที่เราจัดกลุ่มสถานะที่เกี่ยวข้องเข้าด้วยกัน ซึ่งเรียกว่าสถานะย่อยเป็นสถานะขั้นสูง รัฐขั้นสูงนั้นทำหน้าที่เป็นศูนย์กลางสำหรับการทำงานของสถานะทั่วไป และรัฐภายนอกสามารถเปลี่ยนเป็นรัฐขั้นสูงนั้นได้ แต่ไม่ใช่รัฐย่อย
ฉันมีประสบการณ์เพียงเล็กน้อยในเครื่องสถานะแบบลำดับชั้น ดังนั้นฉันจึงไม่สามารถแสดงวิธีการใช้งานเครื่องเหล่านี้ได้มากนัก แต่เป็นสิ่งที่ฉันอยากจะพูดถึง
State Machine นั้นค่อนข้างง่ายที่จะเข้าใจ แต่การทำความเข้าใจว่าจะต้องใช้งานอย่างไรและเมื่อใดนั้นค่อนข้างท้าทาย หากคุณกำลังดิ้นรน ฉันขอแนะนำให้ลองใช้สถานการณ์เหมือนที่ฉันได้สรุปไว้
กรณีการใช้งานอื่น ๆ ?
รูปแบบของรัฐมีความหลากหลายมาก กรณีการใช้งานที่พบบ่อยที่สุดอย่างหนึ่งคือในเกมปัญญาประดิษฐ์
คุณสามารถให้ศัตรูย้ายสถานะได้ ตั้งแต่ "โจมตี" ไปจนถึง "ลาดตระเวน" "ไล่ล่า" ไปจนถึง "สืบสวน" ฯลฯ คุณยังสามารถมีสถานะสุขภาพของตัวละคร เช่น "เดินกะโผลกกะเผลก" "สุขภาพดี" หรือ "คริติคอล" ได้ ". กรณีการใช้งานแทบจะไม่มีที่สิ้นสุด ดังนั้นการทำความเข้าใจ Finite State Machines จะทำให้คุณมีความได้เปรียบในอาชีพการเขียนโปรแกรมของคุณได้อย่างแน่นอน
ขอขอบคุณที่อ่านบทความของฉัน ฉันหวังว่าคุณจะสนุกกับการอ่านในขณะที่ฉันเขียนมัน ฉันหวังว่าคุณจะคอยติดตามโพสต์ของฉันต่อไป
แล้วเจอกัน!