Added new utils from gamejams

This commit is contained in:
Martin Johnson 2021-07-31 22:24:34 -04:00
parent 4e7f047ed7
commit abc67df2a2
26 changed files with 551 additions and 486 deletions

View File

@ -1,21 +1,23 @@
using UnityEditor;
using UnityEngine;
[CustomEditor(typeof(AudioEvent), true)]
public class AudioEventInspector : Editor
namespace AudioEvent
{
[SerializeField] private AudioSource audioSource;
[CustomEditor(typeof(AudioEvent), true)]
public class AudioEventInspector : Editor
{
[SerializeField] private AudioSource _audioSource;
private void OnEnable()
{
audioSource =
_audioSource =
EditorUtility.CreateGameObjectWithHideFlags("Audio preview", HideFlags.HideAndDontSave,
typeof(AudioSource)).GetComponent<AudioSource>();
}
private void OnDisable()
{
DestroyImmediate(audioSource.gameObject);
DestroyImmediate(_audioSource.gameObject);
}
@ -26,9 +28,10 @@ public class AudioEventInspector : Editor
EditorGUI.BeginDisabledGroup(serializedObject.isEditingMultipleObjects);
if (GUILayout.Button("Preview"))
{
((AudioEvent) target).Play(audioSource);
((AudioEvent) target).Play(_audioSource);
}
EditorGUI.EndDisabledGroup();
}
}
}

View File

@ -1,6 +1,9 @@
using UnityEngine;
public abstract class AudioEvent : ScriptableObject
namespace AudioEvent
{
public abstract class AudioEvent : ScriptableObject
{
public abstract void Play(AudioSource source);
}
}

View File

@ -1,20 +1,24 @@
using UnityEngine;
using RangedValue;
using UnityEngine;
[CreateAssetMenu(menuName = "ScriptableObject/SimpleAudioEvent")]
public class SimpleAudioEvent : AudioEvent
namespace AudioEvent
{
public AudioClip[] clips;
public RangedFloat volume;
[CreateAssetMenu(menuName = "ScriptableObject/SimpleAudioEvent")]
public class SimpleAudioEvent : AudioEvent
{
public AudioClip[] Clips;
public RangedFloat Volume = new RangedFloat(0.9f,1f);
[MinMaxRange(0, 2)] public RangedFloat pitch;
[MinMaxRange(0, 2)] public RangedFloat Pitch = new RangedFloat(0.95f,1.05f);
public override void Play(AudioSource source)
{
if (clips.Length == 0) return;
if (Clips.Length == 0) return;
source.clip = clips[Random.Range(0, clips.Length)];
source.volume = Random.Range(volume.minValue, volume.maxValue);
source.pitch = Random.Range(pitch.minValue, pitch.maxValue);
source.clip = Clips[Random.Range(0, Clips.Length)];
source.volume = Random.Range(Volume.MinValue, Volume.MaxValue);
source.pitch = Random.Range(Pitch.MinValue, Pitch.MaxValue);
source.Play();
}
}
}

View File

@ -1,20 +0,0 @@
using UnityEngine;
public class UpdateChild{
public static void recursiveCallOnChild (GameObject _obj)
{
if (_obj == null)
return;
//Changes to the current object
foreach (Transform _child in _obj.transform)
{
if (_child == null)
continue;
recursiveCallOnChild(_child.gameObject);
}
}
}

View File

@ -1,27 +0,0 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ApplyRandForce : MonoBehaviour {
public string buttonName = "Fire1";
public float forceAmount = 10.0f;
public float torqueAmount = 10.0f;
public ForceMode forceMode;
public float maxAngularVelocity;
public Rigidbody rb;
void Start() {
rb.maxAngularVelocity = maxAngularVelocity;
}
void FixedUpdate ()
{
if(Input.GetButtonDown(buttonName))
{
rb.AddForce(Random.onUnitSphere*forceAmount,forceMode);
rb.AddTorque(Random.onUnitSphere * torqueAmount, forceMode);
}
}
}

View File

@ -1,11 +0,0 @@
using UnityEngine;
public class DieValue : MonoBehaviour
{
public int value;
public int getValue() {
return value;
}
}

View File

@ -1,31 +0,0 @@
using UnityEngine;
public class DisplayCurrentDieValue : MonoBehaviour
{
public LayerMask dieValueColliderLayer;
public Rigidbody rb;
private int currentValue;
private bool rollComplete = false;
void Update () {
if (rb.IsSleeping() && !rollComplete)
{
rollComplete = true;
Debug.Log("Dice stopped rolling, result is: " + currentValue.ToString());
}
else if(!rb.IsSleeping())
{
rollComplete = false;
}
RaycastHit hit;
if(Physics.Raycast(transform.position,Vector3.up,out hit,Mathf.Infinity,dieValueColliderLayer)){
// Reading the value of the collider on the die top face
currentValue = hit.collider.GetComponent<DieValue>().getValue();
}
}
}

