포슀트

🫐 MUdons 00 - ActiveToggle, ActiveList μΆ”μƒν΄λž˜μŠ€

https://github.com/Mascari4615/MUdons/commit/2763f45666b8930c75c905e4abb8944f262680fe

🎲 머리글


μ›λž˜ VRChat UdonSharpμ—μ„œλŠ” 좔상 클래슀/λ©”μ„œλ“œλ₯Ό μ§€μ›ν•˜μ§€ μ•Šμ•˜λŠ”λ°, ν˜Ήμ‹œ μ—…λ°μ΄νŠΈκ°€ λ˜μ—ˆλ‚˜ μ‹Άμ–΄ μ‹œλ„ν•΄λ³΄λ‹ˆ μ»΄νŒŒμΌλ„ 잘 되고, μ½”λ“œ κ·ΈλŒ€λ‘œ λΉŒλ“œ ν…ŒμŠ€νŠΈλ₯Ό 진행해도 별닀λ₯Έ λ¬Έμ œλ„ 보이지 μ•Šμ•˜λ‹€. μ–Έμ œλΆ€ν„°μΈκ°€ μ§€μ›λ˜κ³  μžˆμ—ˆλ‚˜λ³΄λ‹€.

상속은 기쑴에도 κ°€λŠ₯ν•˜κΈ΄ ν–ˆμ§€λ§Œ, 좔상 클래슀λ₯Ό μ‚¬μš©ν•  수 있게 된 김에, κ·Έλ™μ•ˆ 고쳐야겠닀 μƒκ°ν•˜κ³  있던 ObjectActive, ObjectActiveList, CanvasGroupActive, CanvasGroupActiveList 그리고 CameraActive μš°λ™μ˜ μ½”λ“œ/ꡬ쑰 쀑볡λ₯Ό κ°œμ„ ν•˜κ³ μž ν–ˆλ‹€.

🎲 문제


λ¨Όμ € ~Active μš°λ™λ“€μ˜ λͺ©μ μ„ 정리해본닀.
~Active μš°λ™λ“€μ€ νŠΉμ • 상황에 νŠΉμ • μš”μ†Œλ₯Ό ν™œμ„±ν™”/λΉ„ν™œμ„±ν™”μ‹œν‚€λŠ” κ°„λ‹¨ν•œ λͺ©μ μ„ 가지고 μžˆλ‹€.

νŠΉμ • μš”μ†Œμ˜ νƒ€μž…μ€ GameObjectκ°€ 될 μˆ˜λ„ 있고, CanvasGroup이 될 μˆ˜λ„ 있고, Cameraκ°€ 될 μˆ˜λ„ 있고, λ‚΄κ°€ λ§Œλ“  MPickup이 될 μˆ˜λ„ μžˆλ‹€.
μ–΄μ¨Œκ±°λ‚˜ ν™œμ„±ν™”/λΉ„ν™œμ„±ν™”μ˜ κ°œλ…μ„ κ°€μ§ˆ 수 μžˆλŠ” 것이면 λœλ‹€.

( λ‚΄κ°€ λ§Œλ“  MPickup은 μ—¬λŸ¬ ν”½μ—… κ΄€λ ¨ κΈ°λŠ₯κ³Ό ν•¨κ»˜ Enable κ°œλ…μ„ 가지고 μžˆλ‹€, Enable의 값에 따라 ν•΄λ‹Ή MPickup의 μ½œλΌμ΄λ”λ‚˜ λ©”μ‰¬λ Œλ”λŸ¬, μžμ‹ μ˜€λΈŒμ νŠΈλ“€μ΄ ν™œμ„±ν™”/λΉ„ν™œμ„±ν™”λ˜λŠ” 식이닀. ) ( .. 이거 μ™„μ „ ~Active κΈ°λŠ₯μ΄μž–μ•„? κ·Έλ ‡λ‹€, MPickup은 ~Acitve κΈ°λŠ₯을 가지고 μžˆμ—ˆλ‹€. μΆ”ν›„ MPickup의 이런 뢀뢄듀은 ~Active μš°λ™μœΌλ‘œ λŒ€μ²΄ν•΄λ„ 쒋을 것 κ°™λ‹€. )

