using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
// Sci-Fi Ship Controller. Copyright (c) 2018-2023 SCSM Pty Ltd. All rights reserved.
namespace SciFiShipController
{
///
/// Class containing common utility methods
///
public class SSCUtils
{
#region Readonly Strings
// Statis strings use more memory but can held to reduce GC
private static readonly string PrptyNameHDIntensity = "intensity";
#endregion
#region Animation
///
/// Return the state Id (hash) of a state in an Animation Controller.
/// NOTE: This does not test if the state exists in any of the layers,
/// and simply returns a hash of the name.
///
///
///
public static int GetAnimationStateId (string stateName)
{
return Animator.StringToHash(stateName);
}
///
/// Check if the Animator is valid and ready for use.
/// Avoids the (reoccurring) "Animator is not playing the AnimatorController" warning,
/// and ensures animator parameters can be fetched correctly.
/// This is the same as a method of the same name in Sticky3D Controller's S3DAnimParm struct.
///
///
///
public static bool IsAnimatorReady(Animator animator)
{
if (animator != null && animator.isActiveAndEnabled && animator.runtimeAnimatorController != null)
{
// NOTE: This will still raise 1 warning if the animator is not ready.
if (animator.GetCurrentAnimatorStateInfo(0).length == 0)
{
// Fix the condition that causes reoccurring "Animator is not playing the AnimatorController"
animator.Rebind();
}
return true;
}
else
{
return false;
}
}
#endregion
#region Animation Curves
///
/// Create an inverse AnimationCurve. This is useful if you want to get the time
/// at a particular value.
///
///
///
public static AnimationCurve InverseAnimCurve(AnimationCurve sourceAnimCurve)
{
AnimationCurve invAnimCurve = null;
if (sourceAnimCurve != null)
{
invAnimCurve = new AnimationCurve();
for (int k = 0; k < sourceAnimCurve.length; k++)
{
// Invert slopes of in/out tangents
float newInTangent = (Mathf.Abs(sourceAnimCurve.keys[k].inTangent) > Mathf.Epsilon ?
(1f / sourceAnimCurve.keys[k].inTangent) : 1000f);
float newOutTangent = (Mathf.Abs(sourceAnimCurve.keys[k].outTangent) > Mathf.Epsilon ?
(1f / sourceAnimCurve.keys[k].outTangent) : 1000f);
// Create new inverted keyframe
Keyframe keyframe = new Keyframe(sourceAnimCurve.keys[k].value, sourceAnimCurve.keys[k].time,
newInTangent, newOutTangent, sourceAnimCurve.keys[k].inWeight, sourceAnimCurve.keys[k].outWeight);
// Add the inverted keyframe to the curve
invAnimCurve.AddKey(keyframe);
}
}
return invAnimCurve;
}
#endregion
#region Enumerations
public enum ViewDirection : int
{
Unknown = 0,
InFront = 1,
Behind = 2,
OffLeftEdge = 3,
OffRightEdge = 4,
OffLowerEdge = 5,
OffUpperEdge = 6
}
#endregion
#region Camera Methods
///
/// Given a 3D point in worldscape, is it [potentially] in view of the supplied camera?
///
///
///
///
public static bool IsPointInCameraView(Camera camera, Vector3 worldSpacePosition)
{
if (camera != null)
{
Vector3 screenPosition = camera.WorldToScreenPoint(worldSpacePosition);
bool isBehindPlayer = screenPosition.z < 0.0f;
bool offLeftEdge = screenPosition.x < 0.0f;
bool offRightEdge = screenPosition.x > Screen.width;
bool offLowerEdge = screenPosition.y < 0.0f;
bool offUpperEdge = screenPosition.y > Screen.height;
return (!(isBehindPlayer || offLeftEdge || offRightEdge || offLowerEdge || offUpperEdge));
}
else { return false; }
}
///
/// Is the 3D world-space point with a offset viewport inside the camera's full screen area?
/// Any points of the viewport that are outside the screen area will also return false.
///
///
///
/// This is usually the screen resolution
/// The width and height as 0-1 values of the full viewSize
/// -1.0 to 1.0 with 0,0 as the centre of the screen
///
public static bool IsPointInScreenViewport(Camera camera, Vector3 worldSpacePosition, Vector2 screenSize, Vector2 viewportSize, Vector2 viewportOffset)
{
if (camera != null)
{
Vector3 screenPosition = camera.WorldToScreenPoint(worldSpacePosition);
// Check if behind or outside visble screen area
if (screenPosition.z < 0.0f || screenPosition.x < 0.0f || screenPosition.x > screenSize.x ||
screenPosition.y < 0.0f || screenPosition.y > screenSize.y) { return false; }
else
{
// Infront of camera
// Check 2D viewport x-axis
float vpWidth = screenSize.x * viewportSize.x;
float vpLeft = screenSize.x * 0.5f - (vpWidth * 0.5f) + (viewportOffset.x * screenSize.x);
float vpRight = vpLeft + vpWidth;
if (screenPosition.x < vpLeft || screenPosition.x > vpRight) { return false; }
else
{
// Check 2D viewport y-axis
float vpHeight = screenSize.y * viewportSize.y;
float vpBottom = screenSize.y * 0.5f - (vpHeight * 0.5f) + (viewportOffset.y * screenSize.y);
float vpTop = vpBottom + vpHeight;
if (screenPosition.y < vpBottom || screenPosition.y > vpTop) { return false; }
else { return true; }
}
}
}
else { return false; }
}
///
/// Get the location or "view direction" of a 3D world-space point relative to the camera
///
///
///
///
///
///
public static ViewDirection PointViewDirection(Camera camera, Vector3 worldSpacePosition, Vector2 screenSize)
{
if (camera != null)
{
Vector3 screenPosition = camera.WorldToScreenPoint(worldSpacePosition);
if (screenPosition.z < 0.0f) { return ViewDirection.Behind; }
else if (screenPosition.x < 0.0f) { return ViewDirection.OffLeftEdge; }
else if (screenPosition.x > screenSize.x) { return ViewDirection.OffRightEdge; }
else if (screenPosition.y < 0.0f) { return ViewDirection.OffLowerEdge; }
else if (screenPosition.y > screenSize.y) { return ViewDirection.OffUpperEdge; }
else { return ViewDirection.InFront; }
}
else { return ViewDirection.Unknown; }
}
#endregion
#region Raycast Methods
///
/// Update the minDistance and normal of closest object with a collider.
///
///
///
///
///
///
public static void GetClosestCollider(Ray ray, Vector3 direction, float lookDistance, ref float minDistance, ref Vector3 objectNormal, out RaycastHit raycastHit)
{
ray.direction = direction;
if (Physics.Raycast(ray, out raycastHit, lookDistance)) { if (raycastHit.distance < minDistance) { minDistance = raycastHit.distance; objectNormal = raycastHit.normal; } }
}
#endregion
#region UI and Canvas Methods
///
/// Find a canvas by name in the scene
///
///
///
public static Canvas FindCanvas(string canvasName)
{
Canvas canvas = null;
if (!string.IsNullOrEmpty(canvasName))
{
List