Announcement

Collapse
No announcement yet.

Overriding component's functionality

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

    Overriding component's functionality

    So, I am trying to do something apparently simple:
    Lets say I want to manage my characters health via a component. So I create a new BP inheriting from Actor-Component: BP_HealthComp, where I set:

    2 floats: Health and MaxHealth.
    4 functions: TakeDamage, DoHealth, SetHealth, SetMaxHealth

    Now in my HumanCharacter blueprint I add the component created above: BP_HealthComp, and I do the same for my OrcCharacter, DragonCharacter, etc.

    However, values like MaxHealth would differ depending on the character type. And I would need to override some functions like the TakeDamage which is different in a dragon than in a human.


    So, is it possible to override a component's functions? What would be the proper approach if not possible?

    I checked also Interfaces, but from what I could see, you can't set a default implementation with interfaces and you can't add variables to interfaces either.


    Maybe this is not possible via Blueprints and C++ is needed? If so, any tutorial?


    Thanks in advance,
    Jorge.

    #2
    your question is...I don't know how to spell the words for it.

    what is the problem?

    so from what I understand you have this component on all "player" and "monsters" right?

    now you have those variables which define health/armor/etc.
    so, in the variable detail panel in your component you have to check "expose on spawn" and "instance editable" .

    when you add this component on a new actor "dragon?" just click on the component inside " 1 simple click" don't push it too hard, and you will see the variables you made and you could change em as you like.

    now as you said you wanted "take damage" to have some kind of different values depending on your "attach power?" then you need to make some math including some armor values which deduct some damage about to be dealt to the monster "dragon?".

    if my answer didn't help. then your question is not helping me .

    Comment


      #3
      Okja 101 covered the variables part - you just set the component variables to Instance Editable and Expose on Spawn. When you add the component to your actor and select the component in the Components window, you can set those values directly, or create functions/events to set them at runtime.

      For overriding component "functionality," there are a few options. First, keep in mind that if you want to call an event or function on a component, you need to get a reference to the component, not just the component's actor. You can do this by getting a reference to the actor, then plugging that into a "Get Component by Class" node with the Class set to your component class. From the output of that node, you can call events or functions on the component instance.

      If you want an event to have the same effect on all instances of the component, regardless of the owning actor, you just create an event or function in the component as usual.

      If you want an event to have *different* functionality per actor class, one option is to create an Event Dispatcher on the component. I created one here called SetMaxHealth that takes a float parameter (maybe not the best example, since setting max health is probably something you want to be the same across all actors):

      Click image for larger version

Name:	CompDispatcher.jpg
Views:	1
Size:	148.6 KB
ID:	1609211

      Once that is created and you add the component to your actor class, you will have it available as an event when you select the component. Click on the green "+" sign to add it to the actor's event graph and do whatever you want with it:

      Click image for larger version

Name:	NPCDispatcher.jpg
Views:	1
Size:	172.5 KB
ID:	1609212

      Another option would be to use a BP interface. In that case, I would put the interface on the actor, not the component. If you implement the interface on the component, you would need a separate component class for every type of actor (human/dragon/golem etc.), which kind of defeats the purpose.

      Comment


        #4
        Also, in the specific case of "Take Damage," the base Actor class in UE4 has a lot of events that handle damage, including an event called "Any Damage." You might want to use that built-in damage-handling system instead of creating your own.

        Click image for larger version

