add unlock system for tools

This commit is contained in:
2026-06-29 22:15:59 -05:00
parent 9dfcee6159
commit fc0bcf0201
12 changed files with 513 additions and 65 deletions
-1
View File
@@ -22,7 +22,6 @@ public class GameManager : MonoBehaviour
// Are we currently in a scene transition?
private bool isTransitioningScenes = false;
private void Awake()
{
// If there is an instance, and it's not me, delete myself.
+90 -18
View File
@@ -13,6 +13,7 @@ public class PlayerController : MonoBehaviour
public float walkSpeed;
public float sprintMultiplier = 1.5f;
public float gravity = 10;
public float rotationSpeed = 15;
Interactable nearestInteractable;
public CameraController cameraController;
@@ -23,14 +24,9 @@ public class PlayerController : MonoBehaviour
private bool isSprinting = false;
private bool paused = false;
// Mathematical plane used to catch the raycast from camera to get direction for
// looking at the mouse
private Plane groundPlane;
// Start is called once before the first execution of Update after the MonoBehaviour is created
void Start()
{
groundPlane = new Plane(Vector3.up, -DougBody.transform.position.y);
characterController = GetComponent<CharacterController>();
cameraController = GetComponent<CameraController>();
SwitchTools(GameManager.Instance.PlayerManager.CurrentToolIndex);
@@ -40,7 +36,6 @@ void Start()
void Update()
{
moveDir = Vector3.zero;
groundPlane.distance = -DougBody.transform.position.y;
// TODO: Move input detection somewhere else
if (Input.GetKeyDown(KeyCode.Escape))
@@ -223,14 +218,67 @@ void ToolUseDetector()
/// </summary>
void RotatePlayerTowardMouse()
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
/*Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
Plane plane = new Plane(cameraController.MainCamera.transform.forward,DougBody.transform.position);
if (groundPlane.Raycast(ray, out var distance))
if (plane.Raycast(ray, out var distance))
{
Vector3 hitPoint = ray.GetPoint(distance);
Vector3 direction = hitPoint - DougBody.transform.position;
direction.y = 0f;
DougBody.transform.LookAt(new Vector3(hitPoint.x, DougBody.transform.position.y, hitPoint.z));
///DougBody.transform.LookAt(new Vector3(hitPoint.x, DougBody.transform.position.y, hitPoint.z));
if (direction.sqrMagnitude > 0.001f)
{
Quaternion targetRotation =
Quaternion.LookRotation(direction);
DougBody.transform.rotation = Quaternion.Slerp(
DougBody.transform.rotation,
targetRotation,
rotationSpeed * Time.deltaTime
);
}
}*/
Camera cam = cameraController.MainCamera;
Ray ray = cam.ScreenPointToRay(Input.mousePosition);
Vector3 targetPoint;
// 1. Try real world hit first (most stable)
if (Physics.Raycast(ray, out RaycastHit hit, 1000f))
{
targetPoint = hit.point;
}
else
{
// 2. Fallback to plane at player height
Plane plane = new Plane(Vector3.up, DougBody.transform.position);
if (!plane.Raycast(ray, out float enter))
return;
targetPoint = ray.GetPoint(enter);
}
// 3. Flatten direction so we only rotate on Y axis
Vector3 direction = targetPoint - DougBody.transform.position;
direction.y = 0f;
if (direction.sqrMagnitude < 0.001f)
return;
// 4. Convert to rotation
Quaternion targetRotation = Quaternion.LookRotation(direction.normalized);
// 5. Smooth rotation
DougBody.transform.rotation = Quaternion.Slerp(
DougBody.transform.rotation,
targetRotation,
rotationSpeed * Time.deltaTime
);
}
/// <summary>
@@ -305,8 +353,12 @@ public Vector3 GetVelocity()
/// Switches the tools in hand by taking an index
///
/// <param name="toolIndex">Index of the tool to switch to</param>
/// <param name="defaultToUp">
/// If true, try the next tool if the tool at toolIndex is not unlocked yet. If false,
/// try the previous tool if the tool at toolIndex is not unlocked.
/// </param>
/// </summary>
private void SwitchTools(int toolIndex)
private void SwitchTools(int toolIndex, bool defaultToUp=true)
{
if (toolIndex < 0 || toolIndex >= tools.Length)
{
@@ -319,9 +371,20 @@ private void SwitchTools(int toolIndex)
tool.gameObject.SetActive(false);
}
// load new tool
GameManager.Instance.PlayerManager.CurrentToolIndex = toolIndex;
tools[GameManager.Instance.PlayerManager.CurrentToolIndex].gameObject.SetActive(true);
if (GameManager.Instance.PlayerManager.IsToolUnlocked(tools[toolIndex].ToolType))
{
// load new tool
GameManager.Instance.PlayerManager.CurrentToolIndex = toolIndex;
tools[GameManager.Instance.PlayerManager.CurrentToolIndex].gameObject.SetActive(true);
}
else
{
// try next index
if (defaultToUp)
SwitchTools(toolIndex + 1, defaultToUp);
else
SwitchTools(toolIndex - 1, defaultToUp);
}
}
/// <summary>
@@ -332,11 +395,11 @@ private void SwitchTools(int toolIndex)
private void SwitchTools(bool up)
{
// calculate index of next tool
int newIndex = up ? GameManager.Instance.PlayerManager.CurrentToolIndex - 1 : GameManager.Instance.PlayerManager.CurrentToolIndex + 1;
int newIndex = up ? GameManager.Instance.PlayerManager.CurrentToolIndex + 1 : GameManager.Instance.PlayerManager.CurrentToolIndex - 1;
if (newIndex < 0)
{
newIndex = tools.Length + newIndex;
newIndex = tools.Length - 1;
}
else if (newIndex >= tools.Length)
{
@@ -346,9 +409,18 @@ private void SwitchTools(bool up)
// disable current tool
tools[GameManager.Instance.PlayerManager.CurrentToolIndex].gameObject.SetActive(false);
// load new tool
GameManager.Instance.PlayerManager.CurrentToolIndex = newIndex;
tools[GameManager.Instance.PlayerManager.CurrentToolIndex].gameObject.SetActive(true);
if (GameManager.Instance.PlayerManager.IsToolUnlocked(tools[newIndex].ToolType))
{
// load new tool
GameManager.Instance.PlayerManager.CurrentToolIndex = newIndex;
tools[GameManager.Instance.PlayerManager.CurrentToolIndex].gameObject.SetActive(true);
}
else
{
// try next tool
if (up) SwitchTools(newIndex + 1);
else SwitchTools(newIndex - 1, false);
}
}
}
@@ -1,3 +1,5 @@
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
@@ -5,6 +7,27 @@
public class PlayerManager : MonoBehaviour
{
public int CurrentToolIndex;
private SerializableDictionary<ToolEnum, bool> unlockedToolDict = null;
public bool IsToolUnlocked(ToolEnum tool)
{
if (unlockedToolDict.TryGetValue(tool, out bool unlocked)) return unlocked;
// If no tool dict saved, init the default one (only None is unlocked)
unlockedToolDict = new SerializableDictionary<ToolEnum, bool>();
foreach (ToolEnum toolType in Enum.GetValues(typeof(ToolEnum)))
{
if (toolType == ToolEnum.None)
unlockedToolDict.Add(toolType, true);
else
unlockedToolDict.Add(toolType, false);
}
// Try again
if (unlockedToolDict.TryGetValue(tool, out bool unlockedRetry)) return unlockedRetry;
return false;
}
#region Player Data Save/Load
public void SavePlayerData(ref PlayerSaveData data)
@@ -12,6 +35,7 @@ public void SavePlayerData(ref PlayerSaveData data)
data.activeToolIndex = GameManager.Instance.PlayerManager.CurrentToolIndex;
data.dougPosition = GameManager.Instance.PlayerController.transform.position;
data.currentScene = SceneManager.GetActiveScene().name;
data.unlockedToolDict = unlockedToolDict;
}
public void LoadPlayerData(PlayerSaveData data)
@@ -25,6 +49,24 @@ public void LoadPlayerData(PlayerSaveData data)
GameManager.Instance.PlayerManager.CurrentToolIndex = data.activeToolIndex;
GameManager.Instance.PlayerController.transform.position = data.dougPosition;
}
if (data.unlockedToolDict != null)
{
unlockedToolDict = data.unlockedToolDict;
}
else
{
// If no tool dict saved, init the default one (only None is unlocked)
unlockedToolDict = new SerializableDictionary<ToolEnum, bool>();
foreach (ToolEnum toolType in Enum.GetValues(typeof(ToolEnum)))
{
if (toolType == ToolEnum.None)
unlockedToolDict.Add(toolType, true);
else
unlockedToolDict.Add(toolType, false);
}
}
}
#endregion
}
@@ -35,4 +77,5 @@ public struct PlayerSaveData
public int activeToolIndex;
public Vector3 dougPosition;
public string currentScene;
public SerializableDictionary<ToolEnum, bool> unlockedToolDict;
}
+14
View File
@@ -0,0 +1,14 @@
using UnityEngine;
public class NoTool : Tool
{
public override void Use()
{
// Do nothin'
}
public override void UseAlt()
{
// Do nothin'
}
}
+2
View File
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 15984e9d9e7f6074f97a69185e39ebb6
+41 -19
View File
@@ -20,7 +20,6 @@ private void Update()
{
if (GameManager.Instance.PlayerController != null)
{
SetGroundPlane();
DrawTargetProjection();
}
}
@@ -43,28 +42,51 @@ private void DrawTargetProjection()
targetProjection = GetComponentInChildren<DecalProjector>();
}
Transform player = GameManager.Instance.PlayerController.DougBody.transform;
Transform camera = GameManager.Instance.PlayerController.cameraController.MainCamera.transform;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (handGroundPlane.Raycast(ray, out var distance))
Vector3 targetPoint;
// First try real world geometry
if (Physics.Raycast(ray, out RaycastHit hit, 1000f))
{
Vector3 hitPoint = ray.GetPoint(distance);
float distanceFromPoint = Vector3.Distance(hitPoint, transform.position);
if (distanceFromPoint > range)
{
Vector3 directionTowardMouse = (hitPoint - transform.position).normalized;
targetProjection.transform.position = new Vector3(transform.position.x, hitPoint.y, transform.position.z);
targetProjection.transform.position += directionTowardMouse * range;
throwForce = range;
}
else
{
targetProjection.gameObject.SetActive(true);
targetProjection.transform.position = hitPoint;
throwForce = distanceFromPoint;
}
targetPoint = hit.point;
}
else
{
// Fallback plane if nothing hit
Plane plane = new Plane(Vector3.up, player.position);
if (!plane.Raycast(ray, out float distance))
return;
targetPoint = ray.GetPoint(distance);
}
// Flatten relative to player
Vector3 offset = targetPoint - player.position;
offset.y = 0f;
// Clamp range
offset = Vector3.ClampMagnitude(offset, range);
throwForce = offset.magnitude;
// Final position
Vector3 finalPosition = player.position + offset;
// Snap back to ground
if (Physics.Raycast(
finalPosition + Vector3.up * 10f,
Vector3.down,
out RaycastHit groundHit,
50f))
{
finalPosition = groundHit.point;
}
targetProjection.transform.position = finalPosition;
}
public override void Use()
+11 -1
View File
@@ -2,8 +2,8 @@
public abstract class Tool : MonoBehaviour
{
[Header("Base Tool Parameters")]
public ToolEnum ToolType = ToolEnum.None;
public float useTimeSec;
public float altUseTimeSec;
public bool inUse;
@@ -14,3 +14,13 @@ public abstract class Tool : MonoBehaviour
// secondary (right click)
public abstract void UseAlt();
}
public enum ToolEnum
{
None,
Shovel,
Flashlight,
GrappleGun,
Ballgun,
ThrowableHand
}
@@ -0,0 +1,31 @@
using System.Collections.Generic;
using UnityEngine;
[System.Serializable]
public class SerializableDictionary<TKey, TValue> : Dictionary<TKey, TValue>, ISerializationCallbackReceiver
{
// THIS DOES NOT WORK IF KEY OR VALUE ARE NON-SERIALIZABLE TYPES
[SerializeField] private List<TKey> keys = new List<TKey>();
[SerializeField] private List<TValue> values = new List<TValue>();
public void OnBeforeSerialize()
{
keys.Clear();
values.Clear();
foreach (var pair in this)
{
keys.Add(pair.Key);
values.Add(pair.Value);
}
}
public void OnAfterDeserialize()
{
Clear();
int count = Mathf.Min(keys.Count, values.Count);
for (int i = 0; i < count; i++)
{
Add(keys[i], values[i]);
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 45405bd3a7fecb94fa903865f4764cda