Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 12 Next capture
2021 2022 2023
1 capture
12 Jun 22 - 12 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
0
Question by RealCool_Guy · Sep 06, 2018 at 07:10 AM · monobehaviourscriptable objectstateactionfinite-state-machine

Rewriting a MonoBehaviour based action using a Finite State Machine

Hello, I am having a little difficulty trying to figure out how I can transform already written code (using Monobehaviour), which dictates a player action, to a Finite State Machine which uses States and pluggable Scriptable objects to perform the same action.

I have a marble, which is the player object, which has a Dash Ability. If it is in range of a Dash-Pad, a gamepad placed at the beginning of a pre-made Path, and the user presses SHIFT, the marble will dash along every waypoint in the Path, then return back to normal speed at the end. The code that I use is shown below

 public class PathwayDash : MonoBehaviour
 {
     public SlowMotion slowmo;
 
     private int pickup_length;
     private int path_id = 0;
     private int path_end = 0;
     private int dash = 0; // start/end the dash
     private int shift_press = 0; //when shift is pressed
     private int entered = 0; //signifies when touching a dash pad
 
     private float entry_speed;
     private Rigidbody rb;
 
     [Header("Pathing")]
     public PickupConnect Path; // Attach to corres. path
     public GameObject dashpad;
 
 
     [HideInInspector]
     public MarbleSmoothRoll marbleroll;
     [HideInInspector]
     //public MarbleUserControl marblecontrol;
 
     [Header("Variables")]
     public float rotation_speed = 5.0f;
     public float reach_distance; //distance to start moving
      //the minimum distance while traveling to a waypoint //the marble must be, before checking to go to next //waypoint
     public float reach_min;
     private float distance;
     [Range(1f, 1000f)]
     public float path_speed;
 
 
     private Vector3 desired_velocity;
     private Vector3 resultant_velocity;
     private Vector3 current_velocity;
     private Vector3 current_position;
     private Vector3 local_vel;
 
     // Use this for initialization
     void Start ()
     {
         pickup_length = Path.pickups.Count - 1;
         print("Pickup length is:" + pickup_length);
         marbleroll = GetComponent<MarbleSmoothRoll>();
 
         rb = GetComponent<Rigidbody>();
     }
 
     private void Update()
     {
         if (entered == 1) //it touched the dashpad
         {
             if (Input.GetKeyDown(KeyCode.LeftShift)) //shift engages the actual dash while on the dashpad
             {
                 shift_press = 1;
                 dash = 1; //signify that the dash has started
                 marbleroll.enabled = false;
                 //marblecontrol.enabled = false;
                 entry_speed = rb.velocity.magnitude; //record the entry speed as the marble enters
                 slowmo.SlowMo();
             }
             else { }          
         }
         else
         {
             shift_press = 0;
         }
 
     }
 
     // Update is called once per frame
     public void FixedUpdate ()
     {
         distance = Vector3.Distance(Path.pickups[path_id].position, transform.position); //Get distance 
         if (dash == 1) //if the dash has started
         {      
             GoToNextPickup(); //move towards current set pickup of path_id          
         }
         else { }
     }
 
     void GoToNextPickup()
     {
         //print("***TimeScale is at: " + Time.timeScale + "\n");
         // print("the current velocity is: " + rb.velocity);
         if (path_end == 1) //it is going towards last pickup
         {
             if(distance < reach_min)
             {
                 dash = 0; //no longer dashing
                 print("Ending dash");
                 SetExitVelocity();
                 slowmo.SlowMo(); //call slow motion to toggle off slowmotion
             }
             else
             {
                 VelocityChange();
             }
         }
         else
         {
             VelocityChange(); //change velocity towards current pikcup
             var rotation = Quaternion.LookRotation(Path.pickups[path_id].position - transform.position);
             transform.rotation = Quaternion.Slerp(transform.rotation, rotation, Time.deltaTime * rotation_speed);
 
             if (distance < reach_min) //If marble is now close to target pickup, make next pickup the target
             {             
                 path_id++; //used to identify next pickup in array
                 print("***Current path_id***: " + path_id + "\n");
                 if (path_id >= pickup_length)
                 {
                     print("End of the path with path_id:" + path_id);
                     path_end = 1; //signify end of path
                 }
             }
         }
       
     }// End GoToNextPickup()
 
     void SetExitVelocity() //used to find the vector of direction for exiting a path and then setting the velocity. Uses the positions of the last two pickups to determine vector.
     {                       //Exits with speed of entry velocity
         distance = Vector3.Distance(Path.pickups[Path.pickups.Count - 1].position, Path.pickups[Path.pickups.Count - 2].position); //Get distance to target pickup
         Vector3 direction = new Vector3(0,0,0);
         direction = (Path.pickups[Path.pickups.Count-1].position) - (Path.pickups[Path.pickups.Count-2].position); //last two pickups make direction
         direction = direction/distance;
         //print("Direction:" + direction + "\n");
         direction = direction * entry_speed ;
         //direction.y = 0f;
         //print("Entry speed: " + entry_speed);
         //print("Setting Exit Velocity: " + direction  + "\n");
         //print("rb.velcoity is : " + rb.velocity + "\n");
         resultant_velocity = direction - rb.velocity;
         rb.AddForce(resultant_velocity, ForceMode.VelocityChange);      
        // print("Velocity of resultant is: " + resultant_velocity + "\n");     
         //print("Exit velocity actually is: " + rb.velocity + "\n");
         path_id = 0;
         path_end = 0;
        
 
     } //End SetExitVelocity()
 
 
     void VelocityChange() //finds vector to pickup, normalizes the vector then multiplies by a speed factor.
     {
         current_velocity = rb.velocity; //current velocity
         print("Current velocity is: " + current_velocity + "\n");
 
         desired_velocity = (((Path.pickups[path_id].position) - transform.position)/distance) * path_speed *
         Time.unscaledDeltaTime/Time.timeScale; //wanted velocity, unscaledDeltaTime/timescale ensures same speed no matter the timescale
         //desired_velocity.y = 0f; //don't want to go in y direction for now
         print("Desired velocity is: " + desired_velocity + "\n");
 
         resultant_velocity = desired_velocity - rb.velocity; //needed velocity to add to reach desired_velocity
         //rb.velocity = desired_velocity;
         rb.AddForce(resultant_velocity, ForceMode.VelocityChange); //change the velocity instantaneously
 
     }//End Velocity Change
 
     private void OnTriggerEnter(Collider other)
     {
         if (other.gameObject == dashpad) //if the marble is currently touching the dash pad
         {
             entered = 1;
             //print("currently on Dashpad");
             if (shift_press == 1) //only start if shift is pressed
             {
                 print("Shift was pressed while on dashpad \n");   
                
             }
         }
 
     }
 
     private void OnTriggerExit(Collider other)
     {
         if (other.gameObject == dashpad)
         {
             entered = 0;
         }
     }
 
 }

