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的程式碼:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
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<audiosource>();
   // 從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<particlesystem>().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<particlesystem>().Play();
   // 播放完後自我銷毀
   StartCoroutine(DestroyParticleWhenFinished(particlePrefab));
  }
 
  IEnumerator DestroyParticleWhenFinished(GameObject particlePrefab){
   while (particlePrefab.GetComponent<particlesystem> ().isPlaying) {
    yield return new WaitForSeconds (PARTICLE_CLEAN_UP_DELAY);
   }
   Destroy (particlePrefab);
   yield return new WaitForEndOfFrame ();
  }
 }
}
</particlesystem></particlesystem></particlesystem></audiosource>


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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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<enemy> ();
    if (enemy != null) {
     float damageToDeal = useParams.baseDamage + (config as AreaEffectConfig).GetDamageToEachTarget ();
     enemy.TakeDamage (damageToDeal);
    }
   }
  }
 
 }
}
</enemy>

SelfHealBehaviour.cs:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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<player> ();
  }
 
  public override void Use(AbilityParams useParams){
   player.Heal ((config as SelfHealConfig).GetExtraHealth ());
   PlayParticleEffect ();
   PlayEffectAudio ();
  }
 }
}
 
</player>



留言