'c#'에 해당되는 글 4건

  1. 2018.12.21 #03.JobSequenceManager
  2. 2018.12.21 NGUI #02.UIPanel
  3. 2018.12.20 #02.JobRepeatManager - 03
  4. 2018.12.19 #02.JobRepeatManager - 01
posted by REDFORCE 2018. 12. 21. 11:20

이번에 적을 글은 전편인 JobRepeatManager 에 이어서 JobSequenceManager 입니다.

(대체 얼마나 할일없으면 나 자신은 이런걸 만드는거니...코딩 덕후 같으니)


다음과 같은 이유나 상황일 때 사용하기 위해 만들었습니다.


 > 순차적인 함수 호출이 필요하다

 > 서로 다른 인스턴스에서의 함수 호출로 인해 순차적 호출 시점 제어가 너무 귀찮다.

 > 각 함수들을 Coroutine 으로 순차적 실행을 하고 싶을 때가 있다.


그래서 만든 것이 JobSequenceManager 입니다.


using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using System.Reflection;
namespace AT.JobManager
{
public class JobSequenceManager : SingletonBase<JobSequenceManager> {
public Queue<Tuple<MethodInfo, object, object[]>> _sequenceJobs = new Queue<Tuple<MethodInfo, object, object[]>>();
public void AddJob<T>(string methodName, params object[] parameter)
{
Type type = typeof(T);
object instance = Activator.CreateInstance(type);
MethodInfo toInvoke = type.GetMethod(methodName);
ParameterInfo[] parameters = toInvoke.GetParameters();
_sequenceJobs.Enqueue(new Tuple<MethodInfo, object, object[]>(toInvoke, instance, parameter));
}
public void OnExecuteSequenceJob()
{
while (_sequenceJobs.Count > 0)
{
var job = _sequenceJobs.Dequeue();
job.Item1.Invoke(job.Item2, job.Item3);
}
}
public void OnExecuteSequenceCoroutine()
{
StopCoroutine(CoExecuteJobSequence());
StartCoroutine(CoExecuteJobSequence());
}
protected IEnumerator CoExecuteJobSequence()
{
while (_sequenceJobs.Count > 0)
{
var job = _sequenceJobs.Dequeue();
job.Item1.Invoke(job.Item2, job.Item3);
yield return null;
}
yield return null;
}
}
}


JobSequenceManager는 다음과 같은 함수와 파라미터를 가지고 있습니다.


 멤버 변수

 설명


 public Queue<Tuple<MethodInfo, object, object[]>> _sequenceJobs


 등록되는 Job을 담고 있는 컨테이너 입니다.



 메소드

 설명 

 public void AddJob<T>(string methodName, params object[] parameter) 


 - 수행시킬 Job을 등록하는 함수입니다. 

 - AddJob<T>에서 <T>에는 본인이 등록 시킬 클래스 타입을 넣어주세요.

 - methodName에는 Type T에 구현되어있는 함수 이름을 넣어주세요.

 - 해당 함수에 파라미터를 전달 하고 싶은게 있을 시엔 parameter에 전달하면 됩니다.


 public void OnExecuteSequenceJob()


 - 등록 된 Job을 수행시킵니다.
 - 모든 Job을 일괄 순차적으로 수행시킵니다.


 public void OnExecuteSequenceCoroutine()


 - 등록 된 Job을 Corotuine 형태로 수행시킵니다.

 - 모든 Job을 순차적으로 수행시킵니다.

 - JobSequenceManager의 CoExecuteJobSequence( )
에서 한 Job마다 수행이 끝날 때마다 yield return null 이 불려집니다.


 protected IEnumerator CoExecuteJobSequence()


 - 등록 된 Job을 IEnumerator(Corotuine) 형태로 수행시키는 함수입니다.

 



사용 방법은 간단하게 아래 샘플로 적어두겠습니다.


using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class JobSequenceTest : MonoBehaviour {
void Start () {
Invoke("RegistJobA", 2.0f);
Invoke("ExecuteJob", 5.0f);
}
public void RegistJobA()
{
AT.JobManager.JobSequenceManager.Instance.AddJob<JobSequenceTest>("OnExecuteFunctionA");
AT.JobManager.JobSequenceManager.Instance.AddJob<JobSequenceTest>("OnExecuteFunctionB", 100, 43.123f);
}
public void ExecuteJob()
{
AT.JobManager.JobSequenceManager.Instance.OnExecuteSequenceJob(); // use case 1 - execute at the Sametime
//AT.JobManager.JobSequenceManager.Instance.OnExecuteSequenceCoroutine(); // use case 2 - coroutine - ( loop ) { Job Execute(); yield return null; }
}
public void OnExecuteFunctionA()
{
Debug.Log("Execute OnExecuteFunctionA");
}
public void OnExecuteFunctionB(int count, float point)
{
Debug.LogFormat("Execute OnExecuteFunctionB : {0}, {1}", count, point);
}
}


