maint: renamed player folder to units to match namespaces. added unit class as well.

This commit is contained in:
Chris
2025-10-04 01:05:37 -04:00
parent a80d1d487e
commit af0aab450b
38 changed files with 22 additions and 6 deletions

View File

@@ -0,0 +1,55 @@
using System;
using System.Collections.Generic;
using Sirenix.OdinInspector;
using UnityEngine;
[Serializable]
public class CameraSettingData{
[Title("Field of View"), HideLabel, InlineProperty] public SettingValue<float> mainFieldOfView = new SettingValue<float>(0f);
[Title("Orbit Position Damping"), HideLabel, InlineProperty] public SettingValue<Vector3> orbitPositionDamping = new SettingValue<Vector3>(Vector3.zero);
[Title("Orbit Target Offset"), HideLabel, InlineProperty] public SettingValue<Vector3> orbitTargetOffset= new SettingValue<Vector3>(Vector3.zero);
[Title("X Axis Look Enabled"), HideLabel, InlineProperty] public SettingValue<bool> axisLookEnabledX = new SettingValue<bool>(true);
[Title("Y Axis Look Enabled"), HideLabel, InlineProperty] public SettingValue<bool> axisLookEnabledY = new SettingValue<bool>(true);
[Title("X Axis Look Gain"), HideLabel, InlineProperty] public SettingValue<float> axisLookGainX = new SettingValue<float>(0f);
[Title("Y Axis Look Gain"), HideLabel, InlineProperty] public SettingValue<float> axisLookGainY = new SettingValue<float>(0f);
[Title("Orbit Follow Top Height"), HideLabel, InlineProperty] public SettingValue<float> orbitFollowTopHeight = new SettingValue<float>(0f);
[Title("Orbit Follow Top Radius"), HideLabel, InlineProperty] public SettingValue<float> orbitFollowTopRadius = new SettingValue<float>(0f);
[Title("Orbit Follow Center Height"), HideLabel, InlineProperty] public SettingValue<float> orbitFollowCenterHeight = new SettingValue<float>(0f);
[Title("Orbit Follow Center Radius"), HideLabel, InlineProperty] public SettingValue<float> orbitFollowCenterRadius = new SettingValue<float>(0f);
[Title("Orbit Follow Bottom Height"), HideLabel, InlineProperty] public SettingValue<float> orbitFollowBottomHeight = new SettingValue<float>(0f);
[Title("Orbit Follow Bottom Radius"), HideLabel, InlineProperty] public SettingValue<float> orbitFollowBottomRadius = new SettingValue<float>(0f);
public SettingValue<Vector2> rotationComposerScreenPos= new SettingValue<Vector2>(Vector2.zero);
public SettingValue<Vector3> cameraOffsetOffset= new SettingValue<Vector3>(Vector3.zero);
public List<IResettableSettingValue> GetAllSettings(){
var outputList = new List<IResettableSettingValue>();
IResettableSettingValue[] settings = {
mainFieldOfView,
orbitPositionDamping,
orbitTargetOffset,
axisLookEnabledX,
axisLookEnabledY,
axisLookGainX,
axisLookGainY,
orbitFollowTopHeight,
orbitFollowTopRadius,
orbitFollowCenterHeight,
orbitFollowCenterRadius,
orbitFollowBottomHeight,
orbitFollowBottomRadius,
rotationComposerScreenPos,
cameraOffsetOffset,
};
outputList.AddRange(settings);
return outputList;
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 14393bffe6324344b5f972412e37a339
timeCreated: 1758568723

View File

@@ -0,0 +1,114 @@
using System;
using Reset.Core;
using Sirenix.OdinInspector;
using Unity.Cinemachine;
using UnityEngine;
public class CameraSettingsProcessor : MonoBehaviour{
public static CameraSettingsProcessor Instance{ get; private set; }
[ShowInInspector, FoldoutGroup("Camera Data", expanded: true), InlineProperty, HideLabel]
public CameraSettingData data;
public static GameObject mainCamera;
private CinemachineCamera main;
private CinemachineOrbitalFollow orbit;
private CinemachineRotationComposer rotComp;
private CinemachineCameraOffset offset;
private CinemachineInputAxisController axisCont;
public void Awake(){
// Singleton management
if (Instance != null && Instance != this) {
Destroy(this);
} else {
Instance = this;
}
// Set references for camera object and cinemachine components
mainCamera = gameObject;
main = mainCamera.GetComponent<CinemachineCamera>();
orbit = mainCamera.GetComponent<CinemachineOrbitalFollow>();
rotComp = mainCamera.GetComponent<CinemachineRotationComposer>();
offset = mainCamera.GetComponent<CinemachineCameraOffset>();
axisCont = mainCamera.GetComponent<CinemachineInputAxisController>();
// Quick check for a camera settings
if (data == null) {
Debug.LogWarning("No Camera Settings Data was found on processing. One will be created. Is this intentional? This will have strong effects on camera movement");
data = new CameraSettingData();
}
// Initialize camera settings values from current values
InitializeAllSettings();
}
void Update(){
SmoothCameraSettings();
ApplyCameraSettings();
}
void SmoothCameraSettings(){
var settings = data.GetAllSettings();
for (int i = 0; i < settings.Count; i++) {
settings[i].SmoothAndEase();
}
}
// Responsible for actively applying the settings to the Cinemachine components
void ApplyCameraSettings(){
main.Lens.FieldOfView = data.mainFieldOfView.Value;
axisCont.Controllers[0].Enabled = data.axisLookEnabledX.Value;
axisCont.Controllers[1].Enabled = data.axisLookEnabledY.Value;
axisCont.Controllers[0].Input.Gain = data.axisLookGainX.Value;
axisCont.Controllers[1].Input.Gain = data.axisLookGainY.Value;
orbit.TrackerSettings.PositionDamping = data.orbitPositionDamping.Value;
orbit.TargetOffset = data.orbitTargetOffset.Value;
orbit.Orbits.Top.Height = data.orbitFollowTopHeight.Value;
orbit.Orbits.Top.Radius = data.orbitFollowTopRadius.Value;
orbit.Orbits.Center.Height = data.orbitFollowCenterHeight.Value;
orbit.Orbits.Center.Radius = data.orbitFollowCenterRadius.Value;
orbit.Orbits.Bottom.Height = data.orbitFollowBottomHeight.Value;
orbit.Orbits.Bottom.Radius = data.orbitFollowBottomRadius.Value;
rotComp.Composition.ScreenPosition = data.rotationComposerScreenPos.Value;
offset.Offset = data.cameraOffsetOffset.Value;
}
[Button]
void InitializeAllSettings(){
data.mainFieldOfView.targetValue = main.Lens.FieldOfView;
data.orbitPositionDamping.targetValue = orbit.TrackerSettings.PositionDamping;
data.orbitTargetOffset.targetValue = orbit.TargetOffset;
data.axisLookEnabledX.targetValue = axisCont.Controllers[0].Enabled;
data.axisLookEnabledY.targetValue = axisCont.Controllers[1].Enabled;
data.axisLookGainX.targetValue = axisCont.Controllers[0].Input.Gain;
data.axisLookGainY.targetValue = axisCont.Controllers[1].Input.Gain;
data.orbitFollowTopHeight.targetValue = orbit.Orbits.Top.Height;
data.orbitFollowTopRadius.targetValue = orbit.Orbits.Top.Radius;
data.orbitFollowCenterHeight.targetValue = orbit.Orbits.Center.Height;
data.orbitFollowCenterRadius.targetValue = orbit.Orbits.Center.Radius;
data.orbitFollowBottomHeight.targetValue = orbit.Orbits.Bottom.Height;
data.orbitFollowBottomRadius.targetValue = orbit.Orbits.Bottom.Radius;
data.rotationComposerScreenPos.targetValue = rotComp.Composition.ScreenPosition;
data.cameraOffsetOffset.targetValue = offset.Offset;
var allSettings = data.GetAllSettings();
for (int i = 0; i < allSettings.Count; i++) {
allSettings[i].Initialize();
allSettings[i].Verify();
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: c032a18b0b0c1da4092bfc365c6e4aad

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 3f97262ab9d9b0541b5e20e7e7673a7c
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,9 @@
using UnityEngine;
namespace Reset.Core{
public interface IDamageable{
public void TakeDamage(DamageSource source);
public void TakeDamage(DamageSource[] sources);
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: f7a90e7b5a2e47747a524df1a8b3bc78

View File

@@ -0,0 +1,72 @@
using System;
using System.Collections.Generic;
using Reset.Core.Tools;
using Reset.Units;
using UnityEngine;
using Random = UnityEngine.Random;
public class UnitCombat : MonoBehaviour{
public List<Collider> draggedUnits = new List<Collider>();
private UnitMovementHandler movement;
private Vector3 lastPosition;
private Vector3 positionDelta;
private float lastSpeed;
private float speedDelta;
private float sinAmplitude;
private float sinOffset;
void Awake(){
movement = GetComponent<UnitMovementHandler>();
}
void Start(){
lastPosition = transform.position;
sinOffset = Random.value;
sinAmplitude = 1 + Random.value / 4f;
}
// Update is called once per frame
void Update(){
DragAttackedUnits();
}
void DragAttackedUnits(){
// Get the original difference in position for speed and direction
positionDelta = Vector3.Lerp(positionDelta, lastPosition.DirectionTo(transform.position), 5f * Time.deltaTime);
speedDelta = Vector3.Distance(lastPosition, transform.position) / Time.deltaTime;
// Add some randomness to the movements based on small offsets
float sinVal = Mathf.Sin(2f + sinOffset) * sinAmplitude;
speedDelta += sinVal;
// Set a floor to prevent them from not moving enough
speedDelta = Mathf.Max(3f, speedDelta);
// Multiply the speed to be lower when further, and faster when close
float speedDiff = Mathf.Lerp(.2f, 1.4f, speedDelta);
speedDelta *= speedDiff;
// Debug
DebugOverlayDrawer.ChangeValue($"Combat - {name}", "Position Delta", positionDelta);
DebugOverlayDrawer.ChangeValue($"Combat - {name}", "Speed Delta", speedDelta);
// Update last known position
lastPosition = transform.position;
// Apply the speed, direction, and rotation to each unit
foreach (Collider draggedUnit in draggedUnits) {
UnitMovementHandler draggedUnitMovement = draggedUnit.GetComponent<UnitMovementHandler>();
if (!draggedUnitMovement) {
Debug.LogError($"No available UnitMovement on {draggedUnit.name}. Aborting drag on this unit.");
continue;
}
draggedUnitMovement.SetNewRotation(-transform.position.DirectionTo(draggedUnit.transform.position), 1f, true);
draggedUnitMovement.SetNewDirection((positionDelta).ToVector2(), 1f, true);
draggedUnitMovement.SetNewSpeed(speedDelta, 1f, true);
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 7719da98d07bb4848a78904f6da4c355

View File

@@ -0,0 +1,80 @@
using System;
using UnityEngine;
using UnityEngine.InputSystem;
using Unity.Cinemachine;
// This class receives input from a PlayerInput component and disptaches it
// to the appropriate Cinemachine InputAxis. The playerInput component should
// be on the same GameObject, or specified in the PlayerInput field.
class CustomInputHandler : InputAxisControllerBase<CustomInputHandler.Reader>
{
[Header("Input Source Override")]
public PlayerInput PlayerInput;
void Awake()
{
// // When the PlayerInput receives an input, send it to all the controllers
// if (PlayerInput == null)
// TryGetComponent(out PlayerInput);
// if (PlayerInput == null)
// Debug.LogError("Cannot find PlayerInput component");
// else
// {
// PlayerInput.notificationBehavior = PlayerNotifications.InvokeCSharpEvents;
// PlayerInput.onActionTriggered += (value) =>
// {
// for (var i = 0; i < Controllers.Count; i++)
// Controllers[i].Input.ProcessInput(value.action);
// };
// }
}
// We process user input on the Update clock
void Update()
{
if (Application.isPlaying){
UpdateControllers();
Controllers[0].Input.ProcessInput(PlayerInput);
Controllers[1].Input.ProcessInput(PlayerInput);
}
}
public void AddEvents(){
// // PlayerInput.notificationBehavior = PlayerNotifications.InvokeCSharpEvents;
// += (value) =>
// {
// for (var i = 0; i < Controllers.Count; i++)
// Controllers[i].Input.ProcessInput(value.action);
// };
}
// Controllers will be instances of this class.
[Serializable]
public class Reader : IInputAxisReader
{
public InputActionReference Input;
Vector2 m_Value; // the cached value of the input
public void ProcessInput(PlayerInput input){
// // If it's my action then cache the new value
// if (Input != null && Input.action.id == action.id)
// {
// if (action.expectedControlType == "Vector2")
// m_Value = action.ReadValue<Vector2>();
// else
// m_Value.x = m_Value.y = action.ReadValue<float>();
// }
m_Value = input.actions["Look"].ReadValue<Vector2>();
m_Value.x *= 200f;
m_Value.y *= -100f;
}
// IInputAxisReader interface: Called by the framework to read the input value
public float GetValue(UnityEngine.Object context, IInputAxisOwner.AxisDescriptor.Hints hint)
{
return (hint == IInputAxisOwner.AxisDescriptor.Hints.Y ? m_Value.y : m_Value.x);
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 058c862723373ca43913637463aff84a

View File

@@ -0,0 +1,10 @@
using Reset.Core;
namespace Reset.Units{
public interface IKillable : IDamageable{
public void Kill();
public float maxHealth{ get; set; }
public float currentHealth{ get; set; }
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a819219f1a77429aacb1aace2e346eab
timeCreated: 1759535007

View File

@@ -0,0 +1,49 @@
using Reset.Items;
using UnityEngine;
using UnityEngine.InputSystem;
namespace Reset.Units{
public class InteractionHandler : MonoBehaviour{
private PlayerEnvironmentManager envManager;
private Inventory inventory;
private EnvironmentObserver observer;
void Awake(){
envManager = GetComponent<PlayerEnvironmentManager>();
inventory = GetComponent<Inventory>();
observer = envManager.FindObserverFromString("itemdrop");
observer.active = true;
}
public void InteractWith(GameObject target){
IInteractable interactable = target.GetComponent<IInteractable>();
if (interactable == null) {
Debug.LogError("This item cannot be interacted with, it has no Interactable interface");
return;
}
if (target.GetComponent<ItemDrop>()) {
inventory.AddToInventory(target.GetComponent<ItemDrop>().item);
}
interactable.Interact();
// Don't do any actions that use the same button (hard set to Jump for now)
Debug.Log($"Just collected the item, consuming the input @ {Time.time}.");
GetComponent<PlayerControls>().SendInputBlock("Jump");
}
void OnInteract(){
CheckForInteraction();
}
private void CheckForInteraction(){
if (observer.active && observer.Evaluate(gameObject)) {
InteractWith(observer.hit.transform.gameObject);
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 96023d711211b9843b01db23192fa84c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: -101
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,47 @@
using System.Collections.Generic;
using NodeCanvas.Tasks.Actions;
using Reset.Items;
using UnityEngine;
namespace Reset.Units{
public class Inventory : MonoBehaviour{
public Weapon rangedWeapon;
public Weapon meleeWeapon;
public Ability spellAbility1;
public Ability spellAbility2;
public Ability toolAbility1;
public Ability toolAbility2;
public List<Item> storedItems = new List<Item>(15);
void Start(){
}
public void AddToInventory(Item newItem){
storedItems.Add(newItem);
}
public void Equip(Item item){
if (item is not IEquipable) {
Debug.LogError("This item is not equippable.", item);
return;
}
if (item is Weapon thisWeapon) {
if (meleeWeapon != null) {
storedItems.Add(meleeWeapon);
}
meleeWeapon = thisWeapon;
}
}
// Update is called once per frame
void Update(){
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: d57ab8ec1cbbdcf46813f268625d3494

View File

@@ -0,0 +1,91 @@
using System;
using System.Collections;
using Drawing;
using Reset;
using Reset.Core;
using Reset.Core.Tools;
using Reset.Units;
using UnityEngine;
using Sirenix.OdinInspector;
using Sirenix.Serialization;
using Unity.Netcode;
public class Player : Unit, IKillable{
[HideInInspector] public PlayerControls controls;
float IKillable.maxHealth{ get; set; }
float IKillable.currentHealth{ get; set; }
void Awake(){
GameManager.Player = gameObject;
controls = GetComponent<PlayerControls>();
}
void Start(){
if (((IKillable)this).maxHealth == 0f) {
Debug.LogError($"Max health is not set for type of <b>{name}</b>. Setting to 100.");
((IKillable)this).currentHealth = 10000f;
} else {
((IKillable)this).currentHealth = ((IKillable)this).maxHealth;
}
if (!NetworkManager.Singleton.IsConnectedClient && !NetworkManager.Singleton.IsHost) {
Attach();
} else {
StartCoroutine(WaitForOnline());
}
}
private IEnumerator WaitForOnline(){
while (!NetworkManager.Singleton.didAwake) {
Debug.Log("waiting");
yield return null;
}
// Debug.Log($"{IsHost}, {IsClient}, {IsLocalPlayer}");
if (IsLocalPlayer){
GameManager.Player = gameObject;
Attach();
}
}
public void Attach(){
if (GameManager.Player == gameObject){
GameManager.RequestNewController();
GetComponent<LockOnManager>().AttachCamera(gameObject);
}
}
protected override void OnNetworkPostSpawn(){
// GetComponent<LockOnManager>().AttachCamera(gameObject);
}
// Update is called once per frame
void Update(){
using (Draw.WithColor(Color.blue)) {
Draw.ingame.Label2D(transform.position + Vector3.up * 2.2f, ((IKillable)this).currentHealth.ToString(),
Color.blue);
}
}
public void TakeDamage(DamageSource[] sources){
foreach (DamageSource source in sources) {
TakeDamage(source);
}
}
public void TakeDamage(DamageSource source){
((IKillable)this).currentHealth -= source.damageDealt;
if (((IKillable)this).currentHealth <= 0) {
Kill();
}
}
public void Kill(){
throw new NotImplementedException();
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 0c0da0317d344ea49b810f0010efefa8

View File

@@ -0,0 +1,53 @@
using System;
using UnityEngine;
public interface ILockOnTarget {
public float lockonTargetRadius { set; }
Transform transform {get;}
GameObject gameObject{ get; }
abstract void OnTargetDelete();
void Help(){
SafelyDeleteTarget();
}
public Vector3 GetReticlePosition(){
float upValue = 0f;
if (gameObject.GetComponent<Renderer>()){
Bounds objectBounds = gameObject.GetComponent<Renderer>().bounds;
upValue = objectBounds.size.y;
}
Vector3 reticlePosition = new Vector3(transform.position.x, transform.position.y + upValue, transform.position.z);
return reticlePosition;
}
public void SafelyDeleteTarget(){
// gameObject.
foreach (LockOnManager.ActiveLockOnTarget target in LockOnManager.Instance.activeTargets) {
if (target.gameObject == this.gameObject) {
GameObject clone = new GameObject{name = $"Target Clone of {gameObject.name}", transform = { position = transform.position}};
target.gameObject = clone;
target.cinemachineTarget.Object = clone.transform;
LockOnManager.Instance.QueueTargetRemoval(clone, true);
}
}
}
}
public class PlayerCamera : MonoBehaviour{
void Start(){
}
// Update is called once per frame
void LateUpdate(){
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: d224dfedffdf96f4896aeee0ce605153

View File

@@ -0,0 +1,120 @@
using System;
using System.Linq;
using UnityEditor;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.UIElements;
using NodeCanvas;
using NodeCanvas.Framework;
using ParadoxNotion;
using Reset;
using Sirenix.OdinInspector;
using Unity.Cinemachine;
using Object = UnityEngine.Object;
public class PlayerControls : MonoBehaviour{
// References
private Player thisPlayer;
private PlayerInput input;
private SignalDefinition inputSignal;
private SignalDefinition blockSignal;
// TODO: Turn these into accessors
public Vector2 rawMoveInput;
public Vector2 rawLookInput;
public GraphOwner graph;
void Awake(){
try {
inputSignal = Resources.Load<SignalDefinition>("InputSignal");
} catch (Exception e) {
Debug.LogError($"Error finding the Input Signal defintion: {e.Message}");
throw;
}
try {
blockSignal = Resources.Load<SignalDefinition>("BlockInputSignal");
} catch (Exception e) {
Debug.LogError($"Error finding the Input Signal defintion: {e.Message}");
throw;
}
thisPlayer = GetComponent<Player>();
graph = GetComponent<GraphOwner>();
input = GetComponent<PlayerInput>();
// Remove all devices from this user
input.user.UnpairDevices();
// Add the delegates for each method
foreach (InputAction action in input.actions) {
action.started += SendInputSignal;
action.canceled += SendInputSignal;
action.performed += SendInputSignal;
}
}
// Remove the delegates for each method
void OnDisable(){
foreach (InputAction action in input.actions) {
action.started -= SendInputSignal;
action.canceled -= SendInputSignal;
action.performed -= SendInputSignal;
}
}
// This will call the SignalInvoke for this type of Signal Defintion. CheckInput is the recieving end.
public void SendInputSignal(InputAction.CallbackContext ctx){
inputSignal.Invoke(transform, transform, false, new object[]{
ctx.action,
ctx.phase
});
}
public void SendInputBlock(string actionName){
Debug.Log($"Sending block request...");
blockSignal.Invoke(transform, transform, false, new object[]{actionName});
}
public void OnMove(InputValue value){
rawMoveInput.x = value.Get<Vector2>().x;
rawMoveInput.y = value.Get<Vector2>().y;
}
public void OnLook(InputValue value){
rawLookInput.x = value.Get<Vector2>().x;
rawLookInput.y = value.Get<Vector2>().y;
// GameManager.Camera.transform.Find("Cinemachine").GetComponent<CustomInputHandler>().Controllers[0].InputValue = rawLookInput.x * 200;
// GameManager.Camera.transform.Find("Cinemachine").GetComponent<CustomInputHandler>().Controllers[1].InputValue = rawLookInput.y * 100;
}
public void OnSprint(){
graph.SendEvent<string>("InputEvent", "Sprint", null);
}
public void OnJump(){
graph.SendEvent<string>("InputEvent", "Jump", null);
}
public void OnLockOn(){
GetComponent<LockOnManager>().ChangeLockOnTarget();
graph.SendEvent<string>("InputEvent", "LockOn", null);
}
public void OnCancelLockOn(){
GetComponent<LockOnManager>().RemoveMainTarget();
graph.SendEvent<string>("InputEvent", "CancelLockOn", null);
}
// public void OnGrapple(InputInteractionContext context){
// if (context.control.IsPressed()) {
// graph.SendEvent<string>("InputEvent", "GrappleDown", null);
// } else {
// graph.SendEvent<string>("InputEvent", "GrappleUp", null);
// }
// }
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 2d0c4f0666ff5f245bf2a135c782127b

View File

@@ -0,0 +1,400 @@
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")] [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;
// 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) {
if (hit.transform.GetComponent<IInteractable>() != null) {
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;
}
}
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;
}
}
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 7a8c0593fef54844383c2f154cf8806c

View File

@@ -0,0 +1,10 @@
namespace Reset.Units{
public enum PlayerFacingDirection{
TowardsTarget = 0,
MatchInput,
MatchCamera,
Static,
Momentum,
SpecifiedDirection
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 81d909774c8e4b66be6e96fd13f9bff4
timeCreated: 1758478092

View File

@@ -0,0 +1,57 @@
using System;
using Reset.Core.Tools;
using Sirenix.OdinInspector;
using UnityEngine;
namespace Reset.Units{
public class ResolvedMovement{
public struct MoveDirection{
private Transform owner;
// Both are in world space, and translated to local
private Vector2 _moveRaw;
private Vector2 _moveDir;
[ShowInInspector]
public Vector2 World{
get{
DebugOverlayDrawer.ChangeValue("Movement", "_moveDir", _moveDir);
return _moveDir;
}
set{
_moveDir = value;
}
}
[ShowInInspector]
public Vector2 Local => owner.InverseTransformDirection(_moveDir.ToVector3()).ToVector2();
[ShowInInspector, PropertySpace(5)]
public Vector2 RawWorld{
get{
DebugOverlayDrawer.ChangeValue("Movement", "_moveRaw", _moveRaw);
// return owner.TransformDirection(_moveDir.ToVector3()).ToVector2();
return _moveRaw;
}
set{
_moveRaw = value;
}
}
[ShowInInspector]
public Vector2 RawLocal => owner.InverseTransformDirection(_moveRaw.ToVector3()).ToVector2();
public MoveDirection(Transform ownerTransform){
owner = ownerTransform;
_moveDir = Vector2.zero;
_moveRaw = Vector2.zero;
}
}
[ShowInInspector, InlineProperty, BoxGroup("Direction"), HideLabel] public MoveDirection moveDirection;
public float moveSpeed;
public Quaternion rotation;
public float rotationSpeed;
public float gravity;
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d663059052094322bff98f9ed2839fd9
timeCreated: 1758478092

View File

@@ -0,0 +1,108 @@
using System;
using Sirenix.OdinInspector;
using UnityEngine;
using UnityEngine.Serialization;
public interface IResettableSettingValue{
public void Verify();
public void SmoothAndEase();
public void Initialize();
}
[Serializable]
public class SettingValue<T> : IResettableSettingValue{
[HorizontalGroup("Settings", width: .3f), VerticalGroup("Settings/Value"), BoxGroup("Settings/Value/Value"), LabelText("Target")]
public T targetValue;
[HorizontalGroup("Settings"), VerticalGroup("Settings/Value"), BoxGroup("Settings/Value/Value"), LabelText("Current")]
public T currentValue;
[HorizontalGroup("Settings"), VerticalGroup("Settings/Smoothing"), BoxGroup("Settings/Smoothing/Smoothing"), LabelText("Target"), ShowIf("@IsSmoothable()")]
public float targetSmoothing; // Smoothing changes how fast the value is changed.
[HorizontalGroup("Settings"), VerticalGroup("Settings/Easing"), BoxGroup("Settings/Easing/Easing"), LabelText("Target"), ShowIf("@IsSmoothable()")]
public float targetEasing; // Easing changes how fast smoothing is changed, when given a new value.
public T Value{
get => currentValue;
set => targetValue = value;
}
[HorizontalGroup("Settings"), VerticalGroup("Settings/Smoothing"), BoxGroup("Settings/Smoothing/Smoothing"), LabelText("Current"), ShowIf("@IsSmoothable()")]
public float currentSmoothing;
[HorizontalGroup("Settings"), VerticalGroup("Settings/Value"), BoxGroup("Settings/Value/Value"), LabelText("Default")]
public T defaultValue;
[HorizontalGroup("Settings"), VerticalGroup("Settings/Smoothing"), BoxGroup("Settings/Smoothing/Smoothing"), LabelText("Default"), ShowIf("@IsSmoothable()")]
public float defaultSmoothing;
[HorizontalGroup("Settings"), VerticalGroup("Settings/Easing"), BoxGroup("Settings/Easing/Easing"), LabelText("Default"), ShowIf("@IsSmoothable()")]
public float defaultEasing;
private float refVelFloat; // For use with SmoothDamp
private Vector3 refVelV3; // For use with SmoothDamp
private Vector2 refVelV2; // For use with SmoothDamp
private bool verified;
bool IsSmoothable(){
return (typeof(T) == typeof(float) || typeof(T) == typeof(Vector2) || typeof(T) == typeof(Vector3) || typeof(T) == typeof(Vector4) || typeof(T) == typeof(Quaternion) );
}
public SettingValue(T initValue, float defaultEasing = 2f, float defaultSmoothing = 1f){
targetValue = initValue;
defaultValue = initValue;
this.defaultSmoothing = defaultSmoothing;
this.defaultEasing = defaultEasing;
targetEasing = defaultEasing;
targetSmoothing = defaultSmoothing;
currentSmoothing = targetSmoothing;
currentValue = targetValue;
refVelFloat = 0;
refVelV3 = default;
refVelV2 = default;
}
public void Verify(){
if (targetValue.Equals(currentValue) == false) {
Debug.LogWarning($"A SettingValue ({this}) doesn't have its current and target value matching. This should have been set!");
}
verified = true;
}
public void SmoothAndEase(){
if (!verified) {
Debug.LogWarning($"A SettingValue ({this}) wasn't verified before being smoothed and eased. What's up with that?");
}
currentSmoothing = Mathf.MoveTowards(currentSmoothing, targetSmoothing, targetEasing * targetEasing * Time.deltaTime);
if (typeof(T) == typeof(float)) {
currentValue = (T)(object)Mathf.SmoothDamp((float)(object)currentValue, (float)(object)targetValue, ref refVelFloat, currentSmoothing * Time.deltaTime);
}
if (typeof(T) == typeof(Vector2)) {
currentValue = (T)(object)Vector2.SmoothDamp((Vector2)(object)currentValue, (Vector2)(object)targetValue, ref refVelV2, currentSmoothing * currentSmoothing * Time.deltaTime);
}
if (typeof(T) == typeof(Vector3)) {
currentValue = (T)(object)Vector3.SmoothDamp((Vector3)(object)currentValue, (Vector3)(object)targetValue, ref refVelV3, currentSmoothing * currentSmoothing * Time.deltaTime);
}
if (typeof(T) == typeof(Vector4) || typeof(T) == typeof(Quaternion)) {
// I have... zero clue if this will work. There is no Vector4 or Quaternion SmoothDamp
Vector3 v3value = Vector3.SmoothDamp((Vector4)(object)currentValue, (Vector4)(object)targetValue, ref refVelV3, currentSmoothing * currentSmoothing * Time.deltaTime);
float v4value = Mathf.SmoothDamp(((Vector4)(object)currentValue).z, ((Vector4)(object)targetValue).z, ref refVelFloat, currentSmoothing * currentSmoothing * Time.deltaTime);
currentValue = (T)(object)new Vector4(v3value.x, v3value.y, v3value.z, v4value);
}
}
public void Initialize(){
currentValue = targetValue;
defaultValue = targetValue;
defaultSmoothing = targetSmoothing;
defaultEasing = targetEasing;
currentSmoothing = targetSmoothing;
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 03e2aa4743404e9fbf8b14033c5eda7b
timeCreated: 1758126336

View File

@@ -0,0 +1,4 @@
using Unity.Netcode;
public class Unit : NetworkBehaviour{
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d9f5c0c9bf384882925c15b3c93fff1b
timeCreated: 1759554015

View File

@@ -0,0 +1,62 @@
using System;
using System.Collections.Generic;
using ParadoxNotion.Design;
using Sirenix.OdinInspector;
using UnityEngine;
namespace Reset.Units{
[Serializable]
public class UnitMovementData{
// Movement Direction
[Title("Direction Changing Softeness"), HideLabel, InlineProperty] public SettingValue<float> directionChangingSoftness = new SettingValue<float>(1f, defaultSmoothing: 1f);
[Title("Direction Spinning Hardness"), HideLabel, InlineProperty] public SettingValue<float> directionSpinningHardness = new SettingValue<float>(3f, defaultSmoothing: 1f);
[Title("Directoon Spinning Speed"), HideLabel, InlineProperty] public SettingValue<float> directionSpinningSpeed= new SettingValue<float>(3f, defaultSmoothing: 1f);
[SliderField(0,1)]
[Title("Air Direction Decay"), HideLabel, InlineProperty] public SettingValue<float> airDirectionDecay = new SettingValue<float>(1f); // TODO: Check default value
// Move Speed
[Title("Acceleration"), HideLabel, InlineProperty] public SettingValue<float> acceleration = new SettingValue<float>(5f);
[Title("Deacceleration"), HideLabel, InlineProperty] public SettingValue<float> deacceleration = new SettingValue<float>(5f);
[Title("Move Speed"), HideLabel, InlineProperty] public SettingValue<float> moveSpeed = new SettingValue<float>(15f, defaultSmoothing: 10f);
// Jumping
// [Title("Jump Power"), HideLabel, InlineProperty] [ShowInInspector] public SettingValue<float> jumpPower = new SettingValue<float>(0f);
// [Title("Move Smoothing"), HideLabel, InlineProperty] public SettingValue<float> jumpPowerDecay = new SettingValue<float>(3f); // TODO: Check default value
// Gravity
[Title("Gravity Power"), HideLabel, InlineProperty] [ShowInInspector] public SettingValue<float> gravityPower = new SettingValue<float>(1f);
[Title("Gravity Max"), HideLabel, InlineProperty] public SettingValue<float> gravityMax = new SettingValue<float>(8f);
[Title("Gravity Acceleration"), HideLabel, InlineProperty] public SettingValue<float> gravityAcceleration = new SettingValue<float>(1f);
[Title("Gravity Scale"), HideLabel, InlineProperty] public SettingValue<float> gravityScale = new SettingValue<float>(1f);
// Rotation
[Title("Rotate Facing"), HideLabel, InlineProperty] public SettingValue<PlayerFacingDirection> facingDirection = new SettingValue<PlayerFacingDirection>(initValue: PlayerFacingDirection.Momentum);
[Title("Rotation Speed"), HideLabel, InlineProperty] public SettingValue<float> rotationSpeed = new SettingValue<float>(5f);
public List<IResettableSettingValue> GetAllSettings(){
var outputList = new List<IResettableSettingValue>();
IResettableSettingValue[] settings = {
directionChangingSoftness,
directionSpinningHardness,
directionSpinningSpeed,
acceleration,
deacceleration,
airDirectionDecay,
moveSpeed,
gravityPower,
gravityMax,
gravityAcceleration,
gravityScale,
facingDirection,
rotationSpeed,
};
outputList.AddRange(settings);
return outputList;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 7f5d86c96bcf450b888da32d1cf253e7
timeCreated: 1758478092

View File

@@ -0,0 +1,326 @@
using System.Collections.Generic;
using UnityEngine;
using Reset.Core.Tools;
using Sirenix.OdinInspector;
namespace Reset.Units{
public class UnitMovementHandler : MonoBehaviour{
[ShowInInspector, InlineProperty, HideLabel, FoldoutGroup("Resolved Movement", expanded: true)]
public ResolvedMovement resolvedMovement;
// SmoothDamp Velocities
private Vector2 refVelocityDirectionChangingHardness;
// Smoothing Values
private float directionChangeDotLerp;
private Vector3 moveSmoothVelocityRef;
private float gravitySmoothVelocityRef;
// References
private CharacterController controller;
private PlayerControls controls;
private LockOnManager lockOnManager;
// Movement Data
[ShowInInspector, PropertyOrder(2), FoldoutGroup("Movement Data", expanded: true), InlineProperty, HideLabel] public UnitMovementData data = new();
// Other
public Vector3 specifiedRotation; // Used for locking a specific direction
void Awake(){
controller = GetComponent<CharacterController>();
controls = GetComponent<PlayerControls>();
lockOnManager = GetComponent<LockOnManager>();
InitAllSettings();
}
void Start(){
resolvedMovement = new ResolvedMovement{
moveDirection = new ResolvedMovement.MoveDirection(transform)
};
}
void Update(){
SmoothAllSettings();
UpdateCurrentDirection();
UpdateCurrentGravity();
UpdateCurrentSpeed();
UpdateCurrentRotation();
// Apply movement
DoMovement(resolvedMovement.moveDirection.World, resolvedMovement.gravity, resolvedMovement.moveSpeed, data.gravityScale.Value);
DebugOverlayDrawer.ChangeValue("Movement", "Move Direction (Local)", resolvedMovement.moveDirection.Local);
DebugOverlayDrawer.ChangeValue("Movement", "Move Direction (World)", resolvedMovement.moveDirection.World);
}
// Update the direction, called every frame
private void UpdateCurrentDirection(){
// Get input value
Vector2 targetDirection = new Vector2(controls.rawMoveInput.x, controls.rawMoveInput.y);
// Rotate input by camera rotation (instead of rotating the output direction by camera rotation)
targetDirection = (Camera.main.transform.rotation * targetDirection.ToVector3()).ToVector2();
// Deadzone
if (targetDirection.magnitude < .08f) {
targetDirection = Vector2.zero;
}
// Set Raw Direction (this is used by the camera later)
resolvedMovement.moveDirection.RawWorld = targetDirection;
// Get current direction
Vector2 currentDirection = resolvedMovement.moveDirection.World;
// Also need to find the dot value of the current input versus the current move direction
float switchedDirection = Vector3.Dot(targetDirection, currentDirection);
float switchedDirectionRemapped = Mathf.Lerp(0, 1, switchedDirection);
directionChangeDotLerp = Mathf.Lerp(switchedDirection, switchedDirectionRemapped, data.directionSpinningHardness.Value * Time.deltaTime);
DebugOverlayDrawer.ChangeValue("Movement", "Direction Change Dot", directionChangeDotLerp);
// Smooth movement. Use deaccel smoothing if the input magnitude is lower, and accel smoothing if it's higher
// Also checks when grounded to only use Slerp on the ground
Vector3 slerpedValue;
Vector2 lerpedValue;
Vector2 newDirection;
if (controller.isGrounded){
slerpedValue = Vector3.Slerp(currentDirection, targetDirection, data.directionSpinningSpeed.Value * Time.deltaTime);
lerpedValue = Vector2.SmoothDamp(currentDirection, targetDirection, ref refVelocityDirectionChangingHardness, data.directionChangingSoftness.Value * Time.deltaTime);
newDirection = Vector2.Lerp(slerpedValue, lerpedValue, directionChangeDotLerp);
} else {
newDirection = Vector2.SmoothDamp(currentDirection, targetDirection, ref refVelocityDirectionChangingHardness, data.directionChangingSoftness.Value * data.airDirectionDecay.Value * Time.deltaTime);
}
// Commit the new direction
resolvedMovement.moveDirection.World = newDirection;
}
// Update the speed, called every frame
private void UpdateCurrentSpeed(){
// ""Smooth"" the speed
float smoothedSpeed;
if (resolvedMovement.moveDirection.Local.magnitude < controls.rawMoveInput.magnitude) {
smoothedSpeed = Mathf.MoveTowards(resolvedMovement.moveSpeed, data.moveSpeed.Value, data.acceleration.Value * Time.deltaTime);
} else {
smoothedSpeed = Mathf.MoveTowards(resolvedMovement.moveSpeed, 0f, data.deacceleration.Value * Time.deltaTime);
}
// Commit the speed
resolvedMovement.moveSpeed = smoothedSpeed;
DebugOverlayDrawer.ChangeValue("Movement", "Resolved Speed", resolvedMovement.moveSpeed);
}
// Update the gravity, called every frame
// NOTE: most gravity interactions, like when grounded or not, is now handled by the state machine
private void UpdateCurrentGravity(){
// Accelerate gravity
if (!controller.isGrounded){
resolvedMovement.gravity -= data.gravityAcceleration.Value * Time.deltaTime;
}
// Create the final gravity value
float gravityMoveDirection = Physics.gravity.y * resolvedMovement.gravity;
}
// Update the rotation, called every frame
private void UpdateCurrentRotation(){
// Get input value
Vector3 inputMovement = new Vector3(controls.rawMoveInput.x, 0f, controls.rawMoveInput.y);
Quaternion targetRotation = Quaternion.identity;
// Switch the desired rotation based on current movement setting
switch (data.facingDirection.Value) {
// Just look at target
case PlayerFacingDirection.TowardsTarget:
// Look directly at the target
if (lockOnManager.mainTarget == null) {
Debug.LogError("Trying to rotate towards a target but there is no target. Not setting a rotation");
targetRotation = transform.rotation;
break;
}
targetRotation = Quaternion.LookRotation(transform.position.DirectionTo(lockOnManager.mainTarget.gameObject.transform.position));
break;
case PlayerFacingDirection.Momentum:
// Look towards the current direction the agent is moving
if (inputMovement.magnitude > .05f){
targetRotation = Quaternion.LookRotation(resolvedMovement.moveDirection.RawWorld.ToVector3(), Vector3.up);
}
break;
case PlayerFacingDirection.MatchInput:
// Look towards the input direction- similar to Momentum but snappier
if (controls.rawMoveInput.magnitude < 0.05f) { break; }
targetRotation = Camera.main.transform.rotation * Quaternion.LookRotation(inputMovement);
break;
case PlayerFacingDirection.MatchCamera:
// Look the same direction as the camera
targetRotation = Quaternion.Euler(Camera.main.transform.rotation.eulerAngles.Flatten(0, null, 0));
break;
case PlayerFacingDirection.Static:
// Don't change
targetRotation = resolvedMovement.rotation;
break;
}
DebugOverlayDrawer.ChangeValue("Rotation", "Target Rotation", targetRotation.eulerAngles);
// Add the current input into the created rotation
if (inputMovement.magnitude > .05) {
resolvedMovement.rotation = targetRotation;
} else if (data.facingDirection.Value == PlayerFacingDirection.MatchCamera) {
resolvedMovement.rotation = targetRotation;
}
// Apply rotation to the character
transform.rotation = Quaternion.Slerp(transform.rotation, resolvedMovement.rotation, data.rotationSpeed.Value * Time.deltaTime).Flatten(0, null, 0);
}
// Custom move from input
private void DoMovement(Vector2 moveDir, float gravDir, float speed, float gravityScale){
// Seperate the different move directions. Additonal becomes it's own because it needs to be added independently of the other two
Vector2 moveXZDir = moveDir;
float moveYDir = gravDir;
// Add their related speeds
moveXZDir *= speed * Time.deltaTime;
moveYDir *= data.gravityScale.Value * Time.deltaTime;
// Construct the direction and move
Vector3 finalDir = new Vector3(moveXZDir.x, moveYDir, moveXZDir.y);
controller.Move(finalDir);
}
// Setting absolute to true will cause the current gravity to snap to the new gravity value.
// Keeping it false will make it apply additively to the current gravity. Both options use relativty for linear interpolation.
public void SetNewGravity(float value, float relativity, bool absolute){ // new
if (absolute){
resolvedMovement.gravity = Mathf.Lerp(resolvedMovement.gravity, value, relativity);
} else {
resolvedMovement.gravity = Mathf.Lerp(resolvedMovement.gravity, resolvedMovement.gravity + value, relativity);
}
}
public void SetNewDirection(Vector2 value, float relativity, bool absolute, Vector2 relativeTo = default, bool relativeToRotation = true){ // new
Vector2 relativeValue;
if (relativeToRotation){
relativeValue = (Quaternion.LookRotation(relativeTo.ToVector3()) * value.ToVector3()).ToVector2();
} else {
relativeValue = relativeTo + value;
}
if (absolute){
resolvedMovement.moveDirection.World = Vector2.Lerp(resolvedMovement.moveDirection.World, relativeValue, relativity);
} else {
resolvedMovement.moveDirection.World = Vector2.Lerp(resolvedMovement.moveDirection.World, resolvedMovement.moveDirection.World + relativeValue, relativity);
}
Debug.Log(resolvedMovement.moveDirection.World);
}
public void SetNewRawDirection(Vector2 value, float relativity, bool absolute, Vector2 relativeTo = default){ // new
Vector2 relativeValue = relativeTo + value;
if (absolute){
resolvedMovement.moveDirection.RawWorld = Vector2.Lerp(resolvedMovement.moveDirection.RawWorld, relativeValue, relativity);
} else {
resolvedMovement.moveDirection.RawWorld = Vector2.Lerp(resolvedMovement.moveDirection.RawWorld, resolvedMovement.moveDirection.RawWorld + relativeValue, relativity);
}
}
public void SetNewSpeed(float value, float relativity, bool absolute, float relativeTo = Mathf.Infinity){ // new
if (absolute){
resolvedMovement.moveSpeed = Mathf.Lerp(resolvedMovement.moveSpeed, value, relativity);
} else {
resolvedMovement.moveSpeed = Mathf.Lerp(resolvedMovement.moveSpeed, resolvedMovement.moveSpeed + value, relativity);
}
}
public void SetNewRotation(Vector3 value, float relativity, bool absolute, Vector3 relativeTo = default){ // new
Quaternion valueAsQuaternion = Quaternion.LookRotation(value);
if (relativeTo != default) {
valueAsQuaternion = Quaternion.LookRotation(relativeTo) * valueAsQuaternion;
}
if (absolute){
resolvedMovement.rotation = Quaternion.Lerp(resolvedMovement.rotation, valueAsQuaternion, relativity);
} else {
resolvedMovement.rotation = Quaternion.Lerp(resolvedMovement.rotation, resolvedMovement.rotation * valueAsQuaternion, relativity);
}
}
public void SetSpecifiedRotation(Vector3 inputRotation){
specifiedRotation = inputRotation;
}
public Vector2 GetResolvedDirection(){
return resolvedMovement.moveDirection.World;
}
public Vector3 GetResolvedDirectionVector3(){
return resolvedMovement.moveDirection.World.ToVector3();
}
public float GetResolvedSpeed(){
return resolvedMovement.moveSpeed;
}
public float GetResolvedGravity(){
return resolvedMovement.gravity;
}
public Quaternion GetResolvedRotation(){
return resolvedMovement.rotation;
}
[Button("Initialize Settings", ButtonHeight = 30), PropertySpace(10,5 )]
void InitAllSettings(){
var settingsList = data.GetAllSettings();
foreach (IResettableSettingValue value in settingsList) {
value.Initialize();
value.Verify();
}
}
void SmoothAllSettings(){
var settingsList = data.GetAllSettings();
foreach (IResettableSettingValue value in settingsList) {
value.SmoothAndEase();
}
}
public void AddToCurrentDirection(Vector3 inputDirection, float power){ // Old
Debug.LogError("Using an old movement command! Switch to one of the new alternatives!");
}
public void SmoothToSpeed(float desiredSpeed, float smoothing){ // Old
Debug.LogError("Using an old movement command! Switch to one of the new alternatives!");
}
public void SetNewDirection(Vector3 inputDirection){ // NOTE: If smoothing desired add a default bool for smoothing maybe? // Old
Debug.LogError("Using an old movement command! Switch to one of the new alternatives!");
}
public void SetNewGravity(float value){
Debug.LogError("Using an old movement command! Switch to one of the new alternatives!");
}
public void OverwriteDirectionFromInput(Vector2 value, float priority, float speed = Mathf.Infinity){ // Old
Debug.LogError("Using an old movement command! Switch to one of the new alternatives!");
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b3b4e13d59527d1429a62dde97c6a001
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 50
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: