1.實現創建水晶并且能與水晶進行交換位置的能力
創建好水晶的預制體,添加動畫控制器,傳入待機和爆炸的動畫
創建Crystal_Skill_Control腳本:
掛載在水晶預制體上
? ? private float crystalExstTime;//水晶存在時間
? ? public void SetupCrystal(float _crystalDuration)
{
crystalExstTime = _crystalDuration;
}
? ? private void Update()
{
crystalExstTime -= Time.deltaTime;
? ? ? ? if(crystalExstTime < 0 )
{
SelfDestroy();//超時自毀
}
}
? ? public void SelfDestroy() => Destroy(gameObject);
創建Crystal_Skill腳本:
掛載在技能管理器上,并在SkillManage腳本上初始化
? ? [SerializeField] private float crystalDuration;//傳入上面的控制腳本
[SerializeField] private GameObject crystalPrefab;//預制體
private GameObject currentCrystal;
? ? public override void UseSkill()
{
base.UseSkill();
? ? ? ? if(currentCrystal==null)//不存在則創建
{
currentCrystal =Instantiate(crystalPrefab,player.transform.position,Quaternion.identity);
? ? ? ? ? ? Crystal_Skill_Control newcrystalScript= currentCrystal.GetComponent<Crystal_Skill_Control>();
? ? ? ? ? ? newcrystalScript.SetupCrystal(crystalDuration);
? ? ? ? }
else//如果場上有水晶則傳送玩家并銷毀水晶
{
player.transform.position = currentCrystal.transform.position;
? ? ? ? ? ? Destroy(currentCrystal);
}
}
Player腳本:
? ? ? ? if(Input.GetKeyDown(KeyCode.F)) //update中
{
skill.crystal.CanbeUsed();
}
2.實現水晶爆炸和變大的功能
給水晶爆炸動畫選取中間一幀調用傷害函數,在結束添加幀事件調用自毀函數
動畫控制器添加觸發Explode
Crystal_Skill_Control腳本:
?private Animator anim ?=>GetComponent<Animator>();//獲取碰撞器和動畫器
private CircleCollider2D cd =>GetComponent<CircleCollider2D>();
private bool canExplode;//是否可以爆炸
private bool canMove;//是否可以移動
private bool canGrow=false;//是否可以變大
?private float moveSpeed;
private float growSpeed=5f;//增大速度
? ? public void SetupCrystal(float _crystalDuration,bool _canExplode,bool _canMove,float _moveSpeed)
{
crystalExstTime = _crystalDuration;
canExplode = _canExplode;
canMove = _canMove;
moveSpeed = _moveSpeed;
? ? }
? ? ? ? if(canGrow)//update()中
{
transform.localScale = Vector2.Lerp(transform.localScale,new Vector3(3,3),growSpeed*Time.deltaTime);//變大
? ? ? ? }
? ? public void AnimationExplodeEvent()//傷害事件
{
Collider2D[] collider2Ds = Physics2D.OverlapCircleAll(transform.position,cd.radius);//用碰撞器的半徑
? ? ? ? foreach (var hit in collider2Ds)
{
if (hit.GetComponent<Enemy>() != null)
{
hit.GetComponent<Enemy>().Damage();
}
}
}
? ? public ?void FinishCrystal()
{
if (canExplode)//如果可以爆炸
{
canGrow = true;//設置變大
anim.SetTrigger("Explode");//播放爆炸動畫
}
else
{
SelfDestroy();
}
}
Crystal_Skill腳本:
? ? [Header("Explode crystal")]
[SerializeField] private bool canExplode;
? ? [Header("Moving crystal")]
[SerializeField] private bool canMove;
[SerializeField] private float moveSpeed;
? ? ? ? else//修改
{
Vector2 playerPos= player.transform.position;
? ? ? ? ? ? player.transform.position = currentCrystal.transform.position;
? ? ? ? ? ? currentCrystal.transform.position = playerPos;//交換水晶與玩家
? ? ? ? ? ? currentCrystal.GetComponent<Crystal_Skill_Control>().FinishCrystal();
}
3.實現水晶的移動
Skill腳本:
? ? protected virtual Transform FindClosetEnemy(Transform _checkTransform)//這是之前寫克隆攻擊時用到的代碼,現將它放在skill上,用于找到指定組件附近最近的敵人
{
Collider2D[] collider2Ds = Physics2D.OverlapCircleAll(_checkTransform.position, 25);
float closestEnemyDistance = Mathf.Infinity;
Transform closestEnemy = null;
foreach (var hit in collider2Ds)
{
? ? ? ? ? ? if (hit.GetComponent<Enemy>() != null)
{
float distanceToEnemy = Vector2.Distance(_checkTransform.position, hit.transform.position);
? ? ? ? ? ? ? ? if (distanceToEnemy < closestEnemyDistance)
{
closestEnemyDistance = distanceToEnemy;
closestEnemy = hit.transform;
}
}
}
? ? ? ? return closestEnemy;
}
自行修改克隆攻擊腳本中的一些代碼
Crystal_Skill_Control腳本:
private Transform closestTarget;//最近的敵人目標
? ? ? ? if (canMoveToEnemy)
{
transform.position =Vector2.MoveTowards(transform.position,closestTarget.position,moveSpeed*Time.deltaTime);//移動
? ? ? ? ? ? if(Vector2.Distance(transform.position,closestTarget.position)<1f )//距離小于1時自毀
{
FinishCrystal();
canMoveToEnemy = false;
}
}
Crystal_Skill腳本:
? ? ? ? if(currentCrystal==null)
{
currentCrystal =Instantiate(crystalPrefab,player.transform.position,Quaternion.identity);
? ? ? ? ? ? Crystal_Skill_Control newcrystalScript= currentCrystal.GetComponent<Crystal_Skill_Control>();
? ? ? ? ? ? newcrystalScript.SetupCrystal(crystalDuration,canExplode,canMoveToEnemy,moveSpeed,FindClosetEnemy(currentCrystal.transform));//傳入上面函數返回的最近敵人
? ? ? ? }
else
{
if (canMoveToEnemy)//水晶移動時不可以換位
{
return;
}
? ? ? ? ? ? Vector2 playerPos= player.transform.position;
? ? ? ? ? ? player.transform.position = currentCrystal.transform.position;
? ? ? ? ? ? currentCrystal.transform.position = playerPos;
? ? ? ? ? ? currentCrystal.GetComponent<Crystal_Skill_Control>().FinishCrystal();
}
4.實現多晶體
玩家可以擁有多個晶體并且可以一次性使用它們
Crystal_Skill腳本:
? ? [Header("Multi Stacking crystal")]
[SerializeField] private bool canMultiStack;//是否可以使用多晶體
[SerializeField] private int?amountofCrystal;//晶體數量
[SerializeField] private float MultiCrystalCooldown;//冷卻時間
[SerializeField] private List<GameObject> crystalLeft=new List<GameObject>();//預備儲存晶體的列表
? ? ? ? if(canMultiCrystal())//update中
{
return;
}
?private bool canMultiCrystal()//是否能夠發射多晶體
{
if(canMultiStack)
{
if(crystalLeft.Count>0)//如果列表里有晶體
{
cooldown = 0;//可以連發
GameObject crystaclToSpawn = crystalLeft[crystalLeft.Count-1];//取最后一個晶體
GameObject newCrystal =Instantiate(crystaclToSpawn,player.transform.position,Quaternion.identity);//生成
? ? ? ? ? ? ?crystalLeft.Remove(crystaclToSpawn);//從列表中移除
Crystal_Skill_Control newcrystalScript = newCrystal.GetComponent<Crystal_Skill_Control>();
? ? ? ? ? ? ?newcrystalScript.SetupCrystal(crystalDuration, canExplode, canMoveToEnemy, moveSpeed, FindClosetEnemy(newCrystal.transform));
? ? ? ? ? ? ?if(crystalLeft.Count<=0)//無晶體重新填裝
{
cooldown = MultiCrystalCooldown;//設置冷卻
RefillCrystal();
}
? ? ? ? ? ? ?return true;
}
}
return false;
}
? ? public void RefillCrystal()//填裝晶體
{
for(int i = 0;i<amountofCrystal;i++)
{
crystalLeft.Add(crystalPrefab);
}
}
5.實現發射晶體的時間窗口
即當玩家只發射一顆晶體時,經過一定時間后也會進入冷卻并重新填裝晶體
Crystal_Skill腳本:
[SerializeField] private float useTimeWindow;//延遲窗口時間
? ? public void RefillCrystal()
{
int ?amountToAdd=amountofCrystal-crystalLeft.Count;//確保填裝數等于設定的數
for(int i = 0;i<amountToAdd;i++)
{
crystalLeft.Add(crystalPrefab);
}
}
? ? private void ResetAbility()//重置
{
if (coolDownTime > 0)//如果已經在冷卻則返回
return;
? ? ? ? cooldown = MultiCrystalCooldown;
RefillCrystal();
}
?if(crystalLeft.Count>0)
{
if(amountofCrystal==crystalLeft.Count)//當發射出第一顆時延遲調用
{
Invoke("ResetAbility", useTimeWindow);
}