Unity练习2 - 《John Lemon's Haunted Jaunt》-5- 敌人静态 Observer

在本教程中,您将:

  • 创建一个静态的石像鬼 (Gargoyle) 敌人
  • 编写自定义脚本,使石像鬼可以找到 JohnLemon
  • 将游戏设置为在抓到 JohnLemon 时重新开始

✅ 创建石像鬼 (Gargoyle) 预制体

现在您的游戏正在成形:JohnLemon 可以在环境中移动,摄像机跟随着他,当他离开鬼屋时,游戏结束。不过,这里还缺少用于增加游戏难度的敌人!

image.png

✅ 给Gargoyle添加动画

石像鬼只一段可以播放的动画,因此其 Animation Controller 将非常简单。

新的 Animation Controller 命名为“Gargoyle”:

image.png

双击 Gargoyle 以打开 Animator 窗口。

展开 Gargoyle@Idle 模型资源,拖入 Idle 动画

image.png

石像鬼 (Gargoyle) 的 Animation Controller 已完成,拖到 Animator 组件的Controller上:


image.png

保存场景,运行看看效果。

✅ 石像鬼添加碰撞体

添加Capsule Collider。

  • 将 Capsule Collider 的 Center 属性设置为 (0, 0.9, 0)
  • 将 Radius 属性更改为 0.3
  • 将 Height 属性更改为 1.8
image.png

✅ 创建触发器以模拟石像鬼的视线

编写一个自定义脚本,以确保石像鬼无法透过墙壁看到玩家,但是首先需要创建一个触发器。

为了方便定位,Create Empty 游戏对象,命名为“PointOfView” 。作为 Gargoyle 的子项,并将触发器置于其上
此游戏对象将充当石像鬼 (Gargoyle) 对于世界的视角。

image.png

石像鬼的动画显示其视线是朝正前方并略微向下,调整 PointOfView 的 Transform :

  • 将 Position 改为 (0, 1.4, 0.4)
  • 将 Rotation 改为 (20, 0, 0)
image.png

注意:如果 Scene 窗口中的 Transform 控制柄不是稍微向下,则可能是因为它们设置为 Global 而不是 Local。

现在,已经设置了石像鬼的视角对象 PointOfView,接下来给PointOfView 添加和配置触发器。

添加一个 Capsule Collider ,启用 Is Trigger。

  • 将 Capsule Collider 的 Center 属性设置为 (0, 0, 0.95)
  • 将 Radius 属性更改为 0.7
  • 将 Height 属性更改为 2
  • 将 Direction 属性从 Y-Axis 更改为 Z-Axis
image.png

➡️ 编写一个脚本来处理 JohnLemon 进入触发器时发生的情况。

创建脚本Observer挂载到PointOfView 游戏对象上。

该脚本将检查玩家角色的 Transform,而不是其游戏对象。这样可以更轻松地了解 JohnLemon 的位置并确定是否可以清楚看到他。

public class Observer : MonoBehaviour
{
    public GameEnding gameEnding;
    public Transform player; // 玩家
    bool m_IsPlayerInRange;

    void OnTriggerEnter(Collider other)
    {
        if (other.transform == player)
        {
            m_IsPlayerInRange = true;
        }
    }

    // 进入此触发器的玩家角色可能并不自动表示游戏结束,
    // 例如,路径中可能有墙壁阻挡,保护了玩家。这意味着检测 JohnLemon 何时离开触发器也很重要。
    void OnTriggerExit(Collider other)
    {
        if (other.transform == player)
        {
            m_IsPlayerInRange = false;
        }
    }
}

✅ 检查敌人的视线是否清晰

在此游戏中,检查敌人对于 JohnLemon 的视线是否清晰很重要。否则,当实际上在路径中遇到的是墙壁时,也可能会触发游戏结束。由于玩家角色的位置随时可能发生变化,因此该检查需要在每一帧中进行。

仅当玩家角色实际在范围内时检查视线才有意义。但是如何实际检查视线呢?射线检测

射线需要一个原点和一个方向。原点就是 PointOfView 游戏对象的位置,但是确定方向要稍微复杂一些。

void Update ()
    {
        if (m_IsPlayerInRange)
        {
            Vector3 direction = player.position - transform.position + Vector3.up;
            Ray ray = new Ray(transform.position, direction);
            RaycastHit raycastHit;

            if(Physics.Raycast(ray, out raycastHit))
            {
                if (raycastHit.collider.transform == player)
                {
                    
                }
            }
        }
    }

