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 KronosAmon · Jun 07, 2019 at 08:24 PM · coroutinefunctioninvoke

Dynamic function calling

I need a method to dynamically call different functions from a huge pile of functions.

.

A small example part of my problem;
There are passive defensive skills which automatically activate when the player about to takes damage.
Problem is there are hundreds of skills and only a couple of it active at a time.
I don't want to call every single function and check if they are currently active,

.

For example, let's say I have Functions as;
SuperBlock, Avoid, ThornMail, CounterAttack, X, Y, Z, SuperArmor ... etc

.

And only "SuperBlock" is activated for the player so I only need to call SuperBlock function.

.

So basically I am trying to avoid doing this;

 bool SuperBlockIsActive = true;
 bool AvoidIsActive = false;
 bool ThornMailIsActive = false;
 bool CounterAttackIsActive = false;

 public void DefensiveSkills(int damage, string attackType)
 {                                                                                       
     if (SuperBlockIsActive)
     {
         SuperBlock (damage, attackType);
     }
     if (AvoidIsActive)
     {
         Avoid (attackType);
     }
     if (ThornMailIsActive )
     {
         ThornMail (damage, attackType);
     }
     if (CounterAttackIsActive )
     {
         CounterAttack (damage, attackType);
     }
 }

 ....

I tried to solve this problem with "Invoke" function but because I need to pass parameters I couldn't achieve what I needed.
What I did was creating a list of active function names and calling them with Invoke.

 private List<string> ActiveDefensiveSkills = new List<string>(); 
 
 private void StartTurn()
 {
     foreach (string function in ActiveDefensiveSkills)
     {
         Invoke(function, 0f);
     }
 }

I need to call functions with parameters like damage, attackType, etc.
I also tried using global variables but because the compiler does not wait for Invoke and continue the code, it doesn't work either.

.

How can I solve this problem?
Is there even a way?

.

Edit:
I decided to share the whole idea just to be more clear so you don't need to read the rest if you are not interested in my code, I am doing a turn-based game and some attack directly hurt the player (Like getting attacked with a sword) and some attacks give damage each turn (Like burn damage for 3 turns).

 using System.Collections.Generic;
 using UnityEngine;
 
 
 [System.Serializable]
 public class Conditions
 {
     public string attackName;
     public int attackDamage;
     public string attackType;
     public int attackTurn;
     public int attackPenetration;
 
     public void NewCondition(string aName, int aDamage, string aType, int aTurn, int aPenetration)
     {
         attackName = aName;
         attackDamage = aDamage;
         attackType = aType;
         attackTurn = aTurn;
         attackPenetration = aPenetration;
     }
 }
 
 
 public class Player : MonoBehaviour
 {
 
     [Header("[Current]")]
     [SerializeField] private int currentHealth = 0;
 
     [Header("[Basic]")]
     [SerializeField] private int maxHealth = 100;
 
     [Header("[Defensive]")]
     [SerializeField] private int armor = 0;
     [SerializeField] private int magicResistance = 0;
     [SerializeField] private float blockChance = 0f;
 
     [Header("[ActiveSkills]")]
     [SerializeField] private List<string> ActiveDefensiveSkills = new List<string>(); //Trigger when get attacked
     [SerializeField] private List<Conditions> ActiveConditions = new List<Conditions>(); //List of active Conditions
 
 
     //---MainFunctions----
 
 
     private void StartTurn()
     {
         //Activated at the beginning of Player's each turn
         foreach (Conditions condition in ActiveConditions)
         {
             Defense(condition.attackDamage, condition.attackType, condition.attackPenetration);
             condition.attackTurn--;
             if (condition.attackTurn <= 0) ActiveConditions.Remove(condition);
         }
     }
 
     public void GetAttacked (string attackName, int attackDamage, string attackType, int attackTurn, int attackPenetration)
     {
         //Enemy calls this function and everything starts from here, after applying the first damage if the attack is turn-based then it gets added to conditions.

         Defense(attackDamage, attackType, attackPenetration);
 
         if (attackTurn > 1)
         {
             attackTurn--;
             Conditions condition = new Conditions();
             condition.NewCondition(attackName, attackDamage, attackType, attackTurn, attackPenetration);
             ActiveConditions.Add(condition);
         }
 
     }
 
     private void Defense(int attackDamage, string attackType, int attackPenetration)
     {
         //Function called when the player takes damage
 
 
         //This is where I have a problem, I want to call only active defensive skills (their name is stored in "ActiveDefensiveSkills")
         foreach (string function in ActiveDefensiveSkills)
         {
             // attackDamage = function(int attackDamage, string attackType, int attackPenetration); i want to do something like this
         }
 
         TakeDamage(attackDamage);
     }
 
     private void TakeDamage(int damage)
     {
         //Implement the damage
         Debug.Log("You took " + damage + " dmg");
 
         if (currentHealth - damage <= 0)
         {
             currentHealth = 0;
             Death();
         }
         else
         {
             currentHealth -= damage;
         }
     }
 
     private void Death()
     {
         Debug.Log("You Died");
     }
 
     //---SkillFunctions---
 
     private int Block(int attackDamage, string attackType, int attackTurn, int attackPenetration)
     {
         //Player can block the some of the incoming damage
         if (blockChance >= Random.Range(1, 101))
         {
             Debug.Log("Damage blocked");
             return attackDamage / 2;
         }
         else return attackDamage;
     }
 
     private int Resistance(int attackDamage, string attackType, int attackTurn, int attackPenetration)
     {
         //Decrease the incoming damage     
         int result = armor - attackPenetration;
         if (result < 0) result = 0;
 
         return attackDamage - result >= 0 ? attackDamage - result : 0;
     }
 
     private int LastStand(int attackDamage, string attackType, int attackTurn, int attackPenetration)
     {
         //Player health will not drop under 1
         return currentHealth - attackDamage >= 1 ? attackDamage : 0;
     }
 
     private int Invulnerability(int attackDamage, string attackType, int attackTurn, int attackPenetration)
     {
         //Player become invulnerable to any kind of attacks
         return 0;
     }
 
 }
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

2 Replies

· Add your reply
  • Sort: 
avatar image
1
Best Answer

Answer by Hellium · Jun 07, 2019 at 09:27 PM

Fore sure there is a way, many ways!


Given the (few) details you gave about your system, a simple solution would be to make all your functions with the same signature: void FunctionName( int damage, string attackType ), even if all the parameters are not used inside the function.


Then, you would use a delegate to "hold" references to the functions to call.


 private System.Action<int,string> activeDefensiveSkills;
 
 private bool SuperBlockIsActive
 {
     set
     { 
          activeDefensiveSkills -= SuperBlock;
          if( value ) activeDefensiveSkills += SuperBlock
     }
 }
 
 private bool AvoidIsActive
 {
     set
     { 
          activeDefensiveSkills -= Avoid ;
          if( value ) activeDefensiveSkills += Avoid 
     }
 }
 private bool ThornMailIsActive
 {
     set
     { 
          activeDefensiveSkills -= ThornMail ;
          if( value ) activeDefensiveSkills += ThornMail 
     }
 }
 private bool CounterAttackIsActive
 {
     set
     { 
          activeDefensiveSkills -= CounterAttack ;
          if( value ) activeDefensiveSkills += CounterAttack 
     }
 };
 
  public void DefensiveSkills(int damage, string attackType)
  {                                                                                       
      if (activeDefensiveSkills != null)
      {
          activeDefensiveSkills(damage, attackType);
      }
  }


But, with more details about your system, a more flexible and less verbose solution may be considered

Comment
Add comment · Show 3 · Share
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
avatar image KronosAmon · Jun 07, 2019 at 11:14 PM 0
Share

Okay, "delegate" looks very promising and you are right I shared the some of the code just to be sure, I tried to cut unimportant parts and just left the "Defense" part and I added some of the actual skills. Is it possible if you can show an example of how I can edit my code in the way you showed?

