This commit is contained in:
SoulliesOfficial
2025-12-17 04:19:38 -05:00
parent 7c1cb7e8e1
commit d15957c719
4315 changed files with 8260710 additions and 2940 deletions

View File

@@ -0,0 +1,38 @@
using UnityEngine;
namespace GraphicsCat
{
public class AutoRotate : MonoBehaviour, IMGUIDockable
{
public bool autoRotateEnabled = true;
public Vector3 autoRotateSpeed = new Vector3(0, 5, 0);
public bool guiEnabled = false;
Transform m_Transform;
void Start()
{
m_Transform = transform;
if (guiEnabled)
IMGUIDock.topRight.DockGUI(this);
}
void Update()
{
if (autoRotateEnabled)
{
m_Transform.Rotate(autoRotateSpeed * Time.deltaTime, Space.Self);
// m_Transform.localEulerAngles = m_Transform.localEulerAngles + autoRotateSpeed * Time.deltaTime;
}
}
public void OnDockGUI()
{
GUILayout.BeginHorizontal();
if (GUILayout.Button($"Auto Rotate {(autoRotateEnabled ? "O" : "X")}"))
autoRotateEnabled = !autoRotateEnabled;
GUILayout.EndHorizontal();
}
}
}

View File

@@ -0,0 +1,19 @@
fileFormatVersion: 2
guid: 64d9c4976bbadb340bad62090109c21d
timeCreated: 1535425205
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 328765
packageName: LitPlus - URP Enhanced Lit Shader
packageVersion: 1.0.1
assetPath: Assets/GraphicsCat/DemoShared/Scripts/AutoRotate.cs
uploadId: 820558

View File

