3D游戏编程7--巡逻兵(总结之前所有出现过的设计模式)

巡逻兵

游戏设计要求

  • 创建一个地图和若干巡逻兵
  • 每个巡逻兵走一个3~5个边的凸多边型,位置数据是相对地址。即每次确定下一个目标位置,用自己当前位置为原点计算
  • 巡逻兵碰撞到障碍物如树,则会自动选下一个点为目标
  • 巡逻兵在设定范围内感知到玩家,会自动追击玩家
  • 失去玩家目标后,继续巡逻
  • 计分:每次甩掉一个巡逻兵计一分,与巡逻兵碰撞游戏结束

程序设计要求

  • 必须使用订阅与发布模式传消息、工厂模式生产巡逻兵

游戏演示

演示了玩家左键劈砍,右键范围攻击,space跳跃,
巡逻兵触发追踪时候扑到动作,以及碰到玩家挥砍动作
还有玩家攻击时候碰到巡逻兵,巡逻兵会直接消失
游戏演示
游戏演示

准备工作

首先需要下载模型,推荐一个网站还算比较好的免费网站,其中我的作业模型就是从里面下载的,场景模型本来也下载了,但是建立一个场景实在太费时间,所以就直接借用了之前大佬的场景。

人物的动作

首先打开下载好的人物模型,点开人物旁边的按钮可以看到很多部件以及动作,可以点开玩一下。如果决定用这个人物模型,就放置到Resources中的Prefabs文件夹里。放到
下载好的人物模型

在Prefabs中的人物就没有那些动作了,这时候就需要自己设计AnimatorController。
Prefabs中的人物模型

新建一个文件夹AnimatorController,里面用来控制动作,在里面Create,Animator Controller,就比如新建了一个skeleton。
Animator Controller

双击skeleton打开,我已经建立好了,稍微有点复杂,首先在左边的Parameters新建变量,我建立了五个变量,bool型的run,bool型的death,以及Trigger型的attack1,attack2,jump。Trigger类型给的解释是,先设置true,然后自动设置为false,但似乎运行的时候有点奇怪,这个是作为skeleton的动作设计,鼠标左键,从右向左劈砍,鼠标右键,旋转劈砍,空格键跳跃。所以从AnyState开始有三个动作,都是Trigger触发,执行完毕后进入到idle状态,在idle状态下,如果run为true就run,death为true就death,其他同理,这时候不能连到Exit,原因是连到Exit就会从入口再次进入,就会发现死了一遍又又一遍。
skeleton设计图

状态绑定动作,就是Motion那里选择动作,一定要记住将动作的Animation Type改成Generic,才可以从Motion里面选择,并且会生成结尾为Avatar的东西,这个在与物体绑定的时候有用。
Rig
动作绑定

下面是转换时候的设置,至于下面为什么有两个run,这个原因是有时候动作切换很不自然,或者延迟太高,多个组合的话会比较好调整,具体设计方法,把loop打勾,然后apply一下就好。这里还有个小技巧,就是两个状态之间的连线怎么删掉的问题,在Transition下面的点减号就行。
run->death
loop

然后在人物增加新组件,Animator,在Controller和Avatar,选择刚刚做好和生成的文件。这样的话一个具有动作的人物就设置好了。
人物Animator

碰撞检测

这个需要设置碰撞盒,这个是我player的碰撞盒,差不多是药丸形状的,注意IsTrigger不打勾,说明碰到了就是碰到了,而不是碰到了触发一下。
碰撞盒

巡逻兵也需要设置碰撞盒,但是比玩家复杂一点,因为巡逻兵需要检测玩家的范围,所以还要给巡逻兵建立一个box Collider,用户触发追踪事件。
巡逻兵碰撞盒
巡逻兵碰撞盒

人物效果看起来是这样的
人物碰撞盒

当然场景也是需要设置9个碰撞盒的,并且勾上IsTrigger,用来判断人物在哪一块区域。
场景碰撞盒

到此为止准备工作算是做完了。

代码实现

这次我整理和参考了以前的PPT和大佬的代码,严格按照所将的设计模式组织代码。

动作代码的实现

首先动作代码可以复用的,一点都不用改的是,SSActionManager.csSSAction.csISSActionInterface.cs其中ISSActionInterface.csISSActionCallback接口。需要改动的就是CCSequenceAction.csCCMoveToAction.cs以及CCActionManager.cs,似乎CC开头的都是自己定义的。这个图还是牧师与魔鬼那章PPT的图,其中CCSequenceAction.cs是定义动作序列,因为坐船是,人物先移动船的上方,在移到船上面,相当于两个动作,所以用动作序列,而船的移动只有左右,所以用CCMoveToAction.cs实现单个动作。本次作业设计的动作也就两个,第一个是PatrolAction.cs实现巡逻动作,一个是PatrolFollowAction.cs实现跟踪动作,然后这个两个动作的管理在PatrolActionManager.cs中,因为PatrolActionManager.cs实现了ISSActionCallback的接口,所有动作的都是靠这个接口来完成的,所以具体的动作管理,也是靠这个接口。
动作设计模式

PatrolAction.cs 巡逻动作实现

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GoPatrolAction : SSAction
{
    private enum Dirction { EAST, NORTH, WEST, SOUTH };
    private float pos_x, pos_z;                 //移动前的初始x和z方向坐标
    private float move_length;                  //移动的长度
    private float move_speed = 1.2f;            //移动速度
    private bool move_sign = true;              //是否到达目的地
    private Dirction dirction = Dirction.EAST;  //移动的方向
    private PatrolData data;                    //侦察兵的数据


    private GoPatrolAction() { }


    public static GoPatrolAction GetSSAction(Vector3 location)
    {
        GoPatrolAction action = CreateInstance<GoPatrolAction>();
        action.pos_x = location.x;
        action.pos_z = location.z;
        //移动的距离随机,在4-7之间
        action.move_length = Random.Range(4, 7);
        return action;
    }
    public override void Update()
    {
        //因为碰撞会产生不可预料的结果,所以还是要保证模型在正确的位置
        if (transform.localEulerAngles.x != 0 || transform.localEulerAngles.z != 0)
        {
            transform.localEulerAngles = new Vector3(0, transform.localEulerAngles.y, 0);
        }            
        if (transform.position.y != 0)
        {
            transform.position = new Vector3(transform.position.x, 0, transform.position.z);
        }
        //巡逻的动作
        Gopatrol();
        //如果有跟随的玩家,并且玩家在自己所在的区域,就会调用ISSActionCallback接口中的函数
        //所以说动作的管理是靠ISSActionCallback实现的,同样在跟踪的时候的动作也会有切换到巡逻状态的方法。
        if (data.follow_player && data.wall_sign == data.sign)
        {
            //当前动作摧毁掉
            this.destroy = true;
            //切换到追踪状态
            this.callback.SSActionEvent(this,0,this.gameobject);
        }
    }
    public override void Start()
    {
        this.gameobject.GetComponent<Animator>().SetBool("run", true);
        data  = this.gameobject.GetComponent<PatrolData>();
    }

    void Gopatrol()
    {
        if (move_sign)
        {
            switch (dirction)
            {
                case Dirction.EAST:
                  
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值