avatar image Hellium KronosAmon · Jun 08, 2019 at 10:18 AM 1
Share

@Liakan


After reviewing the code you have provided, I suggest you to rework it. Your Player class is responsible for too many different things. I believe your current class is at least 1000 or 2000 lines long. Isn't it?


I spent two hours coding a possible rework for your system. I believe the changes are quite heavy, but for maintainability reasons, ease of use and ease of evolution, you should consider it.


The code is commented enough to let you understand how it works, if it's not clear enough, don't hesitate to post comments at the bottom of the page so that we can discuss about it.

avatar image JDelekto Hellium · Jun 08, 2019 at 04:03 PM 0
Share

I like how you used the ability to add/remove handlers from the delegate instance. Uses the intrinsic abilities of the delegate more than my approach. Nice!

avatar image
1

Answer by JDelekto · Jun 08, 2019 at 12:57 AM

One way that I would do this is by having kind of a "sack" of skills that can be added or removed from the script that is attached to your player. The actual actions are private and there are basically three public methods: a) one to add a skill, b) one to remove a skill; and c) one to evoke all the skills that are currently enabled. I typically use a Dictionary of skills (through an enum) which map to a delegate (all of which have the same signature). Here is an example:

 using System;
 using System.Collections;
 using System.Collections.Generic;
 using UnityEngine;
 
 public enum Skill
 {
     SuperBlock,
     Avoid,
     ThornMail,
     CounterAttack
 }
 
 public class PlayerActions : MonoBehaviour
 {
     private Dictionary<Skill, Action<int, string>> actions = new Dictionary<Skill, System.Action<int, string>>();
     
     // Awake is called when the script instance is being loaded.
     protected void Awake()
     {
         // Add 2 skills here (should be called from the outside)
         this.AddSkill(Skill.SuperBlock);
         this.AddSkill(Skill.Avoid);
         
         // Test the use of skills here, but UseDefensiveSkills() should be called from the outside...
         this.UseDefensiveSkills(3, "simple");
         
         // Remove a skill here (should be called from the outside)
         this.RemoveSkill(Skill.SuperBlock);
         
         // Test the use of skills here, but UseDefensiveSkills() should be called from the outside...
         this.UseDefensiveSkills(10, "advanced");        
     }
 
     public void UseDefensiveSkills(int damage, string attackType)
     {
         foreach (Action<int, string> action in actions.Values)
         {
             action(damage, attackType);
         }
     }
 
     public void AddSkill(Skill skillType)
     {
         if (!actions.ContainsKey(skillType))
         {
             switch (skillType)
             {
             case Skill.SuperBlock:
                 actions.Add(skillType, this.SuperBlock);
                 break;
             case Skill.Avoid:
                 actions.Add(skillType, this.Avoid);
                 break;                    
             case Skill.ThornMail:
                 actions.Add(skillType, this.ThornMail);
                 break;                    
             case Skill.CounterAttack:
                 actions.Add(skillType, this.CounterAttack);
                 break;
             default:
                 break;
             }
         }
     }
 
     public void RemoveSkill(Skill skillType)
     {
         if (actions.ContainsKey(skillType))
         {
             actions.Remove(skillType);
         }
     }
 
     private void SuperBlock(int value, string text)
     {
         Debug.Log("SuperBlock!");
     }
 
     private void Avoid(int damage, string attackType)
     {
         Debug.Log("Avoid!");
     }
 
     private void ThornMail(int damage, string attackType)
     {
         Debug.Log("ThornMail!");
     }
 
     private void CounterAttack(int damage, string attackType)
     {
         Debug.Log("CounterAttack!");
     }
 }
Comment
Add comment · Share
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

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

117 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 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 loop a function? 2 Answers

How to use a function from another script inside of the Invoke () class? 2 Answers

Coroutine a function within a loop? 1 Answer

How i can make a function call and then after 15 sec another function call during this function call the first one stops.And this process keep repeating again and again. 1 Answer

function OnTriggerEnter --> if -->if --> Invoke... well, it doesn't work! 2 Answers


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