View File

@ -0,0 +1,19 @@
using UnityEngine;
public static class GameObjectExtensions
{
public static int GetNumberOfComponents(this GameObject gameObject)
{
return gameObject.GetComponentsInChildren<Component>().Length - 1;
}
public static void Show(this GameObject gameObject)
{
gameObject.SetActive(true);
}
public static void Hide(this GameObject gameObject)
{
gameObject.SetActive(false);
}
}

View File

@ -1,226 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GridSystem : MonoBehaviour {
public LayerMask mask;
public GameObject prefab;
public float nodeSize = 1f;
private Grid grid;
private GameObject currentPrefab;
private int gridSizeX;
private int gridSizeZ;
private Boolean currentPrefabOnMat = false;
// Use this for initialization
void Start () {
//Get Size of the gameMat
GetGridSize();
//Create the grid of nodes to the size of the mat
CreateGrid();
}
void Update () {
CheckKeyDown();
CheckMouseDown();
currentPrefabOnMat = MovePrefabToMouse();
}
private Boolean MovePrefabToMouse() {
int x;
int y;
if (MouseOnTableMat(out x, out y)) {
if (currentPrefab != null) {
if (x != -1 && y != -1) {
MoveCurrentPrefab(x, y);
return prefabOnMat();
}
}
}
return false;
}
private bool prefabOnMat() {
Boolean onMat = true;
foreach (Transform child in currentPrefab.transform) {
int x = Mathf.FloorToInt(currentPrefab.transform.position.x -
nodeSize / 2 +
child.localPosition.x / nodeSize +
gridSizeX / 2 -
transform.position.x);
int y = Mathf.FloorToInt(currentPrefab.transform.position.z -
nodeSize / 2 +
child.localPosition.z / nodeSize +
gridSizeZ / 2 -
transform.position.z);
if (!(Mathf.Clamp(x, 0, gridSizeX - 1) == x &&
Mathf.Clamp(y, 0, gridSizeZ - 1) == y)) {
onMat = false;
} else {
if (grid.grid[x, y].occupied) {
onMat = false;
}
}
}
return onMat;
}
private void CheckMouseDown() {
if (Input.GetMouseButtonDown(0) && currentPrefabOnMat) {
foreach (Transform child in currentPrefab.transform) {
int x = Mathf.Clamp(
Mathf.FloorToInt(currentPrefab.transform.position.x -
nodeSize / 2 +
child.localPosition.x / nodeSize +
gridSizeX / 2 -
transform.position.x),
0, gridSizeX - 1);
int y = Mathf.Clamp(
Mathf.FloorToInt(currentPrefab.transform.position.z -
nodeSize / 2 +
child.localPosition.z / nodeSize +
gridSizeZ / 2 -
transform.position.z),
0, gridSizeZ - 1);
grid.grid[x, y].occupied = true;
}
currentPrefab = null;
}
}
private void CheckKeyDown() {
if (Input.GetKeyDown(KeyCode.A)) {
if (currentPrefab == null) {
currentPrefab = Instantiate(prefab)
} else {
Destroy(currentPrefab);
}
}
}
private void MoveCurrentPrefab(int x, int y) {
currentPrefab.transform.position = grid.grid[x,y].centerWorldPosition;
}
private bool MouseOnTableMat(out int x, out int y) {
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hitPoint;
if (Physics.Raycast(ray, out hitPoint, Mathf.Infinity, mask)) {
if (hitPoint.collider == GetComponent<Collider>()) {
x = Mathf.Clamp(
Mathf.FloorToInt(hitPoint.point.x - transform.position.x + gridSizeX / 2),
0, gridSizeX - 1);
y = Mathf.Clamp(
Mathf.FloorToInt(hitPoint.point.z - transform.position.z + gridSizeZ / 2),
0, gridSizeZ - 1);
return true;
}
}
x = -1;
y = -1;
return false;
}
private void GetGridSize() {
Bounds borders = GetComponent<Renderer>().bounds;
gridSizeX = Mathf.RoundToInt(borders.size.x);
gridSizeZ = Mathf.RoundToInt(borders.size.z);
}
private void CreateGrid() {
grid = new Grid(transform.position, gridSizeX, gridSizeZ, nodeSize);
}
}
public class Node{
public Vector3 centerWorldPosition;
public bool occupied;
public Node(Vector3 centerWorldPosition)
{
this.centerWorldPosition = centerWorldPosition;
occupied = false;
}
}
public class Grid{
public Node[,] grid;
public Grid(Vector3 gridCenter, int gridSizeX, int gridSizeZ, float NodeSize){
grid = new Node[gridSizeX, gridSizeZ];
for (int row = 0 ; row < gridSizeX; row++){
for (int collumn = 0; collumn < gridSizeZ ; collumn++){
Vector3 centerWorldPosition = gridCenter - (Vector3.right * gridSizeX / 2) -
(Vector3.forward * gridSizeZ / 2) +
(Vector3.right * NodeSize * row) +
(Vector3.forward * NodeSize * collumn) +
(Vector3.right * NodeSize / 2) +
(Vector3.forward * NodeSize / 2);
grid[row,collumn] = new Node(centerWorldPosition);
}
}
}
public String toString() {
String str = "";
for (int i = 0; i < 8; i++) {
for (int y = 0; y < 4; y++) {
str += $"\n[{i},{y}] : {grid[i, y].centerWorldPosition} - {grid[i, y].occupied}";
}
}
return str;
}
}

