接上个文章。
我们已经实现了人物的移动了。那我们现在可以调整一下我人物移动的速度和角速度,我们可以在人物的navmeshagent的这里,来设置我们的速度参数
我们可以将这个“speed(速度)”保持不变或者你可以调大一点,另外就是“angular speed”可以调大一点,让它转头更加灵敏,这里我设置为800。并且其下面的这个数值(加速度)调成80应该就OK一点,然后就是这个“Stopping distance”,这个其实就是它的自动停止点,比如我鼠标点在某个位置,如果stopping distance是1的话,它在距离这个位置一个格的单位距离的时候,就会自动停下来,这个功能特别适合于我们在攻击敌人的时候,由于我们有不同的武器的不同长度,那我们就会通过代码来控制这个人物停止的距离。默认我们调整为1就好。大家可以根据自己的喜欢了调整。下面的这个“auto breaking”是自动刹车,也就是我人物快到那个点的时候就开始刹车,然后它会慢慢停下来。
我们总体来看的话,我们会发现一个问题,我现在用的这个mouse manager添加event的这个方式,我需要将我的这个人物拖拽到这里来:
那么如果我们以后切换场景或者新建游戏或者多场景转换的时候,我没有办法一个一个去拖拽,这个也不方便我们保存,那我们现在要介绍一种方式,将我们这个人物移动的这个函数注册到这个event事件当中去(现在的话是我们创建的事件然后用destination去接收这个vector3,然后人物去移动的,也就是我们手动改变的这里:
所以我们不用手动去设置了,因为每创建一个人物,都要去点这个destination,也就是启用这个移动人物的函数,那我们现在不用这样做,因为人物都是需要移动的,所以我们现在直接让这个移动人物的函数,在我们这个事件启动的同时,启动这个人物移动的函数,怎么样才能完成这样的操作呢,那就是将我们这个人物移动的这个函数方法注册到这个事件当中去,只要事件一启动,就连同注册到这个事件底下的所有函数都启动,也就是电路中的串联)
我们注册这个函数到这个事件底下的话,无论我们切换场景也好,还是新建角色也好,都可以将这个人物添加到我们这个OnMouseClicked这个event事件当中去。
那我们现在开始来改变一下我们的mousemanager吧
mousemanager希望不用拖拽的方法就可以调用一下事件里面的函数方法的话,我们要将它写成单例模式,
科普(来源百度):单例模式(Singleton Pattern)的定义为:确保一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
那么我们就开始实现吧!
首先我们要创建一个static的自身的类(mousemanager)的一个变量,因为是写单例,所以通常这个变量名都是起“instance”,接下来在Awake()函数中去判断一下我们的这个实例到底存不存在,因为在单例模式中,一个类只能有一个实例,所以如果发现这个实例不为空,则将这个多余的实例删掉,这里删实例的函数是一个Unity
内置的一个函数,叫Destroy()
科普:Destroy()可用于在运行时删除游戏对象或组件。使用该函数可以在运行时动态删除对象,因此通常在脚本中使用。
如何删掉这个已经存在的实例:
Destroy(gameObject)
如果实例存在我们就删掉,然后现在是没有实例的状态了,那么我们就将instance=this,也就是把当前的这个游戏对象赋值给instance,我们当前的游戏人物是小狗骑士,所以我们就相当于把小狗骑士给了这个变量instance,这样呢我们就相当于可以说简单的实现了代理模式,那么我们如何调用这个单例模式呢,我们就要创建第二个代码,就是我们的playercontroller,返回到Unity当中,我们也要将我们的scripts(脚本)进行归类,所以我们得在scripts文件夹底下创建一个文件夹来专门管理manager类的,因为我们除了mousemanager,还会有gamemanager,还要inventorymanager,还有questmanager等等,那我们还要创建另外一些控制器,比如等等我们要写的playercontroller,以后要写的enemycontroller等等等等这些控制器,所以我们要创建第一个文件夹就叫“controller”
并且在这个文件夹底下创建我们第二个代码——“Playcontroller”,并且把我们这个Playcontroller添加到我们小狗骑士身上,因为我们要控制小狗移动所以就把控制器给到小狗身上,双击打开我们的第二个代码“Playcontroller”,因为刚刚我们已经将我们的mousemanager设置成了单例模式了,所以我们就可以快速来访问它。OK啊,那么我们先来创建一些基本变量先,我们先将Start函数和Update函数删掉,需要时我们再添加回来,那么我们首先要获得的第一个变量就是我们的NavMeshAgent,因为我们希望Playcontroller能控制它的agent来进行移动,所以我们先创建变量,想要使用NavMeshAgent,那我们要使用一个命名空间,就是UnityEngine.AI,这样我们就可以创建NavMeshAgent这个类了,我们用这个类创建一个变量,变量名就叫agent,并且所有我人物自身的变量的获取,我们都可以放到Awake()函数里面进行调用,
科普:Awake函数在对象池中也非常有用,它可以使对象在渲染前进行初始化,减少运行时意外错误的发生,并且在脚本实例的整个生命周期中,Awake函数仅执行一次。
以后养成习惯,比如人物自身的Rigidbody、animator、NavMeshAgent等等,这些我们都放到Awake函数里面去调用,因为Awake会在游戏的最开始最优先获得这些变量的赋值,以免我们出现空引用的情况出现,那么我们在awake函数中,让agent去获得我这个导航组件,agent=GetComponent<NavMeshAgent>();
接下来我们要让人物在游戏场景中启用这个方法(导航)的时候,我们就要去mousemanager中去注册它这个方法,因为只有注册在mousemanager这个event事件上的函数,在事件启用的时候才能被启用,因为它们是“串联”关系,所以我们要返回我们的mousemanager中去。
之前我们用的是系统序列化——public Serializable的方式,去让这个事件在Unity编辑器中能够查看得到这个event事件,也就是:
那么由于我们舍弃了这样的拖拽人物到这个事件上然后启用导航移动的这个函数方法的方式,那么我们现在就不需要这两行了
你可以注释掉,由于以后的学习。
因为我已经删掉这个EventVecture3类的定义了,那么我们就用不了这个类了,也就是说我下面这个
public EventVecture3 OnMouseClicked
这句话中的EventVecture3用不了了,那我们这个事件怎么搭建呢,我们可以用另外一个方法,就是使用嗯嘛Unity自带的event.action,想使用这个event.action,我们就要引用另外一个命名空间了,之前那个engine.events命名空间我们就不要了,我们要用的就是“System”,使用system之后呢,我们就可以直接来创建一个event了,事件的关键字是event,然后事件的类型是Action,接下来这个Action需要哪些变量的参数呢,我们可以用尖括号把它写上,就像我们之前提到的,我们这个事件需要一个三维坐标,还记得手动添加destination吗?因为我们也是要用函数实现人物移动,所以等等我们也会用到这个destination去接收vector3,那么函数的参数得是vector3,所以我们这个事件也需要一个vector3,那现在我们就已经创建好了一个事件:
public event Action<Vector3> OnMouseClicked
那么我们现在就已经成功搭建好了这个事件了,那么event事件里的函数方法就是需要人家来写进去,也就是添加函数方法到这个事件里去,换言之就是event事件中的方法是需要一些函数方法去注册到它底下,也就是说如果我这个事件被启动以后,所以注册到这个事件下的函数方法都会被调用,记住这句话!我们来写一下代码。
我希望在我人物启动的时候,向mousemanager注册一下,我是playercontroller,接下来鼠标点击后我会接收你传过来的Vector3,那我们现在要开始写一个函数,这个函数的作用就是将我的这个人物移动到鼠标点击的那个位置(Vector3)去,那我们就将这个函数方法命名为MoveToTag,并且它是需要一个三维坐标的,因为人物要移动过去嘛所以是从一个坐标到另外一个坐标,所以函数形参就是Vector3的一个结构体变量,这个变量我们就命名为target,那么接下来我要在我的Playcontroller一开始游戏的时候,就将这个函数方法注册到我的mousemanager的那个事件(onmouseclicked)当中去,只要那个事件一启用,就会自动调用我们这个人物移动的函数,那我们就把它写在Start函数里去,
科普:当Update函数第一次被调用前会调用Start函数;Start函数只在脚本实例被启用时才会执行;Start函数总是在Awake函数之后执行。
所以我们要在那个onmouseclicked事件是在update函数中去调用的,并且我们的这个函数注册是需要在这个实例启动之前去注册在这个事件之下的,所以我们要在Start函数中去注册好我们这个这个函数方法到这个事件里去,因为上面说了,start函数是在update函数之前被调用。后面我们可能还会改变注册函数的这个代码的位置,但是现在我们可以这么写先。
因为刚刚我们使用了单例模式,所以我们可以直接访问它
mousemanager.instance.onmouseclicked这句话是什么意思呢,就是说,在mousemanager类的底下有一个实例,这个实例身上挂着一个事件,这个事件就是我们的OnMouseClicked,那么如何将函数方法注册到我们这个事件底下呢?就是得用关键字+=
然后将这个函数方法接在等号右边,因为我们要添加这个movetotag(人物移动)的方法进去,如果你想添加这个函数方法进来,那么你的这个定义方式就要和onmouseclicked这个事件的定义方式一样才行,onmouseclicked需要一个一个vector3那么我添加订阅的这个函数方法也得需要一个vector3,那现在我们就算订阅好了,那么我们回到我们movetotag这个函数中去,既然我这个函数订阅到了这个事件底下,那么我们这个函数就可以拿到这个事件里的vector3,既然我拿到了这个三维坐标,那么我们就要实现这个人物移动了。
还记得以前吗?是不是用destination去接收这个vector3的,那这里也一样,因为agent已经得到了navigation的这个导航了,那么在导航底下就同样也有这个destination,我们只要把我们得到的vector3,赋值给destination,那么我们的人物就能从一开始的坐标到我新得到的这个坐标上去。最终代码: