unity手游(2)人物攻击动画判定和怪物AI

前言


本文由作者@zx一路飞奔出品,转载请注明出处

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

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

 

这几天编码下来,是时候写篇博客总结和分享下了,做出来的效果大致为:

方向键控制人物移动,靠近怪物时,按下攻击键,可以锁定怪物,进行攻击。也可以释放技能,

技能分为:单体锁定,无锁定,造成的伤害效果分为:单体伤害,群体伤害。

怪物受到人物的攻击,开始对人物进行攻击,人物远离时,进行追击,当之间的距离达到一定长度时,怪物的攻击目标消失

放上几张图看看效果吧

unity手游(2)人物攻击动画判定和怪物AI_第1张图片unity手游(2)人物攻击动画判定和怪物AI_第2张图片

unity手游(2)人物攻击动画判定和怪物AI_第3张图片unity手游(2)人物攻击动画判定和怪物AI_第4张图片


动画控制


这里的动画控制,并没有使用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();
		playerStatus = this.GetComponent();
		playerSkill = this.GetComponent();
		
	}
	
	// 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 = new List();

    //在技能范围中的enemy
    public List monsterInArea = new List();
    //碰撞组
    public Collider[] groupNearbyObject;


	private int currentUseSkill;
	private PlayerController controller;
    private AnimationController animation;
	// Use this for initialization
	void Start () {
		controller = this.GetComponent ();
        animation = this.GetComponent();
	}
	
	// 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();
                    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 0)
        {
            //对列表中的enemy造成伤害
            foreach (GameObject enemy in monsterInArea)
            {
                EnemyController enemyController;
                enemyController = enemy.GetComponent();
                enemyController.GetDamage(hit, multipleDamage);

            }
        }
    }
}

这里我把普通攻击也算作一种技能。大概的攻击流程就是这样

unity手游(2)人物攻击动画判定和怪物AI_第5张图片

技能范围确定

在释放技能时,如何选定技能攻击范围,让在这个范围内的敌人都受到伤害,要用到 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和怪物之间攻击造成的伤害不仅仅局限在控制台内,让血条,和伤害数字显示在屏幕上,更直观的呈现!



你可能感兴趣的:(unity手游(2)人物攻击动画判定和怪物AI)