View File

@ -1,16 +1,18 @@
using UnityEditor;
using UnityEngine;
[CustomPropertyDrawer(typeof(RangedFloat), true)]
public class RangedFloatDrawer : PropertyDrawer
namespace RangedValue
{
[CustomPropertyDrawer(typeof(RangedFloat), true)]
public class RangedFloatDrawer : PropertyDrawer
{
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
label = EditorGUI.BeginProperty(position, label, property);
position = EditorGUI.PrefixLabel(position, label);
var minProp = property.FindPropertyRelative("minValue");
var maxProp = property.FindPropertyRelative("maxValue");
var minProp = property.FindPropertyRelative("MinValue");
var maxProp = property.FindPropertyRelative("MaxValue");
var minValue = minProp.floatValue;
var maxValue = maxProp.floatValue;
@ -47,4 +49,5 @@ public class RangedFloatDrawer : PropertyDrawer
EditorGUI.EndProperty();
}
}
}

View File

@ -1,13 +1,16 @@
using System;
public class MinMaxRangeAttribute : Attribute
namespace RangedValue
{
public float Min { get; private set; }
public float Max { get; private set; }
public class MinMaxRangeAttribute : Attribute
{
public float Min { get; }
public float Max { get; }
public MinMaxRangeAttribute(float min, float max)
{
Min = min;
Max = max;
}
}
}

View File

@ -1,8 +1,22 @@
using System;
[Serializable]
public struct RangedFloat
namespace RangedValue
{
public float minValue;
public float maxValue;
[Serializable]
public struct RangedFloat
{
public float MinValue;
public float MaxValue;
public RangedFloat(float minValue, float maxValue)
{
MinValue = minValue;
MaxValue = maxValue;
}
public float RandomValueInRange()
{
return UnityEngine.Random.Range(MinValue, MaxValue);
}
}
}

View File

@ -0,0 +1,43 @@
using System.Diagnostics;
using System.IO;
using UnityEditor;
using UnityEngine;
namespace SaveSystem
{
[CustomEditor(typeof(SaveManager), true)]
public class SaveManagerInspector : Editor
{
public override void OnInspectorGUI()
{
DrawDefaultInspector();
SaveManager saveManager = (SaveManager) target;
GUI.enabled = EditorApplication.isPlaying;
EditorGUI.BeginDisabledGroup(serializedObject.isEditingMultipleObjects);
EditorGUILayout.BeginHorizontal();
if (GUILayout.Button("Save", EditorStyles.miniButtonLeft))
{
saveManager.Save();
}
if (GUILayout.Button("Load", EditorStyles.miniButtonRight))
{
saveManager.Load();
}
EditorGUILayout.EndHorizontal();
if (GUILayout.Button("Save to Json"))
{
saveManager.SaveExplicit();
}
GUI.enabled = true;
if (GUILayout.Button("Open explicit save in notepad++"))
{
Process.Start("notepad++.exe", Path.Combine(Application.dataPath, "../") + "saveData01.json");
}
EditorGUI.EndDisabledGroup();
}
}
}

8
SaveSystem/ISaveData.cs Normal file
View File

@ -0,0 +1,8 @@
namespace SaveSystem
{
public interface ISaveData
{
public void Save(ref GameData gameData);
public void Load(ref GameData gameData);
}
}

99
SaveSystem/SaveManager.cs Normal file
View File

