Working on quest functions and increased map
This commit is contained in:
File diff suppressed because it is too large
Load Diff
248
Assets/Prefabs/PushableObject.prefab
Normal file
248
Assets/Prefabs/PushableObject.prefab
Normal file
@@ -0,0 +1,248 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!1 &668200600896096350
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 4648213167836588789}
|
||||
- component: {fileID: 1420193402544156656}
|
||||
- component: {fileID: 5195458718313043737}
|
||||
m_Layer: 9
|
||||
m_Name: SF_Prop_Crops_WheatStack_01
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!4 &4648213167836588789
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 668200600896096350}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 0.5, y: 0.5, z: 0.5}
|
||||
m_ConstrainProportionsScale: 1
|
||||
m_Children: []
|
||||
m_Father: {fileID: 7956216992138273336}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!33 &1420193402544156656
|
||||
MeshFilter:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 668200600896096350}
|
||||
m_Mesh: {fileID: 4300000, guid: 954735d0ae79dc24db704089807c7144, type: 3}
|
||||
--- !u!23 &5195458718313043737
|
||||
MeshRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 668200600896096350}
|
||||
m_Enabled: 1
|
||||
m_CastShadows: 1
|
||||
m_ReceiveShadows: 1
|
||||
m_DynamicOccludee: 1
|
||||
m_StaticShadowCaster: 0
|
||||
m_MotionVectors: 1
|
||||
m_LightProbeUsage: 1
|
||||
m_ReflectionProbeUsage: 1
|
||||
m_RayTracingMode: 2
|
||||
m_RayTraceProcedural: 0
|
||||
m_RayTracingAccelStructBuildFlagsOverride: 0
|
||||
m_RayTracingAccelStructBuildFlags: 1
|
||||
m_SmallMeshCulling: 1
|
||||
m_ForceMeshLod: -1
|
||||
m_MeshLodSelectionBias: 0
|
||||
m_RenderingLayerMask: 1
|
||||
m_RendererPriority: 0
|
||||
m_Materials:
|
||||
- {fileID: 2100000, guid: d726a65f2eaae8847857d20bb327828e, type: 2}
|
||||
m_StaticBatchInfo:
|
||||
firstSubMesh: 0
|
||||
subMeshCount: 0
|
||||
m_StaticBatchRoot: {fileID: 0}
|
||||
m_ProbeAnchor: {fileID: 0}
|
||||
m_LightProbeVolumeOverride: {fileID: 0}
|
||||
m_ScaleInLightmap: 1
|
||||
m_ReceiveGI: 1
|
||||
m_PreserveUVs: 0
|
||||
m_IgnoreNormalsForChartDetection: 0
|
||||
m_ImportantGI: 0
|
||||
m_StitchLightmapSeams: 1
|
||||
m_SelectedEditorRenderState: 3
|
||||
m_MinimumChartSize: 4
|
||||
m_AutoUVMaxDistance: 0.5
|
||||
m_AutoUVMaxAngle: 89
|
||||
m_LightmapParameters: {fileID: 0}
|
||||
m_GlobalIlluminationMeshLod: 0
|
||||
m_SortingLayerID: 0
|
||||
m_SortingLayer: 0
|
||||
m_SortingOrder: 0
|
||||
m_AdditionalVertexStreams: {fileID: 0}
|
||||
--- !u!1 &3084649488060959498
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 7956216992138273336}
|
||||
m_Layer: 9
|
||||
m_Name: Model
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!4 &7956216992138273336
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 3084649488060959498}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 4648213167836588789}
|
||||
m_Father: {fileID: 8734589286718127456}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!1 &3537604211987859112
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 8734589286718127456}
|
||||
- component: {fileID: 6660721327025290466}
|
||||
- component: {fileID: 555404001010922068}
|
||||
- component: {fileID: 163142299225453467}
|
||||
- component: {fileID: 4780405868371040945}
|
||||
m_Layer: 9
|
||||
m_Name: PushableObject
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!4 &8734589286718127456
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 3537604211987859112}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: -6, y: 0, z: -7.5}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 7956216992138273336}
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!65 &6660721327025290466
|
||||
BoxCollider:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 3537604211987859112}
|
||||
m_Material: {fileID: 0}
|
||||
m_IncludeLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 0
|
||||
m_ExcludeLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 0
|
||||
m_LayerOverridePriority: 0
|
||||
m_IsTrigger: 0
|
||||
m_ProvidesContacts: 0
|
||||
m_Enabled: 1
|
||||
serializedVersion: 3
|
||||
m_Size: {x: 1.0743799, y: 1.5028269, z: 1.0900393}
|
||||
m_Center: {x: 0.0058722496, y: 0.74858654, z: 0.0058722496}
|
||||
--- !u!114 &555404001010922068
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 3537604211987859112}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 594cee8a0e8e65f49a379093604ba5b2, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Assembly-CSharp::PushableObject
|
||||
objectDisplayName: Interactive Object
|
||||
customInteractionPoint: {fileID: 0}
|
||||
enableDebugLogs: 0
|
||||
pushForce: 10
|
||||
tipRotation: {x: 0, y: 0, z: -75}
|
||||
tipSpeed: 5
|
||||
useGravity: 0
|
||||
mass: 10
|
||||
--- !u!54 &163142299225453467
|
||||
Rigidbody:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 3537604211987859112}
|
||||
serializedVersion: 5
|
||||
m_Mass: 10
|
||||
m_LinearDamping: 0
|
||||
m_AngularDamping: 0.05
|
||||
m_CenterOfMass: {x: 0, y: 0, z: 0}
|
||||
m_InertiaTensor: {x: 1, y: 1, z: 1}
|
||||
m_InertiaRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_IncludeLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 0
|
||||
m_ExcludeLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 0
|
||||
m_ImplicitCom: 1
|
||||
m_ImplicitTensor: 1
|
||||
m_UseGravity: 1
|
||||
m_IsKinematic: 0
|
||||
m_Interpolate: 0
|
||||
m_Constraints: 0
|
||||
m_CollisionDetection: 0
|
||||
--- !u!114 &4780405868371040945
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 3537604211987859112}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: e69611ed4badc844ab02eb8ebbe1adf0, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Assembly-CSharp::QuestTrigger
|
||||
objectDisplayName: Interactive Object
|
||||
customInteractionPoint: {fileID: 0}
|
||||
enableDebugLogs: 0
|
||||
questId: 3
|
||||
progressAmount: 1
|
||||
triggerType: 4
|
||||
requiredTag:
|
||||
destroyAfterTrigger: 0
|
||||
triggerOnce: 1
|
||||
7
Assets/Prefabs/PushableObject.prefab.meta
Normal file
7
Assets/Prefabs/PushableObject.prefab.meta
Normal file
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 93659a05c2eabfd4a84fd03398457ae3
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -5,7 +5,7 @@ using System.Collections;
|
||||
/// Example interactive object: A bridge that the player can cross.
|
||||
/// Can be destroyed, collapsed, or require conditions to cross safely.
|
||||
/// </summary>
|
||||
public class Bridge : MonoBehaviour, IInteractiveObject
|
||||
public class Bridge : InteractiveObjectBase
|
||||
{
|
||||
[Header("Bridge Settings")]
|
||||
public string bridgeName = "Bridge";
|
||||
@@ -13,38 +13,31 @@ public class Bridge : MonoBehaviour, IInteractiveObject
|
||||
[SerializeField]
|
||||
private float crossingDuration = 3f; // Time to cross the bridge
|
||||
|
||||
[Header("Interaction Point")]
|
||||
public Transform interactionPoint; // Where player should stand to interact
|
||||
|
||||
private Coroutine crossingRoutine;
|
||||
|
||||
public Vector3 GetInteractionPoint()
|
||||
{
|
||||
if (interactionPoint != null)
|
||||
return interactionPoint.position;
|
||||
return transform.position;
|
||||
}
|
||||
|
||||
public bool CanInteract()
|
||||
public override bool CanInteract()
|
||||
{
|
||||
return isPassable;
|
||||
}
|
||||
|
||||
public void Interact(GameObject player)
|
||||
public override void Interact(GameObject player)
|
||||
{
|
||||
if (!isPassable)
|
||||
{
|
||||
Debug.Log("Bridge is not passable!", gameObject);
|
||||
Log("Bridge is not passable!");
|
||||
return;
|
||||
}
|
||||
|
||||
Debug.Log($"Player {player.name} is crossing {bridgeName}", gameObject);
|
||||
Log($"Player {player.name} is crossing {bridgeName}");
|
||||
|
||||
// You can add animation, effects, or checks here
|
||||
// For example: play crossing animation, check integrity, trigger events, etc.
|
||||
}
|
||||
|
||||
public string GetDisplayName() => bridgeName;
|
||||
void OnValidate()
|
||||
{
|
||||
objectDisplayName = bridgeName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Make the bridge impassable (destroyed, collapsed, etc.)
|
||||
@@ -52,7 +45,7 @@ public class Bridge : MonoBehaviour, IInteractiveObject
|
||||
public void Collapse()
|
||||
{
|
||||
isPassable = false;
|
||||
Debug.Log($"{bridgeName} has collapsed!", gameObject);
|
||||
Log($"{bridgeName} has collapsed!");
|
||||
// Add visual effects, sounds, animations here
|
||||
}
|
||||
|
||||
@@ -62,6 +55,6 @@ public class Bridge : MonoBehaviour, IInteractiveObject
|
||||
public void Repair()
|
||||
{
|
||||
isPassable = true;
|
||||
Debug.Log($"{bridgeName} has been repaired!", gameObject);
|
||||
Log($"{bridgeName} has been repaired!");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,18 @@ public abstract class Character : MonoBehaviour
|
||||
[SerializeField]
|
||||
protected int MaxHp = 10;
|
||||
|
||||
[Header("Experience & Leveling")]
|
||||
[SerializeField]
|
||||
protected int currentLevel = 1;
|
||||
[SerializeField]
|
||||
protected int currentExperience = 0;
|
||||
[SerializeField]
|
||||
[Tooltip("Experience points required to reach the next level. Increases per level.")]
|
||||
protected int experiencePerLevel = 100;
|
||||
[SerializeField]
|
||||
[Tooltip("Experience points awarded to the player when this character is defeated.")]
|
||||
protected int experienceReward = 50;
|
||||
|
||||
[Header("Dialogue")]
|
||||
public bool hasDialogue = false;
|
||||
[Tooltip("The Dialogue asset for this character. Will be loaded into the central DialogueController.")]
|
||||
@@ -125,6 +137,10 @@ public abstract class Character : MonoBehaviour
|
||||
|
||||
public int GetCurrentHP() => CurHp;
|
||||
public int GetMaxHP() => MaxHp;
|
||||
public int GetCurrentLevel() => currentLevel;
|
||||
public int GetCurrentExperience() => currentExperience;
|
||||
public int GetExperienceForNextLevel() => experiencePerLevel;
|
||||
public int GetExperienceReward() => experienceReward;
|
||||
|
||||
protected virtual void Awake()
|
||||
{
|
||||
@@ -202,9 +218,58 @@ public abstract class Character : MonoBehaviour
|
||||
public virtual void Die()
|
||||
{
|
||||
SetDeathAnimation();
|
||||
|
||||
// Reward experience to player if this character has an experience reward
|
||||
if (experienceReward > 0 && Player.current != null)
|
||||
{
|
||||
Player.current.AddExperience(experienceReward);
|
||||
Log($"Player gained {experienceReward} experience from defeating {gameObject.name}");
|
||||
}
|
||||
|
||||
Destroy(gameObject);
|
||||
}
|
||||
|
||||
public void AddExperience(int amount)
|
||||
{
|
||||
if (amount <= 0) return;
|
||||
|
||||
currentExperience += amount;
|
||||
Log($"Gained {amount} experience. Total: {currentExperience}/{experiencePerLevel}");
|
||||
|
||||
// Check if level up
|
||||
while (currentExperience >= experiencePerLevel)
|
||||
{
|
||||
LevelUp();
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void LevelUp()
|
||||
{
|
||||
currentExperience -= experiencePerLevel;
|
||||
currentLevel++;
|
||||
|
||||
// Increase experience requirement for next level (e.g., 10% increase or fixed increment)
|
||||
experiencePerLevel += 50; // Increase by 50 exp per level
|
||||
|
||||
Log($"LEVEL UP! Now level {currentLevel}. Next level requires {experiencePerLevel} exp.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Grant experience to the player from a quest or other source.
|
||||
/// </summary>
|
||||
public static void AwardQuestExperience(int amount)
|
||||
{
|
||||
if (Player.current != null)
|
||||
{
|
||||
Player.current.AddExperience(amount);
|
||||
Debug.Log($"Player gained {amount} experience from quest completion!");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning("Cannot award experience: Player.current is null");
|
||||
}
|
||||
}
|
||||
|
||||
public void SetTarget(Character t)
|
||||
{
|
||||
target = t;
|
||||
|
||||
@@ -340,8 +340,8 @@ public class ClickToMoveInputSystem : MonoBehaviour
|
||||
{
|
||||
agent.SetDestination(groundHit.point);
|
||||
}
|
||||
else if (NavMesh.SamplePosition(Player.current.transform.position, out NavMeshHit hit, 2f, NavMesh.AllAreas) &&
|
||||
agent.Warp(hit.position) &&
|
||||
else if (NavMesh.SamplePosition(Player.current.transform.position, out NavMeshHit meshHit, 2f, NavMesh.AllAreas) &&
|
||||
agent.Warp(meshHit.position) &&
|
||||
agent.isOnNavMesh)
|
||||
{
|
||||
agent.SetDestination(groundHit.point);
|
||||
|
||||
@@ -2,7 +2,7 @@ using UnityEngine;
|
||||
using System.Collections;
|
||||
using UnityEngine.AI;
|
||||
|
||||
public class Gate : MonoBehaviour, IInteractiveObject
|
||||
public class Gate : InteractiveObjectBase
|
||||
{
|
||||
[Header("Gate Settings")]
|
||||
public bool startsOpen = false;
|
||||
@@ -17,9 +17,6 @@ public class Gate : MonoBehaviour, IInteractiveObject
|
||||
public float swingSpeed = 180f; // degrees per second
|
||||
public bool rotateAroundLocalY = true;
|
||||
|
||||
[Header("Interaction")]
|
||||
public Transform interactionPoint; // Where player should stand to interact (defaults to this transform)
|
||||
|
||||
[Header("NavMesh")]
|
||||
public NavMeshObstacle navMeshObstacle;
|
||||
|
||||
@@ -164,7 +161,7 @@ public class Gate : MonoBehaviour, IInteractiveObject
|
||||
|
||||
void OnLockedInteract()
|
||||
{
|
||||
Debug.Log($"Gate is locked. Requires key: {(string.IsNullOrEmpty(requiredKeyId) ? "None" : requiredKeyId)}", gameObject);
|
||||
Log($"Gate is locked. Requires key: {(string.IsNullOrEmpty(requiredKeyId) ? "None" : requiredKeyId)}");
|
||||
}
|
||||
|
||||
void OnDestroy()
|
||||
@@ -176,24 +173,22 @@ public class Gate : MonoBehaviour, IInteractiveObject
|
||||
}
|
||||
}
|
||||
|
||||
// IInteractiveObject implementation
|
||||
public Vector3 GetInteractionPoint()
|
||||
{
|
||||
if (interactionPoint != null)
|
||||
return interactionPoint.position;
|
||||
return transform.position;
|
||||
}
|
||||
|
||||
public bool CanInteract()
|
||||
// Override base CanInteract
|
||||
public override bool CanInteract()
|
||||
{
|
||||
return !isLocked;
|
||||
}
|
||||
|
||||
public void Interact(GameObject player)
|
||||
// Override base Interact implementation
|
||||
public override void Interact(GameObject player)
|
||||
{
|
||||
Open();
|
||||
Debug.Log($"Gate {gateName} opened by {player.name}");
|
||||
Log($"Gate {gateName} opened by {player.name}");
|
||||
}
|
||||
|
||||
public string GetDisplayName() => gateName;
|
||||
// Set display name from gateName
|
||||
void OnValidate()
|
||||
{
|
||||
objectDisplayName = gateName;
|
||||
}
|
||||
}
|
||||
94
Assets/Scripts/Interfaces/InteractiveObjectBase.cs
Normal file
94
Assets/Scripts/Interfaces/InteractiveObjectBase.cs
Normal file
@@ -0,0 +1,94 @@
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// Base class for all interactive objects that implement IInteractiveObject.
|
||||
/// Consolidates common properties and methods to reduce code duplication.
|
||||
/// </summary>
|
||||
public abstract class InteractiveObjectBase : MonoBehaviour, IInteractiveObject
|
||||
{
|
||||
[Header("Interactive Object Settings")]
|
||||
[SerializeField]
|
||||
protected string objectDisplayName = "Interactive Object";
|
||||
|
||||
[SerializeField]
|
||||
protected Transform customInteractionPoint; // Optional: use instead of collider center
|
||||
|
||||
[SerializeField]
|
||||
protected bool enableDebugLogs = false;
|
||||
|
||||
/// <summary>
|
||||
/// Get the interaction point - uses custom point if assigned, otherwise collider center
|
||||
/// </summary>
|
||||
public virtual Vector3 GetInteractionPoint()
|
||||
{
|
||||
if (customInteractionPoint != null)
|
||||
{
|
||||
Log("GetInteractionPoint: Using custom interaction point");
|
||||
return customInteractionPoint.position;
|
||||
}
|
||||
|
||||
Collider collider = GetComponent<Collider>();
|
||||
if (collider != null)
|
||||
{
|
||||
Log($"GetInteractionPoint: Using collider center at {collider.bounds.center}");
|
||||
return collider.bounds.center;
|
||||
}
|
||||
|
||||
Log("GetInteractionPoint: No collider found, using transform position");
|
||||
return transform.position;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the display name for this interactive object
|
||||
/// </summary>
|
||||
public virtual string GetDisplayName()
|
||||
{
|
||||
Log($"GetDisplayName: Returning '{objectDisplayName}'");
|
||||
return objectDisplayName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if this object can be interacted with
|
||||
/// Override in derived classes to add custom logic
|
||||
/// </summary>
|
||||
public virtual bool CanInteract()
|
||||
{
|
||||
Log("CanInteract: Default implementation returns true");
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Perform the interaction
|
||||
/// Must be implemented by derived classes
|
||||
/// </summary>
|
||||
public abstract void Interact(GameObject player);
|
||||
|
||||
/// <summary>
|
||||
/// Protected logging utility with optional debug toggle
|
||||
/// </summary>
|
||||
protected void Log(string message)
|
||||
{
|
||||
if (enableDebugLogs)
|
||||
{
|
||||
DebugManager.Log(GetType().Name, message, gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the display name at runtime
|
||||
/// </summary>
|
||||
public void SetDisplayName(string newName)
|
||||
{
|
||||
objectDisplayName = newName;
|
||||
Log($"Display name changed to '{newName}'");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set custom interaction point Transform
|
||||
/// </summary>
|
||||
public void SetCustomInteractionPoint(Transform point)
|
||||
{
|
||||
customInteractionPoint = point;
|
||||
Log($"Custom interaction point set to {point.name}");
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/Interfaces/InteractiveObjectBase.cs.meta
Normal file
2
Assets/Scripts/Interfaces/InteractiveObjectBase.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2020a1589d3df544f93fd540f264786e
|
||||
@@ -4,7 +4,7 @@ using UnityEngine;
|
||||
/// Example interactive object: A ledge that the player can jump over.
|
||||
/// Can have conditions like requiring a minimum jump height or specific equipment.
|
||||
/// </summary>
|
||||
public class Ledge : MonoBehaviour, IInteractiveObject
|
||||
public class Ledge : InteractiveObjectBase
|
||||
{
|
||||
[Header("Ledge Settings")]
|
||||
public string ledgeName = "Ledge";
|
||||
@@ -17,27 +17,31 @@ public class Ledge : MonoBehaviour, IInteractiveObject
|
||||
public Transform jumpEndPoint; // Where player lands after jumping
|
||||
public bool isTraversable = true;
|
||||
|
||||
public Vector3 GetInteractionPoint()
|
||||
/// <summary>
|
||||
/// Check if the player can safely jump this ledge
|
||||
/// (Implement with player stats, equipment, etc.)
|
||||
/// </summary>
|
||||
public bool CanPlayerJump(Character player)
|
||||
{
|
||||
if (jumpStartPoint != null)
|
||||
return jumpStartPoint.position;
|
||||
return transform.position;
|
||||
// Add custom logic here
|
||||
// Example: check if player has enough stamina, equipment bonuses, etc.
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool CanInteract()
|
||||
public override bool CanInteract()
|
||||
{
|
||||
return isTraversable;
|
||||
}
|
||||
|
||||
public void Interact(GameObject player)
|
||||
public override void Interact(GameObject player)
|
||||
{
|
||||
if (!isTraversable)
|
||||
{
|
||||
Debug.Log("Cannot jump over this ledge!", gameObject);
|
||||
Log("Cannot jump over this ledge!");
|
||||
return;
|
||||
}
|
||||
|
||||
Debug.Log($"Player {player.name} is jumping over {ledgeName}", gameObject);
|
||||
Log($"Player {player.name} is jumping over {ledgeName}");
|
||||
|
||||
// You can trigger jump animation, apply force, teleport player, etc.
|
||||
// Example: Move player to the end point
|
||||
@@ -55,26 +59,22 @@ public class Ledge : MonoBehaviour, IInteractiveObject
|
||||
}
|
||||
}
|
||||
|
||||
public string GetDisplayName() => $"{ledgeName} (Height: {height}m)";
|
||||
|
||||
/// <summary>
|
||||
/// Check if the player can safely jump this ledge
|
||||
/// (Implement with player stats, equipment, etc.)
|
||||
/// </summary>
|
||||
public bool CanPlayerJump(Character player)
|
||||
public override Vector3 GetInteractionPoint()
|
||||
{
|
||||
// Add custom logic here
|
||||
// Example: check if player has enough stamina, equipment bonuses, etc.
|
||||
return true;
|
||||
if (jumpStartPoint != null)
|
||||
return jumpStartPoint.position;
|
||||
return base.GetInteractionPoint();
|
||||
}
|
||||
|
||||
public override string GetDisplayName() => $"{ledgeName} (Height: {height}m)";
|
||||
|
||||
/// <summary>
|
||||
/// Block the ledge (might be destroyed, blocked by debris, etc.)
|
||||
/// </summary>
|
||||
public void Block()
|
||||
{
|
||||
isTraversable = false;
|
||||
Debug.Log($"{ledgeName} is now blocked!", gameObject);
|
||||
Log($"{ledgeName} is now blocked!");
|
||||
}
|
||||
|
||||
void OnDrawGizmos()
|
||||
|
||||
@@ -128,27 +128,49 @@ public class QuestManager : MonoBehaviour
|
||||
/// </summary>
|
||||
public bool ProgressQuest(string questId, int amount = 1)
|
||||
{
|
||||
Quest quest = GetActiveQuest(questId);
|
||||
Quest quest = GetQuestById(questId); // Get from available quests, not active
|
||||
if (quest == null)
|
||||
{
|
||||
Debug.LogWarning($"Active quest '{questId}' not found.");
|
||||
Debug.LogWarning($"Quest '{questId}' not found in available quests.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return ProgressQuest(quest, amount);
|
||||
}
|
||||
return ProgressQuest(quest, amount); // Let the Quest overload handle auto-activation
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Progress a quest
|
||||
/// </summary>
|
||||
public bool ProgressQuest(Quest quest, int amount = 1)
|
||||
{
|
||||
if (quest == null || !quest.IsActive())
|
||||
if (quest == null)
|
||||
{
|
||||
Debug.LogWarning("Cannot progress quest - not active.");
|
||||
Debug.LogWarning("Cannot progress null quest.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// If quest not active, try to auto-activate it
|
||||
if (!quest.IsActive())
|
||||
{
|
||||
// Check if quest has unmet prerequisites
|
||||
if (!string.IsNullOrEmpty(quest.prerequisiteQuestId))
|
||||
{
|
||||
Quest prerequisite = GetQuestById(quest.prerequisiteQuestId);
|
||||
if (prerequisite == null || prerequisite.state != QuestState.Completed)
|
||||
{
|
||||
Debug.LogWarning($"Cannot progress quest '{quest.questName}' - prerequisite '{quest.prerequisiteQuestId}' not completed.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Auto-activate the quest if it has no unmet prerequisites
|
||||
quest.state = QuestState.Active;
|
||||
quest.currentProgress = 0;
|
||||
activeQuests.Add(quest);
|
||||
Debug.Log($"Quest auto-activated: {quest.questName}");
|
||||
onQuestStarted?.Invoke(quest);
|
||||
}
|
||||
|
||||
// Progress the quest
|
||||
int previousProgress = quest.currentProgress;
|
||||
quest.AddProgress(amount);
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@ public class PlayerCombatController : MonoBehaviour
|
||||
public int attackDamage = 1;
|
||||
public float attackRange = 1.5f;
|
||||
public float attackCooldown = 0.6f;
|
||||
[Tooltip("Maximum distance to initiate combat (right-click detection range).")]
|
||||
public float maxTargetAcquisitionRange = 10f;
|
||||
[Tooltip("Maximum chase distance. Clear target if enemy gets farther than this.")]
|
||||
public float maxChaseDistance = 20f;
|
||||
|
||||
@@ -82,7 +84,16 @@ public class PlayerCombatController : MonoBehaviour
|
||||
{
|
||||
if (enemy == null) return;
|
||||
|
||||
Log($"Setting target: {enemy.enemyName}");
|
||||
float distance = Vector3.Distance(transform.position, enemy.transform.position);
|
||||
|
||||
// Validate enemy is within acquisition range
|
||||
if (distance > maxTargetAcquisitionRange)
|
||||
{
|
||||
Log($"Enemy {enemy.enemyName} is too far away (distance: {distance:F2}, max: {maxTargetAcquisitionRange:F2}). Cannot target.");
|
||||
return;
|
||||
}
|
||||
|
||||
Log($"Setting target: {enemy.enemyName} (distance: {distance:F2})");
|
||||
targetEnemy = enemy;
|
||||
nextAttackTime = Time.time;
|
||||
|
||||
|
||||
122
Assets/Scripts/PushableObject.cs
Normal file
122
Assets/Scripts/PushableObject.cs
Normal file
@@ -0,0 +1,122 @@
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// Script for objects that can be pushed over when interacted with (right-click).
|
||||
/// Applies torque to tip the object and falls under physics gravity.
|
||||
/// Integrates with the interaction system.
|
||||
/// </summary>
|
||||
public class PushableObject : InteractiveObjectBase
|
||||
{
|
||||
[Header("Push Settings")]
|
||||
[SerializeField]
|
||||
[Tooltip("Force applied to push the object over.")]
|
||||
private float pushForce = 10f;
|
||||
|
||||
[SerializeField]
|
||||
[Tooltip("Rotation angle (in degrees) the object tips to when pushed.")]
|
||||
private Vector3 tipRotation = new Vector3(90f, 0f, 0f);
|
||||
|
||||
[SerializeField]
|
||||
[Tooltip("Speed at which the object rotates to the tip angle.")]
|
||||
private float tipSpeed = 5f;
|
||||
|
||||
[Header("Physics")]
|
||||
[SerializeField]
|
||||
[Tooltip("Whether to use gravity for falling.")]
|
||||
private bool useGravity = true;
|
||||
|
||||
[SerializeField]
|
||||
[Tooltip("Mass of the object (affects how it falls).")]
|
||||
private float mass = 1f;
|
||||
|
||||
private Rigidbody rb;
|
||||
private bool hasTipped = false;
|
||||
private Vector3 originalRotation;
|
||||
private Quaternion targetRotation;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
rb = GetComponent<Rigidbody>();
|
||||
if (rb == null)
|
||||
{
|
||||
rb = gameObject.AddComponent<Rigidbody>();
|
||||
Log("No Rigidbody found. Added one automatically.");
|
||||
}
|
||||
|
||||
// Configure physics
|
||||
rb.useGravity = useGravity;
|
||||
rb.mass = mass;
|
||||
rb.constraints = RigidbodyConstraints.FreezeRotation; // Start frozen until pushed
|
||||
|
||||
originalRotation = transform.eulerAngles;
|
||||
|
||||
objectDisplayName = "Pushable Object";
|
||||
Log($"Initialized on layer '{LayerMask.LayerToName(gameObject.layer)}'. Make sure this layer is in ClickToMoveInputSystem's selectionLayers!");
|
||||
}
|
||||
|
||||
public override bool CanInteract()
|
||||
{
|
||||
return !hasTipped;
|
||||
}
|
||||
|
||||
public override void Interact(GameObject player)
|
||||
{
|
||||
Log("Interact method called");
|
||||
Push();
|
||||
}
|
||||
|
||||
public void Push()
|
||||
{
|
||||
Log("Push method called");
|
||||
if (hasTipped)
|
||||
{
|
||||
Log("Object already tipped, cannot push again.");
|
||||
return;
|
||||
}
|
||||
|
||||
hasTipped = true;
|
||||
Log("Object pushed! Tipping over...");
|
||||
|
||||
// Unfreeze rotation to allow physics
|
||||
rb.constraints = RigidbodyConstraints.FreezePosition;
|
||||
|
||||
// Apply upward impulse to simulate loss of balance
|
||||
rb.linearVelocity = Vector3.up * (pushForce * 0.5f);
|
||||
|
||||
// Calculate target rotation
|
||||
targetRotation = Quaternion.Euler(originalRotation + tipRotation);
|
||||
|
||||
// Use AnimationUtilities to rotate toward tip angle
|
||||
float duration = 1f / tipSpeed;
|
||||
AnimationUtilities.RotateTo(this, transform, targetRotation, duration);
|
||||
rb.constraints = RigidbodyConstraints.FreezeAll; // Freeze all rotation after tipping to prevent wobbling
|
||||
TryGetComponent<QuestTrigger>(out var questTrigger);
|
||||
Debug.Log($"QuestTrigger found: {questTrigger != null}");
|
||||
if (questTrigger != null)
|
||||
{
|
||||
questTrigger.TryTrigger(gameObject);
|
||||
Log("Triggered quest event on push!");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reset the object to its original state
|
||||
/// </summary>
|
||||
public void Reset()
|
||||
{
|
||||
hasTipped = false;
|
||||
rb.linearVelocity = Vector3.zero;
|
||||
rb.angularVelocity = Vector3.zero;
|
||||
rb.constraints = RigidbodyConstraints.FreezeRotation;
|
||||
transform.eulerAngles = originalRotation;
|
||||
Log("Object reset to original position.");
|
||||
}
|
||||
|
||||
private void OnDrawGizmosSelected()
|
||||
{
|
||||
// Draw push direction indicator
|
||||
Gizmos.color = Color.green;
|
||||
Gizmos.DrawRay(transform.position, Vector3.up * 2f);
|
||||
}
|
||||
}
|
||||
|
||||
2
Assets/Scripts/PushableObject.cs.meta
Normal file
2
Assets/Scripts/PushableObject.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 594cee8a0e8e65f49a379093604ba5b2
|
||||
@@ -4,7 +4,7 @@ using UnityEngine;
|
||||
/// Example script showing how to interact with the Quest system
|
||||
/// Attach to objects that trigger quest progression (enemies, items, zones, etc.)
|
||||
/// </summary>
|
||||
public class QuestTrigger : MonoBehaviour
|
||||
public class QuestTrigger : InteractiveObjectBase
|
||||
{
|
||||
[Header("Quest Trigger Settings")]
|
||||
[Tooltip("The quest ID this trigger affects")]
|
||||
@@ -33,6 +33,7 @@ public class QuestTrigger : MonoBehaviour
|
||||
OnCollisionEnter,
|
||||
OnItemPickup,
|
||||
OnDeath,
|
||||
OnInteract,
|
||||
Manual
|
||||
}
|
||||
|
||||
@@ -85,45 +86,87 @@ public class QuestTrigger : MonoBehaviour
|
||||
}
|
||||
}
|
||||
|
||||
void TryTrigger(GameObject triggerer)
|
||||
/// <summary>
|
||||
/// IInteractiveObject implementation - called when player interacts with this object
|
||||
/// </summary>
|
||||
public override void Interact(GameObject player)
|
||||
{
|
||||
if (triggerType == TriggerType.OnInteract)
|
||||
{
|
||||
TryTrigger(player);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// IInteractiveObject implementation - check if interaction is allowed
|
||||
/// </summary>
|
||||
public override bool CanInteract()
|
||||
{
|
||||
// Allow interaction if not already triggered, or if it can trigger multiple times
|
||||
return !hasTriggered || !triggerOnce;
|
||||
}
|
||||
|
||||
public void TryTrigger(GameObject triggerer)
|
||||
{
|
||||
Debug.Log($"[QuestTrigger.TryTrigger] ENTRY - HasTriggered: {hasTriggered}, TriggerOnce: {triggerOnce}");
|
||||
|
||||
// Check if already triggered
|
||||
if (hasTriggered && triggerOnce)
|
||||
{
|
||||
Debug.Log($"[QuestTrigger.TryTrigger] EARLY RETURN - Quest already triggered and triggerOnce is enabled.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check tag requirement
|
||||
if (triggerer != null && !string.IsNullOrEmpty(requiredTag))
|
||||
{
|
||||
Debug.Log($"[QuestTrigger.TryTrigger] Checking tag requirement. Required: {requiredTag}, Triggerer tag: {triggerer.tag}");
|
||||
if (!triggerer.CompareTag(requiredTag))
|
||||
{
|
||||
Debug.Log($"[QuestTrigger.TryTrigger] EARLY RETURN - Tag mismatch.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if QuestManager exists
|
||||
if (QuestManager.Instance == null)
|
||||
{
|
||||
Debug.LogWarning("QuestManager not found in scene.", gameObject);
|
||||
Debug.LogWarning("[QuestTrigger.TryTrigger] EARLY RETURN - QuestManager not found in scene.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if quest is active
|
||||
if (!QuestManager.Instance.IsQuestActive(questId))
|
||||
Debug.Log($"[QuestTrigger.TryTrigger] Getting quest with ID: {questId}");
|
||||
|
||||
// Get the quest from the database
|
||||
Quest quest = QuestManager.Instance.GetQuestById(questId);
|
||||
if (quest == null)
|
||||
{
|
||||
Debug.Log($"Quest '{questId}' is not active. Cannot progress.", gameObject);
|
||||
Debug.LogWarning($"[QuestTrigger.TryTrigger] EARLY RETURN - Quest '{questId}' not found in quest database.");
|
||||
return;
|
||||
}
|
||||
|
||||
Debug.Log($"[QuestTrigger.TryTrigger] Quest found: {quest.questName}. Attempting to progress by {progressAmount}");
|
||||
|
||||
// Progress the quest
|
||||
bool progressed = QuestManager.Instance.ProgressQuest(questId, progressAmount);
|
||||
// Progress the quest (QuestManager handles auto-activation and prerequisite checks)
|
||||
bool progressed = QuestManager.Instance.ProgressQuest(quest, progressAmount);
|
||||
|
||||
Debug.Log($"[QuestTrigger.TryTrigger] ProgressQuest returned: {progressed}");
|
||||
|
||||
if (progressed)
|
||||
{
|
||||
hasTriggered = true;
|
||||
Debug.Log($"Quest '{questId}' progressed by {progressAmount}", gameObject);
|
||||
Debug.Log($"[QuestTrigger.TryTrigger] SUCCESS! Quest '{questId}' progressed by {progressAmount}");
|
||||
|
||||
// Destroy if configured
|
||||
if (destroyAfterTrigger)
|
||||
{
|
||||
Debug.Log($"[QuestTrigger.TryTrigger] Destroying object after trigger.");
|
||||
Destroy(gameObject);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning($"[QuestTrigger.TryTrigger] FAILED to progress quest '{questId}'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
8
Assets/Scripts/Utilities.meta
Normal file
8
Assets/Scripts/Utilities.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 856d1402c92a4d241b1520c6faa70358
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
134
Assets/Scripts/Utilities/AnimationUtilities.cs
Normal file
134
Assets/Scripts/Utilities/AnimationUtilities.cs
Normal file
@@ -0,0 +1,134 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
|
||||
/// <summary>
|
||||
/// Utility class for common animation coroutines and animation helpers.
|
||||
/// Provides reusable animation patterns used across multiple scripts.
|
||||
/// </summary>
|
||||
public static class AnimationUtilities
|
||||
{
|
||||
/// <summary>
|
||||
/// Smoothly rotate an object from its current rotation to a target rotation
|
||||
/// </summary>
|
||||
public static Coroutine RotateTo(MonoBehaviour source, Transform target, Quaternion targetRotation, float duration)
|
||||
{
|
||||
return source.StartCoroutine(RotateToRoutine(target, targetRotation, duration));
|
||||
}
|
||||
|
||||
private static IEnumerator RotateToRoutine(Transform target, Quaternion targetRotation, float duration)
|
||||
{
|
||||
if (duration <= 0) yield break;
|
||||
|
||||
float elapsedTime = 0f;
|
||||
Quaternion startRotation = target.rotation;
|
||||
|
||||
while (elapsedTime < duration)
|
||||
{
|
||||
elapsedTime += Time.deltaTime;
|
||||
float t = Mathf.Clamp01(elapsedTime / duration);
|
||||
target.rotation = Quaternion.Slerp(startRotation, targetRotation, t);
|
||||
yield return null;
|
||||
}
|
||||
|
||||
target.rotation = targetRotation;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Smoothly move an object from its current position to a target position
|
||||
/// </summary>
|
||||
public static Coroutine MoveTo(MonoBehaviour source, Transform target, Vector3 targetPosition, float duration)
|
||||
{
|
||||
return source.StartCoroutine(MoveToRoutine(target, targetPosition, duration));
|
||||
}
|
||||
|
||||
private static IEnumerator MoveToRoutine(Transform target, Vector3 targetPosition, float duration)
|
||||
{
|
||||
if (duration <= 0) yield break;
|
||||
|
||||
float elapsedTime = 0f;
|
||||
Vector3 startPosition = target.position;
|
||||
|
||||
while (elapsedTime < duration)
|
||||
{
|
||||
elapsedTime += Time.deltaTime;
|
||||
float t = Mathf.Clamp01(elapsedTime / duration);
|
||||
target.position = Vector3.Lerp(startPosition, targetPosition, t);
|
||||
yield return null;
|
||||
}
|
||||
|
||||
target.position = targetPosition;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Smoothly rotate an object around a specific axis by a specified angle
|
||||
/// </summary>
|
||||
public static Coroutine RotateAround(MonoBehaviour source, Transform target, float targetAngle, Vector3 axis, float duration)
|
||||
{
|
||||
return source.StartCoroutine(RotateAroundRoutine(target, targetAngle, axis, duration));
|
||||
}
|
||||
|
||||
private static IEnumerator RotateAroundRoutine(Transform target, float targetAngle, Vector3 axis, float duration)
|
||||
{
|
||||
if (duration <= 0) yield break;
|
||||
|
||||
float elapsedTime = 0f;
|
||||
Vector3 eulerAngles = target.localEulerAngles;
|
||||
float startAngle = 0;
|
||||
|
||||
if (axis == Vector3.right) startAngle = eulerAngles.x;
|
||||
else if (axis == Vector3.up) startAngle = eulerAngles.y;
|
||||
else if (axis == Vector3.forward) startAngle = eulerAngles.z;
|
||||
|
||||
while (elapsedTime < duration)
|
||||
{
|
||||
elapsedTime += Time.deltaTime;
|
||||
float t = Mathf.Clamp01(elapsedTime / duration);
|
||||
float currentAngle = Mathf.Lerp(startAngle, targetAngle, t);
|
||||
|
||||
eulerAngles = target.localEulerAngles;
|
||||
if (axis == Vector3.right) eulerAngles.x = currentAngle;
|
||||
else if (axis == Vector3.up) eulerAngles.y = currentAngle;
|
||||
else if (axis == Vector3.forward) eulerAngles.z = currentAngle;
|
||||
|
||||
target.localEulerAngles = eulerAngles;
|
||||
yield return null;
|
||||
}
|
||||
|
||||
eulerAngles = target.localEulerAngles;
|
||||
if (axis == Vector3.right) eulerAngles.x = targetAngle;
|
||||
else if (axis == Vector3.up) eulerAngles.y = targetAngle;
|
||||
else if (axis == Vector3.forward) eulerAngles.z = targetAngle;
|
||||
target.localEulerAngles = eulerAngles;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fade in/out by modifying material color alpha
|
||||
/// </summary>
|
||||
public static Coroutine Fade(MonoBehaviour source, Renderer renderer, float targetAlpha, float duration)
|
||||
{
|
||||
return source.StartCoroutine(FadeRoutine(renderer, targetAlpha, duration));
|
||||
}
|
||||
|
||||
private static IEnumerator FadeRoutine(Renderer renderer, float targetAlpha, float duration)
|
||||
{
|
||||
if (renderer == null || duration <= 0) yield break;
|
||||
|
||||
float elapsedTime = 0f;
|
||||
Material material = renderer.material;
|
||||
Color startColor = material.color;
|
||||
|
||||
while (elapsedTime < duration)
|
||||
{
|
||||
elapsedTime += Time.deltaTime;
|
||||
float t = Mathf.Clamp01(elapsedTime / duration);
|
||||
Color newColor = startColor;
|
||||
newColor.a = Mathf.Lerp(startColor.a, targetAlpha, t);
|
||||
material.color = newColor;
|
||||
yield return null;
|
||||
}
|
||||
|
||||
Color finalColor = startColor;
|
||||
finalColor.a = targetAlpha;
|
||||
material.color = finalColor;
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/Utilities/AnimationUtilities.cs.meta
Normal file
2
Assets/Scripts/Utilities/AnimationUtilities.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ccc2d1cf29c898248bdaa081a1e18bc6
|
||||
63
Assets/Scripts/Utilities/DebugManager.cs
Normal file
63
Assets/Scripts/Utilities/DebugManager.cs
Normal file
@@ -0,0 +1,63 @@
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// Centralized debug logging manager for consistent logging across all systems.
|
||||
/// Provides a global debug toggle and formatted log output.
|
||||
/// </summary>
|
||||
public class DebugManager : MonoBehaviour
|
||||
{
|
||||
public static DebugManager Instance { get; private set; }
|
||||
|
||||
[SerializeField]
|
||||
private bool enableGlobalDebugLogs = true;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
if (Instance != null && Instance != this)
|
||||
{
|
||||
Debug.LogWarning("Multiple DebugManager instances detected. Destroying duplicate.", gameObject);
|
||||
Destroy(gameObject);
|
||||
return;
|
||||
}
|
||||
Instance = this;
|
||||
}
|
||||
|
||||
void OnDestroy()
|
||||
{
|
||||
if (Instance == this) Instance = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Log a message if debug logging is enabled
|
||||
/// </summary>
|
||||
public static void Log(string category, string message, Object context = null)
|
||||
{
|
||||
if (Instance == null || !Instance.enableGlobalDebugLogs) return;
|
||||
Debug.Log($"[{category}] {message}", context);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Log a warning message
|
||||
/// </summary>
|
||||
public static void LogWarning(string category, string message, Object context = null)
|
||||
{
|
||||
Debug.LogWarning($"[{category}] {message}", context);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Log an error message
|
||||
/// </summary>
|
||||
public static void LogError(string category, string message, Object context = null)
|
||||
{
|
||||
Debug.LogError($"[{category}] {message}", context);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set global debug logging state
|
||||
/// </summary>
|
||||
public static void SetDebugEnabled(bool enabled)
|
||||
{
|
||||
if (Instance != null)
|
||||
Instance.enableGlobalDebugLogs = enabled;
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/Utilities/DebugManager.cs.meta
Normal file
2
Assets/Scripts/Utilities/DebugManager.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 03b75d1f7b86ca241b28fa04bf7a3968
|
||||
69
Assets/Scripts/Utilities/ProximityUtility.cs
Normal file
69
Assets/Scripts/Utilities/ProximityUtility.cs
Normal file
@@ -0,0 +1,69 @@
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// Utility class for proximity and distance-based calculations.
|
||||
/// Centralizes common distance checking logic used throughout the game.
|
||||
/// </summary>
|
||||
public static class ProximityUtility
|
||||
{
|
||||
/// <summary>
|
||||
/// Check if two objects are within a specified distance
|
||||
/// </summary>
|
||||
public static bool IsWithinDistance(Vector3 from, Vector3 to, float distance)
|
||||
{
|
||||
return Vector3.Distance(from, to) <= distance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if two Transform objects are within a specified distance
|
||||
/// </summary>
|
||||
public static bool IsWithinDistance(Transform from, Transform to, float distance)
|
||||
{
|
||||
if (from == null || to == null) return false;
|
||||
return IsWithinDistance(from.position, to.position, distance);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if GameObject is within a specified distance
|
||||
/// </summary>
|
||||
public static bool IsWithinDistance(GameObject from, GameObject to, float distance)
|
||||
{
|
||||
if (from == null || to == null) return false;
|
||||
return IsWithinDistance(from.transform.position, to.transform.position, distance);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the distance between two positions
|
||||
/// </summary>
|
||||
public static float GetDistance(Vector3 from, Vector3 to)
|
||||
{
|
||||
return Vector3.Distance(from, to);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the distance between two Transform objects
|
||||
/// </summary>
|
||||
public static float GetDistance(Transform from, Transform to)
|
||||
{
|
||||
if (from == null || to == null) return float.MaxValue;
|
||||
return GetDistance(from.position, to.position);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the distance between two GameObjects
|
||||
/// </summary>
|
||||
public static float GetDistance(GameObject from, GameObject to)
|
||||
{
|
||||
if (from == null || to == null) return float.MaxValue;
|
||||
return GetDistance(from.transform.position, to.transform.position);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if object is in range and handle both close and far cases
|
||||
/// </summary>
|
||||
public static bool CheckRange(Vector3 from, Vector3 to, float minDistance, float maxDistance, out float distance)
|
||||
{
|
||||
distance = GetDistance(from, to);
|
||||
return distance >= minDistance && distance <= maxDistance;
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/Utilities/ProximityUtility.cs.meta
Normal file
2
Assets/Scripts/Utilities/ProximityUtility.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e4d921cf5be3f7d4d89cbc3067f48272
|
||||
Reference in New Issue
Block a user