Unity游戏开发学习记录

Unity练习2 - 《John Lemon's Haunted

2023-12-25  本文已影响0人  我阿郑

在本教程中,您将:

✅ 创建石像鬼 (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。

image.png

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

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

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

image.png

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

image.png

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

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

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

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 脚本已经可以:

image.png

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

接着,设置 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

保存场景。

上一篇下一篇

猜你喜欢

热点阅读