~Active μš°λ™μ˜ μ’…λ₯˜λŠ” 크게 두 κ°€μ§€λ‘œ λ‚˜λ‰œλ‹€.
ν† κΈ€κ³Ό 리슀트.

ν† κΈ€ κΈ°λŠ₯은 λ§κ·ΈλŒ€λ‘œ μš”μ†Œλ“€μ„ ν† κΈ€, ν™œμ„±ν™”/λΉ„ν™œμ„±ν™”ν•˜λŠ” κΈ°λŠ₯이고,
리슀트 κΈ°λŠ₯은 μ—¬λŸ¬ 가지 μš”μ†Œλ“€ μ€‘μ—μ„œ, νŠΉμ • 상황에 맞게 νŠΉμ • μš”μ†Œλ§Œμ„ ν™œμ„±ν™”μ‹œν‚€κ³  λ‚˜λ¨Έμ§€λŠ” λΉ„ν™œμ„±ν™”μ‹œν‚€λŠ”, μ‘°κΈˆμ€ κΈ°λŠ₯이 μΆ”κ°€λœ μš°λ™μ΄λ‹€.

이런 ν† κΈ€, 리슀트 κΈ°λŠ₯듀은, λ˜‘κ°™μ€ μ½”λ“œμ— νƒ€μž…λ§Œ λ‹€λ₯Έ 채 ObjectActive, CanvasGroupActive, CameraActive 같이 μ—¬λŸ¬ μš°λ™μœΌλ‘œ μ‘΄μž¬ν•˜κ³  μžˆλ‹€.
μ½”λ“œκ°€ 많이 μ€‘λ³΅λ˜κ³  μžˆμ—ˆκ³ , μ΄λŠ” 아름닡지 μ•Šλ‹€ !

🎲 좔상화


λ˜‘κ°™μ€ μ½”λ“œμ— νƒ€μž…λ§Œ λ‹€λ₯΄λ©΄.. 이거 μΌλ°˜ν™” μž–μ•„?
ν•˜μ§€λ§Œ μ—„λ°€νžˆ 따져보면 λ˜‘κ°™μ€ μ½”λ“œλŠ” μ•„λ‹ˆμ—ˆλ‹€.

λŒ€μƒμœΌλ‘œ ν•˜λŠ” νƒ€μž…λ“€μ€ κ°œλ…μ μœΌλ‘œλŠ” λͺ¨λ‘ ν™œμ„±ν™”/λΉ„ν™œμ„±ν™” μƒνƒœλ₯Ό 가지고 μžˆμ§€λ§Œ, μ„œλ‘œ λ‹€λ₯Έ λͺ¨μ–‘μœΌλ‘œ κ΅¬ν˜„λ˜μ–΄μžˆκΈ° λ•Œλ¬Έμ— ( GameObject.active와 Camera.enabledλŠ” λ‹€λ₯΄λ‹€ ! ) 이λ₯Ό μΌλ°˜ν™”ν•˜κΈ°μ—” 무리가 μžˆλ‹€.

또 μ• μ΄ˆμ— Unity μΈμŠ€νŽ™ν„°μ—μ„œ μš”μ†Œλ“€μ˜ μ°Έμ‘°λ₯Ό 직접 λ“±λ‘ν•˜λŠ” 것이 μ „μ œλΌ, ν•˜λ‚˜μ˜ 클래슀/λ©”μ„œλ“œμ— μ—¬λŸ¬ νƒ€μž…μ„ μœ„ν•œ μΌλ°˜ν™” λ©”μ„œλ“œλ₯Ό λ§Œλ“€ μˆ˜λŠ” μ—†λ‹€.
ν•˜λ‚˜μ˜ ν΄λž˜μŠ€μ— GameObject, CanvasGroup, Camera νƒ€μž…μ„ Object둜 λ°›μ•„λ‚΄κ±°λ‚˜ μ „λΆ€ ν•˜λ‚˜ν•˜λ‚˜ λ³€μˆ˜λ‘œ λ§Œλ“€ μˆ˜λŠ” μ—†μœΌλ‹ˆκΉŒ..

