Merge branch 'refs/heads/feature/player.basic-combat-pt2' into dev
This commit is contained in:
@@ -21,7 +21,7 @@ namespace Reset {
|
||||
public BBParameter<float> length;
|
||||
public BBParameter<Vector3> direction;
|
||||
public BBParameter<Vector3> offset;
|
||||
public BBParameter<LayerMask> ignoreLayers;
|
||||
public BBParameter<LayerMask> layerMask;
|
||||
|
||||
public BBParameter<float> width;
|
||||
|
||||
@@ -44,7 +44,7 @@ namespace Reset {
|
||||
BBParameterEditor.ParameterField("Length", length);
|
||||
BBParameterEditor.ParameterField("Direction", direction);
|
||||
BBParameterEditor.ParameterField("Offset", offset);
|
||||
BBParameterEditor.ParameterField("Ignore Layers", ignoreLayers);
|
||||
BBParameterEditor.ParameterField("Ignore Layers", layerMask);
|
||||
|
||||
if (castType.value == EnvironmentObserver.CastType.SphereCast || castType.value == EnvironmentObserver.CastType.SphereOverlap) {
|
||||
BBParameterEditor.ParameterField("Width", width);
|
||||
@@ -88,7 +88,7 @@ namespace Reset {
|
||||
length = length.value,
|
||||
direction = direction.value,
|
||||
offset = offset.value,
|
||||
ignoreLayers = ignoreLayers.value,
|
||||
ignoreLayers = layerMask.value,
|
||||
|
||||
width = width.value,
|
||||
|
||||
|
||||
51
Assets/Scripts/Core/Graph Tasks/DragUnits.cs
Normal file
51
Assets/Scripts/Core/Graph Tasks/DragUnits.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
using System.Collections.Generic;
|
||||
using NodeCanvas.Framework;
|
||||
using ParadoxNotion.Design;
|
||||
using UnityEngine;
|
||||
|
||||
|
||||
namespace Reset.Units {
|
||||
|
||||
[Category("Reset/Units/Combat")]
|
||||
[Description("Drag a collection of units along with the attackers movement or actions")]
|
||||
public class DragUnits : ActionTask<UnitCombat>{
|
||||
public BBParameter<List<Collider>> unitsToDrag;
|
||||
public BBParameter<float> dragTime;
|
||||
|
||||
//Use for initialization. This is called only once in the lifetime of the task.
|
||||
//Return null if init was successfull. Return an error string otherwise
|
||||
protected override string OnInit() {
|
||||
return null;
|
||||
}
|
||||
|
||||
//This is called once each time the task is enabled.
|
||||
//Call EndAction() to mark the action as finished, either in success or failure.
|
||||
//EndAction can be called from anywhere.
|
||||
protected override void OnExecute() {
|
||||
foreach (Collider col in unitsToDrag.value) {
|
||||
if (dragTime.value == 0f) {
|
||||
agent.AddDragCollider(col);
|
||||
} else {
|
||||
agent.AddDragCollider(col, dragTime.value);
|
||||
}
|
||||
}
|
||||
|
||||
EndAction(true);
|
||||
}
|
||||
|
||||
//Called once per frame while the action is active.
|
||||
protected override void OnUpdate() {
|
||||
|
||||
}
|
||||
|
||||
//Called when the task is disabled.
|
||||
protected override void OnStop() {
|
||||
|
||||
}
|
||||
|
||||
//Called when the task is paused.
|
||||
protected override void OnPause() {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/Core/Graph Tasks/DragUnits.cs.meta
Normal file
2
Assets/Scripts/Core/Graph Tasks/DragUnits.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a15fb8c5108ca6a47a12fcf6278579fd
|
||||
@@ -8,7 +8,7 @@ namespace Reset.Units {
|
||||
|
||||
[Category("Reset/Movement")]
|
||||
public class ChangeRotationSettings : ActionTask<UnitMovementHandler> {
|
||||
[SerializeField] public EnumValueGroup facingDirection = new EnumValueGroup("Facing Direction", PlayerFacingDirection.TowardsTarget);
|
||||
[SerializeField] public EnumValueGroup facingDirection = new EnumValueGroup("Facing Direction", UnitFacingDirection.TowardsTarget);
|
||||
[SerializeField] public FloatValueGroup rotationSpeed = new (newLabel: "Rotation Speed");
|
||||
|
||||
//Use for initialization. This is called only once in the lifetime of the task.
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
using System;
|
||||
using NodeCanvas.Framework;
|
||||
using ParadoxNotion.Design;
|
||||
using Unity.Netcode;
|
||||
using Unity.Netcode.Components;
|
||||
using Reset.Units;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Reset.Core {
|
||||
[Category("Reset")]
|
||||
[Description("Sends an animation trigger with network sync to networked objects")]
|
||||
public class SendAnimationTrigger : ActionTask<NetworkAnimator>{
|
||||
public class SendAnimationTrigger : ActionTask<UnitAnimation>{
|
||||
public BBParameter<string> trigger;
|
||||
|
||||
//Use for initialization. This is called only once in the lifetime of the task.
|
||||
@@ -21,11 +20,8 @@ namespace Reset.Core {
|
||||
//Call EndAction() to mark the action as finished, either in success or failure.
|
||||
//EndAction can be called from anywhere.
|
||||
protected override void OnExecute(){
|
||||
try {
|
||||
agent.SetTrigger(trigger.value);
|
||||
} catch (Exception e) {
|
||||
Debug.LogError($"Did not set Network Animator trigger <i>{trigger.name}</i> on <b>{(agent == null ? null : agent.name)}</b>: {e.Message}");
|
||||
}
|
||||
agent.SendAnimationTrigger(trigger.value);
|
||||
|
||||
EndAction(true);
|
||||
}
|
||||
|
||||
|
||||
7
Assets/Scripts/Core/IUnitTargetProvider.cs
Normal file
7
Assets/Scripts/Core/IUnitTargetProvider.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Reset.Units{
|
||||
public interface IUnitTargetProvider{
|
||||
public GameObject UnitTarget{ get; }
|
||||
}
|
||||
}
|
||||
3
Assets/Scripts/Core/IUnitTargetProvider.cs.meta
Normal file
3
Assets/Scripts/Core/IUnitTargetProvider.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f0c49825086c41e0990e5fa5d3f0041c
|
||||
timeCreated: 1761269267
|
||||
@@ -15,7 +15,7 @@ using Vector2 = UnityEngine.Vector2;
|
||||
using Vector3 = UnityEngine.Vector3;
|
||||
|
||||
namespace Reset.Units{
|
||||
public class LockOnManager : MonoBehaviour{
|
||||
public class LockOnManager : UnitComponent, IUnitTargetProvider {
|
||||
public class ActiveLockOnTarget{
|
||||
public GameObject gameObject;
|
||||
public float targetWeight;
|
||||
@@ -24,6 +24,9 @@ namespace Reset.Units{
|
||||
}
|
||||
|
||||
public static LockOnManager Instance;
|
||||
|
||||
// IUnitTargetProvider
|
||||
public GameObject UnitTarget => mainTarget.gameObject;
|
||||
|
||||
// Lock On settings
|
||||
[Space(5)] public float lockOnRange = 40f;
|
||||
@@ -32,7 +35,7 @@ namespace Reset.Units{
|
||||
[FormerlySerializedAs("smoothing")] public float smoothTime = 1f;
|
||||
|
||||
// Lock On Tracking
|
||||
[Space(10)] public ActiveLockOnTarget mainTarget;
|
||||
[Space(10), ShowInInspector] public ActiveLockOnTarget mainTarget;
|
||||
|
||||
public List<ActiveLockOnTarget> activeTargets = new List<ActiveLockOnTarget>();
|
||||
|
||||
@@ -47,13 +50,13 @@ namespace Reset.Units{
|
||||
private VisualElement elementRoot;
|
||||
|
||||
private void Awake(){
|
||||
// // Register as singleton
|
||||
// if (Instance == null) {
|
||||
// Instance = this;
|
||||
// } else {
|
||||
// this.enabled = false;
|
||||
// return;
|
||||
// }
|
||||
// Register as singleton
|
||||
if (Instance == null && Unit.UnitIsLocal()) {
|
||||
Instance = this;
|
||||
} else {
|
||||
enabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// References from camera
|
||||
targetGroup = PlayerManager.Camera.transform.Find("Target Group").GetComponent<CinemachineTargetGroup>();
|
||||
@@ -267,7 +270,7 @@ namespace Reset.Units{
|
||||
|
||||
// Catch exception from nothing being found
|
||||
if (!closestTarget) {
|
||||
Debug.LogWarning("Lock-on attempted, but no lock on target was found viable.");
|
||||
Debug.LogWarning($"Lock-on attempted, but no lock on target was found viable. Searched {acceptedTargets.Count} targets.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -310,5 +313,4 @@ namespace Reset.Units{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
using System.Collections.Generic;
|
||||
using Sirenix.OdinInspector;
|
||||
using Unity.Netcode;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Reset.Items{
|
||||
public abstract class Item : ScriptableObject{
|
||||
public abstract class Item : SerializedScriptableObject{
|
||||
public string itemName;
|
||||
|
||||
public float permanency;
|
||||
|
||||
@@ -1,22 +1,58 @@
|
||||
using System;
|
||||
using Drawing;
|
||||
using NodeCanvas.StateMachines;
|
||||
using NodeCanvas.Framework;
|
||||
using Reset.Core;
|
||||
using Reset.Units;
|
||||
using Sirenix.OdinInspector;
|
||||
using Sirenix.Serialization;
|
||||
using Unity.Netcode;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Reset.Items{
|
||||
[CreateAssetMenu(menuName = "Reset/Weapon", fileName = "New Weapon")]
|
||||
public class Weapon : Item, IEquipable{
|
||||
public CombatType combatType;
|
||||
|
||||
public GameObject weaponModel;
|
||||
public NodeCanvas.Framework.Graph weaponFSM;
|
||||
|
||||
void Awake(){
|
||||
|
||||
}
|
||||
public Vector3 handPositionOffset;
|
||||
public Vector3 handRotationOffset;
|
||||
|
||||
public string actorScriptName;
|
||||
|
||||
[OdinSerialize, ShowInInspector] public Type actorScript;
|
||||
|
||||
public void AddActorScript(){
|
||||
// Type actorScript = Type.GetType("ShurikenActor");
|
||||
//
|
||||
// if (actorScript == null) {
|
||||
// Debug.LogError($"The WeaponActor supplied was not found for {itemName}. Does script named '{actorScriptName + ",Assembly-UnityScript"}' exist?");
|
||||
// return;
|
||||
// }
|
||||
|
||||
try {
|
||||
if (actorScript != null) {
|
||||
WeaponActor weaponActor = PlayerManager.Player.AddComponent(actorScript) as WeaponActor;
|
||||
|
||||
weaponActor.relatedObject = PlayerManager.Player.GetComponent<PlayerCombat>().GetCurrentWeaponItem();
|
||||
weaponActor.relatedWeapon = this;
|
||||
weaponActor.relatedGraph = PlayerManager.Player.GetComponent<GraphOwner>();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Debug.LogException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public override void DrawItemInfo(Vector3 position){
|
||||
Debug.Log("hiuh");
|
||||
Draw.ingame.Label2D(position + Vector3.up * 1.6f, "Damage goes here");
|
||||
Draw.ingame.Label2D(position + Vector3.up * 1.35f, "Speed goes here");
|
||||
}
|
||||
|
||||
|
||||
|
||||
public GameObject InstantiateItemObject(){
|
||||
return GameObject.Instantiate(weaponModel);
|
||||
}
|
||||
}
|
||||
}
|
||||
52
Assets/Scripts/Items/WeaponActor.cs
Normal file
52
Assets/Scripts/Items/WeaponActor.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NodeCanvas.Framework;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Reset.Items{
|
||||
public abstract class WeaponActor : MonoBehaviour{
|
||||
public Dictionary<string, Action> weaponEvents = new Dictionary<string, Action>();
|
||||
public Dictionary<string, object> weaponVariables = new Dictionary<string, object>();
|
||||
|
||||
public Weapon relatedWeapon;
|
||||
public GraphOwner relatedGraph;
|
||||
public GameObject relatedObject;
|
||||
|
||||
// Recieve Weapon Catch signal from Animation
|
||||
public void WeaponCatch(){
|
||||
relatedGraph.SendEvent("Weapon Catch");
|
||||
}
|
||||
|
||||
// Recieve Weapon Release signal from Animation
|
||||
public void WeaponRelease(){
|
||||
relatedGraph.SendEvent("Weapon Release");
|
||||
}
|
||||
|
||||
public void RegisterWeaponEvent(string calledName, Action action){
|
||||
weaponEvents.Add(calledName, action);
|
||||
}
|
||||
|
||||
public void RegisterWeaponVariable(string variable, object value){
|
||||
if (weaponVariables.ContainsKey(variable)) {
|
||||
weaponVariables[variable] = value;
|
||||
} else {
|
||||
weaponVariables.Add(variable, value);
|
||||
}
|
||||
}
|
||||
|
||||
public object ReadWeaponVariable<T>(string variable) where T : class{
|
||||
return (T)weaponVariables[variable];
|
||||
}
|
||||
|
||||
public void DoWeaponEvent(string eventName){
|
||||
if (weaponEvents.ContainsKey(eventName)) {
|
||||
weaponEvents[eventName].Invoke();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Debug.LogError($"There is no event by name of {eventName} in {name}");
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Assets/Scripts/Items/WeaponActor.cs.meta
Normal file
3
Assets/Scripts/Items/WeaponActor.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7db5375a3b8f4212b407f4f8f41a8870
|
||||
timeCreated: 1768000398
|
||||
3
Assets/Scripts/Items/Weapons.meta
Normal file
3
Assets/Scripts/Items/Weapons.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 70ef6ecafdc149559e4cb6e19787e719
|
||||
timeCreated: 1768000824
|
||||
146
Assets/Scripts/Items/Weapons/ShurikenActor.cs
Normal file
146
Assets/Scripts/Items/Weapons/ShurikenActor.cs
Normal file
@@ -0,0 +1,146 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
using Unity.Services.Relay.Models;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Assertions.Must;
|
||||
using Quaternion = UnityEngine.Quaternion;
|
||||
using Random = UnityEngine.Random;
|
||||
using Vector3 = UnityEngine.Vector3;
|
||||
|
||||
namespace Reset.Items{
|
||||
public class ShurikenActor : WeaponActor{
|
||||
public GameObject bladeRing;
|
||||
|
||||
public Transform target;
|
||||
private Vector3 targetPosition;
|
||||
|
||||
private Transform originalParent;
|
||||
private Quaternion originalRotation;
|
||||
private Vector3 originalPosition;
|
||||
|
||||
private Vector3 originalWorldPosition;
|
||||
|
||||
private float rotateSpeed;
|
||||
private float rotateSpeedTarget;
|
||||
private float rotationAcceleration;
|
||||
private Quaternion targetRotation;
|
||||
|
||||
void Awake(){
|
||||
// Register Weapon Events
|
||||
weaponEvents.Add("Set Target", SetTarget);
|
||||
weaponEvents.Add("Fly To Target", FlyToTarget);
|
||||
weaponEvents.Add("Fly To Hand", FlyToHand);
|
||||
weaponEvents.Add("Return To Hand", ReturnToHand);
|
||||
}
|
||||
|
||||
void SetTarget(){
|
||||
target = null;
|
||||
|
||||
if (weaponVariables["target"] != null) {
|
||||
target = (Transform)weaponVariables["target"];
|
||||
}
|
||||
}
|
||||
|
||||
void FlyToTarget(){
|
||||
rotateSpeedTarget = 1200f;
|
||||
rotationAcceleration = 200f;
|
||||
RegisterWeaponVariable("state", "Flying To Target");
|
||||
|
||||
float randomRot = Random.Range(-90f, 180f);
|
||||
targetRotation = transform.rotation * originalRotation * Quaternion.AngleAxis(randomRot, Vector3.up);
|
||||
|
||||
originalParent = relatedObject.transform.parent;
|
||||
originalPosition = relatedObject.transform.localPosition;
|
||||
originalRotation = relatedObject.transform.localRotation;
|
||||
|
||||
originalWorldPosition = relatedObject.transform.position;
|
||||
|
||||
relatedObject.transform.SetParent(null, true);
|
||||
}
|
||||
|
||||
void FlyToHand(){
|
||||
rotateSpeed = 3000f;
|
||||
rotateSpeedTarget = 50f;
|
||||
rotationAcceleration = 2f;
|
||||
|
||||
RegisterWeaponVariable("state", new string("Flying To Hand"));
|
||||
}
|
||||
|
||||
void ReturnToHand(){
|
||||
rotateSpeedTarget = 250f;
|
||||
rotationAcceleration = 1f;
|
||||
RegisterWeaponVariable("state", new string("Back In Hand"));
|
||||
|
||||
relatedObject.transform.SetParent(originalParent);
|
||||
relatedObject.transform.localPosition = originalPosition;
|
||||
relatedObject.transform.localRotation = originalRotation;
|
||||
|
||||
target = null;
|
||||
}
|
||||
|
||||
void Start(){
|
||||
// Save refernce to the blade outer ring
|
||||
bladeRing = relatedObject.transform.GetChild(0).gameObject;
|
||||
}
|
||||
|
||||
void Update(){
|
||||
|
||||
|
||||
if (target) {
|
||||
targetPosition = target.transform.position;
|
||||
} else {
|
||||
targetPosition = transform.position + transform.forward * 10f + Vector3.up;
|
||||
}
|
||||
|
||||
|
||||
|
||||
rotateSpeed = Mathf.Lerp(rotateSpeed, rotateSpeedTarget, rotationAcceleration * Time.deltaTime);
|
||||
bladeRing.transform.Rotate(Vector3.up * (rotateSpeed * Time.deltaTime));
|
||||
|
||||
Debug.Log((string)ReadWeaponVariable<string>("state"));
|
||||
|
||||
// Fly to the target
|
||||
if ((string)ReadWeaponVariable<string>("state") == "Flying To Target") {
|
||||
float offset = 9f;
|
||||
|
||||
Vector3 startPos = relatedObject.transform.position;
|
||||
Vector3 endPos = targetPosition;
|
||||
|
||||
Vector3 center = (startPos + endPos) / 2f;
|
||||
center -= new Vector3(0f, offset, 0f);
|
||||
Vector3 relativeCenter = startPos - center;
|
||||
Vector3 relativeEnd = endPos - center;
|
||||
|
||||
relatedObject.transform.position = Vector3.Slerp(relativeCenter, relativeEnd, 5f * Time.deltaTime) + center;
|
||||
relatedObject.transform.rotation = Quaternion.Lerp(relatedObject.transform.rotation, targetRotation, 5f * Time.deltaTime);
|
||||
|
||||
|
||||
|
||||
// relatedObject.transform.position = Vector3.Slerp(relatedObject.transform.position, targetPosition, 5f * Time.deltaTime);
|
||||
|
||||
// When there, set next stage
|
||||
if (Vector3.Distance(relatedObject.transform.position, targetPosition) < .5f) {
|
||||
rotationAcceleration = 50f;
|
||||
rotateSpeedTarget = 1200f;
|
||||
RegisterWeaponVariable("state", new string("At Target"));
|
||||
}
|
||||
}
|
||||
|
||||
// Fly to the hand
|
||||
if ((string)ReadWeaponVariable<string>("state") as string == "Flying To Hand") {
|
||||
relatedObject.transform.position = Vector3.Slerp(relatedObject.transform.position, originalParent.transform.position, 5f * Time.deltaTime);
|
||||
relatedObject.transform.rotation = Quaternion.Lerp(relatedObject.transform.rotation, originalParent.transform.rotation * originalRotation , 5f * Time.deltaTime);
|
||||
|
||||
// When there, set as finished
|
||||
if (Vector3.Distance(relatedObject.transform.position, originalParent.transform.position) < .1f) {
|
||||
RegisterWeaponVariable("state", new string("At Hand"));
|
||||
}
|
||||
}
|
||||
|
||||
RegisterWeaponVariable("relativePosition", relatedObject.transform.position - transform.position);
|
||||
RegisterWeaponVariable("relativeOffset", new Vector3(0f, relatedObject.transform.position.y, Vector3.Distance(relatedObject.transform.position, transform.position)));
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Assets/Scripts/Items/Weapons/ShurikenActor.cs.meta
Normal file
3
Assets/Scripts/Items/Weapons/ShurikenActor.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7d1d2a1a617247459542ec29b4f50785
|
||||
timeCreated: 1768002409
|
||||
6
Assets/Scripts/Units/Combat/CombatType.cs
Normal file
6
Assets/Scripts/Units/Combat/CombatType.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace Reset.Core{
|
||||
public enum CombatType{
|
||||
Melee = 0,
|
||||
Ranged = 1
|
||||
}
|
||||
}
|
||||
3
Assets/Scripts/Units/Combat/CombatType.cs.meta
Normal file
3
Assets/Scripts/Units/Combat/CombatType.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: eb3157c88aaa41f8b0b20d4690bb1459
|
||||
timeCreated: 1767997846
|
||||
@@ -7,7 +7,8 @@ using UnityEngine;
|
||||
using Random = UnityEngine.Random;
|
||||
|
||||
public class UnitCombat : UnitComponent {
|
||||
public List<Collider> draggedUnits = new List<Collider>();
|
||||
// public List<Collider> draggedUnits = new List<Collider>();
|
||||
public Dictionary<Collider, float> draggedColliders = new Dictionary<Collider, float>();
|
||||
|
||||
private UnitMovementHandler movement;
|
||||
|
||||
@@ -18,9 +19,15 @@ public class UnitCombat : UnitComponent {
|
||||
private float speedDelta;
|
||||
private float sinAmplitude;
|
||||
private float sinOffset;
|
||||
|
||||
private List<Collider> activelyDraggedColliders;
|
||||
private List<Collider> expiredColliders;
|
||||
|
||||
void Awake(){
|
||||
movement = GetComponent<UnitMovementHandler>();
|
||||
|
||||
activelyDraggedColliders = new List<Collider>();
|
||||
expiredColliders = new List<Collider>();
|
||||
}
|
||||
|
||||
void Start(){
|
||||
@@ -31,7 +38,53 @@ public class UnitCombat : UnitComponent {
|
||||
|
||||
// Update is called once per frame
|
||||
void Update(){
|
||||
// Calculate movement for dragged units
|
||||
DragAttackedUnits();
|
||||
|
||||
// Check timers to make sure an object doesn't stay dragged
|
||||
CheckUnitTimers();
|
||||
}
|
||||
|
||||
// dragTime is set as a maximum. If no time is provided then it defaults to two seconds.
|
||||
public void AddDragCollider(Collider newCollider, float dragTime = 2f){
|
||||
if (draggedColliders.ContainsKey(newCollider)) {
|
||||
draggedColliders[newCollider] = dragTime;
|
||||
} else {
|
||||
draggedColliders.Add(newCollider, dragTime);
|
||||
activelyDraggedColliders.Add(newCollider);
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveDragCollider(Collider colliderToRemove){
|
||||
draggedColliders.Remove(colliderToRemove);
|
||||
activelyDraggedColliders.Remove(colliderToRemove);
|
||||
}
|
||||
|
||||
public void CallAttack(){
|
||||
Debug.Log("Attacked!");
|
||||
Unit.Graph.SendEvent("Call Attack");
|
||||
}
|
||||
|
||||
void CheckUnitTimers(){
|
||||
// Decrease the timer of the dragged colliders
|
||||
foreach (Collider thisCollider in activelyDraggedColliders) {
|
||||
draggedColliders[thisCollider] -= 1f * Time.deltaTime;
|
||||
|
||||
// Pend them for removal when ready
|
||||
if (draggedColliders[thisCollider] < 0f) {
|
||||
expiredColliders.Add(thisCollider);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove expired colliders
|
||||
for (int i = 0; i < expiredColliders.Count; i++) {
|
||||
RemoveDragCollider(expiredColliders[i]);
|
||||
}
|
||||
|
||||
// Clear list if not empty
|
||||
if (expiredColliders.Count > 0f) {
|
||||
expiredColliders.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
void DragAttackedUnits(){
|
||||
@@ -58,12 +111,15 @@ public class UnitCombat : UnitComponent {
|
||||
lastPosition = transform.position;
|
||||
|
||||
// Apply the speed, direction, and rotation to each unit
|
||||
foreach (Collider draggedUnit in draggedUnits) {
|
||||
foreach (Collider draggedUnit in activelyDraggedColliders) {
|
||||
UnitMovementHandler draggedUnitMovement = draggedUnit.GetComponent<UnitMovementHandler>();
|
||||
if (!draggedUnitMovement) {
|
||||
Debug.LogError($"No available UnitMovement on {draggedUnit.name}. Aborting drag on this unit.");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Prevent units from entering a runaway situation where they constantly pull each other at high speeds across the map
|
||||
speedDelta = Mathf.Min(speedDelta, draggedUnitMovement.data.moveSpeed.Value);
|
||||
|
||||
draggedUnitMovement.SetNewRotation(-transform.position.DirectionTo(draggedUnit.transform.position), 1f, true);
|
||||
draggedUnitMovement.SetNewDirection(positionDelta.ToVector2(), 1f, true);
|
||||
|
||||
@@ -5,19 +5,13 @@ namespace Reset.Units{
|
||||
private bool enabledAsHost = true;
|
||||
|
||||
private Unit _unit;
|
||||
|
||||
internal Unit Unit{
|
||||
get {
|
||||
if (_unit != null) {
|
||||
return _unit;
|
||||
}
|
||||
|
||||
_unit = GetComponent<Unit>();
|
||||
|
||||
if (_unit == null) { _unit = GetComponent<Unit>(); }
|
||||
return _unit;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DisableComponent(){
|
||||
enabledAsHost = false;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
using System;
|
||||
using Reset.Units;
|
||||
using UnityEngine;
|
||||
using UnityEngine.InputSystem;
|
||||
using Unity.Cinemachine;
|
||||
using Unity.Services.Matchmaker.Models;
|
||||
|
||||
// This class receives input from a PlayerInput component and disptaches it
|
||||
// to the appropriate Cinemachine InputAxis. The playerInput component should
|
||||
@@ -32,7 +34,7 @@ class CustomInputHandler : InputAxisControllerBase<CustomInputHandler.Reader>
|
||||
// We process user input on the Update clock
|
||||
void Update()
|
||||
{
|
||||
if (Application.isPlaying){
|
||||
if (Application.isPlaying && PlayerManager.Player){
|
||||
UpdateControllers();
|
||||
Controllers[0].Input.ProcessInput(PlayerInput);
|
||||
Controllers[1].Input.ProcessInput(PlayerInput);
|
||||
|
||||
@@ -5,7 +5,10 @@ using Unity.Netcode;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Reset.Units{
|
||||
public class Enemy : Unit, ILockOnTarget, IKillable {
|
||||
public class Enemy : Unit, ILockOnTarget, IKillable{
|
||||
// Spawn Info
|
||||
public EnemySpawn relatedSpawner;
|
||||
|
||||
// Lock-On
|
||||
public float lockonTargetRadius{ get; set; } = 10f;
|
||||
[ShowInInspector]
|
||||
|
||||
8
Assets/Scripts/Units/Enemy.meta
Normal file
8
Assets/Scripts/Units/Enemy.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6e73b4c62d7c3214db04bcea1fb9f856
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
20
Assets/Scripts/Units/Enemy/EnemyCombat.cs
Normal file
20
Assets/Scripts/Units/Enemy/EnemyCombat.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using Sirenix.OdinInspector;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Reset.Units{
|
||||
public class EnemyCombat : UnitCombat, IUnitTargetProvider{
|
||||
public GameObject target;
|
||||
public GameObject UnitTarget => target;
|
||||
|
||||
[Button]
|
||||
public void SetNewTarget(GameObject newTarget){
|
||||
Unit.Graph.SendEvent("New Target Detected");
|
||||
target = newTarget;
|
||||
}
|
||||
|
||||
public void DropTarget(){
|
||||
target = null;
|
||||
Unit.Graph.SendEvent("Drop Target");
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Assets/Scripts/Units/Enemy/EnemyCombat.cs.meta
Normal file
3
Assets/Scripts/Units/Enemy/EnemyCombat.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f1528d25d3d94df59161993cb28bcb17
|
||||
timeCreated: 1761269778
|
||||
223
Assets/Scripts/Units/Enemy/EnemySpawn.cs
Normal file
223
Assets/Scripts/Units/Enemy/EnemySpawn.cs
Normal file
@@ -0,0 +1,223 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Drawing;
|
||||
using Pathfinding;
|
||||
using Sirenix.OdinInspector;
|
||||
using UnityEngine;
|
||||
using Random = UnityEngine.Random;
|
||||
|
||||
namespace Reset.Units{
|
||||
public class EnemySpawn : MonoBehaviour{
|
||||
enum SpawnerState{
|
||||
Idle = 0,
|
||||
TargetAcquired,
|
||||
TargetDropped,
|
||||
InCombatUnlocked,
|
||||
InCombatLocked,
|
||||
Cooldown,
|
||||
}
|
||||
|
||||
public float radius = 30f;
|
||||
|
||||
public int minimumEnemies = 1;
|
||||
public int maximumEnemies = 5;
|
||||
|
||||
public Vector2 enemyCount;
|
||||
|
||||
// TODO: Replace this with an Enemy selector based on difficulty, random chance, etc?
|
||||
public GameObject enemy;
|
||||
|
||||
public List<GameObject> enemies;
|
||||
|
||||
public GridGraph relatedGraph;
|
||||
|
||||
private SpawnerState spawnerState;
|
||||
private float timeInState;
|
||||
|
||||
void Start(){
|
||||
CreateAstarGraph();
|
||||
SpawnEnemies();
|
||||
}
|
||||
|
||||
void CreateAstarGraph(){
|
||||
relatedGraph = AstarPath.active.data.AddGraph(typeof(GridGraph)) as GridGraph;
|
||||
|
||||
relatedGraph.SetDimensions(Mathf.FloorToInt(radius) * 2, Mathf.FloorToInt(radius) * 2, 1f);
|
||||
relatedGraph.collision.diameter = 3f;
|
||||
|
||||
AstarPath.active.Scan(relatedGraph);
|
||||
|
||||
GetComponent<ProceduralGraphMover>().graph = relatedGraph;
|
||||
}
|
||||
|
||||
void SpawnEnemies(){
|
||||
int count = Random.Range(minimumEnemies, maximumEnemies + 1);
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
Vector3 newPosition = transform.position;
|
||||
|
||||
float randomX = Random.Range(-(radius / 2f), radius / 2f);
|
||||
float randomZ = Random.Range(-(radius / 2f), radius / 2f);
|
||||
|
||||
newPosition += new Vector3(randomX, transform.position.y, randomZ);
|
||||
|
||||
float randomRot = Random.Range(0f, 360f);
|
||||
|
||||
GameObject newEnemy = Instantiate(enemy, newPosition, Quaternion.AngleAxis(randomRot, Vector3.up));
|
||||
newEnemy.GetComponent<Enemy>().relatedSpawner = this;
|
||||
|
||||
enemies.Add(newEnemy);
|
||||
}
|
||||
}
|
||||
|
||||
void Update(){
|
||||
ProcessStateUpdate();
|
||||
DrawSpawnStateGizmos();
|
||||
}
|
||||
|
||||
// Set the new state of the spawner and also reset the timer for state management
|
||||
void ChangeState(SpawnerState newState){
|
||||
spawnerState = newState;
|
||||
ProcessStateStart();
|
||||
timeInState = 0f;
|
||||
}
|
||||
|
||||
|
||||
// Does initial actions on state switch
|
||||
void ProcessStateStart(){
|
||||
switch (spawnerState) {
|
||||
case SpawnerState.TargetDropped:
|
||||
foreach (GameObject thisEnemy in enemies) {
|
||||
thisEnemy.GetComponent<EnemyCombat>().DropTarget();
|
||||
}
|
||||
break;
|
||||
|
||||
case SpawnerState.InCombatUnlocked:
|
||||
foreach (GameObject thisEnemy in enemies) {
|
||||
thisEnemy.GetComponent<Enemy>().Graph.SendEvent("Enter Combat");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Actively processes the state of the spawner and its assocaited enemies
|
||||
void ProcessStateUpdate(){
|
||||
switch (spawnerState) {
|
||||
// Check if player is in range of this spawner. If so, set them as the new target
|
||||
case SpawnerState.Idle:
|
||||
if (PlayerIsInRange(radius, radius / 2f)) {
|
||||
foreach (GameObject thisEnemy in enemies) {
|
||||
thisEnemy.GetComponent<EnemyCombat>().SetNewTarget(PlayerManager.Player);
|
||||
}
|
||||
ChangeState(SpawnerState.TargetAcquired);
|
||||
}
|
||||
break;
|
||||
|
||||
// After grabbing a player in range, make sure they stay in range. If they aren't in range after 5 seconds, disengage. If they are or get too close, engage.
|
||||
case SpawnerState.TargetAcquired:
|
||||
if (PlayerIsInRange(8f, 8f) && timeInState > 1f) {
|
||||
ChangeState(SpawnerState.InCombatUnlocked);
|
||||
return;
|
||||
}
|
||||
|
||||
if (timeInState > 5f) {
|
||||
if (PlayerIsInRange(radius + 2f, radius * .6f)) {
|
||||
ChangeState(SpawnerState.InCombatUnlocked);
|
||||
return;
|
||||
}
|
||||
|
||||
ChangeState(SpawnerState.TargetDropped);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
// Wait a few seconds after dropping the target before doing anything else
|
||||
case SpawnerState.TargetDropped:
|
||||
if (timeInState > 3f) {
|
||||
ChangeState(SpawnerState.Idle);
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
// Set all units to combat. Drop combat if the target gets too far
|
||||
case SpawnerState.InCombatUnlocked:
|
||||
if (!PlayerIsInRange(radius * 1.5f, radius * 1.5f) && timeInState > 5f) {
|
||||
ChangeState(SpawnerState.TargetDropped);
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
// Lock the player to the spawn. Currently unused.
|
||||
case SpawnerState.InCombatLocked:
|
||||
break;
|
||||
|
||||
|
||||
// Long cooldown for after all units have been killed. Currently unused.
|
||||
case SpawnerState.Cooldown:
|
||||
break;
|
||||
}
|
||||
|
||||
// Increment state timer
|
||||
timeInState += 1f * Time.deltaTime;
|
||||
}
|
||||
|
||||
|
||||
|
||||
GameObject PlayerIsInRange(float spawnerRange, float enemyRange){
|
||||
// TODO: Make compatible with all players
|
||||
if (!PlayerManager.Player) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Vector3 playerPos = PlayerManager.Player.transform.position;
|
||||
|
||||
// Skip checking and return null/false if the player is nowhere near the input range
|
||||
if (Vector3.Distance(playerPos, transform.position) > spawnerRange * 2f) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// If they are in range, check if the player is close enough to either an enemy or the input range
|
||||
if (Vector3.Distance(playerPos, transform.position) < spawnerRange) {
|
||||
return PlayerManager.Player;
|
||||
}
|
||||
|
||||
foreach (GameObject thisEnemy in enemies) {
|
||||
if (Vector3.Distance(playerPos, thisEnemy.transform.position) < enemyRange) {
|
||||
return PlayerManager.Player;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void DrawSpawnStateGizmos(){
|
||||
Color cylinderColor = Color.blue;
|
||||
|
||||
switch (spawnerState) {
|
||||
case SpawnerState.Idle:
|
||||
break;
|
||||
case SpawnerState.TargetAcquired:
|
||||
cylinderColor = Color.coral;
|
||||
break;
|
||||
case SpawnerState.TargetDropped:
|
||||
cylinderColor = Color.khaki;
|
||||
break;
|
||||
case SpawnerState.InCombatUnlocked:
|
||||
cylinderColor = Color.red;
|
||||
break;
|
||||
case SpawnerState.InCombatLocked:
|
||||
cylinderColor = Color.violetRed;
|
||||
break;
|
||||
case SpawnerState.Cooldown:
|
||||
cylinderColor = Color.gray;
|
||||
break;
|
||||
}
|
||||
|
||||
Draw.WireCylinder(transform.position, transform.position + Vector3.up * 7f, radius, cylinderColor);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
2
Assets/Scripts/Units/Enemy/EnemySpawn.cs.meta
Normal file
2
Assets/Scripts/Units/Enemy/EnemySpawn.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dd8d1c9c0ddc5c14497e74371d4b0350
|
||||
@@ -26,6 +26,11 @@ public class EnvironmentObserver{
|
||||
BoxCast,
|
||||
SphereCast
|
||||
}
|
||||
|
||||
public enum CastOrigin{
|
||||
Owner,
|
||||
Location
|
||||
}
|
||||
|
||||
[PropertySpace(0, 5), LabelWidth(60)]
|
||||
public string label;
|
||||
@@ -40,6 +45,8 @@ public class EnvironmentObserver{
|
||||
[HideInInspector]
|
||||
public bool active;
|
||||
|
||||
public CastOrigin castOrigin;
|
||||
|
||||
// Parameters for Cast cast types
|
||||
[FoldoutGroup("Settings")] [HideIf("@castType == CastType.BoxOverlap || castType == CastType.SphereOverlap")]
|
||||
public float length;
|
||||
|
||||
8
Assets/Scripts/Units/Graph Tasks.meta
Normal file
8
Assets/Scripts/Units/Graph Tasks.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 114868761d02e2c4281c0daf79f7c446
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
35
Assets/Scripts/Units/Graph Tasks/CheckIfSpawnmate.cs
Normal file
35
Assets/Scripts/Units/Graph Tasks/CheckIfSpawnmate.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
using NodeCanvas.Framework;
|
||||
using ParadoxNotion.Design;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Reset.Units {
|
||||
[Category("Reset/Units")]
|
||||
[Description("Returns true if this unit is in the same spawn group as the provided value.")]
|
||||
public class CheckIfSpawnmate : ConditionTask<Enemy>{
|
||||
protected override string info{ get => $"{target} is spawnmate"; }
|
||||
|
||||
public BBParameter<GameObject> target;
|
||||
|
||||
//Use for initialization. This is called only once in the lifetime of the task.
|
||||
//Return null if init was successfull. Return an error string otherwise
|
||||
protected override string OnInit(){
|
||||
return null;
|
||||
}
|
||||
|
||||
//Called whenever the condition gets enabled.
|
||||
protected override void OnEnable() {
|
||||
|
||||
}
|
||||
|
||||
//Called whenever the condition gets disabled.
|
||||
protected override void OnDisable() {
|
||||
|
||||
}
|
||||
|
||||
//Called once per frame while the condition is active.
|
||||
//Return whether the condition is success or failure.
|
||||
protected override bool OnCheck(){
|
||||
return agent.relatedSpawner.enemies.Contains(target.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 847ff34f59879c844b597a39884f5fe8
|
||||
60
Assets/Scripts/Units/Graph Tasks/CheckWeaponActorVariable.cs
Normal file
60
Assets/Scripts/Units/Graph Tasks/CheckWeaponActorVariable.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
using NodeCanvas.Framework;
|
||||
using ParadoxNotion.Design;
|
||||
using UnityEngine;
|
||||
|
||||
|
||||
namespace Reset.Items{
|
||||
|
||||
[Category("Reset/Combat")]
|
||||
[Description("Check if the provided value matches the weapon actor's variable")]
|
||||
public class CheckWeaponActorVariable<T> : ConditionTask<Transform> where T : class{
|
||||
public BBParameter<string> variable;
|
||||
public BBParameter<T> value;
|
||||
|
||||
WeaponActor actor;
|
||||
|
||||
protected override string info{
|
||||
get{ return $"weapon actor variable <b>{variable.value}</b> is <b><i>{value.value}</b></i>"; }
|
||||
}
|
||||
|
||||
//Use for initialization. This is called only once in the lifetime of the task.
|
||||
//Return null if init was successfull. Return an error string otherwise
|
||||
protected override string OnInit(){
|
||||
return null;
|
||||
}
|
||||
|
||||
//Called whenever the condition gets enabled.
|
||||
protected override void OnEnable(){
|
||||
actor = agent.GetComponent<WeaponActor>();
|
||||
}
|
||||
|
||||
//Called whenever the condition gets disabled.
|
||||
protected override void OnDisable(){
|
||||
|
||||
}
|
||||
|
||||
//Called once per frame while the condition is active.
|
||||
//Return whether the condition is success or failure.
|
||||
protected override bool OnCheck(){
|
||||
if (!actor) {
|
||||
Debug.LogError(
|
||||
$"No weapon actor variable found on this player. Cannot check for value of {variable.value}.",
|
||||
agent);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (actor.weaponVariables.ContainsKey(variable.value)) {
|
||||
T valueAsType = value.value;
|
||||
|
||||
Debug.Log((T)actor.weaponVariables[variable.value]);
|
||||
Debug.Log(valueAsType);
|
||||
return ((T)actor.weaponVariables[variable.value]).Equals(valueAsType);
|
||||
|
||||
} else {
|
||||
Debug.LogError($"No variable found by name {variable.value} on the weapon actor", agent);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 33a915cdda8127941aa56ed4e845d813
|
||||
164
Assets/Scripts/Units/Graph Tasks/CircleTarget.cs
Normal file
164
Assets/Scripts/Units/Graph Tasks/CircleTarget.cs
Normal file
@@ -0,0 +1,164 @@
|
||||
using System;
|
||||
using NodeCanvas.Framework;
|
||||
using ParadoxNotion.Design;
|
||||
using Sirenix.Utilities;
|
||||
using UnityEngine;
|
||||
using Random = UnityEngine.Random;
|
||||
|
||||
|
||||
namespace Reset.Units {
|
||||
|
||||
[Category("Reset/Units")]
|
||||
[Description("Gives the unit a new direction that causes them to circle the target.")]
|
||||
public class CircleTarget : ActionTask<UnitMovementHandler>{
|
||||
enum StrafeDirection{
|
||||
Forward = 0, Backwards, Left, Right
|
||||
}
|
||||
|
||||
// References
|
||||
private IUnitTargetProvider targetProvider;
|
||||
|
||||
// Pause settings
|
||||
[Description("How long will this unit wait to stop moving and pause")]
|
||||
public BBParameter<Vector2> pauseIntervalRange;
|
||||
[Description("How long this unit will within paused movement")]
|
||||
public BBParameter<Vector2> unpauseIntervalRange;
|
||||
|
||||
// Speed settings
|
||||
public BBParameter<Vector2> speedRange;
|
||||
[Description("How long this unit will wait before switching movement speed")]
|
||||
public BBParameter<Vector2> speedChangeIntervalRange;
|
||||
[SliderField(0, 1)]
|
||||
public BBParameter<float> speedSmoothing;
|
||||
|
||||
// Strafe direction settings
|
||||
[Description("How long this unit will wait before switching strafe direction")]
|
||||
public BBParameter<Vector2> strafeDirectionChangeInterval;
|
||||
[SliderField(0, 1)]
|
||||
public BBParameter<float> directionSmoothing;
|
||||
|
||||
// Pause state management
|
||||
private bool strafePaused;
|
||||
private float lastPauseEnterTime;
|
||||
private float lastPauseExitTime;
|
||||
private float pauseChangeTimeElapsed;
|
||||
private float nextPauseChangeTime;
|
||||
|
||||
// Direct state management
|
||||
private float lastDirectionChangeTime;
|
||||
private float directionChangeTimeElapsed;
|
||||
private StrafeDirection currentStrafeDirection;
|
||||
private float nextDirectionChangeTime;
|
||||
|
||||
// Speed state management
|
||||
private float lastSpeedChangeTime;
|
||||
private float speedChangeTimeElapsed;
|
||||
private float currentSpeed;
|
||||
private float nextSpeedChangeTime;
|
||||
|
||||
//Use for initialization. This is called only once in the lifetime of the task.
|
||||
//Return null if init was successfull. Return an error string otherwise
|
||||
protected override string OnInit() {
|
||||
if (pauseIntervalRange.value == Vector2.zero){ Debug.LogError($"Please set a setting for pause intervals on {agent.name}", agent.gameObject);}
|
||||
if (unpauseIntervalRange.value == Vector2.zero){ Debug.LogError($"Please set a setting for unpause intervals on {agent.name}", agent.gameObject);}
|
||||
if (speedRange.value == Vector2.zero){ Debug.LogError($"Please set a setting for speed range on {agent.name}", agent.gameObject);}
|
||||
if (speedChangeIntervalRange.value == Vector2.zero){ Debug.LogError($"Please set a setting for speed change intervals on {agent.name}", agent.gameObject);}
|
||||
if (strafeDirectionChangeInterval.value == Vector2.zero){ Debug.LogError($"Please set a setting for strafe direction change intervals on {agent.name}", agent.gameObject);}
|
||||
|
||||
// Get reference to target provider
|
||||
targetProvider = agent.gameObject.GetComponent<IUnitTargetProvider>();
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
//This is called once each time the task is enabled.
|
||||
//Call EndAction() to mark the action as finished, either in success or failure.
|
||||
//EndAction can be called from anywhere.
|
||||
protected override void OnExecute(){
|
||||
// Reset timers
|
||||
lastPauseEnterTime = 0f;
|
||||
lastPauseExitTime = 0f;
|
||||
pauseChangeTimeElapsed = 0f;
|
||||
lastDirectionChangeTime = 0f;
|
||||
directionChangeTimeElapsed = 0f;
|
||||
|
||||
// Initialize with direction
|
||||
currentStrafeDirection = (StrafeDirection)Random.Range(0, 3);
|
||||
}
|
||||
|
||||
//Called once per frame while the action is active.
|
||||
protected override void OnUpdate() {
|
||||
// Set a new direction
|
||||
agent.SetNewDirection(GetVector3Direction().ToVector2(), directionSmoothing.value, true);
|
||||
|
||||
// Set paused or set new speed if unpaused
|
||||
if (strafePaused) {
|
||||
agent.SetNewSpeed(0f, speedSmoothing.value, true);
|
||||
} else {
|
||||
agent.SetNewSpeed(currentSpeed, speedSmoothing.value, true);
|
||||
}
|
||||
|
||||
// Check the timers
|
||||
CheckForChange();
|
||||
}
|
||||
|
||||
// Method to quickly transform a strafe direction into a Vector3
|
||||
Vector3 GetVector3Direction(){
|
||||
switch (currentStrafeDirection) {
|
||||
case StrafeDirection.Forward:
|
||||
return agent.transform.position.DirectionTo(targetProvider.UnitTarget.transform.position);
|
||||
case StrafeDirection.Backwards:
|
||||
return -agent.transform.position.DirectionTo(targetProvider.UnitTarget.transform.position);
|
||||
case StrafeDirection.Left:
|
||||
return Quaternion.AngleAxis(-90f, Vector3.up) * agent.transform.position.DirectionTo(targetProvider.UnitTarget.transform.position);
|
||||
case StrafeDirection.Right:
|
||||
return Quaternion.AngleAxis(90f, Vector3.up) * agent.transform.position.DirectionTo(targetProvider.UnitTarget.transform.position);
|
||||
default:
|
||||
return Vector3.zero;
|
||||
}
|
||||
}
|
||||
|
||||
void SetNewPauseChangeTime(){
|
||||
if (strafePaused) {
|
||||
nextPauseChangeTime = elapsedTime + Random.Range(unpauseIntervalRange.value.x, unpauseIntervalRange.value.y);
|
||||
} else {
|
||||
nextPauseChangeTime = elapsedTime + Random.Range(pauseIntervalRange.value.x, pauseIntervalRange.value.y);
|
||||
}
|
||||
}
|
||||
|
||||
void SetNewSpeedChangeTime(){
|
||||
nextSpeedChangeTime = elapsedTime + Random.Range(speedChangeIntervalRange.value.x, speedChangeIntervalRange.value.y);
|
||||
}
|
||||
|
||||
void SetNewDirectionChangeTime(){
|
||||
nextDirectionChangeTime = elapsedTime + Random.Range(strafeDirectionChangeInterval.value.x, strafeDirectionChangeInterval.value.y);
|
||||
}
|
||||
|
||||
void CheckForChange(){
|
||||
if (nextPauseChangeTime > elapsedTime) {
|
||||
strafePaused = !strafePaused;
|
||||
SetNewPauseChangeTime();
|
||||
}
|
||||
|
||||
if (nextSpeedChangeTime > elapsedTime) {
|
||||
currentSpeed = Random.Range(speedRange.value.x, speedRange.value.y);
|
||||
SetNewSpeedChangeTime();
|
||||
}
|
||||
|
||||
if (nextDirectionChangeTime > elapsedTime) {
|
||||
currentStrafeDirection = (StrafeDirection)Random.Range(0, 3);
|
||||
SetNewDirectionChangeTime();
|
||||
}
|
||||
}
|
||||
|
||||
//Called when the task is disabled.
|
||||
protected override void OnStop() {
|
||||
|
||||
}
|
||||
|
||||
//Called when the task is paused.
|
||||
protected override void OnPause() {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/Units/Graph Tasks/CircleTarget.cs.meta
Normal file
2
Assets/Scripts/Units/Graph Tasks/CircleTarget.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9f9afac70b34c5e44b747e86f7491aa3
|
||||
57
Assets/Scripts/Units/Graph Tasks/GetCurrentTarget.cs
Normal file
57
Assets/Scripts/Units/Graph Tasks/GetCurrentTarget.cs
Normal file
@@ -0,0 +1,57 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using NodeCanvas.Framework;
|
||||
using ParadoxNotion.Design;
|
||||
using UnityEngine;
|
||||
|
||||
|
||||
namespace Reset.Units {
|
||||
|
||||
[Category("Reset")]
|
||||
[Description("Get the current target and save it to a graph variable")]
|
||||
public class GetCurrentTarget : ActionTask<LockOnManager>{
|
||||
public BBParameter<Transform> target;
|
||||
|
||||
//Use for initialization. This is called only once in the lifetime of the task.
|
||||
//Return null if init was successfull. Return an error string otherwise
|
||||
protected override string OnInit() {
|
||||
return null;
|
||||
}
|
||||
|
||||
//This is called once each time the task is enabled.
|
||||
//Call EndAction() to mark the action as finished, either in success or failure.
|
||||
//EndAction can be called from anywhere.
|
||||
protected override void OnExecute(){
|
||||
if (agent.mainTarget == null) {
|
||||
Debug.LogWarning("There is no LockOnTarget to save as current target");
|
||||
EndAction(true);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
target.value = agent.mainTarget.gameObject.transform;
|
||||
} catch (Exception e) {
|
||||
Debug.LogError($"Failed to save the current LockOnTarget target: {e.Message}");
|
||||
EndAction(false);
|
||||
return;
|
||||
}
|
||||
|
||||
EndAction(true);
|
||||
}
|
||||
|
||||
//Called once per frame while the action is active.
|
||||
protected override void OnUpdate() {
|
||||
|
||||
}
|
||||
|
||||
//Called when the task is disabled.
|
||||
protected override void OnStop() {
|
||||
|
||||
}
|
||||
|
||||
//Called when the task is paused.
|
||||
protected override void OnPause() {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 13713276d513eea4186f28ea39cbf302
|
||||
50
Assets/Scripts/Units/Graph Tasks/GetWeaponActorVariable.cs
Normal file
50
Assets/Scripts/Units/Graph Tasks/GetWeaponActorVariable.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
using NodeCanvas.Framework;
|
||||
using ParadoxNotion.Design;
|
||||
using UnityEngine;
|
||||
|
||||
|
||||
namespace Reset.Items {
|
||||
|
||||
[Category("Reset/Combat")]
|
||||
[Description("Retrieve the valule of a weapon actor variable")]
|
||||
public class GetWeaponActorVariable<T> : ActionTask<Transform>{
|
||||
public BBParameter<string> variable;
|
||||
public BBParameter<T> saveValueTo;
|
||||
|
||||
//Use for initialization. This is called only once in the lifetime of the task.
|
||||
//Return null if init was successfull. Return an error string otherwise
|
||||
protected override string OnInit() {
|
||||
return null;
|
||||
}
|
||||
|
||||
//This is called once each time the task is enabled.
|
||||
//Call EndAction() to mark the action as finished, either in success or failure.
|
||||
//EndAction can be called from anywhere.
|
||||
protected override void OnExecute() {
|
||||
WeaponActor actor = agent.GetComponent<WeaponActor>();
|
||||
|
||||
if (!actor) {
|
||||
Debug.LogError($"No weapon actor variable found on this player. Cannot check for value of {variable.value}.", agent);
|
||||
EndAction(false);
|
||||
}
|
||||
|
||||
saveValueTo.value = (T)actor.weaponVariables[variable.value];
|
||||
EndAction(true);
|
||||
}
|
||||
|
||||
//Called once per frame while the action is active.
|
||||
protected override void OnUpdate() {
|
||||
|
||||
}
|
||||
|
||||
//Called when the task is disabled.
|
||||
protected override void OnStop() {
|
||||
|
||||
}
|
||||
|
||||
//Called when the task is paused.
|
||||
protected override void OnPause() {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f379bc09216af524290d9b72067031d5
|
||||
56
Assets/Scripts/Units/Graph Tasks/SendWeaponActorEvent.cs
Normal file
56
Assets/Scripts/Units/Graph Tasks/SendWeaponActorEvent.cs
Normal file
@@ -0,0 +1,56 @@
|
||||
using System;
|
||||
using NodeCanvas.Framework;
|
||||
using ParadoxNotion.Design;
|
||||
using Reset.Items;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Reset.Items {
|
||||
|
||||
[Category("Reset/Combat")]
|
||||
[Description("Sends a named event as a string to the current weapon's WeaponActor.")]
|
||||
public class SendWeaponActorEvent : ActionTask<Transform>{
|
||||
|
||||
public BBParameter<string> weaponEvent;
|
||||
|
||||
protected override string info{
|
||||
get{ return $"Send weapon event <b>{weaponEvent.value}</b>"; }
|
||||
}
|
||||
|
||||
//Use for initialization. This is called only once in the lifetime of the task.
|
||||
//Return null if init was successfull. Return an error string otherwise
|
||||
protected override string OnInit() {
|
||||
return null;
|
||||
}
|
||||
|
||||
//This is called once each time the task is enabled.
|
||||
//Call EndAction() to mark the action as finished, either in success or failure.
|
||||
//EndAction can be called from anywhere.
|
||||
protected override void OnExecute(){
|
||||
WeaponActor actor = agent.GetComponent<WeaponActor>();
|
||||
|
||||
if (actor == null) {
|
||||
Debug.LogError("No WeaponActor was found on this player.");
|
||||
EndAction(false);
|
||||
}
|
||||
|
||||
actor.DoWeaponEvent(weaponEvent.value);
|
||||
|
||||
EndAction(true);
|
||||
}
|
||||
|
||||
//Called once per frame while the action is active.
|
||||
protected override void OnUpdate() {
|
||||
|
||||
}
|
||||
|
||||
//Called when the task is disabled.
|
||||
protected override void OnStop() {
|
||||
|
||||
}
|
||||
|
||||
//Called when the task is paused.
|
||||
protected override void OnPause() {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 567e649f23e3ba44ea1339e928d17b83
|
||||
65
Assets/Scripts/Units/Graph Tasks/SetNewPathfindingPath.cs
Normal file
65
Assets/Scripts/Units/Graph Tasks/SetNewPathfindingPath.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
using NodeCanvas.Framework;
|
||||
using ParadoxNotion.Design;
|
||||
using Pathfinding;
|
||||
using UnityEngine;
|
||||
|
||||
|
||||
namespace Reset.Units {
|
||||
|
||||
[Category("Reset/Units")]
|
||||
[Description("Set a new path towards a provided target")]
|
||||
public class SetNewPathfindingPath : ActionTask<UnitPathfinding>{
|
||||
public BBParameter<GameObject> target;
|
||||
|
||||
public bool useTimer;
|
||||
public BBParameter<float> timeToRepath;
|
||||
|
||||
//Use for initialization. This is called only once in the lifetime of the task.
|
||||
//Return null if init was successfull. Return an error string otherwise
|
||||
protected override string OnInit() {
|
||||
if (target.isNoneOrNull) {
|
||||
Debug.Log($"There is no target set for {agent.name} to pathfind to.", agent.gameObject);
|
||||
return $"There is no target set for {agent.name} to pathfind to.";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
//This is called once each time the task is enabled.
|
||||
//Call EndAction() to mark the action as finished, either in success or failure.
|
||||
//EndAction can be called from anywhere.
|
||||
protected override void OnExecute() {
|
||||
agent.seeker.StartPath(agent.transform.position, target.value.transform.position, OnPathComplete);
|
||||
|
||||
if (useTimer) {
|
||||
return;
|
||||
}
|
||||
|
||||
EndAction(true);
|
||||
}
|
||||
|
||||
public void OnPathComplete(Path p){
|
||||
if (!p.error) {
|
||||
agent.AssignNewPath(p);
|
||||
}
|
||||
}
|
||||
|
||||
//Called once per frame while the action is active.
|
||||
protected override void OnUpdate() {
|
||||
if (useTimer && elapsedTime > timeToRepath.value) {
|
||||
agent.seeker.StartPath(agent.transform.position, target.value.transform.position, OnPathComplete);
|
||||
EndAction();
|
||||
}
|
||||
}
|
||||
|
||||
//Called when the task is disabled.
|
||||
protected override void OnStop() {
|
||||
|
||||
}
|
||||
|
||||
//Called when the task is paused.
|
||||
protected override void OnPause() {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 71ce4953752dde3449673b8931aefc00
|
||||
64
Assets/Scripts/Units/Graph Tasks/SetWanderingPath.cs
Normal file
64
Assets/Scripts/Units/Graph Tasks/SetWanderingPath.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
using NodeCanvas.Framework;
|
||||
using ParadoxNotion.Design;
|
||||
using Pathfinding;
|
||||
using UnityEngine;
|
||||
|
||||
|
||||
namespace Reset.Units {
|
||||
|
||||
[Category("Reset/Pathfinding")]
|
||||
[Description("Set the agent's current path to a wandering path.")]
|
||||
public class SetWanderingPath : ActionTask<UnitPathfinding> {
|
||||
|
||||
//Use for initialization. This is called only once in the lifetime of the task.
|
||||
//Return null if init was successfull. Return an error string otherwise
|
||||
protected override string OnInit() {
|
||||
return null;
|
||||
}
|
||||
|
||||
//This is called once each time the task is enabled.
|
||||
//Call EndAction() to mark the action as finished, either in success or failure.
|
||||
//EndAction can be called from anywhere.
|
||||
protected override void OnExecute(){
|
||||
Enemy thisEnemy = agent.Unit as Enemy;
|
||||
Vector3 pathTargetPos = agent.transform.position;
|
||||
|
||||
if (thisEnemy.relatedSpawner) {
|
||||
pathTargetPos = thisEnemy.relatedSpawner.transform.position;
|
||||
}
|
||||
|
||||
Vector3 randomizedDestination = new Vector3(
|
||||
Random.Range(-thisEnemy.relatedSpawner.radius, thisEnemy.relatedSpawner.radius),
|
||||
0f,
|
||||
Random.Range(-thisEnemy.relatedSpawner.radius, thisEnemy.relatedSpawner.radius)
|
||||
);
|
||||
|
||||
pathTargetPos += randomizedDestination;
|
||||
|
||||
agent.seeker.StartPath(agent.transform.position, pathTargetPos, OnPathComplete);
|
||||
|
||||
EndAction(true);
|
||||
}
|
||||
|
||||
public void OnPathComplete(Path p){
|
||||
if (!p.error) {
|
||||
agent.AssignNewPath(p);
|
||||
}
|
||||
}
|
||||
|
||||
//Called once per frame while the action is active.
|
||||
protected override void OnUpdate() {
|
||||
|
||||
}
|
||||
|
||||
//Called when the task is disabled.
|
||||
protected override void OnStop() {
|
||||
|
||||
}
|
||||
|
||||
//Called when the task is paused.
|
||||
protected override void OnPause() {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a8b7661016b47ae4e9137833e7111460
|
||||
61
Assets/Scripts/Units/Graph Tasks/SetWeaponActorVariable.cs
Normal file
61
Assets/Scripts/Units/Graph Tasks/SetWeaponActorVariable.cs
Normal file
@@ -0,0 +1,61 @@
|
||||
using System;
|
||||
using NodeCanvas.Framework;
|
||||
using ParadoxNotion.Design;
|
||||
using UnityEngine;
|
||||
|
||||
|
||||
namespace Reset.Items {
|
||||
|
||||
[Category("Reset/Combat")]
|
||||
[Description("Set an existing or new weapon actor variable to the defined value.")]
|
||||
public class SetWeaponActorVariable : ActionTask<Transform>{
|
||||
public BBParameter<string> variable;
|
||||
public BBParameter<object> value;
|
||||
|
||||
protected override string info{
|
||||
get{ return $"Set weapon variable <b>{variable.value}</b> == <i>{value.name}</i>"; }
|
||||
}
|
||||
|
||||
//Use for initialization. This is called only once in the lifetime of the task.
|
||||
//Return null if init was successfull. Return an error string otherwise
|
||||
protected override string OnInit() {
|
||||
return null;
|
||||
}
|
||||
|
||||
//This is called once each time the task is enabled.
|
||||
//Call EndAction() to mark the action as finished, either in success or failure.
|
||||
//EndAction can be called from anywhere.
|
||||
protected override void OnExecute() {
|
||||
WeaponActor actor = agent.GetComponent<WeaponActor>();
|
||||
|
||||
if (actor == null) {
|
||||
Debug.LogError("No WeaponActor was found on this player.");
|
||||
EndAction(false);
|
||||
}
|
||||
|
||||
try {
|
||||
actor.RegisterWeaponVariable(variable.value, value.value);
|
||||
} catch (Exception e) {
|
||||
Debug.LogError($"Failed to set weapon variable '{variable.value}': {e.Message} ");
|
||||
EndAction(false);
|
||||
}
|
||||
|
||||
EndAction(true);
|
||||
}
|
||||
|
||||
//Called once per frame while the action is active.
|
||||
protected override void OnUpdate() {
|
||||
|
||||
}
|
||||
|
||||
//Called when the task is disabled.
|
||||
protected override void OnStop() {
|
||||
|
||||
}
|
||||
|
||||
//Called when the task is paused.
|
||||
protected override void OnPause() {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ff6862d725648584c89631637986f89c
|
||||
41
Assets/Scripts/Units/Graph Tasks/TogglePathfinding.cs
Normal file
41
Assets/Scripts/Units/Graph Tasks/TogglePathfinding.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
using NodeCanvas.Framework;
|
||||
using ParadoxNotion.Design;
|
||||
|
||||
|
||||
namespace Reset.Units {
|
||||
|
||||
[Category("Reset/Units")]
|
||||
[Description("Set whether this unit can use pathfinding or not.")]
|
||||
public class TogglePathfinding : ActionTask<UnitPathfinding>{
|
||||
public BBParameter<bool> value;
|
||||
|
||||
//Use for initialization. This is called only once in the lifetime of the task.
|
||||
//Return null if init was successfull. Return an error string otherwise
|
||||
protected override string OnInit() {
|
||||
return null;
|
||||
}
|
||||
|
||||
//This is called once each time the task is enabled.
|
||||
//Call EndAction() to mark the action as finished, either in success or failure.
|
||||
//EndAction can be called from anywhere.
|
||||
protected override void OnExecute(){
|
||||
agent.pathfindingEnabled = value.value;
|
||||
EndAction(true);
|
||||
}
|
||||
|
||||
//Called once per frame while the action is active.
|
||||
protected override void OnUpdate() {
|
||||
|
||||
}
|
||||
|
||||
//Called when the task is disabled.
|
||||
protected override void OnStop() {
|
||||
|
||||
}
|
||||
|
||||
//Called when the task is paused.
|
||||
protected override void OnPause() {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e7bebfffaed94604db733c72cde6be7d
|
||||
7
Assets/Scripts/Units/IUnitDirectionProvider.cs
Normal file
7
Assets/Scripts/Units/IUnitDirectionProvider.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Reset.Units{
|
||||
public interface IUnitDirectionProvider{
|
||||
public Vector2 Direction{ get; }
|
||||
}
|
||||
}
|
||||
3
Assets/Scripts/Units/IUnitDirectionProvider.cs.meta
Normal file
3
Assets/Scripts/Units/IUnitDirectionProvider.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ba6af7dc2e654035a720449447af3fa8
|
||||
timeCreated: 1761268431
|
||||
@@ -5,12 +5,12 @@ using UnityEngine.InputSystem;
|
||||
namespace Reset.Units{
|
||||
public class InteractionHandler : MonoBehaviour{
|
||||
private PlayerEnvironmentManager envManager;
|
||||
private Inventory inventory;
|
||||
private IInventory inventory;
|
||||
private EnvironmentObserver observer;
|
||||
|
||||
void Awake(){
|
||||
envManager = GetComponent<PlayerEnvironmentManager>();
|
||||
inventory = GetComponent<Inventory>();
|
||||
inventory = GetComponent<IInventory>();
|
||||
|
||||
observer = envManager.FindObserverFromString("itemdrop");
|
||||
|
||||
|
||||
@@ -1,47 +1,13 @@
|
||||
using System.Collections.Generic;
|
||||
using NodeCanvas.Tasks.Actions;
|
||||
using System.Collections.Generic;
|
||||
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 interface IInventory{
|
||||
public List<Item> storedItems{ get; set; }
|
||||
|
||||
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(){
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,2 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d57ab8ec1cbbdcf46813f268625d3494
|
||||
fileFormatVersion: 2
|
||||
guid: daf46ad8d27d4133a55f13a0c8a4efd3
|
||||
timeCreated: 1767808174
|
||||
@@ -8,6 +8,7 @@ using Unity.Netcode;
|
||||
namespace Reset.Units{
|
||||
public class Player : Unit, IKillable, IInteractable{
|
||||
// IKillable
|
||||
[ShowInInspector]
|
||||
public float maxHealth{ get; set; }
|
||||
public float currentHealth{ get; set; }
|
||||
|
||||
@@ -32,7 +33,7 @@ namespace Reset.Units{
|
||||
public float lastKnownReviveTime;
|
||||
|
||||
void Awake(){
|
||||
maxHealth = 20f;
|
||||
|
||||
}
|
||||
|
||||
void AttachToGame(){
|
||||
@@ -82,21 +83,28 @@ namespace Reset.Units{
|
||||
|
||||
// Tell every unit to set the new health value
|
||||
if (UnitIsNetworked()) {
|
||||
SetHealthRpc(newHealth);
|
||||
SetHealthRpc(newHealth);
|
||||
} else {
|
||||
SetHealth(newHealth);
|
||||
}
|
||||
}
|
||||
|
||||
[Rpc(SendTo.Everyone)]
|
||||
public void SetHealthRpc(float health){
|
||||
// Set health to new value, clamped to 0
|
||||
health = Mathf.Max(health, 0f);
|
||||
currentHealth = health;
|
||||
|
||||
SetHealth(health);
|
||||
|
||||
// For local players, run things based on health value.
|
||||
// This Rpc is global but only the owner checks health
|
||||
CheckHealth();
|
||||
}
|
||||
|
||||
private void SetHealth(float health){
|
||||
|
||||
health = Mathf.Max(health, 0f);
|
||||
currentHealth = health;
|
||||
}
|
||||
|
||||
void CheckHealth(){
|
||||
if (UnitIsLocal()){
|
||||
if (currentHealth <= 0f) {
|
||||
|
||||
8
Assets/Scripts/Units/PlayerAnimation.cs
Normal file
8
Assets/Scripts/Units/PlayerAnimation.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Reset.Units{
|
||||
public class PlayerAnimation : UnitAnimation{
|
||||
public Transform rightHand;
|
||||
public Transform leftHand;
|
||||
}
|
||||
}
|
||||
3
Assets/Scripts/Units/PlayerAnimation.cs.meta
Normal file
3
Assets/Scripts/Units/PlayerAnimation.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6974bcad59fb407296da5a7ed7f7a79c
|
||||
timeCreated: 1767815017
|
||||
92
Assets/Scripts/Units/PlayerCombat.cs
Normal file
92
Assets/Scripts/Units/PlayerCombat.cs
Normal file
@@ -0,0 +1,92 @@
|
||||
using System;
|
||||
using Reset.Core;
|
||||
using Reset.Items;
|
||||
using Unity.Netcode;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Profiling;
|
||||
|
||||
namespace Reset.Units{
|
||||
public class PlayerCombat : UnitCombat{
|
||||
public CombatType currentCombatType;
|
||||
|
||||
private IEquipable currentWeapon;
|
||||
private GameObject currentWeaponItem;
|
||||
|
||||
public void DrawWeapon(){
|
||||
if (Unit.UnitIsNetworked()) {
|
||||
CreatePlayerWeaponRpc();
|
||||
} else {
|
||||
CreatePlayerWeapon();
|
||||
}
|
||||
}
|
||||
|
||||
[Rpc(SendTo.Everyone)]
|
||||
public void CreatePlayerWeaponRpc(){
|
||||
CreatePlayerWeapon();
|
||||
}
|
||||
|
||||
public void CreatePlayerWeapon(){
|
||||
// Remove a current weapon
|
||||
DisposeCurrentWeapon();
|
||||
|
||||
// Remove a current weapon
|
||||
if (currentWeapon != null) {
|
||||
Destroy(currentWeaponItem);
|
||||
currentWeaponItem = null;
|
||||
}
|
||||
|
||||
// Switch which weapopn gets pulled out
|
||||
Weapon weaponType = null;
|
||||
|
||||
switch ((Unit.Combat as PlayerCombat).currentCombatType) {
|
||||
case CombatType.Melee:
|
||||
weaponType = ((PlayerInventory)Unit.Inventory).meleeWeapon;
|
||||
break;
|
||||
case CombatType.Ranged:
|
||||
weaponType = ((PlayerInventory)Unit.Inventory).rangedWeapon;
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
// Add weapon to status and hand
|
||||
currentWeapon = weaponType;
|
||||
currentWeaponItem = weaponType.InstantiateItemObject();
|
||||
|
||||
// Move item to hand
|
||||
currentWeaponItem.transform.SetParent((Unit.Animation as PlayerAnimation).rightHand);
|
||||
currentWeaponItem.transform.localPosition = weaponType.handPositionOffset;
|
||||
currentWeaponItem.transform.rotation = (Unit.Animation as PlayerAnimation).rightHand.rotation * Quaternion.Euler(weaponType.handRotationOffset);
|
||||
|
||||
// Add the weapon script for this weapon
|
||||
(currentWeapon as Weapon).AddActorScript();
|
||||
}
|
||||
|
||||
public GameObject GetCurrentWeaponItem(){
|
||||
return currentWeaponItem;
|
||||
}
|
||||
|
||||
public void HolsterWeapon(){
|
||||
DisposeCurrentWeapon();
|
||||
}
|
||||
|
||||
public void DisposeCurrentWeapon(){
|
||||
// Return if no weapon active
|
||||
if (currentWeapon == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Destroy physical mesh
|
||||
Destroy(currentWeaponItem);
|
||||
|
||||
// Destroy weapon actor
|
||||
if ((GetComponent<WeaponActor>()) != null) {
|
||||
Destroy(GetComponent<WeaponActor>());
|
||||
}
|
||||
|
||||
// Remove references
|
||||
currentWeaponItem = null;
|
||||
currentWeapon = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Assets/Scripts/Units/PlayerCombat.cs.meta
Normal file
3
Assets/Scripts/Units/PlayerCombat.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e2b74fc5d4eb483bb4e637c3be6353b2
|
||||
timeCreated: 1767998040
|
||||
@@ -13,14 +13,17 @@ using Sirenix.OdinInspector;
|
||||
using Unity.Cinemachine;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
public class PlayerControls : MonoBehaviour{
|
||||
public class PlayerControls : MonoBehaviour, IUnitDirectionProvider{
|
||||
// References
|
||||
private Player thisPlayer;
|
||||
|
||||
private PlayerInput input;
|
||||
private SignalDefinition inputSignal;
|
||||
private SignalDefinition blockSignal;
|
||||
|
||||
|
||||
// IUnitDirectionProvider
|
||||
public Vector2 Direction => rawMoveInput;
|
||||
|
||||
// TODO: Turn these into accessors
|
||||
public Vector2 rawMoveInput;
|
||||
public Vector2 rawLookInput;
|
||||
|
||||
34
Assets/Scripts/Units/PlayerInventory.cs
Normal file
34
Assets/Scripts/Units/PlayerInventory.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using System.Collections.Generic;
|
||||
using Reset.Items;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Reset.Units{
|
||||
public class PlayerInventory : UnitComponent, IInventory{
|
||||
public Weapon rangedWeapon;
|
||||
public Weapon meleeWeapon;
|
||||
|
||||
public Ability spellAbility1;
|
||||
public Ability spellAbility2;
|
||||
|
||||
public Ability toolAbility1;
|
||||
public Ability toolAbility2;
|
||||
|
||||
public List<Item> storedItems{ get; set; }
|
||||
|
||||
public void EquipToCharacter(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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/Units/PlayerInventory.cs.meta
Normal file
2
Assets/Scripts/Units/PlayerInventory.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d57ab8ec1cbbdcf46813f268625d3494
|
||||
@@ -22,8 +22,16 @@ public class SettingValue<T> : IResettableSettingValue{
|
||||
|
||||
public T Value{
|
||||
get => currentValue;
|
||||
set => targetValue = value;
|
||||
set{
|
||||
if (IsSmoothable()) {
|
||||
targetValue = value;
|
||||
} else {
|
||||
currentValue = value;
|
||||
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")]
|
||||
|
||||
@@ -24,6 +24,30 @@ namespace Reset.Units{
|
||||
get{ if (!_graph) { _graph = GetComponent<GraphOwner>(); } return _graph; }
|
||||
}
|
||||
|
||||
private UnitCombat _combat;
|
||||
internal UnitCombat Combat{
|
||||
get {
|
||||
if (_combat == null) { _combat = GetComponent<UnitCombat>(); }
|
||||
return _combat;
|
||||
}
|
||||
}
|
||||
|
||||
private UnitAnimation _animation;
|
||||
internal UnitAnimation Animation{
|
||||
get {
|
||||
if (_animation == null) { _animation = GetComponent<UnitAnimation>(); }
|
||||
return _animation;
|
||||
}
|
||||
}
|
||||
|
||||
private IInventory _inventory;
|
||||
internal IInventory Inventory{
|
||||
get {
|
||||
if (_inventory == null) { _inventory = GetComponent<IInventory>(); }
|
||||
return _inventory;
|
||||
}
|
||||
}
|
||||
|
||||
// Debug and Gizmos
|
||||
public NetworkVariable<FixedString64Bytes> graphStateAsString;
|
||||
|
||||
@@ -94,13 +118,18 @@ namespace Reset.Units{
|
||||
|
||||
private void DrawStateGizmo(){
|
||||
// Get state from FSM
|
||||
if (UnitIsLocal()) {
|
||||
|
||||
string stateString = "";
|
||||
if (UnitIsNetworked() && UnitIsLocal()) {
|
||||
graphStateAsString.Value = FSM.currentRootStateName;
|
||||
stateString = graphStateAsString.Value.ToString();
|
||||
} else {
|
||||
stateString = FSM.currentRootStateName;
|
||||
}
|
||||
|
||||
// Draw state gizmo, regardless of if local or not
|
||||
try {
|
||||
Draw.ingame.Label2D(transform.position + Vector3.up * 2.7f, graphStateAsString.Value.ToString(),
|
||||
Draw.ingame.Label2D(transform.position + Vector3.up * 2.7f, stateString,
|
||||
Color.red);
|
||||
} catch (Exception e) {
|
||||
Debug.LogError(e.Message);
|
||||
|
||||
@@ -1,25 +1,94 @@
|
||||
using System;
|
||||
using Unity.Netcode.Components;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
namespace Reset.Units{
|
||||
public class UnitAnimation : UnitComponent{
|
||||
public Animator modelAnimator;
|
||||
|
||||
public Transform headBone;
|
||||
public Transform neckBone;
|
||||
|
||||
public Vector3 headRotOffset;
|
||||
public Vector3 neckRotOffset;
|
||||
|
||||
public Vector3 rotationLimit;
|
||||
|
||||
public float moveSpeed;
|
||||
|
||||
// Temporary
|
||||
private float inputMagnitude;
|
||||
|
||||
private NetworkAnimator netAnimator;
|
||||
|
||||
void Update(){
|
||||
// Temporary
|
||||
try {
|
||||
inputMagnitude = Mathf.MoveTowards(inputMagnitude, GetComponent<PlayerControls>().rawMoveInput.magnitude * 2f, 6f * Time.deltaTime);
|
||||
// try {
|
||||
// inputMagnitude = Mathf.MoveTowards(inputMagnitude, GetComponent<PlayerControls>().rawMoveInput.magnitude * 2f, 6f * Time.deltaTime);
|
||||
//
|
||||
// modelAnimator.SetFloat("Move Direction X", Unit.Movement.GetResolvedDirectionLocal().x * inputMagnitude);
|
||||
// modelAnimator.SetFloat("Move Direction Y", Unit.Movement.GetResolvedDirectionLocal().y * inputMagnitude);
|
||||
// } catch (Exception e) {
|
||||
// Debug.LogError($"Failed in setting X and Y move direction floats: {e.Message}");
|
||||
// }
|
||||
|
||||
modelAnimator.SetFloat("Move Direction X", moveSpeed);
|
||||
modelAnimator.SetFloat("Gravity", Unit.Movement.resolvedMovement.gravity);
|
||||
|
||||
modelAnimator.SetBool("Grounded", Physics.Raycast(transform.position, Vector3.down, .2f));
|
||||
}
|
||||
|
||||
public void SendAnimationTrigger(string trigger){
|
||||
if (Unit.UnitIsNetworked()) {
|
||||
try {
|
||||
netAnimator.SetTrigger(trigger);
|
||||
} catch (Exception e){
|
||||
Debug.LogError($"Failed to send network animation trigger: {e.Message}");
|
||||
}
|
||||
|
||||
modelAnimator.SetFloat("Move Direction X", Unit.Movement.GetResolvedDirectionLocal().x * inputMagnitude);
|
||||
modelAnimator.SetFloat("Move Direction Y", Unit.Movement.GetResolvedDirectionLocal().y * inputMagnitude);
|
||||
} catch (Exception e) {
|
||||
Debug.LogError($"Failed in setting X and Y move direction floats: {e.Message}");
|
||||
} else {
|
||||
modelAnimator.SetTrigger(trigger);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void LateUpdate(){
|
||||
// TurnHead();
|
||||
}
|
||||
|
||||
void TurnHead(){
|
||||
var targetObject = (Unit.Combat as EnemyCombat).UnitTarget;
|
||||
|
||||
if (targetObject) {
|
||||
Vector3 headDirToTarget = headBone.position.DirectionTo(targetObject.transform.position);
|
||||
Vector3 neckDirToTarget = neckBone.position.DirectionTo(targetObject.transform.position);
|
||||
|
||||
Quaternion newHeadRot = Quaternion.LookRotation(headDirToTarget);
|
||||
Quaternion newNeckRot = Quaternion.LookRotation(neckDirToTarget);
|
||||
|
||||
Quaternion offsetHeadRot = Quaternion.Euler(headRotOffset);
|
||||
Quaternion offsetNeckRot = Quaternion.Euler(neckRotOffset);
|
||||
|
||||
Debug.Log(offsetNeckRot.eulerAngles);
|
||||
|
||||
Vector3 clampedHeadRot = new Vector3(
|
||||
Mathf.Clamp(newHeadRot.eulerAngles.x, -rotationLimit.x, rotationLimit.x),
|
||||
Mathf.Clamp(newHeadRot.eulerAngles.y, -rotationLimit.y, rotationLimit.y),
|
||||
Mathf.Clamp(newHeadRot.eulerAngles.z, -rotationLimit.z, rotationLimit.z)
|
||||
);
|
||||
|
||||
Vector3 clampedNeckRot = new Vector3(
|
||||
Mathf.Clamp(newNeckRot.eulerAngles.x, -rotationLimit.x, rotationLimit.x),
|
||||
Mathf.Clamp(newNeckRot.eulerAngles.y, -rotationLimit.y, rotationLimit.y),
|
||||
Mathf.Clamp(newNeckRot.eulerAngles.z, -rotationLimit.z, rotationLimit.z)
|
||||
);
|
||||
|
||||
Quaternion outputHeadRot = Quaternion.Lerp(headBone.transform.rotation, Quaternion.Euler(clampedHeadRot), .8f);
|
||||
Quaternion outputNeckRot = Quaternion.Lerp(neckBone.transform.rotation, Quaternion.Euler(clampedNeckRot), .2f);
|
||||
|
||||
headBone.transform.rotation = offsetHeadRot * headBone.transform.rotation * outputHeadRot;
|
||||
neckBone.transform.rotation = (offsetNeckRot * neckBone.transform.rotation * outputNeckRot);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,13 @@
|
||||
namespace Reset.Units{
|
||||
public enum UnitFacingDirection{
|
||||
TowardsTarget = 0,
|
||||
MatchInput,
|
||||
MatchCamera,
|
||||
Static,
|
||||
Momentum,
|
||||
SpecifiedDirection
|
||||
}
|
||||
|
||||
public enum PlayerFacingDirection{
|
||||
TowardsTarget = 0,
|
||||
MatchInput,
|
||||
@@ -31,7 +31,7 @@ namespace Reset.Units{
|
||||
[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("Rotate Facing"), HideLabel, InlineProperty] public SettingValue<UnitFacingDirection> facingDirection = new SettingValue<UnitFacingDirection>(initValue: UnitFacingDirection.Momentum);
|
||||
[Title("Rotation Speed"), HideLabel, InlineProperty] public SettingValue<float> rotationSpeed = new SettingValue<float>(5f);
|
||||
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ using Sirenix.OdinInspector;
|
||||
using Unity.Netcode;
|
||||
|
||||
namespace Reset.Units{
|
||||
public class UnitMovementHandler : UnitComponent{
|
||||
public class UnitMovementHandler : UnitComponent {
|
||||
[ShowInInspector, InlineProperty, HideLabel, FoldoutGroup("Resolved Movement", expanded: true)]
|
||||
public ResolvedMovement resolvedMovement;
|
||||
|
||||
@@ -19,8 +19,8 @@ namespace Reset.Units{
|
||||
|
||||
// References
|
||||
private CharacterController controller;
|
||||
private PlayerControls controls;
|
||||
private LockOnManager lockOnManager;
|
||||
private IUnitDirectionProvider directionProvider;
|
||||
private IUnitTargetProvider targetProvider;
|
||||
|
||||
// Movement Data
|
||||
[ShowInInspector, PropertyOrder(2), FoldoutGroup("Movement Data", expanded: true), InlineProperty, HideLabel] public UnitMovementData data = new();
|
||||
@@ -30,8 +30,8 @@ namespace Reset.Units{
|
||||
|
||||
void Awake(){
|
||||
controller = GetComponent<CharacterController>();
|
||||
controls = GetComponent<PlayerControls>();
|
||||
lockOnManager = GetComponent<LockOnManager>();
|
||||
directionProvider = GetComponent<IUnitDirectionProvider>();
|
||||
targetProvider = GetComponent<IUnitTargetProvider>();
|
||||
|
||||
InitAllSettings();
|
||||
}
|
||||
@@ -52,6 +52,12 @@ namespace Reset.Units{
|
||||
|
||||
// Apply movement
|
||||
DoMovement(resolvedMovement.moveDirection.World, resolvedMovement.gravity, resolvedMovement.moveSpeed, data.gravityScale.Value);
|
||||
|
||||
// Apply movespeed to the Animator
|
||||
if (transform.gameObject == PlayerManager.Player){ // temp
|
||||
Unit.Animation.moveSpeed = resolvedMovement.moveSpeed * resolvedMovement.moveDirection.Local.magnitude / data.moveSpeed.currentValue;
|
||||
}
|
||||
|
||||
DebugOverlayDrawer.ChangeValue("Movement", "Move Direction (Local)", resolvedMovement.moveDirection.Local);
|
||||
DebugOverlayDrawer.ChangeValue("Movement", "Move Direction (World)", resolvedMovement.moveDirection.World);
|
||||
}
|
||||
@@ -59,11 +65,13 @@ namespace Reset.Units{
|
||||
// Update the direction, called every frame
|
||||
private void UpdateCurrentDirection(){
|
||||
// Get input value
|
||||
Vector2 targetDirection = new Vector2(controls.rawMoveInput.x, controls.rawMoveInput.y);
|
||||
Vector2 targetDirection = new Vector2(directionProvider.Direction.x, directionProvider.Direction.y);
|
||||
|
||||
// Rotate input by camera rotation (instead of rotating the output direction by camera rotation)
|
||||
targetDirection = (Camera.main.transform.rotation * targetDirection.ToVector3()).ToVector2();
|
||||
|
||||
if (GetComponent<Player>()) {
|
||||
targetDirection = (Camera.main.transform.rotation * targetDirection.ToVector3()).ToVector2();
|
||||
}
|
||||
|
||||
// Deadzone
|
||||
if (targetDirection.magnitude < .1f) {
|
||||
targetDirection = resolvedMovement.moveDirection.RawWorld;
|
||||
@@ -97,7 +105,7 @@ namespace Reset.Units{
|
||||
newDirection = Vector2.SmoothDamp(currentDirection, targetDirection, ref refVelocityDirectionChangingHardness, data.directionChangingSoftness.Value * data.airDirectionDecay.Value * Time.deltaTime);
|
||||
}
|
||||
|
||||
newDirection = Vector3.Slerp(resolvedMovement.moveDirection.World, newDirection, controls.rawMoveInput.magnitude);
|
||||
newDirection = Vector3.Slerp(resolvedMovement.moveDirection.World, newDirection, directionProvider.Direction.magnitude);
|
||||
|
||||
// Commit the new direction
|
||||
resolvedMovement.moveDirection.World = newDirection;
|
||||
@@ -108,7 +116,7 @@ namespace Reset.Units{
|
||||
// ""Smooth"" the speed
|
||||
float smoothedSpeed;
|
||||
|
||||
if (resolvedMovement.moveDirection.Local.magnitude < controls.rawMoveInput.magnitude) {
|
||||
if (resolvedMovement.moveDirection.Local.magnitude < directionProvider.Direction.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);
|
||||
@@ -135,42 +143,42 @@ namespace Reset.Units{
|
||||
// Update the rotation, called every frame
|
||||
private void UpdateCurrentRotation(){
|
||||
// Get input value
|
||||
Vector3 inputMovement = new Vector3(controls.rawMoveInput.x, 0f, controls.rawMoveInput.y);
|
||||
Vector3 inputMovement = new Vector3(directionProvider.Direction.x, 0f, directionProvider.Direction.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:
|
||||
case UnitFacingDirection.TowardsTarget:
|
||||
// Look directly at the target
|
||||
if (lockOnManager.mainTarget == null) {
|
||||
if (targetProvider.UnitTarget == null) {
|
||||
Debug.LogError("Trying to rotate towards a target but there is no target. Forcing rotation to Static and continuing");
|
||||
data.facingDirection.Value = PlayerFacingDirection.Static;
|
||||
data.facingDirection.currentValue = PlayerFacingDirection.Static;
|
||||
data.facingDirection.Value = UnitFacingDirection.Static;
|
||||
data.facingDirection.currentValue = UnitFacingDirection.Static;
|
||||
|
||||
targetRotation = transform.rotation;
|
||||
break;
|
||||
}
|
||||
|
||||
targetRotation = Quaternion.LookRotation(transform.position.DirectionTo(lockOnManager.mainTarget.gameObject.transform.position));
|
||||
targetRotation = Quaternion.LookRotation(transform.position.DirectionTo(targetProvider.UnitTarget.transform.position));
|
||||
break;
|
||||
case PlayerFacingDirection.Momentum:
|
||||
case UnitFacingDirection.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:
|
||||
case UnitFacingDirection.MatchInput:
|
||||
// Look towards the input direction- similar to Momentum but snappier
|
||||
if (controls.rawMoveInput.magnitude < 0.05f) { break; }
|
||||
if (directionProvider.Direction.magnitude < 0.05f) { break; }
|
||||
|
||||
targetRotation = Camera.main.transform.rotation * Quaternion.LookRotation(inputMovement);
|
||||
break;
|
||||
case PlayerFacingDirection.MatchCamera:
|
||||
case UnitFacingDirection.MatchCamera:
|
||||
// Look the same direction as the camera
|
||||
targetRotation = Quaternion.Euler(Camera.main.transform.rotation.eulerAngles.Flatten(0, null, 0));
|
||||
break;
|
||||
case PlayerFacingDirection.Static:
|
||||
case UnitFacingDirection.Static:
|
||||
// Don't change
|
||||
targetRotation = resolvedMovement.rotation;
|
||||
break;
|
||||
@@ -179,9 +187,9 @@ namespace Reset.Units{
|
||||
DebugOverlayDrawer.ChangeValue("Rotation", "Target Rotation", targetRotation.eulerAngles);
|
||||
|
||||
// Add the current input into the created rotation
|
||||
if (data.facingDirection.Value == PlayerFacingDirection.MatchCamera || data.facingDirection.Value == PlayerFacingDirection.TowardsTarget) {
|
||||
if (data.facingDirection.Value == UnitFacingDirection.MatchCamera || data.facingDirection.Value == UnitFacingDirection.TowardsTarget) {
|
||||
resolvedMovement.rotation = targetRotation;
|
||||
} else if (controls.rawMoveInput.sqrMagnitude > .1){
|
||||
} else if (directionProvider.Direction.sqrMagnitude > .1){
|
||||
resolvedMovement.rotation = targetRotation;
|
||||
}
|
||||
|
||||
@@ -353,6 +361,10 @@ namespace Reset.Units{
|
||||
public Quaternion GetResolvedRotation(){
|
||||
return resolvedMovement.rotation;
|
||||
}
|
||||
|
||||
public bool GetGrounded(){
|
||||
return controller.isGrounded;
|
||||
}
|
||||
|
||||
[Button("Initialize Settings", ButtonHeight = 30), PropertySpace(10,5 )]
|
||||
void InitAllSettings(){
|
||||
@@ -391,6 +403,7 @@ namespace Reset.Units{
|
||||
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!");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
93
Assets/Scripts/Units/UnitPathfinding.cs
Normal file
93
Assets/Scripts/Units/UnitPathfinding.cs
Normal file
@@ -0,0 +1,93 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using Pathfinding;
|
||||
using Pathfinding.Drawing;
|
||||
using UnityEngine;
|
||||
using Vector2 = UnityEngine.Vector2;
|
||||
using Vector3 = UnityEngine.Vector3;
|
||||
|
||||
namespace Reset.Units{
|
||||
[RequireComponent(typeof(Seeker))]
|
||||
public class UnitPathfinding : UnitComponent, IUnitDirectionProvider{
|
||||
public Vector2 Direction{ get; set; }
|
||||
|
||||
public bool pathfindingEnabled;
|
||||
|
||||
public Seeker seeker;
|
||||
|
||||
public float nextWaypointDistance = 3;
|
||||
private int currentWaypoint;
|
||||
|
||||
public Path path;
|
||||
public bool reachedEndOfPath;
|
||||
|
||||
public void Start(){
|
||||
seeker = GetComponent<Seeker>();
|
||||
|
||||
Enemy thisEnemy = (Unit as Enemy);
|
||||
|
||||
if (thisEnemy.relatedSpawner){
|
||||
seeker.graphMask = GraphMask.FromGraph(thisEnemy.relatedSpawner.relatedGraph);
|
||||
} else {
|
||||
Debug.LogWarning("Creating a graph for a singular enemy is not yet implemented. Graph mask not set on this unit.", transform);
|
||||
}
|
||||
}
|
||||
|
||||
public void AssignNewPath(Path p){
|
||||
if (!p.error) {
|
||||
path = p;
|
||||
// Reset the waypoint counter so that we start to move towards the first point in the path
|
||||
currentWaypoint = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void Update(){
|
||||
FollowCurrentPath();
|
||||
}
|
||||
|
||||
private void FollowCurrentPath(){
|
||||
if (path == null) {
|
||||
// We have no path to follow yet, so don't do anything
|
||||
return;
|
||||
}
|
||||
|
||||
reachedEndOfPath = false;
|
||||
// The distance to the next waypoint in the path
|
||||
float distanceToWaypoint;
|
||||
|
||||
while (true) {
|
||||
// If you want maximum performance you can check the squared distance instead to get rid of a
|
||||
// square root calculation. But that is outside the scope of this tutorial.
|
||||
distanceToWaypoint = Vector3.Distance(transform.position, path.vectorPath[currentWaypoint]);
|
||||
if (distanceToWaypoint < nextWaypointDistance) {
|
||||
// Check if there is another waypoint or if we have reached the end of the path
|
||||
if (currentWaypoint + 1 < path.vectorPath.Count) {
|
||||
currentWaypoint++;
|
||||
} else {
|
||||
// Set a status variable to indicate that the agent has reached the end of the path.
|
||||
// You can use this to trigger some special code if your game requires that.
|
||||
reachedEndOfPath = true;
|
||||
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Pass the direction to the direction handler interface
|
||||
if (currentWaypoint + 1 == path.vectorPath.Count) {
|
||||
Direction = (path.vectorPath[currentWaypoint] - transform.position).ToVector2();
|
||||
Direction = Vector3.ClampMagnitude(Direction, 1f);
|
||||
} else {
|
||||
Direction = (path.vectorPath[currentWaypoint] - transform.position).normalized.ToVector2();
|
||||
}
|
||||
|
||||
// Draw an indicator for the path
|
||||
Draw.ingame.SolidCircle(path.vectorPath[currentWaypoint] + Vector3.up * .02f, Vector3.up, .3f);
|
||||
Draw.ingame.Arrow(transform.position, transform.position + Direction.ToVector3(), Vector3.up, .3f);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
2
Assets/Scripts/Units/UnitPathfinding.cs.meta
Normal file
2
Assets/Scripts/Units/UnitPathfinding.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1a72dabd280fe71488a78596bd852afc
|
||||
@@ -14,7 +14,9 @@
|
||||
"GUID:f2d49d9fa7e7eb3418e39723a7d3b92f",
|
||||
"GUID:5540e30183c82e84b954c033c388e06c",
|
||||
"GUID:fe25561d224ed4743af4c60938a59d0b",
|
||||
"GUID:37e17ffe38d86ae48bc3207e83ffef88"
|
||||
"GUID:37e17ffe38d86ae48bc3207e83ffef88",
|
||||
"GUID:efa45043feb7e4147a305b73b5cea642",
|
||||
"GUID:f4059aaf6c60a4a58a177a2609feb769"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
@@ -1,6 +1,6 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3c37fe734c185fc48b50bb4fc19e7537
|
||||
AssemblyDefinitionImporter:
|
||||
guid: a4ea9f82528f3024da71454257341643
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
Reference in New Issue
Block a user