์ด ๋๊น์ง ํ๋ ์ด์ด ์บ๋ฆญํฐ, ์กฐ๋ช ์ฒ๋ฆฌ, ๋ฐฐ๊ฒฝ ์ธํ ์์ ์ ์งํํ์๋ค. ์ค๋์ ํ๋ ์ด์ด๋ฅผ ๋ฐฉํดํ ๊ฒฝ๊ด(Police) ํจํด์ ๋ง๋ค์ด๋ณผ ์๊ฐ์ด๋ค.
๊ณตํฌ ๊ฒ์ ํน์ฑ์, ์ข ์ด๋์ ๋ณด์ฌ์ผ ํ๊ธฐ์ ์ ๋ํฐ ์คํ๋ผ์ดํธ ๋ ๋๋ฌ์์ ์๊ฐ์ ์ด๋ก๊ฒ ๋ฎ์ท๋ค. ์ด์ ์ฐจ๋ก๋๋ก ํ ๋ฒ ๋ง๋ค์ด๋ณด์.
1. ๊ฒฝ๊ด ์ธ๊ด ์ธํ ํ๊ธฐ
ํ์ ์ํ(Idle), ๊ฑท๊ธฐ(Walk), ์ธ๊ธฐ์ฒ ๊ฐ์ง(Feel) ์ ๋๋ฉ์ด์ ์ ๋ค๋ฅธ ํ์์ด ๋ง๋ค์ด์ค ์ํ๋ค. ๋ฐ๋ผ์, ์ด๋ฒ์๋ ๋ฐ๋ก ์ ๋๋ฉ์ด์ ์์ ์ ์ ํด๋ ๋๋ค. ๋ค๋ง, ์๋ ๊ธฐํ์ ์์๋ ์ด๋ฐ ๋ด์ฉ์ด ์์์ง๋ง ์ฐ๋ฆฌ๊ฐ ๊ฐ๋ฐํ๋ ๊ฒ์์ด ๊ณตํฌ๊ฒ์์ธํผ ์ข ๋ ํ์คํ ์ดํํธ๋ฅผ ์ฃผ๊ณ ์ถ์๋ค.
๊ฒฝ๊ด์ด ๋ค๊ณ ์๋ ์์ ๋ฑ์ ๋ผ์ดํธ(Light)๋ฅผ ๋ฌ์์คฌ๋ค. ํฐ์์ด๋ ๋ ธ๋์ ์กฐ๋ช ์ ์คํ๋ ค ํ๋ ์ด์ด๋ฅผ ๊ตฌํ๋ฌ ์จ ๊ฒฝ๊ด ๋๋์ด ๋ฌ๊ธฐ ๋๋ฌธ์ ์ฒ์ ๋ฑ ๋ดค์ ๋, "์, ์ ์ด๊ตฌ๋!"๋ผ๋ ๊ฑธ ๋๋ ์ ์๋๋ก ๋นจ๊ฐ์ ์กฐ๋ช ์ผ๋ก ํด์คฌ๋ค. ํ์ ์์ ์์ฐฐํ๋ฉฐ ๊ฑธ์ ๋๋ ์ ๋ฐ ๋ชจ์ต์ผ๋ก ๊ฑธ์ด๋ค๋๋ ๊ฒ์ด๋ค.
๊ทธ๋ฐ๋ฐ, ๋ ํ๋ ๊ณ ๋ฏผ์ด ์๊ฒผ์๋ค. ์ด๊ฒ ๋ํ, ๊ธฐํ์ ์์๋ ์๋ ๋ด์ฉ์ด์๋ค. ๋ง์ฝ ํ๋ ์ด์ด๊ฐ ์ ์๊ฒ ๋ค์ผฐ์ ๋, ๊ฒฝ๊ด์ด ์ถ์ ์ํ์ ์๋ค๋ ๊ฑธ ํ๋ ์ด์ด์๊ฒ ์ด๋ป๊ฒ ๊ฐ์ธ์์ผ์ค๊น?
์์ ๊ฐ์ด ํํํ๋ฉด ๋๊ฒ ์ค๊ฐ๋๊ณ ์ข์ง ์์๊น๋ ์๊ฐ์ด ๋ค์๋ค. ์์ง์ ์ง ์ด๋ฒคํธ๋ ๋ฌด๋น์ง ์ ๋๋ฌธ์์ ์ฌ์ฉํ๋ Freeform 2D Light๋ฅผ ๊ฒฝ๊ด์๊ฒ๋ ๋ฐฐ์นํด์คฌ๋ค. ํ์ ์์๋ ์ด ๋ถ๋น์ด ๋นํ์ฑํ ๋์ด ์๋ค๊ฐ, ๊ฒฝ๊ด์ด ์ถ์ ์ํ์ ๋ค์ด๊ฐ๋ฉด ์์ ๊ฐ์ด ํ์ฑํ๊ฐ ๋๋ ๊ฒ์ด๋ค.
์ด๋ ๊ฒ ํ๋ฉด, ์ธ๊ด ์ธํ ์ ๋๊ฐ ๋ค ํ ๊ฒ ๊ฐ๋ค.
2. ํ์ง ๋ฐ ์ถ๊ฒฉ ํ์ ์์ญ ์ค์ ํ๊ธฐ
๊ฒฝ๊ด ๋ํ ๋ฌผ๋ฆฌํจ๊ณผ๋ฅผ ๋ฐ์์ผ ํ๊ธฐ์ Rigidbody 2D ์ปดํฌ๋ํธ๋ฅผ ๋ฌ์์คฌ๋ค. ๊ทธ๋ฆฌ๊ณ ๋ณธ๋ ๋ชธ์ฒด, ๊ฒฝ๊ด์ ๋ท ๋ถ๋ถ์ ํ์ง์ฉ, ๊ฒฝ๊ด์ ์ ๋ถ๋ถ ํ๋ ์ด์ด ๋ฐ๊ฒฌ, ๊ฒ์์ค๋ฒ ์์ญ์ ์ฌ์ฉํ ์ฝ๋ผ์ด๋ ์ด๋ ๊ฒ 4๊ฐ๊ฐ ํ์ํ๋ค.
๋ฌผ๋ก , ๋ณธ์ฒด ์ฝ๋ผ์ด๋๋ฅผ ์ ์ธํ๊ณ ๋ ๊ฐ ์ฝ๋ผ์ด๋๋ง๋ค ํธ๋ฆฌ๊ฑฐ ์ด๋ฒคํธ๋ฅผ ๋ฐ์์ํฌ ์์ ์ด๊ธฐ์ ๋น ์ค๋ธ์ ํธ๋ค์ ๋ง๋ค์ด์ ๊ฐ๊ฐ์ ๋ฐฐ์นํด์คฌ๋ค. ๋ณธ์ฒด ์ฝ๋ผ์ด๋ ๋นผ๊ณ ์ ๋ถ "is Trigger"๋ฅผ ํ์ฑํํด์ฃผ๋ฉด ๋๋ค. ์์ง ๊ฒ์ ์ค๋ฒ ํ๋ฉด ๋ฆฌ์์ค๊ฐ ์์ฑ๋์ง ์์๊ธฐ ๋๋ฌธ์ ์ฌ๊ธฐ์ ๊ฒ์ ์ค๋ฒ๊น์ง๋ ๋ค๋ฃจ์ง ์์ ์์ ์ด๋ค.
์ด์ ์คํฌ๋ฆฝํธ ์์ฑํ๋ ์ผ๋ง ๋จ์๋ค.
3. ๊ฒฝ๊ด ํจํด ๋ง๋ค๊ธฐ
๋จผ์ , ์ด๋ค ํจํด๋ค์ ๋ง๋ค์ด์ผ ํ๋์ง ํ ๋ฒ ๋ฆฌ์คํธ๋ฅผ ๋์ดํด๋ดค๋ค.
- ํ์ ์ํ์ผ ๋, ๋๋คํ๊ฒ ์ด๋ฆฌ ์ ๋ฆฌ ๋ค๋๋ฉฐ ์์ฐฐ
- ๊ฒฝ๊ด ๋ค์ ํ๋ ์ด์ด๊ฐ ์ผ์ ๊ฑฐ๋ฆฌ ๋ด์ ๋ค์ด์จ๋ค๋ฉด, ์ธ๊ธฐ์ฒ์ ๋๋ผ๊ณ ์ ๊น ๋จธ๋ญ๊ฑฐ๋ฆฌ๋ค๊ฐ ๋ค๋์๋ด
- ๋ค๋์๋ดค๋๋ฐ ํ๋ ์ด์ด๊ฐ ์๋ค๋ฉด ๋ค์ ํ์ ์ํ, ์๋ค๋ฉด ์ถ๊ฒฉ ์์
- ๊ฒฝ๊ด ์์ ํ๋ ์ด์ด๊ฐ ์ผ์ ๊ฑฐ๋ฆฌ ๋ด์ ๋ค์ด์จ๋ค๋ฉด, ์ถ์ ์ํ๋ก ๋ณํ์ฌ ํ๋ ์ด์ด ์ถ๊ฒฉ ์์
- ํ๋ ์ด์ด๊ฐ ์ถ๊ฒฉ ๋นํ๋ ๋์ค์ ์ค๋ธ์ ํธ ๋ค์ ์จ์๋ค๋ฉด, ๊ฒฝ๊ด์ ํ๋ ์ด์ด๋ฅผ ์ฐพ์ ์ด๋ฆฌ์ ๋ฆฌ ๋ค๋
- ํ๋ ์ด์ด๊ฐ ์จ๊ณ ๋์, ์ผ์ ์๊ฐ์ด ์ง๋๋ฉด ๊ฒฝ๊ด์ ๋ค์ ํ์ ์ํ๋ก ๋์์์ ์์ฐฐ ์์
- "ํ๋ ์ด์ด๊ฐ ๊ฒฝ๊ด์๊ฒ ์กํ๋ฉด ๊ฒ์ ์ค๋ฒ" ํจํด์ ๋์ค์ ๋ฆฌ์์ค๊ฐ ๋ง๋ค์ด์ง๋ฉด ๊ทธ ๋ ๊ตฌํ
์ด๋ ๊ฒ ๋์ด๋ง ํด๋ดค๋๋ฐ, ๋ฒ์จ ๋จธ๋ฆฌ๊ฐ ์ํ ๋ค. ์ด๋ป๊ฒ ๋จผ์ ๊ตฌ์กฐ ํ์ ์ก๊ณ ์์ํด์ผํ ๊น ๊ณ ๋ฏผํ๋ค๊ฐ ํ์์ด ๋์์ธ ํจํด ์ค์ ์ํ ํจํด์ด๋ ๊ฑธ ์ฐพ์๋ค๊ณ ๋งํด์คฌ๋ค. ๊ทธ๋์, ๊ทธ๊ฑธ ๋จผ์ ๊ณต๋ถํ๊ณ ๋ฐ๋ผํด๋ณด๋ฉฐ ๋ง๋ค๊ธด ํ๋๋ฐ ์ด๋ ๊ฒ ํ๋ ๊ฒ ๋ง๋์ง๋ ์์งํ ์๋ฌธ์ด๋ค...ใ ใ ใ
/* < ์ฐธ๊ณ ํ ๊ณต๋ถ ๋ธ๋ก๊ทธ> (๊ณต๋ถํ๋ ์๋นต๋ง ๋), (๊ธ๋ฆญ์ ๋ง๋ค์ด๊ฐ๋ ์ธ์ ๋), (๊น์ ๋ฏผ ๋ฒจ๋ก๊ทธ ๋) */
StateMachine.cs
์ํ๋ค๋ ์๋ช ์ฃผ๊ธฐ๊ฐ ์๋ค๋ ๊ธ์ ๋ณด๊ณ ๋จผ์ ์ธํฐํ์ด์ค๋ฅผ ์์ฑํ๋ค. Enemy๋ ๊ฒฝ๊ด์ ๋ถ์ฐฉํ ํด๋์ค๋ค.
public interface IState
{
void StateEnter(Enemy enemy);
void StateFixedUpdate(Enemy enemy);
void StateUpdate(Enemy enemy);
void StateExit(Enemy enemy);
}
์ํ์ ๋ค์ด๊ฐ์ ๋(StateEnter), ๋ฌผ๋ฆฌ ์ฐ์ฐ๊ณผ ๊ด๋ จ๋ ๋ก์ง์ ์ฌ์ฉํ๋ ์ํ(StateFixedUpdate), ๋งค ํ๋ ์๋ง๋ค ์คํ๋๋ ์ํ(StateUpdate), ์ํ๋ฅผ ๋ฒ์ด๋ ๋ ์คํ(StateExit) ์ด๋ ๊ฒ ๊ตฌ์ฑํ๋ค. ๊ทธ๋ฆฌ๊ณ ์ด๋ฐ ์ํ๋ค์ ์ ์ดํด์ค StateMachine ํด๋์ค๋ฅผ ๋ง๋ค์๋ค.
public class StateMachine
{
public IState currentState { get; private set; }
public StateMachine(Enemy enemy, IState defaultState)
{
currentState = defaultState;
currentState.StateEnter(enemy);
}
public void SetState(Enemy enemy, IState state)
{
if (currentState == null || currentState == state)
{
Debug.Log("์ํ๋ฅผ ๋ณ๊ฒฝํ ์ ์์ต๋๋ค.");
return;
}
currentState.StateExit(enemy);
currentState = state;
currentState.StateEnter(enemy);
}
public void UpdateState(Enemy enemy)
{
currentState.StateUpdate(enemy);
}
public void FixedUpdateState(Enemy enemy)
{
currentState.StateFixedUpdate(enemy);
}
}
์ด๊ฑธ ๋ฐํ์ผ๋ก ์ํ ํด๋์ค๋ค์ ์ฑ๊ธํค ํจํด์ผ๋ก ์ ์ํ์๋ค. (์ํ ์ ํ๋ง๋ค ์๋ก์ด ๊ฐ์ฒด ์์ฑ์ ๊ฐ๋น์ง๋ผ๊ณ ํ๋จ)
public class Idle : IState // ํ์ ์ํ
{
private static Idle Instance = new Idle();
private Idle() { }
public static Idle GetInstance() { return Instance; }
...
}
public class FeelStrange : IState // ์ธ๊ธฐ์ฒ์ ๋๋ผ๋ ์ํ
{
private static FeelStrange Instance = new FeelStrange();
private FeelStrange() { }
public static FeelStrange GetInstance()
{
return Instance;
}
...
}
public class ChaseState : IState // ์ถ๊ฒฉ ์ํ
{
private static ChaseState Instance = new ChaseState();
private ChaseState() { }
public static ChaseState GetInstance()
{
return Instance;
}
...
}
๊ทธ๋ฆฌ๊ณ ๊ฐ ์ํ ์์์ ๊ฒฝ๊ด์ด ํด์ผํ ๊ธฐ๋ฅ๋ค์ ์คํํ๋ ๋ก์ง์ ๋ง๋ค์ด์ฃผ๋ฉด ๋๋ค. ํ๋ํ๋ ๋ค ์ ์ด๊ฐ๋ฉฐ ์ค๋ช ํ์๋, ๊ธ์ด ๋๋ฌด ๊ธธ์ด์ง ๊ฒ ๊ฐ์๊ณ ์ฝ๊ฒ ์ค๋ช ํ๊ธฐ์๋ ์กฐ๊ธ ๋ฒ ์ฐจ๋ณด์ฌ์ ๊ฐ๋จํ ์ ์๋ค.
Enemy.cs์ ์ ์ํ ํด๋์ค๋ค์ ๋ด๋ถ ๋ด์ฉ๋ค์ ์๋ตํ๋ค. /* ์์ธํ ๋ด๋ถ ์ฝ๋๋ ๋์ค์ ์ด ๊ธ์๋ค๊ฐ ๊นํ๋ธ ์ฃผ์๋ฅผ ์ฌ๋ฆฌ๋๋ก ํ๊ฒ ์ต๋๋ค. */
๊ทธ๋๋ ํ์ง ์ฝ๋ผ์ด๋์ ์ถ๊ฒฉ ์ฝ๋ผ์ด๋ ๋ก์ง์ ๋ด๋นํ๋ Detect.cs์ Chase.cs ์ ๋๋ ์ ์ ์ ์์ ๊ฒ ๊ฐ๋ค.
Detect.cs
Enemy ํด๋์ค ๋ด๋ถ์ StateMachine ๊ฐ์ฒด๋ฅผ ์์ฑํด๋์ ๊ฒ ์๋ค. ๊ทธ๊ฑธ ์ฐธ๊ณ ํ์ฌ ๋ด์ฃผ๊ธธ ๋ฐ๋๋ค.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Detect : MonoBehaviour
{
public Enemy enemy;
private void OnTriggerEnter2D(Collider2D collision)
{
Find(collision);
}
private void OnTriggerStay2D(Collider2D collision)
{
Find(collision);
}
private void Find(Collider2D collision)
{
if (collision != null)
{
SujiController suji = collision.GetComponent<SujiController>();
bool findSuji = collision.gameObject.CompareTag("Player") && !suji.IsHiding;
if (findSuji && enemy.StateMachine.currentState != ChaseState.GetInstance())
{
enemy.StateMachine.SetState(enemy, FeelStrange.GetInstance());
}
}
}
}
๊ฒฝ๊ด์ด ์ถ๊ฒฉ ์ํ์ผ ๋๋ ๋ค๋์๋ณผ ํ์๊ฐ ์๊ณ , ํ๋ ์ด์ด ์บ๋ฆญํฐ๊ฐ ์จ์ง ์์์ ๋ ์๋ํด์ผ ํ๋ค. ํ๊ทธ๋ ๋ ์ด์ด ์ค์ ์ด๋ค ๊ฑธ๋ก ํ๋ ์ด์ด๋ฅผ ๊ฐ๋ณํด๋ผ๊น ๊ณ ๋ฏผํ๋ค๊ฐ ์ฐ์ ํ๊ทธ๋ฅผ ์ฌ์ฉํ๋ค.
OntriggerStay2D()์๋ ๊ฐ์ ๊ธฐ๋ฅ์ ๋ฃ์ด์ค์ผ๋ก์จ, ๋ฒ๊ทธ๋ฅผ ์์ ํ๋ค. ๊ฒฝ๊ด ๋ค์ ๊ฐ์ ์จ๊ณ ๋ค์ ๋์์ ๋ค์ ๊ฐ๋ฉด ์๋ํ์ง ์๋ ๋ฒ๊ทธ๊ฐ ์์๋ค.
Chase.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Chase : MonoBehaviour
{
public Enemy enemy;
private void OnTriggerEnter2D(Collider2D collision)
{
ChaseTarget(collision);
}
private void OnTriggerStay2D(Collider2D collision)
{
ChaseTarget(collision);
}
private void ChaseTarget(Collider2D collision)
{
bool findSuji = collision != null && collision.gameObject.CompareTag("Player");
if (findSuji)
{
SujiController suji = collision.gameObject.GetComponent<SujiController>();
bool isNotHide = suji != null && !suji.IsHiding;
if (isNotHide)
{
// ์์ง ์ถ๊ฒฉ์ ์์
ChaseState state = ChaseState.GetInstance();
state.SetTarget(collision.gameObject.transform);
enemy.StateMachine.SetState(enemy, state);
}
}
}
}
๋น์ทํ ๊ตฌ์ฑ์ด๋ผ์ ์ดํดํ๊ธฐ ์ฌ์ธ ๊ฒ์ผ๋ก ์๊ฐ๋๋ค. ์ฐจ์ด์ ์ด๋ผ๋ฉด ์ถ๊ฒฉ์ ์์ํด์ผ ํ๋ฏ๋ก, ํ๊ฒ ๋์์ ์ง์ ํด์ฃผ๋ ๋ถ๋ถ์ด ์๋ ๊ฒ์ ๋ณผ ์ ์๋ค.
์์ฑ
๋ฒ๊ทธ๋ ๋ง์ด ๋์ค๊ณ , ์๋ก์ด ๊ตฌ์กฐ ์ค๊ณ์ ๋์ ํด๋ณด๋ฉฐ ํ ๊ฑฐ๋ผ ํ๋ฃจ์ข ์ผ ์ฝ๋ฉํ๊ธฐ๋ ํ๋ค. ๊ฝค๋ ์ด๋ ต๊ณ ํ๋ค์๋ ๋ถ๋ถ์ธ๋ฐ ๊ทธ๋๋ ์ ๋์์ค์ ๋๋ฌด ๊ธฐ๋ปค๋ค.
๊ณผ์ ์ ์ ๋ถ ์ ๊ณ ์ถ์์ผ๋, ๋ด์ฉ์ด ๋งค์ฐ ๊ธธ์ด์ง ๊ฒ ๊ฐ์์ ์ด๋ ๊ฒ ๋ฐ์ ์ค๋ช ํ์ง ๋ชปํ ๋ถ๋ถ์ด ๋งค์ฐ ์์ฌ์ ๋ค.
๊ฒฝ๊ด์ด ๋ค๋์ ๋ดค์ ๋, ์์ ๊ฒฝ์ฐ ๊ทธ๋ฅ ๋ค์ ๊ฐ๋ ์์๋ ์ฐ๊ณ ์ถ์๋๋ฐ ์ข์ฒ๋ผ ๊ฐ์ด ์ ๋์ค์ง ์์ ํฌ๊ธฐํ๋ค....ใ ใ