Boat Attack使用了Universal RP的许多新图形功能,可以用于探索 Universal RP 的使用方式和技巧。
您最多选择25个主题 主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 

493 行
19 KiB

using System;
using UnityEngine;
using UnityEditor.ShortcutManagement;
using UnityEngine.UIElements;
namespace UnityEditor.Rendering.LookDev
{
class CameraController : Manipulator
{
float m_StartZoom = 0.0f;
float m_ZoomSpeed = 0.0f;
float m_TotalMotion = 0.0f;
Vector3 m_MotionDirection = new Vector3();
float m_FlySpeedNormalized = .5f;
float m_FlySpeed = 1f;
float m_FlySpeedAccelerated = 0f;
const float m_FlySpeedMin = .01f;
const float m_FlySpeedMax = 2f;
//[TODO: check if necessary to add hability to deactivate acceleration]
const float k_FlyAcceleration = 1.1f;
bool m_ShiftBoostedFly = false;
bool m_InFlyMotion;
bool m_IsDragging;
ViewTool m_BehaviorState;
static TimeHelper s_Timer = new TimeHelper();
protected CameraState m_CameraState;
DisplayWindow m_Window;
protected Action m_Focused;
Rect screen => target.contentRect;
bool inFlyMotion
{
get => m_InFlyMotion;
set
{
if (value ^ m_InFlyMotion)
{
if (value)
{
s_Timer.Begin();
EditorApplication.update += UpdateMotion;
}
else
{
m_FlySpeedAccelerated = 0f;
m_MotionDirection = Vector3.zero;
m_ShiftBoostedFly = false;
EditorApplication.update -= UpdateMotion;
}
m_InFlyMotion = value;
}
}
}
float flySpeedNormalized
{
get => m_FlySpeedNormalized;
set
{
m_FlySpeedNormalized = Mathf.Clamp01(value);
float speed = Mathf.Lerp(m_FlySpeedMin, m_FlySpeedMax, m_FlySpeedNormalized);
// Round to nearest decimal: 2 decimal points when between [0.01, 0.1]; 1 decimal point when between [0.1, 10]; integral between [10, 99]
speed = (float)(System.Math.Round((double)speed, speed < 0.1f ? 2 : speed < 10f ? 1 : 0));
m_FlySpeed = Mathf.Clamp(speed, m_FlySpeedMin, m_FlySpeedMax);
}
}
float flySpeed
{
get => m_FlySpeed;
set => flySpeedNormalized = Mathf.InverseLerp(m_FlySpeedMin, m_FlySpeedMax, value);
}
virtual protected bool isDragging
{
get => m_IsDragging;
set
{
//As in scene view, stop dragging as first button is release in case of multiple button down
if (value ^ m_IsDragging)
{
if (value)
{
target.RegisterCallback<MouseMoveEvent>(OnMouseDrag);
target.CaptureMouse();
EditorGUIUtility.SetWantsMouseJumping(1);
}
else
{
EditorGUIUtility.SetWantsMouseJumping(0);
target.ReleaseMouse();
target.UnregisterCallback<MouseMoveEvent>(OnMouseDrag);
}
m_IsDragging = value;
}
}
}
public CameraController(CameraState cameraState, DisplayWindow window, Action focused)
{
m_CameraState = cameraState;
m_Window = window;
m_Focused = focused;
}
private void ResetCameraControl()
{
isDragging = false;
inFlyMotion = false;
m_BehaviorState = ViewTool.None;
}
protected virtual void OnScrollWheel(WheelEvent evt)
{
// See UnityEditor.SceneViewMotion.HandleScrollWheel
switch (m_BehaviorState)
{
case ViewTool.FPS: OnChangeFPSCameraSpeed(evt); break;
default: OnZoom(evt); break;
}
}
void OnMouseDrag(MouseMoveEvent evt)
{
switch (m_BehaviorState)
{
case ViewTool.Orbit: OnMouseDragOrbit(evt); break;
case ViewTool.FPS: OnMouseDragFPS(evt); break;
case ViewTool.Pan: OnMouseDragPan(evt); break;
case ViewTool.Zoom: OnMouseDragZoom(evt); break;
default: break;
}
}
void OnKeyDown(KeyDownEvent evt)
{
OnKeyDownFPS(evt);
OnKeyDownReset(evt);
}
void OnChangeFPSCameraSpeed(WheelEvent evt)
{
float scrollWheelDelta = evt.delta.y;
flySpeedNormalized -= scrollWheelDelta * .01f;
string cameraSpeedDisplayValue = flySpeed.ToString(flySpeed < 0.1f ? "F2" : flySpeed < 10f ? "F1" : "F0");
if (flySpeed < 0.1f)
cameraSpeedDisplayValue = cameraSpeedDisplayValue.TrimStart(new Char[] { '0' });
GUIContent cameraSpeedContent = EditorGUIUtility.TrTempContent(
$"{cameraSpeedDisplayValue}x");
m_Window.ShowNotification(cameraSpeedContent, .5f);
evt.StopPropagation();
}
void OnZoom(WheelEvent evt)
{
float scrollWheelDelta = evt.delta.y;
float relativeDelta = m_CameraState.viewSize * scrollWheelDelta * .015f;
const float deltaCutoff = .3f;
if (relativeDelta > 0 && relativeDelta < deltaCutoff)
relativeDelta = deltaCutoff;
else if (relativeDelta < 0 && relativeDelta > -deltaCutoff)
relativeDelta = -deltaCutoff;
m_CameraState.viewSize += relativeDelta;
evt.StopPropagation();
}
void OnMouseDragOrbit(MouseMoveEvent evt)
{
Quaternion rotation = m_CameraState.rotation;
rotation = Quaternion.AngleAxis(evt.mouseDelta.y * .003f * Mathf.Rad2Deg, rotation * Vector3.right) * rotation;
rotation = Quaternion.AngleAxis(evt.mouseDelta.x * .003f * Mathf.Rad2Deg, Vector3.up) * rotation;
m_CameraState.rotation = rotation;
evt.StopPropagation();
}
void OnMouseDragFPS(MouseMoveEvent evt)
{
Vector3 camPos = m_CameraState.pivot - m_CameraState.rotation * Vector3.forward * m_CameraState.distanceFromPivot;
Quaternion rotation = m_CameraState.rotation;
rotation = Quaternion.AngleAxis(evt.mouseDelta.y * .003f * Mathf.Rad2Deg, rotation * Vector3.right) * rotation;
rotation = Quaternion.AngleAxis(evt.mouseDelta.x * .003f * Mathf.Rad2Deg, Vector3.up) * rotation;
m_CameraState.rotation = rotation;
m_CameraState.pivot = camPos + rotation * Vector3.forward * m_CameraState.distanceFromPivot;
evt.StopPropagation();
}
void OnMouseDragPan(MouseMoveEvent evt)
{
//[TODO: fix WorldToScreenPoint and ScreenToWorldPoint
var screenPos = m_CameraState.QuickProjectPivotInScreen(screen);
screenPos += new Vector3(evt.mouseDelta.x, -evt.mouseDelta.y, 0);
//Vector3 newWorldPos = m_CameraState.ScreenToWorldPoint(screen, screenPos);
Vector3 newWorldPos = m_CameraState.QuickReprojectionWithFixedFOVOnPivotPlane(screen, screenPos);
Vector3 worldDelta = newWorldPos - m_CameraState.pivot;
worldDelta *= EditorGUIUtility.pixelsPerPoint;
if (evt.shiftKey)
worldDelta *= 4;
m_CameraState.pivot += worldDelta;
evt.StopPropagation();
}
void OnMouseDragZoom(MouseMoveEvent evt)
{
float zoomDelta = HandleUtility.niceMouseDeltaZoom * (evt.shiftKey ? 9 : 3);
m_TotalMotion += zoomDelta;
if (m_TotalMotion < 0)
m_CameraState.viewSize = m_StartZoom * (1 + m_TotalMotion * .001f);
else
m_CameraState.viewSize = m_CameraState.viewSize + zoomDelta * m_ZoomSpeed * .003f;
evt.StopPropagation();
}
void OnKeyDownReset(KeyDownEvent evt)
{
if (evt.keyCode == KeyCode.Escape)
ResetCameraControl();
evt.StopPropagation();
}
void OnKeyDownFPS(KeyDownEvent evt)
{
if (m_BehaviorState != ViewTool.FPS)
return;
//Note: Keydown is called in loop but between first occurence of the
// loop and laters, there is a small pause. To deal with this, we
// need to register the UpdateMovement function to the Editor update
KeyCombination combination;
if (GetKeyCombinationByID("3D Viewport/Fly Mode Forward", out combination) && combination.Match(evt))
RegisterMotionChange(Vector3.forward, evt);
if (GetKeyCombinationByID("3D Viewport/Fly Mode Backward", out combination) && combination.Match(evt))
RegisterMotionChange(Vector3.back, evt);
if (GetKeyCombinationByID("3D Viewport/Fly Mode Left", out combination) && combination.Match(evt))
RegisterMotionChange(Vector3.left, evt);
if (GetKeyCombinationByID("3D Viewport/Fly Mode Right", out combination) && combination.Match(evt))
RegisterMotionChange(Vector3.right, evt);
if (GetKeyCombinationByID("3D Viewport/Fly Mode Up", out combination) && combination.Match(evt))
RegisterMotionChange(Vector3.up, evt);
if (GetKeyCombinationByID("3D Viewport/Fly Mode Down", out combination) && combination.Match(evt))
RegisterMotionChange(Vector3.down, evt);
}
void OnKeyUpFPS(KeyUpEvent evt)
{
if (m_BehaviorState != ViewTool.FPS)
return;
KeyCombination combination;
if (GetKeyCombinationByID("3D Viewport/Fly Mode Forward", out combination) && combination.Match(evt))
RegisterMotionChange(Vector3.back, evt);
if (GetKeyCombinationByID("3D Viewport/Fly Mode Backward", out combination) && combination.Match(evt))
RegisterMotionChange(Vector3.forward, evt);
if (GetKeyCombinationByID("3D Viewport/Fly Mode Left", out combination) && combination.Match(evt))
RegisterMotionChange(Vector3.right, evt);
if (GetKeyCombinationByID("3D Viewport/Fly Mode Right", out combination) && combination.Match(evt))
RegisterMotionChange(Vector3.left, evt);
if (GetKeyCombinationByID("3D Viewport/Fly Mode Up", out combination) && combination.Match(evt))
RegisterMotionChange(Vector3.down, evt);
if (GetKeyCombinationByID("3D Viewport/Fly Mode Down", out combination) && combination.Match(evt))
RegisterMotionChange(Vector3.up, evt);
}
void RegisterMotionChange<T>(Vector3 direction, KeyboardEventBase<T> evt)
where T : KeyboardEventBase<T>, new()
{
m_ShiftBoostedFly = evt.shiftKey;
m_MotionDirection = new Vector3(
Mathf.Clamp(m_MotionDirection.x + direction.x, -1, 1),
Mathf.Clamp(m_MotionDirection.y + direction.y, -1, 1),
Mathf.Clamp(m_MotionDirection.z + direction.z, -1, 1));
inFlyMotion = m_MotionDirection.sqrMagnitude != 0f;
evt.StopPropagation();
}
Vector3 GetMotionDirection()
{
var deltaTime = s_Timer.Update();
Vector3 result;
float speed = (m_ShiftBoostedFly ? 5 * flySpeed : flySpeed);
if (m_FlySpeedAccelerated == 0)
m_FlySpeedAccelerated = 9;
else
m_FlySpeedAccelerated *= Mathf.Pow(k_FlyAcceleration, deltaTime);
result = m_MotionDirection.normalized * m_FlySpeedAccelerated * speed * deltaTime;
return result;
}
void UpdateMotion()
{
if (Mathf.Approximately(m_MotionDirection.sqrMagnitude, 0f))
inFlyMotion = false;
else
m_CameraState.pivot += m_CameraState.rotation * GetMotionDirection();
}
bool GetKeyCombinationByID(string ID, out KeyCombination combination)
{
var sequence = ShortcutManager.instance.GetShortcutBinding(ID).keyCombinationSequence.GetEnumerator();
if (sequence.MoveNext()) //have a first entry
{
combination = new KeyCombination(sequence.Current);
return true;
}
else
{
combination = default;
return false;
}
}
void OnMouseUp(MouseUpEvent evt)
{
ResetCameraControl();
evt.StopPropagation();
}
void OnMouseDown(MouseDownEvent evt)
{
bool onMac = Application.platform == RuntimePlatform.OSXEditor;
if (evt.button == 2)
m_BehaviorState = ViewTool.Pan;
else if (evt.button == 0 && evt.ctrlKey && onMac || evt.button == 1 && evt.altKey)
{
m_BehaviorState = ViewTool.Zoom;
m_StartZoom = m_CameraState.viewSize;
m_ZoomSpeed = Mathf.Max(Mathf.Abs(m_StartZoom), .3f);
m_TotalMotion = 0;
}
else if (evt.button == 0)
m_BehaviorState = ViewTool.Orbit;
else if (evt.button == 1 && !evt.altKey)
m_BehaviorState = ViewTool.FPS;
// see also SceneView.HandleClickAndDragToFocus()
if (evt.button == 1 && onMac)
m_Window.Focus();
target.Focus(); //required for keyboard event
isDragging = true;
evt.StopPropagation();
m_Focused?.Invoke();
}
protected override void RegisterCallbacksOnTarget()
{
target.focusable = true; //prerequisite for being focusable and recerive keydown events
target.RegisterCallback<MouseUpEvent>(OnMouseUp);
target.RegisterCallback<MouseDownEvent>(OnMouseDown);
target.RegisterCallback<WheelEvent>(OnScrollWheel);
target.RegisterCallback<KeyDownEvent>(OnKeyDown);
target.RegisterCallback<KeyUpEvent>(OnKeyUpFPS);
}
protected override void UnregisterCallbacksFromTarget()
{
target.UnregisterCallback<MouseUpEvent>(OnMouseUp);
target.UnregisterCallback<MouseDownEvent>(OnMouseDown);
target.UnregisterCallback<WheelEvent>(OnScrollWheel);
target.UnregisterCallback<KeyDownEvent>(OnKeyDown);
target.UnregisterCallback<KeyUpEvent>(OnKeyUpFPS);
}
struct KeyCombination
{
KeyCode key;
EventModifiers modifier;
public bool shiftOnLastMatch;
public KeyCombination(UnityEditor.ShortcutManagement.KeyCombination shortcutCombination)
{
key = shortcutCombination.keyCode;
modifier = EventModifiers.None;
if ((shortcutCombination.modifiers & ShortcutModifiers.Shift) != 0)
modifier |= EventModifiers.Shift;
if ((shortcutCombination.modifiers & ShortcutModifiers.Alt) != 0)
modifier |= EventModifiers.Alt;
if ((shortcutCombination.modifiers & ShortcutModifiers.Action) != 0)
{
if (Application.platform == RuntimePlatform.OSXEditor || Application.platform == RuntimePlatform.OSXPlayer)
modifier |= EventModifiers.Command;
else
modifier |= EventModifiers.Control;
}
shiftOnLastMatch = false;
}
//atLeastModifier allow case were A is required but event provide shift+A
public bool Match(IKeyboardEvent evt, bool atLeastForModifier = true)
{
shiftOnLastMatch = evt.shiftKey;
if (atLeastForModifier)
return key == evt.keyCode && modifier == (evt.modifiers & modifier);
else
return key == evt.keyCode && modifier == evt.modifiers;
}
}
struct TimeHelper
{
long lastTime;
public void Begin() => lastTime = System.DateTime.Now.Ticks;
public float Update()
{
float deltaTime = (System.DateTime.Now.Ticks - lastTime) / 10000000.0f;
lastTime = System.DateTime.Now.Ticks;
return deltaTime;
}
}
}
class SwitchableCameraController : CameraController
{
CameraState m_FirstView;
CameraState m_SecondView;
ViewIndex m_CurrentViewIndex;
bool switchedDrag = false;
bool switchedWheel = false;
public SwitchableCameraController(CameraState cameraStateFirstView, CameraState cameraStateSecondView, DisplayWindow window, Action<ViewIndex> focused)
: base(cameraStateFirstView, window, null)
{
m_FirstView = cameraStateFirstView;
m_SecondView = cameraStateSecondView;
m_CurrentViewIndex = ViewIndex.First;
m_Focused = () => focused?.Invoke(m_CurrentViewIndex);
}
void SwitchTo(ViewIndex index)
{
CameraState stateToSwitch;
switch (index)
{
case ViewIndex.First:
stateToSwitch = m_FirstView;
break;
case ViewIndex.Second:
stateToSwitch = m_SecondView;
break;
default:
throw new ArgumentException("Unknown ViewIndex");
}
if (stateToSwitch != m_CameraState)
m_CameraState = stateToSwitch;
m_CurrentViewIndex = index;
}
public void SwitchUntilNextEndOfDrag()
{
switchedDrag = true;
SwitchTo(ViewIndex.Second);
}
override protected bool isDragging
{
get => base.isDragging;
set
{
bool switchBack = false;
if (switchedDrag && base.isDragging && !value)
switchBack = true;
base.isDragging = value;
if (switchBack)
SwitchTo(ViewIndex.First);
}
}
public void SwitchUntilNextWheelEvent()
{
switchedWheel = true;
SwitchTo(ViewIndex.Second);
}
protected override void OnScrollWheel(WheelEvent evt)
{
base.OnScrollWheel(evt);
if (switchedWheel)
SwitchTo(ViewIndex.First);
}
}
}