给猫看的游戏AI实战(一)Unity俯视角游戏初步

作者:Levelplay皮皮关游戏教育
2017-10-12
11 8 2

作者注

应 indienova 邀请,转自本人的知乎专栏,为 AI 系列的第一篇。如有任何疑问欢迎各位朋友指出。

前言

大家好,如题目所说的,我给自己开(wa)了一个坑,专门讲讲和游戏AI有关的内容。难度不高,会从非常非常基础开始,假定用户是我家的大猫 (´・ω・`) 。

现在Unity方面的教程和视频可以说是汗牛充栋,为了找到有特点的角度作者我也是煞费苦心。机缘巧合最近在做一些游戏AI相关的内容,于是就开了这个坑。

废话不说,第一节作为适应课程,内容相对很少,但是之后有不少例子是基于这种俯视角游戏的,俯视角游戏在观察 AI 行为方面很直观很好用,所以要先搭一个学习用的工程。没基础的同学跟着自己做一下就能搞定,有基础的同学可以一遍扫过去毫无压力。

1

一、场景搭建

2

按照上图所示,搭建一个简单的场景

1、创建一个平面作为地板,地板放大一些,3倍左右,地板默认有 Mesh Collider 组件。

2、放几个方块 Cube 作为障碍物,Cube默认有碰撞体 Box Collider 组件。

3、创建一个胶囊体代表玩家,正面最好加上一个彩色的方块以区分正面在哪。下面有主角特写,感觉有点像小黄人。

3
4、创建材质可以用来改变贴图的颜色。在Assets目录里创建 Material,然后调个颜色,拖到物体上即可。下面有截图。

4

5、为方便起见,给主角加上 Rigid Body 刚体组件,这是一种物理有关的组件。有了它就可以用速度、力等方法控制角色了。但是这里注意取消重力,也就是取消 Use Gravity。

5

前面的步骤非常基本,我家的猫已经做完了。接下来要写点代码控制主角的移动。

6、在 Project 窗口 Assets 文件夹点击右键—— Create 新建——C# Script,创建一个新的 C# 脚本,命名为 Player,拖拽到场景里的 Player 上(胶囊体)。

7、双击这个脚本,开始编辑吧。我们的代码都经过了简化,力求简单。

public class Player : MonoBehaviour {
    // 用于控制移动速度,public变量方便在编辑器里修改
    public float moveSpeed = 6;
    // 代表刚体组件的变量
    Rigidbody myRigidbody;

    // Player GameObject初始化时调用
    void Start()
    {
        // 在这里给myRigidbody变量赋值,方便后面的使用。
        // 如果不用这个变量也可以,那每次都需要GetComponent,会很麻烦。
        myRigidbody = GetComponent();
    }

    // 每帧被引擎调用一次,下面详细说明
    void Update()
    {
       // 后面添加
    }
}

8、我们打算用鼠标+键盘控制 Player 的移动,和大量流行的俯视角射击游戏是一样的,鼠标用来指方向、开火。键盘的 WASD 用来移动。首先解决鼠标指哪,主角就转向哪的问题:

根据鼠标位置,创建一个射线:

// 说明:鼠标在屏幕上有一个位置Input.mousePosition,这个位置和游戏无关
// 就是你windows鼠标的位置而已
// 通过Camera.main,也就是主摄像机的,就可以把鼠标位置转换到游戏中去,制造一条射线
// 这个射线很好理解,你拿一根针,垂直屏幕,戳在鼠标的位置,就是这条射线ray了。
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);

我们拿着这个射线让它往垂直屏幕的方向射过去,就会碰到物体(准确的说是有 Collider 碰撞组件或者 Rigid Body 刚体组件的物体),然后碰撞的交点就是我们输入的点:

// 先创建一个RaycastHit变量,也就是射线碰撞信息,在下面一句用到。       
RaycastHit hitt = new RaycastHit();
// 这句话的意思:让射线ray打出去,距离100(很长的距离),把碰撞结果返回到hitt变量中。
// out参数就是用来输出结果的,是C#特有的写法。
Physics.Raycast(ray, out hitt, 100, LayerMask.GetMask("Ground"));

如果不严谨地做,最后的 LayerMask.GetMask("Ground") 参数是不需要的,不写的话如果鼠标当前位置正好是 Player 或者障碍物,就可能会造成偏差。所以我们限定射线只能和地板碰撞,穿过其他东西。这里需要设置一下地板的 Layer,如下图操作:

6

在任意一个空白的 Layer 上写上 Ground,就创建了一个 Ground 层。然后把地板的 Layer 设置为 Ground 层即可。

7

接着写代码:

// 打印Log,方便查看碰撞是否成功了。如果不成功八成是Layer设置的问题
        Debug.Log("hitt", hitt.transform);
// 如果碰撞到了东西
        if (hitt.transform != null)
        {
// 让当前物体(也就是挂着这个脚本的Player)转向目标点。注意目标点的写法。
            transform.LookAt(new Vector3(hitt.point.x, transform.position.y, hitt.point.z));
        }

Update 函数整个代码如下:

void Update()
    {
        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
        RaycastHit hitt = new RaycastHit();
        Physics.Raycast(ray, out hitt, 100, LayerMask.GetMask("Ground"));
        Debug.Log("hitt", hitt.transform);
        if (hitt.transform != null)
        {
            transform.LookAt(new Vector3(hitt.point.x, transform.position.y, hitt.point.z));
        }
    }

9、经过步骤8的详细说明,我们已经可以能让主角跟随鼠标转动了,播放一下,看看效果。

8
10、之后添加用 WASD 移动的代码,很简单只有一句,加在 Update 函数的最后面一句即可:

myRigidbody.velocity = new Vector3(Input.GetAxisRaw("Horizontal"), 0, Input.GetAxisRaw("Vertical")).normalized * moveSpeed;

这一句对新手很不友好,可以解释为以下N句:

  // 获得输入的H轴和V轴,也就是横轴和纵轴,也就是 W、S 键或是 A、D 键的状态。
        float input_h = Input.GetAxisRaw("Horizontal");
        float input_v = Input.GetAxisRaw("Vertical");
        // 输入是一个-1~+1之间的浮点数,把它转化成方向向量
        Vector3 vec = new Vector3(input_h, 0, input_v);
        // 当W键和D键同时按下时,vec会比单按W键要长一些,你可以想想为什么。
        // 所以这里要把输入归一化,无论怎么按键,vec长度都要一致。
        vec = vec.normalized;
        // 乘以moveSpeed可以让调整vec的长度
        vec = vec * moveSpeed;
        // 把vec赋值给刚体的速度,就可以让刚体运动起来了
        myRigidbody.velocity = vec;

以上两种方法,任选一种,初学者建议把后面这段,粘贴到 Update 函数里面,最后的位置。

11、这样,我们的主角就动起来咯~~

9

小结

本节课程基础到没朋友,前半部分我真怀疑大猩猩经过训练也能做出来 o(*≧▽≦)ツ┏━┓

主要是让我自己也熟悉一下,下节课就开车咯,大家要亲手做一下才能上车。

  • 官网
  • 游戏开发技术交流群:610475807
  • 微信公众号:皮皮关

近期点赞的会员

 分享这篇文章

您可能还会对这些文章感兴趣

参与此文章的讨论

  1. Simidal 2017-10-12

    。-。、观摩一下

  2. WeiweiFeng 2017-10-12

    我觉得也许可以把本节的最终效果在 一开始就展示出来哦。这样会更有动力看下去的,嘿嘿

您需要登录或者注册后才能发表评论

登录/注册