unity手遊(2)人物攻擊動畫判定和怪物AI

前言


本文由作者@zx一路飛奔出品,轉載請註明出處

文章地址:http://blog.csdn.net/u014735301/article/details/43055923

作者微博:http://weibo.com/u/1847349851

 

這幾天編碼下來,是時候寫篇博客總結和分享下了,做出來的效果大致爲:

方向鍵控制人物移動,靠近怪物時,按下攻擊鍵,可以鎖定怪物,進行攻擊。也可以釋放技能,

技能分爲:單體鎖定,無鎖定,造成的傷害效果分爲:單體傷害,羣體傷害。

怪物受到人物的攻擊,開始對人物進行攻擊,人物遠離時,進行追擊,當之間的距離達到一定長度時,怪物的攻擊目標消失

放上幾張圖看看效果吧




動畫控制


這裏的動畫控制,並沒有使用unity自身的動畫狀態機,而是通過腳本寫一個狀態類,通過對狀態的判斷,播放對應的動畫片段

在人物控制腳本中,使用了委託的方式,根據狀態的不同來調用動畫控制腳本中對應的方法

下面給出代碼片段供參考
	//人物狀態 枚舉
	public enum ControlAnimationState {Idle,Move,WaitAttack,Attack,Cast,ActiveSkill,TakeAtk,Death};
	//動畫狀態
	public ControlAnimationState ctrlAnimState;
	//人物狀態
	void HeroAnimationState() {
		//靜止
		if(ctrlAnimState == ControlAnimationState.Idle)
		{
			animation.animationState = animation.Idle;
		}
		//行走
		if(ctrlAnimState == ControlAnimationState.Move)
		{
			animation.animationState = animation.Move;
		}
		//攻擊
		if (ctrlAnimState == ControlAnimationState.Attack) 
		{
            animation.animationState = animation.Attack;                      
		}
        //等待攻擊
        if (ctrlAnimState == ControlAnimationState.WaitAttack)
        {
            animation.animationState = animation.Idle;
            WaitAttack();       
        }

	}

下面把動畫控制腳本代碼全部給出

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

public class AnimationController : MonoBehaviour {

	public delegate void AnimationHandle();
	public AnimationHandle animationState;

	//動畫類型1
	[System.Serializable]
	public class AnimationType01
	{
		public AnimationClip animation;
		public float speedAnimation = 1.0f;
	}
	//動畫類型2 可以控制動畫播放速度
	[System.Serializable]
	public class AnimationType02
	{
		public AnimationClip animation;
		public float speedAnimation = 1.0f;
		public bool speedTuning;
	}

	//釋放技能動畫
	[System.Serializable]
	public class AnimationSkill
	{
        public PlayerSkillController.SkillType skillType;
		public int skillIndex;
		public AnimationClip animationSkill;
		public float speedAnimation;
        public float attackTimer;
		public bool speedTuning;
		
	}
	public AnimationType01 idle,death; //靜止,施法,死亡
	public AnimationType02 move; //行走
	public AnimationSkill skillSetup;//技能

	private PlayerController playerController;
	private PlayerSkillController playerSkill;
    private PlayerStatus playerStatus;
    public bool checkAttack;    //檢測攻擊動畫是否播放結束
    public bool checkSkill;     

	// Use this for initialization
	void Start () {
        checkAttack = true;
        checkSkill = true; 
		playerController = this.GetComponent<PlayerController>();
		playerStatus = this.GetComponent<PlayerStatus>();
		playerSkill = this.GetComponent<PlayerSkillController>();
		
	}
	
	// Update is called once per frame
	void Update () {
		
		if(animationState != null){
			animationState();	
		}
		
	}
	
	//Idle Method
	public void Idle(){
		animation.CrossFade(idle.animation.name);
		animation[idle.animation.name].speed = idle.speedAnimation;
	}
	
	//Move Method
	public void Move(){
		animation.Play(move.animation.name);
		
		if(move.speedTuning)  //可以變換速度
		{
			//與人物移動速度相關
			animation[move.animation.name].speed = (playerStatus.status.movespd * 2) * move.speedAnimation;	
		}else
		{
			animation[move.animation.name].speed = move.speedAnimation;
		}
	}
	//攻擊(釋放技能)
	public void Attack()
	{   
        //接受參數
        ReceiveParam();
        if (skillSetup.skillType==PlayerSkillController.SkillType.FreeTarget)
        {
            //playerSkill.UseSkill("Attack", skillSetup.skillIndex);
           // checkSkill = false;       
        }
        animation.Play(skillSetup.animationSkill.name);

        if (skillSetup.speedTuning)  //能進行速度控制
		{
            animation[skillSetup.animationSkill.name].speed = skillSetup.speedAnimation * (playerStatus.status.atkSpd * 2);
            
		}else
		{
            animation[skillSetup.animationSkill.name].speed = skillSetup.speedAnimation;
		}

		//計算傷害,當動畫播放時間超出攻擊時間
        if (animation[skillSetup.animationSkill.name].normalizedTime > skillSetup.attackTimer && animation[skillSetup.animationSkill.name].normalizedTime < 0.9f)
        {

            if (checkSkill)
            {
                playerSkill.UseSkill("Attack", skillSetup.skillIndex);
                checkSkill = false;
            }

        }
		//攻擊動畫播放結束
		if(animation[skillSetup.animationSkill.name].normalizedTime > 0.9f)
		{
            playerController.ctrlAnimState = PlayerController.ControlAnimationState.WaitAttack;
            playerController.onceAttack = false;
            checkAttack = true;
            checkSkill = true;
		}
	}
	//死亡
	public void Death()
	{
		animation.CrossFade(death.animation.name);
		animation[death.animation.name].speed = death.speedAnimation;
	}

