本系列文章由Aimar_Johnny编写,欢迎转载,转载请标明出处,谢谢。
/lzhq1982/article/details/12653945
我们的世界有了怪物,那么你怎么忍心不去虐他们一下,勇士,挥舞你的大刀,去砍他们吧。呃,有点血腥,少儿不宜。
如上一篇所说,我这里的交互全是在单例脚本中实现的。命名为BattleScene,单例脚本负责事件的分发和传递,Hero和Monster脚本负责触发和接收处理消息,设计理念是在Hero脚本中你绝对看不到Monster,同样,Monster脚本中,你也看不到Hero,他们都在单例脚本中,单例脚本中有这么一段核心的消息传递代码:
publicvoidSendGameMessage(MESSAGE_TYPEtype,Tt)
{
switch(type){
caseMESSAGE_TYPE.MESSAGE_HERO_RUN_AT_TARGET:
_hero.SendMessage("RunAtTarget",t);
break;
caseMESSAGE_TYPE.MESSAGE_HERO_BE_HURT:
_hero.SendMessage("ReduceHp",t);
break;
caseMESSAGE_TYPE.MESSAGE_ENEMY_BE_HURT:
if(_enemy)
_enemy.SendMessage("BeHurt");
break;
caseMESSAGE_TYPE.MESSAGE_ENEMY_REDUCE_HP:
if(_enemy)
_enemy.SendMessage("ReduceHp",t);
break;
}
}
每一条消息传递一个交互,现在看不懂没关系,下面我再细细讲解,只是这里的,如果有人不清楚,可以说一下,它是C#里的泛型的概念,C++里也有类似的模板,说白了,就是可以代替你想要的类型,用处很广,这里用来传递数值,因为不知道数值会是什么类型,所以泛型就大显神威了。
言归正传,我的英雄和怪物的交互流程是这样的,点击一个怪->英雄跑过去->英雄发起攻击->怪物受击->怪物反击->英雄受击->英雄继续攻击->如此反复,直到一方死去。下面就按照这个流程看看我是怎么实现的。
1、点击一个怪
voidOnMouseDown()
{
BattleScene.GetInstance().SendGameMessage(BattleScene.MESSAGE_TYPE.MESSAGE_HERO_RUN_AT_TARGET,gameObject);
}
这是怪物脚本上的响应鼠标点击的函数,就一句,向单例脚本发消息,第一个参数是消息类型,第二个参数是传递的数值,这里传的是怪物的gameobject。然后你可以对照上面的消息传递代码,向hero脚本的RunAtTarget()这个函数传递消息,意思是英雄啊,你该向那个怪跑了。
2、英雄跑过去
voidRunAtTarget(GameObjectobj)
{
BattleScene.GetInstance().Enemy=obj;
curState=en_state.en_state_run;
isHunting=true;
}
caseen_state.en_state_run:
curAnimClip=animation["Run"].clip;
gameObject.animation.CrossFade(curAnimClip.name);
Vector3forward=transform.TransformDirection(Vector3.forward);
_controller.SimpleMove(forward*runSpeed);
if(BattleScene.GetInstance().Enemy){
smoothRotate(BattleScene.GetInstance().Enemy.transform.position);
}elseif(runTarget!=Vector3.zero){
。。。
}
break;
RunAtTarget的参数传递的是怪物的gameobject,但我不会将这个怪物存在hero脚本中,我把它传给了单例脚本,以后想要获得这个怪物信息,找单例脚本要去,这里粘一下单例脚本的Enemy代码
publicGameObjectEnemy
{
get{return_enemy;}
set{_enemy=value;}
}
C#的这种get和set方式挺简便的,我就给试上了。然后在update的状态机里处理奔跑,把以前单纯的奔跑改了一下,如果有Enemy,就向Enemy平滑转身,smoothRotate是处理平滑转身的,不清楚的可以在我前面的文章里找到详细代码。
3、英雄发起攻击
if(isHunting){
if(BattleScene.GetInstance().IsInAttackArea()){
curState=en_state.en_state_attack;
BattleScene.GetInstance().SendGameMessage(BattleScene.MESSAGE_TYPE.MESSAGE_ENEMY_BE_HURT,0);
isHunting=false;
}
}
caseen_state.en_state_attack:
if(BattleScene.GetInstance().Enemy){
AnimationStatestate=animation[curAnimClip.name];
if(state.time>=state.length-0.1f){
intrand=Random.Range(0,3);
if(rand==0)
curAttackState=attack_state.attack_state_0;
elseif(rand==1)
curAttackState=attack_state.attack_state_1;
elseif(rand==2)
curAttackState=attack_state.attack_state_2;
isReduceEnemyHp=true;
}
if(state.time>=state.length/2&&state.time
if(isReduceEnemyHp){
isReduceEnemyHp=false;
BattleScene.GetInstance().SendGameMessage(BattleScene.MESSAGE_TYPE.MESSAGE_ENEMY_REDUCE_HP,20);
}
}
if(curAttackState==attack_state.attack_state_0)
curAnimClip=animation["Attack"].clip;
elseif(curAttackState==attack_state.attack_state_1)
curAnimClip=animation["Attack00"].clip;
elseif(curAttackState==attack_state.attack_state_2)
curAnimClip=animation["Attack01"].clip;
animation.CrossFade(curAnimClip.name);
transform.Translate(Vector3.zero);
}else{
curState=en_state.en_state_idel;
curAnimClip=animation["Attack"].clip;
}
break;
代码有点长,前面的代码是在update里随时判断英雄是否跑到怪物身边,也就是是否在攻击范围内,如果是,置攻击状态,在状态机里处理,同时给怪物发消息,你受到攻击了,不要再悠闲的溜达了,赶紧还击吧。判断攻击范围代码如下:
publicboolIsInAttackArea()
{
if(_hero&&_enemy){
CharacterControllerenemyController=_enemy.GetComponent();
CharacterControllerheroController=_hero.GetComponent();
floatdist=Vector3.Distance(_hero.transform.position,_enemy.transform.position);
if(dist<=enemyController.radius+heroController.radius+1.0f)
returntrue;
}
returnfalse;
}
注意这段代码是在单例脚本里的,因为要用到hero和enemy,原理就是用他们的CharacterController得到他们的半径,如果他们的距离小于这两个半径之和,就说明发生碰撞了,就可以攻击了,加1是不想他们太近而已,哈哈。
接着说状态机里处理攻击的部分,先获得攻击动画的属性,如果该攻击动画要播完了,就随机出下一个攻击动画,我这里有三个攻击动作用来随机,如果攻击动作播了一半了,大概就是刀落在怪身上了,向怪发出掉血消息,如果怪物没了,则重置英雄为休息状态。这里插播一下怪物掉血,当英雄砍到怪物身上后,发出怪掉血信息,怪接收到掉血信息,如上面单例脚本的消息传递部分,执行ReduceHp代码。
voidReduceHp(intnHp)
{
curHP-=nHp;
if(curHP<=0){
BattleScene.GetInstance().Enemy=null;
Destroy(gameObject);
}
}
代码很简单,怪物累积减血,当血量小于等于零时,怪物死亡,删除掉该怪物,其实如果有死亡动画,播动画更好,我这里没有,就直接删了。
4、怪物受击
如果觉得《unity怪物攻击玩家减血_unity3d学习笔记(七)利用单例脚本实现英雄与怪物的攻击与受击...》对你有帮助,请点赞、收藏,并留下你的观点哦!