@ -0,0 +1,99 @@
using System;
using System.Collections.Generic;
using System.IO;
using Core;
using UnityEngine;
using UnityEngine.Events;
namespace SaveSystem
{
public class SaveManager : Singleton<SaveManager>
{
public static UnityAction<GameData> OnLoadCompleted;
private static readonly List<ISaveData> SaveDataList = new List<ISaveData>();
private GameData _saveGame;
private int _loadedGameNumber = -1;
protected override void Awake()
{
base.Awake();
_saveGame = new GameData();
}
private void Start() => Load();
public void Save(int saveNumber = 1)
{
foreach (ISaveData saveData in SaveDataList)
{
saveData.Save(ref _saveGame);
}
string json = JsonUtility.ToJson(_saveGame);
byte[] plainTextBytes = System.Text.Encoding.UTF8.GetBytes(json);
string b64 = Convert.ToBase64String(plainTextBytes);
using StreamWriter sw = new StreamWriter($"saveData{saveNumber:00}.sgd");
sw.Write(b64);
_loadedGameNumber = -1;
}
public void SaveExplicit()
{
foreach (ISaveData saveData in SaveDataList)
{
saveData.Save(ref _saveGame);
}
string json = JsonUtility.ToJson(_saveGame);
using StreamWriter sw = new StreamWriter($"saveData01.json");
sw.Write(json);
}
public void Load(int saveNumber = 1)
{
if (saveNumber != _loadedGameNumber)
{
try
{
using StreamReader sr = new StreamReader($"saveData{saveNumber:00}.sgd");
string b64 = sr.ReadToEnd();
byte[] plainTextBytes = Convert.FromBase64String(b64);
string json = System.Text.Encoding.UTF8.GetString(plainTextBytes);
_saveGame = JsonUtility.FromJson<GameData>(json);
}
catch (FileNotFoundException)
{
Debug.LogWarning($"Tried to load saveData{saveNumber:00}.sgd but file doesn't exist");
}
_loadedGameNumber = saveNumber;
}
foreach (ISaveData saveData in SaveDataList)
{
saveData.Load(ref _saveGame);
}
OnLoadCompleted?.Invoke(_saveGame);
}
public static void Register(ISaveData saveData)
{
if (!SaveDataList.Contains(saveData))
{
SaveDataList.Add(saveData);
}
}
public static void Unregister(ISaveData saveData)
{
SaveDataList.Remove(saveData);
}
protected override void OnDestroy()
{
base.OnDestroy();
Save();
}
}
}

39
SerializableDictionary.cs Normal file
View File

@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using UnityEngine;
[Serializable]
public class SerializableDictionary<TKey, TValue> : Dictionary<TKey, TValue>, ISerializationCallbackReceiver
{
[SerializeField] private List<TKey> _keys = new List<TKey>();
[SerializeField] private List<TValue> _values = new List<TValue>();
public void OnBeforeSerialize()
{
_keys.Clear();
_values.Clear();
foreach (KeyValuePair<TKey,TValue> pair in this)
{
_keys.Add(pair.Key);
_values.Add(pair.Value);
}
}
public void OnAfterDeserialize()
{
Clear();
if (_keys.Count != _values.Count)
{
throw new Exception(
$"There are {_keys.Count} keys and {_values.Count} values after deserialization." +
" Make sure that both key and value types are serializable.");
}
for (int i = 0; i < _keys.Count; ++i)
{
Add(_keys[i], _values[i]);
}
}
}

77
Singleton.cs Normal file
View File

@ -0,0 +1,77 @@
using System;
using UnityEngine;
namespace Core
{
public abstract class Singleton<T> : MonoBehaviour where T : Singleton<T>
{
private static T _instance;
public static T Instance
{
get
{
if (!HasInstance)
{
Debug.LogWarning(
$"Trying to access a singleton of type {typeof(T).Name} that is not in the scene. " +
"If you are trying to see if the Instance exists, you should use the HasInstance static property instead.");
}
return _instance;
}
}
private bool _destroyedByScript;
public static bool HasInstance => _instance != null;
protected virtual void Awake()
{
_destroyedByScript = false;
if (HasInstance)
{
Debug.Log($"Two or more instances of a singleton of type {typeof(T).Name} were found in the scene. " +
"The new instance of the singleton trying to register will be removed.");
DestroyInstance();
return;
}
try
{
_instance = (T) Convert.ChangeType(this, typeof(T));
}
catch (InvalidCastException)
{
Debug.Assert(false, "Singleton's T type should be the derived class.");
}
}
private void DestroyInstance()
{
_destroyedByScript = true;
if (gameObject.GetNumberOfComponents() == 1)
{
Destroy(gameObject);
}
else
{
Destroy(this);
}
}
protected virtual void OnDestroy()
{
if (_instance == this)
{
_instance = null;
}
else if (!_destroyedByScript)
{
Debug.LogWarning($"Instance of {typeof(T).Name} deleted, but was not the singleton instance.");
}
}
}
}