'Unity Engine > Unity3D Engine' 카테고리의 다른 글

#04.C# Job System_02  (0) 2018.12.23
#04.C# Job System_01  (0) 2018.12.22
#02.JobRepeatManager - 03  (0) 2018.12.20
#02.JobRepeatManager - 02  (0) 2018.12.20
#02.JobRepeatManager - 01  (0) 2018.12.19
posted by REDFORCE 2018. 12. 21. 00:53

Unity 2018.2.1f1 - NGUI 3.12.0 을 기반으로 작성 되었습니다.

-----------------------

이번에 작성할 내용은 UIRoot에 이어서 UIPanel입니다.




UIPanel 은 NGUI의 UIWidget들이 담겨져서 보여지게 되는 일련의 가장 뒷편에 깔리는 도화지 같은 개념이라 보시면 됩니다.


 

 설명

 Alpha 


 해당 패널의 Hierarcy 구조상 하위에 붙은 모든 Widget들의 Alpha에 영향을 미치는 값 입니다. 하위에 붙어있는 Panel에는 영향을 주지 않습니다.

 

 Depth


 만약 UIPanel이 2개 이상 UIRoot에 자식으로 활성화되어 있을 시

먼저 보여지게 될 순서를 의미합니다.


 Clipping


 특정 영역을 설정하고 그 영역 밖에 있는 부분을 렌더링하지 않는 것을 의미합니다.


 > None : 클리핑을 하지 않습니다.

 > Texture Mask : 등록 된 Texture 영역을 제외하고 클리핑 합니다.

 > Soft Clipping : 패널 영역을 설정하고 클리핑 합니다. Softness 값을 주어 외곽부분을 부드럽게 클리핑 되도록 합니다.

 > Constrained But Dont Clip : 패널 영역을 지정하지만 클리핑은 하지 않습니다.


 Advanced Options

  

 Render Q :
  - Rendering 되어질 때의 순서를 정할 수 있습니다.

  - UI에 Particle System 을 사용하는 상황 같은 Render Queue 경쟁이 벌어질 수 있는 환경에서 설정하는 값 입니다. 

 Sort Order :
  - 같은 DEpth에서 렌더링 순서를 결정합니다.
  - Unity Sprite의 OrderInLayer와 유사합니다.
  - 높을 수록 위에 출력됩니다.

 Normal : 체크하면 패널이 Light에 영향을 받습니다.

 Cull : ScrollView 패널에서 성능 향상을 위해 사용되어지는 옵션입니다. 패널이 드래그(Drag) 되어 Widget이 패널 영역을 벗어날 시 자식위젯들을 Rendering 하지 않습니다.

 Visible : 패널 내부의 Widget들이 스크린 안에 있는지 계산하여 Rendering 여부를 결정하는 데, 이 계산을 건너띄게 만듭니다.
UIWidget 들이 패널을 벗어날 일이 없을 경우 체크하게 되면 성능이 향상 됩니다.

 Padding : 패널의 외각 부분을 부드럽게 만듭니다.
 (클리핑 옵션이 Softness 인 상태에서는 의미는 없는 듯)

 Static : 패널 내의 위젯들이 이동이 없는 경우 Static으로 설정하면 유니티가 Position, Rotation, Scale 값을 계산하지 않아 성능이 향상 됩니다.


 Anchors


  UIPanel 또는 UIWidget 에서 특정 위치를 기준으로 좌표가 잡히도록 설정하는 값 입니다.


 Show Draw Calls

 Draw Call Tool 창을 불러옵니다.


'Unity Engine > NGUI' 카테고리의 다른 글

[NGUI] Infinite ScrollView (AT)  (1) 2019.06.20
NGUI #01.UIRoot  (0) 2018.12.18
posted by REDFORCE 2018. 12. 20. 02:56

#02.JobRepeatManager - 01

#02.JobRepeatManager - 02


이번 글은 마지막으로 JobRepeatManager 에 대한 간단한 구조 설명과

사용 예제로 마무리 하겠습니다.


다소 글이 길어질 수 있습니다. 만약 액기스만 뽑아먹을래! 하는 분은 샘플예제만 보셔도 됩니다.

해서...샘플 예제를 먼저 적어두겠습니다.


using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using AT.JobManager;
public class JobRepeat_Sample : MonoBehaviour {
void Start ()
{
JobRepeatManager.Instance.AddDelegateJob("My_Job_01", OnExecuteTodo, 1.0f, 0, new object[] { "My_Job_01", 0});
JobRepeatManager.Instance.AddDelegateJob("My_Job_02", OnExecuteTodo, 2.0f, 0, new object[] { "My_Job_02", 1});
JobRepeatManager.Instance.AddDelegateJob("My_Job_03", OnExecuteTodo, 2.0f, 10, new object[] { "My_Job_03", 2});
JobRepeatManager.Instance.AddDelegateJob("My_Job_04", OnExecuteTodo, 2.0f, 15, new object[] { "My_Job_04", 3}, OnEndActionWhenDrop);
JobRepeatManager.Instance.AddDelegateJob("My_Job_05", OnExecuteTodo, 1.0f, 20, new object[] { "My_Job_05", 4}, OnEndActionWhenDrop, OnCheckDoCondition);
JobRepeatManager.Instance.AddDelegateJob("My_Job_06", OnExecuteTodo, 1.0f, 20, new object[] { "My_Job_06", 5}, OnEndActionWhenDrop, OnCheckDoCondition, OnCheckDrop);
IEnumerator myJobCoroutine = CoJobExecuteTodo();
JobRepeatManager.Instance.AddCoroutineJob("My_JobCoroutine", myJobCoroutine, 1.0f, 10, new object[] { "My_JobCoroutine" });
}
public IEnumerator CoJobExecuteTodo(params object[] param)
{
for(int i = 0; i < 10; i++)
{
Debug.Log("{0} : JobCoroutine To do!!");
yield return new WaitForSeconds(0.5f);
}
}
public void OnExecuteTodo(params object[] param)
{
// To do
Debug.LogFormat("{0} : To do !!", param[0]);
}
public void OnEndActionWhenDrop(params object[] param)
{
// Execute When Dropped
Debug.LogFormat("{0} : End Action!", param[0]);
}
public bool OnCheckDoCondition(params object[] param)
{
// true -> Available Execute : false -> Block Execute
return true;
}
public bool OnCheckDrop(params object[] param)
{
// true > Job Drop : false > Nothing
Debug.LogFormat("{0} : Job drop", param[0]);
return true;
}
}


----------------------------------------


2. JobRepeatManager


JobRepeatManager 는 Singleton Pattern으로 인스턴스가 생성 됩니다.


싱글톤 패턴으로 생성 되어있는 인스턴스 이므로 만약 JobRepeatManager를 사용하실 분들은 간단히 static으로 인스턴스를 넣어주세요.