Name:	Damage.jpg
Views:	1
Size:	245.4 KB
ID:	1609214
        Last edited by DsyD; 04-17-2019, 11:39 PM.

        Comment


          #5
          Hi Okja and DsyD,
          Please note questions are just an example, what I want to build is a solid pipeline where blueprints could be made of components (like adding modules by aggregation).

          So to let each blueprint set each specific values I can use a constructor, that sounds totally fine.
          To let a blueprint override a component's function I can't just override it :'(.

          However, there seems to be 2 workarounds:

          Event Dispatchers
          But I don't get it, since an event dispatcher is (like the publisher in an observer pattern) supposed to broadcast 1 event to many subscribers (each blueprint class).
          Interfaces
          They would be valid if they would let you use a default implementation.

          Another example to clarify, lets say you have objects that can burn. They would have a flammable component to handle their burning behavior. But a torch or a tree would burn differently.

          *Guess I will have to look into C++ for this

          Thanks

          Comment


            #6
            Originally posted by JRamon View Post
            Hi Okja and DsyD,
            Event Dispatchers
            But I don't get it, since an event dispatcher is (like the publisher in an observer pattern) supposed to broadcast 1 event to many subscribers (each blueprint class).
            I think it would be more accurate to say that they're designed to broadcast an event to specific instances of another class that register for them. In this case, the owning Actor instance is "registering" to receive events from the instance of the Component. When the event is called, the Actor can choose to call a default implementation defined on the Component, or it can choose to ignore the default and do something completely different. Or it can do the default, plus something else.

            Can you give an example where the event dispatcher method would not work for you?

            Comment


              #7
              Can you give an example where the event dispatcher method would not work for you?
              The pipeline is thought to be used intensively and... certainly I'm not sure. It would work but involving extra tasks like binding/unbinding and, thinking ahead, I guess later having to do things like changing the signature of the event dispatcher could be a chaos if you are using it in let's say 100 blueprints.


              I am trying to find the best solution and by now I guess it is to use a blueprint component where I would implement all the defaults and then a child-blueprint component that would be the implemented by the blueprints making use of it.

              E.g.:
              MyHealthDefaultComp (for the default implementation)
              MyHealthComp (that would inherit the default imp allowing also to override what I would need)

              So:
              A dragon implements MyHealthComp and overrides TakeDamage to give a shout out if the damage taken is > 1000
              A soldier implements MyHealthComp and overrides TakeDamage to throw himself to the floor
              An officer would seek shelter.
              An animal would start running.
              A wild animal would turn to attack you.
              etc.

              In all cases needed, the default functionality would be applied so each actor health would be reduced having also the possibility to override it.
              If I have to change the signature, let's say an extra input needed, I would only have to change it in the default comp. (I guess).

              Comment


                #8
                Here's how I would start trying to implement that:

                1. Create a single MyHealthComp actor component with a) an Event Dispatcher called TakeDamage with some appropriate signature, and b) an Event called DefaultTakeDamage that implements default behavior (e.g., subtracting from a Health variable). The two signatures do not necessarily need to be the same.

                2. Create a dragon actor and add MyHealthComp. Select MyHealthComp and add TakeDamage to the graph, as I described above. Create a "Shout" event. From the TakeDamage event, send the execution into some set of conditions for calling the Shout event and call it if necessary, then as a final step call DefaultTakeDamage on the MyHealthComp component.

                3. Create an Animal actor and add MyHealthComp. Add TakeDamage to its graph, send its exec output into a "Run Away" function. Don't do anything with the DefaultTakeDamage event on the MyHealthComp component.

                Example 2 is one where you call the default implementation on MyHealthComp, example 3 is one where you don't. There's no binding necessary here, I guess because the Actor is referring to a component that it "owns" (this doesn't seem to be well-documented). So unless I'm misunderstanding something in your examples, there's no need to implement multiple component sub-classes, you should be able to just use one component class.

                Comment


                  #9
                  Hi I tested it and seems to work fine! I still wonder how bind/unbind isn't required while even in epic tutorials it is said that binding is required. I hope a future engine update wouldn't change this (I read interfaces allowed a default implementation while ago but it was dropped).

                  I will make some tests before going for the pipeline, but your solution seems to works pretty good.

                  BTW, are you already using this pipeline in a any released game or app?

                  Comment


                    #10
                    Yeah, normally you do need to explicitly bind/unbind events to dispatchers. With the above method, I believe what's happening is that adding "MyEvent (MyComponent)" to the Actor creates an event on the Actor that is automatically bound to the dispatcher "MyEvent" on the component. I guess this is possible because the Actor "owns" the component, but I couldn't find documentation on it, so it's confusing. I don't know why they don't talk about this in either the Event Dispatcher docs or the Component docs.

                    Comment


                      #11
                      Originally posted by JRamon View Post
                      BTW, are you already using this pipeline in a any released game or app?
                      I don't have any released games but I am using this in a prototype. I have an "Interactor" component that I add to any interactable actor (door, chair, NPC). It automatically handles highlighting the actor in-game when needed, and it has an "Interact" event dispatcher that allows its owning actor to define what it does when the player interacts with it.

                      Comment


                        #12
                        Oh, as an aside regarding interfaces: I think the choice is either a) support multiple inheritance in BPs, or b) provide BP Interfaces. If b) then interfaces really shouldn't have default implementation because there is no mechanism for method resolution (i.e., if two interfaces implement a function with the same name and a class implements both interfaces). If they do provide a mechanism for method resolution, they might as well support multiple inheritance...but I think that would make BPs way more complicated!

                        Comment

                        Working...
                        X