[Unity6] 2D Behavior Tree 1( Patrol, Wait )

์—…๋ฐ์ดํŠธ:

์นดํ…Œ๊ณ ๋ฆฌ:

ํƒœ๊ทธ: ,




Behavior Tree

Behaviour Tree Manual
-ย Behaviour Tree - AI ์บ๋ฆญํ„ฐ์˜ ํ–‰๋™ ์ œ์–ด๋‚˜ ์ƒํƒœ ๊ด€๋ฆฌ๋ฅผ ์œ„ํ•ด ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
-ย Behaviour Tree๋Š” ๊ฒŒ์ž„ ๊ฐœ๋ฐœ์—์„œ ๋น„์„ ํ˜•์ ์ด๊ณ , ๋ณต์žกํ•œ AI ์˜์‚ฌ ๊ฒฐ์ •์„ ๊ฐ„๋‹จํ•˜๊ฒŒ ๋ชจ๋“ˆํ™”ํ•˜์—ฌ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค๋‹ˆ๋‹ค.
-ย Unity6๋ถ€ํ„ฐ Built-in์œผ๋กœ ์ง€์›, ๋…ธ๋“œ ๊ธฐ๋ฐ˜์œผ๋กœ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๊ฒŒ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.



Node

โœ” Action Node : ์‹ค์ œ ํ–‰๋™์„ ์ˆ˜ํ–‰ํ•˜๋Š” ๋…ธ๋“œ
ย ย  ํŠธ๋ฆฌ์˜ ํ•˜๋‹จ์— ์œ„์น˜, ์—์ด์ „ํŠธ๊ฐ€ ํ•ด์•ผ ํ•  ๊ตฌ์ฒด์ ์ธ ์ž‘์—… ์ •์˜
ย 
โœ” Modifier Node : ํŠธ๋ฆฌ์˜ ํ๋ฆ„์ด๋‚˜ ์‹คํ–‰ ์กฐ๊ฑด์„ ์ˆ˜์ •
ย ย  ํŠน์ • ์กฐ๊ฑด์„ ํ™•์ธ, ์‹คํ–‰ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜
ย 
โœ” Sequencing Node : ํ•ด๋‹น ๋…ธ๋“œ๊ฐ€ ์•„๋‹Œ ์ž์‹ ๋…ธ๋“œ๋“ค์„ ํŠน์ • ์กฐ๊ฑด์— ๋”ฐ๋ผ ์‹คํ–‰
ย ย  ๋ชจ๋“  ์ž์‹ ๋…ธ๋“œ๊ฐ€ ์„ฑ๊ณตํ•ด์•ผ ์ž์‹ ๋„ ์„ฑ๊ณต ์ƒํƒœ ๋ฐ˜ํ™˜
ย 
โœ” Join Node : ์—ฌ๋Ÿฌ ๋…ธ๋“œ์—์„œ ๋ฐœ์ƒํ•œ ๊ฒฐ๊ณผ๋ฅผ ๊ฒฐํ•ฉํ•ด ํ–‰๋™์„ ๊ฒฐ์ •ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ



์‚ฌ์šฉ๋ฒ•

1.ย Package Manager - Behavior install
Image
ย 
2.ย Project - Behavior Graph ์ƒ์„ฑ
Image
ย 
3.ย Behavior Graph ๋”๋ธ”ํด๋ฆญ - ์šฐํด๋ฆญ-> ๋…ธ๋“œ ์ถ”๊ฐ€



Behavior Graph

Image
BlackBoard : ์‚ฌ์šฉํ•˜๋Š” ๋ณ€์ˆ˜ ๋ชฉ๋ก, ๊ธฐ๋ณธ์ ์œผ๋กœ ์ž๊ธฐ ์ž์‹ ์„ ๋‚˜ํƒ€๋‚ด๋Š” Self ๋ณ€์ˆ˜ ์ƒ์„ฑ
ย BlackBoard์— ์„ ์–ธํ•œ ๋ณ€์ˆ˜๋Š” ์™ธ๋ถ€์—์„œ ์ ‘๊ทผ ๊ฐ€๋Šฅํ•˜๊ธฐ ๋•Œ๋ฌธ์— Insspector View์— ์ถœ๋ ฅ๋˜๋ฉฐ, ์Šคํฌ๋ฆฝํŠธ์—์„œ Get/Set ๊ฐ€๋Šฅ
ย 
๋…ธ๋“œ ์ƒ์„ฑ : ์šฐํด๋ฆญ - Add
ย Action -> Navigation -> Nav To Target(๋ชฉํ‘œ์—๊ฒŒ ์ด๋™), To Location, Patrol(์ˆœ์ฐฐ)์„ ์ œ๊ณต
Image



์ˆœ์ฐฐ(Patrols)

1.ย  ๋…ธ๋“œ ์ƒ์„ฑ : Add -> Action -> Navigation -> Patrol
2.ย  Agent - Self ๋“œ๋ž˜๊ทธ
3.ย  WayPoint ์„ค์ •(PatrolPoints)
BlackBoard์— List - GameObject List ์ถ”๊ฐ€, ๋“œ๋ž˜๊ทธ
ย 
Image
Image



๋Œ€๊ธฐ(Patrols)

1.ย  Add -> Flow -> Sequence
2.ย  Add -> Action -> Delay - Wait(Range)
3.ย  Add -> Flow -> TimeOut
ย 
1~3์ดˆ ๋Œ€๊ธฐ 10์ดˆ ์ˆœ์ฐฐ ๋ฐ˜๋ณต
Image





์ฝ”๋“œ

EnemyFSM

