欢迎光临散文网 会员登陆 & 注册

Unity暑期萌新入门:静态敌人篇

2019-08-02 10:50 作者:皮皮关做游戏  | 我要投稿

作者:Truly


大渣好。

经过前几篇的学习,我们已经可以控制John在关卡里自由地行走,并且到达出口时能显示通关的结束界面了。接下来我们会在关卡里添加敌人,当玩家被敌人发现时,就会重新开始关卡。今天,我们先添加虽然不会动,但是喜欢在角落里蹲人的石像鬼。

一、设置石像鬼(Gargoyle)的预制体(推荐参照GIF快速操作)

制作敌人的方法跟我们制作主角的方法有点相似,就当作重温制作角色的步骤吧。我们要先把模型实例化为对象(GameObject)再进行操作。

流程:

1.创建石像鬼(Gargoyle)预制体

(1)在Project窗口中,打开文件夹Assets > Models > Characters,并找到Gargoyle 模型。

(2)把Gargoyle 模型拖动到Hierarchy窗口中,创建它的实例。

(3)把Hierarchy窗口中的Gargoyle GameObject拖到Assets> Prefabs文件夹中,创建预制体。此时会弹出对话框,选择Original Prefab


2.编辑预制体

我们将会为Gargoyle预制体添加动画碰撞体(Collider)视野检测模拟(Trigger)

在Hierarchy窗口中,点击Gargoyle GameObject右侧的箭头,打开预制体模式进行编辑。

(1)添加动画

石像鬼只有一个站立观看的Idle动画,把设置好动画的Animator Controller拖进预制体的Animator组件中的Controller属性栏中即可。

①在Project窗口中打开Assets > Animation > Animators文件夹。创建Animator Controller,命名为Gargoyle

双击新建的Gargoyle Animator Controller打开Animator视窗。

③在Project窗口中打开Assets > Animation > Animation文件夹。展开Gargoyle @ Idle,Idle动画拖进Animator视窗中。

④把设置好的Gargoyle(Animator Controller)拖进Gargoyle预制体的Animator组件的Controller属性框。


(2)添加Collider(碰撞体)

①在Inspector窗口中,点击Add Component,搜索并添加Capsule Collider

②设置Collider参数,使碰撞体更加贴合石像鬼:

  • Center:(0, 0.9, 0)

  • Radius:0.3

  • Height:1.8


(3)添加模拟检测范围的Trigger(触发器)

在通关界面篇,我们设置Trigger来检测玩家是否到达出口,这里我们用相同的方法给石像鬼设置Trigger模拟检测范围。

①在Gargoyle GameObject下新建一个空的子对象(Create Empty),按F2重命名为PointOfView

②设置PointOfView的Transform组件,调节模拟视线的位置和旋转:

  • Position(位置) : 01.40.4)

  • Rotation(旋转) : 2000)

设置完成后,确保旋转切换工具是Local的情况下,可以看到PointOfViewde坐标的Z轴会从石像鬼的头部指向斜下。

③在PointOfView的Inspector中点击Add Component,搜索并添加Capsule Collider

④设置Collider参数:

  • 在Capsule Collider组件里勾选Is Trigger,转换为触发器。

  • Center(中心) :( 000.95)

  • Radius(半径) :0.7

  • Height(高) :2

  • Direction(方向):下拉菜单改为Z-Axis


(4)场景中摆放Gargoyle GameObject

①点击Hierarchy窗口中,Gargoyle左侧的 < 退出预制体模式。