✅ 修改 GameEnding 脚本

此游戏需要两种不同的方式来结束关卡:一种是 JohnLemon 逃脱,另一种是他被抓住

被敌人抓住的玩家能够重新开始该关卡,而不是退出游戏。

创建两种结束关卡的方式:

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

public class GameEnding : MonoBehaviour
{

    public float fadeDuration = 1f;  // 淡入淡出的默认值为1秒
    public float displayImageDuration = 1f;

    public GameObject player; // 玩家
    public CanvasGroup exitBackgroundImageCanvasGroup;
    // 此代码将为新图像创建另一个 CanvasGroup,如果抓住 JohnLemon,将显示这些图像。
    public CanvasGroup caughtBackgroundImageCanvasGroup;

    bool m_IsPlayerAtExit;
    bool m_IsPlayerCaught; // 玩家是否被抓
    float m_Timer; // 需要一个计时器来确保游戏在淡入淡出之前不会结束


    void OnTriggerEnter(Collider other)
    {// 监听触发器callback
        if (other.gameObject == player)
        {
            m_IsPlayerAtExit = true; // 发生触发
        }
    }

    public void CaughtPlayer()
    {
        m_IsPlayerCaught = true;
    }

    void Update()
    {
        if (m_IsPlayerAtExit)
        {
            EndLevel(exitBackgroundImageCanvasGroup,false);
        }
        else if (m_IsPlayerCaught)
        {
            EndLevel(caughtBackgroundImageCanvasGroup,true);
        }
    }

    void EndLevel(CanvasGroup imageCanvasGroup, bool doRestart)
    {
        m_Timer += Time.deltaTime;

        imageCanvasGroup.alpha = m_Timer / fadeDuration;

        if (m_Timer > fadeDuration + displayImageDuration)
        {
            if (doRestart)
            {
                // 只有一个场景,因此索引为 0。
                SceneManager.LoadScene(0);
            }
            else
            {
                Application.Quit();
            }
        }
    }

    //void EndLevel()
    //{
    //    //获取从上一帧以来经过的时间的方式:使用 Time.deltaTime
    //    m_Timer += Time.deltaTime;
    //    // 设置 Canvas Group 的 Alpha
    //    exitBackgroundImageCanvasGroup.alpha = m_Timer / fadeDuration;
    //    if (m_Timer > fadeDuration + displayImageDuration)
    //    {
    //        // 当计时器值大于持续时间时,淡入淡出将结束
    //        // 该方法确实可以退出游戏,但仅适用于完全构建的应用程序
    //        // 目前在Unity Editor中没有效果
    //        Application.Quit();

    //    }

    //}
}

✅ 完善您的石像鬼 (Gargoyle) 预制件

您的 Observer 脚本已经可以:

  • 识别玩家角色进入触发器
  • 使用射线投射 (Raycast) 并知道它是否击中碰撞体
  • 识别该碰撞体是否为玩家角色
image.png

您可以稍后添加更多的石像鬼 (Gargoyle) 预制件实例,但让我们将当前已有的这个实例放在起始房间中的一角

  • 将其 Position 属性更改为 (-15.2, 0, 0.8)
  • 将其 Rotation 属性更改为 (0, 135, 0)

接着,设置 Observer 脚本所需的两个引用:


image.png

接下来,您需要创建 UI 来处理由于已经抓到玩家而结束关卡的情况。在 Hierarchy 窗口展开 FaderCanvas 游戏对象。

image.png

选中 ExitImageBackground 游戏对象,Ctrl + D (Windows) 或 CMD + D (macOS)复制一份,新的副本重命名为 CaughtImageBackground,展开其子项,将 ExitImage 游戏对象重命名为 CaughtImage,将Source修改为 Caught精灵。

image.png

选择 GameEnding 游戏对象,将 CaughtImageBackground 游戏对象拖到 Game Ending 的 Caught Background Image Canvas Group 字段上:

image.png

保存场景。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,427评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,551评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,747评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,939评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,955评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,737评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,448评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,352评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,834评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,992评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,133评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,815评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,477评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,022评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,147评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,398评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,077评论 2 355

推荐阅读更多精彩内容