JobRepeatManager에는 다음과 같은 Public 함수와 멤버변수가 있습니다.


 멤버변수 및 애트리뷰트

 설명

 public List<JobRepeatBase> JobList


 get; _jobList


 public float MinDelayTime


 get;set; => m_MinDelayTime 



 메소드 이름

 설명 


 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)


 - JobRepeatBase를 생성 및 등록하는 함수

 - 일반적인 Delegate 형태의 Job을 등록할 수 있습니다.


 public AddCoroutineJob (string key, 

 IEnumerator coroutineJobTodo, 

 float delay = 1.0f, 

 int repeatCount = 0,

 object[] param = null,

 JobToDoCondition toDoCondition = null, 

 JobAutoDropCondition autoDropCondition = null,

 bool isImmediately = true)


 - JobRepeatBase를 생성 및 등록하는 함수

 - Coroutine 형태의 Job을 생성합니다.

 - Todo를 추가로 Job에 등록하고 싶으실 땐 AddFunctionChain을 호출하여 사용합니다. 

 public bool JobStart (string key)


 - 특정 등록되어진 Job을 key 값으로 찾아서 Job을 Execute 시킵니다.

 - 정상적인 실행이 성공되면 true를 리턴합니다. 실패 시 return false


 public bool RemoveJob(string key)


 - 특정 등록되어진 Job을 Key 값으로 찾아서 드랍(Drop)시킵니다. 정상적으로 드랍에 성공할 시 true를 리턴합니다.


 public int JobDropAll ( )


 - 등록되어진 모든 Job을 Drop 시킵니다.

 - 드랍시킨 Job 갯수를 리턴합니다.


 public bool ChangeJobDelay (string key, float newDelay)


 - 특정 등록되어진 Job을 Key 값으로 찾은 뒤 해당 Job의 DelayTime을 변경합니다.

 - 만약 수행중(Job Started)인 Job 이었다면 다음 수행부터 Delay가 변경됩니다.

 - 변경에 선공하면 true 를 리턴합니다.


 public bool ChangeJobRepeatCount (string key, int repeatCount)


 - 특정 등록되어진 Job을 Key 값으로 찾은 뒤 해당 Job의 RepeatCount를 변경합니다.

 - 만약 수행중(Job Started)인 Job 이었다면 다음 수행부터 RepeatCount가 변경됩니다.

 - 변경에 성공하면 true를 리턴합니다.



 public bool AddFunctionChain(string key,

 JobTodo todo = null, 

 JobTodoCondition TodoCheck = null,

 JobAutoDropCondition autodropCondition = null, bool isExecuteImmediately = true)


 - key값으로 찾은 Job에 등록되어진 Delegate를 추가로 등록하는 함수입니다.


 public bool RemoveFunctionChain(string key, JobTodo todo = null,   JobTodoCondition todoCheck = null,

 JobAutoDropCondition autodropCondition = null, bool isExecuteImmediately = true)


 -Key 값으로 찾은 Job에 등록되어진 Delegate를 삭제하는 함수입니다.

 public JobRepeatBase GetJobBase(string key)

 - Key 값으로 JobRepeatBase 정보를 찾는 함수입니다.

 private IEnumerator CoAutoDropWorkers()


 - Drop 상태가 된 Job을 삭제시키는 Coroutine 입니다.

 - JobRepeatManager 의 instance가 Start 될 때 실행 됩니다.

 - Drop 상태 체크는 

private WaitForSeconds dropManagingDelay 마다 수행됩니다.

 

 private IEnumerator CoJobHandle(string key)


 - JobRepeatManager에 등록되어진 Job들을 동작시키는 Corotuine 입니다.

 - JobRepeatBase에 담겨져있는 Job.state 값에 따라 Job을 동작시킵니다.



-------------------------------


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 - 02  (0) 2018.12.20
#02.JobRepeatManager - 01  (0) 2018.12.19
#01.A에서 B로 일정 시간동안 움직이기  (0) 2018.12.18
posted by REDFORCE 2018. 12. 19. 01:45

#02.JobRepeatManager - 01


이번 글에 작성할 내용은 개인적으로 유니티를 사용하면서 이런 스크립트가 있으면 편할 것 같은데? 라는 생각에 만들어 본 스크립트이다.


그 이름은 Job Repeat Manager !!!!!!!!!!!


개인적으로 만들어놓고 그래서 요놈이 효율적일까? 괜찮은가? 안전한가? 

라는 질문을 수 차례했으나 답은...나도 모르겠당...ㅎㅎ


그래도 올려두면 누군가는 쓰지 않을까? 라는 생각에 피드백도 받아볼 겸 적는다.

-------------------------------


JobRepeatManager는 글쓴이가 심심해서 만들어 본 유틸리티 성향의(?) 스크립트 입니다.

처음에 만들게 된 이유는 다음의 예로 들어보겠습니다.


목적 : 어떤 함수의 내용을 반복적으로 수행하게 하고 싶다
 예) public void MyFunction() { ... } 을 50 번만 2.0초 간격으로 수행하고 싶다.


  + 그러나 public bool isExecute;  라는 변수가 true 일때만 하고 싶다.

  + 만약 public bool isDrop; 이라는 변수가 true 면은 중간에 멈추고 싶다.

  + Invoke나 Coroutine을 쓸 수는 없는 상황이다.

  + 수행되고 있는 펑션의 상태를 Visual로 보고싶다.


위와 같은 상황에서...이걸 어떻게 해결 해야할까...라는 고민중에

결국 다음과 같은 조건을 충족하도록 만들어본 유틸리티 코드입니다.


 조 건 

  1. Update() 를 쓰고 싶지는 않다.
  2. 어떤 내용(To do)을 n번 실행하게 하고 싶다.
  3. 수행되는 내용을 GameObject 로 관리하고 싶다.
  4. 람다(Lambda) 또는 Delegate를 이용한 Event 형태의 Job을 만들어서 관리하고 싶다.


결과적으로 만든 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