跨平台开发

Time:10 分钟

本文概述了在多个平台上进行 Roblox 游戏开发的最佳方法。

Roblox 理念

与其他游戏引擎不同,Roblox 本身就是跨平台的,玩家们可以在电脑上发现并游玩游戏,然后拿起手机并接着之前的进度继续游玩。在大多数情况下,Roblox 游戏应该可以在所有平台上游玩,而不是仅针对一个平台优化而在其他平台上“勉强能玩”。

手机第一

近期数据表明超过 55% 的 Roblox 游戏进程是在手机上进行的,所以您应该付出同等的努力来打造出色的手机游戏体验。在计划您的 UI 和控制时,请考虑以下几点:

UI 布局

即使 GUI 完美地适合电脑屏幕,在较小的手机屏幕上也不一定同样好用。例如,这个 GUI 中的颜色自定义砖块在手机上就变得太小太窄了:

https://developer.roblox.com/assets/bltdfd5f2922f2d90a6/Cross-Platform-UI-Layout-1.png

相比之下,稍微重新设计 GUI 就能同时提升电脑用户和手机用户的体验:

https://developer.roblox.com/assets/blt1c2c28ad6447cb63/Cross-Platform-UI-Layout-2.png

保留区域

在移动设备上,/articles/customizing game controls#mobile-controls|默认控制占据屏幕左下角及/或右下角的一部分。在设计游戏 UI 时,请避免将重要信息或虚拟按钮放在这些区域。

https://developer.roblox.com/assets/blt16669895b1299201/Cross-Platform-Reserved-Regions.png

如果您的游戏使用默认UserChoice控制设置(/articles/customizing game controls#mobile-controls|reference),屏幕上显示的控制可能会有少许偏差。在设计您的 UI 时请考虑到这个可能性。

拇指区

大多数手机玩家使用两个大拇指,一个控制虚拟摇杆/方向键,另一个控制跳跃键。根据设备尺寸和玩家的手部大小, 远离底部角落的按钮会很难按或者根本够不到,所以您应当避免将常用按钮置于绿色区域外。

https://developer.roblox.com/assets/blt8500a5dd039d1510/Cross-Platform-Thumb-Zones.png

适应性 UI

舒适的拇指区在手机与平板电脑上有所差别,因为平板电脑的屏幕更大。请记住一个放在屏幕顶部边缘 30% 以下的按钮在手机上可以轻易够到,但在平板电脑上几乎不可能够到。

https://developer.roblox.com/assets/bltcbf8f4be72c57d4e/Cross-Platform-Adaptable-UI.png

相对跳跃按钮来设置自定义按钮的位置更为可靠。以下代码获取跳跃按钮的位置,并在上面创建一个占位的 ImageButton 按钮:

    
    -- 获得玩家的跳跃键的参考
    local player = game.Players.LocalPlayer
    local PlayerGui = player:WaitForChild("PlayerGui")
    local ScreenGui = PlayerGui:WaitForChild("ScreenGui")
    local TouchGui = PlayerGui:WaitForChild("TouchGui")
    local TouchControlFrame = TouchGui:WaitForChild("TouchControlFrame")
    local JumpButton = TouchControlFrame:WaitForChild("JumpButton")
    
    -- 获得按键的绝对尺寸和位置
    local absSizeX, absSizeY = JumpButton.AbsoluteSize.X, JumpButton.AbsoluteSize.Y
    local absPositionX, absPositionY = JumpButton.AbsolutePosition.X, JumpButton.AbsolutePosition.Y
    
    -- 在跳跃键上创建新按键
    local customButton = Instance.new("ImageButton")
    customButton.Parent = ScreenGui
    customButton.AnchorPoint = Vector2.new(0.5, 1)
    customButton.Size = UDim2.new(0, absSizeX*0.8, 0, absSizeY*0.8)
    customButton.Position = UDim2.new(0, absPositionX+(absSizeX/2), 0, absPositionY-20)

基于内容的 UI

移动设备上的屏幕空间非常有限,因此在游玩时应当只显示最关键的信息。例如,如果玩家有一个特殊动作可以打开门,那么无需把“开门”的按钮一直显示在屏幕上,而应当只在玩家接近门的时候才显示,并在玩家离开一段距离之后消失。

输入检测

有时必须获知玩家当前的设备以便调整 UI,显示行动按钮或提示等等。例如,如果玩家接近一只宝箱,并且有一个获取金币的动作,您可以在电脑端玩家的屏幕上显示 T 键图标,但在移动端玩家的屏幕上激活“收集”按钮。

https://developer.roblox.com/assets/blt25ec84c0f29d3831/Cross-Platform-Input-Detection-Mobile.png

移动端:激活收集按钮

https://developer.roblox.com/assets/blt57b1b33eb3c0444f/Cross-Platform-Input-Detection-PC.png

电脑端:T 键提示

以下 ModuleScript,置于 ReplicatedStorage 内并重命名为 PlayerInputModule,可用于获取玩家的输入类型,以便您根据上文的建议调整 UI 布局或内容。

    
    local UserInputService = game:GetService("UserInputService")
    
    local PlayerInput = {}
    
    local inputTypeString
    -- 如果设备有激活的键盘和鼠标,默认使用这些输出
    if UserInputService.KeyboardEnabled and UserInputService.MouseEnabled then
    	inputTypeString = "Keyboard/Mouse"
    -- 或如果有触控功能但没有键盘和鼠标,默认使用触控输入
    elseif UserInputService.TouchEnabled then
    	inputTypeString = "Touch"
    -- 或如果设备有活动的手柄,默认手柄输入
    elseif UserInputService.GamepadEnabled then
    	inputTypeString = "Gamepad"
    end
    
    function PlayerInput.getInputType()
    	local lastInputEnum = UserInputService:GetLastInputType()
    
    	if lastInputEnum == Enum.UserInputType.Keyboard or string.find(tostring(lastInputEnum.Name), "MouseButton") or lastInputEnum == Enum.UserInputType.MouseWheel then
    		inputTypeString = "Keyboard/Mouse"
    	elseif lastInputEnum == Enum.UserInputType.Touch then
    		inputTypeString = "Touch"
    	elseif string.find(tostring(lastInputEnum.Name), "Gamepad") then
    		inputTypeString = "Gamepad"
    	end
    	return inputTypeString, lastInputEnum
    end
    
    return PlayerInput

请注意脚本使用 UserInputService/GetLastInputType|UserInputService:GetLastInputType() 方法,而不仅是检查设备是否有触控之类的输入“能力”。这是因为电脑端玩家可能会倾向于使用键盘和鼠标,即使他们的电脑有触控屏,而您的 UI 应该遵从活动的输入类型。

PlayerInputModule 脚本就位后,您可以用如下方式从 LocalScript 获取玩家最后的输入类型:

    
    local ReplicatedStorage = game:GetService("ReplicatedStorage")
    
    -- 对模块进行 Require
    local PlayerInputModule = require(ReplicatedStorage:WaitForChild("PlayerInputModule"))
    
    local currentPlayerInput, inputEnum = PlayerInputModule.getInputType()
    print(currentPlayerInput, inputEnum)

***Roblox官方链接:跨平台开发