层次
遗产聊天系统使用的是 客户端-服务器模型 。服务器端聊天模块组件 ChatChannel 和 ChatSpeaker 管理由服务器上的 ChatService 处理,而客户端负责输入和显示消息。服务器和客户端之间的通信由 RemoteEvents 自动处理。
引擎服务本身是聊天系统的必要存储单元:当 Roblox 地点加载(在客户端或 Studio 运行或游戏时)时,以下组件将自动加载到 服务中,如果 为真,如果是真的。
- 聊天模块 — 这个 是由聊天服务运行器需要的模块集。该文件夹中的所有内容都由脚本需要,用于在服务器上创建自定义行为。
- 客户端聊天模块 — 这个文件夹包含各种由 ModuleScripts 需要的 **** 。
- 命令模块 — 包含用于实现客户端聊天命令的模块。
- 消息创建器模块 — 包含用于处理和格式化消息的模块。
- 聊天常量 — 包含服务器和客户端共享的常量。
- 聊天设置 — 存储各种设置以配置聊天窗口的不同方面。
- 聊天本地化 — 存储文本翻译的数据结构。
- ChatServiceRunner — 这个 Script 运行聊天服务器组件。一般来说,这不需要修改以创建自定义聊天行为和功能。
- 泡泡聊天 — 在他们的游戏虚拟形象上显示用户聊天消息(如果启用)。
- 聊天脚本 — 这个 LocalScript 运行了聊天客户端组件。像 ChatServiceRunner 一样,这不需要修改以自定义聊天。当游戏运行时,这会自动克隆到 StarterPlayerScripts 。
修改聊天系统
要修改或自定义遗产聊天系统,您必须先复制上面的架构。
使用 播放 按钮运行体验(F5)。
选择并复制(Ctrl C 或 ⌘ C)添加到 Chat 的对象。
使用 停止 按钮停止体验 (ShiftF5).
确保 Chat.LoadDefaultChat 启用。
聊天工作流
在制作模块来自定义聊天之前,了解聊天消息通过的工作流程很重要。除了发送短信外,聊天系统内置了各种命令,因此每条消息都必须检查以确定它们是否需要被解释为命令或仅仅是文本消信息。即使是文本消息在过程中也可以修改和过滤。
用户在聊天输入中获得焦点并输入字符后,客户端立即进行了几个检查。如果角色是 Esc,输入框关闭,没有采取任何行动。如果角色不是 Enter ,文本将通过 进行中的命令处理器 传送。这些用于评估文本以确定是否需要采取任何行动。例如,当用户开始使用 /whisper 命令进行悄悄话时,命令后立即输入用户名称,输入框会立即更改以表明用户正在进入悄悄话通频道。
在聊天客户端上,有两种类型的处理器:进行中和已完成。前者在每个字符输入完成后进行评估,而后者只在用户输入完成并击中 Enter 后进行评估。
当用户完成输入并按下 Enter 文本时,其输入会通过几个更多的命令处理器发送。如果一个 进行中 命令创建了自定义聊天状态,聊天检查状态以确定最终命令是否应该执行,以及消息是否应该继续。如果允许继续消息,则文本通过另一组称为 完成 的处理器发送。如果这些处理器中的任何一个返回真值,消息传送将停止。否则,消息将发送到服务器。
一旦消息到达服务器,它将经过另一组命令处理器。与客户端上的 完成 处理器类似,如果这些处理器中的任何一个返回真值,那么消息执行就停止。否则,消息将通过一组过滤器(包括默认 Roblox 聊天过滤器)传递。一旦所有这些都完成后,消息将发送到所有通道和适当的发言者。
服务器模块
放入 聊天模块 的模块可用于各种用途。这些模块可用于管理聊天频道和发言者、添加过滤器和命令功能、运行聊天机器人或服务器上需要处理的任何其他内容。要与聊天系统交互,每个模块都需要通过 ChatService 对象。
当 聊天服务运行器 启动时,需要每个模块在 聊天模块 内。它期望每个模块返回一个函数,然后依次调用每个模块,将其 ChatService 对象传递给每个函数。无论模块想要做什么(运行机器人、添加过滤函数等),它都需要遵循这种形式才能工作。
样本模块框架
local function Run(ChatService)
-- 代码放在这里
end
return Run
添加通道
一个 ChatModule 最简单的事情是管理 通道 。通道对象可以使用 AddChannel() 的 方法来创建。请注意,只有在调用该通道的成员(例如其属性和函数)时才需要使用通道对象。当从 ChatService 或 ChatSpeakers 的上下文中引用通道时,通频道名称用于引用它。
local function Run(ChatService)
local myChannel = ChatService:AddChannel("MyChannel")
end
return Run
基本通道配置
通道有几个属性可以用来稍微修改它们。例如,该模块创建一个通道并设置欢迎消息,并且当用户进入体验时自动加入通道。
local function Run(ChatService)
local myChannel = ChatService:AddChannel("MyChannel")
-- 设置用户加入频道时显示的消息
myChannel.WelcomeMessage = "Welcome to my channel!"
-- 使玩家在进入游戏时自动加入频道
myChannel.AutoJoin = true
end
return Run
通道事件
通道有几个事件可以订阅。当聊天频道发布 聊天消息 、当 聊天发言者 离开或加入、或当发言者静音或解除静音时,这些事件会触发。例如,该模块将创建一个名为 MyChannel 的通道。每当发言者加入或离开频道,系统消息将发送到频道中的所有发言者通知他们关于事件的信息。
local function Run(ChatService)
local myChannel = ChatService:AddChannel("MyChannel")
local function onSpeakerJoined(speakerName)
myChannel:SendSystemMessage(speakerName .. " has joined the channel.")
end
local function onSpeakerLeft(speakerName)
myChannel:SendSystemMessage(speakerName .. " has left the channel.")
end
myChannel.SpeakerJoined:Connect(onSpeakerJoined)
myChannel.SpeakerLeft:Connect(onSpeakerLeft)
end
return Run
命令功能
聊天模块 可以做的另一个强大功能是聊天 命令 。当消息发送到服务器时,聊天将通过已注册到 ChatService 和相关通频道的每个命令函数发送消息。这些函数发送到发送消息的麦克风声器、消信息和通道。函数可以执行任何需要的操作,然后返回真或假。如果函数返回真值,那么消息将停止由聊天系统处理。它不会被发送到任何更多的命令函数,也不会显示在聊天窗口中。如果函数返回 false,那么消息将通过所有其他命令函数继续。如果命令函数中没有返回真值,那么消息将通过过滤器发送,然后显示。
命令功能经常用于实现管理命令,这是一些用户可以使用的文本命令,可以通过聊天中说的特定文本来操纵体验状态。
在这个例子中,一个 聊天模块 被用来创建一个 Part 如果用户在聊天中输入 /part 。注意,如果返回了真值,那么该部分将被创建,从而阻止了消息的继续,也不会显示任何消息。如果零件未创建,该函数需要返回 false,以便消息继续通过系统工作。
local Workspace = game:GetService("Workspace")
local function Run(ChatService)
local function createPart(speakerName, message, channelName)
if string.sub(message, 1, 5) == "/part" then
local newPart = Instance.new("Part")
newPart.Parent = Workspace
return true
end
return false
end
ChatService:RegisterProcessCommandsFunction("createPart", createPart)
end
return Run
both 聊天频道 和 聊天服务 本身都可以有聊天命令。 聊天服务 命令处理器将在服务器收到的每个消息上运行,而频道命令只会在命令注册到的频道上发送的消息上运行。
过滤函数
由 命令函数 停止的消息不会通过注册到 聊天服务 和相关通道的所有过滤函数。每个过滤器函数都会传递发言麦克风、消息对象和通道名称。对消息对象进行的任何更改将持续存在,每个后续的过滤函数都会看到更新的消信息。请注意,过滤函数不需要返回值。
在这个例子中,简单的过滤函数被注册,使每个消息都出现在小写形式。
local function Run(ChatService)
local function makeLowercase(sender, messageObject, channelName)
messageObject.Message = string.lower(messageObject.Message)
end
ChatService:RegisterFilterMessageFunction("makeLowercase", makeLowercase)
end
return Run
客户端模块
放入 客户端聊天模块 的模块可用于为客户端制定自定义行为。这些模块被分为两个不同的文件夹:命令模块和消息创建器模块。
命令模块
命令模块 与服务器上注册 命令功能 的模块非常相似。这些模块定义了在用户输入文本后会发射的函数。该文本可以阅读,命令可以将消息传送到服务器或停止消信息的进度。在消息结尾评估的命令以 COMPLETED_MESSAGE_PROCESSOR 标记,而在每个字符之后评估的命令以 IN_PROGRESS_MESSAGE_PROCESSOR 标记。
在两种类型的命令中,模块必须返回一个词典,该词典说明命令应使用哪种类型的处理器,以及在处理器被调用时执行哪个函数。例如,一个 完成的消息处理器 应采取形式:
local util = require(script.Parent:WaitForChild("Util"))
function ProcessMessage(message, ChatWindow, ChatSettings)
end
return {
[util.KEY_COMMAND_PROCESSOR_TYPE] = util.COMPLETED_MESSAGE_PROCESSOR,
[util.KEY_PROCESSOR_FUNCTION] = ProcessMessage
}
请注意,枚举 KEY_COMMAND_PROCESSOR_TYPE 在 Util ModuleScript 内的 CommandModules 文件夹中定义。
已完成的消息命令
完成消息命令 在用户完成输入并击中 Enter 后进行评估。处理器的功能是通过 ChatMessage 对象、客户端的 ChatWindow 和 ChatSettings 表传递。如果函数返回真值,那么消息将停止处理并不会发送到服务器。否则它将被发送到所有其他处理器,最终到服务器,如果没有其他处理器停止它。
例如,以下处理器将在用户输入命令 /last 时移除当前通道中最旧的消息。
local util = require(script.Parent:WaitForChild("Util"))
function ProcessMessage(message, ChatWindow, ChatSettings)
if string.sub(message, 1, 5) == "/last" then
local currentChannel = ChatWindow:GetCurrentChannel()
if currentChannel then
currentChannel:RemoveLastMessageFromChannel()
end
return true
end
return false
end
return {
[util.KEY_COMMAND_PROCESSOR_TYPE] = util.COMPLETED_MESSAGE_PROCESSOR,
[util.KEY_PROCESSOR_FUNCTION] = ProcessMessage
}
进行中的命令
进行中的命令 每当用户在聊天输入中键入字符时,都会被评估。例如,以下代码在每次按键后播放一次,使其听起来像是用户在打字机上打字:
local util = require(script.Parent:WaitForChild("Util"))
local keyEffect = Instance.new("Sound")
keyEffect.SoundId = "rbxassetid://12221976"
keyEffect.Parent = script
function ProcessMessage(message, ChatWindow, ChatBar, ChatSettings)
keyEffect:Play()
end
return {
[util.KEY_COMMAND_PROCESSOR_TYPE] = util.IN_PROGRESS_MESSAGE_PROCESSOR,
[util.KEY_PROCESSOR_FUNCTION] = ProcessMessage
}
进行中的命令经常用于制作一个 自定义状态 让聊天向特定用户发送消息,而不仅仅是当前频道。例如,嘘和团队聊天系统检查用户是否输入了 /whisper 或 /team respectively,并将完成的消息发送给仅适合的用户。
期望一个自定义状态是表,具有以下功能:
- TextUpdated() — 当输入框中的文本更改时调用。
- GetMessage() — 在用户完成输入消息并击中 Enter 之后调用。该函数应返回一个字符串。
- ProcessCompletedMessage() — 在消息正在处理时调用。自定义状态处理器总是会在完成的消息处理器之前发射。像其他处理器一样,这个函数如果消息停止发送,应返回真,否则应返回错误。
- Destroy() — 在消息发送后调用。应用于清理自定义状态设置的任何东西。
为了使用自定义状态,命令模块的 ProcessMessage() 函数必须返回状态。基本自定义状态将采取以下形式:
local util = require(script.Parent:WaitForChild("Util"))
local oneLineState = {}
oneLineState.__index = oneLineState
function oneLineState:TextUpdated()
local text = self.TextBox.Text
local length = string.len(text)
if length > 20 then
local chopLength = length - 20
local addToPrefix = string.sub(text, 1, chopLength)
self.Prefix = self.Prefix .. addToPrefix
self.TextBox.Text = string.sub(text, chopLength + 1)
end
end
function oneLineState:GetMessage()
local fullString = self.Prefix .. self.TextBox.Text
return fullString
end
function oneLineState:ProcessCompletedMessage()
return false
end
function oneLineState:Destroy()
self.Destroyed = true
end
function oneLineState.new(ChatWindow, ChatBar, ChatSettings)
local obj = {}
setmetatable(obj, oneLineState)
obj.Destroyed = false
obj.ChatWindow = ChatWindow
obj.ChatBar = ChatBar
obj.ChatSettings = ChatSettings
obj.TextBox = ChatBar:GetTextBox()
obj.MessageModeLabel = ChatBar:GetMessageModeTextLabel()
obj.Prefix = ""
return obj
end
local function ProcessMessage(message, ChatWindow, ChatBar, ChatSettings)
return oneLineState.new(ChatWindow, ChatBar, ChatSettings)
end
return {
[util.KEY_COMMAND_PROCESSOR_TYPE] = util.IN_PROGRESS_MESSAGE_PROCESSOR,
[util.KEY_PROCESSOR_FUNCTION] = ProcessMessage
}
使用自定义状态的一个主要优势是,模块可以编辑聊天栏和其包含的文本,而用户在输入功能和外观方面都在输入时,然后轻松重置它(一旦发送消息,自定义状态就会自动移除,一切都会恢复正常)。例如,这段代码设置了一个仅允许一次显示 20 个字符的自定义状态,以便在文本框中显示。如果用户继续输入,字符串的开头字符暂时被移除。当用户发送消信息时,所有删除的字符都会添加回到消信息中。
local util = require(script.Parent:WaitForChild("Util"))
local oneLineState = {}
oneLineState.__index = oneLineState
function oneLineState:TextUpdated()
local text = self.TextBox.Text
local length = string.len(text)
if length > 20 then
local chopLength = length - 20
local addToPrefix = string.sub(text, 1, chopLength)
self.Prefix = self.Prefix .. addToPrefix
self.TextBox.Text = string.sub(text, chopLength + 1)
end
end
function oneLineState:GetMessage()
local fullString = self.Prefix .. self.TextBox.Text
return fullString
end
function oneLineState:ProcessCompletedMessage()
return false
end
function oneLineState:Destroy()
self.Destroyed = true
end
function oneLineState.new(ChatWindow, ChatBar, ChatSettings)
local obj = {}
setmetatable(obj, oneLineState)
obj.Destroyed = false
obj.ChatWindow = ChatWindow
obj.ChatBar = ChatBar
obj.ChatSettings = ChatSettings
obj.TextBox = ChatBar:GetTextBox()
obj.MessageModeLabel = ChatBar:GetMessageModeTextLabel()
obj.Prefix = ""
return obj
end
local function ProcessMessage(message, ChatWindow, ChatBar, ChatSettings)
return oneLineState.new(ChatWindow, ChatBar, ChatSettings)
end
return {
[util.KEY_COMMAND_PROCESSOR_TYPE] = util.IN_PROGRESS_MESSAGE_PROCESSOR,
[util.KEY_PROCESSOR_FUNCTION] = ProcessMessage
}
如前所述,一旦发送了消息,任何自定义状态都将被移除,聊天将恢复正常。如果需要在发送消信息前重置自定义状态,状态可以通过 ChatBar:ResetCustomState() 进行重置。请注意,这将使聊天栏的文本框的焦点也移除。
消息创建模块
客户端组件中可以使用的另一种模块类型是 消息创建器 模块。这种类型的模块用于在聊天窗口中创建显示消信息的 GUI 元素。每种类型的消息创建者定义了一个新的消息输入,因此不同类型的消息可以用不同的格式创建。此外,图形用户界面元素可以这样添加到消息显示,从而允许添加图像、按钮等等。
这些模块需要在多个不同位置进行配置。对于每个消息输入,必须在 ModuleScript 内部有一个 **** 。此外, 聊天常规 ModuleScript最后,模块仅在聊天服务器组件创建了带有给定消息输入的新消息时才使用。
以下示例将通过制作一个每 5 秒钟说出时间的机器人,发送的消息获得红色背景。
首开始, 聊天常量 ModuleScript 需要添加一个新类型的消信息的字段。
-- 聊天常量local module = {}--[[ 消息类型 ]]module.MessageTypeDefault = "Message"module.MessageTypeSystem = "System"module.MessageTypeMeCommand = "MeCommand"module.MessageTypeWelcome = "Welcome"module.MessageTypeSetCore = "SetCore"module.MessageTypeWhisper = "Whisper"module.MessageTypeTime = "Time"module.MajorVersion = 0module.MinorVersion = 2return module
机器人本身在服务器上创建了一个新的 聊天模块 。请注意,过滤函数用于将新的消息类型添加到机器人发送的消息中。
-- 新模块脚本将放置在聊天模块中
local Chat = game:GetService("Chat")
local ReplicatedModules = Chat:WaitForChild("ClientChatModules")
local ChatConstants = require(ReplicatedModules:WaitForChild("ChatConstants"))
local function Run(ChatService)
local timeBot = ChatService:AddSpeaker("TimeBot")
timeBot:JoinChannel("All")
local function addMessageType(speaker, messageObject, channelName)
if speaker == "TimeBot" then
messageObject.MessageType = ChatConstants.MessageTypeTime
end
end
ChatService:RegisterFilterMessageFunction("TimeBotFilter", addMessageType)
task.spawn(function()
while task.wait(5) do
timeBot:SayMessage("The current time is: " .. os.time(), "All", {})
end
end)
end
return Run
最后,必须制作一个消息创建模块。该模块必须返回包含两个元素的词典:消信息类型,索引为 KEY_MESSAGE_TYPE ,以及在创建消息图形用户界面元素时调用的函数,索引为 KEY_CREATOR_FUNCTION 。
由 KEY_CREATOR_FUNCTION 存储的函数需要返回包含多个组件的词典。首先,需要包含一个 Frame 和 TextLabel ,这些将显示在聊天窗口中。这些可以使用函数 util:CreateBaseMessage() 创建。词典还需要包含一个函数,以便在消息文本更新时运行。当消息首次出现在客户端时,它们在处理和过滤消息时拥有空白的替换文本,因此这类消息对象需要处理得到更新通话时发生的事情。接下来,词典需要包含一个函数来确定框架的高度。该函数经常调用 util:GetMessageHeight() 函数。最后,词典需要包含几个函数,定义元素在窗口消失时应该褪色的方式(该实用函数为 util:CreateFadeFunctions())。
-- 新模块脚本将包含在 MessageCreatorModules 中
local messageCreatorModules = script.Parent
local util = require(messageCreatorModules:WaitForChild("Util"))
local clientChatModules = messageCreatorModules.Parent
local ChatSettings = require(clientChatModules:WaitForChild("ChatSettings"))
local ChatConstants = require(clientChatModules:WaitForChild("ChatConstants"))
local function CreateMessageLabel(messageData, channelName)
-- 创建框架和文本标签的 GUI 对象来保存消信息
local BaseFrame, BaseMessage = util:CreateBaseMessage("", ChatSettings.DefaultFont, ChatSettings.ChatWindowTextSize, ChatSettings.DefaultMessageColor)
-- 将框架的背景更改为红色
BaseFrame.BackgroundColor3 = Color3.new(1,0,0)
BaseFrame.BackgroundTransparency = 0
-- 处理更新占位符消息文本
local function UpdateTextFunction(messageObject)
if messageObject.IsFiltered then
BaseMessage.Text = messageObject.Message
end
end
UpdateTextFunction(messageData)
-- 使用 util 函数来确定框架的高度
local function GetHeightFunction(xSize)
return util:GetMessageHeight(BaseMessage, BaseFrame, xSize)
end
-- 创建在聊天窗口消失时调用的渐变函数
local FadeParameters = {}
FadeParameters[BaseMessage] = {
TextTransparency = {FadedIn = 0, FadedOut = 1},
TextStrokeTransparency = {FadedIn = 0.75, FadedOut = 1}
}
local FadeInFunction, FadeOutFunction, UpdateAnimFunction = util:CreateFadeFunctions(FadeParameters)
-- 返回定义消息标签的词典
return {
[util.KEY_BASE_FRAME] = BaseFrame,
[util.KEY_BASE_MESSAGE] = BaseMessage,
[util.KEY_UPDATE_TEXT_FUNC] = UpdateTextFunction,
[util.KEY_GET_HEIGHT] = GetHeightFunction,
[util.KEY_FADE_IN] = FadeInFunction,
[util.KEY_FADE_OUT] = FadeOutFunction,
[util.KEY_UPDATE_ANIMATION] = UpdateAnimFunction
}
end
return {
[util.KEY_MESSAGE_TYPE] = ChatConstants.MessageTypeTime,
[util.KEY_CREATOR_FUNCTION] = CreateMessageLabel
}