镜头操控¶
Time:15 分钟
除了基本的articles/customizing the camera|镜头自定义
之外,开发者还可以通过操控游戏镜头达成如创建特定玩家(角色)视图系统、锁定镜头至世界特定位置及创建独特镜头效果等目的。
镜头属性¶
Roblox 的 Camera
有若干 内置 属性,其中包括:
属性 描述
Camera/CFrame
镜头的 BasePart/CFrame|CFrame
。在放置或移动游戏镜头时常会用到这一属性。
Camera/Focus
镜头在 3D 空间中所朝向的点。如果把镜头朝向某个特定的方向,则需要更新这一属性,因为部分视觉效果会根据其距离焦点的距离来变更细致程度。
Camera/FieldOfView
在屏幕上可观察到的游戏世界的范围,其范围在垂直方向的 1 至 120 度之间(默认值为 70)。
为镜头编写脚本¶
每个玩家的镜头都处于其本地设备上,因此应当将自定义镜头代码放置至 StarterPlayer → StarterPlayerScripts 下的 LocalScript
中。在此类脚本中,可以通过 Workspace/CurrentCamera|CurrentCamera
对象来访问摄像头。
获取当前镜头 ```
local camera = workspace.CurrentCamera
### 越肩视角
基本的越肩视角 镜头,常见于 第三人称 射击游戏当中,可以通过下列脚本来实现。此镜头将会锁定在角色的背后,玩家可通过鼠标来转动视角(非方向输入)。
越肩镜头视角 ```
local Players = game:GetService("Players")
local ContextActionService = game:GetService("ContextActionService")
local UserInputService = game:GetService("UserInputService")
local RunService = game:GetService("RunService")
local camera = workspace.CurrentCamera
local cameraOffset = Vector3.new(2, 2, 8)
local player = Players.LocalPlayer
player.CharacterAdded:Connect(function(character)
local humanoid = character:WaitForChild("Humanoid")
local rootPart = character:WaitForChild("HumanoidRootPart")
humanoid.AutoRotate = false
local cameraAngleX = 0
local cameraAngleY = 0
local function playerInput(actionName, inputState, inputObject)
-- 根据输入变化计算镜头/玩家的旋转
if inputState == Enum.UserInputState.Change then
cameraAngleX = cameraAngleX - inputObject.Delta.X
-- 减少垂直方向鼠标/触控灵敏度并夹住垂直轴
cameraAngleY = math.clamp(cameraAngleY-inputObject.Delta.Y*0.4, -75, 75)
-- 以 X delta 来旋转根部件 CFrame
rootPart.CFrame = rootPart.CFrame * CFrame.Angles(0, math.rad(-inputObject.Delta.X), 0)
end
end
ContextActionService:BindAction("PlayerInput", playerInput, false, Enum.UserInputType.MouseMovement, Enum.UserInputType.Touch)
RunService.RenderStepped:Connect(function()
if camera.CameraType ~= Enum.CameraType.Scriptable then
camera.CameraType = Enum.CameraType.Scriptable
end
local startCFrame = CFrame.new((rootPart.CFrame.Position)) * CFrame.Angles(0, math.rad(cameraAngleX), 0) * CFrame.Angles(math.rad(cameraAngleY), 0, 0)
local cameraCFrame = startCFrame:ToWorldSpace(CFrame.new(cameraOffset.X, cameraOffset.Y, cameraOffset.Z))
local cameraFocus = startCFrame:ToWorldSpace(CFrame.new(cameraOffset.X, cameraOffset.Y, -10000))
camera.CFrame = CFrame.new(cameraCFrame.Position, cameraFocus.Position)
end)
end)
local function focusControl(actionName, inputState, inputObject)
-- 在输入开始时锁定并隐藏鼠标图标
if inputState == Enum.UserInputState.Begin then
UserInputService.MouseBehavior = Enum.MouseBehavior.LockCenter
UserInputService.MouseIconEnabled = false
ContextActionService:UnbindAction("FocusControl", focusControl, false, Enum.UserInputType.MouseButton1, Enum.UserInputType.Touch, Enum.UserInputType.Focus)
end
end
ContextActionService:BindAction("FocusControl", focusControl, false, Enum.UserInputType.MouseButton1, Enum.UserInputType.Touch, Enum.UserInputType.Focus)
注意:可以通过调整第 7 行上的镜头偏移值改变镜头相对于角色的位置。例如,将其放在角色的左肩膀上 (-2, 2, 8
) 或者是将镜头拉到离玩家更远的位置 (2, 2, 15
)。
缩近/拉远¶
使用下面的脚本可以实现一种简单的缩近/拉远镜头系统。本例中使用鼠标中间来进行放大和缩小,使用 ContextActionService
来在移动设备上创建虚拟的缩放按钮。
缩近/拉远 ```
local Players = game:GetService("Players")
local ContextActionService = game:GetService("ContextActionService")
local UserInputService = game:GetService("UserInputService")
local TweenService = game:GetService("TweenService")
local camera = workspace.CurrentCamera
local player = Players.LocalPlayer
local tweenInfo = TweenInfo.new(0.5, Enum.EasingStyle.Quint, Enum.EasingDirection.Out)
local initialFieldOfView = camera.FieldOfView
local initialCameraPosition = camera.CFrame
local isZoomed = false
local inTween = false
local target
player.CharacterAdded:Connect(function(character)
local humanoid = character:WaitForChild("Humanoid")
local rootPart = character:WaitForChild("HumanoidRootPart")
local initialWalkSpeed = humanoid.WalkSpeed
local initialJumpPower = humanoid.JumpPower
local cameraAngleX = 0
local cameraAngleY = 0
local function moveInput(actionName, inputState, inputObject)
if not isZoomed then return end
if inputState == Enum.UserInputState.Change then
cameraAngleX = math.clamp(cameraAngleX-inputObject.Delta.X*0.4, -90, 90)
cameraAngleY = math.clamp(cameraAngleY-inputObject.Delta.Y*0.4, -75, 75)
local camPosition = rootPart.CFrame:ToWorldSpace(CFrame.new(0, 2, -2))
local camRotation = camPosition * CFrame.Angles(0, math.rad(cameraAngleX), 0) * CFrame.Angles(math.rad(cameraAngleY), 0, 0)
target = camRotation:ToWorldSpace(CFrame.new(0, 0, -20))
camera.CFrame = CFrame.new(camRotation.Position, target.Position)
camera.Focus = CFrame.new(target.Position)
end
end
local function scopeInOut(actionName, inputState, inputObject)
if inTween == true then return end
if (inputObject.UserInputType == Enum.UserInputType.MouseButton3 and (inputState == Enum.UserInputState.Begin or inputState == Enum.UserInputState.End))
or (inputObject.UserInputType == Enum.UserInputType.Touch and inputState == Enum.UserInputState.End) then
inTween = true
-- 缩近
if isZoomed == false then
initialCameraPosition = camera.CFrame
camera.CameraType = Enum.CameraType.Scriptable
UserInputService.MouseBehavior = Enum.MouseBehavior.LockCenter
UserInputService.MouseIconEnabled = false
-- 根据镜头方向来旋转根部件 CFrame
local faceToward = camera.CFrame:Lerp(rootPart.CFrame, 1.1)
rootPart.CFrame = CFrame.new(rootPart.CFrame.Position, Vector3.new(faceToward.Position.X, rootPart.Position.Y, faceToward.Position.Z))
target = rootPart.CFrame:ToWorldSpace(CFrame.new(0, 2, -20))
cameraAngleX, cameraAngleY = 0, 0
humanoid.WalkSpeed = 0
humanoid.JumpPower = 0
wait(0.05)
local camPosition = rootPart.CFrame:ToWorldSpace(CFrame.new(0, 2, -2))
camera.CFrame = CFrame.new(camera.CFrame.Position, target.Position)
local tween = TweenService:Create(camera, tweenInfo, {CFrame=camPosition, FieldOfView=12})
tween.Completed:Connect(function()
ContextActionService:BindAction("MoveInput", moveInput, false, Enum.UserInputType.MouseMovement, Enum.UserInputType.Touch)
ContextActionService:SetTitle("ScopeInOut", "–")
camera.Focus = CFrame.new(target.Position)
isZoomed = true
inTween = false
end)
tween:Play()
-- 拉远
elseif isZoomed == true then
ContextActionService:UnbindAction("MoveInput", moveInput, false, Enum.UserInputType.MouseMovement, Enum.UserInputType.Touch)
UserInputService.MouseBehavior = Enum.MouseBehavior.Default
UserInputService.MouseIconEnabled = true
local tween = TweenService:Create(camera, tweenInfo, {CFrame=initialCameraPosition, FieldOfView=initialFieldOfView})
tween.Completed:Connect(function()
ContextActionService:SetTitle("ScopeInOut", "+")
camera.CameraType = Enum.CameraType.Custom
humanoid.WalkSpeed = initialWalkSpeed
humanoid.JumpPower = initialJumpPower
isZoomed = false
inTween = false
end)
tween:Play()
end
end
end
ContextActionService:BindAction("ScopeInOut", scopeInOut, true, Enum.UserInputType.MouseButton3)
ContextActionService:SetPosition("ScopeInOut", UDim2.new(0.65, 0, 0.1, 0))
ContextActionService:SetTitle("ScopeInOut", "+")
end)
缩近与拉远的速度可以通过第 9 行上 `TweenInfo.new()` 的第一个参数进行调整,并且作用域缩放力可以使用第 60 行上的 `FieldOfView` 值(介于 10 和 20 之间最佳)进行调整。
### 绕着对象旋转
若要让镜头全部或部分围绕镜头旋转,请尝试使用以下脚本。该脚本具有可调整镜头偏移、旋转时间、重复计数、过渡类型等功能。
绕对象旋转 ```
local TweenService = game:GetService("TweenService")
local RunService = game:GetService("RunService")
local target = workspace.Part -- 想要绕着旋转的对象
local camera = workspace.CurrentCamera
camera.CameraType = Enum.CameraType.Scriptable
camera.Focus = target.CFrame
local rotationAngle = Instance.new("NumberValue")
local tweenComplete = false
local cameraOffset = Vector3.new(0, 10, 12)
local rotationTime = 15 -- 时间,以秒计
local rotationDegrees = 360
local rotationRepeatCount = -1 -- 使用 -1 以无限次重复
local lookAtTarget = true -- 镜头是否倾斜以直接指向目标
local function updateCamera()
local rotatedCFrame = CFrame.Angles(0, math.rad(rotationAngle.Value), 0)
camera.CFrame = rotatedCFrame:ToWorldSpace(CFrame.new(cameraOffset))
if lookAtTarget == true then
camera.CFrame = CFrame.new(camera.CFrame.Position, target.Position)
end
end
-- 设置并开始旋转过渡
local tweenInfo = TweenInfo.new(rotationTime, Enum.EasingStyle.Linear, Enum.EasingDirection.InOut, rotationRepeatCount)
local tween = TweenService:Create(rotationAngle, tweenInfo, {Value=rotationDegrees})
tween.Completed:Connect(function()
tweenComplete = true
end)
tween:Play()
-- 在过渡运行时更新镜头位置
RunService.RenderStepped:Connect(function()
if tweenComplete == false then
updateCamera()
end
end)
调整第 11 至 15 行上的变量可以控制脚本的行为表现。举例来说,如果镜头位于目标对象上方比较高的位置,如 cameraOffset
矢量值为 0, 30, 12
,则 lookAtTarget
设置控制是否向下倾斜镜头来直接观察对象。
横向卷轴镜头¶
通过下列脚本可以实现常用的横向卷轴镜头系统。此示例的镜头锁定在了 X 面上,此外 Z 偏移可以根据玩家调整。
若想确保该脚本正常运行, 必须 通过将 DevTouchMovementMode 设置为 Thumbstick 来在移动设备上启用传统的拇指摇杆控制模式。详情请参阅articles/customizing game controls|自定义游戏控制
一文。
横向卷轴镜头系统 ```
local Players = game:GetService("Players")
local ContextActionService = game:GetService("ContextActionService")
local UserInputService = game:GetService("UserInputService")
local RunService = game:GetService("RunService")
local cameraChase = {left=10, right=10, up=5, down=0}
local cameraDistance = 200
local camera = workspace.CurrentCamera
camera.CameraType = Enum.CameraType.Scriptable
camera.FieldOfView = 10
local player = Players.LocalPlayer
local leftValue, rightValue, initialTouchPos, jumpTouchInput, thumbstickMinPush, cameraPosZ = 0, 0, 0, nil, 15, 0
local humanoid, rootPart
local function handleMovement(actionName, inputState)
if inputState == Enum.UserInputState.Begin then
if actionName == "Left" then
leftValue, rightValue = 1, 0
elseif actionName == "Right" then
rightValue, leftValue = 1, 0
end
elseif inputState == Enum.UserInputState.End then
if actionName == "Left" or actionName == "Stop" then
leftValue = 0
end
if actionName == "Right" or actionName == "Stop" then
rightValue = 0
end
end
end
local function onMove()
if humanoid then
local moveDirection = rightValue - leftValue
humanoid:Move(Vector3.new(moveDirection, 0, 0), false)
end
end
local function updateCamera()
if camera.CameraType ~= Enum.CameraType.Scriptable then
camera.CameraType = Enum.CameraType.Scriptable
end
if rootPart then
-- 负责处理水平方向镜头追踪
if rootPart.Position.X < camera.CFrame.Position.X - cameraChase["left"] then
camera.CFrame = CFrame.new(Vector3.new(rootPart.Position.X+cameraChase["left"], camera.CFrame.Position.Y, cameraPosZ))
elseif rootPart.Position.X > camera.CFrame.Position.X + cameraChase["right"] then
camera.CFrame = CFrame.new(Vector3.new(rootPart.Position.X-cameraChase["right"], camera.CFrame.Position.Y, cameraPosZ))
end
-- 负责处理垂直方向镜头追踪
if rootPart.Position.Y < camera.CFrame.Position.Y - cameraChase["down"] then
camera.CFrame = CFrame.new(Vector3.new(camera.CFrame.Position.X, rootPart.Position.Y+cameraChase["down"], cameraPosZ))
elseif rootPart.Position.Y > camera.CFrame.Position.Y + cameraChase["up"] then
camera.CFrame = CFrame.new(Vector3.new(camera.CFrame.Position.X, rootPart.Position.Y-cameraChase["up"], cameraPosZ))
end
end
end
local function handleTouchInput(input, gameProcessed)
if input.UserInputState == Enum.UserInputState.Begin and gameProcessed == true then
-- 如果触控起始于 jump 按钮,则追踪该按钮
if humanoid.Jump == true then
jumpTouchInput = input
-- 或者触控起始于拇指摇杆,设置初始位置
else
initialTouchPos = input.Position.X
end
elseif input.UserInputState == Enum.UserInputState.Change and gameProcessed == true then
-- 在玩家移动方面忽略起始于 jump 按钮的触控操作
if jumpTouchInput == input then return end
-- 只有当拇指摇杆被推动一定程度之后才会处理移动动作
if input.Position.X < initialTouchPos - thumbstickMinPush then
handleMovement("Left", Enum.UserInputState.Begin)
elseif input.Position.X > initialTouchPos + thumbstickMinPush then
handleMovement("Right", Enum.UserInputState.Begin)
end
elseif input.UserInputState == Enum.UserInputState.End then
-- 如果触控操作在 jump 按钮上结束,停止追踪触控
if jumpTouchInput == input then
jumpTouchInput = nil
-- 或者触控操作在拇指摇杆上结束,停止所有移动动作
else
handleMovement("Stop", input.UserInputState)
initialTouchPos = 0
end
end
end
player.CharacterAdded:Connect(function(character)
humanoid = player.Character:WaitForChild("Humanoid")
rootPart = player.Character:WaitForChild("HumanoidRootPart")
-- 设置初始镜头位置
camera.CFrame = CFrame.new(Vector3.new(rootPart.Position.X, rootPart.Position.Y, rootPart.Position.Z+cameraDistance))
cameraPosZ = camera.CFrame.Position.Z
-- 如果触控启用,则连接触控处理函数
if UserInputService.TouchEnabled == true then
UserInputService.TouchStarted:Connect(handleTouchInput)
UserInputService.TouchMoved:Connect(handleTouchInput)
UserInputService.TouchEnded:Connect(handleTouchInput)
end
end)
RunService:BindToRenderStep("Input", Enum.RenderPriority.Input.Value, onMove)
RunService:BindToRenderStep("Camera", Enum.RenderPriority.Camera.Value, updateCamera)
ContextActionService:BindAction("Left", handleMovement, false, "a", Enum.KeyCode.Left)
ContextActionService:BindAction("Right", handleMovement, false, "d", Enum.KeyCode.Right)
第 6 行上的 `cameraChase` 值控制在镜头开始追随玩家之前,玩家可以移离“屏幕中心”的格数。在 `cameraDistance` 值(第 7 行)的基础上每个方向值都可以调整,以便适配您的游戏设计,达到心中最好的观感效果。
***__Roblox官方链接__:[镜头操控](https://developer.roblox.com/zh-cn/articles/Camera-manipulation)