您的位置:首页 > 移动开发 > Unity3D

unity手游<少侠历险记>(2)人物攻击动画判定和怪物AI

2016-06-24 10:56 555 查看
本文由作者@zx一路飞奔出品,转载请注明出处
文章地址:http://blog.csdn.net/u014735301/article/details/43055923



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

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

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

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

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

放上几张图看看效果吧









动画控制

这里的动画控制,并没有使用unity自身的动画状态机,而是通过脚本写一个状态类,通过对状态的判断,播放对应的动画片段

在人物控制脚本中,使用了委托的方式,根据状态的不同来调用动画控制脚本中对应的方法

下面给出代码片段供参考

[csharp] view
plain copy

//人物状态 枚举

public enum ControlAnimationState {Idle,Move,WaitAttack,Attack,Cast,ActiveSkill,TakeAtk,Death};

//动画状态

public ControlAnimationState ctrlAnimState;

[csharp] view
plain copy

//人物状态

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();

}

}

下面把动画控制脚本代码全部给出

[csharp] view
plain copy

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;

}

}

}

技能控制

[csharp] view
plain copy

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操作

[csharp] view
plain copy

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;

}

}

总结

目前大致做了这么多东西,后续的计划是加上一个属性脚本,控制人物和 怪物的基本属性,由于是学习用,不太复杂,通过属性脚来计算人物造成的伤害等

[csharp] view
plain copy

//角色自身基础属性

[System.Serializable]

public class AttributeBase

{

public int lv, hp, mp, atk, def;//等级,血,蓝,攻击,防御

public float criticalRate, atkSpd,movespd, exp;//暴击率,攻速,移速,经验

}

其次是研究NGUI HUD插件,让 player和怪物之间攻击造成的伤害不仅仅局限在控制台内,让血条,和伤害数字显示在屏幕上,更直观的呈现!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: