#02.JobRepeatManager - 01
이번 글에 작성할 내용은 개인적으로 유니티를 사용하면서 이런 스크립트가 있으면 편할 것 같은데? 라는 생각에 만들어 본 스크립트이다.
그 이름은 Job Repeat Manager !!!!!!!!!!!
개인적으로 만들어놓고 그래서 요놈이 효율적일까? 괜찮은가? 안전한가?
라는 질문을 수 차례했으나 답은...나도 모르겠당...ㅎㅎ
그래도 올려두면 누군가는 쓰지 않을까? 라는 생각에 피드백도 받아볼 겸 적는다.
-------------------------------
JobRepeatManager는 글쓴이가 심심해서 만들어 본 유틸리티 성향의(?) 스크립트 입니다.
처음에 만들게 된 이유는 다음의 예로 들어보겠습니다.
목적 : 어떤 함수의 내용을 반복적으로 수행하게 하고 싶다
예) public void MyFunction() { ... } 을 50 번만 2.0초 간격으로 수행하고 싶다.
+ 그러나 public bool isExecute; 라는 변수가 true 일때만 하고 싶다.
+ 만약 public bool isDrop; 이라는 변수가 true 면은 중간에 멈추고 싶다.
+ Invoke나 Coroutine을 쓸 수는 없는 상황이다.
+ 수행되고 있는 펑션의 상태를 Visual로 보고싶다.
위와 같은 상황에서...이걸 어떻게 해결 해야할까...라는 고민중에
결국 다음과 같은 조건을 충족하도록 만들어본 유틸리티 코드입니다.
조 건 |
|
결과적으로 만든 JobRepeatManager 는 아래의 2가지 클래스 파일로 구성 되어 있습니다.
본문에서는 Gist 코드만 남기고 설명은 다음글로 넘기겠습니다.
1) JobRepeatBase.cs
using System.Collections; | |
using System.Collections.Generic; | |
using UnityEngine; | |
using UnityEngine.Events; | |
#if UNITY_EDITOR | |
using UnityEditor; | |
namespace AT.JobManager | |
{ | |
[CustomEditor(typeof(JobRepeatBase))] | |
public class JobBaseEditor : Editor | |
{ | |
public override void OnInspectorGUI() | |
{ | |
DrawDefaultInspector(); | |
JobRepeatBase job = (JobRepeatBase)target; | |
EditorGUILayout.BeginHorizontal(); | |
if (GUILayout.Button("Execute Immediately")) | |
{ | |
if (job.jobTodo != null) | |
{ | |
job.jobTodo(); | |
job.excuteCount++; | |
} | |
} | |
EditorGUILayout.EndHorizontal(); | |
EditorGUILayout.BeginHorizontal(); | |
if (GUILayout.Button("Drop")) | |
{ | |
job.state = JobRepeatBase.JOB_STATE.JOB_DROP; | |
} | |
if(GUILayout.Button("Drop Immediately")) | |
{ | |
JobRepeatManager.Instance.RemoveJob(job.key); | |
DestroyImmediate(job.gameObject); | |
} | |
EditorGUILayout.EndHorizontal(); | |
} | |
} | |
} | |
#endif | |
namespace AT.JobManager | |
{ | |
[System.Serializable] | |
public class JobRepeatBase : MonoBehaviour { | |
public enum JOB_STATE | |
{ | |
NONE, | |
JOB_EMPTY, | |
JOB_STANBY, | |
JOB_WORKING, | |
JOB_WAITING, | |
JOB_DROP, | |
} | |
public delegate void JobTodo(params object[] param); | |
public delegate void JobEndAction(params object[] param); | |
public delegate bool JobToDoCondition(params object[] param); | |
public delegate bool JobAutoDropCondition(params object[] param); | |
public string key; | |
public float repeatDelay; | |
public int repeatCount; | |
public int excuteCount; | |
public IEnumerator jobCoroutine; | |
public JobTodo jobTodo; | |
public JobEndAction jobEndAction; | |
public JobToDoCondition jobToDoCheck; | |
public JobAutoDropCondition jobAutoDropCheck; | |
public JOB_STATE state; | |
public IEnumerator worker; | |
public object[] parameter; | |
} | |
} |
2) JobRepeatManager.cs
using System.Collections; | |
using System.Collections.Generic; | |
using UnityEngine; | |
using JobTodo = AT.JobManager.JobRepeatBase.JobTodo; | |
using JobToDoCondition = AT.JobManager.JobRepeatBase.JobToDoCondition; | |
using JobAutoDropCondition = AT.JobManager.JobRepeatBase.JobAutoDropCondition; | |
using JobEndAction = AT.JobManager.JobRepeatBase.JobEndAction; | |
using JOB_STATE = AT.JobManager.JobRepeatBase.JOB_STATE; | |
#if UNITY_EDITOR | |
using UnityEditor; | |
namespace AT.JobManager | |
{ | |
[CustomEditor(typeof(JobRepeatManager))] | |
public class JobRepeatManagerEditor : Editor | |
{ | |
private JobRepeatManager manager; | |
public override void OnInspectorGUI() | |
{ | |
manager = (JobRepeatManager)target; | |
EditorGUILayout.BeginHorizontal(); | |
if (GUILayout.Button("Add Empty Job")) | |
{ | |
int EmptyJobCount = manager.JobList.FindAll(job => job.key.Contains("EmptyJob")).Count; | |
manager.AddDelegateJob(string.Format("EmptyJob_{0}", EmptyJobCount), null); | |
} | |
if (GUILayout.Button("Drop All Job")) | |
{ | |
if(manager.JobList.Count > 0) | |
{ | |
foreach(var jobItem in manager.JobList) | |
{ | |
DestroyImmediate(jobItem.gameObject); | |
} | |
manager.JobList.Clear(); | |
} | |
} | |
EditorApplication.update(); | |
EditorGUILayout.EndHorizontal(); | |
DrawDefaultInspector(); | |
} | |
} | |
} | |
#endif | |
namespace AT.JobManager | |
{ | |
[ExecuteInEditMode] | |
public class JobRepeatManager : SingletonBase<JobRepeatManager> | |
{ | |
// Job Information Container - Job Data Struct With Delegate Job | |
[SerializeField] protected List<JobRepeatBase> _jobList = new List<JobRepeatBase>(); | |
public List<JobRepeatBase> JobList { get { return _jobList; } } | |
// Coroutine Job Information Container - Job Data Struct With Coroutine Job | |
[SerializeField] protected Dictionary<string, Coroutine> _coroutineJobList = new Dictionary<string, Coroutine>(); | |
// Min DelayTime | |
protected float m_MinDelayTime = 0.1f; | |
public float MinDelayTime { get { return m_MinDelayTime; } set { m_MinDelayTime = value; } } | |
/// <summary> | |
/// Job RepeatManager > Adding Job | |
/// <para>key = JobKeyName, | |
/// todo = ExecuteFunctionPointer, | |
/// delay = Update Sequence Delay(Seconds), | |
/// repeatCount = Total Execute Count, | |
/// parameter = params object[] : Your Parameter | |
/// todoCondition = Execute Condition(true = Available Execute, false = Block Execute), | |
/// autoDropCondition = Job Drop Condition(Flag : true = Drop, false = MoveNext)</para> | |
/// </summary> | |
public bool AddDelegateJob(string key, JobTodo toDo, float delay = 1.0f, int repeatCount = 0, object[] parameter = null, | |
JobEndAction endActionWhenDrop = null, | |
JobToDoCondition toDoCondition = null, JobAutoDropCondition autoDropCondition = null, bool isImmediately = true) | |
{ | |
// Already Registered Job Check | |
if (JobList.Find(job => job.key.Equals(key)) != null) | |
return false; | |
GameObject JobObject = new GameObject(key); | |
JobObject.transform.parent = this.transform; | |
JobRepeatBase newJob = JobObject.AddComponent<JobRepeatBase>(); | |
newJob.key = key; | |
newJob.jobCoroutine = null; | |
newJob.jobTodo = toDo; | |
newJob.repeatDelay = delay; | |
newJob.repeatCount = repeatCount; | |
newJob.excuteCount = 0; | |
newJob.jobToDoCheck = toDoCondition; | |
newJob.jobAutoDropCheck = autoDropCondition; | |
newJob.jobEndAction = endActionWhenDrop; | |
newJob.state = JOB_STATE.JOB_STANDBY; | |
newJob.worker = CoJobHandle(key); | |
newJob.parameter = parameter; | |
if(toDo == null) | |
{ | |
Debug.LogWarningFormat("Are You Sure Adding Empty Job? Todo Parameter is null (key:{0})", key); | |
newJob.state = JOB_STATE.JOB_EMPTY; | |
} | |
newJob.repeatDelay = newJob.repeatDelay < m_MinDelayTime ? m_MinDelayTime : newJob.repeatDelay; | |
JobList.Add(newJob); | |
if (isImmediately) | |
{ | |
StartCoroutine(newJob.worker); | |
} | |
return true; | |
} | |
/// <summary> | |
/// Job RepeatManager > Coroutine Type Adding Job | |
/// <para>key = JobKeyName, | |
/// todo = Coroutine Type Todo, | |
/// delay = Update Sequence Delay(Seconds), | |
/// repeatCount = Total Execute Count, | |
/// parameter = params object[] : Your Parameter | |
/// todoCondition = Execute Condition(Flag : true = Available Execute, false = Block Execute), | |
/// autoDropCondition = Job Drop Condition(Flag : true = Drop, false = MoveNext), | |
/// </summary> | |
public bool AddCoroutineJob(string key, IEnumerator coroutineJobTodo, float delay = 1.0f, int repeatCount = 0, object[] param = null, | |
JobToDoCondition toDoCondition = null, JobAutoDropCondition autoDropCondition = null, | |
bool isImmediately = true) | |
{ | |
// Already Registered Job Check | |
if (JobList.Find(job => job.key.Equals(key)) != null) | |
return false; | |
GameObject JobObject = new GameObject(key); | |
JobObject.transform.parent = this.transform; | |
JobRepeatBase newJob = JobObject.AddComponent<JobRepeatBase>(); | |
newJob.key = key; | |
newJob.jobCoroutine = coroutineJobTodo; | |
newJob.jobTodo = null; | |
newJob.repeatDelay = delay; | |
newJob.repeatCount = repeatCount; | |
newJob.excuteCount = 0; | |
newJob.jobToDoCheck = toDoCondition; | |
newJob.jobAutoDropCheck = autoDropCondition; | |
newJob.state = JOB_STATE.JOB_STANDBY; | |
newJob.worker = CoJobHandle(key); | |
newJob.parameter = param; | |
if (coroutineJobTodo == null) | |
{ | |
Debug.LogWarningFormat("Are You Sure Adding Empty Job? Todo Parameter is null (key:{0})", key); | |
newJob.state = JOB_STATE.JOB_EMPTY; | |
} | |
newJob.repeatDelay = newJob.repeatDelay < m_MinDelayTime ? m_MinDelayTime : newJob.repeatDelay; | |
JobList.Add(newJob); | |
if (isImmediately) | |
{ | |
StartCoroutine(newJob.worker); | |
} | |
return true; | |
} | |
private void Start() | |
{ | |
StartCoroutine(CoAutoDropWorkers()); | |
} | |
public bool RemoveJob(string key) | |
{ | |
JobRepeatBase findJob = JobList.Find(job => job.key.Equals(key)); | |
if (findJob == null) | |
return false; | |
DestroyImmediate(findJob.gameObject); | |
return JobList.Remove(findJob); | |
} | |
public bool JobStart(string key) | |
{ | |
JobRepeatBase findJob = JobList.Find(job => job.key.Equals(key)); | |
if (findJob == null) | |
return false; | |
StopCoroutine(findJob.worker); | |
findJob.state = JOB_STATE.JOB_STANDBY; | |
StartCoroutine(findJob.worker); | |
return true; | |
} | |
public int JobDropAll() | |
{ | |
int droppedJobCount = 0; | |
if(JobList.Count > 0) | |
{ | |
JobList.Clear(); | |
droppedJobCount = transform.childCount; | |
transform.DestroyAllChildren(); | |
} | |
return droppedJobCount; | |
} | |
public bool ChangeJobDelay(string key, float newDelay) | |
{ | |
JobRepeatBase findJob = JobList.Find(job => job.key.Equals(key)); | |
if (findJob == null) | |
return false; | |
findJob.repeatDelay = newDelay; | |
StopCoroutine(findJob.worker); | |
findJob.state = JOB_STATE.JOB_STANDBY; | |
StartCoroutine(findJob.worker); | |
return true; | |
} | |
public bool ChangeRepeatCount(string key, int repeatCount) | |
{ | |
JobRepeatBase findJob = JobList.Find(job => job.key.Equals(key)); | |
if (findJob == null) | |
return false; | |
findJob.repeatCount = repeatCount; | |
StopCoroutine(findJob.worker); | |
findJob.state = JOB_STATE.JOB_STANDBY; | |
StartCoroutine(findJob.worker); | |
return true; | |
} | |
public bool AddFunctionChain(string key, JobTodo Todo = null, JobToDoCondition toDoCheck = null, JobAutoDropCondition autoDropCondition = null, bool isExecuteImmediately = true) | |
{ | |
JobRepeatBase Job = JobList.Find(job => job.key.Equals(key)); | |
if (Job == null) | |
return false; | |
StopCoroutine(Job.worker); | |
Job.jobTodo += Todo; | |
Job.jobToDoCheck += toDoCheck; | |
Job.jobAutoDropCheck += autoDropCondition; | |
if (Job.jobTodo == null) | |
{ | |
Job.state = JOB_STATE.JOB_EMPTY; | |
return true; | |
} | |
if (isExecuteImmediately) | |
{ | |
Job.state = JOB_STATE.JOB_STANDBY; | |
StartCoroutine(Job.worker); | |
} | |
return true; | |
} | |
public bool RemoveFunctionChain(string key, JobTodo Todo = null, JobToDoCondition toDoCheck = null, | |
JobAutoDropCondition autoDropCondition = null, bool isExecuteImmediately = true) | |
{ | |
JobRepeatBase Job = JobList.Find(job => job.key.Equals(key)); | |
if (Job == null) | |
return false; | |
StopCoroutine(Job.worker); | |
Job.jobTodo -= Todo; | |
Job.jobToDoCheck -= toDoCheck; | |
Job.jobAutoDropCheck -= autoDropCondition; | |
if (Job.jobTodo == null) | |
{ | |
Job.state = JOB_STATE.JOB_EMPTY; | |
return true; | |
} | |
if (isExecuteImmediately) | |
{ | |
Job.state = JOB_STATE.JOB_STANDBY; | |
StartCoroutine(Job.worker); | |
} | |
return true; | |
} | |
public JobRepeatBase GetJobBase(string key) | |
{ | |
return JobList.Find(x => x.key == key); | |
} | |
private WaitForSeconds _dropManagingDelay = new WaitForSeconds(3.0f); | |
private IEnumerator CoAutoDropWorkers() | |
{ | |
while (gameObject.activeSelf) | |
{ | |
var dropItems = JobList.FindAll(Job => Job.state == JOB_STATE.JOB_DROP); | |
foreach(var dropItem in dropItems) | |
{ | |
dropItem.jobEndAction?.Invoke(dropItem.parameter); | |
JobList.Remove(dropItem); | |
DestroyImmediate(dropItem.gameObject); | |
} | |
yield return _dropManagingDelay; | |
} | |
} | |
private IEnumerator CoJobHandle(string key) | |
{ | |
yield return null; | |
JobRepeatBase findJob = JobList.Find(x => x.key == key); | |
if (findJob == null) | |
yield break; | |
switch (findJob.state) | |
{ | |
case JOB_STATE.JOB_EMPTY: | |
yield break; | |
case JOB_STATE.JOB_STANDBY: | |
if (findJob.jobToDoCheck != null) | |
{ | |
if (findJob.jobToDoCheck(findJob.parameter)) | |
{ | |
findJob.state = JOB_STATE.JOB_WORKING; | |
findJob.jobTodo?.Invoke(findJob.parameter); | |
if (findJob.jobCoroutine != null) | |
yield return StartCoroutine(findJob.jobCoroutine); | |
findJob.excuteCount++; | |
if (findJob.excuteCount >= findJob.repeatCount && findJob.repeatCount != 0) | |
findJob.state = JOB_STATE.JOB_DROP; | |
else | |
findJob.state = JOB_STATE.JOB_WAITING; | |
} | |
} | |
else | |
{ | |
findJob.state = JOB_STATE.JOB_WORKING; | |
findJob.jobTodo?.Invoke(findJob.parameter); | |
if (findJob.jobCoroutine != null) | |
yield return StartCoroutine(findJob.jobCoroutine); | |
findJob.excuteCount++; | |
if (findJob.excuteCount >= findJob.repeatCount && findJob.repeatCount != 0) | |
findJob.state = JOB_STATE.JOB_DROP; | |
else | |
findJob.state = JOB_STATE.JOB_WAITING; | |
} | |
if (findJob.jobAutoDropCheck != null) | |
{ | |
if (findJob.jobAutoDropCheck(findJob.parameter)) | |
{ | |
findJob.state = JOB_STATE.JOB_DROP; | |
break; | |
} | |
} | |
break; | |
case JOB_STATE.JOB_WAITING: | |
WaitForSeconds WaitForDelay = new WaitForSeconds(findJob.repeatDelay); | |
yield return WaitForDelay; | |
findJob.state = JOB_STATE.JOB_STANDBY; | |
break; | |
case JOB_STATE.JOB_DROP: | |
yield break; | |
} | |
yield return StartCoroutine(CoJobHandle(findJob.key)); | |
} | |
} | |
} | |
'Unity Engine > Unity3D Engine' 카테고리의 다른 글
#04.C# Job System_01 (0) | 2018.12.22 |
---|---|
#03.JobSequenceManager (0) | 2018.12.21 |
#02.JobRepeatManager - 03 (0) | 2018.12.20 |
#02.JobRepeatManager - 02 (0) | 2018.12.20 |
#01.A에서 B로 일정 시간동안 움직이기 (0) | 2018.12.18 |