@@ -0,0 +1,531 @@
using System;
using UnityEngine;
namespace GraphicsCat
{
public struct CameraMovementInput
{
public Vector3 translationDelta;
public Vector3 rotationDelta;
public float zoomDelta;
public bool isDragging;
}
public partial class CameraRig : MonoBehaviour, IMGUIDockable
{
public enum RigType
{
Free,
TopDown,
Locked,
}
const int k_SpaceHeight = 10;
[Space(k_SpaceHeight)]
public RigType rigType = RigType.Free;
[Space(k_SpaceHeight)]
[Range(0, 20)]
public float moveSpeed = 6f;
[Range(0, 0.2f)]
public float rotateSpeed = 0.1f;
[Range(0, 200f)]
public float zoomSpeed = 40f;
[Space(k_SpaceHeight)]
public Transform targetTransform;
[Space(k_SpaceHeight)]
public bool guiEnabled = true;
private Transform m_CachedTransform;
private Vector3 m_CameraInitialPosition;
private Quaternion m_CameraInitialRotation;
private Vector3 m_TargetInitialPosition;
private Quaternion m_TargetInitialRotation;
private Touch m_MovementTouchInitial;
private Touch m_MovementTouchCurrent;
private Vector3 m_LastRotationMousePosition;
private Vector3 m_LastDragMousePosition;
private float m_MovementSpeedMultiplier;
private Vector3 m_TopDownPanDelta;
private bool m_IsInitialized;
private bool m_IsRigTypeSwitcherVisible = false;
private void ProcessCameraMovement(CameraMovementInput input)
{
bool isFreeType = (rigType == RigType.Free);
bool isTopDownType = (rigType == RigType.TopDown);
if (input.translationDelta != Vector3.zero)
{
ApplyMovementDelta(input.translationDelta);
}
if (input.rotationDelta != Vector3.zero && targetTransform != null)
{
if (isFreeType)
{
OrbitAroundTarget(input.rotationDelta);
}
}
if (input.zoomDelta != 0f)
{
if (isFreeType && targetTransform != null)
{
AdjustDistanceToTarget(input.zoomDelta);
}
else if (isTopDownType)
{
Vector3 zoomDeltaVector = m_CachedTransform.forward * input.zoomDelta;
m_CachedTransform.position += zoomDeltaVector;
}
}
}
private void ApplyMovementDelta(Vector3 delta)
{
m_CachedTransform.position += delta;
if (targetTransform != null)
targetTransform.position += delta;
}
private void OrbitAroundTarget(Vector3 delta)
{
if (targetTransform == null) return;
m_CachedTransform.RotateAround(targetTransform.position, Vector3.up, delta.x);
m_CachedTransform.RotateAround(targetTransform.position, -m_CachedTransform.right, delta.y);
}
private void RotateCamera(Vector3 delta)
{
if (targetTransform == null) return;
Vector3 referenceUp = Vector3.up;
if (Vector3.Dot(m_CachedTransform.forward, Vector3.up) > 0.99f || Vector3.Dot(m_CachedTransform.forward, Vector3.up) < -0.99f)
referenceUp = m_CachedTransform.right;
Vector3 cameraRight = Vector3.Cross(referenceUp, m_CachedTransform.forward).normalized;
Vector3 cameraUp = Vector3.Cross(m_CachedTransform.forward, cameraRight).normalized;
Quaternion horizontalQuat = Quaternion.AngleAxis(delta.x, cameraUp);
Quaternion verticalQuat = Quaternion.AngleAxis(delta.y, cameraRight);
Quaternion totalRotation = horizontalQuat * verticalQuat;
Vector3 cameraToTarget = targetTransform.position - m_CachedTransform.position;
Vector3 newCameraToTarget = totalRotation * cameraToTarget;
targetTransform.position = m_CachedTransform.position + newCameraToTarget;
m_CachedTransform.LookAt(targetTransform.position);
}
private void AdjustDistanceToTarget(float delta)
{
if (targetTransform == null) return;
Vector3 cameraToTarget = m_CachedTransform.position - targetTransform.position;
float newDistance = Mathf.Max(cameraToTarget.magnitude + delta, 0.1f);
m_CachedTransform.position = targetTransform.position + cameraToTarget.normalized * newDistance;
}
private void ResetToInitialState()
{
m_CachedTransform.SetPositionAndRotation(m_CameraInitialPosition, m_CameraInitialRotation);
if (targetTransform != null)
targetTransform.SetPositionAndRotation(m_TargetInitialPosition, m_TargetInitialRotation);
}
public void OnDockGUI()
{
GUILayout.BeginHorizontal();
{
if (m_IsRigTypeSwitcherVisible)
{
var btnText = GetRigTypeDescription(rigType);
if (GUILayout.Button(btnText))
rigType = GetNextRigType(rigType);
}
bool defaultStatus = true;
defaultStatus &= (m_CachedTransform.position == m_CameraInitialPosition);
defaultStatus &= (m_CachedTransform.rotation == m_CameraInitialRotation);
GUI.enabled = !defaultStatus;
if (GUILayout.Button("Reset Camera"))
ResetToInitialState();
GUI.enabled = true;
}
GUILayout.EndHorizontal();
}
private void Start()
{
m_CachedTransform = transform;
m_CameraInitialPosition = m_CachedTransform.position;
m_CameraInitialRotation = m_CachedTransform.rotation;
if (targetTransform != null)
{
m_TargetInitialPosition = targetTransform.position;
m_TargetInitialRotation = targetTransform.rotation;
}
m_MovementTouchInitial.fingerId = int.MinValue;
m_IsInitialized = true;
UpdateDockGUI();
}
private void OnValidate()
{
UpdateDockGUI();
}
private void UpdateDockGUI()
{
if (m_IsInitialized == false)
return;
if (guiEnabled)
IMGUIDock.topRight.DockGUI(this);
else
IMGUIDock.topRight.UndockGUI(this);
}
private void Update()
{
if (rigType == RigType.Locked)
return;
if (Application.isMobilePlatform)
HandleMobileInput();
else
HandleDesktopInput();
}
private Vector3 CalculateWorldPanDelta(Vector2 screenDelta)
{
var cam = GetComponent<Camera>();
if (cam == null) return Vector3.zero;
float distance;
if (rigType == RigType.Free && targetTransform != null)
{
distance = Vector3.Distance(m_CachedTransform.position, targetTransform.position);
}
else if (rigType == RigType.TopDown)
{
float camHeight = m_CachedTransform.position.y;
float forwardY = m_CachedTransform.forward.y;
if (Mathf.Abs(forwardY) < 1e-3 || Mathf.Abs(camHeight) < 1e-3)
{
distance = camHeight;
}
else
{
distance = camHeight / Mathf.Abs(forwardY);
}
}
else
{
return Vector3.zero;
}
float halfFOV = cam.fieldOfView * 0.5f;
float worldHeightAtDistance = 2f * distance * Mathf.Tan(halfFOV * Mathf.Deg2Rad);
float worldWidthAtDistance = worldHeightAtDistance * cam.aspect;
float percentX = screenDelta.x / Screen.width;
float percentY = screenDelta.y / Screen.height;
Vector3 worldDelta = Vector3.zero;
worldDelta -= m_CachedTransform.right * (percentX * worldWidthAtDistance);
if (rigType == RigType.Free)
{
worldDelta -= m_CachedTransform.up * (percentY * worldHeightAtDistance);
}
else if (rigType == RigType.TopDown)
{
worldDelta -= GetHorizontalForwardVector() * (percentY * worldHeightAtDistance);
}
return worldDelta;
}
private void HandleDesktopInput()
{
var input = new CameraMovementInput();
var isTopDownType = rigType == RigType.TopDown;
var isFreeType = (rigType == RigType.Free);
var deltaTime = Time.smoothDeltaTime;
Vector3 keyboardMovement = Vector3.zero;
if (isFreeType && targetTransform != null)
{
if (Input.GetKey(KeyCode.W)) keyboardMovement += m_CachedTransform.forward;
if (Input.GetKey(KeyCode.S)) keyboardMovement -= m_CachedTransform.forward;
if (Input.GetKey(KeyCode.D)) keyboardMovement += m_CachedTransform.right;
if (Input.GetKey(KeyCode.A)) keyboardMovement -= m_CachedTransform.right;
if (Input.GetKey(KeyCode.E)) keyboardMovement += Vector3.up;
if (Input.GetKey(KeyCode.Q)) keyboardMovement -= Vector3.up;
}
else if (isTopDownType)
{
if (Input.GetKey(KeyCode.Q)) input.zoomDelta += moveSpeed * deltaTime;
if (Input.GetKey(KeyCode.E)) input.zoomDelta -= moveSpeed * deltaTime;
if (Input.GetKey(KeyCode.W)) keyboardMovement += GetHorizontalForwardVector();
if (Input.GetKey(KeyCode.S)) keyboardMovement -= GetHorizontalForwardVector();
if (Input.GetKey(KeyCode.D)) keyboardMovement += m_CachedTransform.right;
if (Input.GetKey(KeyCode.A)) keyboardMovement -= m_CachedTransform.right;
}
if (keyboardMovement != Vector3.zero)
{
m_MovementSpeedMultiplier += deltaTime;
input.translationDelta = keyboardMovement.normalized * moveSpeed * deltaTime * m_MovementSpeedMultiplier;
}
else
{
m_MovementSpeedMultiplier = 1;
}
if (isFreeType && targetTransform != null)
{
if (Input.GetMouseButtonDown(0) || Input.GetMouseButtonDown(1))
m_LastRotationMousePosition = Input.mousePosition;
if (Input.GetMouseButtonDown(2))
m_LastDragMousePosition = Input.mousePosition;
if (Input.GetMouseButton(0))
{
var rotateMouseDelta = (Input.mousePosition - m_LastRotationMousePosition);
if (rotateMouseDelta.sqrMagnitude > float.Epsilon)
{
input.rotationDelta = rotateMouseDelta * rotateSpeed;
m_LastRotationMousePosition = Input.mousePosition;
}
}
else if (Input.GetMouseButton(1))
{
var rotateMouseDelta = (Input.mousePosition - m_LastRotationMousePosition);
if (rotateMouseDelta.sqrMagnitude > float.Epsilon)
{
var adjustedDelta = new Vector3(rotateMouseDelta.x * rotateSpeed, -rotateMouseDelta.y * rotateSpeed, 0);
RotateCamera(adjustedDelta * 0.5f);
m_LastRotationMousePosition = Input.mousePosition;
}
}
else if (Input.GetMouseButton(2))
{
var mouseDelta = (Input.mousePosition - m_LastDragMousePosition);
if (mouseDelta.sqrMagnitude > float.Epsilon)
{
input.translationDelta = CalculateWorldPanDelta(mouseDelta);
input.isDragging = true;
m_LastDragMousePosition = Input.mousePosition;
}
}
}
else if (isTopDownType)
{
if (Input.GetMouseButtonDown(0) || Input.GetMouseButtonDown(1) || Input.GetMouseButtonDown(2))
m_LastDragMousePosition = Input.mousePosition;
if (Input.GetMouseButton(0) || Input.GetMouseButton(1) || Input.GetMouseButton(2))
{
var mouseDelta = (Input.mousePosition - m_LastDragMousePosition);
if (mouseDelta.sqrMagnitude > float.Epsilon)
{
input.translationDelta = CalculateWorldPanDelta(mouseDelta);
input.isDragging = true;
m_LastDragMousePosition = Input.mousePosition;
}
}
}
if (Input.mouseScrollDelta.y != 0 && IsMouseInViewPort())
{
if (isFreeType && targetTransform != null)
{
input.zoomDelta = -(Input.mouseScrollDelta.y * zoomSpeed * Time.deltaTime);
}
else if (isTopDownType)
{
input.zoomDelta = Input.mouseScrollDelta.y * zoomSpeed * Time.deltaTime;
}
}
ProcessCameraMovement(input);
}
private void HandleMobileInput()
{
if (rigType == RigType.Free)
HandleFreeMobileInput();
else if (rigType == RigType.TopDown)
HandleTopDownMobileInput();
}
private void HandleFreeMobileInput()
{
if (targetTransform == null) return;
var input = new CameraMovementInput();
var touches = Input.touches;
if (touches.Length == 2)
{
Touch t0 = touches[0];
Touch t1 = touches[1];
Vector2 t0PrevPos = t0.position - t0.deltaPosition;
Vector2 t1PrevPos = t1.position - t1.deltaPosition;
float prevTouchDeltaMag = (t0PrevPos - t1PrevPos).magnitude;
float currentTouchDeltaMag = (t0.position - t1.position).magnitude;
float pinchDelta = prevTouchDeltaMag - currentTouchDeltaMag;
input.zoomDelta = pinchDelta * zoomSpeed * Time.smoothDeltaTime * 0.002f;
}
else
{
foreach (var touch in touches)
{
if (touch.phase == TouchPhase.Began)
m_MovementTouchInitial = touch;
else if (touch.phase == TouchPhase.Ended)
{
if (m_MovementTouchInitial.fingerId == touch.fingerId)
m_MovementTouchInitial.fingerId = int.MinValue;
}
}
m_MovementTouchCurrent.fingerId = int.MinValue;
foreach (var touch in touches)
{
if (touch.fingerId == m_MovementTouchInitial.fingerId)
m_MovementTouchCurrent = touch;
}
if (m_MovementTouchCurrent.fingerId != int.MinValue)
{
var rotateStrength = (m_MovementTouchCurrent.position - m_MovementTouchInitial.position) / Screen.width;
if (rotateStrength != Vector2.zero)
{
var deltaTime = Time.smoothDeltaTime;
var rotationDelta = 1000f * 20 * deltaTime * rotateSpeed * rotateStrength;
input.rotationDelta = new Vector3(rotationDelta.x, rotationDelta.y, 0);
}
}
}
ProcessCameraMovement(input);
}
private void HandleTopDownMobileInput()
{
var input = new CameraMovementInput();
var touches = Input.touches;
foreach (var touch in touches)
{
if (touch.phase == TouchPhase.Began)
m_MovementTouchInitial = touch;
else if (touch.phase == TouchPhase.Ended)
{
if (m_MovementTouchInitial.fingerId == touch.fingerId)
m_MovementTouchInitial.fingerId = int.MinValue;
}
}
m_MovementTouchCurrent.fingerId = int.MinValue;
foreach (var touch in touches)
{
if (touch.fingerId == m_MovementTouchInitial.fingerId)
m_MovementTouchCurrent = touch;
}
if (m_MovementTouchCurrent.fingerId != int.MinValue)
{
if (m_MovementTouchCurrent.deltaPosition != Vector2.zero)
{
input.translationDelta = CalculateWorldPanDelta(m_MovementTouchCurrent.deltaPosition);
input.isDragging = true;
}
}
else
{
m_TopDownPanDelta *= Mathf.Lerp(1f, 0f, Time.smoothDeltaTime * 5);
input.translationDelta = m_TopDownPanDelta;
}
ProcessCameraMovement(input);
}
private bool IsMouseInViewPort()
{
if (Input.mousePosition.x < 0 || Input.mousePosition.x > Screen.width ||
Input.mousePosition.y < 0 || Input.mousePosition.y > Screen.height)
{
return false;
}
return true;
}
private Vector3 GetScreenSpaceUpVector()
{
var forward = m_CachedTransform.forward;
if (Mathf.Abs(forward.y) < 0.01)
return m_CachedTransform.up;
else
{
forward.y = 0;
return forward.normalized;
}
}
private Vector3 GetHorizontalForwardVector()
{
var forward = m_CachedTransform.forward;
forward.y = 0;
return forward.normalized;
}
private string GetRigTypeDescription(RigType type)
{
var description = "Camera: Unknown Type";
switch (type)
{
case RigType.Free: description = "Camera: Free"; break;
case RigType.TopDown: description = "Camera: TopDown"; break;
case RigType.Locked: description = "Camera: Locked"; break;
}
return description;
}
private RigType GetNextRigType(RigType currentRigType)
{
var rigTypeCount = Enum.GetValues(typeof(RigType)).Length;
return (RigType)(((int)currentRigType + 1) % rigTypeCount);
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: ca55bded83820ca4598ef9e7ced323db
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 328765
packageName: LitPlus - URP Enhanced Lit Shader
packageVersion: 1.0.1
assetPath: Assets/GraphicsCat/DemoShared/Scripts/CameraRig.cs
uploadId: 820558

View File

@@ -0,0 +1,99 @@
using UnityEngine;
namespace GraphicsCat
{
[ExecuteAlways]
public class LookAtMainCamera : MonoBehaviour
{
public enum AlignMode { Full, YAxisOnly }
[Tooltip("Full: face camera on all axes. YAxisOnly: only rotate around world Y to face camera.")]
public AlignMode alignMode = AlignMode.Full;
[Tooltip("Local self-rotation speed in degrees per second on each axis.")]
public Vector3 selfRotationDegreesPerSecond = Vector3.zero;
[Tooltip("Optional euler offset (applied after aligning to camera).")]
public Vector3 rotationOffsetEuler = Vector3.zero;
[Tooltip("Use LateUpdate for camera-following (recommended if camera moves in Update).")]
public bool useLateUpdate = true;
[Tooltip("If true, the look direction will be inverted (useful if your mesh faces -Z by default).")]
public bool invertLookDirection = true;
[Tooltip("If set, this camera will be used instead of Camera.main.")]
public Camera overrideCamera;
// cached camera transform (will be updated if main camera changes)
Transform targetCameraTransform;
void OnEnable()
{
TryCacheCamera();
}
void TryCacheCamera()
{
if (overrideCamera != null)
{
targetCameraTransform = overrideCamera.transform;
return;
}
if (Camera.main != null)
targetCameraTransform = Camera.main.transform;
}
void Update()
{
if (!useLateUpdate)
AlignToCameraAndSelfRotate();
}
void LateUpdate()
{
if (useLateUpdate)
AlignToCameraAndSelfRotate();
}
void AlignToCameraAndSelfRotate()
{
if (targetCameraTransform == null)
TryCacheCamera();
// 1) Align to camera
if (targetCameraTransform != null)
{
Vector3 camPos = targetCameraTransform.position;
Vector3 toCam = camPos - transform.position;
// optionally invert direction if front faces -Z
if (invertLookDirection)
toCam = -toCam;
if (alignMode == AlignMode.Full)
{
if (toCam.sqrMagnitude > 0.0001f)
transform.rotation = Quaternion.LookRotation(toCam.normalized, Vector3.up);
}
else // YAxisOnly
{
toCam.y = 0f;
if (toCam.sqrMagnitude > 0.0001f)
transform.rotation = Quaternion.LookRotation(toCam.normalized, Vector3.up);
}
// apply an optional rotation offset after facing the camera
if (rotationOffsetEuler != Vector3.zero)
transform.rotation *= Quaternion.Euler(rotationOffsetEuler);
}
// 2) Apply local self-rotation (spin) in local space
if (selfRotationDegreesPerSecond != Vector3.zero)
{
transform.Rotate(selfRotationDegreesPerSecond * Time.deltaTime, Space.Self);
}
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 822323b47cbe74b42910054c3bfb9579
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 328765
packageName: LitPlus - URP Enhanced Lit Shader
packageVersion: 1.0.1
assetPath: Assets/GraphicsCat/DemoShared/Scripts/LookAtMainCamera.cs
uploadId: 820558

View File

@@ -0,0 +1,96 @@
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
namespace GraphicsCat
{
[ExecuteAlways]
public class ShadowDistance : MonoBehaviour
{
public enum Mode
{
Static,
Dynamic
}
public Mode mode = Mode.Static;
[EnableIf(nameof(mode), CompareFunction.Equal, Mode.Static)]
[Range(1f, 100f)]
public float staticDistance = 10f;
[InspectorDisplayName("Dynamic Distance Target")]
[EnableIf(nameof(mode), CompareFunction.Equal, Mode.Dynamic)]
public GameObject dynamicDistanceTarget;
[InspectorDisplayName("Dynamic Distance Offset")]
[EnableIf(nameof(mode), CompareFunction.Equal, Mode.Dynamic)]
public float dynamicDistanceOffset = 0f;
float m_TargetHalfSize = 0;
void OnEnable()
{
UpdateShadowDistance();
UpdateDynamicDistanceTargetHalfSize();
}
void Update()
{
UpdateShadowDistance();
}
void OnValidate()
{
UpdateShadowDistance();
UpdateDynamicDistanceTargetHalfSize();
}
void UpdateShadowDistance()
{
if (mode == Mode.Static)
SetShadowDistance(staticDistance);
else
UpdateShadowDistanceByLookTarget();
}
void UpdateShadowDistanceByLookTarget()
{
var target = dynamicDistanceTarget;
if (target == null)
target = gameObject;
Camera mainCamera = Camera.main;
if (mainCamera == null)
return;
var targetDistance = (target.transform.position - mainCamera.transform.position).magnitude;
var shadowDistance = targetDistance + m_TargetHalfSize + dynamicDistanceOffset;
SetShadowDistance(shadowDistance);
}
float GetShadowDistance()
{
var uprAsset = GraphicsSettings.currentRenderPipeline as UniversalRenderPipelineAsset;
if (uprAsset != null)
return uprAsset.shadowDistance;
else
return QualitySettings.shadowDistance;
}
void SetShadowDistance(float val)
{
var urpAsset = GraphicsSettings.currentRenderPipeline as UniversalRenderPipelineAsset;
if (urpAsset != null)
urpAsset.shadowDistance = val;
else
QualitySettings.shadowDistance = val;
}
void UpdateDynamicDistanceTargetHalfSize()
{
var bounds = BoundsUtils.GetHierarchyBounds(dynamicDistanceTarget);
m_TargetHalfSize = bounds.extents.magnitude;
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 6e3c94ff415e5b6419fe6590448f1106
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 328765
packageName: LitPlus - URP Enhanced Lit Shader
packageVersion: 1.0.1
assetPath: Assets/GraphicsCat/DemoShared/Scripts/ShadowDistance.cs
uploadId: 820558

View File

@@ -0,0 +1,305 @@
using UnityEngine;
namespace GraphicsCat
{
public class SlideRotationController : MonoBehaviour, IMGUIDockable
{
private const float INERTIA_THRESHOLD = 0.1f; // Threshold to stop inertia rotation
[Header("Slide Rotation")]
[InspectorDisplayName("Enable")]
public bool slideRotationEnabled = false;
[InspectorDisplayName("Sensitivity")]
public Vector3 slideRotationSensitivity = new(0, 20, 0);
[InspectorDisplayName("Inertia Damping")]
[Range(0f, 1f)]
public float inertiaDamping = 0.8f; // 0 = no inertia, 1 = maximum inertia
[Header("Auto Rotation")]
[InspectorDisplayName("Enable")]
public bool autoRotationEnabled = true;
[InspectorDisplayName("Speed")]
public Vector3 autoRotationSpeed = new(0, 5, 0);
[Header("Other")]
public bool displayInGameGUI = false;
// Input state
private bool m_IsDragging = false;
private Vector3 m_LastPointerPosition = Vector3.zero;
private Vector3 m_PointerDelta = Vector3.zero;
private Transform m_Target = null;
// Touch input
private Touch m_MainTouch;
private bool m_IsTouchActive = false;
// Auto rotation
private bool m_WasAutoRotationEnabled;
private bool m_IsAutoRotationPaused = false;
// Inertia
private Vector3 m_CurrentInertia = Vector3.zero;
private Vector3 m_LastAngularVelocity = Vector3.zero;
private bool m_HasAngularMovement = false; // Track if there was actual angular movement
private void Start()
{
m_Target = transform;
IMGUIDock.topRight.DockGUI(this);
}
private void Update()
{
if (!slideRotationEnabled)
return;
m_PointerDelta = Vector3.zero;
HandleMouseInput();
if (Application.isMobilePlatform)
HandleTouchInput();
}
private void FixedUpdate()
{
Vector3 slideRotationDelta = Vector3.zero;
if (slideRotationEnabled)
{
// Handle manual rotation
if (m_PointerDelta != Vector3.zero)
{
slideRotationDelta = ComputeRotationDelta(m_PointerDelta.x, m_PointerDelta.y);
}
// Apply inertia when not actively rotating
else if (m_CurrentInertia.magnitude > INERTIA_THRESHOLD)
{
slideRotationDelta = m_CurrentInertia;
m_CurrentInertia *= inertiaDamping;
if (m_CurrentInertia.magnitude < INERTIA_THRESHOLD)
m_CurrentInertia = Vector3.zero;
}
// Apply manual rotation or inertia
if (slideRotationDelta != Vector3.zero)
{
Quaternion deltaRotation = Quaternion.Euler(slideRotationDelta * Time.fixedDeltaTime);
m_Target.localRotation = deltaRotation * m_Target.localRotation;
m_LastAngularVelocity = slideRotationDelta;
m_HasAngularMovement = true; // There was actual rotation this frame
}
else
{
// No rotation this frame
m_HasAngularMovement = false;
}
}
// Apply auto rotation
if (slideRotationDelta == Vector3.zero && autoRotationEnabled && !m_IsAutoRotationPaused)
{
ApplyAutoRotation();
}
}
/// <summary>
/// Apply auto rotation according to configured speed.
/// </summary>
private void ApplyAutoRotation()
{
Quaternion rotation = Quaternion.Euler(autoRotationSpeed * Time.fixedDeltaTime);
m_Target.localRotation = rotation * m_Target.localRotation;
}
/// <summary>
/// Compute rotation delta based on pointer movement.
/// </summary>
private Vector3 ComputeRotationDelta(float x, float y)
{
return Vector3.Scale(new Vector3(y, x, 0), -slideRotationSensitivity);
}
/// <summary>
/// Handle mouse input for slide rotation.
/// </summary>
private void HandleMouseInput()
{
if (Input.GetMouseButtonDown(0))
{
if (IsPointerOverTarget(Input.mousePosition))
{
m_IsDragging = true;
m_LastPointerPosition = Input.mousePosition;
m_CurrentInertia = Vector3.zero; // Reset inertia when starting drag
m_HasAngularMovement = false;
PauseAutoRotation();
}
}
else if (Input.GetMouseButtonUp(0))
{
if (m_IsDragging)
{
m_IsDragging = false;
// Only apply inertia if there was actual angular movement
if (m_HasAngularMovement)
ApplyInertia();
else
m_CurrentInertia = Vector3.zero; // No movement, no inertia
ResumeAutoRotation();
}
}
else if (Input.GetMouseButton(0) && m_IsDragging)
{
Vector3 currentPosition = Input.mousePosition;
m_PointerDelta = currentPosition - m_LastPointerPosition;
m_LastPointerPosition = currentPosition;
}
else
{
m_IsDragging = false;
}
}
/// <summary>
/// Handle touch input for slide rotation.
/// </summary>
private void HandleTouchInput()
{
int touchCount = Input.touchCount;
if (touchCount <= 0)
{
m_IsTouchActive = false;
return;
}
for (int i = 0; i < touchCount; i++)
{
Touch touch = Input.GetTouch(i);
switch (touch.phase)
{
case TouchPhase.Began:
if (IsPointerOverTarget(touch.position))
{
m_MainTouch = touch;
m_IsTouchActive = true;
m_LastPointerPosition = touch.position;
m_CurrentInertia = Vector3.zero; // Reset inertia when starting touch
m_HasAngularMovement = false;
PauseAutoRotation();
}
break;
case TouchPhase.Moved:
if (m_IsTouchActive && touch.fingerId == m_MainTouch.fingerId)
{
Vector3 currentTouchPosition = touch.position;
m_PointerDelta = currentTouchPosition - m_LastPointerPosition;
m_LastPointerPosition = currentTouchPosition;
}
break;
case TouchPhase.Ended:
case TouchPhase.Canceled:
if (m_IsTouchActive && touch.fingerId == m_MainTouch.fingerId)
{
m_IsTouchActive = false;
m_PointerDelta = Vector3.zero;
// Only apply inertia if there was actual angular movement
if (m_HasAngularMovement)
ApplyInertia();
else
m_CurrentInertia = Vector3.zero; // No movement, no inertia
ResumeAutoRotation();
}
break;
}
}
}
/// <summary>
/// Apply inertia based on the last angular velocity.
/// Inertia is directly proportional to the angular velocity at the moment of release.
/// </summary>
private void ApplyInertia()
{
// Only apply inertia if there was significant angular velocity
// This ensures inertia magnitude correlates with rotation speed at release
if (inertiaDamping > 0 && m_LastAngularVelocity.magnitude > INERTIA_THRESHOLD)
m_CurrentInertia = m_LastAngularVelocity * inertiaDamping;
else
m_CurrentInertia = Vector3.zero;
}
/// <summary>
/// Temporarily pause auto rotation during user interaction.
/// </summary>
private void PauseAutoRotation()
{
if (autoRotationEnabled)
{
m_WasAutoRotationEnabled = autoRotationEnabled;
autoRotationEnabled = false;
m_IsAutoRotationPaused = true;
}
}
/// <summary>
/// Resume auto rotation after user interaction ends.
/// </summary>
private void ResumeAutoRotation()
{
if (m_IsAutoRotationPaused)
{
autoRotationEnabled = m_WasAutoRotationEnabled;
m_IsAutoRotationPaused = false;
}
}
/// <summary>
/// Check if the given screen point is over this object using renderer bounds.
/// </summary>
private bool IsPointerOverTarget(Vector3 screenPoint)
{
Camera camera = Camera.main;
if (camera == null)
return false;
Renderer[] renderers = m_Target.GetComponentsInChildren<Renderer>();
if (renderers.Length == 0)
return false;
Ray ray = camera.ScreenPointToRay(screenPoint);
foreach (Renderer renderer in renderers)
{
if (renderer.enabled && renderer.bounds.IntersectRay(ray))
return true;
}
return false;
}
public void OnDockGUI()
{
if (!displayInGameGUI)
return;
GUILayout.BeginHorizontal();
if (GUILayout.Button("Slide Rotation " + (slideRotationEnabled ? "O" : "X")))
slideRotationEnabled = !slideRotationEnabled;
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
if (GUILayout.Button("Auto Rotation " + (autoRotationEnabled ? "O" : "X")))
autoRotationEnabled = !autoRotationEnabled;
GUILayout.EndHorizontal();
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: db0cfd0a7c12f76469f9da3ae9ea890c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 328765
packageName: LitPlus - URP Enhanced Lit Shader
packageVersion: 1.0.1
assetPath: Assets/GraphicsCat/DemoShared/Scripts/SlideRotationController.cs
uploadId: 820558

View File

@@ -0,0 +1,107 @@
using UnityEngine;
namespace GraphicsCat
{
public class SoftBoundaryFloating : MonoBehaviour
{
[Header("Floating Area")]
public Vector3 floatingAreaSize = new Vector3(5f, 5f, 5f);
[Tooltip("Start changing direction when close to boundary")]
public float boundaryBufferDistance = 0.5f;
[Header("Movement Settings")]
public float maxSpeed = 1f;
public float directionChangeSmoothness = 2f;
public Vector2 directionChangeInterval = new Vector2(2f, 5f);
private Vector3 _currentDirection;
private Vector3 _targetDirection;
private Vector3 _initialLocalPosition;
private float _nextDirectionChangeTime;
private void Start()
{
_initialLocalPosition = transform.localPosition;
_currentDirection = Random.insideUnitSphere.normalized;
_targetDirection = Random.insideUnitSphere.normalized;
ScheduleNextDirectionChange();
}
private void Update()
{
if (Time.time >= _nextDirectionChangeTime || IsNearBoundary())
{
_targetDirection = CalculateNewDirection();
ScheduleNextDirectionChange();
}
_currentDirection = Vector3.Slerp(
_currentDirection,
_targetDirection,
directionChangeSmoothness * Time.deltaTime
);
transform.localPosition += _currentDirection * maxSpeed * Time.deltaTime;
}
private bool IsNearBoundary()
{
Vector3 localPos = transform.localPosition - _initialLocalPosition;
float halfX = floatingAreaSize.x / 2f - boundaryBufferDistance;
float halfY = floatingAreaSize.y / 2f - boundaryBufferDistance;
float halfZ = floatingAreaSize.z / 2f - boundaryBufferDistance;
return Mathf.Abs(localPos.x) > halfX ||
Mathf.Abs(localPos.y) > halfY ||
Mathf.Abs(localPos.z) > halfZ;
}
private Vector3 CalculateNewDirection()
{
Vector3 localPos = transform.localPosition - _initialLocalPosition;
Vector3 awayFromEdge = Vector3.zero;
float weightX = Mathf.Clamp01((Mathf.Abs(localPos.x) - floatingAreaSize.x / 2f + boundaryBufferDistance) / boundaryBufferDistance);
float weightY = Mathf.Clamp01((Mathf.Abs(localPos.y) - floatingAreaSize.y / 2f + boundaryBufferDistance) / boundaryBufferDistance);
float weightZ = Mathf.Clamp01((Mathf.Abs(localPos.z) - floatingAreaSize.z / 2f + boundaryBufferDistance) / boundaryBufferDistance);
awayFromEdge.x = -Mathf.Sign(localPos.x) * weightX;
awayFromEdge.y = -Mathf.Sign(localPos.y) * weightY;
awayFromEdge.z = -Mathf.Sign(localPos.z) * weightZ;
Vector3 randomDir = Random.insideUnitSphere.normalized;
return (randomDir + awayFromEdge * 2f).normalized;
}
private void ScheduleNextDirectionChange()
{
_nextDirectionChangeTime = Time.time + Random.Range(
directionChangeInterval.x,
directionChangeInterval.y
);
}
// Correction: Gizmos position displays correctly in edit mode
private void OnDrawGizmosSelected()
{
Gizmos.color = Color.cyan;
// Key change: get the world matrix of the current object
Matrix4x4 gizmoMatrix = Matrix4x4.TRS(
transform.position,
transform.rotation,
transform.lossyScale
);
Gizmos.matrix = gizmoMatrix;
Gizmos.DrawWireCube(Vector3.zero, floatingAreaSize);
if (boundaryBufferDistance > 0)
{
Gizmos.color = Color.yellow;
Vector3 bufferSize = floatingAreaSize - Vector3.one * boundaryBufferDistance * 2f;
Gizmos.DrawWireCube(Vector3.zero, bufferSize);
}
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 305ecfd60dd56e344a675b8b415c585b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 328765
packageName: LitPlus - URP Enhanced Lit Shader
packageVersion: 1.0.1
assetPath: Assets/GraphicsCat/DemoShared/Scripts/SoftBoundaryFloating.cs
uploadId: 820558