PGRData/Script/matrix/xentity/xplanet/xgameobject/XPlanetCamera.lua
2024-09-01 22:49:41 +02:00

1104 lines
No EOL
40 KiB
Lua

---@class XPlanetCamera
---@field _Root CS.UnityEngine.Transform 父节点可能为空
---@field _GameObject CS.UnityEngine.GameObject
---@field _Transform CS.UnityEngine.Transform
---@field _Camera CS.UnityEngine.Camera
---@field _CameraMode XPlanetConfigs.SceneCameraMode
local XPlanetCamera = XClass(nil, "XPlanetCamera")
local Input = CS.UnityEngine.Input
local Quaternion = CS.UnityEngine.Quaternion
local RotateUnit = 57.29578 -- 旋转度
function XPlanetCamera:Ctor(root, gameObject)
self._Root = root
self._GameObject = gameObject
self._Transform = gameObject.transform
self._CameraMode = XPlanetConfigs.SceneCameraMode.FreeMode
self._FreeModeStartDragRotation = self:GetTransform().rotation
self:_InitFollowModeParams()
self:_InitFreeModeParams()
self._BeforeModeChangeRootPosition = false -- 模式改变前根节点坐标
self._BeforeModeChangeRootRotation = false -- 模式改变前根节点旋转
self._BeforeModeChangeCamPosition = false -- 模式改变前相机节点坐标
self._BeforeModeChangeCamRotation = false -- 模式改变前相机节点旋转
self._FreeModeScrollValue = 1
self._IsInMove = false
self:InitCamera()
end
--region 模式参数
function XPlanetCamera:ChangeMode(mode)
-- 切换模式时记录当前相机状态
self._BeforeModeChangeRootPosition = self:GetTransform().position
self._BeforeModeChangeRootRotation = self:GetTransform().rotation
self._BeforeModeChangeCamPosition = self:GetCameraTransform().position
self._BeforeModeChangeCamRotation = self:GetCameraTransform().rotation
if mode == XPlanetConfigs.SceneCameraMode.FreeMode then
-- 跟随视角转自由视角需要重置一下camRoot
if self._CameraMode == XPlanetConfigs.SceneCameraMode.FollowMode then
local lookPosition = not XTool.UObjIsNil(self._FollowModeTran) and self._FollowModeTran.position or self:GetCameraCenterRayPosition()
local rot = self:GetLookAtRotation(self._FollowModeStartPosition, lookPosition, self._FollowModeStartRotation)
self:SetCameraRootWorldRotation(rot)
self:SetCameraWorldPosition(self._BeforeModeChangeCamPosition)
self:SetCameraWorldRotation(self._BeforeModeChangeCamRotation)
self._FreeModeLookPosition = lookPosition
self._FollowModeTran = false
self._FollowModeStartPosition = false
self._FollowModeStartRotation = false
elseif self._CameraMode == XPlanetConfigs.SceneCameraMode.StaticMode then
self._FreeModeScrollValue = 1
end
end
self._CameraMode = mode
end
function XPlanetCamera:CheckIsInFollowMode()
return self:_CheckMode(XPlanetConfigs.SceneCameraMode.FollowMode)
end
function XPlanetCamera:CheckIsInFreeMode()
return self:_CheckMode(XPlanetConfigs.SceneCameraMode.FreeMode)
end
function XPlanetCamera:CheckIsInStaticMode()
return self:_CheckMode(XPlanetConfigs.SceneCameraMode.StaticMode)
end
function XPlanetCamera:CheckIsInMovieMode()
return self:_CheckMode(XPlanetConfigs.SceneCameraMode.MovieMode)
end
---@param sceneCameraMode number XPlanetConfigs.SceneCameraMode
function XPlanetCamera:_CheckMode(sceneCameraMode)
return self._CameraMode == sceneCameraMode
end
--endregion
--region 自由模式(可交互旋转)
function XPlanetCamera:_InitFreeModeParams()
self._FreeModeStartDragRotation = nil
self._FreeModeStartDragMousePosition = nil
self._FreeModeIsDrag = false
self._FreeModeAfterDragSpeed = 0
self._FreeModeAfterDragDelta = nil
self._FreeModeAfterDragBefore = nil
self._FreeModeAfterDragAfter = nil
end
---限制滑轮高度
function XPlanetCamera:_FreeModeSetAndCheckScrollValue(scrollValue)
self._FreeModeScrollValue = scrollValue or 1
self._FreeModeScrollValue = math.min(self._FreeModeScrollValue, 1)
self._FreeModeScrollValue = math.max(self._FreeModeScrollValue, 0)
end
function XPlanetCamera:_FreeModeGetCurCameraParams()
local maxCam = XDataCenter.PlanetManager.GetCamera(XPlanetConfigs.GetCamBuildMax())
local minCam = XDataCenter.PlanetManager.GetCamera(XPlanetConfigs.GetCamBuildMin())
local position, rotation, fov
position = Vector3.SlerpUnclamped(minCam:GetPosition(), maxCam:GetPosition(), self._FreeModeScrollValue)
rotation = Quaternion.SlerpUnclamped(minCam:GetRotation(), maxCam:GetRotation(), self._FreeModeScrollValue)
fov = minCam:GetFov() + (minCam:GetFov() - maxCam:GetFov()) * self._FreeModeScrollValue
return position, rotation, fov
end
function XPlanetCamera:_FreeModeSetCurCamera()
local position, rotation, fov = self:_FreeModeGetCurCameraParams()
self:SetCameraLocalPosition(position)
self:SetCameraFov(fov)
self:SetCameraLocalRotation(rotation)
end
function XPlanetCamera:ChangeFreeModeByCamera(cb)
local beforeIsFollow = self._CameraMode == XPlanetConfigs.SceneCameraMode.FollowMode
self:_FreeModeSetAndCheckScrollValue(self._FreeModeScrollValue)
self:ChangeMode(XPlanetConfigs.SceneCameraMode.FreeMode)
self:_InitFreeModeParams()
self._Camera.transform:SetParent(self._Transform)
self:SetCameraRootWorld(Vector3.zero)
if beforeIsFollow then
self:PlaySound(XPlanetConfigs.SoundCueId.CamFar)
self:MoveCamFollowToFree(nil, cb)
else
self:_FreeModeSetCurCamera()
if cb then cb() end
end
end
---自由模式拖拽转动
function XPlanetCamera:FreeModeBeginDrag()
if not self:CheckIsInFreeMode()
or XDataCenter.GuideManager.CheckIsInGuidePlus() -- 引导时屏蔽旋转
or Input.touchCount == 2 then -- 二指缩放屏蔽旋转
return
end
self._FreeModeStartDragRotation = self:GetTransform().rotation
self._FreeModeStartDragMousePosition = Input.mousePosition
self._FreeModeIsDrag = true
self._FreeModeAfterDragSpeed = 0
self._FreeModeAfterDragBefore = Input.mousePosition
self._FreeModeAfterDragAfter = nil
self._FreeModeAfterDragDelta = nil
end
---自由模式拖拽转动
function XPlanetCamera:FreeModeOnDrag()
if not self:CheckIsInFreeMode()
or Input.touchCount == 2 -- 二指缩放屏蔽旋转
or XDataCenter.GuideManager.CheckIsInGuidePlus() -- 引导时屏蔽旋转
or not self._FreeModeIsDrag then
self._FreeModeStartDragRotation = nil
self._FreeModeStartDragMousePosition = nil
self._FreeModeIsDrag = false
self._FreeModeAfterDragSpeed = 0
return
end
local deltaMousePosition = Input.mousePosition - self._FreeModeStartDragMousePosition
local screen = CS.UnityEngine.Screen
local screenDragDelta = Vector2(deltaMousePosition.x / screen.width, deltaMousePosition.y / screen.height)
local tran = self:GetTransform()
local cameraTran = self:GetCameraTransform()
local x = screenDragDelta.x * RotateUnit * XPlanetConfigs.GetPlanetRotateSpeed()
local y = screenDragDelta.y * RotateUnit * XPlanetConfigs.GetPlanetRotateSpeed()
-- 上下拖拽相当于将目标物体按相机的right方向旋转
local yRot = Quaternion.AngleAxis(-y, cameraTran.right)
-- 左右拖拽相当于将目标物体按相机的up方向旋转
local xRot = Quaternion.AngleAxis(x, cameraTran.up)
local rot = xRot * yRot
tran.rotation = rot * self._FreeModeStartDragRotation
self._FreeModeAfterDragAfter = Input.mousePosition
self._FreeModeAfterDragDelta = self._FreeModeAfterDragAfter - self._FreeModeAfterDragBefore
self._FreeModeAfterDragDelta = Vector2(self._FreeModeAfterDragDelta.x / screen.width, self._FreeModeAfterDragDelta.y / screen.height)
self._FreeModeAfterDragBefore = Input.mousePosition
end
---自由模式拖拽转动
function XPlanetCamera:FreeModeEndDrag()
if not self:CheckIsInFreeMode()
or XDataCenter.GuideManager.CheckIsInGuidePlus() -- 引导时屏蔽旋转
or Input.touchCount == 2 then -- 二指缩放屏蔽旋转
return
end
self._FreeModeStartDragRotation = nil
self._FreeModeStartDragMousePosition = nil
self._FreeModeIsDrag = false
self._FreeModeAfterDragSpeed = XPlanetConfigs.GetPlanetRotateSpeed()
end
---自由模式拖拽惯性
function XPlanetCamera:FreeAfterDragUpdate()
if not self:CheckIsInFreeMode() or Input.touchCount >= 1 or self._FreeModeIsDrag then
return
end
if not self._FreeModeAfterDragDelta or not self._FreeModeAfterDragSpeed or self._FreeModeAfterDragSpeed <= 0 then
return
end
local tran = self:GetTransform()
local cameraTran = self:GetCameraTransform()
-- 速度衰减
if self._FreeModeAfterDragSpeed > 0.01 then
self._FreeModeAfterDragSpeed = self._FreeModeAfterDragSpeed - self._FreeModeAfterDragSpeed / XPlanetConfigs.GetPlanetRotateReduction()
else
self:FreeAfterDragStop()
return
end
local x = self._FreeModeAfterDragDelta.x * RotateUnit * self._FreeModeAfterDragSpeed
local y = self._FreeModeAfterDragDelta.y * RotateUnit * self._FreeModeAfterDragSpeed
local yRot = Quaternion.AngleAxis(-y, cameraTran.right)
local xRot = Quaternion.AngleAxis(x, cameraTran.up)
local rot = xRot * yRot
tran.rotation = rot * tran.rotation
end
---自由模式停止拖拽惯性
function XPlanetCamera:FreeAfterDragStop()
self._FreeModeAfterDragDelta = nil
self._FreeModeAfterDragSpeed = 0
end
local TouchPhase = CS.UnityEngine.TouchPhase
local oldTouch1, oldTouch2
---自由模式缩放
function XPlanetCamera:FreeModeScroll()
if (not self:CheckIsInFreeMode()) or self._IsInMove or self._FreeModeIsDrag then
return
end
if Input.GetAxis("Mouse ScrollWheel") ~= 0 then
local value = CS.UnityEngine.Mathf.Clamp(self._FreeModeScrollValue + Input.GetAxis("Mouse ScrollWheel") * 0.7, 0, 1)
self:_FreeModeSetAndCheckScrollValue(value)
self:_FreeModeSetCurCamera()
end
if (Input.touchCount == 2) then
self:FreeAfterDragStop()
local touch1 = Input.GetTouch(0)
local touch2 = Input.GetTouch(1)
if touch2.phase == TouchPhase.Began then
oldTouch1 = touch1
oldTouch2 = touch2
return
end
if (not oldTouch1) or (not oldTouch2) then
return
end
local oldDistance = Vector2.Distance(oldTouch1.position,oldTouch2.position)
local newDistance = Vector2.Distance(touch1.position,touch2.position)
local offset = oldDistance - newDistance
local screen = CS.UnityEngine.Screen
offset = offset / (screen.width / 2)
local value = CS.UnityEngine.Mathf.Clamp(self._FreeModeScrollValue + offset, 0, 1)
oldTouch1 = touch1
oldTouch2 = touch2
self:_FreeModeSetAndCheckScrollValue(value)
self:_FreeModeSetCurCamera()
end
end
--endregion
--region 跟随模式(跟随某物体,不可交互)
function XPlanetCamera:_InitFollowModeParams()
-- 跟随定位到角色身上参数
self._FollowModeIsStart = false
self._FollowModeTran = nil
self._FollowModeCam = nil
self._FollowModeCurTranForward = false
self._FollowModeStartPosition = false
self._FollowModeStartRotation = false
-- 不固定朝向角色前方参数
self._FollowUnLockCurCamPos2Planet = false -- 相机当前相对球心坐标
self._FollowUnLockCurFollowTranPos2Planet = false -- 跟随目标当前相对球心坐标
self._FollowUnLockCamStartRotation = false -- 开始跟随时相机旋转度
self._FollowUnLockDeltaRotation = false -- 跟随模式下与开始跟随时相机坐标旋转度差值
end
---@param camera XPlanetSceneCamera
function XPlanetCamera:ChangeFollowModeByCamera(followTran, camera, cb, isNoAnim)
if not followTran then
XUiManager.TipErrorWithKey("PlanetRunningNoLeader")
if cb then cb() end
return
end
self:ChangeMode(XPlanetConfigs.SceneCameraMode.FollowMode)
self._FollowModeTran = followTran
self._FollowModeCam = camera
self._FollowModeIsStart = false
if not isNoAnim then
self:SetFollowModeWithAnim(cb)
else
-- 将镜头定位到角色
local fromFollowRotation = self:GetTransform().localRotation
local fromPosition = self:GetCameraCenterRayPosition()
local target = self._FollowModeTran.position
local toRotation = self:GetLookAtRotation(fromPosition, target, fromFollowRotation)
self:SetCameraRootWorldRotation(toRotation)
self._FollowModeCurTranForward = self._FollowModeTran.forward
self._FollowModeStartPosition = self._FollowModeTran.position
self._FollowModeStartRotation = self._Transform.rotation
self._Camera.transform:SetParent(self._FollowModeTran)
self:SetCameraRootWorldRotation(self:GetCamRootFollowTranRot())
self:SetCameraLocal(self._FollowModeCam)
self._Camera.transform:SetParent(self._Transform)
self._FollowModeIsStart = true
if cb then cb() end
end
end
function XPlanetCamera:SetFollowModeWithAnim(cb)
self:PlaySound(XPlanetConfigs.SoundCueId.CamNear)
self:FreeModeToOtherMode(self._FollowModeTran, self._FollowModeCam, nil, function()
self._FollowUnLockCurCamPos2Planet = self:GetCameraTransform().position - self:GetTransform().position
self._FollowUnLockCurFollowTranPos2Planet = self._FollowModeTran.position - self:GetTransform().position
self._FollowUnLockCamStartRotation = self:GetCameraTransform().rotation
self._FollowUnLockDeltaRotation = nil
self._FollowModeIsStart = true
if cb then cb() end
end)
end
---@param explore XPlanetRunningExplore
function XPlanetCamera:FollowModeUpdate(explore, deltaTime)
if not self:CheckIsInFollowMode() then
return
end
if not self._FollowModeIsStart then
return
end
if not explore:GetCaptainTransform() then
return
end
if explore:GetCaptainTransform() ~= self._FollowModeTran then
self._FollowModeTran = explore:GetCaptainTransform()
self._FollowModeCurTranForward = self._FollowModeTran.forward
end
if self._FollowModeCurTranForward == self._FollowModeTran.forward then
return
end
self._FollowModeCurTranForward = self._FollowModeTran.forward
-- 不固定朝向角色前方
local newCurFollowTranPos2Planet = self._FollowModeTran.position - self:GetTransform().position
if newCurFollowTranPos2Planet == self._FollowUnLockCurFollowTranPos2Planet then
return
end
local deltaRot = Quaternion.FromToRotation(self._FollowUnLockCurFollowTranPos2Planet, newCurFollowTranPos2Planet)
if not self._FollowUnLockDeltaRotation then
self._FollowUnLockDeltaRotation = deltaRot
else
self._FollowUnLockDeltaRotation = deltaRot * self._FollowUnLockDeltaRotation
end
local newCamPosition = deltaRot * self._FollowUnLockCurCamPos2Planet + self:GetTransform().position
self:SetCameraWorldPosition(newCamPosition)
self:SetCameraWorldRotation(self._FollowUnLockDeltaRotation * self._FollowUnLockCamStartRotation)
self._FollowUnLockCurCamPos2Planet = self:GetCameraTransform().position - self:GetTransform().position
self._FollowUnLockCurFollowTranPos2Planet = self._FollowModeTran.position - self:GetTransform().position
end
---获取跟随机位Cam相对于CamRoot的LocalPosition, LocalRotation, fov
function XPlanetCamera:_GetFollowResultLocalParams()
local fromCamPosition = self:GetCameraTransform().localPosition
local fromCamRotation = self:GetCameraTransform().localRotation
local fromCamFov = self._Camera.fieldOfView
local pos, rot, fov
if XTool.UObjIsNil(self._FollowModeTran) then
return fromCamPosition, fromCamRotation, fromCamFov
end
self._Camera.transform:SetParent(self._FollowModeTran)
self:SetCameraLocal(self._FollowModeCam)
self._Camera.transform:SetParent(self._Transform)
pos = self:GetCameraTransform().localPosition
rot = self:GetCameraTransform().localRotation
fov = self._Camera.fieldOfView
self:SetCameraLocalPosition(fromCamPosition)
self:SetCameraLocalRotation(fromCamRotation)
self:SetCameraFov(fromCamFov)
return pos, rot, fov
end
---获取跟随机位Cam的Up向量(用于平滑过渡)
function XPlanetCamera:_GetFollowModeResultCamUp()
local fromCamPosition = self:GetCameraTransform().localPosition
local fromCamRotation = self:GetCameraTransform().localRotation
local fromCamFov = self._Camera.fieldOfView
if XTool.UObjIsNil(self._FollowModeTran) then
return self._Camera.transform.up
end
self._Camera.transform:SetParent(self._FollowModeTran)
self:SetCameraLocal(self._FollowModeCam)
self._Camera.transform:SetParent(self._Transform)
local result = self._Camera.transform.up
self:SetCameraLocalPosition(fromCamPosition)
self:SetCameraLocalRotation(fromCamRotation)
self:SetCameraFov(fromCamFov)
return result
end
function XPlanetCamera:_GetCamToLocalParams4CamRoot()
local fromCamRotation = self:GetCameraTransform().localRotation
local fromCamPosition = self:GetCameraTransform().localPosition
local fromCamFov = self._Camera.fieldOfView
return fromCamPosition, fromCamRotation, fromCamFov
end
function XPlanetCamera:_GetFollowTargetPosition()
if not self._FollowModeTran then
return Vector3.zero
end
return self._FollowModeTran.position
end
function XPlanetCamera:GetCamRootFollowTranRot()
local forward = self._FollowModeTran.position - self._Transform.position
local up = self._FollowModeTran.forward
return Quaternion.LookRotation(forward, up)
end
--endregion
--region 静态模式(不可交互)
---@param camera XPlanetSceneCamera
function XPlanetCamera:ChangeStaticModeByCamera(camera, positionOffset, rotationOffset, isAnim, beginCb, endCb)
self:ChangeMode(XPlanetConfigs.SceneCameraMode.StaticMode)
self:GetCameraTransform():SetParent(self:GetTransform())
self:ResetCameraRoot()
if isAnim then
local fromPosition = self:GetCameraTransform().localPosition
local fromRotation = self:GetCameraTransform().localRotation
local fromFov = self._Camera.fieldOfView
local toPosition = camera:GetPosition()
local toRotation = camera:GetRotation()
if positionOffset then
toPosition = toPosition + positionOffset
end
if rotationOffset then
toRotation = toRotation * rotationOffset
end
self:MoveCamTo(fromPosition, fromRotation, fromFov, toPosition, toRotation, camera:GetFov(), beginCb, endCb)
else
if beginCb then beginCb() end
self:SetCameraLocal(camera, positionOffset, rotationOffset)
if endCb then endCb() end
end
end
function XPlanetCamera:StaticModeMove(position, rotation, isAnim, beginCb, endCb)
if not self:CheckIsInStaticMode() or self._IsInMove then
return
end
self:GetCameraTransform():SetParent(self:GetTransform())
if isAnim then
local fromPosition = self:GetCameraTransform().localPosition
local fromRotation = self:GetCameraTransform().localRotation
local fromFov = self._Camera.fieldOfView
local toPosition = position and position or fromPosition
local toRotation = rotation and rotation or fromRotation
local toFov = self._Camera.fieldOfView
self:MoveCamTo(fromPosition, fromRotation, fromFov, toPosition, toRotation, toFov, beginCb, endCb)
else
if beginCb then beginCb() end
self:SetCameraLocalPosition(position)
self:SetCameraLocalRotation(rotation)
if endCb then endCb() end
end
end
--endregion
--region 剧情模式(不可交互)
function XPlanetCamera:ChangeMovieModeByCamera(camera, lookAtTran, dontAnim, callBack)
if not lookAtTran then
if callBack then
callBack()
end
XLog.Error("PlanetCamera change cam Error: lookAtTran is null!")
return
end
self:ChangeMode(XPlanetConfigs.SceneCameraMode.MovieMode)
if dontAnim then
self:GetCameraTransform():SetParent(lookAtTran)
self:SetCameraLocal(camera)
self:GetCameraTransform():SetParent(self:GetTransform())
if callBack then
callBack()
end
else
self:FreeModeToOtherMode(lookAtTran, camera, nil, callBack)
end
end
--endregion
--region Camera
function XPlanetCamera:SetCameraFov(fov)
if self._Camera then
self._Camera.fieldOfView = fov
end
end
---@param camera XPlanetSceneCamera
function XPlanetCamera:SetCameraLocal(camera, positionOffset, rotationOffset)
if camera then
local position = camera:GetPosition()
if positionOffset then
position = position + positionOffset
end
local rotation = camera:GetRotation()
if rotationOffset then
rotation = rotation * rotationOffset
end
self:GetCameraTransform().localPosition = position
self:GetCameraTransform().localRotation = rotation
self._Camera.fieldOfView = camera:GetFov()
end
end
function XPlanetCamera:SetCameraLocalPosition(position)
if position then
self:GetCameraTransform().localPosition = position
end
end
function XPlanetCamera:SetCameraLocalRotation(rotation)
if rotation then
self:GetCameraTransform().localRotation = rotation
end
end
---@param camera XPlanetSceneCamera
function XPlanetCamera:SetCameraWorld(camera, positionOffset, rotationOffset)
if camera then
local position = camera:GetPosition()
if positionOffset then
position = position + positionOffset
end
local rotation = camera:GetRotation()
if rotationOffset then
rotation = rotation * rotationOffset
end
self:GetCameraTransform().position = position
self:GetCameraTransform().rotation = rotation
self._Camera.fieldOfView = camera:GetFov()
end
end
function XPlanetCamera:SetCameraWorldPosition(position)
if position then
self:GetCameraTransform().position = position
end
end
function XPlanetCamera:SetCameraWorldRotation(rotation)
if rotation then
self:GetCameraTransform().rotation = rotation
end
end
function XPlanetCamera:ResetCamera()
self:GetCameraTransform().localPosition = Vector3.zero
self:GetCameraTransform().localRotation = Quaternion.identity
self._Camera.fieldOfView = 60
end
--endregion
--region CameraRoot
function XPlanetCamera:SetCameraRootLocal(position, rotation)
if position then
self:GetTransform().localPosition = position
end
if rotation then
self:GetTransform().localRotation = rotation
end
end
function XPlanetCamera:SetCameraRootLocalPosition(position)
if position then
self:GetTransform().localPosition = position
end
end
function XPlanetCamera:SetCameraRootLocalRotation(rotation)
if rotation then
self:GetTransform().localRotation = rotation
end
end
function XPlanetCamera:SetCameraRootWorld(position, rotation)
if position then
self:GetTransform().position = position
end
if rotation then
self:GetTransform().rotation = rotation
end
end
function XPlanetCamera:SetCameraRootWorldPosition(position)
if position then
self:GetTransform().position = position
end
end
function XPlanetCamera:SetCameraRootWorldRotation(rotation)
if rotation then
self:GetTransform().rotation = rotation
end
end
function XPlanetCamera:ResetCameraRoot()
self:GetTransform().localPosition = Vector3.zero
self:GetTransform().localRotation = Quaternion.identity
end
--endregion
--region Anim
---自由模式聚焦动画
function XPlanetCamera:FreeModeLookAt(position, beginCb, endCb)
if not self:CheckIsInFreeMode() then
return
end
self:FreeAfterDragStop()
local tran = self:GetTransform()
local fromRotation = tran.rotation
local toRotation = self:GetTowardRotation(self:GetCameraCenterRayPosition(), position) * fromRotation
self:_AnimRotateCamRoot(fromRotation, toRotation, beginCb, endCb)
end
function XPlanetCamera:FreeModeToOtherMode(followTran, camera, beginCb, endCb)
if self._IsInMove then
return
end
self:FreeAfterDragStop()
local fromPosition, fromRotation, fromFov
local toPosition, toRotation, toFov
local fromUp, toUp
local tempRot = Quaternion.identity
local targetPos = followTran.position
-- 机位变转
local onRefresh04 = function(t)
self:SetCameraLocalPosition(Vector3.SlerpUnclamped(fromPosition, toPosition, t))
local forward = targetPos - self:GetCameraTransform().position
tempRot:SetLookRotation(forward, Vector3.SlerpUnclamped(fromUp, toUp, t))
self:SetCameraWorldRotation(tempRot)
self:SetCameraFov(fromFov + (toFov - fromFov) * t)
end
local onEnd = function()
self:SetCameraLocalPosition(toPosition)
self:SetCameraWorldRotation(toRotation)
self:SetCameraFov(toFov)
XLuaUiManager.SetMask(false)
self._IsInMove = false
if endCb then endCb() end
end
-- 校准
local onRefresh03 = function(t)
tempRot:SetLookRotation(targetPos - self:GetCameraTransform().position, fromUp)
self:SetCameraWorldRotation(Quaternion.Lerp(fromRotation, tempRot, t))
end
local onEnd03 = function()
XUiHelper.Tween(1.2, onRefresh04, onEnd)
end
-- 定位到followTran
local rootFromRotation = self:GetTransform().localRotation
local rootFromPosition = self:GetCameraCenterRayPosition()
local followPosition = followTran.position
local rootToRotation = self:GetTowardRotation(rootFromPosition, followPosition) * rootFromRotation
local onRefresh02 = function(t)
self:SetCameraRootLocal(nil, Quaternion.Lerp(rootFromRotation, rootToRotation, t))
end
local onEnd02 = function()
self._FollowModeCurTranForward = followTran.forward
self._FollowModeStartPosition = followTran.position
self._FollowModeStartRotation = self._Transform.rotation
fromPosition = self:GetCameraTransform().localPosition
fromRotation = self:GetCameraTransform().rotation
fromFov = self._Camera.fieldOfView
fromUp = self:GetCameraTransform().up
self:GetCameraTransform():SetParent(followTran)
self:SetCameraLocal(camera)
self:GetCameraTransform():SetParent(self:GetTransform())
toPosition = self:GetCameraTransform().localPosition
toRotation = self:GetCameraTransform().rotation
toFov = camera:GetFov()
toUp = self:GetCameraTransform().up
-- 计算相机注视点
local target2CamVector = followTran.position - self:GetCameraTransform().position
local camTarget2CamVector = Vector3.Project(target2CamVector, self:GetCameraTransform().forward)
targetPos = self:GetCameraTransform().position + camTarget2CamVector
self:SetCameraLocalPosition(fromPosition)
self:SetCameraWorldRotation(fromRotation)
XUiHelper.Tween(0.1, onRefresh03, onEnd03)
end
-- 缩放至最高
self._FreeToFollowScrollValue = self._FreeModeScrollValue
local onRefresh01 = function(t)
self:_FreeModeSetAndCheckScrollValue((1 - self._FreeToFollowScrollValue) * t + self._FreeToFollowScrollValue)
self:_FreeModeSetCurCamera()
end
local onEnd01 = function()
XUiHelper.Tween(0.5, onRefresh02, onEnd02)
end
XLuaUiManager.SetMask(true)
self._IsInMove = true
if beginCb then beginCb() end
-- 最高不缩放
if self._FreeModeScrollValue == 1 then
onEnd01()
else
XUiHelper.Tween(0.5 * (1 - self._FreeModeScrollValue), onRefresh01, onEnd01)
end
end
function XPlanetCamera:FreeModeScrollToValue(value, beginCb, endCb)
if self._IsInMove then
return
end
self:FreeAfterDragStop()
self._FreeToFollowScrollValue = self._FreeModeScrollValue
local onRefresh01 = function(t)
self:_FreeModeSetAndCheckScrollValue((value - self._FreeToFollowScrollValue) * t + self._FreeToFollowScrollValue)
self:_FreeModeSetCurCamera()
end
local onEnd01 = function()
self:_FreeModeSetAndCheckScrollValue(value)
self:_FreeModeSetCurCamera()
XLuaUiManager.SetMask(false)
self._IsInMove = false
if endCb then endCb() end
end
XLuaUiManager.SetMask(true)
self._IsInMove = true
if beginCb then beginCb() end
-- 最高不缩放
if self._FreeModeScrollValue == value then
onEnd01()
else
XUiHelper.Tween(0.5, onRefresh01, onEnd01)
end
end
---旋转根节点
function XPlanetCamera:_AnimRotateCamRoot(fromRotation, toRotation, beginCb, endCb)
if self._IsInMove then
return
end
XLuaUiManager.SetMask(true)
self._IsInMove = true
if beginCb then beginCb() end
XUiHelper.Tween(0.5, function(t)
self:SetCameraRootLocalRotation(Quaternion.Slerp(fromRotation, toRotation, t))
end, function()
self:SetCameraRootLocalRotation(toRotation)
XLuaUiManager.SetMask(false)
self._IsInMove = false
if endCb then endCb() end
end)
end
--endregion
--region 音效
---@param cueId number XPlanetConfigs.SoundCueId
function XPlanetCamera:PlaySound(cueId)
XSoundManager.PlaySoundByType(cueId, XSoundManager.SoundType.Sound)
end
--endregion
--region 相机轨迹动画
function XPlanetCamera:MoveCamRootTo(fromPosition, fromRotation, toPosition, toRotation, beginCb, endCb)
if self._IsInMove then
return
end
XLuaUiManager.SetMask(true)
if beginCb then beginCb() end
XUiHelper.Tween(0.5, function(t)
self._IsInMove = true
if fromPosition then
self:SetCameraRootLocalPosition(CS.UnityEngine.Vector3.Slerp(fromPosition, toPosition, t))
end
self:SetCameraRootLocalRotation(Quaternion.Slerp(fromRotation, toRotation, t))
end, function()
if endCb then endCb() end
self:SetCameraRootLocalRotation(toRotation)
self:SetCameraRootLocalPosition(toPosition)
XLuaUiManager.SetMask(false)
self._IsInMove = false
end)
end
---@param fromCamera XPlanetSceneCamera
---@param toCamera XPlanetSceneCamera
function XPlanetCamera:MoveCamToByCamera(fromCamera, toCamera, beginCb, endCb)
local fromPosition = fromCamera and fromCamera:GetPosition() or self:GetCameraTransform().localPosition
local fromRotation = fromCamera and fromCamera:GetRotation() or self:GetCameraTransform().localRotation
local fromFov = fromCamera and fromCamera:GetFov() or self._Camera.fieldOfView
local toPosition = toCamera:GetPosition()
local toRotation = toCamera:GetRotation()
local toFov = toCamera:GetFov()
self:MoveCamTo(fromPosition, fromRotation, fromFov, toPosition, toRotation, toFov, beginCb, endCb)
end
function XPlanetCamera:MoveCamTo(fromPosition, fromRotation, fromFov, toPosition, toRotation, toFov, beginCb, endCb)
if self._IsInMove then
return
end
XLuaUiManager.SetMask(true)
if beginCb then beginCb() end
XUiHelper.Tween(0.5, function(t)
self._IsInMove = true
self:SetCameraLocalRotation(Quaternion.Slerp(fromRotation, toRotation, t))
self:SetCameraLocalPosition(CS.UnityEngine.Vector3.Slerp(fromPosition, toPosition, t))
self:SetCameraFov(fromFov + (toFov - fromFov) * t)
end, function()
self:SetCameraLocalRotation(toRotation)
self:SetCameraLocalPosition(toPosition)
self:SetCameraFov(toFov)
XLuaUiManager.SetMask(false)
self._IsInMove = false
if endCb then endCb() end
end)
end
---跟随视角转自由平滑过渡
function XPlanetCamera:MoveCamFollowToFree(beginCb, endCb)
if self._IsInMove then
return
end
self._IsInMove = true
local tempUp = self:GetCameraTransform().up
local fromPosition, fromRotation, fromFov = self:_GetCamToLocalParams4CamRoot()
self:_FreeModeSetCurCamera()
local targetUp = self:GetCameraTransform().up
local toPosition, toRotation, toFov = self:_GetCamToLocalParams4CamRoot()
self:SetCameraLocalPosition(fromPosition)
self:SetCameraLocalRotation(fromRotation)
self:SetCameraFov(fromFov)
local tempRot = Quaternion.identity
local target2CamVector = self._FreeModeLookPosition - self:GetCameraTransform().position
local camTarget2CamVector = Vector3.Project(target2CamVector, self:GetCameraTransform().forward)
local targetPos = self:GetCameraTransform().position + camTarget2CamVector
local onRefresh = function(t)
-- 二次缓动position以适应rotation平滑缓动
self:SetCameraLocalPosition(Vector3.Slerp(fromPosition, Vector3.Slerp(fromPosition, toPosition, t), t))
local up = CS.UnityEngine.Vector3.Lerp(tempUp, targetUp, t) -- 二次缓动rotation平滑看向跟随目标
tempRot:SetLookRotation(targetPos - self:GetCameraTransform().position, up)
self:SetCameraWorldRotation(tempRot)
self:SetCameraFov(fromFov + (toFov - fromFov) * t)
--fromRotation = self:GetCameraTransform().localRotation -- 一次缓动rotation结果
end
local onFinish = function()
XUiHelper.Tween(0.1, function(t)
local tempF = Quaternion.Slerp(self:GetCameraTransform().localRotation, toRotation, t)
self:SetCameraLocalRotation(tempF)
end, function()
self:_FreeModeSetCurCamera()
XLuaUiManager.SetMask(false)
self._IsInMove = false
if endCb then
endCb()
end
end)
end
if beginCb then beginCb() end
XLuaUiManager.SetMask(true)
XUiHelper.Tween(1, onRefresh, onFinish)
end
---获取聚焦球体目标旋转向量
---@param fromPosition Vector3 当前相机朝向点世界坐标
---@param targetPosition Vector3 目标点世界坐标
---@param fromRotation Quaternion 起始旋转向量
function XPlanetCamera:GetLookAtRotation(fromPosition, targetPosition, fromRotation)
local tran = self:GetTransform()
local fromDirection = (fromPosition - tran.position).normalized
local toDirection = (targetPosition - tran.position).normalized
local rot = Quaternion.FromToRotation(fromDirection, toDirection)
return rot * fromRotation
end
---获取CameraRoot聚焦目标旋转差值
---@param fromPosition Vector3 当前相机朝向点世界坐标
---@param targetPosition Vector3 目标点世界坐标
function XPlanetCamera:GetTowardRotation(fromPosition, targetPosition)
local tran = self:GetTransform()
local fromDirection = (fromPosition - tran.position).normalized
local toDirection = (targetPosition - tran.position).normalized
local rot = Quaternion.FromToRotation(fromDirection, toDirection)
return rot
end
---获取Transform朝向lookPosition的LocalRotation
---@return Quaternion
function XPlanetCamera:GetTranLookAtLocalRotation(transform, lookPosition, up)
local rot = transform.rotation
transform:LookAt(lookPosition, up)
local toR = transform.localRotation
transform.rotation = rot
return toR
end
--endregion
--region 射线检测
---射线检测地板对象
---@return CS.UnityEngine.Transfrom
function XPlanetCamera:RayTileCastByScreenPoint(screenPoint, mask)
local ray = self._Camera:ScreenPointToRay(screenPoint)
if not mask then
mask = CS.UnityEngine.LayerMask.GetMask(HomeSceneLayerMask.HomeCharacter) | CS.UnityEngine.LayerMask.GetMask(HomeSceneLayerMask.Room)
end
local hit = self:GetCameraTransform():PhysicsRayCast(Vector3.zero, ray.direction, mask)
return hit
end
---射线检测地板对象
---@return CS.UnityEngine.Transfrom
function XPlanetCamera:RayTileCast()
return self:RayCast(CS.UnityEngine.LayerMask.GetMask(HomeSceneLayerMask.Room))
end
---射线检测角色对象
---@return CS.UnityEngine.Transfrom
function XPlanetCamera:RayRoleCast()
return self:RayCast(CS.UnityEngine.LayerMask.GetMask(HomeSceneLayerMask.HomeCharacter))
end
---射线检测场景所有层级对象
---@return CS.UnityEngine.Transfrom
function XPlanetCamera:RayCast(mask)
local platform = CS.UnityEngine.Application.platform
local touchPosition = Vector3.zero
if platform == CS.UnityEngine.RuntimePlatform.WindowsEditor or
platform == CS.UnityEngine.RuntimePlatform.WindowsPlayer
then
touchPosition = Input.mousePosition
else
if Input.touchCount > 1 then
touchPosition = Vector3(Input.GetTouch(0).position.x, Input.GetTouch(0).position.y, 0)
else
touchPosition = Input.mousePosition
end
end
local ray = self._Camera:ScreenPointToRay(touchPosition)
if not mask then
mask = CS.UnityEngine.LayerMask.GetMask(HomeSceneLayerMask.HomeCharacter) | CS.UnityEngine.LayerMask.GetMask(HomeSceneLayerMask.Room)
end
local hit = self:GetCameraTransform():PhysicsRayCast(Vector3.zero, ray.direction, mask)
return hit
end
function XPlanetCamera:GetCameraCenterRay(mask)
local screen = CS.UnityEngine.Screen
local screenPointCenter = Vector3(screen.width / 2, screen.height / 2, 0)
local ray = self._Camera:ScreenPointToRay(screenPointCenter)
if not mask then
mask = CS.UnityEngine.LayerMask.GetMask(HomeSceneLayerMask.HomeCharacter) | CS.UnityEngine.LayerMask.GetMask(HomeSceneLayerMask.Room)
end
local hit = self:GetCameraTransform():PhysicsRayCast(Vector3.zero, ray.direction, mask)
return hit
end
---获取相机中心射线碰撞点坐标
---@return Vector3
function XPlanetCamera:GetCameraCenterRayPosition(mask)
local hit = self:GetCameraCenterRay(mask)
if hit then
return hit.position
end
return Vector3.forward
end
--endregion
--region 加载
---获取CameraRoot
function XPlanetCamera:GetCamera()
return self._Camera
end
---获取CameraRoot
function XPlanetCamera:GetTransform()
return self._Transform
end
---获取Camera
function XPlanetCamera:GetCameraTransform()
return self._Camera.transform
end
function XPlanetCamera:InitCamera()
if not self._Camera then
local go = self._Transform:LoadPrefab(XPlanetConfigs.GetCamPrefab())
if XTool.UObjIsNil(go) then
go = CS.UnityEngine.GameObject("_PlanetCamera")
end
go.transform:SetParent(self._Transform)
self._Camera = go:GetComponent("Camera")
if XTool.UObjIsNil(self._Camera) then
self._Camera = go:AddComponent(typeof(CS.UnityEngine.Camera))
self._Camera.usePhysicalProperties = true
end
-- 物理相机缩放处理
local screenWidth = CS.UnityEngine.Screen.width
local screenHeight = CS.UnityEngine.Screen.height
if screenWidth / screenHeight > 2 then -- 长屏适配
local oldSize = self._Camera.sensorSize
self._Camera.sensorSize = Vector2(43, oldSize.y)
end
-- 设置相机碰撞检测
go:AddComponent(typeof(CS.UnityEngine.PostProcessing.PostProcessingBehaviour))
local physicsRaycaster = go:GetComponent(typeof(CS.UnityEngine.EventSystems.PhysicsRaycaster))
if not physicsRaycaster then
physicsRaycaster = go:AddComponent(typeof(CS.UnityEngine.EventSystems.PhysicsRaycaster))
end
physicsRaycaster:SetEventMask(CS.UnityEngine.LayerMask.GetMask(HomeSceneLayerMask.HomeCharacter) | CS.UnityEngine.LayerMask.GetMask(HomeSceneLayerMask.Room))
self:ResetCamera()
end
local postProcessContainer = self._GameObject:GetComponent(typeof(CS.XPostProcessContainer))
if postProcessContainer then
postProcessContainer:SetPostProcessingProfile(self._Camera.gameObject)
end
end
--- 释放资源
---@return void
function XPlanetCamera:Release()
self:TryDestroy(self._GameObject)
self._GameObject = nil
self._Transform = nil
self._Camera = nil
self:_InitFreeModeParams()
self:_InitFollowModeParams()
end
--- 删除游戏物体
---@param obj UnityEngine.GameObject 物体
---@return boolean 是否销毁成功
function XPlanetCamera:TryDestroy(obj)
if XTool.UObjIsNil(obj) then
return false
end
XUiHelper.Destroy(obj)
obj = nil
return true
end
function XPlanetCamera:Exist()
return not XTool.UObjIsNil(self._GameObject)
end
--endregion
return XPlanetCamera