It looks like too much, but it's pretty simple. While it isn't the end of the Dash, the code will continue to run GoToNextPickup(). In GoToNextPickup(), the function VelocityChange() simply changes the velocity to go towards the target pickup, if it gets close enough to that pickup, which is dictated by the variable reach_min, GoToNextPickup will increase the index of the pickup array so that the target pickup is now the next pickup.

Once it reaches the final pickup I have a SetExitVelocity code which will stop the dash and return back to the speed with which the marble entered the dash.

It may not be the cleanest way, but I've worked on it long enough to iron out any bug I encountered, and in the end it works well.

However, now I am trying to clean up the implementation of the Dash action. I've research Scriptable Objects and watched videos on creating Finite State Machine. I feel like this would be a very good fit to replace my original dash action. It will also give me the opportunity to easily create new actions. However I'm stuck on exactly how to transition this code over.

Following Richard Fine's example for Pluggable AI, I've created the following scripts

Dash Action Script

 [CreateAssetMenu(menuName = "Pluggable/Actions/Dash")]
 public class DashAction : Action
 {
     public override void Act(StateController controller)
     {
        
     }
 
     private void Dash(StateController controller)
     {
 
     }
 
 }


Action Script

 public abstract class Action : ScriptableObject
 {
     public abstract void Act(StateController controller);
 
 }


State Controller Script

 public class StateController : MonoBehaviour
 {
     public State currentState;
     private bool DashActive;
     [HideInInspector] public Rigidbody Object; //Object that will be controlled
     [HideInInspector] public List<Transform> wayPointList;
     [HideInInspector] public int nextWayPoint;
 
     private void Awake()
     {
         Object = GetComponent<Rigidbody>();
     }
 
     private void Update()
     {
         if (!DashActive) return;
         currentState.UpdateState(this);
     }
 
     private void OnDrawGizmos()
     {
         if(currentState != null)
         {
             Gizmos.color = currentState.sceneGizmocolor;
             Gizmos.DrawWireSphere(transform.position, 1f);
         }
     }
 }

State Script

 [CreateAssetMenu(menuName = "Pluggable/State")]
 public class State : ScriptableObject
 {
     public Action[] actions;
     public Color sceneGizmocolor = Color.grey;
 
     public void UpdateState(StateController controller)
     {
         DoActions(controller);
     }
 
     public void DoActions(StateController controller)
     {
         for (int i= 0; i<actions.Length; i++)
         {
             actions[i].Act(controller);
         }
     }
 
 }

I'm just trying to transfer the Dash action solely before adding in any transitions from state to state. I'm just a little confused how to even start rewritting the function in the DashAction scriptable object. I'm assuming that in the Dash() function of the Dash:Action script I should put my GoToNextPickup function, and the current State of my Marble will be the condition that decides if it continues dashing, but im still unsure of how to transfer the code and also unsure of how I'm supposed to implement the StateController contoller variable.

Any help or tips would be greatly appreciated

Comment
Add comment
10 |3000 characters needed characters left characters exceeded
â–¼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

0 Replies

· Add your reply
  • Sort: 

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

88 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

How to do a custom editor for a ScriptableObject that is a property of a MonoBehaviour 2 Answers

What are ways to give Scriptable objects a state? 0 Answers

Why child ScriptableObjects class instance cannot be placed in their parent ScriptableObject field in the inspector? 1 Answer

Unexpected state transition in animator controller 0 Answers

Support for Func and Action delegate on Iphone micro mscorlib? 1 Answer


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges