356 lines
14 KiB
C#
356 lines
14 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using Drawing;
|
|
using Sirenix.OdinInspector;
|
|
using Sirenix.Serialization;
|
|
using Unity.Mathematics;
|
|
using UnityEngine.Serialization;
|
|
|
|
[Serializable]
|
|
public class EnvironmentObserver{
|
|
enum LabelDrawingLocation{
|
|
PlayerOffset,
|
|
HitLocation,
|
|
IntersectingLength,
|
|
}
|
|
|
|
enum ObserverGizmoDrawingCondition{
|
|
Always,
|
|
OnlyActive,
|
|
Never
|
|
}
|
|
|
|
public enum CastType{
|
|
Ray,
|
|
BoxOverlap,
|
|
SphereOverlap,
|
|
BoxCast,
|
|
SphereCast
|
|
}
|
|
|
|
[PropertySpace(0, 5), LabelWidth(60)]
|
|
public string label;
|
|
[PropertySpace(0, 10), LabelWidth(60)]
|
|
public CastType castType;
|
|
|
|
[Button(ButtonSizes.Large), GUIColor("@GetObserverStatusColorStatic(active, hit)"), PropertyOrder(-1), PropertySpace(5, 5)]
|
|
public void Active(){
|
|
active = !active;
|
|
}
|
|
|
|
[HideInInspector]
|
|
public bool active;
|
|
|
|
// Parameters for Cast cast types
|
|
[FoldoutGroup("Settings")] public float length;
|
|
[FoldoutGroup("Settings")] public Vector3 direction;
|
|
[FoldoutGroup("Settings")] public Vector3 offset;
|
|
[PropertySpace(0, 5), FoldoutGroup("Settings")] public LayerMask ignoreLayers = ~0;
|
|
|
|
[ShowIfGroup("Settings/CastsOnly", VisibleIf = "@castType == CastType.SphereCast || castType == CastType.SphereOverlap")]
|
|
[FoldoutGroup("Settings")] public float width;
|
|
|
|
// Parameters for Overlap cast types
|
|
[ShowIfGroup("Settings/3DOnly", VisibleIf = "@castType == CastType.BoxCast && castType != CastType.BoxOverlap")] [FoldoutGroup("Settings")]
|
|
public Vector3 size;
|
|
|
|
[ShowIfGroup("Settings/3DOnly")]
|
|
public Vector3 rotation;
|
|
|
|
[HideInInspector]
|
|
public RaycastHit hit;
|
|
|
|
[HideInInspector]
|
|
public Collider[] overlapHits;
|
|
|
|
[FoldoutGroup("Text")]
|
|
[BoxGroup("Text/Label")] public bool drawLabel;
|
|
[ShowInInspector, SerializeField] [BoxGroup("Text/Label")] LabelDrawingLocation labelTextLocation;
|
|
[BoxGroup("Text/Label")] public float labelSize;
|
|
[BoxGroup("Text/Label")] public Vector3 labelLocationOffset;
|
|
[BoxGroup("Text/Label")] public Vector3 labelRotationOffset;
|
|
|
|
[BoxGroup("Text/Hit")] public bool drawHitName;
|
|
[ShowInInspector, SerializeField] [BoxGroup("Text/Hit")] LabelDrawingLocation hitTextLocation;
|
|
[BoxGroup("Text/Hit")] public float hitTextSize;
|
|
[BoxGroup("Text/Hit")] public Vector3 hitLocationOffset;
|
|
[BoxGroup("Text/Hit")] public Vector3 hitRotationOffset;
|
|
[FoldoutGroup("Text"), SerializeField, ShowInInspector] ObserverGizmoDrawingCondition gizmoDrawingCondition;
|
|
|
|
[SerializeReference, PropertySpace(5, 5)]
|
|
public List<EnvironmentObserver> children;
|
|
|
|
// NOTE: I had a ref for a RaycastHit here that would correspond to hit but idk if it's needed.
|
|
public bool Evaluate(GameObject source){
|
|
if (active) {
|
|
// Remove player's layer from LayerMask.
|
|
ignoreLayers -= source.layer;
|
|
|
|
// Set some of the variables used later during casting
|
|
Vector3 relativeStart = source.transform.position + offset;
|
|
Vector3 relativeStartWithRotation = source.transform.position + source.transform.rotation * offset ;
|
|
|
|
switch (castType) {
|
|
case CastType.Ray:
|
|
Physics.Raycast(relativeStart, source.transform.rotation * direction, out hit, length, ignoreLayers);
|
|
break;
|
|
case CastType.BoxOverlap:
|
|
overlapHits = Physics.OverlapBox(relativeStartWithRotation, size / 2f,
|
|
source.transform.rotation * Quaternion.Euler(rotation), ignoreLayers);
|
|
|
|
if (overlapHits.Length > 0) {
|
|
return true;
|
|
};
|
|
|
|
break;
|
|
case CastType.SphereOverlap:
|
|
break;
|
|
case CastType.BoxCast:
|
|
// TODO: Make this not an if statement. Check that it works with NodeCanvas first
|
|
if (Physics.BoxCast(relativeStartWithRotation, size / 2f,
|
|
source.transform.rotation * Quaternion.Euler(rotation) * direction,
|
|
out hit, source.transform.rotation * Quaternion.Euler(rotation), length,
|
|
ignoreLayers)
|
|
) {
|
|
};
|
|
break;
|
|
case CastType.SphereCast:
|
|
// TODO: Make this not an if statement. Check that it works with NodeCanvas first
|
|
if (Physics.SphereCast(relativeStartWithRotation, width / 2f,
|
|
source.transform.rotation * Quaternion.Euler(rotation) * direction,
|
|
out hit, length,
|
|
ignoreLayers)
|
|
) {
|
|
};
|
|
break;
|
|
}
|
|
|
|
if (hit.transform != null) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public void DrawObserverGizmo(GameObject source){
|
|
if (gizmoDrawingCondition == ObserverGizmoDrawingCondition.Never ||
|
|
(gizmoDrawingCondition == ObserverGizmoDrawingCondition.OnlyActive ! & active)) {
|
|
return;
|
|
}
|
|
|
|
Vector3 relativeStart = source.transform.position + offset;
|
|
Vector3 relativeStartWithRotation = source.transform.position + source.transform.rotation * (offset);
|
|
|
|
// Setup the variables for boxcast, spherecast, etc
|
|
// Create an offset start point for the center of the wirebox, since gizmos are drawn with their pivot in the center.
|
|
Vector3 offsetWithRotationAndLength = (Quaternion.LookRotation(direction) * Quaternion.Euler(rotation)) * (Vector3.forward * (length / 2));
|
|
Vector3 offsetFromCenter = relativeStartWithRotation + source.transform.rotation * offsetWithRotationAndLength;
|
|
|
|
// Also create a rotation for use with the gizmos. Mainly just to shorten the lines
|
|
Quaternion gizmosRotation = source.transform.rotation * Quaternion.LookRotation(direction) * Quaternion.Euler(rotation);
|
|
Vector3 firstBoxOffset = gizmosRotation * (Vector3.forward * size.z);
|
|
|
|
Color gizmoColor = Evaluate(source) ? Color.green : Color.red;
|
|
gizmoColor = active ? gizmoColor : Color.gray;
|
|
|
|
using (Draw.ingame.WithColor(gizmoColor)){
|
|
switch (castType) {
|
|
case CastType.Ray:
|
|
Draw.ingame.Line(relativeStart, relativeStart + (source.transform.rotation * direction.normalized) * length);
|
|
break;
|
|
case CastType.BoxOverlap:
|
|
Draw.ingame.WireBox(relativeStartWithRotation, source.transform.rotation * Quaternion.Euler(rotation), size);
|
|
break;
|
|
case CastType.SphereCast:
|
|
Draw.ingame.SolidCircle(relativeStartWithRotation, relativeStartWithRotation - Camera.main.transform.position, width * 1, gizmoColor.Alpha(.5f));
|
|
Draw.ingame.WireCapsule(relativeStartWithRotation, relativeStartWithRotation + gizmosRotation * (Vector3.forward * (length - width / 2)), width);
|
|
break;
|
|
case CastType.BoxCast:
|
|
// Draw the gizmos for the boxcast
|
|
Draw.ingame.WireBox(offsetFromCenter, gizmosRotation, new float3(size.x, size.y, length));
|
|
Draw.ingame.SolidBox(relativeStartWithRotation, gizmosRotation, size, gizmoColor.Alpha(.1f));
|
|
|
|
Draw.ingame.WireBox(relativeStartWithRotation, gizmosRotation, size);
|
|
break;
|
|
default:
|
|
throw new ArgumentOutOfRangeException();
|
|
}
|
|
|
|
Draw.ingame.SolidCircle(relativeStartWithRotation, relativeStartWithRotation - Camera.main.transform.position, .4f);
|
|
Draw.ingame.SolidCircle(hit.point, hit.point - Camera.main.transform.position, .4f);
|
|
|
|
// Set up variables for label (not hit name)
|
|
Vector3 labelStartPos = Vector3.zero;
|
|
switch (labelTextLocation) {
|
|
case LabelDrawingLocation.PlayerOffset:
|
|
labelStartPos = source.transform.position;
|
|
break;
|
|
case LabelDrawingLocation.IntersectingLength:
|
|
labelStartPos = offsetFromCenter;
|
|
break;
|
|
case LabelDrawingLocation.HitLocation:{
|
|
if (hit.transform != null) {
|
|
labelStartPos = hit.point;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Draw label
|
|
if (drawLabel) {
|
|
Draw.ingame.Label3D(
|
|
labelStartPos + labelLocationOffset,
|
|
gizmosRotation * Quaternion.Euler(labelRotationOffset),
|
|
label,
|
|
labelSize,
|
|
LabelAlignment.MiddleLeft,
|
|
gizmoColor
|
|
);
|
|
}
|
|
|
|
// Set up variables for hit name
|
|
// Since the label is already drawn just use the previous startPos
|
|
switch (labelTextLocation) {
|
|
case LabelDrawingLocation.PlayerOffset:
|
|
labelStartPos = source.transform.position;
|
|
break;
|
|
case LabelDrawingLocation.IntersectingLength:
|
|
labelStartPos = offsetFromCenter;
|
|
break;
|
|
case LabelDrawingLocation.HitLocation:{
|
|
if (hit.transform != null) {
|
|
labelStartPos = hit.point;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Draw hitname
|
|
if (drawLabel) {
|
|
Draw.ingame.Label3D(
|
|
labelStartPos + labelLocationOffset,
|
|
gizmosRotation * Quaternion.Euler(labelRotationOffset),
|
|
label,
|
|
hitTextSize,
|
|
LabelAlignment.MiddleLeft,
|
|
gizmoColor
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
static Color GetObserverStatusColorStatic(bool active, RaycastHit hit){
|
|
if (active) {
|
|
if (hit.Equals(default(RaycastHit))) {
|
|
return Color.green;
|
|
}
|
|
|
|
return Color.red;
|
|
}
|
|
|
|
return Color.gray;
|
|
}
|
|
}
|
|
|
|
public class PlayerEnvironmentManager : MonoBehaviour{
|
|
[OdinSerialize]
|
|
public List<EnvironmentObserver> observers;
|
|
|
|
void Start(){
|
|
CheckDuplicateLabels(observers);
|
|
}
|
|
|
|
// TODO: Not working.
|
|
void CheckDuplicateLabels(List<EnvironmentObserver> sourceList){
|
|
foreach (EnvironmentObserver sourceObserver in observers) {
|
|
foreach (EnvironmentObserver observer in sourceList) {
|
|
if (sourceObserver == observer) {
|
|
continue;
|
|
}
|
|
|
|
if (sourceObserver.label == observer.label) {
|
|
Debug.LogError($"Duplicate label found in observer: {observer.label} is in use multiple times");
|
|
}
|
|
|
|
if (observer.children != null && observer.children.Count > 0) {
|
|
CheckDuplicateLabels(observer.children);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public bool EvaluateFromString(string searchLabel, List<EnvironmentObserver> observerList = null){
|
|
List<EnvironmentObserver> listToUse = observers;
|
|
|
|
if (observerList != null) {
|
|
listToUse = observerList;
|
|
}
|
|
|
|
foreach (EnvironmentObserver observer in listToUse) {
|
|
if (observer.label == searchLabel) {
|
|
return observer.Evaluate(gameObject);
|
|
}
|
|
|
|
if (observer.children != null && observer.children.Count > 0) {
|
|
foreach (EnvironmentObserver childObserver in observer.children) {
|
|
EvaluateFromString(searchLabel, childObserver.children);
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public EnvironmentObserver FindObserverFromString(string searchLabel, List<EnvironmentObserver> observerList = null){
|
|
List<EnvironmentObserver> listToUse = observers;
|
|
|
|
if (observerList != null) {
|
|
listToUse = observerList;
|
|
}
|
|
|
|
foreach (EnvironmentObserver observer in listToUse) {
|
|
if (observer.label == searchLabel) {
|
|
return observer;
|
|
}
|
|
|
|
if (observer.children != null && observer.children.Count > 0) {
|
|
foreach (EnvironmentObserver childObserver in observer.children) {
|
|
FindObserverFromString(searchLabel, childObserver.children);
|
|
}
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
void Update(){
|
|
}
|
|
|
|
void LateUpdate(){
|
|
// Draw Gizmos
|
|
foreach (EnvironmentObserver observer in observers) {
|
|
observer.DrawObserverGizmo(gameObject);
|
|
|
|
if (observer.children != null && observer.children.Count > 0) {
|
|
foreach (EnvironmentObserver childObserver in observer.children) {
|
|
childObserver.DrawObserverGizmo(gameObject);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Clear hit
|
|
foreach (EnvironmentObserver observer in observers) {
|
|
observer.hit = default;
|
|
|
|
if (observer.children != null && observer.children.Count > 0) {
|
|
foreach (EnvironmentObserver childObserver in observer.children) {
|
|
childObserver.hit = default;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|