News Forums RAIN Sample Projects, How To, and Code RAIN Actions

This topic contains 9 replies, has 5 voices, and was last updated by  ninjapps 4 months, 3 weeks ago.

Viewing 10 posts - 1 through 10 (of 10 total)
  • Author
    Posts
  • #30996

    Adam
    Participant

    I have been developing a 2D game for some time, using RAIN for the AI and A* Pathfinding project for navigation.
    In the name of good software development practice I have been developing reusable RAIN actions for use in
    my BTs. I have decided to share some. First up is an action that allows you to call a method on a component attached to the body of an AI. I have found this extremely useful for simple tasks like playing effects and such as it allows you to perform a custom action without creating a new class.
    I hope someone finds this useful.

    
    using UnityEngine;
    using System.Collections;
    using RAIN.Action;
    using RAIN.Core;
    using RAIN.Representation;
    using System.Reflection;
    using System;
    [RAINAction]
    public class CallMethod : RAINAction
    {
        public CallMethod()
        {
            actionName = "CallMethod";
        }
        //The name of the component which has the method to call. This should be attached to the AI.Body gameObject. 
        public Expression TargetComponent;
        //The name of the method to call.     
        public Expression TargetMethod;                
        //cache the method signature to avoid expensive reflection calls.
        private MethodInfo _methodInfo;
        private object _component;
        //cache the target names so that we can test if they change
        private string _previousTargetComponent = "";
        private string _previousTargetMethod = "";
        public override void Start(AI ai)
        {
            //test if target has changed
            bool targetComponentChanged = string.IsNullOrEmpty(_previousTargetComponent)  ||
                                         _previousTargetComponent !=  TargetComponent.ExpressionAsEntered;
            bool targetMethodChanged = string.IsNullOrEmpty(_previousTargetMethod)  || 
                                         _previousTargetMethod !=  TargetMethod.ExpressionAsEntered;
            if (targetComponentChanged || targetMethodChanged || _component == null || _methodInfo == null)
            {
                //get target component type
                Type componentType = Type.GetType(TargetComponent.ExpressionAsEntered);
                if (componentType != null)
                {
                    //get the target method signature
                    _methodInfo = componentType.GetMethod(TargetMethod.ExpressionAsEntered);
                    //get the target component
                    _component = ai.Body.gameObject.GetComponent(TargetComponent.ExpressionAsEntered);
                    //update target names
                    _previousTargetMethod = TargetMethod.ExpressionAsEntered;
                    _previousTargetComponent = TargetComponent.ExpressionAsEntered;
                }
            }
        }
        public override RAINAction.ActionResult Execute(AI ai)
        {
            if (_methodInfo == null || _component == null)
            {
                Debug.LogError("RAINAction.CallMethod has failed: Cannot find method to call.");
                return ActionResult.FAILURE;
            }
            //invoke the methods
            _methodInfo.Invoke(_component, null);
            return ActionResult.SUCCESS;
        }
    }
    

    Adam

    #31140

    Drum
    Participant

    Excellent! Just what I was looking for. Thanks!

    #32911

    gitoffame
    Participant

    Hello.
    I’m new to RAIN, and am trying to run a pretty standard .js script from within the behavior tree.
    I assume this is what I need for that, but keep getting “method not found”. I have the name of the script in the TargetComponent field, and script’s function that I’m trying to call in the TargetMethod field. The script is a component of my character’s top-level gameObject.
    Any suggestions?
    Thanks.

    #32963

    prime
    Keymaster

    I’m a bit confused. what TargetComponent field are you referring to? and what TargetMethod field?

    #32964

    prime
    Keymaster

    Oops. I guess I should have read the whole thread.

    Hmm. Seems like a pretty inefficient approach - assuming that it is a bit uncommon to share code between your components and your AI. In most cases I would just grab the Component directly off the object and then call the method without resorting to reflection. Was that code built to handle many shared calls (like dozens?)

    In most cases you will just want to create a Custom Action in RAIN through the behavior tree, then fill in the Start and Execute methods.

    #32974

    Adam
    Participant

    @gitoffame your approach seems correct, but the script wasnt tested with JS. I am at work at the moment but I will test when I get home and see if I can get it to work for JS

    @prime, correct, reflection may not be the most efficient approach, but the script is not meant for heavy lifting. It is meant for simple one off calls that I don’t think warrant an entire custom action. Unity uses reflection extensively. All of the Monobehaviour methods (Awake, Update etc ) are called using reflection for example, and Unity’s messaging systems, which I though RAIN uses, also functions because of reflection. I dont think reflection it is such a major concern as long as you understand the implications of using it. Perhaps I should have mentioned something about these implications in the original post though so people are aware of them.

    #32979

    prime
    Keymaster

    @Adam fair enough. I tend to caution people on the forums because not everyone is a programmer, so not everyone will understand the implications of using reflection. For most people who aren’t power programmers, they’ll be better off just writing custom actions that find and cache the relevant component, then just call methods directly.

    Also, we’ve considered adding a reflection layer ourselves that would allow you access to public methods, properties, and variables on your component scripts within behavior trees. If we get enough interest we’ll do it.

    Thanks

    #33042

    gitoffame
    Participant

    Ah. I’ll admit, first off, that I’m just stumbling into all this. Unfortunately, I’m also pretty unfamiliar with C#. Maybe it’s time I learned a little…
    What I’m trying to do is pretty basic; I have a couple sub-objects in my character object that I want to activate at the start of the attack animation and deactivate at the end of the animation. If I can pull that off, I think I’ll be pretty much set with what I need to accomplish for this project.
    Thanks for your replies.

    #33043

    prime
    Keymaster

    Many ways to do this:

    1) Use Unity’s event system associated with animations. You can set events that are raised when the animation hits certain points in the anim curve, and then handle those in a component on your character object. None of that needs to be done through RAIN.

    2) If you are managing the animations through RAIN and want to manage the activations as well, then just add a Custom Action to your behavior tree to run in parallel with the animation. It can activate your subobjects in the Start method and disable them in the Stop method.

    #35984

    ninjapps
    Participant

    Hi Adam,

    Thanks for sharing!

    Any chance you share how you implemented A* :)

    Thanks in advance.

    • This reply was modified 4 months, 3 weeks ago by  ninjapps.
Viewing 10 posts - 1 through 10 (of 10 total)

You must be logged in to reply to this topic.