Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

Tower defense - Skill Tree

Discussion in 'Scripting' started by Laamouri-Amine, Jan 15, 2020.

  1. Laamouri-Amine

    Laamouri-Amine

    Joined:
    Oct 6, 2018
    Posts:
    3
    Hello dear developers !

    I hope everyone is doing great. I have been searching for a way to solve a certain problem concerning a Skill tree for a tower defense game.

    What i have right now is the skill tree scene. I can do everything except applying the upgrades on the tower and the characters i have in the game. Each skill has a script "Skill" with a bool to check if it's upgraded or not. I saw the techniques using Scriptable Objects, but that doesn't work because i have a variety of different skills. For example each archer/mortar/Mage tower has a script "AttackRanged" responsible for firing projectiles, It's also the script i want to modify.

    This is the script main script :

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.UI;
    5.  
    6. public class SkillMenu : MonoBehaviour
    7. {
    8.     public static SkillMenu instance;
    9.  
    10.     public Skill[] skills;
    11.     public SkillButton[] skillButtons;
    12.     public Image[] connerStars;
    13.  
    14.     public Image skillImage;
    15.     public Text skillStarText;
    16.     public Text skillDesText;
    17.     public Text totalStarsText;//MARKER 最高处星星个数
    18.  
    19.     public SkillManager sManager;
    20.     public Skill activeSkill;
    21.  
    22.     [SerializeField] private int totalStars;
    23.     public int remainingStars;
    24.     /*  public Sprite normalStarSprite;
    25.      public Sprite upgradeStarSprite;*/
    26.  
    27.     public Skill[] arrowSkills;
    28.     public Skill[] soliderSkills;
    29.     public Skill[] boomSkills;
    30.     public Skill[] wizardSkills;
    31.     public Skill[] meteorSkills;
    32.  
    33.  
    34.  
    35.  
    36.     public Text tipText;
    37.     public Transform canvasTrans;
    38.     public Sprite questionSprite;
    39.  
    40.  
    41.  
    42.     private void Awake()
    43.     {
    44.         if (instance == null)
    45.         {
    46.             instance = this;
    47.         }
    48.         else
    49.         {
    50.             if (instance != this)
    51.             {
    52.                 Destroy(gameObject);
    53.             }
    54.  
    55.             DontDestroyOnLoad(gameObject);
    56.         }
    57.     }
    58.  
    59.     private void Start()
    60.     {
    61.         for (int i = 0; i < skills.Length; i++)
    62.         {
    63.             skillButtons[i].buttonValue = i;
    64.         }
    65.  
    66.         int sum = 0;
    67.  
    68.         for (int i = 1; i < 14; i++)
    69.         {
    70.             sum += PlayerPrefs.GetInt("Lv" + i.ToString());//Add the level 1 stars number, level 2 stars number.....
    71.         }
    72.         totalStars = sum;
    73.         remainingStars = totalStars;
    74.         for (int i = 0; i < skills.Length; i++)
    75.         {
    76.             if (skills[i].isUpgraded)
    77.             {
    78.                 remainingStars -= skills[i].starsNumber;
    79.             }
    80.         }
    81.  
    82.         CheckTowersLevel();
    83.  
    84.     }
    85.  
    86.  
    87.  
    88.     private void Update()
    89.     {
    90.  
    91.  
    92.         ShowStars();
    93.  
    94.         for (int i = 0; i < skills.Length; i++)
    95.         {
    96.             if (skills[i].isUpgraded)
    97.             {
    98.                 //colorful
    99.                 skillButtons[i].transform.GetComponent<Image>().color = new Vector4(1, 1, 1, 1);
    100.             }
    101.             else
    102.             {
    103.                 //Dark
    104.                 skillButtons[i].transform.GetComponent<Image>().color = new Vector4(0.1f, 0.1f, 0.1f, 1);
    105.             }
    106.         }
    107.  
    108.  
    109.     }
    110.  
    111.  
    112.  
    113.  
    114.  
    115.     public void CheckTowersLevel()
    116.     {
    117.         /* CheckTowerStars(arrowSkills, arrowStars);
    118.          CheckTowerStars(soliderSkills, soliderStars);
    119.          CheckTowerStars(boomSkills, boomStars);
    120.          CheckTowerStars(wizardSkills, wizardStars);*/
    121.     }
    122.  
    123.     /* void CheckTowerStars(Skill[] _skills, GameObject[] _stars)//solider
    124.      {
    125.          for(int i = 0; i < _skills.Length; i++)
    126.          {
    127.              if(_skills[i].isUpgraded)
    128.              {
    129.                  _stars[i].gameObject.GetComponent<Image>().sprite = upgradeStarSprite;
    130.              }
    131.              else
    132.              {
    133.                  _stars[i].gameObject.GetComponent<Image>().sprite = normalStarSprite;
    134.              }
    135.          }
    136.      }*/
    137.  
    138.     private void ShowStars()
    139.     {
    140.         totalStarsText.text = "" + remainingStars;
    141.     }
    142.  
    143.     public void ResetAll()
    144.     {
    145.         for (int i = 0; i < connerStars.Length; i++)
    146.         {
    147.  
    148.             remainingStars = totalStars;
    149.  
    150.             skills[i].isUpgraded = false;
    151.         }
    152.  
    153.         skillImage.sprite = questionSprite;
    154.         skillStarText.text = "x";
    155.         skillDesText.text = "Select one skill to check";
    156.  
    157.  
    158.         CheckTowersLevel();
    159.  
    160.         //public Image skillImage;
    161.         //public Text skillStarText;
    162.         //public Text skillDesText, skillDesHisText;
    163.         //public Text totalStarsText;//MARKER 最高处星星个数
    164.         //public Text upgradeText;//MARKER 这一列所有技能统一需要的✨数量
    165.  
    166.     }
    167.  
    168.     public void UpgradeSkill()//MARKER 这只是单一技能的升级,可能出现一个问题,Lv2升级了Lv没有升级//Time 7:13PM
    169.     {
    170.         UpgradePreviousSkill(Skill.SkillType.Arrow, arrowSkills);
    171.         UpgradePreviousSkill(Skill.SkillType.Solider, soliderSkills);
    172.         UpgradePreviousSkill(Skill.SkillType.Boom, boomSkills);
    173.         UpgradePreviousSkill(Skill.SkillType.Wizard, wizardSkills);
    174.         UpgradePreviousSkill(Skill.SkillType.Meteor, meteorSkills);
    175.  
    176.         CheckTowersLevel();
    177.         //int totalNumber = 0;
    178.  
    179.         //if(activeSkill.skillType == Skill.SkillType.Arrow)
    180.         //{
    181.         //    for(int i = 0; i < activeSkill.skillLevel; i++)
    182.         //    {
    183.         //        totalNumber = arrowSkills[i].starsNumber;
    184.         //    }
    185.         //}
    186.  
    187.         //if (activeSkill != null)
    188.         //{
    189.         //    if (remainingStars >= totalNumber)
    190.         //    {
    191.         //        //Debug.Log("We can Upgrade this skill");
    192.         //        remainingStars -= totalNumber;
    193.         //        for(int i = 0; i < activeSkill.skillLevel; i++)
    194.         //        {
    195.         //            arrowSkills[i].isUpgraded = true;
    196.         //            arrowSkills[i].gameObject.transform.parent.gameObject.transform.GetChild(1).GetComponent<Image>().sprite = upgradeStarSprite;
    197.         //        }
    198.         //    }
    199.         //    else
    200.         //    {
    201.         //        Debug.Log("Not Enough Stars");
    202.         //    }
    203.         //}
    204.  
    205.         //-=-=-=-=-=-=-=-=-=-=-
    206.  
    207.         //if (activeSkill != null)//OPTIONAL 这是最简单的单个点击可以升级的版本,不包含升级之前的技能
    208.         //{
    209.         //    if (remainingStars >= activeSkill.starsNumber)
    210.         //    {
    211.         //        //Debug.Log("We can Upgrade this skill");
    212.         //        remainingStars -= activeSkill.starsNumber;
    213.         //        activeSkill.isUpgraded = true;
    214.  
    215.         //        activeSkill.gameObject.transform.parent.gameObject.transform.GetChild(1).GetComponent<Image>().sprite = upgradeStarSprite;
    216.         //    }
    217.         //    else
    218.         //    {
    219.         //        Debug.Log("Not Enough Stars");
    220.         //    }
    221.         //}
    222.     }
    223.  
    224.     private void UpgradePreviousSkill(Skill.SkillType _skillType, Skill[] _skill)
    225.     {
    226.         int totalNumber = 0;
    227.  
    228.         if (activeSkill != null && activeSkill.skillType == _skillType && !activeSkill.isUpgraded)
    229.         {
    230.             for (int i = 0; i < activeSkill.skillLevel; i++)
    231.             {
    232.                 if (_skill[i].isUpgraded == false)
    233.                 {
    234.                     totalNumber += _skill[i].starsNumber;
    235.                 }
    236.             }
    237.  
    238.             if (activeSkill != null)//MARKER 这整个If Statement一开始不是放在这里面,是放出去的,但是如果放出去,按Upgrade会所有的都显示
    239.             {
    240.                 if (remainingStars >= totalNumber)
    241.                 {
    242.                     //Debug.Log("We can Upgrade this skill");
    243.                     remainingStars -= totalNumber;
    244.                     for (int i = 0; i < activeSkill.skillLevel; i++)
    245.                     {
    246.                         if (!_skill[i].isUpgraded)
    247.                         {
    248.                             _skill[i].isUpgraded = true;
    249.  
    250.  
    251.                             _skill[i].transform.GetChild(0).gameObject.SetActive(true);//TIME Jul 10 - 2:59 Upgrade Effect
    252.                             _skill[i].transform.GetChild(0).GetComponent<ParticleSystem>().Play();//Each Click will play once
    253.                         }
    254.                     }
    255.                 }
    256.                 else
    257.                 {
    258.                     Debug.Log("Not Enough Stars");//MARKER Make One tip animation now
    259.                     StartCoroutine(ActiveTipTextCo());
    260.                 }
    261.             }
    262.         }
    263.     }
    264.  
    265.    
    266.  
    267.     IEnumerator ActiveTipTextCo()
    268.     {
    269.         tipText.GetComponent<Text>().color = new Vector4(0.42f, 0.08f, 0.4f, 1);
    270.         tipText.gameObject.SetActive(true);
    271.         yield return new WaitForSeconds(1.5f);
    272.         tipText.gameObject.SetActive(false);
    273.     }
    274.  
    275.  
    276.  
    277. }
    278.  
    This is the Skill script that contains the skill info :
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class Skill : MonoBehaviour
    6. {
    7.     public Sprite skillSprite;
    8.     public int starsNumber;
    9.     [TextArea(1,3)]
    10.     public string skillDes;
    11.     public string skillHistory;
    12.     public int skillLevel;
    13.  
    14.     public bool isUpgraded;
    15.  
    16.     public enum SkillType
    17.     {
    18.         Arrow,
    19.         Solider,
    20.         Boom,
    21.         Wizard,
    22.         Meteor
    23.     }
    24.  
    25.     public SkillType skillType;
    26.  
    27. }
    28.  
    29.  
    30.  
    Lastly the script i want to apply the upgrades on :
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. /// <summary>
    6. /// Attack with ranged weapon
    7. /// </summary>
    8. public class AttackRanged : Attack
    9. {
    10.     // Prefab for arrows
    11.     public GameObject arrowPrefab;
    12.     // From this position arrows will fired
    13.     public Transform firePoint;
    14.     private AttackRanged aRscript;
    15.     // Animation controller for this AI
    16.     private Animator anim;
    17.     // Counter for cooldown calculation
    18.     private float cooldownCounter;
    19.  
    20.     /// <summary>
    21.     /// Awake this instance.
    22.     /// </summary>
    23.     void Awake()
    24.     {
    25.         anim = GetComponentInParent<Animator>();
    26.         cooldownCounter = cooldown;
    27.         Debug.Assert(arrowPrefab && firePoint, "Wrong initial parameters");
    28.     }
    29.  
    30.     /// <summary>
    31.     /// Update this instance.
    32.     /// </summary>
    33.     void FixedUpdate()
    34.     {
    35.         if (cooldownCounter < cooldown)
    36.         {
    37.             cooldownCounter += Time.fixedDeltaTime;
    38.         }
    39.     }
    40.  
    41.     /// <summary>
    42.     /// Attack the specified target if cooldown expired
    43.     /// </summary>
    44.     /// <param name="target">Target.</param>
    45.     public override void TryAttack(Transform target)
    46.     {
    47.         if (cooldownCounter >= cooldown)
    48.         {
    49.             cooldownCounter = 0f;
    50.             Fire(target);
    51.         }
    52.     }
    53.  
    54.     /*public void archerSkillOne()
    55.     {
    56.         GameObject archerTowerOne = GameObject.Find("L1Bowman");
    57.          int dmg = archerTowerOne.GetComponent<AttackRanged>().damage;
    58.         dmg = damage + 20;
    59.        
    60.     }*/
    61.  
    62.     private IEnumerator FireCoroutine(Transform target, GameObject bulletPrefab)
    63.     {
    64.         if (target != null && bulletPrefab != null)
    65.         {
    66.             // If unit has animator
    67.             if (anim != null && anim.runtimeAnimatorController != null)
    68.             {
    69.                 // Search for clip
    70.                 foreach (AnimationClip clip in anim.runtimeAnimatorController.animationClips)
    71.                 {
    72.                     if (clip.name == "Attack")
    73.                     {
    74.                         // Play animation
    75.                         anim.SetTrigger("attack");
    76.                         break;
    77.                     }
    78.                 }
    79.             }
    80.             // Delay to synchronize with animation
    81.             yield return new WaitForSeconds(fireDelay);
    82.             if (target != null)
    83.             {
    84.                 // Create arrow
    85.                 GameObject arrow = Instantiate(bulletPrefab, firePoint.position, firePoint.rotation);
    86.                 IBullet bullet = arrow.GetComponent<IBullet>();
    87.                 bullet.SetDamage(damage);
    88.                 bullet.Fire(target);
    89.                 // Play sound effect
    90.                 if (sfx != null && AudioManager.instance != null)
    91.                 {
    92.                     AudioManager.instance.PlayAttack(sfx);
    93.                 }
    94.             }
    95.         }
    96.     }
    97.  
    98.     /// <summary>
    99.     /// Make ranged attack
    100.     /// </summary>
    101.     /// <param name="target">Target.</param>
    102.     public override void Fire(Transform target)
    103.     {
    104.         StartCoroutine(FireCoroutine(target, arrowPrefab));
    105.     }
    106.  
    107.     /// <summary>
    108.     /// Make ranged attack with special bullet
    109.     /// </summary>
    110.     /// <param name="target">Target.</param>
    111.     /// <param name="bulletPrefab">Bullet prefab.</param>
    112.     public void Fire(Transform target, GameObject bulletPrefab)
    113.     {
    114.         StartCoroutine(FireCoroutine(target, bulletPrefab));
    115.     }
    116.  
    117.     void OnDestroy()
    118.     {
    119.         StopAllCoroutines();
    120.     }
    121. }
    122.  
    Any suggestions or ideas would be helpful.

    Thank you.
     
  2. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,545
    I'll assume this:
    • SkillMenu is on a GameObject in your skill tree scene.
    • AttackRanged is on GameObjects in your gameplay scene.
    You need your Skill info to be available to both scenes. One way to do this is to create a GameObject that survives scene changes by using DontDestroyOnLoad(). It's not the only way, but it's probably the simplest for right now. Put all of your Skill scripts on this GameObject. You can add a SkillManager script, something like:
    Code (csharp):
    1. public class SkillManager : MonoBehaviour
    2. {
    3.     public Skill[] skills;
    4.     public static SkillManager instance;
    5.     void Awake()
    6.     {
    7.         instance = this;
    8.         DontDestroyOnLoad(this.gameObject);
    9.     }
    10. }
    Your SkillMenu script will need to connect to the Skill scripts at runtime instead of in the inspector. Something like:
    Code (csharp):
    1. private void Awake()
    2. {
    3.     skills = SkillManager.instance.skills;
    4.     ...
    Similarly, your gameplay scripts (AttackRanged, etc.) will also need to connect to the Skill scripts at runtime. Something like:
    Code (csharp):
    1. void Start()
    2. {
    3.     foreach (Skill skill in SkillManager.instance.skills)
    4.     {
    5.         // Apply the skill to this gameplay script.
    6.     }
    7. }
    (This is off topic and there's no point getting sidetracked with it right now, but your SkillMenu.Update() method is probably doing too much work. You don't need to do all of that work every single frame.)
     
  3. Laamouri-Amine

    Laamouri-Amine

    Joined:
    Oct 6, 2018
    Posts:
    3
    Thank you Tony. I understood the instance part. SO, what you want me to do is creating a method in the SkillManager script that contains the changes i need to do, then call the method in the AttackRanged script ?

    I wanted to write sth like

    if (skill[0].isUpgraded) {
    Find the name of the tower and apply the effects on the AttackRanged script
    }

    But i can't use the script of the array.
     
  4. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,545
    Your Skill scripts will be on the same GameObject as the SkillManager script. Your AttackRanged and SkillMenu scripts will not have design-time references to the Skill scripts. Instead, they must use SkillManager.instance at runtime to get access to the Skill scripts.