Prefab
Behavior Agent ์ปดํฌ๋„ŒํŠธ ์ถ”๊ฐ€
Behavior Agent Graph ๋“ฑ๋ก
ย 
EnemyFSM
behaviorAgent.SetVariableValue(โ€œPatrolPointsโ€, wayPoints.ToList());
Behavior Graph์˜ Blackboard์— ์„ ์–ธํ•œ ๋ณ€์ˆ˜ ๊ฐ’์„ ์„ค์ • (๋ณ€์ˆ˜๋ช… ๋˜‘๊ฐ™์ด)
Image

EnemyFSM
using System.Linq;
using UnityEngine;
using UnityEngine.AI;
using Unity.Behavior;

public class EnemyFSM : MonoBehaviour
{
	private	Transform			target;
	private	NavMeshAgent		navMeshAgent;
	private	BehaviorGraphAgent	behaviorAgent;

	public void Setup(Transform target, GameObject[] wayPoints)
	{
		this.target = target;

		navMeshAgent	= GetComponent<NavMeshAgent>();
		behaviorAgent	= GetComponent<BehaviorGraphAgent>();
		navMeshAgent.updateRotation = false;
		navMeshAgent.updateUpAxis = false;

		behaviorAgent.SetVariableValue("PatrolPoints", wayPoints.ToList());
	}
}



EnemySpawner

EnemySpawner WayPoint ์„ค์ •
EnemySpawner ์ ์˜ ์Šคํฐ ๊ฐ€๋Šฅ ์œ„์น˜ ์Šคํฐ, WayPoint(์ˆœ์ฐฐ๊ตฌ์—ญ)์„ ์ •ํ•˜์—ฌ Enemy ์Šคํฐ.
Image

EnemySpawner
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Tilemaps;

public class EnemySpawner : MonoBehaviour
{
	[SerializeField]
	private	Tilemap			tilemap;
	[SerializeField]
	private	GameObject		enemyPrefab;
	[SerializeField]
	private	Transform		target;
	[SerializeField]
	private	int				enemyCount = 10;

	private	Vector3			offset = new Vector3(0.5f, 0.5f, 0);
	private	List<Vector3>	possibleTiles = new List<Vector3>();

	[System.Serializable]
	private struct WayPointData
	{
		public GameObject[] wayPoints;
	}
	[SerializeField]
	private	WayPointData[]	wayPointData;

	private void Awake()
	{
		// Tilemap์˜ Bounds ์žฌ์„ค์ • (๋งต์„ ์ˆ˜์ •ํ–ˆ์„ ๋•Œ Bounds๊ฐ€ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š๋Š” ๋ฌธ์ œ ํ•ด๊ฒฐ)
		tilemap.CompressBounds();
		// ํƒ€์ผ๋งต์˜ ๋ชจ๋“  ํƒ€์ผ์„ ๋Œ€์ƒ์œผ๋กœ ์  ๋ฐฐ์น˜๊ฐ€ ๊ฐ€๋Šฅํ•œ ํƒ€์ผ ๊ณ„์‚ฐ
		CalculatePossibleTiles();

		// ์ž„์˜์˜ ํƒ€์ผ์— enemyCount ์ˆซ์ž๋งŒํผ ์  ์ƒ์„ฑ
		for ( int i = 0; i < enemyCount; ++ i )
		{
			int index	 = Random.Range(0, possibleTiles.Count);
			int wayIndex = Random.Range(0, wayPointData.Length);
			GameObject clone = Instantiate(enemyPrefab, possibleTiles[index], Quaternion.identity, transform);
			clone.GetComponent<EnemyFSM>().Setup(target, wayPointData[wayIndex].wayPoints);
		}
	}

	private void CalculatePossibleTiles()
	{
		BoundsInt	bounds	 = tilemap.cellBounds;
		// ํƒ€์ผ๋งต ๋‚ด๋ถ€ ๋ชจ๋“  ํƒ€์ผ์˜ ์ •๋ณด๋ฅผ ๋ถˆ๋Ÿฌ์™€ allTiles ๋ฐฐ์—ด์— ์ €์žฅ
		TileBase[]	allTiles = tilemap.GetTilesBlock(bounds);

		// ์™ธ๊ณฝ ๋ผ์ธ์„ ์ œ์™ธํ•œ ๋ชจ๋“  ํƒ€์ผ ๊ฒ€์‚ฌ
		for ( int y = 1; y < bounds.size.y-1; ++ y )
		{
			for ( int x = 1; x < bounds.size.x-1; ++ x )
			{
				// [y * bounds.size.x + x]๋ฒˆ์งธ ๋ฐฉ์˜ ํƒ€์ผ ์ •๋ณด๋ฅผ ๋ถˆ๋Ÿฌ์˜ด
				TileBase tile = allTiles[y * bounds.size.x + x];
				// ํ•ด๋‹น ํƒ€์ผ์ด ๋น„์–ด์žˆ์ง€ ์•Š์œผ๋ฉด ์  ๋ฐฐ์น˜ ๊ฐ€๋Šฅ ํƒ€์ผ๋กœ ํŒ๋‹จ
				if ( tile != null )
				{
					Vector3Int	localPosition	= bounds.position + new Vector3Int(x, y);
					Vector3		position		= tilemap.CellToWorld(localPosition) + offset;
					position.z = 0;
					possibleTiles.Add(position);
				}
			}
		}
	}
}




์ด๊ฒƒ์ €๊ฒƒ ๋ฉ”๋ชจ

BehaviorGraph ์ •๋ ฌ

๋…ธ๋“œ ์œ„ ์šฐํด๋ฆญ - Align - All Children (๋…ธ๋“œ ์ •๋ ฌ)
Image





์žก๋‹ด, ์ผ๊ธฐ?

์œ ๋‹ˆํ‹ฐ6 NavMesh, BehaviorTree




๐Ÿ“”

๋Œ“๊ธ€๋‚จ๊ธฐ๊ธฐ