μ§€μ›ν•˜μ§€ μ•Šμ•„
μ»΄ν¬λ„ŒνŠΈλ“€μ΄λΌλ„ Behaviour.enableλ₯Ό μ¨μ„œ μΌλ°˜ν™”ν•΄λ³΄λ €κ³  ν–ˆλŠ”λ°, UdonSharpμ—μ„œλŠ” Behaviour.enabled의 set을 μ§€μ›ν•˜μ§€ μ•Šμ•˜λ‹€.

κ·Έλž˜μ„œ ~Active μš°λ™λ“€μ˜ κ³΅ν†΅λœ κ°œλ…κ³Ό κΈ°λŠ₯은 미리 κ΅¬ν˜„λ˜μ–΄ μžˆμ§€λ§Œ, ν™œμ„±ν™”/λΉ„ν™œμ„±ν™” μ²˜λ¦¬λŠ” 좔상화 λ©”μ„œλ“œλ‘œ 남겨둔 좔상화 클래슀λ₯Ό λ§Œλ“€κ³ ,
이λ₯Ό 각 νƒ€μž…λ§ˆλ‹€ 상속받아 ν™œμ„±ν™”/λΉ„ν™œμ„±ν™” μƒνƒœμ— 따라 각 νƒ€μž… 별 처리λ₯Ό κ΅¬ν˜„ν•˜λ„λ‘ ν–ˆλ‹€.

🎲 μ½”λ“œ - Core (ActiveToggle, ActiveList)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
public abstract class ActiveToggle : MBase
{
	[Header("_" + nameof(ActiveToggle))]
	[SerializeField] private bool defaultActive;

	[Header("_" + nameof(ActiveToggle) + " - Options")]
	[SerializeField] private MBool mBool;

	private bool _active;
	public bool Active
	{
		get => _active;
		private set
		{
			_active = value;
			UpdateActive();
		}
	}

	private void Start()
	{
		Init();
	}

	private void Init()
	{
		SetActive(defaultActive);

		if (mBool != null)
		{
			mBool.RegisterListener(this, nameof(UpdateValueByMBool));
			UpdateValueByMBool();
		}

		UpdateActive();
	}

	protected abstract void UpdateActive();

	public void SetActive(bool newActive)
	{
		MDebugLog($"{nameof(SetActive)}({newActive})");

		if (mBool != null)
		{
			mBool.SetValue(newActive);
		}
		else
		{
			Active = newActive;
		}
	}

	public void UpdateValueByMBool()
	{
		if (mBool)
			Active = mBool.Value;
	}