49
Timer.cs Normal file
View File

@ -0,0 +1,49 @@
using UnityEngine;
using UnityEngine.Events;
namespace Core
{
public class Timer : MonoBehaviour
{
public UnityAction onTimeOver;
public bool IsRunning { get; private set; }
public float CurrentTime { get; private set; }
[SerializeField] private float _startingTime = 600;
[SerializeField] private bool _isCountDown;
private void Start()
{
CurrentTime = _startingTime;
}
private void Update()
{
if (IsRunning)
{
if (_isCountDown)
{
if (CurrentTime > 0)
{
CurrentTime -= Time.deltaTime;
}
else
{
CurrentTime = 0;
IsRunning = false;
onTimeOver?.Invoke();
}
}
else
{
CurrentTime += Time.deltaTime;
}
}
}
public void StartTimer() => IsRunning = true;
public void StopTimer() => IsRunning = false;
}
}

View File

@ -0,0 +1,21 @@
using AudioEvent;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;
namespace UI
{
public class ButtonUIComponent : UIComponentBase
{
[SerializeField] private Button _button;
[SerializeField] private SimpleAudioEvent _clickSound;
[SerializeField] private AudioSource _audioSource;
private void Awake() => _button.onClick.AddListener(() => _clickSound.Play(_audioSource));
public void OnClick(UnityAction callback) => _button.onClick.AddListener(callback);
private void OnDestroy() => _button.onClick.RemoveAllListeners();
}
}

View File

@ -1,17 +0,0 @@
using UnityEngine;
namespace Util.UiComponents
{
public class ComponentBase : MonoBehaviour
{
public void Show()
{
gameObject.SetActive(true);
}
public void Hide()
{
gameObject.SetActive(false);
}
}
}

View File

@ -1,15 +0,0 @@
using TMPro;
using UnityEngine;
namespace Util.UiComponents
{
public class TextComponent : ComponentBase
{
[SerializeField] private TMP_Text text;
public void SetText(string newText)
{
text.text = newText;
}
}
}

View File

@ -1,33 +0,0 @@
using System;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;
namespace Util.UiComponents
{
public class SliderComponent : ComponentBase
{
[SerializeField] private Slider slider;
public void SetValue(float value, float max)
{
slider.maxValue = max;
slider.value = value;
}
public float GetValue()
{
return slider.value;
}
public void SetOnValueChanged(UnityAction<float> callBack)
{
slider.onValueChanged.AddListener(callBack);
}
private void OnDestroy()
{
slider.onValueChanged?.RemoveAllListeners();
}
}
}

View File

@ -0,0 +1,25 @@
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;
namespace UI
{
public class SliderUIComponent : UIComponentBase
{
[SerializeField] private Slider _slider;
public void SetValue(float value, float max)
{
_slider.maxValue = max;
_slider.value = value;
}
public void SetValue(float value) => _slider.value = value;
public float GetValue() => _slider.value;
public void OnValueChanged(UnityAction<float> callBack) => _slider.onValueChanged.AddListener(callBack);
private void OnDestroy() => _slider.onValueChanged?.RemoveAllListeners();
}
}

View File

@ -1,15 +0,0 @@
using TMPro;
using UnityEngine;
namespace Util.UiComponents
{
public class TextComponent : ComponentBase
{
[SerializeField] private TMP_Text text;
public void SetText(string newText)
{
text.text = newText;
}
}
}

View File

@ -0,0 +1,22 @@
using TMPro;
using UnityEngine;
namespace UI
{
public class TextUIComponent : UIComponentBase
{
[SerializeField] private TMP_Text _text;
private string _initialText;
private void Awake() => _initialText = _text.text;
public void SetText(string newText) => _text.text = string.IsNullOrEmpty(newText) ? string.Empty : newText;
public void ResetText() => _text.text = _initialText;
public void AddText(string textToAdd) => _text.text += textToAdd;
public void HideText() => _text.text = string.Empty;
}
}

View File

@ -0,0 +1,28 @@
using UnityEngine;
namespace UI
{
public class UIComponentBase : MonoBehaviour
{
public void Show()
{
gameObject.Show();
}
public void Hide()
{
gameObject.Hide();
}
public void ToggleDisplay()
{
gameObject.SetActive(!gameObject.activeInHierarchy);
}
public void SetActive(bool active)
{
gameObject.SetActive(active);
}
}
}