Files
project-reset/Assets/Scripts/Units/EnvironmentObserver.cs

300 lines
12 KiB
C#

using System;
using System.Collections.Generic;
using Drawing;
using Sirenix.OdinInspector;
using Unity.Mathematics;
using UnityEngine;
[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")] [HideIf("@castType == CastType.BoxOverlap || castType == CastType.SphereOverlap")]
public float length;
[FoldoutGroup("Settings")] public Vector3 direction;
[FoldoutGroup("Settings")] public Vector3 offset;
[PropertySpace(0, 5), FoldoutGroup("Settings")] public LayerMask ignoreLayers = ~0;
[FoldoutGroup("Settings")] [ShowIf("@castType == CastType.BoxOverlap || castType == CastType.SphereOverlap")]
public List<Collider> ignoreObjects = new List<Collider>();
[FoldoutGroup("Settings")] [ShowIf("@castType == CastType.BoxOverlap || castType == CastType.SphereOverlap")]
public bool dontIgnoreSelf;
[ShowIfGroup("Settings/SpheresOnly", VisibleIf = "@castType == CastType.SphereCast || castType == CastType.SphereOverlap")]
[FoldoutGroup("Settings")] public float width;
// Parameters for Overlap cast types
[FoldoutGroup("Settings")]
[ShowIfGroup("Settings/BoxesOnly", VisibleIf = "@castType == CastType.BoxCast || castType == CastType.BoxOverlap")]
public Vector3 size;
[ShowIfGroup("Settings/BoxesOnly")]
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; // TODO: Rework
// 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:
// Create original box overlap
Collider[] originalOverlap = Physics.OverlapBox(relativeStartWithRotation, size / 2f,
source.transform.rotation * Quaternion.Euler(rotation), ignoreLayers);
// Convert to a list for editing
List<Collider> collidersAsList = new List<Collider>();
collidersAsList.AddRange(originalOverlap);
// Remove any specifically specified objects
foreach (Collider ignoredObject in ignoreObjects) {
if (collidersAsList.Contains(ignoredObject)) {
collidersAsList.Remove(ignoredObject);
}
}
// Remove the source but only if requested
if (!dontIgnoreSelf) {
if (collidersAsList.Contains(source.GetComponent<Collider>())) {
collidersAsList.Remove(source.GetComponent<Collider>());
}
}
// Send back to an array and done
overlapHits = collidersAsList.ToArray();
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) {
IInteractable interactable = hit.transform.GetComponent<IInteractable>();
if (interactable != null && interactable.CanInteract()) {
hit.transform.GetComponent<IInteractable>().OnObserverDetected(this);
}
return true;
}
}
return false;
}
public void DrawObserverGizmo(GameObject source, bool drawAnyways = false){
if (!drawAnyways){
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;
if (direction == Vector3.zero) {
offsetWithRotationAndLength = Quaternion.Euler(rotation) * (Vector3.forward * (length / 2));
} else {
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;
if (direction == Vector3.zero) {
gizmosRotation = source.transform.rotation * Quaternion.Euler(rotation);
} else {
gizmosRotation = source.transform.rotation * Quaternion.LookRotation(direction) * Quaternion.Euler(rotation);
}
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;
}
}