    //從playskill腳本接受參數
    public void ReceiveParam() {
        if (checkAttack)
        {
        //技能ID
        skillSetup.skillIndex = playerSkill.FindSkillIndex(playerController.skillID);
        //技能類型
        skillSetup.skillType = playerSkill.activeSkillAttack[skillSetup.skillIndex].skillType;
        //技能動畫
        skillSetup.animationSkill = playerSkill.activeSkillAttack[skillSetup.skillIndex].animationAttack;
        //動畫播放時間
        skillSetup.speedAnimation = playerSkill.activeSkillAttack[skillSetup.skillIndex].speedAnimation;
        //播放時間是否可控
        skillSetup.speedTuning = playerSkill.activeSkillAttack[skillSetup.skillIndex].speedTuning;
        //技能攻擊時間
        skillSetup.attackTimer = playerSkill.activeSkillAttack[skillSetup.skillIndex].attackTimer;
        checkAttack = false;
        }
    }
        
}

技能控制


using UnityEngine;
using System.Collections;
using System.Collections.Generic;
/*
	人物技能控制腳本
 */
public class PlayerSkillController : MonoBehaviour {
	//技能類型:單體鎖定,無鎖定,
	public enum SkillType {LockTarget,FreeTarget,Instance};
	//傷害類型:單體傷害,羣體傷害
	public enum TargetSkill {SingleTarget,MultipleTarget};


    //主動釋放技能
	[System.Serializable]
	public class ActiveSkillAttack
	{
		public string skillName = "Skill Attack";//技能名稱
		public int skillID; //技能id
		public int unlockLevel = 1;//解鎖等級
		public Texture2D skillIcon;//技能圖標
		public int mpUse;//mp消耗
        public TargetSkill targetSkill = PlayerSkillController.TargetSkill.SingleTarget;//傷害類型
		public SkillType skillType = PlayerSkillController.SkillType.LockTarget;//技能類型
		public float skillRange;//技能範圍
		public AnimationClip animationAttack;//技能動畫
		public float speedAnimation = 1;//動畫播放速度
		public float CD = 0;//冷卻時間
		public float attackTimer = 0.5f;
        public float percentDamage = 1;//傷害百分比
		public float multipleDamage = 1;//傷害次數
		[Multiline]
		public string description = "";	//技能描述
		
		public bool speedTuning;//動畫播放速度是否可控
		
		public GameObject skillFX;
		public AudioClip soundFX;

	}

	public List<ActiveSkillAttack> activeSkillAttack = new List<ActiveSkillAttack>();

    //在技能範圍中的enemy
    public List<GameObject> monsterInArea = new List<GameObject>();
    //碰撞組
    public Collider[] groupNearbyObject;


	private int currentUseSkill;
	private PlayerController controller;
    private AnimationController animation;
	// Use this for initialization
	void Start () {
		controller = this.GetComponent<PlayerController> ();
        animation = this.GetComponent<AnimationController>();
	}
	
