mirror of
https://github.com/GTA-ASM/SanAndreasUnity
synced 2024-11-10 06:34:16 +00:00
delete all scripts from Utilities folder, except 2 of them which are moved to different folders
This commit is contained in:
parent
1c96fc50f6
commit
e783257e4b
117 changed files with 0 additions and 6472 deletions
|
@ -1,9 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: 71d578a17e6a4cd4c893fbabe558ec16
|
|
||||||
folderAsset: yes
|
|
||||||
timeCreated: 1427127911
|
|
||||||
licenseType: Pro
|
|
||||||
DefaultImporter:
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
|
@ -1,113 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using UnityEngine;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
|
|
||||||
namespace SanAndreasUnity.Utilities
|
|
||||||
{
|
|
||||||
|
|
||||||
public class AsyncLoader<TKey, TObj>
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// All successfully loaded objects.
|
|
||||||
/// </summary>
|
|
||||||
private readonly Dictionary<TKey, TObj> m_Loaded = new Dictionary<TKey, TObj>();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Objects currently being loaded. Value represents list of subscribers which will be called when loading is finished.
|
|
||||||
/// </summary>
|
|
||||||
private readonly Dictionary<TKey, List<System.Action<TObj>>> m_Loading = new Dictionary<TKey, List<System.Action<TObj>>> ();
|
|
||||||
|
|
||||||
|
|
||||||
public AsyncLoader ()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public AsyncLoader (IEqualityComparer<TKey> comparer)
|
|
||||||
{
|
|
||||||
m_Loaded = new Dictionary<TKey, TObj> (comparer);
|
|
||||||
m_Loading = new Dictionary<TKey, List<System.Action<TObj>>> (comparer);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int GetNumObjectsLoaded ()
|
|
||||||
{
|
|
||||||
return m_Loaded.Count;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int GetNumObjectsLoading ()
|
|
||||||
{
|
|
||||||
return m_Loading.Count;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsObjectLoaded (TKey key)
|
|
||||||
{
|
|
||||||
return m_Loaded.ContainsKey (key);
|
|
||||||
}
|
|
||||||
|
|
||||||
public TObj GetLoadedObject (TKey key)
|
|
||||||
{
|
|
||||||
return m_Loaded [key];
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool TryLoadObject (TKey key, System.Action<TObj> onFinish)
|
|
||||||
{
|
|
||||||
ThreadHelper.ThrowIfNotOnMainThread(); // not needed, but to make sure
|
|
||||||
|
|
||||||
if (m_Loaded.ContainsKey(key))
|
|
||||||
{
|
|
||||||
onFinish(m_Loaded[key]);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_Loading.ContainsKey(key))
|
|
||||||
{
|
|
||||||
// this object is loading
|
|
||||||
// subscribe to finish event
|
|
||||||
m_Loading[key].Add(onFinish);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// insert it into loading dict
|
|
||||||
m_Loading[key] = new List<System.Action<TObj>>() {onFinish};
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool TryGetLoadedObject(TKey key, out TObj loadedObject)
|
|
||||||
{
|
|
||||||
ThreadHelper.ThrowIfNotOnMainThread(); // not needed, but to make sure
|
|
||||||
|
|
||||||
return m_Loaded.TryGetValue(key, out loadedObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnObjectFinishedLoading (TKey key, TObj obj, bool bSuccess)
|
|
||||||
{
|
|
||||||
ThreadHelper.ThrowIfNotOnMainThread(); // not needed, but to make sure
|
|
||||||
|
|
||||||
if (bSuccess)
|
|
||||||
{
|
|
||||||
if (m_Loaded.ContainsKey(key))
|
|
||||||
{
|
|
||||||
// this object was loaded in the meantime
|
|
||||||
// this can happen if someone else is loading objects synchronously
|
|
||||||
Debug.LogErrorFormat("Redundant load of object ({0}): {1}", typeof(TObj), key);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_Loaded.Add(key, obj);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_Loading.TryGetValue(key, out var subscribersList))
|
|
||||||
{
|
|
||||||
// remove from loading dict
|
|
||||||
m_Loading.Remove(key);
|
|
||||||
|
|
||||||
// invoke subscribers
|
|
||||||
foreach (var action in subscribersList)
|
|
||||||
Utilities.F.RunExceptionSafe(() => action(obj));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: 3e2b0a3b29a83c74d9cd4906420d6175
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
|
@ -1,276 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Threading;
|
|
||||||
using UnityEngine;
|
|
||||||
using Debug = UnityEngine.Debug;
|
|
||||||
|
|
||||||
namespace SanAndreasUnity.Utilities
|
|
||||||
{
|
|
||||||
public class BackgroundJobRunner
|
|
||||||
{
|
|
||||||
// TODO: maybe convert to class, because it takes 36 bytes - it's too much for red-black tree operations
|
|
||||||
public struct Job<T>
|
|
||||||
{
|
|
||||||
public System.Func<T> action;
|
|
||||||
public System.Action<T> callbackSuccess;
|
|
||||||
public System.Action<System.Exception> callbackError;
|
|
||||||
public System.Action<T> callbackFinish;
|
|
||||||
public float priority;
|
|
||||||
internal object result;
|
|
||||||
internal System.Exception exception;
|
|
||||||
internal long id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class JobComparer : IComparer<Job<object>>
|
|
||||||
{
|
|
||||||
public int Compare(Job<object> a, Job<object> b)
|
|
||||||
{
|
|
||||||
if (a.id == b.id)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (a.priority != b.priority)
|
|
||||||
return a.priority <= b.priority ? -1 : 1;
|
|
||||||
|
|
||||||
// priorities are the same
|
|
||||||
// the advantage has the job which was created earlier
|
|
||||||
|
|
||||||
return a.id <= b.id ? -1 : 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class ThreadParameters
|
|
||||||
{
|
|
||||||
public readonly BlockingCollection<Job<object>> jobs =
|
|
||||||
new BlockingCollection<Job<object>>(new System.Collections.Concurrent.ConcurrentQueue<Job<object>>());
|
|
||||||
public readonly Utilities.ConcurrentQueue<Job<object>> processedJobs = new Utilities.ConcurrentQueue<Job<object>>();
|
|
||||||
private bool _shouldThreadExit = false;
|
|
||||||
private readonly object _shouldThreadExitLockObject = new object();
|
|
||||||
|
|
||||||
public bool ShouldThreadExit()
|
|
||||||
{
|
|
||||||
lock (_shouldThreadExitLockObject)
|
|
||||||
return _shouldThreadExit;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void TellThreadToExit()
|
|
||||||
{
|
|
||||||
lock (_shouldThreadExitLockObject)
|
|
||||||
_shouldThreadExit = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Thread _thread;
|
|
||||||
private readonly ThreadParameters _threadParameters = new ThreadParameters();
|
|
||||||
private readonly Queue<Job<object>> _processedJobsBuffer = new Queue<Job<object>>(256);
|
|
||||||
|
|
||||||
private long _lastJobId = 0;
|
|
||||||
private readonly object _lastJobIdLockObject = new object();
|
|
||||||
|
|
||||||
private long _lastProcessedJobId = 0;
|
|
||||||
|
|
||||||
private readonly Stopwatch _stopwatch = new Stopwatch();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void StartThread()
|
|
||||||
{
|
|
||||||
if (_thread != null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_thread = new Thread(ThreadFunction);
|
|
||||||
_thread.Start(_threadParameters);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ShutDown()
|
|
||||||
{
|
|
||||||
if (_thread != null)
|
|
||||||
{
|
|
||||||
var sw = System.Diagnostics.Stopwatch.StartNew();
|
|
||||||
// _thread.Interrupt ();
|
|
||||||
_threadParameters.TellThreadToExit();
|
|
||||||
if (_thread.Join(7000))
|
|
||||||
Debug.LogFormat("Stopped background thread in {0} ms", sw.Elapsed.TotalMilliseconds);
|
|
||||||
else
|
|
||||||
Debug.LogError("Failed to stop background thread");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RegisterJob<T>(Job<T> job)
|
|
||||||
{
|
|
||||||
// note: this function can be called from any thread
|
|
||||||
|
|
||||||
if (null == job.action)
|
|
||||||
throw new ArgumentException("Job must have an action");
|
|
||||||
|
|
||||||
if (0f == job.priority)
|
|
||||||
throw new ArgumentException("You forgot to assign job priority");
|
|
||||||
|
|
||||||
job.exception = null;
|
|
||||||
|
|
||||||
var j = new Job<object>()
|
|
||||||
{
|
|
||||||
priority = job.priority,
|
|
||||||
action = () => job.action(),
|
|
||||||
callbackError = job.callbackError,
|
|
||||||
};
|
|
||||||
if (job.callbackSuccess != null)
|
|
||||||
j.callbackSuccess = (arg) => job.callbackSuccess((T)arg);
|
|
||||||
if (job.callbackFinish != null)
|
|
||||||
j.callbackFinish = (arg) => job.callbackFinish((T)arg);
|
|
||||||
|
|
||||||
lock (_lastJobIdLockObject)
|
|
||||||
// make sure that changing id and adding new job is atomic operation, otherwise
|
|
||||||
// multiple threads accessing this part of code can cause the jobs to be inserted out of order
|
|
||||||
{
|
|
||||||
j.id = ++_lastJobId;
|
|
||||||
_threadParameters.jobs.Add(j);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdateJobs(ushort maxTimeToUpdateMs)
|
|
||||||
{
|
|
||||||
ThreadHelper.ThrowIfNotOnMainThread();
|
|
||||||
|
|
||||||
this.UpdateJobsInternal(maxTimeToUpdateMs);
|
|
||||||
}
|
|
||||||
|
|
||||||
void UpdateJobsInternal(ushort maxTimeToUpdateMs)
|
|
||||||
{
|
|
||||||
|
|
||||||
// get all processed jobs
|
|
||||||
|
|
||||||
_stopwatch.Restart();
|
|
||||||
|
|
||||||
Job<object> job;
|
|
||||||
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
if (maxTimeToUpdateMs != 0 && _stopwatch.ElapsedMilliseconds >= maxTimeToUpdateMs)
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (_processedJobsBuffer.Count > 0)
|
|
||||||
job = _processedJobsBuffer.Dequeue();
|
|
||||||
else
|
|
||||||
{
|
|
||||||
int numCopied = _threadParameters.processedJobs.DequeueToQueue(_processedJobsBuffer, 256);
|
|
||||||
if (numCopied == 0)
|
|
||||||
break;
|
|
||||||
|
|
||||||
job = _processedJobsBuffer.Dequeue();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (job.exception != null)
|
|
||||||
{
|
|
||||||
// error happened
|
|
||||||
|
|
||||||
if (job.callbackError != null)
|
|
||||||
Utilities.F.RunExceptionSafe(() => job.callbackError(job.exception));
|
|
||||||
|
|
||||||
Debug.LogException(job.exception);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// success
|
|
||||||
if (job.callbackSuccess != null)
|
|
||||||
Utilities.F.RunExceptionSafe(() => job.callbackSuccess(job.result));
|
|
||||||
}
|
|
||||||
|
|
||||||
// invoke finish callback
|
|
||||||
if (job.callbackFinish != null)
|
|
||||||
F.RunExceptionSafe(() => job.callbackFinish(job.result));
|
|
||||||
|
|
||||||
_lastProcessedJobId = job.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public long GetNumJobsPendingApproximately()
|
|
||||||
{
|
|
||||||
ThreadHelper.ThrowIfNotOnMainThread();
|
|
||||||
|
|
||||||
// this is not done in a critical section: calling Count on 2 multithreaded collections
|
|
||||||
return (long)_threadParameters.jobs.Count + (long)_threadParameters.processedJobs.Count + (long)_processedJobsBuffer.Count;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long GetNumPendingJobs()
|
|
||||||
{
|
|
||||||
// this will not work if collections used are not FIFO collections (eg. other than queues)
|
|
||||||
// - this will be the case if job priority is used
|
|
||||||
|
|
||||||
ThreadHelper.ThrowIfNotOnMainThread();
|
|
||||||
|
|
||||||
lock (_lastJobIdLockObject)
|
|
||||||
{
|
|
||||||
if (_lastProcessedJobId > _lastJobId)
|
|
||||||
throw new Exception($"Last processed job id ({_lastProcessedJobId}) is higher than last registered job id ({_lastJobId}). This should not happen.");
|
|
||||||
|
|
||||||
return _lastJobId - _lastProcessedJobId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public long GetLastProcessedJobId()
|
|
||||||
{
|
|
||||||
ThreadHelper.ThrowIfNotOnMainThread();
|
|
||||||
return _lastProcessedJobId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int? GetBackgroundThreadId()
|
|
||||||
{
|
|
||||||
ThreadHelper.ThrowIfNotOnMainThread();
|
|
||||||
return _thread?.ManagedThreadId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int GetProcessedJobsBufferCount()
|
|
||||||
{
|
|
||||||
ThreadHelper.ThrowIfNotOnMainThread();
|
|
||||||
return _processedJobsBuffer.Count;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsBackgroundThreadRunning()
|
|
||||||
{
|
|
||||||
ThreadHelper.ThrowIfNotOnMainThread();
|
|
||||||
|
|
||||||
if (null == _thread)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (_thread.ThreadState != System.Threading.ThreadState.Running)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void EnsureBackgroundThreadStarted()
|
|
||||||
{
|
|
||||||
ThreadHelper.ThrowIfNotOnMainThread();
|
|
||||||
|
|
||||||
this.StartThread();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ThreadFunction(object objectParameter)
|
|
||||||
{
|
|
||||||
ThreadParameters threadParameters = (ThreadParameters)objectParameter;
|
|
||||||
|
|
||||||
while (!threadParameters.ShouldThreadExit())
|
|
||||||
{
|
|
||||||
Job<object> job;
|
|
||||||
if (!threadParameters.jobs.TryTake(out job, 200))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
job.result = job.action();
|
|
||||||
}
|
|
||||||
catch (System.Exception ex)
|
|
||||||
{
|
|
||||||
job.exception = ex;
|
|
||||||
}
|
|
||||||
|
|
||||||
threadParameters.processedJobs.Enqueue(job);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: fdd88c441d4ecba469ce3b23cab23fa3
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
|
@ -1,78 +0,0 @@
|
||||||
|
|
||||||
namespace SanAndreasUnity.Utilities
|
|
||||||
{
|
|
||||||
|
|
||||||
public class CmdLineUtils
|
|
||||||
{
|
|
||||||
|
|
||||||
public static string[] GetCmdLineArgs()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
string[] commandLineArgs = System.Environment.GetCommandLineArgs();
|
|
||||||
if (commandLineArgs != null)
|
|
||||||
return commandLineArgs;
|
|
||||||
}
|
|
||||||
catch (System.Exception) {}
|
|
||||||
|
|
||||||
return new string[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool GetArgument(string argName, ref string argValue)
|
|
||||||
{
|
|
||||||
|
|
||||||
string[] commandLineArgs = GetCmdLineArgs();
|
|
||||||
|
|
||||||
if (commandLineArgs.Length < 2) // first argument is program path
|
|
||||||
return false;
|
|
||||||
|
|
||||||
string search = "-" + argName + ":";
|
|
||||||
var foundArg = System.Array.Find(commandLineArgs, arg => arg.StartsWith(search));
|
|
||||||
if (null == foundArg)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// found specified argument
|
|
||||||
// extract value
|
|
||||||
|
|
||||||
argValue = foundArg.Substring(search.Length);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool GetUshortArgument(string argName, ref ushort argValue)
|
|
||||||
{
|
|
||||||
string str = null;
|
|
||||||
if (GetArgument(argName, ref str))
|
|
||||||
{
|
|
||||||
if (ushort.TryParse(str, out ushort parsedValue))
|
|
||||||
{
|
|
||||||
argValue = parsedValue;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool TryGetUshortArgument(string argName, out ushort argValue)
|
|
||||||
{
|
|
||||||
argValue = 0;
|
|
||||||
string str = null;
|
|
||||||
if (GetArgument(argName, ref str))
|
|
||||||
{
|
|
||||||
if (ushort.TryParse(str, out ushort parsedValue))
|
|
||||||
{
|
|
||||||
argValue = parsedValue;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool HasArgument(string argName)
|
|
||||||
{
|
|
||||||
string str = null;
|
|
||||||
return GetArgument(argName, ref str);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: e31806ef68d937367a8bd9ec86a05f51
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 15250
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
|
@ -1,63 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace SanAndreasUnity.Utilities
|
|
||||||
{
|
|
||||||
public static class CollectionExtensions
|
|
||||||
{
|
|
||||||
public static T RemoveLast<T>(this IList<T> list)
|
|
||||||
{
|
|
||||||
T lastElement = list[list.Count - 1];
|
|
||||||
list.RemoveAt(list.Count - 1);
|
|
||||||
return lastElement;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static T RemoveFirst<T>(this IList<T> list)
|
|
||||||
{
|
|
||||||
T firstElement = list[0];
|
|
||||||
list.RemoveAt(0);
|
|
||||||
return firstElement;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Queue<T> ToQueue<T>(this IEnumerable<T> enumerable)
|
|
||||||
{
|
|
||||||
return new Queue<T>(enumerable);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Queue<T> ToQueueWithCapacity<T>(this IEnumerable<T> enumerable, int capacity)
|
|
||||||
{
|
|
||||||
var queue = new Queue<T>(capacity);
|
|
||||||
foreach (var item in enumerable)
|
|
||||||
queue.Enqueue(item);
|
|
||||||
return queue;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static T[] ToArrayOfLength<T>(this IEnumerable<T> enumerable, int length)
|
|
||||||
{
|
|
||||||
T[] array = new T[length];
|
|
||||||
int i = 0;
|
|
||||||
foreach (var item in enumerable)
|
|
||||||
{
|
|
||||||
array[i] = item;
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
return array;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IEnumerable<T> AppendIf<T>(this IEnumerable<T> enumerable, bool condition, T element)
|
|
||||||
{
|
|
||||||
return condition ? enumerable.Append(element) : enumerable;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void AddMultiple<T>(this ICollection<T> collection, T value, int count)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < count; i++)
|
|
||||||
collection.Add(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void AddMultiple<T>(this ICollection<T> collection, int count)
|
|
||||||
{
|
|
||||||
collection.AddMultiple(default, count);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: d1a08f49d78741c47838f4a11e35ecc5
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
|
@ -1,87 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace SanAndreasUnity.Utilities
|
|
||||||
{
|
|
||||||
public class ConcurrentProducerConsumerSortedSet<T> : IProducerConsumerCollection<T>
|
|
||||||
{
|
|
||||||
private SortedSet<T> _sortedSet;
|
|
||||||
private object _lockObject = new object();
|
|
||||||
|
|
||||||
|
|
||||||
public ConcurrentProducerConsumerSortedSet(IComparer<T> comparer)
|
|
||||||
{
|
|
||||||
_sortedSet = new SortedSet<T>(comparer);
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerator<T> GetEnumerator()
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
IEnumerator IEnumerable.GetEnumerator()
|
|
||||||
{
|
|
||||||
return GetEnumerator();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void CopyTo(Array array, int index)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Count
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
lock (_lockObject)
|
|
||||||
{
|
|
||||||
return _sortedSet.Count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsSynchronized => throw new NotImplementedException();
|
|
||||||
|
|
||||||
public object SyncRoot => throw new NotImplementedException();
|
|
||||||
|
|
||||||
public void CopyTo(T[] array, int index)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public T[] ToArray()
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool TryAdd(T item)
|
|
||||||
{
|
|
||||||
lock (_lockObject)
|
|
||||||
{
|
|
||||||
if (!_sortedSet.Add(item))
|
|
||||||
throw new ArgumentException($"Item with this key already exists: {item}");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool TryTake(out T result)
|
|
||||||
{
|
|
||||||
lock (_lockObject)
|
|
||||||
{
|
|
||||||
if (_sortedSet.Count > 0)
|
|
||||||
{
|
|
||||||
T min = _sortedSet.Min;
|
|
||||||
if (!_sortedSet.Remove(min))
|
|
||||||
throw new Exception("Failed to remove min element");
|
|
||||||
result = min;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
result = default;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: 9c1410970028b404588b4f41a7f34497
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
|
@ -1,79 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace SanAndreasUnity.Utilities
|
|
||||||
{
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Alternative to System.Collections.Concurrent.ConcurrentQueue
|
|
||||||
/// (It's only available in .NET 4.0 and greater)
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// It's a bit slow (as it uses locks), and only provides a small subset of the interface
|
|
||||||
/// Overall, the implementation is intended to be simple & robust
|
|
||||||
/// </remarks>
|
|
||||||
public class ConcurrentQueue<T>
|
|
||||||
{
|
|
||||||
private readonly System.Object _queueLock = new System.Object();
|
|
||||||
private readonly Queue<T> _queue = new Queue<T>();
|
|
||||||
|
|
||||||
public void Enqueue(T item)
|
|
||||||
{
|
|
||||||
lock (_queueLock)
|
|
||||||
{
|
|
||||||
_queue.Enqueue(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool TryDequeue(out T result)
|
|
||||||
{
|
|
||||||
lock (_queueLock)
|
|
||||||
{
|
|
||||||
if (_queue.Count == 0)
|
|
||||||
{
|
|
||||||
result = default(T);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
result = _queue.Dequeue();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public T[] DequeueAll()
|
|
||||||
{
|
|
||||||
lock (_queueLock)
|
|
||||||
{
|
|
||||||
T[] copy = _queue.ToArray();
|
|
||||||
_queue.Clear();
|
|
||||||
return copy;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int DequeueToQueue(Queue<T> collection, int maxNumItems)
|
|
||||||
{
|
|
||||||
lock (_queueLock)
|
|
||||||
{
|
|
||||||
int numAdded = 0;
|
|
||||||
while (_queue.Count > 0 && numAdded < maxNumItems)
|
|
||||||
{
|
|
||||||
collection.Enqueue(_queue.Dequeue());
|
|
||||||
numAdded++;
|
|
||||||
}
|
|
||||||
return numAdded;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Count
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
lock (_queueLock)
|
|
||||||
{
|
|
||||||
return _queue.Count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: 0a08e17e16c677f42a9157199dc3d272
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
|
@ -1,184 +0,0 @@
|
||||||
using Newtonsoft.Json.Linq;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace SanAndreasUnity.Utilities
|
|
||||||
{
|
|
||||||
public static class Config
|
|
||||||
{
|
|
||||||
public const string const_game_dir = "game_dir";
|
|
||||||
|
|
||||||
|
|
||||||
public static string UserConfigFileName => "config.user.json";
|
|
||||||
|
|
||||||
public static string ConfigFilesDirectoryPath
|
|
||||||
{
|
|
||||||
get {
|
|
||||||
#if UNITY_EDITOR || UNITY_STANDALONE
|
|
||||||
return Directory.GetCurrentDirectory ();
|
|
||||||
#else
|
|
||||||
return Application.persistentDataPath;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string UserConfigFilePath => Path.Combine(ConfigFilesDirectoryPath, UserConfigFileName);
|
|
||||||
|
|
||||||
public static string GamePath => GetPath (const_game_dir);
|
|
||||||
|
|
||||||
public static string DataPath
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
#if UNITY_EDITOR
|
|
||||||
return Path.Combine(Directory.GetCurrentDirectory(), "Data");
|
|
||||||
#elif UNITY_STANDALONE
|
|
||||||
return Path.Combine(Application.dataPath, "Data");
|
|
||||||
#else
|
|
||||||
return Path.Combine(Application.persistentDataPath, "Data");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private static bool _loaded = false;
|
|
||||||
|
|
||||||
private static JObject _root = new JObject ();
|
|
||||||
private static JObject _user = new JObject ();
|
|
||||||
|
|
||||||
private static readonly Dictionary<string, string> _substitutions = new Dictionary<string, string> ();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static Config()
|
|
||||||
{
|
|
||||||
Load();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static TVal ConvertVal<TVal>(JToken val)
|
|
||||||
{
|
|
||||||
// note that if you pass string[] as type, it will fail on IL2CPP
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return (TVal)Convert.ChangeType(val, typeof(TVal));
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
return val.ToObject<TVal>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static TVal Get<TVal>(string key)
|
|
||||||
{
|
|
||||||
Load();
|
|
||||||
|
|
||||||
var userVal = _user[key];
|
|
||||||
if (userVal != null)
|
|
||||||
return ConvertVal<TVal>(userVal);
|
|
||||||
|
|
||||||
return ConvertVal<TVal>(_root[key]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string GetString(string key)
|
|
||||||
{
|
|
||||||
return Get<string>(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int GetInt(string key)
|
|
||||||
{
|
|
||||||
return Get<int>(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool GetBool(string key)
|
|
||||||
{
|
|
||||||
return Get<bool>(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string GetSubstitution(string key)
|
|
||||||
{
|
|
||||||
Load();
|
|
||||||
|
|
||||||
if (_substitutions.ContainsKey(key)) return _substitutions[key];
|
|
||||||
|
|
||||||
string subs;
|
|
||||||
if (key == "data_dir")
|
|
||||||
{
|
|
||||||
subs = DataPath;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
subs = ReplaceSubstitutions(GetString(key));
|
|
||||||
}
|
|
||||||
|
|
||||||
_substitutions.Add(key, subs);
|
|
||||||
return subs;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static readonly Regex _regex = new Regex(@"\$\{(?<key>[a-z0-9_]+)\}", RegexOptions.Compiled);
|
|
||||||
|
|
||||||
private static string ReplaceSubstitutions(string value)
|
|
||||||
{
|
|
||||||
Load();
|
|
||||||
return _regex.Replace(value, x => GetSubstitution(x.Groups["key"].Value));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string GetPath(string key)
|
|
||||||
{
|
|
||||||
Load();
|
|
||||||
return ReplaceSubstitutions(GetString(key));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string[] GetPaths(string key)
|
|
||||||
{
|
|
||||||
Load();
|
|
||||||
return Get<JArray>(key)
|
|
||||||
.Select(x => ReplaceSubstitutions((string)x))
|
|
||||||
.ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void SetString (string key, string value)
|
|
||||||
{
|
|
||||||
Load();
|
|
||||||
_user [key] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void Load ()
|
|
||||||
{
|
|
||||||
if (_loaded)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_loaded = true;
|
|
||||||
|
|
||||||
_root = new JObject ();
|
|
||||||
_user = new JObject ();
|
|
||||||
_substitutions.Clear ();
|
|
||||||
|
|
||||||
|
|
||||||
_root = JObject.Parse (Resources.Load<TextAsset>("config").text);
|
|
||||||
|
|
||||||
if (File.Exists (UserConfigFilePath))
|
|
||||||
{
|
|
||||||
_user = JObject.Parse (File.ReadAllText (UserConfigFilePath));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void SaveUserConfig ()
|
|
||||||
{
|
|
||||||
Load();
|
|
||||||
File.WriteAllText (UserConfigFilePath, _user.ToString (Newtonsoft.Json.Formatting.Indented));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void SaveUserConfigSafe ()
|
|
||||||
{
|
|
||||||
Load();
|
|
||||||
F.RunExceptionSafe (() => SaveUserConfig ());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: 25ec4ed6b4fec874abfdd29f60d05484
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 9150
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
|
@ -1,13 +0,0 @@
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace SanAndreasUnity.Utilities
|
|
||||||
{
|
|
||||||
// Note about script execution order: execute this script before others to make sure config is loaded before their Awake() is called.
|
|
||||||
public class ConfigManager : MonoBehaviour
|
|
||||||
{
|
|
||||||
void Awake()
|
|
||||||
{
|
|
||||||
//Config.Load();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: 1f08780b55b9ede4cb0ffd248924ba25
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: -31000
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
|
@ -1,57 +0,0 @@
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace SanAndreasUnity.Utilities
|
|
||||||
{
|
|
||||||
public class CoroutineManager : StartupSingleton<CoroutineManager>
|
|
||||||
{
|
|
||||||
private static CoroutineRunner m_coroutineRunner = new CoroutineRunner();
|
|
||||||
|
|
||||||
|
|
||||||
#if UNITY_EDITOR
|
|
||||||
[UnityEditor.InitializeOnLoadMethod]
|
|
||||||
static void InitOnLoad()
|
|
||||||
{
|
|
||||||
UnityEditor.EditorApplication.update -= EditorUpdate;
|
|
||||||
UnityEditor.EditorApplication.update += EditorUpdate;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void EditorUpdate()
|
|
||||||
{
|
|
||||||
// note: this will also update coroutines in play mode, and also while the Editor is paused.
|
|
||||||
// if coroutines wish to avoid that, we need to add a flag for every coroutine
|
|
||||||
m_coroutineRunner.Update();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void Update()
|
|
||||||
{
|
|
||||||
if (!Application.isEditor)
|
|
||||||
m_coroutineRunner.Update();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static CoroutineInfo Start(IEnumerator coroutine)
|
|
||||||
{
|
|
||||||
return m_coroutineRunner.StartCoroutine(coroutine, null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static CoroutineInfo Start(
|
|
||||||
IEnumerator coroutine,
|
|
||||||
System.Action onFinishSuccess,
|
|
||||||
System.Action<System.Exception> onFinishError)
|
|
||||||
{
|
|
||||||
return m_coroutineRunner.StartCoroutine(coroutine, onFinishSuccess, onFinishError);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Stop(CoroutineInfo coroutineInfo)
|
|
||||||
{
|
|
||||||
m_coroutineRunner.StopCoroutine(coroutineInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool IsRunning(CoroutineInfo coroutineInfo)
|
|
||||||
{
|
|
||||||
return m_coroutineRunner.IsCoroutineRunning(coroutineInfo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: 81bcbcc6c7d0163408eea6ffa3732506
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 20
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
|
@ -1,111 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace SanAndreasUnity.Utilities
|
|
||||||
{
|
|
||||||
public class CoroutineInfo
|
|
||||||
{
|
|
||||||
private static long s_lastId = 0;
|
|
||||||
public long Id { get; } = ++s_lastId;
|
|
||||||
|
|
||||||
public IEnumerator coroutine { get; }
|
|
||||||
public System.Action onFinishSuccess { get; }
|
|
||||||
public System.Action<System.Exception> onFinishError { get; }
|
|
||||||
|
|
||||||
public CoroutineInfo(IEnumerator coroutine, Action onFinishSuccess, Action<Exception> onFinishError)
|
|
||||||
{
|
|
||||||
this.coroutine = coroutine;
|
|
||||||
this.onFinishSuccess = onFinishSuccess;
|
|
||||||
this.onFinishError = onFinishError;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class CoroutineRunner
|
|
||||||
{
|
|
||||||
private List<CoroutineInfo> m_coroutines = new List<CoroutineInfo>();
|
|
||||||
private List<CoroutineInfo> m_newCoroutines = new List<CoroutineInfo>();
|
|
||||||
|
|
||||||
|
|
||||||
public CoroutineInfo StartCoroutine(IEnumerator coroutine, System.Action onFinishSuccess, System.Action<System.Exception> onFinishError)
|
|
||||||
{
|
|
||||||
var coroutineInfo = new CoroutineInfo(coroutine, onFinishSuccess, onFinishError);
|
|
||||||
m_newCoroutines.Add(coroutineInfo);
|
|
||||||
return coroutineInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void StopCoroutine(CoroutineInfo coroutineInfo)
|
|
||||||
{
|
|
||||||
if (null == coroutineInfo)
|
|
||||||
return;
|
|
||||||
|
|
||||||
int index = m_coroutines.IndexOf(coroutineInfo);
|
|
||||||
if (index >= 0)
|
|
||||||
m_coroutines[index] = null;
|
|
||||||
|
|
||||||
m_newCoroutines.Remove(coroutineInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsCoroutineRunning(CoroutineInfo coroutineInfo)
|
|
||||||
{
|
|
||||||
if (null == coroutineInfo)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return m_coroutines.Contains(coroutineInfo) || m_newCoroutines.Contains(coroutineInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Update()
|
|
||||||
{
|
|
||||||
m_coroutines.RemoveAll(c => null == c);
|
|
||||||
|
|
||||||
m_coroutines.AddRange(m_newCoroutines);
|
|
||||||
m_newCoroutines.Clear();
|
|
||||||
|
|
||||||
for (int i = 0; i < m_coroutines.Count; i++)
|
|
||||||
{
|
|
||||||
this.UpdateCoroutine(m_coroutines[i], i);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void UpdateCoroutine(CoroutineInfo coroutine, int coroutineIndex)
|
|
||||||
{
|
|
||||||
bool isFinished = false;
|
|
||||||
bool isSuccess = false;
|
|
||||||
System.Exception failureException = null;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (!coroutine.coroutine.MoveNext())
|
|
||||||
{
|
|
||||||
isFinished = true;
|
|
||||||
isSuccess = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (System.Exception ex)
|
|
||||||
{
|
|
||||||
isFinished = true;
|
|
||||||
isSuccess = false;
|
|
||||||
failureException = ex;
|
|
||||||
Debug.LogException(ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isFinished)
|
|
||||||
{
|
|
||||||
m_coroutines[coroutineIndex] = null;
|
|
||||||
|
|
||||||
if (isSuccess)
|
|
||||||
{
|
|
||||||
if (coroutine.onFinishSuccess != null)
|
|
||||||
F.RunExceptionSafe(coroutine.onFinishSuccess);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (coroutine.onFinishError != null)
|
|
||||||
F.RunExceptionSafe(() => coroutine.onFinishError(failureException));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: 577a4c9d6ba16494f8bea1797e91fb73
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
|
@ -1,112 +0,0 @@
|
||||||
using UnityEngine;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace SanAndreasUnity.Utilities
|
|
||||||
{
|
|
||||||
|
|
||||||
public class CustomInput : MonoBehaviour
|
|
||||||
{
|
|
||||||
public static CustomInput Instance { get; private set; }
|
|
||||||
|
|
||||||
public bool IsActive { get; set; } = false;
|
|
||||||
|
|
||||||
Dictionary<string,float> axes = new Dictionary<string, float>();
|
|
||||||
Dictionary<string,bool> buttons = new Dictionary<string, bool>();
|
|
||||||
Dictionary<string,bool> buttonsDown = new Dictionary<string, bool>();
|
|
||||||
Dictionary<KeyCode,bool> keysDown = new Dictionary<KeyCode, bool>();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public float GetAxis(string name){
|
|
||||||
if (!this.IsActive)
|
|
||||||
return Input.GetAxis(name);
|
|
||||||
float value = 0;
|
|
||||||
if (axes.TryGetValue (name, out value))
|
|
||||||
return value;
|
|
||||||
return Input.GetAxis(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
public float GetAxisRaw(string name){
|
|
||||||
if (!this.IsActive)
|
|
||||||
return Input.GetAxisRaw(name);
|
|
||||||
float value = 0;
|
|
||||||
if (axes.TryGetValue (name, out value))
|
|
||||||
return value;
|
|
||||||
return Input.GetAxisRaw(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetAxis(string name, float value){
|
|
||||||
axes [name] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool GetButton(string name){
|
|
||||||
if (!this.IsActive)
|
|
||||||
return Input.GetButton(name);
|
|
||||||
bool value = false;
|
|
||||||
if (buttons.TryGetValue (name, out value))
|
|
||||||
return value;
|
|
||||||
return Input.GetButton(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool GetButtonNoDefaultInput(string name){
|
|
||||||
if (!this.IsActive)
|
|
||||||
return false;
|
|
||||||
bool value = false;
|
|
||||||
buttons.TryGetValue (name, out value);
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool HasButton(string name){
|
|
||||||
if (!this.IsActive)
|
|
||||||
return false;
|
|
||||||
return buttons.ContainsKey (name);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool GetButtonDown(string name){
|
|
||||||
if (!this.IsActive)
|
|
||||||
return Input.GetButtonDown(name);
|
|
||||||
bool value = false;
|
|
||||||
if (buttonsDown.TryGetValue (name, out value))
|
|
||||||
return value;
|
|
||||||
return Input.GetButtonDown(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetButton(string name, bool pressed){
|
|
||||||
buttons [name] = pressed;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetButtonDown(string name, bool pressed){
|
|
||||||
buttonsDown [name] = pressed;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool GetKeyDown(KeyCode keyCode){
|
|
||||||
if (!this.IsActive)
|
|
||||||
return Input.GetKeyDown(keyCode);
|
|
||||||
bool value = false;
|
|
||||||
if (keysDown.TryGetValue (keyCode, out value))
|
|
||||||
return value;
|
|
||||||
return Input.GetKeyDown(keyCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetKeyDown(KeyCode keyCode, bool pressed)
|
|
||||||
{
|
|
||||||
keysDown [keyCode] = pressed;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ResetAllInput()
|
|
||||||
{
|
|
||||||
axes.Clear();
|
|
||||||
buttons.Clear();
|
|
||||||
buttonsDown.Clear();
|
|
||||||
keysDown.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void Awake()
|
|
||||||
{
|
|
||||||
Instance = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: 15fdcd3faa706b9fd9a440dcd477822b
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 14800
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
|
@ -1,131 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.Events;
|
|
||||||
|
|
||||||
namespace SanAndreasUnity.Utilities
|
|
||||||
{
|
|
||||||
|
|
||||||
public class DamageInfo
|
|
||||||
{
|
|
||||||
public float amount = 0f;
|
|
||||||
public string damageType = null;
|
|
||||||
public Transform raycastHitTransform = null;
|
|
||||||
public Vector3 hitDirection = Vector3.forward;
|
|
||||||
public Vector3 hitPoint = Vector3.zero;
|
|
||||||
public Vector3 hitNormal = Vector3.up;
|
|
||||||
public object attacker = null;
|
|
||||||
public object attackingPlayer = null;
|
|
||||||
public object data = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class DamageType
|
|
||||||
{
|
|
||||||
public static readonly string
|
|
||||||
Bullet = "Bullet",
|
|
||||||
Explosion = "Explosion",
|
|
||||||
Gas = "Gas",
|
|
||||||
Flame = "Flame",
|
|
||||||
Melee = "Melee";
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Damageable : MonoBehaviour
|
|
||||||
{
|
|
||||||
|
|
||||||
[SerializeField] private float m_health = 0f;
|
|
||||||
public float Health { get { return m_health; } set { m_health = value; } }
|
|
||||||
|
|
||||||
[SerializeField] private UnityEvent m_onDamage = new UnityEvent ();
|
|
||||||
public UnityEvent OnDamageEvent => m_onDamage;
|
|
||||||
|
|
||||||
public DamageInfo LastDamageInfo { get; private set; }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public void Damage (DamageInfo info)
|
|
||||||
{
|
|
||||||
this.LastDamageInfo = info;
|
|
||||||
|
|
||||||
F.RunExceptionSafe(() => m_onDamage.Invoke());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void HandleDamageByDefault ()
|
|
||||||
{
|
|
||||||
DamageInfo info = this.LastDamageInfo;
|
|
||||||
|
|
||||||
this.Health -= info.amount;
|
|
||||||
|
|
||||||
if (this.Health <= 0f) {
|
|
||||||
Destroy (this.gameObject);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void InflictDamageToObjectsInArea(
|
|
||||||
Vector3 center,
|
|
||||||
float radius,
|
|
||||||
float damageAmount,
|
|
||||||
AnimationCurve damageOverDistanceCurve,
|
|
||||||
string damageType,
|
|
||||||
object attacker = null,
|
|
||||||
object attackingPlayer = null)
|
|
||||||
{
|
|
||||||
Collider[] overlappingColliders = Physics.OverlapSphere(center, radius);
|
|
||||||
|
|
||||||
var damagables = new Dictionary<Damageable, List<Collider>>();
|
|
||||||
|
|
||||||
foreach (var collider in overlappingColliders)
|
|
||||||
{
|
|
||||||
var damagable = collider.GetComponentInParent<Damageable>();
|
|
||||||
if (damagable != null)
|
|
||||||
{
|
|
||||||
if (damagables.ContainsKey(damagable))
|
|
||||||
{
|
|
||||||
damagables[damagable].Add(collider);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
damagables.Add(damagable, new List<Collider>() { collider });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var pair in damagables)
|
|
||||||
{
|
|
||||||
Damageable damageable = pair.Key;
|
|
||||||
List<Collider> colliders = pair.Value;
|
|
||||||
|
|
||||||
// find closest point from all colliders
|
|
||||||
|
|
||||||
float closestPointDistance = float.MaxValue;
|
|
||||||
|
|
||||||
foreach (var collider in colliders)
|
|
||||||
{
|
|
||||||
Vector3 closestPointOnCollider = collider.ClosestPointOrBoundsCenter(center);
|
|
||||||
float distanceToPointOnCollider = Vector3.Distance(center, closestPointOnCollider);
|
|
||||||
|
|
||||||
if (distanceToPointOnCollider < closestPointDistance)
|
|
||||||
{
|
|
||||||
closestPointDistance = distanceToPointOnCollider;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// apply damage based on closest distance
|
|
||||||
|
|
||||||
float distance = closestPointDistance;
|
|
||||||
float distanceFactor = damageOverDistanceCurve.Evaluate(Mathf.Clamp01(distance / radius));
|
|
||||||
float damageAmountBasedOnDistance = damageAmount * distanceFactor;
|
|
||||||
|
|
||||||
F.RunExceptionSafe(() => damageable.Damage(new DamageInfo
|
|
||||||
{
|
|
||||||
amount = damageAmountBasedOnDistance,
|
|
||||||
damageType = damageType,
|
|
||||||
attacker = attacker,
|
|
||||||
attackingPlayer = attackingPlayer,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: 62374ff6df2c74f9699075492ef7fdb9
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 9200
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
|
@ -1,22 +0,0 @@
|
||||||
using System;
|
|
||||||
using UnityEngine;
|
|
||||||
using Object = UnityEngine.Object;
|
|
||||||
|
|
||||||
namespace SanAndreasUnity.Utilities
|
|
||||||
{
|
|
||||||
public class DestroyWhenInHeadlessMode : MonoBehaviour
|
|
||||||
{
|
|
||||||
public Component[] componentsToDestroy = Array.Empty<Component>();
|
|
||||||
|
|
||||||
private void Start() // use Start() to avoid problems with script execution order
|
|
||||||
{
|
|
||||||
if (F.IsInHeadlessMode)
|
|
||||||
{
|
|
||||||
foreach (var component in this.componentsToDestroy)
|
|
||||||
{
|
|
||||||
Object.Destroy(component);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: 4bb8f0fea23c3fe44828518a6ea9ad15
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 5098
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
|
@ -1,22 +0,0 @@
|
||||||
using System;
|
|
||||||
using UnityEngine;
|
|
||||||
using Object = UnityEngine.Object;
|
|
||||||
|
|
||||||
namespace SanAndreasUnity.Utilities
|
|
||||||
{
|
|
||||||
public class DestroyWhenNotOnServer : MonoBehaviour
|
|
||||||
{
|
|
||||||
public Component[] componentsToDestroy = Array.Empty<Component>();
|
|
||||||
|
|
||||||
private void Awake()
|
|
||||||
{
|
|
||||||
if (!NetUtils.IsServer)
|
|
||||||
{
|
|
||||||
foreach (var component in this.componentsToDestroy)
|
|
||||||
{
|
|
||||||
Object.Destroy(component);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: 6d68c88ae66cbc64c997da0c710b080c
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 5096
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
|
@ -1,30 +0,0 @@
|
||||||
using System.Collections;
|
|
||||||
using System.Linq;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace SanAndreasUnity.Utilities
|
|
||||||
{
|
|
||||||
|
|
||||||
public class DestroyWhenParticleSystemsFinish : MonoBehaviour
|
|
||||||
{
|
|
||||||
|
|
||||||
IEnumerator Start()
|
|
||||||
{
|
|
||||||
var systems = this.GetComponentsInChildren<ParticleSystem>();
|
|
||||||
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
yield return null;
|
|
||||||
|
|
||||||
if (systems.All(s => !s.isPlaying))
|
|
||||||
{
|
|
||||||
Object.Destroy(this.gameObject);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: 09d6d62b56f152e449d502dce1494c75
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 5062
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
|
@ -1,19 +0,0 @@
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace SanAndreasUnity.Utilities
|
|
||||||
{
|
|
||||||
|
|
||||||
public class DontDestroyOnLoad : MonoBehaviour
|
|
||||||
{
|
|
||||||
|
|
||||||
void Awake ()
|
|
||||||
{
|
|
||||||
if (null == this.transform.parent)
|
|
||||||
{
|
|
||||||
DontDestroyOnLoad (this.gameObject);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: 409653cfe36614ceab3cbfbf9ed04fce
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 15150
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
|
@ -1,52 +0,0 @@
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace SanAndreasUnity.Utilities
|
|
||||||
{
|
|
||||||
public class ETAMeasurer
|
|
||||||
{
|
|
||||||
System.Diagnostics.Stopwatch m_stopwatch = System.Diagnostics.Stopwatch.StartNew();
|
|
||||||
float m_lastProgressPerc = 0f;
|
|
||||||
float m_changeInterval = 1f;
|
|
||||||
public string ETA { get; private set; } = "0";
|
|
||||||
|
|
||||||
|
|
||||||
public ETAMeasurer(float changeInterval)
|
|
||||||
{
|
|
||||||
if (float.IsNaN(changeInterval) || changeInterval < 0f)
|
|
||||||
throw new System.ArgumentOutOfRangeException(nameof(changeInterval));
|
|
||||||
|
|
||||||
m_changeInterval = changeInterval;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdateETA(float newProgressPerc)
|
|
||||||
{
|
|
||||||
if (float.IsNaN(newProgressPerc))
|
|
||||||
return;
|
|
||||||
|
|
||||||
newProgressPerc = Mathf.Clamp01(newProgressPerc);
|
|
||||||
|
|
||||||
double elapsedSeconds = m_stopwatch.Elapsed.TotalSeconds;
|
|
||||||
|
|
||||||
if (elapsedSeconds < m_changeInterval)
|
|
||||||
return;
|
|
||||||
|
|
||||||
m_stopwatch.Restart();
|
|
||||||
|
|
||||||
if (m_lastProgressPerc > newProgressPerc) // progress reduced
|
|
||||||
{
|
|
||||||
// don't change current ETA
|
|
||||||
m_lastProgressPerc = newProgressPerc;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
double processedPerc = newProgressPerc - m_lastProgressPerc;
|
|
||||||
double percPerSecond = processedPerc / elapsedSeconds;
|
|
||||||
|
|
||||||
double percLeft = 1.0 - newProgressPerc;
|
|
||||||
double secondsLeft = percLeft / percPerSecond;
|
|
||||||
this.ETA = F.FormatElapsedTime(secondsLeft);
|
|
||||||
|
|
||||||
m_lastProgressPerc = newProgressPerc;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: 2e316e455c8b49c4c87d253c3e3e9d69
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
|
@ -1,38 +0,0 @@
|
||||||
#if UNITY_EDITOR
|
|
||||||
using UnityEditor;
|
|
||||||
using UnityEditor.SceneManagement;
|
|
||||||
#endif
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace SanAndreasUnity.Utilities
|
|
||||||
{
|
|
||||||
public static class EditorUtilityEx
|
|
||||||
{
|
|
||||||
public static void MarkObjectAsDirty(Object obj)
|
|
||||||
{
|
|
||||||
#if UNITY_EDITOR
|
|
||||||
EditorUtility.SetDirty(obj);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool MarkActiveSceneAsDirty()
|
|
||||||
{
|
|
||||||
#if UNITY_EDITOR
|
|
||||||
if (Application.isPlaying) // exception will be thrown if we attempt this in play mode
|
|
||||||
return false;
|
|
||||||
return EditorSceneManager.MarkSceneDirty(EditorSceneManager.GetActiveScene());
|
|
||||||
#else
|
|
||||||
return false;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool IsAsset(Object obj)
|
|
||||||
{
|
|
||||||
#if UNITY_EDITOR
|
|
||||||
return AssetDatabase.Contains(obj);
|
|
||||||
#else
|
|
||||||
return false;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: 81721a1077d644a4f806a84eb4bf3de7
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
|
@ -1,68 +0,0 @@
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace SanAndreasUnity.Utilities
|
|
||||||
{
|
|
||||||
|
|
||||||
public class ExplosionForce : MonoBehaviour
|
|
||||||
{
|
|
||||||
public float explosionForce = 4;
|
|
||||||
public float upwardsModifier = 1f;
|
|
||||||
public float radius = 10f;
|
|
||||||
public float explosionMultiplier = 1f;
|
|
||||||
public LayerMask layerMask;
|
|
||||||
|
|
||||||
|
|
||||||
private IEnumerator Start()
|
|
||||||
{
|
|
||||||
// wait one frame because some objects can be spawned right after the explosion
|
|
||||||
yield return null;
|
|
||||||
|
|
||||||
float multiplier = this.explosionMultiplier;
|
|
||||||
|
|
||||||
float r = radius * multiplier;
|
|
||||||
var cols = Physics.OverlapSphere(this.transform.position, r, layerMask);
|
|
||||||
|
|
||||||
var rigidbodies = new Dictionary<Rigidbody, List<Collider>>();
|
|
||||||
foreach (var col in cols)
|
|
||||||
{
|
|
||||||
if (col.attachedRigidbody != null)
|
|
||||||
{
|
|
||||||
if (rigidbodies.ContainsKey(col.attachedRigidbody))
|
|
||||||
{
|
|
||||||
rigidbodies[col.attachedRigidbody].Add(col);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
rigidbodies.Add(col.attachedRigidbody, new List<Collider>() { col });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var pair in rigidbodies)
|
|
||||||
{
|
|
||||||
Rigidbody rb = pair.Key;
|
|
||||||
var colliders = pair.Value;
|
|
||||||
|
|
||||||
// apply higher force on objects with higher mass
|
|
||||||
float massFactor = Mathf.Pow(rb.mass, 0.95f);
|
|
||||||
|
|
||||||
foreach (var collider in colliders)
|
|
||||||
{
|
|
||||||
Vector3 closestPointOnCollider = collider.ClosestPointOrBoundsCenter(this.transform.position);
|
|
||||||
|
|
||||||
Vector3 diff = closestPointOnCollider - this.transform.position;
|
|
||||||
float distance = diff.magnitude;
|
|
||||||
float distanceFactor = Mathf.Sqrt(1.0f - Mathf.Clamp01(distance / r));
|
|
||||||
|
|
||||||
rb.AddForceAtPosition((diff.normalized * explosionForce + Vector3.up * upwardsModifier) * multiplier * distanceFactor * massFactor / colliders.Count, closestPointOnCollider, ForceMode.Impulse);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: 788838a8a852c8843ac90fb32e785378
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 5060
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: aed867b325463d442b5f5ecdd33932ec
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 7250
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
|
@ -1,22 +0,0 @@
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace SanAndreasUnity.Utilities
|
|
||||||
{
|
|
||||||
public class FaceTowardsCamera : MonoBehaviour
|
|
||||||
{
|
|
||||||
public Transform[] transformsToFace;
|
|
||||||
|
|
||||||
void Update()
|
|
||||||
{
|
|
||||||
var cam = Camera.current;
|
|
||||||
if (cam != null)
|
|
||||||
{
|
|
||||||
Quaternion quaternion = Quaternion.LookRotation(-cam.transform.forward, cam.transform.up);
|
|
||||||
for (int i = 0; i < this.transformsToFace.Length; i++)
|
|
||||||
{
|
|
||||||
this.transformsToFace[i].rotation = quaternion;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: cf07d69a4ea5dbd438aee477f5300420
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 5094
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
|
@ -1,137 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
|
|
||||||
namespace SanAndreasUnity.Utilities
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Represents a subsection of another stream, starting from a certain offset and
|
|
||||||
/// with a given length.
|
|
||||||
/// </summary>
|
|
||||||
public class FrameStream : Stream
|
|
||||||
{
|
|
||||||
private readonly Stream _baseStream;
|
|
||||||
|
|
||||||
private readonly long _offset;
|
|
||||||
private readonly long _length;
|
|
||||||
|
|
||||||
private long _position;
|
|
||||||
|
|
||||||
public override bool CanRead
|
|
||||||
{
|
|
||||||
get { return true; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool CanSeek
|
|
||||||
{
|
|
||||||
get { return true; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool CanWrite
|
|
||||||
{
|
|
||||||
get { return false; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public override long Length
|
|
||||||
{
|
|
||||||
get { return _length; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public override long Position
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return _position;
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
Seek(value, SeekOrigin.Begin);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public long AbsoluteOffset
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
var frameStream = _baseStream as FrameStream;
|
|
||||||
if (frameStream == null) return _offset;
|
|
||||||
return _offset + frameStream.AbsolutePosition;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public long AbsolutePosition
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return _position + AbsoluteOffset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public FrameStream(Stream baseStream, long offset, long length)
|
|
||||||
{
|
|
||||||
_baseStream = baseStream;
|
|
||||||
|
|
||||||
_offset = offset;
|
|
||||||
_length = length;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int Read(byte[] buffer, int offset, int count)
|
|
||||||
{
|
|
||||||
if (_position > _length)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
var basePos = _offset + _position;
|
|
||||||
|
|
||||||
if (_baseStream.Position != basePos)
|
|
||||||
{
|
|
||||||
_baseStream.Seek(basePos, SeekOrigin.Begin);
|
|
||||||
}
|
|
||||||
|
|
||||||
var read = _baseStream.Read(buffer, offset, (int)Math.Min(_length - _position, count));
|
|
||||||
_position += read;
|
|
||||||
|
|
||||||
return read;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override long Seek(long offset, SeekOrigin origin)
|
|
||||||
{
|
|
||||||
switch (origin)
|
|
||||||
{
|
|
||||||
case SeekOrigin.Begin:
|
|
||||||
_position = offset;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SeekOrigin.Current:
|
|
||||||
_position += offset;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SeekOrigin.End:
|
|
||||||
_position = _length - offset;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_position < 0 || _position > _length)
|
|
||||||
{
|
|
||||||
throw new ArgumentOutOfRangeException("offset");
|
|
||||||
}
|
|
||||||
|
|
||||||
return _baseStream.Seek(_position + _offset, SeekOrigin.Begin) - _offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Flush()
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void SetLength(long value)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Write(byte[] buffer, int offset, int count)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: 50e57e1fd74e6034db728443bef0cfef
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 14600
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
|
@ -1,352 +0,0 @@
|
||||||
// http://www.unity3d-france.com/unity/phpBB3/viewtopic.php?f=24&t=5409
|
|
||||||
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
public class GLDebug : MonoBehaviour
|
|
||||||
{
|
|
||||||
private struct Line
|
|
||||||
{
|
|
||||||
public Vector3 start;
|
|
||||||
public Vector3 end;
|
|
||||||
public Color color;
|
|
||||||
public double startTime;
|
|
||||||
public float duration;
|
|
||||||
|
|
||||||
public Line (Vector3 start, Vector3 end, Color color, double startTime, float duration)
|
|
||||||
{
|
|
||||||
this.start = start;
|
|
||||||
this.end = end;
|
|
||||||
this.color = color;
|
|
||||||
this.startTime = startTime;
|
|
||||||
this.duration = duration;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool DurationElapsed (bool drawLine)
|
|
||||||
{
|
|
||||||
if (drawLine)
|
|
||||||
{
|
|
||||||
GL.Color (color);
|
|
||||||
GL.Vertex (start);
|
|
||||||
GL.Vertex (end);
|
|
||||||
}
|
|
||||||
return Time.timeAsDouble - startTime >= duration;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static GLDebug instance;
|
|
||||||
private static Material matZOn;
|
|
||||||
private static Material matZOff;
|
|
||||||
|
|
||||||
public KeyCode toggleKey;
|
|
||||||
public bool displayLines = true;
|
|
||||||
#if UNITY_EDITOR
|
|
||||||
public bool displayGizmos = true;
|
|
||||||
#endif
|
|
||||||
//public ScreenRect rect = new ScreenRect (0, 0, 150, 20);
|
|
||||||
|
|
||||||
private List<Line> linesZOn = new List<Line> ();
|
|
||||||
private List<Line> linesZOff = new List<Line> ();
|
|
||||||
// private float milliseconds;
|
|
||||||
|
|
||||||
public Shader zOnShader;
|
|
||||||
public Shader zOffShader;
|
|
||||||
|
|
||||||
|
|
||||||
void Awake ()
|
|
||||||
{
|
|
||||||
if (instance)
|
|
||||||
{
|
|
||||||
DestroyImmediate (this);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
instance = this;
|
|
||||||
SetMaterial ();
|
|
||||||
|
|
||||||
if (null == this.GetComponent<Camera> ())
|
|
||||||
{
|
|
||||||
Debug.LogError ("There should be camera attached to the same game object");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetMaterial ()
|
|
||||||
{
|
|
||||||
matZOn = new Material(this.zOnShader);
|
|
||||||
matZOn.hideFlags = HideFlags.HideAndDontSave;
|
|
||||||
//matZOn.shader.hideFlags = HideFlags.HideAndDontSave;
|
|
||||||
|
|
||||||
matZOff = new Material(this.zOffShader);
|
|
||||||
matZOff.hideFlags = HideFlags.HideAndDontSave;
|
|
||||||
//matZOff.shader.hideFlags = HideFlags.HideAndDontSave;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Update ()
|
|
||||||
{
|
|
||||||
if (Input.GetKeyDown (toggleKey))
|
|
||||||
displayLines = !displayLines;
|
|
||||||
|
|
||||||
if (!displayLines)
|
|
||||||
{
|
|
||||||
// Stopwatch timer = Stopwatch.StartNew ();
|
|
||||||
|
|
||||||
linesZOn = linesZOn.Where (l => !l.DurationElapsed (false)).ToList ();
|
|
||||||
linesZOff = linesZOff.Where (l => !l.DurationElapsed (false)).ToList ();
|
|
||||||
|
|
||||||
// timer.Stop ();
|
|
||||||
// milliseconds = timer.Elapsed.Ticks / 10000f;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*void OnGUI ()
|
|
||||||
{
|
|
||||||
GUI.Label (rect, "GLDebug : " + milliseconds.ToString ("f") + " ms");
|
|
||||||
}*/
|
|
||||||
|
|
||||||
#if UNITY_EDITOR
|
|
||||||
void OnDrawGizmos ()
|
|
||||||
{
|
|
||||||
if (!displayGizmos || !Application.isPlaying)
|
|
||||||
return;
|
|
||||||
for (int i = 0; i < linesZOn.Count; i++)
|
|
||||||
{
|
|
||||||
Gizmos.color = linesZOn[i].color;
|
|
||||||
Gizmos.DrawLine (linesZOn[i].start, linesZOn[i].end);
|
|
||||||
}
|
|
||||||
for (int i = 0; i < linesZOff.Count; i++)
|
|
||||||
{
|
|
||||||
Gizmos.color = linesZOff[i].color;
|
|
||||||
Gizmos.DrawLine (linesZOff[i].start, linesZOff[i].end);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void OnPostRender ()
|
|
||||||
{
|
|
||||||
if (!displayLines) return;
|
|
||||||
|
|
||||||
// Stopwatch timer = Stopwatch.StartNew ();
|
|
||||||
|
|
||||||
matZOn.SetPass (0);
|
|
||||||
GL.Begin (GL.LINES);
|
|
||||||
linesZOn = linesZOn.Where (l => !l.DurationElapsed (true)).ToList ();
|
|
||||||
GL.End ();
|
|
||||||
|
|
||||||
matZOff.SetPass (0);
|
|
||||||
GL.Begin (GL.LINES);
|
|
||||||
linesZOff = linesZOff.Where (l => !l.DurationElapsed (true)).ToList ();
|
|
||||||
GL.End ();
|
|
||||||
|
|
||||||
// timer.Stop ();
|
|
||||||
// milliseconds = timer.Elapsed.Ticks / 10000f;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void DrawLine (Vector3 start, Vector3 end, Color color, float duration = 0, bool depthTest = false)
|
|
||||||
{
|
|
||||||
if (duration == 0 && !instance.displayLines)
|
|
||||||
return;
|
|
||||||
if (start == end)
|
|
||||||
return;
|
|
||||||
if (depthTest)
|
|
||||||
instance.linesZOn.Add (new Line (start, end, color, Time.timeAsDouble, duration));
|
|
||||||
else
|
|
||||||
instance.linesZOff.Add (new Line (start, end, color, Time.timeAsDouble, duration));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Draw a line from start to end with color for a duration of time and with or without depth testing.
|
|
||||||
/// If duration is 0 then the line is rendered 1 frame.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="start">Point in world space where the line should start.</param>
|
|
||||||
/// <param name="end">Point in world space where the line should end.</param>
|
|
||||||
/// <param name="color">Color of the line.</param>
|
|
||||||
/// <param name="duration">How long the line should be visible for.</param>
|
|
||||||
/// <param name="depthTest">Should the line be obscured by objects closer to the camera ?</param>
|
|
||||||
public static void DrawLine (Vector3 start, Vector3 end, Color? color = null, float duration = 0, bool depthTest = false)
|
|
||||||
{
|
|
||||||
DrawLine (start, end, color ?? Color.white, duration, depthTest);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Draw a line from start to start + dir with color for a duration of time and with or without depth testing.
|
|
||||||
/// If duration is 0 then the ray is rendered 1 frame.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="start">Point in world space where the ray should start.</param>
|
|
||||||
/// <param name="dir">Direction and length of the ray.</param>
|
|
||||||
/// <param name="color">Color of the ray.</param>
|
|
||||||
/// <param name="duration">How long the ray should be visible for.</param>
|
|
||||||
/// <param name="depthTest">Should the ray be obscured by objects closer to the camera ?</param>
|
|
||||||
public static void DrawRay (Vector3 start, Vector3 dir, Color? color = null, float duration = 0, bool depthTest = false)
|
|
||||||
{
|
|
||||||
if (dir == Vector3.zero)
|
|
||||||
return;
|
|
||||||
DrawLine (start, start + dir, color, duration, depthTest);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Draw an arrow from start to end with color for a duration of time and with or without depth testing.
|
|
||||||
/// If duration is 0 then the arrow is rendered 1 frame.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="start">Point in world space where the arrow should start.</param>
|
|
||||||
/// <param name="end">Point in world space where the arrow should end.</param>
|
|
||||||
/// <param name="arrowHeadLength">Length of the 2 lines of the head.</param>
|
|
||||||
/// <param name="arrowHeadAngle">Angle between the main line and each of the 2 smaller lines of the head.</param>
|
|
||||||
/// <param name="color">Color of the arrow.</param>
|
|
||||||
/// <param name="duration">How long the arrow should be visible for.</param>
|
|
||||||
/// <param name="depthTest">Should the arrow be obscured by objects closer to the camera ?</param>
|
|
||||||
public static void DrawLineArrow (Vector3 start, Vector3 end, float arrowHeadLength = 0.25f, float arrowHeadAngle = 20, Color? color = null, float duration = 0, bool depthTest = false)
|
|
||||||
{
|
|
||||||
DrawArrow (start, end - start, arrowHeadLength, arrowHeadAngle, color, duration, depthTest);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Draw an arrow from start to start + dir with color for a duration of time and with or without depth testing.
|
|
||||||
/// If duration is 0 then the arrow is rendered 1 frame.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="start">Point in world space where the arrow should start.</param>
|
|
||||||
/// <param name="dir">Direction and length of the arrow.</param>
|
|
||||||
/// <param name="arrowHeadLength">Length of the 2 lines of the head.</param>
|
|
||||||
/// <param name="arrowHeadAngle">Angle between the main line and each of the 2 smaller lines of the head.</param>
|
|
||||||
/// <param name="color">Color of the arrow.</param>
|
|
||||||
/// <param name="duration">How long the arrow should be visible for.</param>
|
|
||||||
/// <param name="depthTest">Should the arrow be obscured by objects closer to the camera ?</param>
|
|
||||||
public static void DrawArrow (Vector3 start, Vector3 dir, float arrowHeadLength = 0.25f, float arrowHeadAngle = 20, Color? color = null, float duration = 0, bool depthTest = false)
|
|
||||||
{
|
|
||||||
if (dir == Vector3.zero)
|
|
||||||
return;
|
|
||||||
DrawRay (start, dir, color, duration, depthTest);
|
|
||||||
Vector3 right = Quaternion.LookRotation (dir) * Quaternion.Euler (0, 180 + arrowHeadAngle, 0) * Vector3.forward;
|
|
||||||
Vector3 left = Quaternion.LookRotation (dir) * Quaternion.Euler (0, 180 - arrowHeadAngle, 0) * Vector3.forward;
|
|
||||||
DrawRay (start + dir, right * arrowHeadLength, color, duration, depthTest);
|
|
||||||
DrawRay (start + dir, left * arrowHeadLength, color, duration, depthTest);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Draw a square with color for a duration of time and with or without depth testing.
|
|
||||||
/// If duration is 0 then the square is renderer 1 frame.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="pos">Center of the square in world space.</param>
|
|
||||||
/// <param name="rot">Rotation of the square in euler angles in world space.</param>
|
|
||||||
/// <param name="scale">Size of the square.</param>
|
|
||||||
/// <param name="color">Color of the square.</param>
|
|
||||||
/// <param name="duration">How long the square should be visible for.</param>
|
|
||||||
/// <param name="depthTest">Should the square be obscured by objects closer to the camera ?</param>
|
|
||||||
public static void DrawSquare (Vector3 pos, Vector3? rot = null, Vector3? scale = null, Color? color = null, float duration = 0, bool depthTest = false)
|
|
||||||
{
|
|
||||||
DrawSquare (Matrix4x4.TRS (pos, Quaternion.Euler (rot ?? Vector3.zero), scale ?? Vector3.one), color, duration, depthTest);
|
|
||||||
}
|
|
||||||
/// <summary>
|
|
||||||
/// Draw a square with color for a duration of time and with or without depth testing.
|
|
||||||
/// If duration is 0 then the square is renderer 1 frame.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="pos">Center of the square in world space.</param>
|
|
||||||
/// <param name="rot">Rotation of the square in world space.</param>
|
|
||||||
/// <param name="scale">Size of the square.</param>
|
|
||||||
/// <param name="color">Color of the square.</param>
|
|
||||||
/// <param name="duration">How long the square should be visible for.</param>
|
|
||||||
/// <param name="depthTest">Should the square be obscured by objects closer to the camera ?</param>
|
|
||||||
public static void DrawSquare (Vector3 pos, Quaternion? rot = null, Vector3? scale = null, Color? color = null, float duration = 0, bool depthTest = false)
|
|
||||||
{
|
|
||||||
DrawSquare (Matrix4x4.TRS (pos, rot ?? Quaternion.identity, scale ?? Vector3.one), color, duration, depthTest);
|
|
||||||
}
|
|
||||||
/// <summary>
|
|
||||||
/// Draw a square with color for a duration of time and with or without depth testing.
|
|
||||||
/// If duration is 0 then the square is renderer 1 frame.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="matrix">Transformation matrix which represent the square transform.</param>
|
|
||||||
/// <param name="color">Color of the square.</param>
|
|
||||||
/// <param name="duration">How long the square should be visible for.</param>
|
|
||||||
/// <param name="depthTest">Should the square be obscured by objects closer to the camera ?</param>
|
|
||||||
public static void DrawSquare (Matrix4x4 matrix, Color? color = null, float duration = 0, bool depthTest = false)
|
|
||||||
{
|
|
||||||
Vector3
|
|
||||||
p_1 = matrix.MultiplyPoint3x4 (new Vector3 ( .5f, 0, .5f)),
|
|
||||||
p_2 = matrix.MultiplyPoint3x4 (new Vector3 ( .5f, 0, -.5f)),
|
|
||||||
p_3 = matrix.MultiplyPoint3x4 (new Vector3 (-.5f, 0, -.5f)),
|
|
||||||
p_4 = matrix.MultiplyPoint3x4 (new Vector3 (-.5f, 0, .5f));
|
|
||||||
|
|
||||||
DrawLine (p_1, p_2, color, duration, depthTest);
|
|
||||||
DrawLine (p_2, p_3, color, duration, depthTest);
|
|
||||||
DrawLine (p_3, p_4, color, duration, depthTest);
|
|
||||||
DrawLine (p_4, p_1, color, duration, depthTest);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Draw a cube with color for a duration of time and with or without depth testing.
|
|
||||||
/// If duration is 0 then the square is renderer 1 frame.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="pos">Center of the cube in world space.</param>
|
|
||||||
/// <param name="rot">Rotation of the cube in euler angles in world space.</param>
|
|
||||||
/// <param name="scale">Size of the cube.</param>
|
|
||||||
/// <param name="color">Color of the cube.</param>
|
|
||||||
/// <param name="duration">How long the cube should be visible for.</param>
|
|
||||||
/// <param name="depthTest">Should the cube be obscured by objects closer to the camera ?</param>
|
|
||||||
public static void DrawCube (Vector3 pos, Vector3? rot = null, Vector3? scale = null, Color? color = null, float duration = 0, bool depthTest = false)
|
|
||||||
{
|
|
||||||
DrawCube (Matrix4x4.TRS (pos, Quaternion.Euler (rot ?? Vector3.zero), scale ?? Vector3.one), color, duration, depthTest);
|
|
||||||
}
|
|
||||||
/// <summary>
|
|
||||||
/// Draw a cube with color for a duration of time and with or without depth testing.
|
|
||||||
/// If duration is 0 then the square is renderer 1 frame.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="pos">Center of the cube in world space.</param>
|
|
||||||
/// <param name="rot">Rotation of the cube in world space.</param>
|
|
||||||
/// <param name="scale">Size of the cube.</param>
|
|
||||||
/// <param name="color">Color of the cube.</param>
|
|
||||||
/// <param name="duration">How long the cube should be visible for.</param>
|
|
||||||
/// <param name="depthTest">Should the cube be obscured by objects closer to the camera ?</param>
|
|
||||||
public static void DrawCube (Vector3 pos, Quaternion? rot = null, Vector3? scale = null, Color? color = null, float duration = 0, bool depthTest = false)
|
|
||||||
{
|
|
||||||
DrawCube (Matrix4x4.TRS (pos, rot ?? Quaternion.identity, scale ?? Vector3.one), color, duration, depthTest);
|
|
||||||
}
|
|
||||||
/// <summary>
|
|
||||||
/// Draw a cube with color for a duration of time and with or without depth testing.
|
|
||||||
/// If duration is 0 then the square is renderer 1 frame.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="matrix">Transformation matrix which represent the cube transform.</param>
|
|
||||||
/// <param name="color">Color of the cube.</param>
|
|
||||||
/// <param name="duration">How long the cube should be visible for.</param>
|
|
||||||
/// <param name="depthTest">Should the cube be obscured by objects closer to the camera ?</param>
|
|
||||||
public static void DrawCube (Matrix4x4 matrix, Color? color = null, float duration = 0, bool depthTest = false)
|
|
||||||
{
|
|
||||||
Vector3
|
|
||||||
down_1 = matrix.MultiplyPoint3x4 (new Vector3 ( .5f, -.5f, .5f)),
|
|
||||||
down_2 = matrix.MultiplyPoint3x4 (new Vector3 ( .5f, -.5f, -.5f)),
|
|
||||||
down_3 = matrix.MultiplyPoint3x4 (new Vector3 (-.5f, -.5f, -.5f)),
|
|
||||||
down_4 = matrix.MultiplyPoint3x4 (new Vector3 (-.5f, -.5f, .5f)),
|
|
||||||
up_1 = matrix.MultiplyPoint3x4 (new Vector3 ( .5f, .5f, .5f)),
|
|
||||||
up_2 = matrix.MultiplyPoint3x4 (new Vector3 ( .5f, .5f, -.5f)),
|
|
||||||
up_3 = matrix.MultiplyPoint3x4 (new Vector3 (-.5f, .5f, -.5f)),
|
|
||||||
up_4 = matrix.MultiplyPoint3x4 (new Vector3 (-.5f, .5f, .5f));
|
|
||||||
|
|
||||||
DrawLine (down_1, down_2, color, duration, depthTest);
|
|
||||||
DrawLine (down_2, down_3, color, duration, depthTest);
|
|
||||||
DrawLine (down_3, down_4, color, duration, depthTest);
|
|
||||||
DrawLine (down_4, down_1, color, duration, depthTest);
|
|
||||||
|
|
||||||
DrawLine (down_1, up_1, color, duration, depthTest);
|
|
||||||
DrawLine (down_2, up_2, color, duration, depthTest);
|
|
||||||
DrawLine (down_3, up_3, color, duration, depthTest);
|
|
||||||
DrawLine (down_4, up_4, color, duration, depthTest);
|
|
||||||
|
|
||||||
DrawLine (up_1, up_2, color, duration, depthTest);
|
|
||||||
DrawLine (up_2, up_3, color, duration, depthTest);
|
|
||||||
DrawLine (up_3, up_4, color, duration, depthTest);
|
|
||||||
DrawLine (up_4, up_1, color, duration, depthTest);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// EXTRAS
|
|
||||||
public static void DrawCircle (Vector3 center, float radius, Color? color = null, float duration = 0, bool depthTest = false)
|
|
||||||
{
|
|
||||||
// float degRad = Mathf.PI / 180;
|
|
||||||
for(float theta = 0.0f; theta < (2*Mathf.PI); theta += 0.2f)
|
|
||||||
{
|
|
||||||
Vector3 ci = (new Vector3(Mathf.Cos(theta) * radius + center.x, Mathf.Sin(theta) * radius + center.y, center.z));
|
|
||||||
DrawLine (ci, ci+new Vector3(0,0.02f,0), color, duration, depthTest);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: 27b72409b037b08459c5c96c104ca2fe
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 10750
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
|
@ -1,8 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: dccc1b72c027da84091293c4a668033a
|
|
||||||
folderAsset: yes
|
|
||||||
DefaultImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
|
@ -1,514 +0,0 @@
|
||||||
using UnityEngine;
|
|
||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
/*
|
|
||||||
File browser for selecting files or folders at runtime.
|
|
||||||
*/
|
|
||||||
|
|
||||||
public enum FileBrowserType {
|
|
||||||
File,
|
|
||||||
Directory
|
|
||||||
}
|
|
||||||
|
|
||||||
public class FileBrowser {
|
|
||||||
|
|
||||||
// Called when the user clicks cancel or select
|
|
||||||
public delegate void FinishedCallback(string path);
|
|
||||||
// Defaults to working directory
|
|
||||||
public string CurrentDirectory {
|
|
||||||
get {
|
|
||||||
return m_currentDirectory;
|
|
||||||
}
|
|
||||||
set {
|
|
||||||
SetNewDirectory(value);
|
|
||||||
SwitchDirectoryNow();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
protected string m_currentDirectory;
|
|
||||||
// Optional pattern for filtering selectable files/folders. See:
|
|
||||||
// http://msdn.microsoft.com/en-us/library/wz42302f(v=VS.90).aspx
|
|
||||||
// and
|
|
||||||
// http://msdn.microsoft.com/en-us/library/6ff71z1w(v=VS.90).aspx
|
|
||||||
public string SelectionPattern {
|
|
||||||
get {
|
|
||||||
return m_filePattern;
|
|
||||||
}
|
|
||||||
set {
|
|
||||||
m_filePattern = value;
|
|
||||||
ReadDirectoryContents();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
protected string m_filePattern;
|
|
||||||
|
|
||||||
protected List<(string, string)> m_topPanelEntries = new List<(string, string)>();
|
|
||||||
|
|
||||||
// Optional image for directories
|
|
||||||
public Texture2D DirectoryImage {
|
|
||||||
get {
|
|
||||||
return m_directoryImage;
|
|
||||||
}
|
|
||||||
set {
|
|
||||||
m_directoryImage = value;
|
|
||||||
BuildContent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
protected Texture2D m_directoryImage;
|
|
||||||
|
|
||||||
// Optional image for files
|
|
||||||
public Texture2D FileImage {
|
|
||||||
get {
|
|
||||||
return m_fileImage;
|
|
||||||
}
|
|
||||||
set {
|
|
||||||
m_fileImage = value;
|
|
||||||
BuildContent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
protected Texture2D m_fileImage;
|
|
||||||
|
|
||||||
// Browser type. Defaults to File, but can be set to Folder
|
|
||||||
public FileBrowserType BrowserType {
|
|
||||||
get {
|
|
||||||
return m_browserType;
|
|
||||||
}
|
|
||||||
set {
|
|
||||||
m_browserType = value;
|
|
||||||
ReadDirectoryContents();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
protected FileBrowserType m_browserType;
|
|
||||||
protected string m_newDirectory;
|
|
||||||
protected string[] m_currentDirectoryParts;
|
|
||||||
|
|
||||||
protected string[] m_files;
|
|
||||||
protected GUIContent[] m_filesWithImages;
|
|
||||||
protected int m_selectedFile;
|
|
||||||
|
|
||||||
protected string[] m_nonMatchingFiles;
|
|
||||||
protected GUIContent[] m_nonMatchingFilesWithImages;
|
|
||||||
protected int m_selectedNonMatchingDirectory;
|
|
||||||
|
|
||||||
protected string[] m_directories;
|
|
||||||
protected GUIContent[] m_directoriesWithImages;
|
|
||||||
protected int m_selectedDirectory;
|
|
||||||
|
|
||||||
protected string[] m_nonMatchingDirectories;
|
|
||||||
protected GUIContent[] m_nonMatchingDirectoriesWithImages;
|
|
||||||
|
|
||||||
protected bool m_currentDirectoryMatches;
|
|
||||||
|
|
||||||
protected GUIStyle CentredText {
|
|
||||||
get {
|
|
||||||
if (m_centredText == null) {
|
|
||||||
m_centredText = new GUIStyle(GUI.skin.label);
|
|
||||||
m_centredText.alignment = TextAnchor.MiddleLeft;
|
|
||||||
m_centredText.fixedHeight = this.ButtonStyle.fixedHeight;
|
|
||||||
}
|
|
||||||
return m_centredText;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
protected GUIStyle m_centredText;
|
|
||||||
|
|
||||||
protected GUIStyle m_buttonStyle;
|
|
||||||
protected GUIStyle ButtonStyle {
|
|
||||||
get {
|
|
||||||
if (null == m_buttonStyle) {
|
|
||||||
// m_buttonStyle = new GUIStyle(GUI.skin.button);
|
|
||||||
// m_buttonStyle.fixedHeight = 25;
|
|
||||||
m_buttonStyle = GUI.skin.button;
|
|
||||||
}
|
|
||||||
return m_buttonStyle;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected string m_name;
|
|
||||||
protected Rect m_screenRect;
|
|
||||||
protected GUIStyle m_areaStyle;
|
|
||||||
|
|
||||||
protected Vector2 m_scrollPosition;
|
|
||||||
|
|
||||||
protected FinishedCallback m_callback;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public FileBrowser(Rect screenRect, string name, GUIStyle areaStyle, FinishedCallback callback) {
|
|
||||||
m_name = name;
|
|
||||||
m_screenRect = screenRect;
|
|
||||||
m_areaStyle = areaStyle;
|
|
||||||
m_browserType = FileBrowserType.File;
|
|
||||||
m_callback = callback;
|
|
||||||
SetNewDirectory(Directory.GetCurrentDirectory());
|
|
||||||
SwitchDirectoryNow();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void SetNewDirectory(string directory) {
|
|
||||||
m_newDirectory = directory;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void SwitchDirectoryNow() {
|
|
||||||
if (m_newDirectory == null || m_currentDirectory == m_newDirectory) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
m_currentDirectory = m_newDirectory;
|
|
||||||
m_scrollPosition = Vector2.zero;
|
|
||||||
m_selectedDirectory = m_selectedNonMatchingDirectory = m_selectedFile = -1;
|
|
||||||
ReadDirectoryContents();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void ReadDirectoryContents() {
|
|
||||||
|
|
||||||
// refresh top panel
|
|
||||||
try {
|
|
||||||
m_topPanelEntries.Clear ();
|
|
||||||
m_topPanelEntries.AddRange( GetDirectoriesForTopPanel() );
|
|
||||||
} catch {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_currentDirectory == "/") {
|
|
||||||
m_currentDirectoryParts = new string[] {""};
|
|
||||||
m_currentDirectoryMatches = false;
|
|
||||||
} else {
|
|
||||||
m_currentDirectoryParts = m_currentDirectory.Split(Path.DirectorySeparatorChar);
|
|
||||||
if (SelectionPattern != null) {
|
|
||||||
string[] generation = GetDirectories(
|
|
||||||
Path.GetDirectoryName(m_currentDirectory),
|
|
||||||
SelectionPattern
|
|
||||||
);
|
|
||||||
m_currentDirectoryMatches = Array.IndexOf(generation, m_currentDirectory) >= 0;
|
|
||||||
} else {
|
|
||||||
m_currentDirectoryMatches = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (BrowserType == FileBrowserType.File || SelectionPattern == null) {
|
|
||||||
m_directories = GetDirectories(m_currentDirectory);
|
|
||||||
m_nonMatchingDirectories = new string[0];
|
|
||||||
} else {
|
|
||||||
m_directories = GetDirectories(m_currentDirectory, SelectionPattern);
|
|
||||||
var nonMatchingDirectories = new List<string>();
|
|
||||||
foreach (string directoryPath in GetDirectories(m_currentDirectory)) {
|
|
||||||
if (Array.IndexOf(m_directories, directoryPath) < 0) {
|
|
||||||
nonMatchingDirectories.Add(directoryPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
m_nonMatchingDirectories = nonMatchingDirectories.ToArray();
|
|
||||||
for (int i = 0; i < m_nonMatchingDirectories.Length; ++i) {
|
|
||||||
int lastSeparator = m_nonMatchingDirectories[i].LastIndexOf(Path.DirectorySeparatorChar);
|
|
||||||
m_nonMatchingDirectories[i] = m_nonMatchingDirectories[i].Substring(lastSeparator + 1);
|
|
||||||
}
|
|
||||||
Array.Sort(m_nonMatchingDirectories);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < m_directories.Length; ++i) {
|
|
||||||
m_directories[i] = m_directories[i].Substring(m_directories[i].LastIndexOf(Path.DirectorySeparatorChar) + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (BrowserType == FileBrowserType.Directory || SelectionPattern == null) {
|
|
||||||
m_files = GetFiles(m_currentDirectory);
|
|
||||||
m_nonMatchingFiles = new string[0];
|
|
||||||
} else {
|
|
||||||
m_files = GetFiles(m_currentDirectory, SelectionPattern);
|
|
||||||
var nonMatchingFiles = new List<string>();
|
|
||||||
foreach (string filePath in GetFiles(m_currentDirectory)) {
|
|
||||||
if (Array.IndexOf(m_files, filePath) < 0) {
|
|
||||||
nonMatchingFiles.Add(filePath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
m_nonMatchingFiles = nonMatchingFiles.ToArray();
|
|
||||||
for (int i = 0; i < m_nonMatchingFiles.Length; ++i) {
|
|
||||||
m_nonMatchingFiles[i] = Path.GetFileName(m_nonMatchingFiles[i]);
|
|
||||||
}
|
|
||||||
Array.Sort(m_nonMatchingFiles);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < m_files.Length; ++i) {
|
|
||||||
m_files[i] = Path.GetFileName(m_files[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
Array.Sort(m_files);
|
|
||||||
|
|
||||||
BuildContent();
|
|
||||||
|
|
||||||
m_newDirectory = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
static string[] GetFiles (string path, string searchPattern)
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
return Directory.GetFiles( path, searchPattern );
|
|
||||||
} catch {
|
|
||||||
return new string[0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static string[] GetFiles (string path)
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
return Directory.GetFiles( path );
|
|
||||||
} catch {
|
|
||||||
return new string[0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static string[] GetDirectories (string path, string searchPattern)
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
return Directory.GetDirectories( path, searchPattern );
|
|
||||||
} catch {
|
|
||||||
return new string[0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static string[] GetDirectories (string path)
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
return Directory.GetDirectories( path );
|
|
||||||
} catch {
|
|
||||||
return new string[0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static List<(string, string)> GetDirectoriesForTopPanel()
|
|
||||||
{
|
|
||||||
if (Application.platform == RuntimePlatform.Android)
|
|
||||||
{
|
|
||||||
return new string[]{"/", "/sdcard/", "/storage/", "/storage/sdcard0/", "/storage/sdcard1/", "/storage/emulated/0/"}
|
|
||||||
.Select(s => (s, s))
|
|
||||||
.ToList();
|
|
||||||
}
|
|
||||||
else if (Application.platform == RuntimePlatform.LinuxPlayer || Application.platform == RuntimePlatform.LinuxEditor)
|
|
||||||
{
|
|
||||||
return new string[]{"/", "/home/", "/mnt/", "/media/"}
|
|
||||||
.Select(s => (s, s))
|
|
||||||
.ToList();
|
|
||||||
}
|
|
||||||
else if (Application.platform == RuntimePlatform.WindowsPlayer || Application.platform == RuntimePlatform.WindowsEditor)
|
|
||||||
{
|
|
||||||
var result = new List<(string, string)>();
|
|
||||||
|
|
||||||
result.AddRange(Directory.GetLogicalDrives().Select(s => (s, s)));
|
|
||||||
|
|
||||||
var specialFolders = new[]
|
|
||||||
{
|
|
||||||
Environment.SpecialFolder.UserProfile,
|
|
||||||
Environment.SpecialFolder.DesktopDirectory,
|
|
||||||
Environment.SpecialFolder.MyDocuments,
|
|
||||||
Environment.SpecialFolder.ProgramFiles,
|
|
||||||
Environment.SpecialFolder.ProgramFilesX86,
|
|
||||||
};
|
|
||||||
|
|
||||||
string[] specialFolderNames = new[]
|
|
||||||
{
|
|
||||||
"User",
|
|
||||||
"Desktop",
|
|
||||||
"Documents",
|
|
||||||
"Program Files",
|
|
||||||
"Program Files (x86)",
|
|
||||||
};
|
|
||||||
|
|
||||||
for (int i = 0; i < specialFolders.Length; i++)
|
|
||||||
{
|
|
||||||
string path = string.Empty;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
path = Environment.GetFolderPath(specialFolders[i]);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(path))
|
|
||||||
result.Add((specialFolderNames[i], path));
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return Directory.GetLogicalDrives()
|
|
||||||
.Select(s => (s, s))
|
|
||||||
.ToList();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
protected void BuildContent() {
|
|
||||||
m_directoriesWithImages = new GUIContent[m_directories.Length];
|
|
||||||
for (int i = 0; i < m_directoriesWithImages.Length; ++i) {
|
|
||||||
m_directoriesWithImages[i] = new GUIContent(m_directories[i], DirectoryImage);
|
|
||||||
}
|
|
||||||
m_nonMatchingDirectoriesWithImages = new GUIContent[m_nonMatchingDirectories.Length];
|
|
||||||
for (int i = 0; i < m_nonMatchingDirectoriesWithImages.Length; ++i) {
|
|
||||||
m_nonMatchingDirectoriesWithImages[i] = new GUIContent(m_nonMatchingDirectories[i], DirectoryImage);
|
|
||||||
}
|
|
||||||
m_filesWithImages = new GUIContent[m_files.Length];
|
|
||||||
for (int i = 0; i < m_filesWithImages.Length; ++i) {
|
|
||||||
m_filesWithImages[i] = new GUIContent(m_files[i], FileImage);
|
|
||||||
}
|
|
||||||
m_nonMatchingFilesWithImages = new GUIContent[m_nonMatchingFiles.Length];
|
|
||||||
for (int i = 0; i < m_nonMatchingFilesWithImages.Length; ++i) {
|
|
||||||
m_nonMatchingFilesWithImages[i] = new GUIContent(m_nonMatchingFiles[i], FileImage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnGUI() {
|
|
||||||
|
|
||||||
if (m_areaStyle != null)
|
|
||||||
GUILayout.BeginArea(m_screenRect, m_name, m_areaStyle);
|
|
||||||
else
|
|
||||||
GUILayout.BeginArea(m_screenRect, m_name);
|
|
||||||
|
|
||||||
// display top panel
|
|
||||||
if (m_topPanelEntries.Count > 0) {
|
|
||||||
GUILayout.BeginHorizontal();
|
|
||||||
|
|
||||||
foreach (var entry in m_topPanelEntries) {
|
|
||||||
if (GUILayout.Button (entry.Item1, this.ButtonStyle)) {
|
|
||||||
SetNewDirectory (entry.Item2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
GUILayout.FlexibleSpace();
|
|
||||||
GUILayout.EndHorizontal ();
|
|
||||||
}
|
|
||||||
|
|
||||||
// display directory parts
|
|
||||||
GUILayout.BeginHorizontal();
|
|
||||||
|
|
||||||
for (int parentIndex = 0; parentIndex < m_currentDirectoryParts.Length; ++parentIndex) {
|
|
||||||
if (parentIndex == m_currentDirectoryParts.Length - 1) {
|
|
||||||
GUILayout.Label(m_currentDirectoryParts[parentIndex], CentredText);
|
|
||||||
} else if (GUILayout.Button(m_currentDirectoryParts[parentIndex], this.ButtonStyle)) {
|
|
||||||
string parentDirectoryName = m_currentDirectory;
|
|
||||||
for (int i = m_currentDirectoryParts.Length - 1; i > parentIndex; --i) {
|
|
||||||
parentDirectoryName = Path.GetDirectoryName(parentDirectoryName);
|
|
||||||
}
|
|
||||||
SetNewDirectory(parentDirectoryName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
GUILayout.FlexibleSpace();
|
|
||||||
GUILayout.EndHorizontal();
|
|
||||||
|
|
||||||
m_scrollPosition = GUILayout.BeginScrollView(
|
|
||||||
m_scrollPosition,
|
|
||||||
false,
|
|
||||||
true,
|
|
||||||
GUI.skin.horizontalScrollbar,
|
|
||||||
GUI.skin.verticalScrollbar,
|
|
||||||
GUI.skin.box
|
|
||||||
);
|
|
||||||
m_selectedDirectory = xGUILayout.SelectionList(
|
|
||||||
m_selectedDirectory,
|
|
||||||
m_directoriesWithImages,
|
|
||||||
this.ButtonStyle,
|
|
||||||
DirectoryDoubleClickCallback
|
|
||||||
);
|
|
||||||
if (m_selectedDirectory > -1) {
|
|
||||||
m_selectedFile = m_selectedNonMatchingDirectory = -1;
|
|
||||||
}
|
|
||||||
m_selectedNonMatchingDirectory = xGUILayout.SelectionList(
|
|
||||||
m_selectedNonMatchingDirectory,
|
|
||||||
m_nonMatchingDirectoriesWithImages,
|
|
||||||
this.ButtonStyle,
|
|
||||||
NonMatchingDirectoryDoubleClickCallback
|
|
||||||
);
|
|
||||||
if (m_selectedNonMatchingDirectory > -1) {
|
|
||||||
m_selectedDirectory = m_selectedFile = -1;
|
|
||||||
}
|
|
||||||
GUI.enabled = BrowserType == FileBrowserType.File;
|
|
||||||
m_selectedFile = xGUILayout.SelectionList(
|
|
||||||
m_selectedFile,
|
|
||||||
m_filesWithImages,
|
|
||||||
this.ButtonStyle,
|
|
||||||
FileDoubleClickCallback
|
|
||||||
);
|
|
||||||
GUI.enabled = true;
|
|
||||||
if (m_selectedFile > -1) {
|
|
||||||
m_selectedDirectory = m_selectedNonMatchingDirectory = -1;
|
|
||||||
}
|
|
||||||
GUI.enabled = false;
|
|
||||||
xGUILayout.SelectionList(
|
|
||||||
-1,
|
|
||||||
m_nonMatchingFilesWithImages,
|
|
||||||
this.ButtonStyle
|
|
||||||
);
|
|
||||||
GUI.enabled = true;
|
|
||||||
GUILayout.EndScrollView();
|
|
||||||
|
|
||||||
GUILayout.Space(20);
|
|
||||||
|
|
||||||
GUILayout.BeginHorizontal();
|
|
||||||
GUILayout.FlexibleSpace();
|
|
||||||
|
|
||||||
if (SanAndreasUnity.Utilities.GUIUtils.ButtonWithCalculatedSize("Cancel")) {
|
|
||||||
m_callback(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
GUILayout.Space(5);
|
|
||||||
|
|
||||||
if (BrowserType == FileBrowserType.File) {
|
|
||||||
GUI.enabled = m_selectedFile > -1;
|
|
||||||
} else {
|
|
||||||
if (SelectionPattern == null) {
|
|
||||||
GUI.enabled = true;//m_selectedDirectory > -1;
|
|
||||||
} else {
|
|
||||||
GUI.enabled = m_selectedDirectory > -1 ||
|
|
||||||
(
|
|
||||||
m_currentDirectoryMatches &&
|
|
||||||
m_selectedNonMatchingDirectory == -1 &&
|
|
||||||
m_selectedFile == -1
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
string selectButtonText = BrowserType == FileBrowserType.File ? "Select" : "Select current folder";
|
|
||||||
|
|
||||||
if (SanAndreasUnity.Utilities.GUIUtils.ButtonWithCalculatedSize (selectButtonText)) {
|
|
||||||
if (BrowserType == FileBrowserType.File) {
|
|
||||||
m_callback(Path.Combine(m_currentDirectory, m_files[m_selectedFile]));
|
|
||||||
} else {
|
|
||||||
// if (m_selectedDirectory > -1) {
|
|
||||||
// m_callback(Path.Combine(m_currentDirectory, m_directories[m_selectedDirectory]));
|
|
||||||
// } else {
|
|
||||||
// m_callback(m_currentDirectory);
|
|
||||||
// }
|
|
||||||
m_callback(m_currentDirectory);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
GUI.enabled = true;
|
|
||||||
GUILayout.EndHorizontal();
|
|
||||||
GUILayout.EndArea();
|
|
||||||
|
|
||||||
if (Event.current.type == EventType.Repaint) {
|
|
||||||
SwitchDirectoryNow();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void FileDoubleClickCallback(int i) {
|
|
||||||
if (BrowserType == FileBrowserType.File) {
|
|
||||||
m_callback(Path.Combine(m_currentDirectory, m_files[i]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void DirectoryDoubleClickCallback(int i) {
|
|
||||||
SetNewDirectory(Path.Combine(m_currentDirectory, m_directories[i]));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void NonMatchingDirectoryDoubleClickCallback(int i) {
|
|
||||||
SetNewDirectory(Path.Combine(m_currentDirectory, m_nonMatchingDirectories[i]));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Vector2 GetRecommendedSize()
|
|
||||||
{
|
|
||||||
float width = Mathf.Max (Screen.width * 0.75f, 600);
|
|
||||||
float height = width * 9f / 16f;
|
|
||||||
return new Vector2(width, height);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: 229e43233fee52441b6819dd07765a7d
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 6950
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
|
@ -1,360 +0,0 @@
|
||||||
using UnityEngine;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace SanAndreasUnity.Utilities
|
|
||||||
{
|
|
||||||
public enum ScreenCorner { TopRight, TopLeft, BottomRight, BottomLeft }
|
|
||||||
|
|
||||||
public static class GUIUtils
|
|
||||||
{
|
|
||||||
|
|
||||||
private static GUIStyle styleWithBackground = new GUIStyle ();
|
|
||||||
|
|
||||||
private static GUIStyle s_centeredLabelStyle = null;
|
|
||||||
public static GUIStyle CenteredLabelStyle {
|
|
||||||
get {
|
|
||||||
if (null == s_centeredLabelStyle)
|
|
||||||
s_centeredLabelStyle = new GUIStyle (GUI.skin.label) { alignment = TextAnchor.MiddleCenter };
|
|
||||||
return s_centeredLabelStyle;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Rect ScreenRect { get { return new Rect (0, 0, Screen.width, Screen.height); } }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static Rect GetCornerRect(ScreenCorner corner, Vector2 size, Vector2? padding = null)
|
|
||||||
{
|
|
||||||
return GetCornerRect(corner, size.x, size.y, padding);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Rect GetCornerRect(ScreenCorner corner, float width, float height, Vector2? padding = null)
|
|
||||||
{
|
|
||||||
float padX = 0,
|
|
||||||
padY = 0;
|
|
||||||
|
|
||||||
if (padding != null)
|
|
||||||
{
|
|
||||||
padX = padding.Value.x;
|
|
||||||
padY = padding.Value.y;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (corner)
|
|
||||||
{
|
|
||||||
case ScreenCorner.TopLeft:
|
|
||||||
return new Rect(padX, padY, width, height);
|
|
||||||
|
|
||||||
case ScreenCorner.TopRight:
|
|
||||||
return new Rect(Screen.width - (width + padX), padY, width, height);
|
|
||||||
|
|
||||||
case ScreenCorner.BottomLeft:
|
|
||||||
return new Rect(padX, Screen.height - (height + padY), width, height);
|
|
||||||
|
|
||||||
case ScreenCorner.BottomRight:
|
|
||||||
return new Rect(Screen.width - (width + padX), Screen.height - (height + padY), width, height);
|
|
||||||
}
|
|
||||||
|
|
||||||
return default(Rect);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Rect GetCenteredRect( Vector2 size ) {
|
|
||||||
|
|
||||||
Vector2 pos = new Vector2 (Screen.width * 0.5f, Screen.height * 0.5f);
|
|
||||||
pos -= size * 0.5f;
|
|
||||||
|
|
||||||
return new Rect (pos, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Rect GetCenteredRectPerc( Vector2 sizeInScreenPercentage ) {
|
|
||||||
|
|
||||||
return GetCenteredRect (new Vector2 (Screen.width * sizeInScreenPercentage.x, Screen.height * sizeInScreenPercentage.y));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Vector2 CalcScreenSizeForContent( GUIContent content, GUIStyle style ) {
|
|
||||||
|
|
||||||
return style.CalcScreenSize (style.CalcSize (content));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Vector2 CalcScreenSizeForText( string text, GUIStyle style ) {
|
|
||||||
|
|
||||||
return CalcScreenSizeForContent (new GUIContent (text), style);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool ButtonWithCalculatedSize( string text ) {
|
|
||||||
return ButtonWithCalculatedSize(new GUIContent(text));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool ButtonWithCalculatedSize(string text, float minWidth, float minHeight, GUIStyle style)
|
|
||||||
{
|
|
||||||
return ButtonWithCalculatedSize(new GUIContent(text), minWidth, minHeight, style);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool ButtonWithCalculatedSize(string text, float minWidth, float minHeight)
|
|
||||||
{
|
|
||||||
return ButtonWithCalculatedSize(text, minWidth, minHeight, GUI.skin.button);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool ButtonWithCalculatedSize(GUIContent content)
|
|
||||||
{
|
|
||||||
return ButtonWithCalculatedSize(content, 0f, 0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool ButtonWithCalculatedSize(GUIContent content, float minWidth, float minHeight)
|
|
||||||
{
|
|
||||||
return ButtonWithCalculatedSize(content, minWidth, minHeight, GUI.skin.button);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool ButtonWithCalculatedSize(GUIContent content, float minWidth, float minHeight, GUIStyle style)
|
|
||||||
{
|
|
||||||
Vector2 size = CalcScreenSizeForContent (content, style);
|
|
||||||
|
|
||||||
if (size.x < minWidth)
|
|
||||||
size.x = minWidth;
|
|
||||||
if (size.y < minHeight)
|
|
||||||
size.y = minHeight;
|
|
||||||
|
|
||||||
return GUILayout.Button (content, style, GUILayout.Width (size.x), GUILayout.Height (size.y));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool ButtonWithColor( Rect rect, string text, Color color) {
|
|
||||||
|
|
||||||
var oldColor = GUI.backgroundColor;
|
|
||||||
GUI.backgroundColor = color;
|
|
||||||
|
|
||||||
bool result = GUI.Button (rect, text);
|
|
||||||
|
|
||||||
GUI.backgroundColor = oldColor;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void DrawRect (Rect position, Color color, GUIContent content = null)
|
|
||||||
{
|
|
||||||
var backgroundColor = GUI.backgroundColor;
|
|
||||||
GUI.backgroundColor = color;
|
|
||||||
styleWithBackground.normal.background = Texture2D.whiteTexture;
|
|
||||||
GUI.Box (position, content ?? GUIContent.none, styleWithBackground);
|
|
||||||
GUI.backgroundColor = backgroundColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void DrawBar (Rect rect, float fillPerc, Color fillColor, Color backgroundColor, float borderWidth)
|
|
||||||
{
|
|
||||||
fillPerc = Mathf.Clamp01 (fillPerc);
|
|
||||||
|
|
||||||
Rect fillRect = rect;
|
|
||||||
fillRect.position += Vector2.one * borderWidth;
|
|
||||||
fillRect.size -= Vector2.one * borderWidth * 2;
|
|
||||||
|
|
||||||
// first fill with black - that will be the border
|
|
||||||
GUIUtils.DrawRect( rect, Color.black );
|
|
||||||
|
|
||||||
// fill with background
|
|
||||||
GUIUtils.DrawRect( fillRect, backgroundColor );
|
|
||||||
|
|
||||||
// draw filled part
|
|
||||||
fillRect.width *= fillPerc;
|
|
||||||
GUIUtils.DrawRect( fillRect, fillColor );
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int TabsControl (int currentTabIndex, params string[] tabNames)
|
|
||||||
{
|
|
||||||
return GUILayout.Toolbar (currentTabIndex, tabNames);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Rect GetRectForBarAsBillboard (Vector3 worldPos, float worldWidth, float worldHeight, Camera cam)
|
|
||||||
{
|
|
||||||
|
|
||||||
Vector3 camRight = cam.transform.right;
|
|
||||||
// Vector3 camUp = cam.transform.up;
|
|
||||||
|
|
||||||
// Vector3 upperLeft = worldPos - camRight * worldWidth * 0.5f + camUp * worldHeight * 0.5f;
|
|
||||||
// Vector3 upperRight = upperLeft + camRight * worldWidth;
|
|
||||||
// Vector3 lowerLeft = upperLeft - camUp * worldHeight;
|
|
||||||
// Vector3 lowerRight = lowerLeft + camRight * worldWidth;
|
|
||||||
|
|
||||||
Vector3 leftWorld = worldPos - camRight * worldWidth * 0.5f;
|
|
||||||
Vector3 rightWorld = worldPos + camRight * worldWidth * 0.5f;
|
|
||||||
|
|
||||||
Vector3 leftScreen = cam.WorldToScreenPoint (leftWorld);
|
|
||||||
Vector3 rightScreen = cam.WorldToScreenPoint (rightWorld);
|
|
||||||
|
|
||||||
if (leftScreen.z < 0 || rightScreen.z < 0)
|
|
||||||
return Rect.zero;
|
|
||||||
|
|
||||||
// transform to gui coordinates
|
|
||||||
leftScreen.y = Screen.height - leftScreen.y;
|
|
||||||
rightScreen.y = Screen.height - rightScreen.y;
|
|
||||||
|
|
||||||
float screenWidth = rightScreen.x - leftScreen.x;
|
|
||||||
float screenHeight = screenWidth * worldHeight / worldWidth;
|
|
||||||
|
|
||||||
return new Rect (new Vector2(leftScreen.x, leftScreen.y - screenHeight * 0.5f), new Vector2(screenWidth, screenHeight) );
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void CenteredLabel(Vector2 pos, string text) {
|
|
||||||
|
|
||||||
Vector2 size = CalcScreenSizeForText (text, GUI.skin.label);
|
|
||||||
|
|
||||||
GUI.Label (new Rect (pos - size * 0.5f, size), text);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void DrawHorizontalLine(float height, float spaceBetween, Color color)
|
|
||||||
{
|
|
||||||
GUILayout.Space(spaceBetween);
|
|
||||||
float width = GUILayoutUtility.GetLastRect().width;
|
|
||||||
Rect rect = GUILayoutUtility.GetRect(width, height);
|
|
||||||
GUIUtils.DrawRect(rect, color);
|
|
||||||
GUILayout.Space(spaceBetween);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary> Draws the texture flipped around Y axis. </summary>
|
|
||||||
public static void DrawTextureWithYFlipped(Rect rect, Texture2D tex) {
|
|
||||||
|
|
||||||
var savedMatrix = GUI.matrix;
|
|
||||||
|
|
||||||
GUIUtility.ScaleAroundPivot (new Vector2 (1, -1), rect.center);
|
|
||||||
|
|
||||||
GUI.DrawTexture (rect, tex);
|
|
||||||
|
|
||||||
GUI.matrix = savedMatrix;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Rect DrawItemsInARowPerc (Rect rect, System.Action<Rect, string> drawItem, string[] items, float[] widthPercs ) {
|
|
||||||
|
|
||||||
Rect itemRect = rect;
|
|
||||||
float x = rect.position.x;
|
|
||||||
|
|
||||||
for (int i = 0; i < items.Length; i++) {
|
|
||||||
float width = widthPercs [i] * rect.width;
|
|
||||||
|
|
||||||
itemRect.position = new Vector2 (x, itemRect.position.y);
|
|
||||||
itemRect.width = width;
|
|
||||||
|
|
||||||
drawItem (itemRect, items [i]);
|
|
||||||
|
|
||||||
x += width;
|
|
||||||
}
|
|
||||||
|
|
||||||
rect.position += new Vector2 (x, 0f);
|
|
||||||
rect.width -= x;
|
|
||||||
return rect;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Rect DrawItemsInARow (Rect rect, System.Action<Rect, string> drawItem, string[] items, float[] widths ) {
|
|
||||||
|
|
||||||
float[] widthPercs = new float[widths.Length];
|
|
||||||
for (int i = 0; i < widths.Length; i++) {
|
|
||||||
widthPercs [i] = widths [i] / rect.width;
|
|
||||||
}
|
|
||||||
|
|
||||||
return DrawItemsInARowPerc (rect, drawItem, items, widthPercs);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Rect GetNextRectInARowPerc (Rect rowRect, ref int currentRectIndex, float spacing, params float[] widthPercs) {
|
|
||||||
|
|
||||||
float x = rowRect.position.x;
|
|
||||||
|
|
||||||
for (int i = 0; i < currentRectIndex; i++) {
|
|
||||||
x += widthPercs [i] * rowRect.width;
|
|
||||||
x += spacing;
|
|
||||||
}
|
|
||||||
|
|
||||||
float width = widthPercs [currentRectIndex] * rowRect.width;
|
|
||||||
currentRectIndex++;
|
|
||||||
|
|
||||||
return new Rect( x, rowRect.position.y, width, rowRect.height );
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Rect GetNextRectInARow (Rect rowRect, ref int currentRectIndex, float spacing, params float[] widths) {
|
|
||||||
|
|
||||||
float[] widthPercs = new float[widths.Length];
|
|
||||||
for (int i = 0; i < widths.Length; i++) {
|
|
||||||
widthPercs [i] = widths [i] / rowRect.width;
|
|
||||||
}
|
|
||||||
|
|
||||||
return GetNextRectInARowPerc (rowRect, ref currentRectIndex, spacing, widthPercs);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int DrawPagedViewNumbers (Rect rect, int currentPage, int numPages)
|
|
||||||
{
|
|
||||||
int resultingPage = currentPage;
|
|
||||||
float spacing = 1f;
|
|
||||||
|
|
||||||
var btnRect=rect;
|
|
||||||
btnRect.width = 25f;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* <_x_x_...x_>
|
|
||||||
* suppose we got y pages, then there are y+1 spacing.
|
|
||||||
* totalWidth =y*btnWidth+(y+1)*spacing+2*btnWidth
|
|
||||||
* 1) if totalWidth<= rect.width use < and > , when click result page -- or ++
|
|
||||||
* 2) if totalWidth> rect.width use << and >> , when click add number of max to all,
|
|
||||||
* result page will be max+!
|
|
||||||
*/
|
|
||||||
|
|
||||||
var totalWidth = numPages * btnRect.width + (numPages + 1) * spacing + 2 * btnRect.width;
|
|
||||||
var showNextSign = totalWidth <= rect.width;
|
|
||||||
var maxShow =showNextSign?numPages:Mathf.FloorToInt( numPages/( (totalWidth-2*btnRect.width) /( rect.width-2*btnRect.width)));
|
|
||||||
|
|
||||||
|
|
||||||
if (GUI.Button (btnRect,showNextSign? "<":"<<")) {
|
|
||||||
if (showNextSign)
|
|
||||||
{
|
|
||||||
resultingPage--;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
resultingPage -= maxShow;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
btnRect.position += new Vector2(btnRect.width + spacing, 0f);
|
|
||||||
|
|
||||||
int startBtnIndex = 0;
|
|
||||||
if (maxShow != 0)
|
|
||||||
{
|
|
||||||
if (currentPage % maxShow == 0)
|
|
||||||
{
|
|
||||||
startBtnIndex =((currentPage / maxShow)-1)*maxShow;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
startBtnIndex =currentPage / maxShow*maxShow;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (int i = 0; i < maxShow; i++)
|
|
||||||
{
|
|
||||||
var btnIndex = startBtnIndex + i + 1;
|
|
||||||
|
|
||||||
var style = currentPage == btnIndex ? GUI.skin.box : GUI.skin.button;
|
|
||||||
if (GUI.Button (btnRect, (btnIndex).ToString (), style))
|
|
||||||
resultingPage = btnIndex ;
|
|
||||||
btnRect.position += new Vector2(btnRect.width + spacing, 0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (GUI.Button (btnRect, showNextSign?">":">>")) {
|
|
||||||
if (showNextSign)
|
|
||||||
{
|
|
||||||
resultingPage++;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
resultingPage += maxShow;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
resultingPage = Mathf.Clamp( resultingPage, 1, numPages );
|
|
||||||
|
|
||||||
return resultingPage;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int DrawPagedViewNumbers (Rect rect, int currentPage, int totalNumItems, int numItemsPerPage)
|
|
||||||
{
|
|
||||||
int numPages = Mathf.CeilToInt (totalNumItems / (float) numItemsPerPage);
|
|
||||||
return DrawPagedViewNumbers (rect, currentPage, numPages);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: 54c9105fbede21845adb37571b796cad
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 14850
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
|
@ -1,54 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace SanAndreasUnity.Utilities
|
|
||||||
{
|
|
||||||
public class TextGizmo
|
|
||||||
{
|
|
||||||
private static TextGizmo tg = null;
|
|
||||||
private Dictionary<char, string> texturePathLookup;
|
|
||||||
private Camera editorCamera = null;
|
|
||||||
private const int CHAR_TEXTURE_HEIGHT = 8; // todo: line breaks
|
|
||||||
private const int CHAR_TEXTURE_WIDTH = 6;
|
|
||||||
private const string characters = "abcdefghijklmnopqrstuvwxyz0123456789";
|
|
||||||
|
|
||||||
public static void Init()
|
|
||||||
{
|
|
||||||
tg = new TextGizmo();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* singleton constructor */
|
|
||||||
|
|
||||||
private TextGizmo()
|
|
||||||
{
|
|
||||||
editorCamera = Camera.current;
|
|
||||||
texturePathLookup = new Dictionary<char, string>();
|
|
||||||
for (int c = 0; c < characters.Length; c++)
|
|
||||||
{
|
|
||||||
texturePathLookup.Add(characters[c], "TextGizmo/text_" + characters[c] + ".tif");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* only call this method from a OnGizmos() method */
|
|
||||||
|
|
||||||
public static void Draw(Vector3 position, string text)
|
|
||||||
{
|
|
||||||
if (tg == null) Init();
|
|
||||||
|
|
||||||
string lowerText = text.ToLower();
|
|
||||||
Vector3 screenPoint = tg.editorCamera.WorldToScreenPoint(position);
|
|
||||||
int offset = 20;
|
|
||||||
for (int c = 0; c < lowerText.Length; c++)
|
|
||||||
{
|
|
||||||
if (tg.texturePathLookup.ContainsKey(lowerText[c]))
|
|
||||||
{
|
|
||||||
Vector3 worldPoint = tg.editorCamera.ScreenToWorldPoint(new Vector3(screenPoint.x + offset, screenPoint.y, screenPoint.z));
|
|
||||||
|
|
||||||
Gizmos.DrawIcon(worldPoint, tg.texturePathLookup[lowerText[c]]);
|
|
||||||
|
|
||||||
offset += CHAR_TEXTURE_WIDTH;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: 56e1d137930c73144bb33c1de5196b93
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 9500
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
|
@ -1,84 +0,0 @@
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
public class xGUILayout
|
|
||||||
{
|
|
||||||
public delegate void DoubleClickCallback(int index);
|
|
||||||
|
|
||||||
public static int SelectionList(int selected, GUIContent[] list)
|
|
||||||
{
|
|
||||||
return SelectionList(selected, list, "button", null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int SelectionList(int selected, GUIContent[] list, GUIStyle elementStyle)
|
|
||||||
{
|
|
||||||
return SelectionList(selected, list, elementStyle, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int SelectionList(int selected, GUIContent[] list, DoubleClickCallback callback)
|
|
||||||
{
|
|
||||||
return SelectionList(selected, list, "button", callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int SelectionList(int selected, GUIContent[] list, GUIStyle elementStyle, DoubleClickCallback callback)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < list.Length; ++i)
|
|
||||||
{
|
|
||||||
Rect elementRect = GUILayoutUtility.GetRect(list[i], elementStyle);
|
|
||||||
bool hover = elementRect.Contains(Event.current.mousePosition);
|
|
||||||
if (hover && Event.current.type == EventType.MouseDown && Event.current.clickCount == 1) // added " && Event.current.clickCount == 1"
|
|
||||||
{
|
|
||||||
selected = i;
|
|
||||||
Event.current.Use();
|
|
||||||
}
|
|
||||||
else if (hover && callback != null && Event.current.type == EventType.MouseDown && Event.current.clickCount == 2) //Changed from MouseUp to MouseDown
|
|
||||||
{
|
|
||||||
callback(i);
|
|
||||||
Event.current.Use();
|
|
||||||
}
|
|
||||||
else if (Event.current.type == EventType.Repaint)
|
|
||||||
{
|
|
||||||
elementStyle.Draw(elementRect, list[i], hover, false, i == selected, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return selected;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int SelectionList(int selected, string[] list)
|
|
||||||
{
|
|
||||||
return SelectionList(selected, list, "button", null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int SelectionList(int selected, string[] list, GUIStyle elementStyle)
|
|
||||||
{
|
|
||||||
return SelectionList(selected, list, elementStyle, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int SelectionList(int selected, string[] list, DoubleClickCallback callback)
|
|
||||||
{
|
|
||||||
return SelectionList(selected, list, "button", callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int SelectionList(int selected, string[] list, GUIStyle elementStyle, DoubleClickCallback callback)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < list.Length; ++i)
|
|
||||||
{
|
|
||||||
Rect elementRect = GUILayoutUtility.GetRect(new GUIContent(list[i]), elementStyle);
|
|
||||||
bool hover = elementRect.Contains(Event.current.mousePosition);
|
|
||||||
if (hover && Event.current.type == EventType.MouseDown)
|
|
||||||
{
|
|
||||||
selected = i;
|
|
||||||
Event.current.Use();
|
|
||||||
}
|
|
||||||
else if (hover && callback != null && Event.current.type == EventType.MouseUp && Event.current.clickCount == 2)
|
|
||||||
{
|
|
||||||
callback(i);
|
|
||||||
Event.current.Use();
|
|
||||||
}
|
|
||||||
else if (Event.current.type == EventType.Repaint)
|
|
||||||
{
|
|
||||||
elementStyle.Draw(elementRect, list[i], hover, false, i == selected, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return selected;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: 10222b6cb1ee1324db5061c7649c32e5
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 12700
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
|
@ -1,22 +0,0 @@
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace SanAndreasUnity.Utilities
|
|
||||||
{
|
|
||||||
|
|
||||||
public interface IState {
|
|
||||||
|
|
||||||
void OnBecameActive();
|
|
||||||
void OnBecameInactive();
|
|
||||||
bool RepresentsState(System.Type type); // TODO: should be removed
|
|
||||||
bool RepresentsState<T>() where T : IState; // TODO: should be removed
|
|
||||||
void UpdateState();
|
|
||||||
void LateUpdateState();
|
|
||||||
void FixedUpdateState();
|
|
||||||
|
|
||||||
object ParameterForEnteringState { set; }
|
|
||||||
|
|
||||||
double LastTimeWhenActivated { get; set; }
|
|
||||||
double LastTimeWhenDeactivated { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: f502096bd6a1442d0b73c2884a32feec
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
|
@ -1,39 +0,0 @@
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace SanAndreasUnity.Utilities
|
|
||||||
{
|
|
||||||
public static class MathUtils
|
|
||||||
{
|
|
||||||
public static float DistanceFromPointToLineSegment(Vector3 p, Vector3 v, Vector3 w)
|
|
||||||
{
|
|
||||||
// Return minimum distance between line segment vw and point p
|
|
||||||
float l2 = Vector3.SqrMagnitude(v - w); // i.e. |w-v|^2 - avoid a sqrt
|
|
||||||
if (l2 == 0.0f) return Vector3.Distance(p, v); // v == w case
|
|
||||||
// Consider the line extending the segment, parameterized as v + t (w - v).
|
|
||||||
// We find projection of point p onto the line.
|
|
||||||
// It falls where t = [(p-v) . (w-v)] / |w-v|^2
|
|
||||||
// We clamp t from [0,1] to handle points outside the segment vw.
|
|
||||||
float t = Mathf.Max(0, Mathf.Min(1, Vector3.Dot(p - v, w - v) / l2));
|
|
||||||
Vector3 projection = v + t * (w - v); // Projection falls on the segment
|
|
||||||
return Vector3.Distance(p, projection);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Vector3 MinComponents(Vector3 a, Vector3 b)
|
|
||||||
{
|
|
||||||
return new Vector3(Mathf.Min(a.x, b.x), Mathf.Min(a.y, b.y), Mathf.Min(a.z, b.z));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Vector3 MaxComponents(Vector3 a, Vector3 b)
|
|
||||||
{
|
|
||||||
return new Vector3(Mathf.Max(a.x, b.x), Mathf.Max(a.y, b.y), Mathf.Max(a.z, b.z));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Vector3 NormalizedOrZero(this Vector3 vec)
|
|
||||||
{
|
|
||||||
if (vec == Vector3.zero)
|
|
||||||
return Vector3.zero;
|
|
||||||
|
|
||||||
return vec.normalized;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: 7988e4c48d3669647a4fdf11e186dc5c
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
|
@ -1,352 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.AI;
|
|
||||||
|
|
||||||
namespace SanAndreasUnity.Utilities
|
|
||||||
{
|
|
||||||
public class MovementAgent : MonoBehaviour
|
|
||||||
{
|
|
||||||
public NavMeshAgent NavMeshAgent { get; private set; }
|
|
||||||
|
|
||||||
private double m_lastTimeWhenSearchedForPath = 0f;
|
|
||||||
|
|
||||||
public Vector3? Destination { get; set; } = null;
|
|
||||||
private Vector3? m_lastAssignedDestination = null;
|
|
||||||
private Vector3? m_lastPositionWhenAssignedDestination = null;
|
|
||||||
|
|
||||||
private double m_lastTimeWhenWarped = 0f;
|
|
||||||
private double m_timeWhenSampledOffNavMesh = 0f;
|
|
||||||
|
|
||||||
public float warpSampleDistance = 4.5f;
|
|
||||||
|
|
||||||
public float[] destinationSampleDistances = new float[] { 3f, 8f, 100f };
|
|
||||||
|
|
||||||
public Vector3 DesiredDirection
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (!m_isMovingOffNavMesh)
|
|
||||||
return this.NavMeshAgent.desiredVelocity.NormalizedOrZero();
|
|
||||||
|
|
||||||
// agent is not on nav mesh
|
|
||||||
|
|
||||||
if (!this.Destination.HasValue && !m_sampledPosOffNavMesh.HasValue)
|
|
||||||
return Vector3.zero;
|
|
||||||
|
|
||||||
Vector3 myPosition = this.NavMeshAgent.transform.position;
|
|
||||||
float stoppingDistance = this.StoppingDistance;
|
|
||||||
|
|
||||||
// if we are in range of destination, don't move
|
|
||||||
if (this.Destination.HasValue && Vector3.Distance(this.Destination.Value, myPosition) <= stoppingDistance)
|
|
||||||
return Vector3.zero;
|
|
||||||
|
|
||||||
Vector3 effectiveDestination = m_sampledPosOffNavMesh ?? this.Destination.Value;
|
|
||||||
|
|
||||||
Vector3 diff = effectiveDestination - myPosition;
|
|
||||||
return diff.normalized;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Vector3 DesiredDirectionXZ
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
Vector3 desiredDir = this.DesiredDirection;
|
|
||||||
if (desiredDir.y == 0f)
|
|
||||||
return desiredDir;
|
|
||||||
return desiredDir.WithXAndZ().NormalizedOrZero();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Vector3? CalculatedDestination { get; private set; } = null;
|
|
||||||
|
|
||||||
private bool m_isMovingOffNavMesh = false;
|
|
||||||
private Vector3? m_sampledPosOffNavMesh = null;
|
|
||||||
|
|
||||||
public float StoppingDistance
|
|
||||||
{
|
|
||||||
get => this.NavMeshAgent.stoppingDistance;
|
|
||||||
set => this.NavMeshAgent.stoppingDistance = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void Awake()
|
|
||||||
{
|
|
||||||
this.NavMeshAgent = this.GetComponentOrThrow<NavMeshAgent>();
|
|
||||||
|
|
||||||
this.NavMeshAgent.updatePosition = false;
|
|
||||||
this.NavMeshAgent.updateRotation = false;
|
|
||||||
this.NavMeshAgent.updateUpAxis = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RunUpdate()
|
|
||||||
{
|
|
||||||
this.Update();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Update()
|
|
||||||
{
|
|
||||||
|
|
||||||
NavMeshAgent agent = this.NavMeshAgent;
|
|
||||||
|
|
||||||
if (!agent.enabled)
|
|
||||||
{
|
|
||||||
this.ResetParams();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
double currentTime = Time.timeAsDouble;
|
|
||||||
Vector3 myPosition = agent.transform.position;
|
|
||||||
|
|
||||||
agent.nextPosition = myPosition;
|
|
||||||
|
|
||||||
Vector3 retreivedNextPosition = agent.nextPosition;
|
|
||||||
|
|
||||||
if (currentTime - m_lastTimeWhenWarped > 1f
|
|
||||||
&& (retreivedNextPosition.WithXAndZ() != myPosition.WithXAndZ() || !agent.isOnNavMesh))
|
|
||||||
{
|
|
||||||
m_lastTimeWhenWarped = currentTime;
|
|
||||||
|
|
||||||
// here we sample position to prevent Unity to spam with warning messages saying that agent is
|
|
||||||
// not close to nav mesh
|
|
||||||
if (NavMesh.SamplePosition(myPosition, out var hit, this.warpSampleDistance, agent.areaMask)
|
|
||||||
&& agent.Warp(myPosition))
|
|
||||||
{
|
|
||||||
if (this.Destination.HasValue && agent.isOnNavMesh)
|
|
||||||
{
|
|
||||||
this.SetDestination();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Debug.Log($"warped agent {agent.name} - bWarp {bWarp}, isOnNavMesh {agent.isOnNavMesh}, pos diff {retreivedNextPosition - myPosition}, bSetDestination {bSetDestination}", this);
|
|
||||||
}
|
|
||||||
|
|
||||||
// no need to set velocity, it's automatically set by Agent
|
|
||||||
//this.NavMeshAgent.velocity = this.Velocity;
|
|
||||||
|
|
||||||
// update calculated destination
|
|
||||||
this.CalculatedDestination = agent.hasPath ? agent.destination : (Vector3?)null;
|
|
||||||
|
|
||||||
// if agent is off nav mesh, try to get it back
|
|
||||||
if (!agent.isOnNavMesh)
|
|
||||||
{
|
|
||||||
m_isMovingOffNavMesh = true; // immediately start moving when agent goes off the nav mesh
|
|
||||||
if (currentTime - m_timeWhenSampledOffNavMesh > 2.5f)
|
|
||||||
{
|
|
||||||
// try to sample position on nav mesh where agent could go
|
|
||||||
|
|
||||||
m_timeWhenSampledOffNavMesh = currentTime;
|
|
||||||
m_sampledPosOffNavMesh = null;
|
|
||||||
|
|
||||||
if (NavMesh.SamplePosition(myPosition, out var hit, 150f, agent.areaMask))
|
|
||||||
{
|
|
||||||
m_sampledPosOffNavMesh = hit.position;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Debug.Log($"Tried to sample position off nav mesh - agent {agent.name}, sampled pos {m_sampledPosOffNavMesh}, distance {hit.distance}", this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_isMovingOffNavMesh = false;
|
|
||||||
m_sampledPosOffNavMesh = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.Destination.HasValue)
|
|
||||||
{
|
|
||||||
this.ResetParams();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentTime - m_lastTimeWhenSearchedForPath < 0.4f)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (agent.pathPending)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!agent.isOnNavMesh)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!m_lastAssignedDestination.HasValue)
|
|
||||||
{
|
|
||||||
this.SetDestination();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if target position changed by some delta value (this value should depend on distance to target
|
|
||||||
// - if target is too far away, value should be higher)
|
|
||||||
|
|
||||||
Vector3 diffToTarget = this.Destination.Value - myPosition;
|
|
||||||
float distanceToTarget = diffToTarget.magnitude;
|
|
||||||
Vector3 deltaPos = this.Destination.Value - m_lastAssignedDestination.Value;
|
|
||||||
float deltaPosLength = deltaPos.magnitude;
|
|
||||||
|
|
||||||
// we require 10% change, with 1.5 as min
|
|
||||||
float requiredPosChange = Mathf.Max(distanceToTarget * 0.1f, 1.5f);
|
|
||||||
|
|
||||||
if (deltaPosLength > requiredPosChange)
|
|
||||||
{
|
|
||||||
this.SetDestination();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if angle to target changed by some delta value (eg. 25 degrees)
|
|
||||||
// - this will make the ped turn fast in response to target changing movement direction
|
|
||||||
|
|
||||||
Vector3 lastDiffToTarget = m_lastAssignedDestination.Value - m_lastPositionWhenAssignedDestination.Value;
|
|
||||||
float angleDelta = Vector3.Angle(this.Destination.Value - m_lastPositionWhenAssignedDestination.Value, lastDiffToTarget);
|
|
||||||
if (angleDelta > 25f)
|
|
||||||
{
|
|
||||||
this.SetDestination();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// regularly update path on some higher interval (eg. 5s)
|
|
||||||
// - this interval could also depend on distance to target
|
|
||||||
|
|
||||||
// from 5 to 12, with sqrt function, 150 as max distance
|
|
||||||
float regularUpdateInterval = 5 + 7 * Mathf.Clamp01(Mathf.Sqrt(Mathf.Min(distanceToTarget, 150f) / 150f));
|
|
||||||
|
|
||||||
if (currentTime - m_lastTimeWhenSearchedForPath > regularUpdateInterval
|
|
||||||
&& this.Destination.Value != m_lastAssignedDestination.Value)
|
|
||||||
{
|
|
||||||
this.SetDestination();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// handle cases when destination changes by significant amount, but it's not recognized
|
|
||||||
// by "delta position" method above
|
|
||||||
// - this happens when position delta is too small, but Agent should still do re-path
|
|
||||||
// (for example, he needs to touch the destination object)
|
|
||||||
|
|
||||||
float deltaInPosition = (this.Destination.Value - m_lastAssignedDestination.Value).magnitude;
|
|
||||||
float currentDistance = (m_lastAssignedDestination.Value - myPosition).magnitude;
|
|
||||||
if (deltaInPosition > currentDistance)
|
|
||||||
{
|
|
||||||
Debug.Log($"delta pos higher than current distance - agent {agent.name}, delta {deltaInPosition}, current distance {currentDistance}", this);
|
|
||||||
this.SetDestination();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// handle case caused by bug in NavMesh system - it can not calculate paths that are too
|
|
||||||
// long (or have too many corners). The path returned has status Partial, and it's final
|
|
||||||
// corner is not even close to destination. Not only that it doesn't return full path, but it actually
|
|
||||||
// returns path which contain closest point to destination, which can be totally wrong path.
|
|
||||||
|
|
||||||
// be careful not to mix this case with regular partial paths that happen because destination is really
|
|
||||||
// not reachable. This can actually happen quite often, if destination is, for example, on nearby roof.
|
|
||||||
|
|
||||||
float stoppingDistance = this.StoppingDistance;
|
|
||||||
float distanceToCalculatedDestination = this.CalculatedDestination.HasValue ? Vector3.Distance(this.CalculatedDestination.Value, myPosition) : float.PositiveInfinity;
|
|
||||||
float originalDistanceToCalculatedDestination = this.CalculatedDestination.HasValue ? Vector3.Distance(m_lastPositionWhenAssignedDestination.Value, this.CalculatedDestination.Value) : float.PositiveInfinity;
|
|
||||||
|
|
||||||
if (this.CalculatedDestination.HasValue
|
|
||||||
&& currentTime - m_lastTimeWhenSearchedForPath > 3f // seems like it's not needed, but just in case
|
|
||||||
&& originalDistanceToCalculatedDestination > 50f // this will make a difference between regular partial paths
|
|
||||||
&& originalDistanceToCalculatedDestination > stoppingDistance + 3f // also need to handle case when stopping distance is too large
|
|
||||||
&& (distanceToCalculatedDestination < 4f || distanceToCalculatedDestination <= stoppingDistance) // already stopped or close enough
|
|
||||||
&& agent.pathStatus == NavMeshPathStatus.PathPartial)
|
|
||||||
{
|
|
||||||
Debug.Log($"re-path due to bug in NavMesh system - agent {agent.name}, distanceToCalculatedDestination {distanceToCalculatedDestination}, originalDistanceToCalculatedDestination {originalDistanceToCalculatedDestination}", this);
|
|
||||||
this.SetDestination();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2nd solution for problem above
|
|
||||||
|
|
||||||
float distanceTraveled = (myPosition - m_lastPositionWhenAssignedDestination.Value).magnitude;
|
|
||||||
|
|
||||||
if (currentTime - m_lastTimeWhenSearchedForPath > 3f
|
|
||||||
&& distanceTraveled > 200f
|
|
||||||
&& agent.pathStatus == NavMeshPathStatus.PathPartial)
|
|
||||||
{
|
|
||||||
Debug.Log($"re-path due to bug in NavMesh system #2 - agent {agent.name}, distanceTraveled {distanceTraveled}", this);
|
|
||||||
this.SetDestination();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetDestination()
|
|
||||||
{
|
|
||||||
NavMeshAgent navMeshAgent = this.NavMeshAgent;
|
|
||||||
|
|
||||||
m_lastTimeWhenSearchedForPath = Time.timeAsDouble;
|
|
||||||
m_lastAssignedDestination = this.Destination.Value;
|
|
||||||
m_lastPositionWhenAssignedDestination = navMeshAgent.transform.position;
|
|
||||||
|
|
||||||
// here we need to sample position on navmesh first, because otherwise agent will fail
|
|
||||||
// to calculate path if target position is not on navmesh, and as a result he will be stopped
|
|
||||||
|
|
||||||
// there is a performance problem: if target position is on isolated part of navmesh,
|
|
||||||
// path calculation will take too long because the algorithm tries to go through all
|
|
||||||
// surrounding nodes, and in the meantime agent stays in place
|
|
||||||
|
|
||||||
// that's why we manually calculate path and assign it to agent - in this case, there is no waiting
|
|
||||||
// for path to be calculated asyncly, and agent starts moving immediately. The potential problem
|
|
||||||
// is that CalculatePath() can take 1-2 ms.
|
|
||||||
|
|
||||||
// TODO: performance optimization: this can be done "asyncly": register pathfinding request, and process
|
|
||||||
// requests from all agents in Update() function of some Manager script, with some time limit (eg. 1 ms)
|
|
||||||
|
|
||||||
if (this.SamplePosition(this.Destination.Value, this.destinationSampleDistances, out var hit))
|
|
||||||
{
|
|
||||||
// TODO: re-use NavMeshPath object
|
|
||||||
var navMeshPath = new NavMeshPath();
|
|
||||||
NavMesh.CalculatePath(navMeshAgent.nextPosition, hit.position, navMeshAgent.areaMask, navMeshPath);
|
|
||||||
navMeshAgent.path = navMeshPath;
|
|
||||||
|
|
||||||
this.CalculatedDestination = navMeshAgent.hasPath ? navMeshAgent.destination : (Vector3?)null;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// if position can not be sampled, we stop the agent
|
|
||||||
navMeshAgent.ResetPath();
|
|
||||||
this.CalculatedDestination = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ResetParams()
|
|
||||||
{
|
|
||||||
m_lastAssignedDestination = null;
|
|
||||||
m_lastPositionWhenAssignedDestination = null;
|
|
||||||
this.CalculatedDestination = null;
|
|
||||||
|
|
||||||
if (this.NavMeshAgent.hasPath)
|
|
||||||
this.NavMeshAgent.ResetPath();
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool SamplePosition(Vector3 pos, float[] sampleDistances, out NavMeshHit hit)
|
|
||||||
{
|
|
||||||
int areaMask = this.NavMeshAgent.areaMask;
|
|
||||||
|
|
||||||
for (int i = 0; i < sampleDistances.Length; i++)
|
|
||||||
{
|
|
||||||
if (NavMesh.SamplePosition(pos, out hit, sampleDistances[i], areaMask))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
hit = default;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnDrawGizmosSelected()
|
|
||||||
{
|
|
||||||
if (null == this.NavMeshAgent)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!this.NavMeshAgent.hasPath)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Gizmos.color = Color.Lerp(Color.red, Color.black, 0.5f);
|
|
||||||
|
|
||||||
Vector3[] corners = this.NavMeshAgent.path.corners;
|
|
||||||
for (int i = 1; i < corners.Length; i++)
|
|
||||||
{
|
|
||||||
Gizmos.DrawWireSphere(corners[i], 0.75f);
|
|
||||||
Gizmos.DrawLine(corners[i - 1], corners[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: e5192eaa721a64d4c9e80632f38623ac
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 5118
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
|
@ -1,16 +0,0 @@
|
||||||
|
|
||||||
namespace SanAndreasUnity.Utilities
|
|
||||||
{
|
|
||||||
|
|
||||||
|
|
||||||
public class NetUtils
|
|
||||||
{
|
|
||||||
|
|
||||||
public static System.Func<bool> IsServerImpl = () => false;
|
|
||||||
|
|
||||||
public static bool IsServer => IsServerImpl();
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: 30e28a51325bebb33982b6ac0217e1c1
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 5700
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
|
@ -1,33 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Diagnostics;
|
|
||||||
|
|
||||||
namespace SanAndreasUnity.Utilities
|
|
||||||
{
|
|
||||||
public static class Profiler
|
|
||||||
{
|
|
||||||
private class ProfileFrame : IDisposable
|
|
||||||
{
|
|
||||||
private readonly string _name;
|
|
||||||
private readonly Stopwatch _timer;
|
|
||||||
|
|
||||||
public ProfileFrame(string name)
|
|
||||||
{
|
|
||||||
_name = name;
|
|
||||||
_timer = new Stopwatch();
|
|
||||||
_timer.Start();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
_timer.Stop();
|
|
||||||
|
|
||||||
UnityEngine.Debug.LogFormat("{0}: {1:F2} ms", _name, _timer.Elapsed.TotalMilliseconds);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IDisposable Start(string name)
|
|
||||||
{
|
|
||||||
return new ProfileFrame(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: 272dc48038f695d43acf0833c8a35c22
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 5100
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
|
@ -1,44 +0,0 @@
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace SanAndreasUnity.Utilities
|
|
||||||
{
|
|
||||||
public class PushableByDamage : MonoBehaviour
|
|
||||||
{
|
|
||||||
public float forceMultiplier = 1;
|
|
||||||
private Damageable _damageable;
|
|
||||||
|
|
||||||
|
|
||||||
private void Awake()
|
|
||||||
{
|
|
||||||
_damageable = this.GetComponentOrThrow<Damageable>();
|
|
||||||
_damageable.OnDamageEvent.AddListener(this.OnDamaged);
|
|
||||||
}
|
|
||||||
|
|
||||||
void OnDamaged()
|
|
||||||
{
|
|
||||||
if (!NetUtils.IsServer)
|
|
||||||
return;
|
|
||||||
|
|
||||||
DamageInfo damageInfo = _damageable.LastDamageInfo;
|
|
||||||
|
|
||||||
if (damageInfo.damageType != DamageType.Bullet)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (null == damageInfo.raycastHitTransform)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var c = damageInfo.raycastHitTransform.GetComponent<Collider>();
|
|
||||||
if (null == c)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var rb = c.attachedRigidbody;
|
|
||||||
if (null == rb)
|
|
||||||
return;
|
|
||||||
|
|
||||||
rb.AddForceAtPosition(
|
|
||||||
damageInfo.hitDirection * damageInfo.amount.SqrtOrZero() * this.forceMultiplier,
|
|
||||||
damageInfo.hitPoint,
|
|
||||||
ForceMode.Impulse);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: 4dafeb77b323d57489cc5b15183dc41b
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 5076
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
|
@ -1,16 +0,0 @@
|
||||||
namespace SanAndreasUnity.Utilities
|
|
||||||
{
|
|
||||||
public class Ref<T>
|
|
||||||
{
|
|
||||||
public T value;
|
|
||||||
|
|
||||||
public Ref()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public Ref(T value)
|
|
||||||
{
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: 36af7629553cfad40afab00af2253f88
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
|
@ -1,35 +0,0 @@
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace SanAndreasUnity.Utilities
|
|
||||||
{
|
|
||||||
|
|
||||||
public class Rotator : MonoBehaviour {
|
|
||||||
|
|
||||||
public Vector3 angles = Vector3.zero;
|
|
||||||
public bool changeEulers = false;
|
|
||||||
|
|
||||||
|
|
||||||
void Update () {
|
|
||||||
|
|
||||||
Vector3 delta = this.angles * Time.deltaTime;
|
|
||||||
if (delta.sqrMagnitude < float.Epsilon)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (this.changeEulers) {
|
|
||||||
Vector3 eulers = this.transform.localEulerAngles;
|
|
||||||
eulers += delta;
|
|
||||||
this.transform.localEulerAngles = eulers;
|
|
||||||
} else {
|
|
||||||
|
|
||||||
this.transform.rotation *=
|
|
||||||
Quaternion.AngleAxis (delta.x, Vector3.right)
|
|
||||||
* Quaternion.AngleAxis (delta.y, Vector3.up)
|
|
||||||
* Quaternion.AngleAxis (delta.z, Vector3.forward);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: 42985861e4d9144258d49089df72c083
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 5750
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
|
@ -1,3 +0,0 @@
|
||||||
{
|
|
||||||
"name": "SanAndreasUnity.Utilities"
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: e2bff2fa8d032b63b9d3231c79d6019c
|
|
||||||
AssemblyDefinitionImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
|
@ -1,116 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace SanAndreasUnity.Utilities
|
|
||||||
{
|
|
||||||
public static class SingletonConstants
|
|
||||||
{
|
|
||||||
public static readonly IReadOnlyList<string> nonAllowedMethodNames = new string[]
|
|
||||||
{
|
|
||||||
"Awake",
|
|
||||||
"OnEnable",
|
|
||||||
"OnDisable",
|
|
||||||
"Start",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public class SingletonComponent<T> : MonoBehaviour
|
|
||||||
where T : SingletonComponent<T>
|
|
||||||
{
|
|
||||||
#if !UNITY_EDITOR
|
|
||||||
public static T Singleton { get; private set; }
|
|
||||||
#else
|
|
||||||
private static T s_cachedSingleton;
|
|
||||||
public static T Singleton
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (!F.IsAppInEditMode)
|
|
||||||
{
|
|
||||||
return s_cachedSingleton;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (s_cachedSingleton != null)
|
|
||||||
return s_cachedSingleton;
|
|
||||||
|
|
||||||
T[] objects = FindObjectsOfType<T>();
|
|
||||||
|
|
||||||
if (objects.Length == 0)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
if (objects.Length > 1)
|
|
||||||
throw new Exception($"Found multiple singleton objects of type {typeof(T).Name}. Make sure there is only 1 singleton object created per type.");
|
|
||||||
|
|
||||||
s_cachedSingleton = objects[0];
|
|
||||||
return s_cachedSingleton;
|
|
||||||
}
|
|
||||||
private set
|
|
||||||
{
|
|
||||||
s_cachedSingleton = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
protected SingletonComponent()
|
|
||||||
{
|
|
||||||
Type type = this.GetType();
|
|
||||||
var bindingFlags = System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic;
|
|
||||||
for (int i = 0; i < SingletonConstants.nonAllowedMethodNames.Count; i++)
|
|
||||||
{
|
|
||||||
string methodName = SingletonConstants.nonAllowedMethodNames[i];
|
|
||||||
var methodInfo = type.GetMethod(methodName, bindingFlags);
|
|
||||||
if (methodInfo != null)
|
|
||||||
throw new Exception($"{type.Name} is using non-allowed method {methodName}. Singletons should not have any of following methods: {string.Join(", ", SingletonConstants.nonAllowedMethodNames)}.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Awake()
|
|
||||||
{
|
|
||||||
if (Singleton != null)
|
|
||||||
{
|
|
||||||
throw new Exception($"Awake() method called twice for singleton of type {this.GetType().Name}");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.OnSingletonAwakeValidate();
|
|
||||||
|
|
||||||
Singleton = (T)this;
|
|
||||||
|
|
||||||
this.OnSingletonAwake();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void OnSingletonAwake()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void OnSingletonAwakeValidate()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnDisable()
|
|
||||||
{
|
|
||||||
if (Singleton != this)
|
|
||||||
return;
|
|
||||||
|
|
||||||
this.OnSingletonDisable();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void OnSingletonDisable()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Start()
|
|
||||||
{
|
|
||||||
if (this != Singleton)
|
|
||||||
return;
|
|
||||||
|
|
||||||
this.OnSingletonStart();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void OnSingletonStart()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: 83a159913a6b77f4f939ea2f34da3a65
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
|
@ -1,16 +0,0 @@
|
||||||
using UnityEngine.SceneManagement;
|
|
||||||
|
|
||||||
namespace SanAndreasUnity.Utilities
|
|
||||||
{
|
|
||||||
public class StartupSingleton<T> : SingletonComponent<T>
|
|
||||||
where T : StartupSingleton<T>
|
|
||||||
{
|
|
||||||
protected override void OnSingletonAwakeValidate()
|
|
||||||
{
|
|
||||||
Scene activeScene = SceneManager.GetActiveScene();
|
|
||||||
|
|
||||||
if (!activeScene.IsValid() || activeScene.buildIndex != 0)
|
|
||||||
throw new System.Exception("Startup singleton can only be initialized in startup scene");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: 43e2ad434a43a4a4f8369aee19fed4b4
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
|
@ -1,68 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace SanAndreasUnity.Utilities
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Container for <see cref="IState"/> objects.
|
|
||||||
/// </summary>
|
|
||||||
public class StateContainer<TState>
|
|
||||||
where TState : IState
|
|
||||||
{
|
|
||||||
private readonly List<TState> _states = new List<TState>();
|
|
||||||
public IReadOnlyList<TState> States => _states;
|
|
||||||
|
|
||||||
|
|
||||||
public TState GetState(System.Type type)
|
|
||||||
{
|
|
||||||
return this._states.FirstOrDefault (s => s.GetType ().Equals (type));
|
|
||||||
}
|
|
||||||
|
|
||||||
public T GetState<T>()
|
|
||||||
where T : TState
|
|
||||||
{
|
|
||||||
return (T) this.GetState(typeof(T));
|
|
||||||
}
|
|
||||||
|
|
||||||
public TState GetStateOrLogError(System.Type type)
|
|
||||||
{
|
|
||||||
var state = this.GetState (type);
|
|
||||||
if(null == state)
|
|
||||||
Debug.LogErrorFormat ("Failed to find state of type {0}", type);
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
|
|
||||||
public T GetStateOrLogError<T>()
|
|
||||||
where T : TState
|
|
||||||
{
|
|
||||||
return (T) this.GetStateOrLogError(typeof(T));
|
|
||||||
}
|
|
||||||
|
|
||||||
public T GetStateOrThrow<T>()
|
|
||||||
where T : TState
|
|
||||||
{
|
|
||||||
var state = this.GetState<T>();
|
|
||||||
if (null == state)
|
|
||||||
throw new ArgumentException($"Failed to find state of type {typeof(T).Name}");
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerable<TState> GetStatesThatInherit<TParent>()
|
|
||||||
where TParent : IState
|
|
||||||
{
|
|
||||||
return _states.OfType<TParent>().Cast<TState>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AddState(TState stateToAdd)
|
|
||||||
{
|
|
||||||
_states.Add(stateToAdd);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AddStates(IEnumerable<TState> statesToAdd)
|
|
||||||
{
|
|
||||||
_states.AddRange(statesToAdd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: e69f407670bd6c74cbedc3900bf033a9
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
|
@ -1,61 +0,0 @@
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace SanAndreasUnity.Utilities
|
|
||||||
{
|
|
||||||
|
|
||||||
public class StateMachine {
|
|
||||||
|
|
||||||
IState m_currentState;
|
|
||||||
public IState CurrentState { get { return m_currentState; } }
|
|
||||||
bool m_isSwitchingState = false;
|
|
||||||
public double TimeWhenSwitchedState { get; private set; }
|
|
||||||
public long FrameWhenSwitchedState { get; private set; }
|
|
||||||
|
|
||||||
|
|
||||||
public void SwitchStateWithParameter(IState newState, object parameterForEnteringState) {
|
|
||||||
|
|
||||||
if(m_isSwitchingState)
|
|
||||||
throw new System.Exception("Already switching state");
|
|
||||||
|
|
||||||
if (newState == m_currentState)
|
|
||||||
return;
|
|
||||||
|
|
||||||
m_isSwitchingState = true;
|
|
||||||
|
|
||||||
IState oldState = m_currentState;
|
|
||||||
|
|
||||||
m_currentState = newState;
|
|
||||||
|
|
||||||
|
|
||||||
if (oldState != null)
|
|
||||||
{
|
|
||||||
// need to catch exception here, because otherwise it would freeze the state machine - it would
|
|
||||||
// no longer be possible to switch states, because 'm_isSwitchingState' is true
|
|
||||||
F.RunExceptionSafe(() =>
|
|
||||||
{
|
|
||||||
oldState.LastTimeWhenDeactivated = Time.timeAsDouble;
|
|
||||||
oldState.OnBecameInactive();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
m_isSwitchingState = false;
|
|
||||||
|
|
||||||
this.TimeWhenSwitchedState = Time.timeAsDouble;
|
|
||||||
this.FrameWhenSwitchedState = Time.frameCount;
|
|
||||||
|
|
||||||
if (m_currentState != null)
|
|
||||||
{
|
|
||||||
m_currentState.ParameterForEnteringState = parameterForEnteringState;
|
|
||||||
m_currentState.LastTimeWhenActivated = Time.timeAsDouble;
|
|
||||||
m_currentState.OnBecameActive();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SwitchState(IState newState)
|
|
||||||
{
|
|
||||||
this.SwitchStateWithParameter(newState, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: 87049d4bb02fc4614b36c80bc088bc39
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 11950
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
|
@ -1,58 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace SanAndreasUnity.Utilities
|
|
||||||
{
|
|
||||||
public class Stats
|
|
||||||
{
|
|
||||||
public class Entry
|
|
||||||
{
|
|
||||||
public string category = "";
|
|
||||||
public string text = null;
|
|
||||||
public System.Action<GetStatsContext> getStatsAction = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class GetStatsContext
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// This is where the stats should be stored.
|
|
||||||
/// </summary>
|
|
||||||
public readonly StringBuilder stringBuilder = new StringBuilder();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// If true, stats can be drawn using imGui, for slightly nicer output.
|
|
||||||
/// </summary>
|
|
||||||
public readonly bool isOnGui = false;
|
|
||||||
|
|
||||||
public GetStatsContext()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public GetStatsContext(bool isOnGui)
|
|
||||||
{
|
|
||||||
this.isOnGui = isOnGui;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AppendLine(string text) => this.stringBuilder.AppendLine(text);
|
|
||||||
public void AppendLine() => this.stringBuilder.AppendLine();
|
|
||||||
public void Append(string text) => this.stringBuilder.Append(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
static Dictionary<string, List<Entry>> s_entries = new Dictionary<string, List<Entry>>();
|
|
||||||
public static IEnumerable<KeyValuePair<string, List<Entry>>> Entries => s_entries;
|
|
||||||
public static IEnumerable<string> Categories => s_entries.Select(pair => pair.Key);
|
|
||||||
|
|
||||||
public static UnityEngine.Rect DisplayRect { get; set; }
|
|
||||||
|
|
||||||
|
|
||||||
public static void RegisterStat(Entry entry)
|
|
||||||
{
|
|
||||||
if (s_entries.ContainsKey(entry.category))
|
|
||||||
s_entries[entry.category].Add(entry);
|
|
||||||
else
|
|
||||||
s_entries[entry.category] = new List<Entry>(){entry};
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: 341c06175ec8b97e8a993ba8d30e82d2
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 9250
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
|
@ -1,14 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
namespace SanAndreasUnity.Utilities
|
|
||||||
{
|
|
||||||
public static class ThreadHelper
|
|
||||||
{
|
|
||||||
public static void ThrowIfNotOnMainThread()
|
|
||||||
{
|
|
||||||
if (Thread.CurrentThread.ManagedThreadId != 1)
|
|
||||||
throw new Exception("This can only be executed on the main thread");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: 8ed180a98e16c5a4cbcc059ace2bb0ec
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
|
@ -1,36 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace SanAndreasUnity.Utilities
|
|
||||||
{
|
|
||||||
public static class Tools
|
|
||||||
{
|
|
||||||
public static byte[] ReadBytes(this Stream self, int count)
|
|
||||||
{
|
|
||||||
var data = new byte[count];
|
|
||||||
for (var i = 0; i < count; ++i)
|
|
||||||
{
|
|
||||||
var bt = self.ReadByte();
|
|
||||||
if (bt == -1) throw new EndOfStreamException();
|
|
||||||
|
|
||||||
data[i] = (byte)bt;
|
|
||||||
}
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String ReadString(this BinaryReader reader, int length)
|
|
||||||
{
|
|
||||||
var bytes = reader.ReadBytes(length);
|
|
||||||
return Encoding.UTF8.GetString(bytes).TrimNullChars();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String TrimNullChars(this String str)
|
|
||||||
{
|
|
||||||
for (var i = 0; i < str.Length; ++i) if (str[i] == '\0') return str.Substring(0, i);
|
|
||||||
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: 9068ac64bca552f41a9d126b0ac27cca
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 7950
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
|
@ -1,36 +0,0 @@
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace SanAndreasUnity.Utilities
|
|
||||||
{
|
|
||||||
|
|
||||||
public struct TransformDataStruct
|
|
||||||
{
|
|
||||||
public Vector3 position;
|
|
||||||
public Quaternion rotation;
|
|
||||||
public Vector3 scale;
|
|
||||||
|
|
||||||
public TransformDataStruct(Vector3 position, Quaternion rotation, Vector3 scale)
|
|
||||||
{
|
|
||||||
this.position = position;
|
|
||||||
this.rotation = rotation;
|
|
||||||
this.scale = scale;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TransformDataStruct(Vector3 position, Quaternion rotation) : this(position, rotation, Vector3.one)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public TransformDataStruct(Vector3 position) : this(position, Quaternion.identity)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public TransformDataStruct(Transform tr) : this(tr.position, tr.rotation, tr.lossyScale)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: 5a676040a6b851344acb77794d43843a
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
|
@ -1,8 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: cb808958b3d2238438ca3ce2ecaed75e
|
|
||||||
folderAsset: yes
|
|
||||||
DefaultImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
|
@ -1,92 +0,0 @@
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.UI;
|
|
||||||
using UnityEngine.EventSystems;
|
|
||||||
|
|
||||||
namespace SanAndreasUnity.Utilities
|
|
||||||
{
|
|
||||||
|
|
||||||
public class ArrowsMovementButton : MonoBehaviour, IPointerDownHandler, IPointerUpHandler, IPointerEnterHandler, IPointerExitHandler, IDragHandler
|
|
||||||
{
|
|
||||||
|
|
||||||
public RawImage leftArrow, rightArrow, upArrow, downArrow;
|
|
||||||
|
|
||||||
bool m_isPointerDown = false;
|
|
||||||
public bool IsPointerDown => m_isPointerDown;
|
|
||||||
|
|
||||||
public bool IsPointerInside { get; private set; } = false;
|
|
||||||
|
|
||||||
public Vector2 LastPointerPos { get; private set; } = Vector2.zero;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void OnDisable()
|
|
||||||
{
|
|
||||||
m_isPointerDown = false;
|
|
||||||
this.IsPointerInside = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnPointerDown(PointerEventData pointerEventData)
|
|
||||||
{
|
|
||||||
m_isPointerDown = true;
|
|
||||||
this.LastPointerPos = pointerEventData.position;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnPointerUp(PointerEventData pointerEventData)
|
|
||||||
{
|
|
||||||
m_isPointerDown = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnPointerEnter(PointerEventData pointerEventData)
|
|
||||||
{
|
|
||||||
this.IsPointerInside = true;
|
|
||||||
this.LastPointerPos = pointerEventData.position;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnPointerExit(PointerEventData pointerEventData)
|
|
||||||
{
|
|
||||||
this.IsPointerInside = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnDrag (PointerEventData pointerEventData)
|
|
||||||
{
|
|
||||||
this.LastPointerPos = pointerEventData.position;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public Vector2 GetMovementNonNormalized()
|
|
||||||
{
|
|
||||||
if (!m_isPointerDown || !this.IsPointerInside)
|
|
||||||
return Vector2.zero;
|
|
||||||
Vector2 pointerPos = this.LastPointerPos;
|
|
||||||
Vector2 localPoint = Vector2.zero;
|
|
||||||
if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(this.transform as RectTransform, pointerPos, null, out localPoint))
|
|
||||||
return Vector2.zero;
|
|
||||||
Vector2 diff = localPoint;
|
|
||||||
return diff;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Vector2 GetMovement()
|
|
||||||
{
|
|
||||||
return this.GetMovementNonNormalized().normalized;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Vector2 GetMovementPercentage()
|
|
||||||
{
|
|
||||||
Rect rect = (this.transform as RectTransform).rect;
|
|
||||||
float width = rect.width;
|
|
||||||
float height = rect.height;
|
|
||||||
if (width < float.Epsilon || height < float.Epsilon)
|
|
||||||
return Vector2.zero;
|
|
||||||
|
|
||||||
Vector2 diff = this.GetMovementNonNormalized();
|
|
||||||
float xPerc = diff.x / (width * 0.5f);
|
|
||||||
float yPerc = diff.y / (height * 0.5f);
|
|
||||||
xPerc = Mathf.Clamp(xPerc, -1f, 1f);
|
|
||||||
yPerc = Mathf.Clamp(yPerc, -1f, 1f);
|
|
||||||
return new Vector2(xPerc, yPerc);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: 8c4791292f9958a9fbed806e4ded7cfc
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 10850
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
|
@ -1,126 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace SanAndreasUnity.Utilities
|
|
||||||
{
|
|
||||||
|
|
||||||
public class Bar : MonoBehaviour {
|
|
||||||
|
|
||||||
public Transform Border { get; private set; }
|
|
||||||
public Transform Background { get; private set; }
|
|
||||||
public Transform Fill { get; private set; }
|
|
||||||
|
|
||||||
public Renderer BorderRenderer { get; private set; }
|
|
||||||
public Renderer BackgroundRenderer { get; private set; }
|
|
||||||
public Renderer FillRenderer { get; private set; }
|
|
||||||
|
|
||||||
public Color BorderColor { get { return this.BorderRenderer.material.color; } set { this.BorderRenderer.material.color = value; } }
|
|
||||||
public Color BackgroundColor { get { return this.BackgroundRenderer.material.color; } set { this.BackgroundRenderer.material.color = value; } }
|
|
||||||
public Color FillColor { get { return this.FillRenderer.material.color; } set { this.FillRenderer.material.color = value; } }
|
|
||||||
|
|
||||||
[SerializeField] private Vector3 m_barSize = new Vector3 (1f, 0.2f, 1f);
|
|
||||||
public Vector3 BarSize { get { return m_barSize; } set { m_barSize = value; } }
|
|
||||||
|
|
||||||
[SerializeField] private float m_maxHeightOnScreen = 10f;
|
|
||||||
public float MaxHeightOnScreen { get { return m_maxHeightOnScreen; } set { m_maxHeightOnScreen = value; } }
|
|
||||||
|
|
||||||
public bool faceTowardsCamera = true;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void Awake ()
|
|
||||||
{
|
|
||||||
this.Border = this.transform.Find ("Border");
|
|
||||||
this.Background = this.transform.Find ("Background");
|
|
||||||
this.Fill = this.transform.Find ("Fill");
|
|
||||||
|
|
||||||
this.BorderRenderer = this.Border.GetComponent<Renderer> ();
|
|
||||||
this.BackgroundRenderer = this.Background.GetComponent<Renderer> ();
|
|
||||||
this.FillRenderer = this.Fill.GetComponent<Renderer> ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Update ()
|
|
||||||
{
|
|
||||||
Camera cam = Camera.main;
|
|
||||||
|
|
||||||
// update size
|
|
||||||
this.transform.SetGlobalScale (this.BarSize);
|
|
||||||
|
|
||||||
if (cam)
|
|
||||||
{
|
|
||||||
if (this.faceTowardsCamera)
|
|
||||||
{
|
|
||||||
// make rotation same as camera's rotation
|
|
||||||
this.transform.rotation = cam.transform.rotation;
|
|
||||||
|
|
||||||
if (this.MaxHeightOnScreen > 0)
|
|
||||||
{
|
|
||||||
// limit height on screen
|
|
||||||
|
|
||||||
// get current height on screen
|
|
||||||
|
|
||||||
Vector3 top = this.transform.position + this.transform.up * this.transform.lossyScale.y * 0.5f;
|
|
||||||
Vector3 bottom = this.transform.position - this.transform.up * this.transform.lossyScale.y * 0.5f;
|
|
||||||
|
|
||||||
Vector3 screenTop = cam.WorldToScreenPoint( top );
|
|
||||||
Vector3 screenBottom = cam.WorldToScreenPoint( bottom );
|
|
||||||
|
|
||||||
if (screenTop.z >= 0 && screenBottom.z >= 0)
|
|
||||||
{
|
|
||||||
float heightOnScreen = Mathf.Abs( screenTop.y - screenBottom.y );
|
|
||||||
if (heightOnScreen > this.MaxHeightOnScreen)
|
|
||||||
{
|
|
||||||
// reduce height of bar
|
|
||||||
|
|
||||||
float ratio = this.MaxHeightOnScreen / heightOnScreen;
|
|
||||||
|
|
||||||
Vector3 newSize = this.transform.lossyScale;
|
|
||||||
newSize.y *= ratio;
|
|
||||||
this.transform.SetGlobalScale( newSize );
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetFillPerc (float fillPerc)
|
|
||||||
{
|
|
||||||
fillPerc = Mathf.Clamp01 (fillPerc);
|
|
||||||
|
|
||||||
Vector3 scale = this.Fill.localScale;
|
|
||||||
scale.x = fillPerc;
|
|
||||||
this.Fill.localScale = scale;
|
|
||||||
|
|
||||||
// reposition it
|
|
||||||
Vector3 pos = this.Fill.localPosition;
|
|
||||||
pos.x = - (1.0f - fillPerc) / 2.0f;
|
|
||||||
this.Fill.localPosition = pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
public void SetBorderWidth (float borderWidth)
|
|
||||||
{
|
|
||||||
// borderWidthPerc = Mathf.Clamp (borderWidthPerc, 0f, 0.5f);
|
|
||||||
|
|
||||||
// stretch border to parent
|
|
||||||
this.Border.localScale = Vector3.one;
|
|
||||||
|
|
||||||
// reduce width and height of background and fill objects
|
|
||||||
|
|
||||||
Vector3 size = this.BarSize;
|
|
||||||
size.x -= borderWidth * 2;
|
|
||||||
size.y -= borderWidth * 2;
|
|
||||||
|
|
||||||
this.Background.SetGlobalScale( size );
|
|
||||||
this.Fill.SetGlobalScale( size );
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: c7111f45c86a74947af5f6831ab01122
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 8550
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
|
@ -1,37 +0,0 @@
|
||||||
using System.Collections;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.EventSystems;
|
|
||||||
|
|
||||||
namespace SanAndreasUnity.Utilities
|
|
||||||
{
|
|
||||||
|
|
||||||
public class CustomEventSystemForMixingIMGUIAndNewUI : EventSystem
|
|
||||||
{
|
|
||||||
bool m_wasAnyElementActiveLastFrame = false;
|
|
||||||
|
|
||||||
|
|
||||||
protected override void Start()
|
|
||||||
{
|
|
||||||
base.Start();
|
|
||||||
|
|
||||||
StartCoroutine(this.Coroutine());
|
|
||||||
}
|
|
||||||
|
|
||||||
IEnumerator Coroutine()
|
|
||||||
{
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
yield return null;
|
|
||||||
m_wasAnyElementActiveLastFrame = GUIUtility.hotControl != 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Update()
|
|
||||||
{
|
|
||||||
if (!m_wasAnyElementActiveLastFrame)
|
|
||||||
base.Update();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue