Related Posts Plugin for WordPress, Blogger...

4-1 From Interface To Inheritance

今天要來重構技能系統,目前的設計有太多重複的程式碼,每當要新增不同的技能時,都要複製『播放粒子系統』、『播放音效』等同樣的程式碼,今天要將重複的部分都移動父類別。如對本章重構內容有不懂之處,請先看看以前的文章唷:

3-9 Special Abilities System Overview

3-10 Create An Area Of Effect Ability 

3-18 Spawning Particles At Runtime 

3-19 Self Heal Special Ability Challenge

3-20 Triggering Special Abilities From Keys

3-21 Add An Effect Audio On Special Abilities 

3-24 Finishing The AOE And Self Heal Particle Effect 

首先,將ISpecialAbility.cs刪除。

將SpecialAbilityConfig.cs修改成AbilityConfig.cs。

原本寫在 SpecialAbilityConfig裡面的Class名稱,也記得要一併修改成AbilityConfig唷!用Refactor工具一併修改名稱吧。

接著,新增Script,名稱為AbilityBehaviour。我們會將播放粒子系統跟播放技能音效的方法都統一寫在AbilityBehaviour,讓繼承AbilityBehaviour的Class直接使用。

改完以後,大家應該會發現原本繼承ISpecialAbility的地方提示紅字。


將MonoBehaviour跟ISpecialAbility都刪掉,統一繼承AbilityBehaviour。

在AbilityConfig中的AddComponent方法回傳ISpecialAbility,請改成AbilityBehaviour。其餘有繼承AbilityConfig的類別中,同樣實作AddComponent方法的回傳值也必須改成AbilityBehaviour。


接著,提供AbilityBehaviour的程式碼:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace RPG.Character{
 public abstract class AbilityBehaviour : MonoBehaviour {

  protected AbilityConfig config;

  const float PARTICLE_CLEAN_UP_DELAY = 20f;

  public abstract void Use(AbilityParams useParams);

  public void SetConfig(AbilityConfig config){
   this.config = config;
  }

  protected void PlayEffectAudio(){
   // 取得音效Component
   AudioSource myAudioSource = GetComponent();
   // 從config取得技能音效
   AudioClip audioClip = config.GetAudioClip ();
   // 使用PlayOnShot播放技能音效,可避免和其他技能音效重疊在一起
   myAudioSource.PlayOneShot (audioClip);
  }

  protected void PlayParticleEffect(){
   // 初始化粒子系統,綁定到Player身上
   GameObject particlePrefab = Instantiate(config.GetParticlePrefab(),
    transform.position,
    Quaternion.identity,
    transform);
   // 啟動粒子系統
   particlePrefab.GetComponent().Play();
   // 播放完後自我銷毀
   StartCoroutine(DestroyParticleWhenFinished(particlePrefab));
  }

  protected void PlayParticleEffectOnTarget(AbilityParams useParams){
   // 初始化粒子系統,綁定到Enemy身上
   GameObject particlePrefab = Instantiate(config.GetParticlePrefab(),
    useParams.target.transform.position,
    Quaternion.identity,
    useParams.target.transform);
   // 啟動粒子系統
   particlePrefab.GetComponent().Play();
   // 播放完後自我銷毀
   StartCoroutine(DestroyParticleWhenFinished(particlePrefab));
  }

  IEnumerator DestroyParticleWhenFinished(GameObject particlePrefab){
   while (particlePrefab.GetComponent ().isPlaying) {
    yield return new WaitForSeconds (PARTICLE_CLEAN_UP_DELAY);
   }
   Destroy (particlePrefab);
   yield return new WaitForEndOfFrame ();
  }
 }
}


記得在PowerAttackBehaviour、AreaEffectBehaviour、SelfHealBehaviour中有使用Use的地方,都要用Override。

以下提供有修改到的程式碼,大家應該會發現比之前的還要簡潔很多。改完以後,請大家自行測試看看是否還能正常發動技能唷!
PowerAttackBehaviour.cs:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace RPG.Character{
 public class PowerAttackBehaviour : AbilityBehaviour{

  public override void Use(AbilityParams useParams){
   DealDamage (useParams);
   PlayParticleEffectOnTarget (useParams);
   PlayEffectAudio ();
  }

  private void DealDamage(AbilityParams useParams){
   float damageToDeal = (config as PowerAttackConfig).GetExtraDamage () + useParams.baseDamage;
   useParams.target.TakeDamage (damageToDeal);
  }

 }
}

AreaEffectBehaviour.cs:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using RPG.Core;

namespace RPG.Character{
 public class AreaEffectBehaviour : AbilityBehaviour{

  public override void Use(AbilityParams useParams){
   DealRadiaDamage (useParams);
   PlayParticleEffect ();
   PlayEffectAudio ();
  }

  private void DealRadiaDamage(AbilityParams useParams){
   RaycastHit[] hits = Physics.SphereCastAll (
    transform.position,       // 以Player為中心點
    (config as AreaEffectConfig).GetRadius (), // 掃描的球體半徑範圍
    Vector3.up,         // 掃描的方向不重要,因為我們發動的是Player為原點的範圍技
    (config as AreaEffectConfig).GetRadius () // 掃描距離設定成跟掃描半徑一樣即可
   );

   foreach (RaycastHit hit in hits) {
    Enemy enemy = hit.collider.gameObject.GetComponent ();
    if (enemy != null) {
     float damageToDeal = useParams.baseDamage + (config as AreaEffectConfig).GetDamageToEachTarget ();
     enemy.TakeDamage (damageToDeal);
    }
   }
  }

 }
}

SelfHealBehaviour.cs:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using RPG.Core;

namespace RPG.Character{
 public class SelfHealBehaviour : AbilityBehaviour {

  Player player;

  void Start(){
   player = GetComponent ();
  }

  public override void Use(AbilityParams useParams){
   player.Heal ((config as SelfHealConfig).GetExtraHealth ());
   PlayParticleEffect ();
   PlayEffectAudio ();
  }
 }
}




留言