我们使用了以下系统来支持 both the 基础游戏系统 以及任何 主设计要求 的目标。
使用管理器
使用管理器 提供了简单的 API 来将抓取到的对象应用到某物上,例如将一件分层服装应用到模特上。此 API 的主功能是 UseManager.AddUse (标签、目标对象、距离、成功、未装备任何东西、错误装备、额外数据),将一组标签绑定到 目标对象 。当玩家拥有一个标签并单击目标对象时,成功回调函数会被调用。其他调用函数允许我们在玩家单击无抓取项目或使用错误类型的物品目时显示额外的视觉信息。
我们可以通过 UseManager.RemoveUse 移除"使用",这通常在任务完成或特定物品"使用"时有用。此外,我们可以通过 AddUseTargets 和 RemoveUseTargets 添加或移除目标。
亮点
当玩家靠近感兴趣的物品,例如海豹,我们希望那件物品与周围环境脱颖而出。为了实现这一点,我们创建了一个名为 LocalScript 的 高亮项目 ,使用一个球体围绕玩家检测其他网格的触碰,连接到 Touched 和 TouchEnded 事件。getHighlight 函数检查触摸网格或其父辈上的几个标签,使用 GetTaggedObjectUpHierarchy 帮助函数。如果不需要任何高亮显示,我们可以使用 NoHighlight 标签强行删除它。然而,如果它需要但不太适合各种其他标签,我们可以使用 重要 标签强制它。
这 使用了一个新的引擎功能 绘制对象轮廓并/或填充对象内部的定义颜色;了解有关如何使用此精选的更多信息,请参阅 《亮点对象》 。Highlights 和鼠标滚轮 OnItemIndicator 系统共同工作,因此 Highlights 不仅决定是否需要为网格添加阴影,还提供了网格类型给 OnItemIndicator .
HighlightItemsFunc 用于与其他客户系统交流。例如,事件管理器使用 启用 命令以启用或禁用某些场景中的Highlight,而 OnItemIndicator 使用GetType来查询对象的类型。要检测到物品不再存在,例如当损坏的房间被摧毁时,我们连接到 CollectionService.GetInstanceRemovedSignal 。
传说和思想泡泡
传说 和 思想泡泡 是 2 个相似的系统。传说使用 ScreenGui 作为屏幕上的 UI 容器,包含一个子 Frame 来控制其子 TextLabels 和 ImageLabels 的大小和缩放,传说等待玩家单击屏幕任何位置来移除它。同样,思维泡泡使用 BillboardGui 与子 TextLabel 对非知识对象进行通信,并在对象附近的 3D 空间显示对话,对话期间长达指定时间和冷却期,文本不占整个屏幕的空间。了解这些系统背后的设计更多信息,请参阅传说和思想泡泡。
传说实现在 传说管理器 LocalScript 。当单击或触摸时,它使用助助函数 utils.RaycastAlongPointingDir 发射射线投射,并使用 NoPlayerCollision 群组。如果单击或父辈之一的网格拥有 传说 或 思想泡泡 标签,我们将显示用户界面。文本、说明和图像由对象上的 LoreText、LoreCaption 和 LoreImage 属性定义。
请注意,我们使用 Camera.ViewportPointToRay 或 Camera.ScreenPointToRay 来构建射线,取决于是否从非触摸或触摸中调用。坐标系在某些不同的坐标系中。对于鼠标,我们从 Class.UserInputService.InputEnded``:Connect 获得 MouseButton1,对于触摸设备,我们从 Class.UserInputService.TouchTapInWorld``:Connect 获得。
思想泡泡整体相似,使用射线投射来检查网格或其父辈是否具有 思想泡泡 标签。它还使用思想文本属性为文本,以及一个 思想泡泡 标签来指向用于在世界上放置用户界面的空间对象。使用相同位置对象的思想泡泡,但文本不同的泡泡有不同的冷却时间。
特殊情况
传说有一些特殊情况,其中之一是被损坏的海豹。当玩家单击损坏的密封时,会显示知识界面,并等待单击启动任务,这会影响游戏流程。这由使用可绑定 GameStateClient 来请求传说用户界面的 LoreManagerFunc 来处理。通过 GameStateClient 将回调提供给 Lore 系统,以便知道玩家何时关闭了记忆。另一个特殊情况是当 思想泡泡 和 传说 标签位于同一对象上时。在这种情况下,为了避免知识和思想泡泡文本重叠,我们在关闭知识后运行思想泡泡。 LoreManager 还处理了一个特殊情况,即当玩家拾起房间的密封时,单击关闭的门显示小场景。
关于物品指示器
我们想在玩家查看特定兴趣项目时,在屏幕中心显示不同的图标。客户端脚本 OnItemindicator 在相机上进行射线投射 Class.CFrame.LookVector,分析结果。根据结果,它在 OnItemIndicator2 ScreenGui 中设置一个图像。
当没有受到关注的项目被击中时,默认图标是一个小点。我们可以通过添加 关于物品指示器 字符串属性到特定网格上,使用 onItemIndicatorImages 的名称,例如手、眼或门当前锁定,来设置任何图标。特性只在极少数情况下需要,大部分时间其他现有标签或系统提供图标类型。了解更多详情,请参阅 Update 函数。
类型检查在优先级顺序中检查一些。在 OnItemIndicator 覆盖之后,我们检查是否可以抓取或作为“手”图标的抽屉通过 utils.CanGrabModel(model) 或 utils.GetTaggedObjectUpHierarchy("Drawer2", model) 。之后,我们调用 HighlightManager 来确定高亮状态、项目类型和要使用的图标。例如:
highlightItemsFunc:Invoke({"GetType", curInst})
故事和思想泡泡标签稍后通过检查标签进行检查。对于门,我们有 2 个不同的图标: 门目前关闭 和 门始终锁定 。 设置一个真或假的 属性为可打开或关闭的门,我们使用属性的存在和值。看起来像门但不打开的对象具有 门锁 标签。
门管理器
门管理器 LocalScript 门 标签和CollectionService关闭和打开门。门有前面和后面的触发器,我们用触摸和 touchEnded 事件连接。我们创建了青少年来打开和关闭门从前面和后面。我们维护一个 玩家靠近 地图 (玩家触发触发器,单独为前面和后面。
每扇门都有简单的状态系统,DoorState(关闭、打开、打开、关闭),使用青少年进行转换。我们可以通过调用 DoorManager.EnableDoor 启用或禁用门的打开或关闭功能,该方法设置了一个 DoorEnabled 属性。
大师动画师
主动画师 播放动画图像(纹理图集),我们曾使用它来动画电视屏幕。要浏览图像,我们需要知道一组参数:行和列数量、总框架数量、时期、图像尺寸和一组图像ID。系统允许我们在多个图像上进行动画,每个可能分为行和列的子图像。我们可以通过特性或值来提供这些数据,但在此体验中,我们使用了助助脚本。UpdateImageAnimations(dT) 计算出我们需要使用时间和参数显示的图像或子图像。如果我们需要切换到新图像,我们设置图像。如果我们需要更改任何子图像,我们设置 ImageRectOffset。
一个具有动画的 SurfaceGui 对象将有一个动画师 ModuleScript ,主要目的是提供一个 Animator.GetParams 函数,返回所有参数。这有助于使用 图像动画 标签和 收集这些对象,并找到在其下方的动画师 。然后使用 pcall 要求动画师 ModuleScript 并调用 GetParams 在它上。
本地空间动画
本地空间动画使用 LocalSpaceRotation 标签来旋转大多数"化妆"对象,其旋转速度和延迟位于 X、Y 或 Z 轴附近。我们使用它来实现玩家不会与之交互的远程对象,或者对小型对象的模拟影响不大。参数通过 Speed , Delay 和 Axis 值定义。有关实施细节,请参阅旋转云网格。
头灯管理器
头灯管理器 在用户选择屏幕上的LocalScript来切换头顶聚光灯打开或关闭时,处理使用ImageButton向服务器发射评论,并切换关闭和打开的音效。当角色添加或其 Head 被更改时,giveCharacterHeadlamp 函数克隆 面前附件灯 灯,并使用一些偏移和旋转从 面前附件附件 来定位灯。
座位管理器
我们不希望玩家在靠近可以坐下的对象时自动坐下。相反,我们想要要求用户在座位附近单击以坐下。 座位管理器 脚本添加了ClickDetectors,在单击时调用 座位 ,当单击时调用seat:Sit(humanoid)。当将玩家传送到房间的正常和损坏状态之间时,我们不能让玩家坐下,因为 CFrame 坐标变更将无法工作,因此 SeatManager 有一个功能可以在传送之前和之后几秒钟禁用或启用座位。
抽屉管理器
抽屉管理器脚本使用 Drawer2 标签和 来处理单击抽屉以打开或关闭它们,并播放相应的音频。打开和关闭操作由设置目标位置为 PrismaticConstraint 来完成。
杀死卷积量
在主游戏区域的几个区域中,例如电火花和房子开始路径附近的水,玩家可以在进入音量标签 Humanoid.Health 时将其设置为 0 当进入音量标签 KillVolume 时。 杀死音量 脚本使用Touched:Connect来确定玩家何时进入音量,然后将其生命值减少到0。
玩家任务重生
PlayerMissionRespawn 脚本使用 RespawnVolume 标签和 CollectionService 来处理那些导致玩家重生的音量。我们将这些卷放在损坏的房间下,因为许多任务有空隙或移动平台,玩家可能会掉下去。当触摸时,脚本播放一个小的 传送_跳跃 切场景,并使用GameStateFunc命令调用GameEvents.PlayerRespawn.
当处理 GameEvents.PlayerRespawn 时,脚本可以使用 RespawnPositions , 如果任务配置提供它。如果没有,它使用 TeleportPositions 为特定任务。我们没有“检查点”系统,因此 CalcClosestTeleportPos 只选择最接近的 重生 或 传送 位置,从玩家击中 RespawnVolume 的地方,使用唯一的横向、“2D”距离。
小型助手系统
钢琴经理
钢琴管理器 脚本使用 钢琴 标签和 CollectionService 添加 ClickDetectors 并在键盘单击时播放其中一个钢琴声音。
祭祀支持
玩家放置海豹的大厅有一个复杂的装置,每当海豹放置在其定义位置时,都会发生变化。例如,根据放置的海豹数量,特定事件会播放启用/禁用灯光和光束、更改某些对象的透明度等等。 祭祀支持 是对那些事件的小包装,提供参数给事件,例如在哪个特定密封上播放它,取决于放置了哪个特定密封。
可恢复管理器
一些可抓取的对象对游戏玩法很重要,例如海豹,如果玩家将它们丢在某处,我们不希望它们丢失。如果对象具有 可恢复 标签,那么在添加到恢复系统时, 可恢复管理器 脚本记住其变形。当玩家丢下这样的对象时,抓取系统调用 restorableManager.StartTracking .如果对象在五秒内没有再次被拾起,那么 可恢复管理器 脚本将它放置在原始变形中并重置跟踪时间。
门户
在几个任务中,我们在任务内传送玩家短距离,例如重生玩家,他们从旋转平台上掉下来。为了简化设置这种类型的传送,我们在脚本中称为“门户”,使用了 ProcessPortal 中的助助函数 **** 。例如,如果 P1 是定义初始触发器的部分,而 P2 是定义目标玩家变形的部分,下面的代码片段可以定义这样的门户功能:
P1.Touched:Connect(function(otherPart) utils.ProcessPortal(otherPart, P2) end)
过程门户 处理检查其他部分是否是人类,通过一个 CFrame 坐标变更传送玩家,并调用一个小场景来隐藏过渡,使用 Teleport_Jump 事件在 事件管理器 中执行。
配置脚本
我们有几个配置、数据定义和共同功能脚本: 示范配置 . 任务定义。枚举游戏状态、客户端服务器通信事件。 示范全球设置 .我们在一个场景开发,但在其他地方发布(和游戏测试)。脚本检查 placeID 并启用/禁用各种捷径和调试功能。 演示工具 。各种实用功能。处理变形。设置可见度、锚定或其他属性。检查一个方块子中的点。按“点击”名称在层级中找到对象。管理临时存储(可用于暂时将模型“移到很远的地方”并稍后带回)。点击检测器助手。正在获取支协助。支持检查标签(特别是沿着层次)。将触发器连接到 事件管理器。 音频实用程序 。一些函数来播放设置合中的重要随机声音。 GrabUtil 。抓取帮助函数。