在本教程中,您将:
- 创建一个静态的石像鬼 (Gargoyle) 敌人
- 编写自定义脚本,使石像鬼可以找到 JohnLemon
- 将游戏设置为在抓到 JohnLemon 时重新开始
✅ 创建石像鬼 (Gargoyle) 预制体
现在您的游戏正在成形:JohnLemon 可以在环境中移动,摄像机跟随着他,当他离开鬼屋时,游戏结束。不过,这里还缺少用于增加游戏难度的敌人!
✅ 给Gargoyle添加动画
石像鬼只一段可以播放的动画,因此其 Animation Controller 将非常简单。
新的 Animation Controller 命名为“Gargoyle”:
双击 Gargoyle 以打开 Animator 窗口。
展开 Gargoyle@Idle 模型资源,拖入 Idle 动画
石像鬼 (Gargoyle) 的 Animation Controller 已完成,拖到 Animator 组件的Controller上:
保存场景,运行看看效果。
✅ 石像鬼添加碰撞体
添加Capsule Collider。
- 将 Capsule Collider 的 Center 属性设置为 (0, 0.9, 0)
- 将 Radius 属性更改为 0.3
- 将 Height 属性更改为 1.8
✅ 创建触发器以模拟石像鬼的视线
编写一个自定义脚本,以确保石像鬼无法透过墙壁看到玩家
,但是首先需要创建一个触发器。
为了方便定位,Create Empty 游戏对象,命名为“PointOfView
” 。作为 Gargoyle 的子项,并将触发器置于其上
。
此游戏对象将充当石像鬼 (Gargoyle) 对于世界的视角。
石像鬼的动画显示其视线是朝正前方并略微向下
,调整 PointOfView 的 Transform :
- 将 Position 改为 (0, 1.4, 0.4)
- 将 Rotation 改为 (20, 0, 0)
注意:如果 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
➡️ 编写一个脚本来处理 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) 并知道它是否击中碰撞体
- 识别该碰撞体是否为玩家角色
您可以稍后添加更多的石像鬼 (Gargoyle) 预制件实例,但让我们将当前已有的这个实例放在起始房间中的一角
。
- 将其 Position 属性更改为 (-15.2, 0, 0.8)
- 将其 Rotation 属性更改为 (0, 135, 0)
接着,设置 Observer 脚本所需的两个引用:
接下来,您需要创建 UI 来处理由于已经抓到玩家而结束关卡的情况。在 Hierarchy 窗口展开 FaderCanvas 游戏对象。
选中 ExitImageBackground 游戏对象,Ctrl + D (Windows) 或 CMD + D (macOS)复制一份,新的副本重命名为 CaughtImageBackground,展开其子项,将 ExitImage 游戏对象重命名为 CaughtImage,将Source修改为 Caught精灵。
选择 GameEnding 游戏对象,将 CaughtImageBackground 游戏对象拖到 Game Ending 的 Caught Background Image Canvas Group
字段上:
保存场景。