	// Update is called once per frame
	void Update () {
	
	}
	public void UseSkill(string skillType,int indexSkill)
	{
		if (skillType=="Attack") {
			//爲鎖定攻擊
			if (activeSkillAttack[indexSkill].skillType == SkillType.LockTarget)
			{  
				//技能特效
                if (activeSkillAttack[indexSkill].skillFX != null)
                {   
                    //在目標處生成技能特效
                    Instantiate(activeSkillAttack[indexSkill].skillFX, controller.target.gameObject.transform.position, Quaternion.identity);
                }
				//技能音效
                if (activeSkillAttack[indexSkill].soundFX != null)
                {
                    //在目標處生成技能音效
                    AudioSource.PlayClipAtPoint(activeSkillAttack[indexSkill].soundFX, controller.target.gameObject.transform.position);
                }
                //如果爲鎖定且單體攻擊
                if (activeSkillAttack[indexSkill].targetSkill == TargetSkill.SingleTarget)
                {
                    EnemyController enemy;
                    enemy = controller.target.GetComponent<EnemyController>();
                    enemy.GetDamage(activeSkillAttack[indexSkill].percentDamage*100, activeSkillAttack[indexSkill].multipleDamage);
                }
                //如果爲鎖定且羣體攻擊
                if (activeSkillAttack[indexSkill].targetSkill == TargetSkill.MultipleTarget)
                {
                    MakeDamage(activeSkillAttack[indexSkill].percentDamage * 100, activeSkillAttack[indexSkill].multipleDamage, activeSkillAttack[indexSkill].skillRange, controller.target.transform);
                }
			}
            //爲無鎖定攻擊
            if (activeSkillAttack[indexSkill].skillType == SkillType.FreeTarget)
            {
                //技能特效
                if (activeSkillAttack[indexSkill].skillFX != null)
                {
                    //在目標處生成技能特效
                    Instantiate(activeSkillAttack[indexSkill].skillFX, transform.position, Quaternion.identity);
                }
                //技能音效
                if (activeSkillAttack[indexSkill].soundFX != null)
                {
                    //在目標處生成技能音效
                    AudioSource.PlayClipAtPoint(activeSkillAttack[indexSkill].soundFX, transform.position);
                }
                //爲無鎖定攻擊(默認是羣體傷害)
                if (activeSkillAttack[indexSkill].skillType == SkillType.FreeTarget)
                {
                    MakeDamage(activeSkillAttack[indexSkill].percentDamage * 100, activeSkillAttack[indexSkill].multipleDamage, activeSkillAttack[indexSkill].skillRange, this.transform);
                }
            }

		}
	}

	//返回技能在列表中的下標
	public int FindSkillIndex(int skillID)
	{
		for(int i=0;i<activeSkillAttack.Count;i++)
		{
			if(skillID == activeSkillAttack[i].skillID)
			{
				currentUseSkill = i;
				return currentUseSkill;
			}
			
		}
		return 0;
	}

    //對技能範圍內的enemy造成傷害
    public void MakeDamage(float hit, float multipleDamage, float radius,Transform transform)
    {
        //清空列表
        monsterInArea.Clear();
        //返回球型半徑之內(包括半徑)的所有碰撞體 collider[]。
        groupNearbyObject = Physics.OverlapSphere(transform.position, radius);
        foreach (Collider groupObj in groupNearbyObject)
        {   
            //其中是enemy的話 加入列表
            if (groupObj.gameObject.tag == Tags.enemy)
                monsterInArea.Add(groupObj.transform.gameObject);
        }
        if (monsterInArea.Count > 0)
        {
            //對列表中的enemy造成傷害
            foreach (GameObject enemy in monsterInArea)
            {
                EnemyController enemyController;
                enemyController = enemy.GetComponent<EnemyController>();
                enemyController.GetDamage(hit, multipleDamage);

            }
        }
    }
}

這裏我把普通攻擊也算作一種技能。大概的攻擊流程就是這樣



技能範圍確定

在釋放技能時,如何選定技能攻擊範圍,讓在這個範圍內的敵人都受到傷害,要用到Physics.OverlapSphere(transform.position, radius);

代碼可以參考 上面 MakeDamage()方法,通過在目標位置畫出半徑爲radius的相交球,返回在這個球內的怪物,對其操作就行了


怪物AI


談AI,其實真算不上,就是通過對怪物和player之間距離的控制來對怪物target操作

	void Update () {

        EnemyAnimationState();
        Move();
	}
    public void Move() {
        if (target != null)
        {
            this.transform.LookAt(target.transform.position);
            destinationDistance = Vector3.Distance(target.transform.position, this.transform.position);
            if (destinationDistance < 1.4f)
            {
                moveSpeed = 0;
                WaitAttack();
            }
            if (destinationDistance > 1.4f && destinationDistance < 5f)
            {
                 moveSpeed = 2f;
                 ctrlAnimState = ControlAnimationState.Move;	
            }
            if (destinationDistance > 5f)
            {
                target = null;
                moveSpeed = 0;
                ctrlAnimState = ControlAnimationState.Idle;	
            } 
        }

總結


目前大致做了這麼多東西,後續的計劃是加上一個屬性腳本,控制人物和 怪物的基本屬性,由於是學習用,不太複雜,通過屬性腳來計算人物造成的傷害等

    //角色自身基礎屬性
    [System.Serializable]
    public class AttributeBase
    {
        public int lv, hp, mp, atk, def;//等級,血,藍,攻擊,防禦
        public float criticalRate, atkSpd,movespd, exp;//暴擊率,攻速,移速,經驗
    }

其次是研究NGUI HUD插件,讓 player和怪物之間攻擊造成的傷害不僅僅侷限在控制檯內,讓血條,和傷害數字顯示在屏幕上,更直觀的呈現!



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章