	public void SetMBool(MBool mBool)
	{
		this.mBool = mBool;
		UpdateValueByMBool();
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
public abstract class ActiveList : MBase
{
	[Header("_" + nameof(ActiveList))]
	[SerializeField] private int defaultValue;

	[Header("_" + nameof(ActiveList) + " - Options")]
	[SerializeField] protected MValue mValue;
	[SerializeField] protected ActiveListOption option;
	[SerializeField] protected int targetIndex = NONE_INT;

	private int _value;
	public int Value
	{
		get => _value;
		private set
		{
			_value = value;
			UpdateActive();
		}
	}

	private void Start()
	{
		Init();
	}

	private void Init()
	{
		SetValue(defaultValue);

		if (mValue != null)
		{
			InitMValueMinMax();
			mValue.RegisterListener(this, nameof(UpdateActiveByMValue));
		}

		UpdateActive();
	}

	/// <summary>
	/// λŒ€μƒμ΄ λ˜λŠ” μš”μ†Œλ“€μ˜ 수λ₯Ό λ°”νƒ•μœΌλ‘œ mValue.SetMinMaxValue
	/// </summary>
	protected abstract void InitMValueMinMax();

	/// <summary>
	/// ActiveListOption에 λŒ€ν•˜μ—¬ 각 μΌ€μ΄μŠ€ λ³„λ‘œ κ΅¬ν˜„
	/// </summary>
	/// <param name="value"></param>
	protected abstract void UpdateActive();

	public void SetValue(int newValue)
	{
		MDebugLog($"{nameof(SetValue)}({newValue})");
	
		if (mValue != null)
		{
			mValue.SetValue(newValue);
		}
		else
		{
			Value = newValue;
		}
	}

	public void UpdateActiveByMValue()
	{
		if (mValue)
			Value = mValue.Value;
	}

	public void SetMValue(MValue mValue)
	{
		this.mValue = mValue;
		InitMValueMinMax();
		UpdateActiveByMValue();
	}
}

🎲 μ½”λ“œ - Impl (ObjectActive, ObjectActiveList)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class ObjectActive : ActiveToggle
{
	[Header("_" + nameof(ObjectActive))]
	[SerializeField] private GameObject[] activeObjects;
	[SerializeField] private GameObject[] disableObjects;

	protected override void UpdateActive()
	{
		MDebugLog($"{nameof(UpdateActive)}");

		foreach (GameObject o in activeObjects)
			o.SetActive(Active);

		foreach (GameObject o in disableObjects)
			o.SetActive(!Active);
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
public class ObjectActiveList : ActiveList
{
	[Header("_" + nameof(ObjectActiveList))]
	[SerializeField] private GameObject[] objectList;
	// [SerializeField] private Behaviour[] behaviours; // 240819 : U#μ—μ„œ .enabled set을 μ§€μ›ν•˜μ§€ μ•ŠμŒ

	protected override void InitMValueMinMax()
	{
		int maxLen = Mathf.Max(objectList.Length);
		mValue.SetMinMaxValue(0, maxLen);
	}

	protected override void UpdateActive()
	{
		MDebugLog($"{nameof(UpdateActive)}({Value})");

		switch (option)
		{
			// MValueλ₯Ό 리슀트의 인덱슀둜 μ‚¬μš©ν•  λ•Œ
			// 예λ₯Ό λ“€μ–΄, MValueκ°€ 0일 λ•Œ 리슀트의 0번째 였브젝트만 ν™œμ„±ν™”, λ‚˜λ¨Έμ§€λŠ” λΉ„ν™œμ„±ν™”
			case ActiveListOption.UseMValueAsListIndex:
				for (int i = 0; i < objectList.Length; i++)
				{
					if (objectList[i])
						objectList[i].SetActive(i == Value);
				}
				break;

			// MValueλ₯Ό νƒ€κ²Ÿ 인덱슀둜 μ‚¬μš©ν•  λ•Œ
			// 예λ₯Ό λ“€μ–΄, MValueκ°€ 0일 λ•Œ νƒ€κ²Ÿ μΈλ±μŠ€κ°€ 0이면 리슀트의 λͺ¨λ“  였브젝트 ν™œμ„±ν™”, μ•„λ‹ˆλ©΄ λΉ„ν™œμ„±ν™”
			case ActiveListOption.UseMValueAsTargetIndex:
				bool isTargetIndex = Value == targetIndex;

				foreach (GameObject obj in objectList)
				{
					if (obj)
						obj.SetActive(isTargetIndex);
				}
				break;
			
			default:
				MDebugLog($"{nameof(UpdateActive)}({Value}) - {option}, Invalid Option");
				break;
		}
	}
}

🎲 말꼬리


ObjectActiveList의 경우, ν”„λ‘œμ νŠΈλ₯Ό 진행함에 따라 GameObject 뿐만 μ•„λ‹ˆλΌ CanvasGroupκ³Ό MPickup도 ν•¨κ»˜ λ‹€λ£¨κ²Œ λ˜μ—ˆλŠ”λ°, μ΄λ²ˆμ— ꡬ쑰λ₯Ό λ°”κΎΈλ©΄μ„œ GameObject만 닀루도둝 μˆ˜μ •ν–ˆλ‹€.

CanvasGroup은 λ³„λ„μ˜ CanvasGroupListκ°€ μžˆμœΌλ‹ˆ μ΄κ²ƒμœΌλ‘œ λŒ€μ²΄ν•˜λ©΄ 되고, MPickup은 λ”± ν•œ ν”„λ‘œμ νŠΈμ—μ„œλ§Œ μ‚¬μš©ν–ˆλ˜ 터라, λ‹€μ‹œ ν•„μš”ν•˜κ²Œ λœλ‹€λ©΄ CanvasGroupList처럼 λ³„λ„μ˜ μš°λ™μ„ μƒˆλ‘œ λ§Œλ“€μ–΄μ•Ό ν•  것 κ°™λ‹€.

λŸ¬λ‚˜μ €λŸ¬λ‚˜ κ°„λ‹¨ν•œ λͺ©μ μ„ 가진 μš°λ™λ“€μ΄λΌ, ν”„λ‘œμ νŠΈλ§ˆλ‹€ κ·Έλ•Œκ·Έλ•Œ μƒˆλ‘œ λ§Œλ“€μ–΄λ„ 되긴 ν•˜μ§€λ§Œ..
자주 μ“°μ΄λŠ” κΈ°λŠ₯은 계속 λ‹€μ‹œ λ§Œλ“œλŠ” 것 λ³΄λ‹€λŠ” ν•œ 번 잘 λ§Œλ“€μ–΄μ„œ μž¬μ‚¬μš©ν•˜λŠ”κ²Œ μž‘μ—… 효율이 μ’‹λ‹€κ³  μƒκ°ν•΄μ„œ μ΄λ ‡κ²Œ μž‘μ€ λͺ¨λ“ˆλ“€μ„ ν•˜λ‚˜ν•˜λ‚˜ λ§Œλ“€κΈ° μ‹œμž‘ν–ˆλ‹€.

그런데 κ·Έλ ‡κ²Œ λ§Œλ“  κΈ°λŠ₯듀을 λ‹€μ‹œ λ””λ²¨λ‘­ν•˜λŠ” κ³Όμ •μ—μ„œ, 이전 ν”„λ‘œμ νŠΈλ“€μ˜ μš°λ™ μƒ˜ν”Œλ“€λ„ ν•¨κ»˜ μœ μ§€λ³΄μˆ˜ν•˜κ²Œ λ˜λŠ”λ°, μ΄λŸ¬λ‚˜ κ·Έλ•Œκ·Έλ•Œ λ˜‘κ°™μ€ κΈ°λŠ₯을 μƒˆλ‘œ λ§Œλ“œλŠ” 것 보닀 더 λ§Žμ€ μ‹œκ°„μ΄ μ“°κ²Œ λ˜λŠ” 것 같기도 ν•˜λ‹€.

λ³΄ν†΅μ˜ λΌμ΄λΈŒλŸ¬λ¦¬λ“€μ²˜λŸΌ ꡬ쑰가 μ•ˆμ •ν™”λ˜μ–΄, λ‹€λ₯Έ μ‚¬λžŒλ“€λ„ ꡬ쑰변경 걱정없이 편히 μ“Έ 수 있고, λ‚˜λŠ” μƒˆ κΈ°λŠ₯ κ°œλ°œμ— λ”μš± μ‹ κ²½μ“Έ 수 있으면 μ’‹κ² λ‹€.

v1.0.0을 ν–₯ν•΄.

To be Continued

이 κΈ°μ‚¬λŠ” μ €μž‘κΆŒμžμ˜ CC BY 4.0 λΌμ΄μ„ΌμŠ€λ₯Ό λ”°λ¦…λ‹ˆλ‹€.