Initial commit

This commit is contained in:
2026-02-10 21:27:46 +00:00
commit 3a8163af21
3261 changed files with 563042 additions and 0 deletions

View File

@@ -0,0 +1,36 @@
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.AI;
public abstract class Character : MonoBehaviour
{
[Header("Stats")]
public int CurHp = 10;
public int MaxHp = 10;
[Header("Components")]
// Optional movement controller reference, assign in inspector if used
public NavMeshMovementController MovementController;
protected Character target;
public event UnityAction onTakeDamage;
public void TakeDamage(int damageToTake)
{
CurHp -= damageToTake;
onTakeDamage?.Invoke();
if (CurHp <= 0) Die();
}
public virtual void Die()
{
Destroy(gameObject);
}
public void SetTarget(Character t)
{
target = t;
}
public Character GetTarget() => target;
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 88a8c6497f18c2440a367d9c2daa3146

View File

@@ -0,0 +1,97 @@
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.InputSystem;
public class ClickToMoveInputSystem : MonoBehaviour
{
[Header("References")]
public Camera cam;
public LayerMask movementLayers; // ground
public LayerMask selectionLayers; // enemies, NPCs, interactables
void Awake()
{
if (cam == null) cam = Camera.main;
}
// Hook this to PointerPos action (Value Vector2)
Vector2 pointerScreenPos;
public void OnPointerPos(InputAction.CallbackContext ctx)
{
pointerScreenPos = ctx.ReadValue<Vector2>();
}
// Hook this to LeftClick action (Button)
public void OnLeftClick(InputAction.CallbackContext ctx)
{
if (!ctx.performed) return;
if (cam == null) return;
Ray ray = cam.ScreenPointToRay(pointerScreenPos);
if (Physics.Raycast(ray, out RaycastHit hit, 500f, selectionLayers))
{
// Try to find a Character on the hit object or its parents
var character = hit.collider.GetComponentInParent<Character>();
if (character != null)
{
if (Player.Current != null) Player.Current.SetTarget(character);
var selectable = hit.collider.GetComponentInParent<ISelectable>();
selectable?.OnSelected();
return;
}
// Generic interactable
var interactable = hit.collider.GetComponentInParent<Interactable>();
interactable?.OnSelect();
return;
}
// Nothing selected, clear target
if (Player.Current != null) Player.Current.SetTarget(null);
}
// Hook this to RightClick action (Button)
public void OnRightClick(InputAction.CallbackContext ctx)
{
if (!ctx.performed) return;
if (cam == null) return;
if (Player.Current == null) return;
Ray ray = cam.ScreenPointToRay(pointerScreenPos);
if (Physics.Raycast(ray, out RaycastHit hit, 500f, movementLayers))
{
// Move player's movement controller or NavMeshAgent
var movement = Player.Current.MovementController;
if (movement != null)
{
movement.MoveTo(hit.point);
}
else
{
// fallback: try to find NavMeshAgent on player root
var agent = Player.Current.GetComponent<UnityEngine.AI.NavMeshAgent>();
if (agent != null) agent.SetDestination(hit.point);
}
}
}
// Optional fallback if PointerPos not wired
void Update()
{
if (cam == null) return;
if (Mouse.current == null) return;
pointerScreenPos = Mouse.current.position.ReadValue();
if (Mouse.current.leftButton.wasPressedThisFrame)
{
// simulate InputAction callback context by calling handler directly
OnLeftClick(new InputAction.CallbackContext());
}
if (Mouse.current.rightButton.wasPressedThisFrame)
{
OnRightClick(new InputAction.CallbackContext());
}
}
}

View File

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

View File

@@ -0,0 +1,9 @@
using UnityEngine;
public class Interactable : MonoBehaviour
{
public virtual void OnSelect()
{
// implement interaction logic here
}
}

View File

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

View File

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

View File

@@ -0,0 +1,4 @@
public interface ISelectable
{
void OnSelected();
}

View File

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

View File

@@ -0,0 +1,47 @@
using UnityEngine;
using TMPro;
using UnityEngine.UI;
public class NameplateController : MonoBehaviour
{
[Header("UI refs")]
public TextMeshProUGUI nameText;
public Image backgroundImage; // optional background to tint
public Outline uiOutline; // optional UI outline component (for name text)
Camera mainCam;
Transform target;
void Awake()
{
mainCam = Camera.main;
}
public void Initialize(Selectable s, Color color, Vector3 offset)
{
target = s.transform;
if (nameText != null) nameText.text = s.displayName;
ApplyColor(color);
FollowTarget(target, offset);
}
public void FollowTarget(Transform t, Vector3 offset)
{
if (t == null) return;
Vector3 worldPos = t.position + offset;
transform.position = worldPos;
// Face camera
if (mainCam != null)
{
transform.rotation = Quaternion.LookRotation(transform.position - mainCam.transform.position);
}
}
void ApplyColor(Color c)
{
if (nameText != null) nameText.color = c;
if (backgroundImage != null) backgroundImage.color = new Color(c.r, c.g, c.b, backgroundImage.color.a);
if (uiOutline != null) uiOutline.effectColor = c;
}
}

View File

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

View File

@@ -0,0 +1,32 @@
using UnityEngine;
using UnityEngine.AI;
[RequireComponent(typeof(NavMeshAgent))]
public class NavMeshMovementController : MonoBehaviour
{
NavMeshAgent agent;
void Awake()
{
agent = GetComponent<NavMeshAgent>();
}
public void MoveTo(Vector3 worldPosition)
{
if (agent == null) return;
agent.isStopped = false;
agent.SetDestination(worldPosition);
}
public void Stop()
{
if (agent == null) return;
agent.isStopped = true;
}
public bool IsAtDestination(float tolerance = 0.1f)
{
if (agent == null) return true;
return !agent.pathPending && agent.remainingDistance <= agent.stoppingDistance + tolerance;
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 448c1cb12aa58e0408460dc7f94df117

23
Assets/Scripts/Player.cs Normal file
View File

@@ -0,0 +1,23 @@
using UnityEngine;
public class Player : Character
{
public static Player Current;
void Awake()
{
Current = this;
}
void OnDestroy()
{
if (Current == this) Current = null;
}
// Example hook for when a target is set
public override void Die()
{
base.Die();
// add player-specific death logic here
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 63c7f1b83c004a1419e85f2827182ac1

View File

@@ -0,0 +1,28 @@
using UnityEngine;
public enum Attitude { Friendly, Neutral, Enemy }
[RequireComponent(typeof(Collider))]
public class Selectable : MonoBehaviour
{
[Header("Identification")]
public string displayName = "Name";
public Attitude attitude = Attitude.Neutral;
[Header("Optional visuals")]
[Tooltip("Optional child GameObject used as a 3D outline/highlight. Enable/disable to show outline.")]
public GameObject outlineObject;
// Called by selection system
public void OnSelected()
{
if (outlineObject != null) outlineObject.SetActive(true);
SelectionManager.Instance.Select(this);
}
// Called by selection system
public void OnDeselected()
{
if (outlineObject != null) outlineObject.SetActive(false);
}
}

View File

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

View File

@@ -0,0 +1,81 @@
using UnityEngine;
using TMPro;
public class SelectionManager : MonoBehaviour
{
public static SelectionManager Instance { get; private set; }
[Header("Nameplate")]
public Canvas worldSpaceCanvas; // a World Space Canvas in scene
public GameObject nameplatePrefab; // prefab containing TextMeshProUGUI
public Vector3 nameplateOffset = new Vector3(0, 2.2f, 0);
[Header("Colors")]
public Color friendlyColor = Color.green;
public Color neutralColor = Color.cyan;
public Color enemyColor = Color.red;
Selectable current;
GameObject activeNameplate;
NameplateController nameplateController;
void Awake()
{
if (Instance != null && Instance != this) Destroy(gameObject);
Instance = this;
}
public void Select(Selectable s)
{
if (current == s) return;
ClearSelection();
current = s;
if (nameplatePrefab != null && worldSpaceCanvas != null)
{
activeNameplate = Instantiate(nameplatePrefab, worldSpaceCanvas.transform, false);
nameplateController = activeNameplate.GetComponent<NameplateController>();
if (nameplateController != null) nameplateController.Initialize(s, GetColorForAttitude(s.attitude), nameplateOffset);
}
}
public void ClearSelection()
{
if (current != null) current.OnDeselected();
current = null;
if (activeNameplate != null) Destroy(activeNameplate);
activeNameplate = null;
nameplateController = null;
}
Color GetColorForAttitude(Attitude a)
{
switch (a)
{
case Attitude.Friendly: return friendlyColor;
case Attitude.Neutral: return neutralColor;
case Attitude.Enemy: return enemyColor;
default: return neutralColor;
}
}
void Update()
{
// If nameplate exists, update its world position to follow the selected target
if (nameplateController != null && current != null)
{
nameplateController.FollowTarget(current.transform, nameplateOffset);
}
// Optional: clear selection if target destroyed
if (current == null && activeNameplate != null)
{
Destroy(activeNameplate);
activeNameplate = null;
nameplateController = null;
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 833844438e957ca44921bb85bb9e97b4

View File

@@ -0,0 +1,40 @@
using UnityEngine;
using UnityEngine.AI;
[RequireComponent(typeof(NavMeshAgent))]
public class SimplePlayerController : MonoBehaviour
{
public float rotationSpeed = 10f;
public float stopDistanceTolerance = 0.1f;
NavMeshAgent agent;
Vector3 lastVelocity;
void Awake()
{
agent = GetComponent<NavMeshAgent>();
agent.updateRotation = false; // manual rotation
}
void Update()
{
Vector3 vel = agent.velocity;
if (vel.sqrMagnitude > 0.01f)
{
Quaternion targetRot = Quaternion.LookRotation(vel.normalized);
transform.rotation = Quaternion.Slerp(transform.rotation, targetRot, rotationSpeed * Time.deltaTime);
}
if (!agent.pathPending && agent.remainingDistance <= agent.stoppingDistance + stopDistanceTolerance)
{
agent.isStopped = true;
}
else
{
agent.isStopped = false;
}
lastVelocity = vel;
}
public Vector3 GetVelocity() => lastVelocity;
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 9b1cb8a471cfe7d47a408690f585a039

View File

@@ -0,0 +1,73 @@
using UnityEngine;
[AddComponentMenu("Camera/Top Down Follow Camera")]
public class TopDownCameraController : MonoBehaviour
{
[Header("Target")]
public Transform target;
[Header("Positioning")]
public float distance = 8f; // horizontal distance from target
public float height = 6f; // vertical offset above target
[Range(0f, 89f)]
public float pitch = 80f; // X rotation in degrees (80 by default)
[Header("Smoothing")]
public bool smoothPosition = true;
public float positionSmoothTime = 0.12f;
public bool smoothRotation = true;
public float rotationSmoothTime = 0.08f;
Vector3 positionVelocity;
Quaternion rotationVelocity;
void Reset()
{
pitch = 80f;
distance = 8f;
height = 6f;
}
void LateUpdate()
{
if (target == null) return;
// Calculate desired camera position in local space relative to target
// Start with a rotation that looks down by 'pitch' degrees
Quaternion pitchRot = Quaternion.Euler(pitch, 0f, 0f);
// Desired offset: move back along the camera's forward by 'distance', then up by 'height'
Vector3 localOffset = pitchRot * new Vector3(0f, height, -distance);
Vector3 desiredPosition = target.position + localOffset;
// Smooth position
if (smoothPosition)
{
transform.position = Vector3.SmoothDamp(transform.position, desiredPosition, ref positionVelocity, positionSmoothTime);
}
else
{
transform.position = desiredPosition;
}
// Desired rotation: look at target but keep the pitch
Vector3 lookDirection = (target.position - transform.position).normalized;
Quaternion desiredRotation = Quaternion.LookRotation(lookDirection, Vector3.up);
// Optionally lock X rotation to the pitch value to avoid subtle tilts
Vector3 euler = desiredRotation.eulerAngles;
euler.x = pitch;
desiredRotation = Quaternion.Euler(euler);
// Smooth rotation
if (smoothRotation)
{
transform.rotation = Quaternion.Slerp(transform.rotation, desiredRotation, 1f - Mathf.Exp(-rotationSmoothTime * 60f * Time.deltaTime));
}
else
{
transform.rotation = desiredRotation;
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 13cefd56ccade4042bdb196df1a17efe