




已阅读5页,还剩12页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
泰课在线2013.1.193.【第一张地图与跳跃的改善】继续吧,为了方便继续编写的敌人之类的脚本,所以打算今天先把第一张地图画个大概。结果,画了我好久的地图. ()我对美术方面的天赋简直是惨不忍睹了.加之地图中途画的差不多的时候Unity又崩溃了一次.忘了保存(o)。于是又忙活半天.这个地图完全靠临时发挥了,第一张地图我画了好几次了,基本上每次画出来的地形之类的都完全不一样,所以就不多解释了,就上一张大概的场景图吧:看起来好简单吧?唉.开启编辑器,这时侯如果在场景中转悠的话,很轻易就可以发现人物跳跃的功能有些瑕疵,就是跳动速度太快,相机都晃的花眼.事实证明,偷懒是行不通的.那么现在还是打开PlayerControl脚本改造一下吧。在脚本的Move函数中,原本的跳跃功能只有短短两行,就是判断人物是否位于地面,在的话,就把人物向上抬升一段距离,结果造成跳跃过于突兀的效果。现在的话,可以先把判断角色是否位于地面的那段if语句注释掉,删掉也可以。为了使跳跃起来不那么突兀,我的想法是定义一个bool变量,当角色在地面上按下空格键时,变量为true,否则当角色跳起来超过一定高度就为false,判断高度的方式,我想来想去,用了射线检测的方式(当然,没用的错误的实验肯定不会写出来的)。首先在最开始Start函数上面定义一个bool变量:#region 判断private bool m_jumpTmp=true;/判断是否可以跳跃#endregion然后在玩家按下空格后,定义一个临时的变量存放角色向下的方向,再判断角色是否位于地面,如果在地面,就可以挑。否则跳跃超过1.5米后,就得等落地了才能跳了:if(Input.GetKey(KeyCode.Space)Vector3 m_direction=m_transform.TransformDirection(Vector3.down);/方向向下的临时变量if(m_controller.isGrounded)m_jumpTmp=true;else if(!Physics.Raycast(m_transform.position,m_direction,1.5f)/判断跳跃高度m_jumpTmp=false;if(m_jumpTmp)m_animation.CrossFade(Jump2);m_transform.Translate(Vector3.up*m_jump*Time.deltaTime);之所以把东西写在按空格键里面,是因为节省些许性能。把代码如此改了之后,再回到游戏中运行试一下,跳跃果然要缓多了。2014.1.204.【敌人的AI(一)】今天就先来写敌人的AI吧。首先导入一群怪物的模型包,然后在根目录下新建一个文件夹,名为_Enemy,然后将导入的怪物模型移动进去,以方便后来分辨。然后在_Script中新建一个文件夹,名为Enemy,然后在Enemy文件夹中新建一个脚本,名为Enemy。因为考虑到敌人肯定不止一个,所以每个敌人都采用单独的脚本肯定不现实(这也使我当初重写了四五遍敌人脚本后才恍悟到的.),我就想到是否可以直接先写一个敌人的主要脚本,往后每个敌人的脚本都从中继承,每个敌人到时只需要改一点就可以了。在脚本最开始,首先定义敌人的属性及自身的对象:#region 属性public string m_name;/名字public int m_level;/等级,以判断角色应当获取的经验public int m_maxLife;/生命public int m_currentLife;/当前生命public int m_defense;/防御力public int m_attack;/攻击力public float m_speed;/速度public int m_rotateSpeed;/旋转速度public float m_attackRadius;/攻击半径#endregion#region 对象public Transform m_transform;/自己#endregion在这里,对敌人就不设置魔法值了,其它属性跟角色的差不多罢。因为在游戏中我还设置了Boss,因此还必须定义一个判断敌人是否是Boss的bool变量,默认为false:#region 判断public bool m_isBoss=false;#endregion然后在Start函数中取得自身对象:m_transform=this.transform;/获取自己对象因为现在这个敌人脚本只是一个基类,因此还必须要求从其继承的那些敌人能够为自己的属性进行赋值,所以我又定义了一个SetAttribute的虚函数函数,类型为保护,这样在子类中就可以用这个类进行敌人各项属性的赋值:protected void SetAttribute(string name,level,int Life,int defense,int attack,int speed,int rotatteSpeed,float attackRadius)m_name=name;m_level=level;m_maxLife=Life;m_currentLife=Life;m_defense=defense;m_attack=attack;m_speed=speed;m_rotateSpeed=rotatteSpeed;m_attackRadius=attackRadius;为了不在子类中更改Start函数,另外还要再声明一个Init的虚函数,里面什么都不写,并在Start函数最开始进行调用:void Start () Init();m_transform=this.transform;/获取自己对象protected virtual void Init()这样的话,子类初始化什么东西的话,就可以直接在这个函数中搞定了。然后,敌人的状态肯定要分为没有目标时,干什么,有目标时就攻击目标之类的动作,所以,应该再定义一个bool类型的变量,进行这点的判断吧。这个类型就定义在“判断”那一堆:#region 判断public bool m_isBoss=false;protected bool m_isIdle=true;#endregion这时就可以在Update函数中进行判断了:if(m_isIdle)/如果处于空闲状态else这时候,我选择在最开始加上一堆放“变量”的,并在那儿定义一个随机值m_idleStat用来作为随机播放动画的随机值,m_idleTime用来限制每一个动画播放的时长:#region 变量protected int m_idleStat;/定义空闲状态动画的随机值protected float m_idleTime=0;/每一个空闲状态持续时间#endregin现在就可以在Update函数中加上这些:if(m_isIdle)m_idleTime-=Time.deltaTime;if(m_idleTime0)/空闲随机时间到了就随机做出新动作m_idleStat=Random.Range(0,11);audio.clip=m_sound;audio.Play();m_idleTime=3;其中的随机值用来随机出010几个数字,用来判断随机播放什么动画。并且,在播放动画的同时,叫一声。声音变量定义在前面“变量”标签中,现在既然涉及到声音,那就把攻击声音和技能声音一块儿定义了:/声音public AudioClip m_sound;public AudioClip m_attackSound;public AudioClip m_skillSound;既然涉及到了动画,那就要获取动画组件了。在脚本Start函数上面的“对象”标签中,再定义一个动画组件:protected Animation m_animation;/动画组件并在Start函数中进行初始化:m_animation=GetComponent();这时候再定义一个“动画”标签,将敌人的各个动画分别赋值给定义的string类型,这样会 更加好控制:#region 动画状态protected string m_animationIdle=idle1;protected string m_animationIdle2=idle2;protected string m_animationIdle3=idle3;protected string m_animationWalk=walk;protected string m_animationAttack=attack;protected string m_animationSkill=skill;protected string m_animationDeath=death;#endregion这些动画,到时候需要在Inspector中对敌人的动画进行改名,这时先就定义好吧。动画之类的都定义好了,在Update函数中就可以进行随机动画的判断了。这里我使用的Swicth,以前本来也不是使用switch进行判断的,现在写的时候觉得switch可能比较简单些,就改了结果好像还复杂些了.不过没办法,折腾了半天,都差不多弄好了,就不改了:switch(m_idleStat)case 1:m_animation.CrossFade(m_animationIdle);audio.clip=m_sound;audio.Play();break;case 2:m_animation.CrossFade(m_animationIdle);m_transform.Rotate(0,m_rotateSpeed*Time.deltaTime,0);break;case 3:m_animation.CrossFade(m_animationIdle2);break;case 4:m_animation.CrossFade(m_animationIdle2);break;case 5:m_animation.CrossFade(m_animationWalk);m_transform.Rotate(0,m_rotateSpeed*Time.deltaTime,0);break;case 6:m_animation.CrossFade(m_animationWalk);m_transform.Rotate(0,m_rotateSpeed*Time.deltaTime,0);break;case 7:m_animation.CrossFade(m_animationWalk);m_transform.Rotate(0,m_rotateSpeed*Time.deltaTime,0);break;case 8:m_animation.CrossFade(m_animationWalk);m_transform.Translate(Vector3.forward*m_speed*Time.deltaTime);break;default:m_animation.CrossFade(m_animationIdle3);m_transform.Translate(Vector3.forward*m_speed*Time.deltaTime);break;原理就是使用switch判断每个随机数字,在某些数字上就旋转或者前进,或者嫌在原地。东西看起一大堆,实际上原理很简单.然后在状态最后向自己前面发出一条射线,判断自己前方有没有人,有的话,就将状态m_isIdle置为false:m_forward=m_transform.TransformDirection(Vector3.forward);if(Physics.Raycast(m_transform.position,m_forward,out m_hit,m_attackDistance,m_layerMask)m_attackTarget=m_hit.collider.gameObject.transform;/如果看见任何人类对象,就获取其位置,并将Idle状态置为falsem_isIdle=false;其中的m_forward是向前的一个Vector3变量,定义在前面“变量”标签中:protected Vector3 m_forward;/敌人的前方向hit是属于RaycastHit变量,所以也要在前面定义:protected RaycastHit m_hit;/射线碰撞m_attackDistance是定义的敌人视线距离(还是那句话,我数学不好,就不用那些复杂的模拟现实去判断怪物的视角高度之类的了,直接一条有距离的射线从敌人面前射出去,撞上了就代表“看见了”.),也要定义在最前面“变量”标签中:protected float m_attackDistance=8;/会攻击的视线距离至于m_layerMask则是层蒙版,今天在这个上面又折腾了好久.我在编辑器的层标签里面新建了一个标签,命名为:“Human”,并把它指派给了玩家,然后再在最开始变量标签中定义了一个层protected LayerMask m_layerMas;在Start函数中进行初始化:m_layerMask=1LayerMask.NameToLayer(Human);最后m_attackTarget就是属于“看见”的目标对象了,看见了谁,就把谁赋给它,然后对这个对象进行攻击等等,也先定义在开始的“对象”标签中:public Transform m_attackTarget;/攻击对象获取到了看见的对象后,就可以把m_isIdle这个空闲状态设置为false了,代表不再闲着没事儿,而是应该进行攻击了。接下来就在Update函数中的if(m_isIdle)判断后面加上一个else语句,里面放上RotateTo();Attack();两个函数。这两个函数写在外边,也是同样考虑到以后有的敌人可能会改变攻击方式的。首先是Rotate函数,定义在Update函数下边吧:protected void RotateTo()Vector3 m_oldAngle=m_transform.eulerAngles;/当前旋转角度m_transform.LookAt(m_attackTarget.position);/盯着攻击对象float m_target=m_transform.eulerAngles.y;/目标角度吧float m_targetAngle=Mathf.MoveTowardsAngle(m_oldAngle.y,m_target,m_rotateSpeed*Time.deltaTime);/计算应转动的角度m_transform.eulerAngles=new Vector3(0,m_targetAngle,0);/转动角度作用就是改变敌人的当前面向的角度到面向攻击对象的方向,说实话,这一段我也不是很明白,只是知道大概达到了转向的效果。在Rotate函数下继续定义Attack攻击函数,在函数中用到的变量m_distanceToOther是敌人当前与攻击目标的距离,定义在前面的“变量”标签中:protected float m_distanceToOther;/与攻击对象的距离出此之外还有攻击的粒子效果,粒子效果放在“对象”标签中,这里我就先把两个粒子效果都定义起来了:public Transform m_attackParticle;/普通攻击粒子效果public Transform m_skillParticle;/技能攻击粒子效果这个粒子效果只是用来判断攻击对象的,真正的伤害计算我准备放到粒子效果里面,那样的话,大概能做到无论是NPC还是敌人都能进行攻击罢?(为自由的游戏而奋斗)。攻击函数:protected void Attack()m_distanceToOther=Vector3.Distance(m_transform.position,m_attackTarget.position);if(m_distanceToOtherm_attackRadius)/目标进入攻击半径,攻击m_forward=m_transform.TransformDirection(Vector3.forward);if(Physics.Raycast(m_transform.position,m_forward,out m_hit,m_attackRadius,m_layerMask)m_animation.CrossFade(m_animationAttack);Instantiate(m_attackTransform,m_hit.collider.gameObject.transform.position,Quaternion.identity);/攻击,实例化一个粒子else if(m_distanceToOther20)/如果距离小于20,对目标进行追击m_animation.CrossFade(m_animationWalk);m_transform.Translate(Vector3.forward*m_speed*Time.deltaTime);else if(m_distanceToOther90)/ 如果距离大于20,就回到Idle状态,不追目标了m_isIdle=true;else/超过九十米的话,为了省点资源,就把这个敌人销毁罢Destroy(this.gameObject);这个攻击函数会先对自己与攻击对象的距离进行判断,若果两者之间的距离小于自身的攻击半径,就向前发射一条射线,若果碰撞到了任何人类层的目标,就进行攻击(实例化一个普通攻击的粒子效果)。否则两则距离小于20米的话,就项目标跑过去,当然,距离大于90米的话(出了玩家的视线范围),就直接销毁自身。好了,天色不早了,接下来的明天继续吧。2014.1.215.【敌人AI(二)与.攻击判定】昨天的脚本现在其实已经可以运行进行测试了。那么就先测试一下。首先找到_Enemy文件夹中导入的怪物模型,这里我以wolf,也就是狼为例,它也将作为玩家最先见到的敌人。将狼的模型拖入场景中,然后在Inspector面板中将小狼模型所需要用到的动画按照先前脚本中定义的名字改名,这里主要是为了方便,因为默认状态动画有了,就不用在继承“敌人”脚本的子类中去赋值动画名称了,相比在脚本中赋值动画名来说,在Inspector中改动画的名字显得轻松多勒!如图:只需要将需要用到的动画改名就可以了。然后就是脚本了。在_Script文件夹下的Enemy文件夹下新建一个脚本,名为“EnemyWolf”,双击打开脚本。在最开始那儿就不能让其继承自Monobehavaour,而要改为Enemy。然后覆盖先前啥都没写的Init函数,他将实现小狼属性的初始化:public class EnemyWolf : Enemy protected override void Init()SetAttribute(小狼,2,100,20,20,2,60,2.5f);好了!这个脚本就算完成了!什么?!就这点?木错,以后的敌人跟这个相差应该也不是很大,就是这样。然后将一个狗叫的音效拖到脚本的m_sound上(浪叫不好找啊,就用狗叫来代替咯.),并将音效赋给小狼的AudioSource组件,去掉勾选默认播放。现在可以将脚本拖到场景里的小狼身上了,运行游戏,小狼开始在原地转悠起来,过一会儿叫一声.并且看见玩家的角色后,还会跑过来一直跟着。(本来是有问题的,调试了良久之后,放在这儿的脚本当然没有问题了.),现在初步的AI就完成了。额.又调试了一下,发现还是有个小问题,就是敌人经常看不见玩家的角色似的?直接无视?又仔细调试了几遍,发现了原因所在, 这个原因挺重要的,就在这儿提一下: 默认这些对象的position一般都是物体的 最底部,也就是人的脚下之类的地方,直接发射射线的话,从地面射出的射线恐怕也只有射中别人的脚.( ),所以发射射线时,应该让坐标向上抬起一段距离,所以应该改一改Enemy的脚本,把脚本中两处发射射线的地方,第一个起始位置加上一个Vector3.up:if(Physics.Raycast(m_transform.position+Vector3.up,m_forward,out m_hit,m_attackRadius,m_layerMask)。现在就没问题了,当角色进入攻击半径时,它甚至会进行攻击。只是因为现在还没有将攻击的粒子赋给它,所以控制台会打印一个错误。刚才用手机去了一趟官网,被打击勒.那么导入粒子资源包,创建一个_Particle文件夹存放,然后拖放一个粒子效果到小狼脚本上去,同时将小狼攻击音效拖放到其身上,再次运行游戏,便可以看见攻击效果了。考虑到以后工程项目中都会存在这些资源,所以以后我都不会再提起导入资源包的事儿了。虽然是这些行为看似没问题了,不过小狼的攻击明显过快,这里就还的设置一个攻击间隔,定义在最开始的“变量”标签中:protected float m_attackDelay=2;/攻击延时然后再Attack函数中进行攻击间隔的判断,只有当攻击间隔时间到了,小狼才会再次攻击:m_attackDelay-=Time.deltaTime;if(m_distanceToOtherm_attackRadius)/目标进入攻击半径,攻击if(m_attackDelay0)m_forward=m_transform.TransformDirection(Vector3.forward);if(Physics.Raycast(m_transform.position+Vector3.up,m_forward,out m_hit,m_attackRadius,m_layerMask)m_animation.CrossFade(m_animationAttack);Instantiate(m_attackParticle,m_hit.collider.gameObject.transform.position+Vector3.up,Quaternion.identity);/攻击,实例化一个粒子m_attackDelay=2;/攻击成功,重置延时同时我在实例化攻击粒子时加了一个Vector3.up向量,令粒子产生在玩家的身体中间,这样看起来好看些。如此,攻击也还算正常了。敌人的技能应该属于随机发出来的,所以还要定义一个随机数,通过随机出敌人何时发出技能,随机数依然定义在最开始的“变量”标签中:protected float m_skillRandom;/随机出现技能然后在修改一下上面的攻击射线碰撞:if(Physics.Raycast(m_transform.position+Vector3.up,m_forward,out m_hit,m_attackRadius,m_layerMask)m_skillRandom=Random.value;if(m_skillRandom0)m_realAttack=(int)Random.Range(m_attack-m_attack*0.25f,m_attack+m_attack*0.25f);/随机伤害值,范围在正负25%以内Debug.Log (伤害:+m_realAttack);if(m_realAttack-m_defense0)m_life-=(m_realAttack-m_defense);elsem_life-=1;if(m_life=0)m_life=0;m_isDead=true;这些函数的作用就是通过对方的攻击对生命值进行加减,通过将传入的攻击力进行正负25%波动的后(这样可以免得攻击力),再进行实际伤害判定,如果攻击力超过防御,就受到攻击减去防御力的伤害,或者如果无法破防,就强制扣除一点生命。如果生命小于等于零,就将生命置为零,并将物体设定为“死亡”状态。好,那么现在打开_Script下的PlayerControl脚本,在这个脚本中需要定义一个函数对BattleChange脚本中的数据进行同步。首先需要在”对象”标签中BattleChange脚本对象的定义:private BattleChange m_changeState;/战斗时改变生命等属性脚本对象并在Start函数中获取BattleChange脚本对象,同时为其变量赋值:m_changeState=GetComponent();m_changeState.m_defense=m_defense;/防御m_changeState.m_life=m_currentLife;/生命m_changeState.m_magic=m_currentMagic;/魔法值然后在末尾定义一个名为GetState的函数,负责对数据进行同步:void GetState()m_currentLife=m_changeState.m_life;m_currentMagic=m_changeState.m_magic;最后在Update函数中加上GetState函数调用即可。玩家判定伤害这儿就算完成了,接下来是对敌人的主脚本的修改。对敌人的主脚本修改就比较简单了直接在原本实例化攻击粒子效果之后加上发射一条射线,获取所碰撞到的对象BattleChange脚本即可:if(Physics.Raycast(m_transform.position+Vector3.up,m_forward,out m_hit,m_attackRadius,m_layerMask)m_skillRandom=Random.value;if(m_skillRandom0.96f)/大概有4%的几率发出技能m_animation.CrossFade(m_animationAttack);/攻击动画Instantiate(m_attackParticle,m_hit.collider.gameObject.transform.position+Vector3.up,Quaternion.identity);/攻击,实例化一个粒子m_damageTarget=m_hit.collider.gameObject.GetComponent();audio.PlayOneShot(m_attackSound);/播放声音m_damageTarget.Damage(m_attack);/调用对象的伤害函数if(m_damageTarget.m_isDead)/如果攻击目标死掉了的话,就回到Idle状态吧m_isIdle=true;elsem_animation.CrossFade(m_animationSkill);Instantiate(m_skillParticle,m_hit.collider.gameObject.transform.position+Vector3.up,Quaternion.identity);/攻击,实例化一个技能粒子m_damageTarget=m_hit.collider.gameObject.GetComponent();audio.PlayOneShot(m_skillSound);m_damageTarget.Damage(m_attack*3);if(m_damageTarget.m_isDead)/如果攻击目标死掉了的话,就回到Idle状态吧m_isIdle=true;m_attackDelay=2;现在再运行游戏,小狼对玩家的攻击已经正常了,如图:不过在玩家生命数低于0时,也就是死亡后,小狼还是在烦人地不停地攻击,这点可以修改一下敌人主脚本中Update函数中的射线碰撞进行判断,只需要添加一行代码就可以了;if(m_attackTarget.GetComponent().m_life!=0)m_isIdle=false;敌人的可以攻击了!可是玩家自己的角色角还只能站着挨打或者跑?太不妥当了!那么,现在就打开PlayerControl脚本,老样子,首先还是在前面的“变量”标签中定义三个变量:private RaycastHit hit;private Vector3 m_forward;/向前的方向private LayerMask m_layerMask;/射线检测层其中那个层遮罩还必须在Start中初始化:m_layerMask=LayerMask.NameToLayer(Human);然后是在最后添加一个函数,我就起名叫Attack罢:void Attack()if(m_currentLife0)if(Input.GetMouseButtonDown(0)m_animation.CrossFade(Attack3-2);m_forward=m_transform.TransformDirection(Vector3.forward);if(Physics.Raycast(m_transform.position+Vector3.up,m_forward,hit,2,m_layerMask)以上的代码足以让角色可以攻击,不过却造成不了伤害。现在在开始的“对象”标签中,定义一个普通攻击的粒子效果以及音效和一个攻击对象:public Transform m_attackParticle; /普通攻击粒子public AudioClip m_attackAudio; /普通攻击音效private BattleChange m_nowAttackTarget;/当前攻击对象然后再上面的Atack函数中判定射线是否碰撞到物体语句里面就可以引用了:if(Physics.Raycast(m_tra
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 护工护理基础知识培训课件
- 2024年许昌市特招医学院校毕业生和特岗全科医生招聘真题
- 二零二五年度中科软件园办公场地租赁合同设施使用规定
- 二零二五年信息安全工程师劳动合同范本
- 2025版集资房物业管理及服务标准合同
- 2025版夫妻双方签订的离婚后子女医疗费用承担协议
- 护套工艺基础知识培训课件
- 装配式路面板施工方案
- 定西钢结构隔层施工方案
- 护士抽血基本知识培训课件
- 2023年中小学心理健康教育课程标准
- 汛期安全教育知识
- 《进项税额的抵扣》课件
- 新能源汽车驱动电机种类及未来发展趋势
- 瑜伽消费市场潜力评估-洞察分析
- 苗木培育及示范林抚育投标方案(技术方案)
- 海姆立克急救法课件
- 工业锅炉水质课件
- 暑假工招工合同范例
- FOCUS-PDCA改善案例-提高术前手术部位皮肤准备合格率医院品质管理成果汇报
- 天津市南开区2024-2025学年九年级上学期期中考试化学试题
评论
0/150
提交评论