②我们返回Scene场景中,设置Gargoyle GameObject的Transform组件:

  • Position:(-15.2 0.8 

  • Rotation:(135 

完成后Ctrl+S保存场景。我们为石像鬼的预制体添加并设置了动画、碰撞体以及模拟检测范围的触发器,已经完成了大部分的基本设置。


二、设置被抓界面

当判定玩家被抓时,会淡入一个被抓的界面,做法跟上一篇通关界面相似。我们偷懒一下,复制一份胜利界面,把通关前景图替换被抓的前景图就可以了~

1.展开FaderCanvas,选中ExitImageBackground GameObject,Ctrl + D进行复制。

2.把复制出来的ExitImageBackground(1) 重命名为CaughtImageBackground。展开子项,把ExitImage 重命名为CaughtImage


3.在Inspector窗口中找到CaughtImage 的Image组件,点击Source Image右侧圆圈,选择Caught。完成后Ctrl+S保存场景。


三、编写脚本

1.思路

(1)判断玩家是否进入检测范围。

(2)如果玩家在检测范围内,进行视线模拟,避免隔着障碍物(墙)还能看到玩家。

(3)玩家被发现后,淡入被抓的结束界面。

(4)结束界面淡入完成后,重新加载游戏。


2.新建脚本并添加为组件

(1)打开Asset > Scripts文件夹,新建脚本并命名为Observer

(2)打开Gargoyle预制体模式,把Observer脚本拖到Hierarchy里的PointOfView GameObject中,使其添加为组件。


3.检测玩家

(1)双击打开Observer脚本

(2)范围检测

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

 

public class Observer : MonoBehaviour

{

    public Transform player;            //玩家

    public GameEnding gameEnding;       //GameEnding脚本

 

    bool m_IsPlayerInRange;             //玩家是否在探测范围

 

    //玩家进入检测范围

    private void OnTriggerEnter(Collider other)

    {

        if (other.transform == player)

        {

            m_IsPlayerInRange = true;

        }

    }

 

    //玩家离开检测范围

    private void OnTriggerExit(Collider other)

    {

        if (other.transform == player)

        {

            m_IsPlayerInRange = false;

        }  

    }

 

(3)视线模拟

当玩家进入检测范围时,为了避免在墙后也能发现玩家的情况,因此在Update()里我们用射线(Ray)进行视线模拟。射线从敌人的位置射向玩家,当射线直接打到玩家时,淡入结束界面。由于玩家的position在脚底,所以我们加上Vector3.up(0,1,0),让射线能够往玩家身上打。


//每帧执行

    private void Update()

    {       

        //如果玩家进入灯光范围,用射线模拟视线检测

        if (m_IsPlayerInRange)

        {

            //射线方向为从敌人指向玩家

            Vector3 direction = player.position + Vector3.up - transform.position ;

            Ray ray = new Ray(transform.position, direction);   //(射线的起点,方向)

            RaycastHit raycastHit;                              //储存射线的碰撞信息                

 

            if (Physics.Raycast(ray, out raycastHit))

            {

                if (raycastHit.collider.transform == player)    //如果射线打中的是玩家      

                {

                    gameEnding.CaughtPlayer();                  //玩家被抓

                }

            }

        }

    }

}

 

gameEnding.CaughtPlayer(); 这里暂时会报红,完成下边的步骤后便会解决。

到这一步我们完成了视线检测,Ctrl + S保存脚本。


4.修改GameEnding脚本(建议直接看修改后脚本)

当玩家在检测范围内,并且能被射线能直接打中时,淡入被抓的结束界面,这时候我们需要回到GameEnding脚本进行编辑。

(1)增加变量

  • public CanvasGroup CaughtImageBackgroud:被抓的界面

  • bool m_IsPlayerCaught :玩家是否被抓

(2)为EndLevel()添加参数

当玩家通关或者被抓时,控制对应的界面淡入,淡入完成后判断是否重新载入游戏,因此我们需用CanvasGroup(胜利/失败界面)和bool(是否重载游戏)变量作为参数。

  • void EndLevel(CanvasGroup imageCanvasGroup,bool doRestart)

(3)重载场景

重载场景需要在脚本上方添加命名空间:using UnityEngine.SceneManagement;

场景的索引从0开始数且我们只有一个场景,因此我们重启的场景用:SceneManager.LoadScene(0);

(4)Update()

每帧检测时,我们需要判断玩家是到达终点还是被抓,然后给EndLevel()输入对应的参数。

修改后的脚本:


using System.Collections;

using System.Collections.Generic;

using UnityEngine;

using UnityEngine.SceneManagement;

 

public class GameEnding : MonoBehaviour

{

    public float fadeDuration = 1f;                     //淡入淡出时间1S

    public float displayImageDuration = 1f;             //淡入完毕后等待时间1S

    public GameObject player;                           //玩家

    public CanvasGroup exitBackgroundImageCanvasGroup;  //用于调节通关界面alpha值的CanvasGroup   

    public CanvasGroup caughtBackgroundImageCanvasGroup;//用于调节被抓界面alpha值的CanvasGroup

 

    bool m_IsPlayerAtExit;                              //玩家是否到终点

    bool m_IsPlayerCaught;                              //玩家是否被抓

    float m_Timer;                                      //计时器 

 

    /// <summary>

    /// 达到出口,进入触发器时调用

    /// </summary>

    /// <param name="other"></param>

    private void OnTriggerEnter(Collider other)

    {

        //如果进入触发器范围的是玩家,改变布尔变量

        if (other.gameObject == player)                

        {

            m_IsPlayerAtExit = true;

        }

    }

 

    //玩家被抓时在Observer脚本里调用

    public void CaughtPlayer()

    {

        m_IsPlayerCaught = true;

    }

 

    /// <summary>

    /// 每帧调用

    /// </summary>

    private void Update()

    {      

        if (m_IsPlayerAtExit)       //如果玩家到达出口,执行EndLevel();                      

        {

            EndLevel(exitBackgroundImageCanvasGroup,false);

        }

        else if (m_IsPlayerCaught)  //如果玩家被抓,执行重新加载场景;

        {

            EndLevel(caughtBackgroundImageCanvasGroup,true);

        }

    }

 

    /// <summary>

    /// 关卡结束

    /// </summary>

    void EndLevel(CanvasGroup imageCanvasGroup,bool doRestart)

    {

        m_Timer = m_Timer + Time.deltaTime; //计时器,每帧累加时间

       

        //alpha值随着百分比改变

        imageCanvasGroup.alpha = m_Timer / fadeDuration;

 

        //当计时器时间大于总等待时间

        if (m_Timer > fadeDuration + displayImageDuration)

        {

            if (doRestart)

            {

                SceneManager.LoadScene(0);//场景载入,只有一个场景,下标为0

            }

            else

            {

                Application.Quit();//退出游戏,这个要游戏打包后才会执行,在Unity里不会关闭游戏。

            }

        }

    }

}

 

修改完成后保存脚本。


四、在Unity中为脚本引用赋值

(1)选中PointOfView GameObject,为Observer脚本的引用赋值。

(2)选中GameEnding GameObject,为GameEnding脚本的引用赋值。

进行到这里,石像鬼设置基本完成,Ctrl + S保存场景。


结语:这一篇中我们添加并设置了石像鬼的预制体(动画、碰撞盒和模拟检测范围的触发器),在脚本中用射线模拟视线检测。运行游戏,试试石像鬼的检测是否奏效吧~下一篇,我们将添加动态的敌人---幽灵,结合在环境篇中设置的NavMesh,使幽灵可以在设定的路线上巡逻。


迫不及待想自行开始制作的小伙伴,可以浏览John Lemon's Haunted Jaunt官方教程:https://learn.unity.com/project/john-lemon-s-haunted-jaunt-3d-beginner

咱们的游戏开发交流群也欢迎强势插入:869551769

希望参与线下游戏开发学习的,欢~~~~~~迎访问:levelpp.com/

Unity暑期萌新入门:静态敌人篇的评论 (共 条)

分享到微博请遵守国家法律