์๋ ๊ธ์ ์ฐธ๊ณ ํ์ต๋๋ค!
[Unity2D] ์ ๋ชฌ์คํฐ ๊ตฌํํ๊ธฐ
[์ ๋ํฐ ๊ธฐ์ด - B18] ๋ชฌ์คํฐ AI ๊ตฌํํ๊ธฐ [์ ๋ํฐ ๊ธฐ์ด - B19] ํ๋ ์ด์ด ํผ๊ฒฉ ์ด๋ฒคํธ ๊ตฌํํ๊ธฐ ์์์ ๋ณด๊ณ ์ฐ์ฌ์ง ๊ฒ์๊ธ ์ ๋๋ค. ์ด๋ฒ์๊ฐ์๋ ๋๋์ด ๋ชฌ์คํฐ์ ๋ํ ๊ตฌํ์ ํ๋ฒ ํด๋ณด๋๋ก ํ ํ ๋ฐ
iagreebut.tistory.com
๋ชฌ์คํฐ AI ๊ตฌํํ๊ธฐ
1) ์ค๋น
์ ๋๋ฉ์ด์ ๋ฑ์ ๋ฃ์ ๋ชฌ์คํฐ๋ฅผ ์ค๋นํด์ค๋๋ค.
[์ ๋ํฐ] 2D๋ํธ ์บ๋ฆญํฐ ์ ๋๋ฉ์ด์
Box Collider 2D ๋ฅผ ์ถ๊ฐํฉ๋๋ค. ํฌ๊ธฐ๋ฅผ ์กฐ์ ํด์ค๋๋ค. ๋ฌผ๋ฆฌ๋ฅผ ์ ์ฉ ์์ผ์ผ ํ๋ฏ๋ก (์ค๋ ฅ ๋ฐ๊ธฐ ์ํจ) ์บ๋ฆญํฐ์ rigid body 2D๋ฅผ ์ถ๊ฐํฉ๋๋ค. ๊ทธ๋ผ ์ ์๋ํ๋์ง ํ์ธํ๊ธฐ ์ํด, ๊ฒ์์ ์์ํ์ ๋ ์์
avoc-o-d.tistory.com
2) ๋ชฌ์คํฐ ๊ธฐ๋ณธ ์ด๋
์คํฌ๋ฆฝํธ๋ฅผ ์์ฑ ํ, Monster์ ์ ์ฉ

์๋ ฅ์ ์ฃผ์ด์ผ ํ๋ฏ๋ก Rigidbody2D๋ฅผ ์ฌ์ฉํ๊ณ
์ ์ ํค๋ณด๋ ์ ๋ ฅ์ ํตํด ์์ง์ด๋๊ฒ ์๋๊ธฐ ๋๋ฌธ์, ์ผ๋จ ์๊ธฐ๊ฐ ์์์ ์ผ์ชฝ ๋ฐฉํฅ์ผ๋ก๋ง ์ด๋ํ๋๋ก ํด๋ณด๊ฒ ์ต๋๋ค.
Rigidbody2D rigid;
private void Awake() {
rigid = GetComponent<Rigidbody2D>();
}
void FixedUpdate()
{
rigid.velocity = new Vector2(-1, rigid.velocity.y); // ์ผ์ชฝ์ผ๋ก๋ง ์ด๋
}
3) ํ๋ ์ค์ (์ธ๊ณต์ง๋ฅ ๋ถ์ฌ)
์ ์ ํ ์ชฝ ๋ฐฉํฅ์ผ๋ก๋ง ๊ฐ๋ ๊ฒ ์๋๋ผ,
์ค์ค๋ก ํน์ ๊ตฌ๊ฐ์ ๋ฐฐํํ๊ฑฐ๋ ์ค๊ฐ์ ์ฌ๋ ๋ฑ์ ํ์๋ฅผ ํ๋๋ก ํ๋์ ์ค์ ํด์ฃผ์ด์ผ ํฉ๋๋ค.
๊ทธ๋ผ ๊ธฐํ ๋จ๊ณ๋ฅผ ๊ฑฐ์ณ์ ๋ชฌ์คํฐ๊ฐ ์ด๋ค ํ๋์, ์ด๋ค ํ์ด๋ฐ์ ํ ์ง, ์ด๋ค ๊ตฌ๊ฐ์ ๋์๋ค๋์ง์ ๋ํ์ฌ ๊ธฐํ์ ํด์ฃผ์ธ์!
์๋ ์ฝ๋์์๋ ์ค๋ฅธ์ชฝ ์ด๋ / ์ผ์ชฝ ์ด๋ / ๋ฉ์ถค ์ผ๋ก ์ค์ ํ๊ฒ ์ต๋๋ค.
๋๋ค ํจ์(Random.Range(min,max))๋ฅผ ์ด์ฉํด -1 / 0 / 1 ๋์๋ฅผ ์์ฑ ํ,
์ด๋ฅผ Vector2์ x๊ฐ์ผ๋ก ๋ถ์ฌํด์ ๊ฐ๊ฐ -1: ์ผ์ชฝ, 0: ์ ์ง, 1: ์ค๋ฅธ์ชฝ ์ผ๋ก ์ด๋ํ๋๋ก ํฉ๋๋ค.
// min <= ๋์ < max
Random.Range(min, max);
// ๋๋ ์ด๋ฅผ ๋ฃ์ ์ฌ๊ท ํธ์ถ
Invoke("ํจ์ ์ด๋ฆ", delay sec);
๐ Think() ์๋ ์ฝ๋ ๊ฒฐ๊ณผ, 5์ด์ ํ ๋ฒ์ฉ ์ด๋ ๋ฐฉํฅ์ผ๋ก ๊ฐ ์ง ์ ํ์ฌ ์์ง์ด๊ฒ ๋ฉ๋๋ค.
๐ Update ๋์ , FixedUpdate๋ฅผ ์ฐ๋ ์ด์ !
๋ฌผ๋ฆฌ ํจ๊ณผ๊ฐ ์ ์ฉ๋(Rigidbody) ์ค๋ธ์ ํธ๋ฅผ ์กฐ์ ํ ๋ ์ฌ์ฉ๋ฉ๋๋ค.
Update๋ ๋ถ๊ท์นํ ํธ์ถ์์ผ๋ก ๋ฌผ๋ฆฌ์์ง ์ถฉ๋๊ฒ์ฌ ๋ฑ์ด ์ ๋๋ก ์๋ ์ ์๊ธฐ ๋๋ฌธ์ ๋๋ค.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MonsterMove : MonoBehaviour
{
Rigidbody2D rigid;
public int nextMove; // ๋ค์ ํ๋ ์งํ
private void Awake() {
rigid = GetComponent<Rigidbody2D>();
Invoke("Think", 5); // ์คํ๋ ๋ ๋ง๋ค(์ต์ด 1ํ) nextMove๋ณ์๊ฐ ์ด๊ธฐํ ๋๋๋ก ํจ
}
void FixedUpdate()
{
rigid.velocity = new Vector2(nextMove,rigid.velocity.y); // nextMove ์ผ์ชฝ(-1) | ๋ฉ์ถค(0) | ์ค๋ฅธ์ชฝ(1) ์ผ๋ก ์ด๋
}
// ๋ชฌ์คํฐ๊ฐ ์ค์ค๋ก ํ๋จ
void Think(){
nextMove = Random.Range(-1, 2); // (์ผ์ชฝ ์ด๋(-1) | ๋ฉ์ถค(0) | ์ค๋ฅธ์ชฝ ์ด๋(1) ์ผ๋ก 3๊ฐ์ง ํ๋ ํ๋จ)
Invoke("Think", 5); // 5์ด์ ๋๋ ์ด๋ฅผ ๋ถ์ฌํ์ฌ ์ฌ์คํ
}
}
๐ค๊ทธ๋ ์ง๋ง,,, ์ด๋๋ก๋ฉด 5์ด๊ฐ ์ง๋ ์ดํ์๋ ์ค๋ฅธ์ชฝ ๋ฐฉํฅ์ผ๋ก ๊ณ์ ์ด๋ํ๊ฒ ๋์์ ๋, ๊ฒฐ๊ตญ.. ๋งต ๋ฐ์ผ๋ก ํ์ถํด๋ฒ๋ฆฌ๋ ์๊ฐ ์์ผ๋ ์ด๋ฅผ ๋ง์์ฃผ๋๋ก ํฉ์๋ค...
4) ์ง๋ฅ ๋์ด๊ธฐ
RayHit๋ฅผ ์ด๋ํ์ฌ ์์ ์ ์์ด ๋ญ๋ ๋ฌ์ง์ธ์ง ์ฒดํฌํ ํ, ๋ญ๋ ๋ฌ์ง๋ฅผ ๋ง๋๋ฉด ๋ฐ๋ ๋ฐฉํฅ์ผ๋ก ๋๋๋ก ๋ง๋ค์ด๋ด ์๋ค.
!!!๋ญ๋ ๋ฌ์ง๋ฅผ ๋ง๋๋ฉด ๋ฐ๋ก ๋ฐ๋ํธ์ผ๋ก ๋ค๋ ์ ์๋๋ก!!! ์์ง์ผ ๋๋ง๋ค ์ธก์ ํ๋๋ก ํฉ๋๋ค.
๐ ์งํ layer๋ฅผ Platform์ผ๋ก ์ค์
์บ๋ฆญํฐ ์์ผ๋ก ray๋ฅผ ์๋ฉฐ, ์๊ณ ์๋ ๊ณณ์ด ์งํ์ธ์ง ์ฒดํฌํ๊ธฐ ์ํจ์ ๋๋ค.
LayerMask.GetMask("Platform") ๋ฅผ ํตํด layer๊ฐ Platform์ธ ์งํ์ ํ์ธํ ์ ์๋๋ก ์ค์ ํด์ค๋๋ค.

void FixedUpdate()
{
// nextMove ์ผ์ชฝ(-1) | ๋ฉ์ถค(0) | ์ค๋ฅธ์ชฝ(1) ์ผ๋ก ์ด๋
rigid.velocity = new Vector2(nextMove, rigid.velocity.y);
// ์์ ์ ํ ์นธ ์ ์งํ์ ํ์ํ๊ธฐ ์ํด, position.x + nextMove (-1, 1, 0 ์ด๋ฏ๋ก ์ ์ ํจ)
Vector2 frontVec = new Vector2(rigid.position.x + nextMove, rigid.position.y);
// ํ์นธ ์ ๋ถ๋ถ์๋ ์ชฝ์ผ๋ก ray๋ฅผ ์จ
Debug.DrawRay(frontVec, Vector3.down, new Color(0,1,0));
// ๋ ์ด๋ฅผ ์ด์ ๋ง์ ์ค๋ธ์ ํธ๋ฅผ ํ์ง
RaycastHit2D raycast = Physics2D.Raycast(frontVec, Vector3.down,1,LayerMask.GetMask("Platform"));
// ํ์ง๋ ์ค๋ธ์ ํธ๊ฐ null : ๊ทธ ์์ ์งํ์ด ์์ => ๋ค๋๋๋ก
if(raycast.collider == null){
nextMove = nextMove * (-1); // ์ง์ ๋ฐฉํฅ์ ๋ฐ๊พธ์ด ์ฃผ์์ผ๋ Think๋ ์ ์ ๋ฉ์ถ์ด์ผ ํจ
CancelInvoke(); // think๋ฅผ ์ ์ ๋ฉ์ถ ํ ์ฌ์คํ
Invoke("Think", 5);
}
}
๐ ๋ญ๋ ๋ฌ์ง ํ์ง ๊ฑฐ๋ฆฌ ์ค์ด๊ธฐ
ray๊ฐ 1์นธ ์์ ๋ด๋ค๋ณด๋ ๊ฒ์ด ๋๋ฌด ๋ฉ๋ค๋ฉด, 0.4์ ๋ ์์ ํ์งํ๋๋ก ์ ์ ํ ์๋ฅผ ๊ณฑํด์ฃผ์๋ฉด ๋ฉ๋๋ค.
Vector2 frontVec = new Vector2(rigid.position.x + nextMove * 0.4f, rigid.position.y);
๐ ์๊ฐํ๋ ์๊ฐ ๋๋ค์ผ๋ก ๋ถ์ฌ
5์ด ๋ง๋ค ์๊ฐํ๊ธฐ๋ณด๋ค๋ ์๊ฐํ๋ ์๊ฐ๋ ๋๋ค์ผ๋ก ๋ถ์ฌํ๋ฉด ๋์ฑ ๋ ๋๋ํด๋ณด์ด๋ ์ธ๊ณต์ง๋ฅ์ด ๋ ๊ฒ์ ๋๋ค!!
void Think(){
...
float time = Random.Range(2f, 5f); //์๊ฐํ๋ ์๊ฐ์ ๋๋ค์ผ๋ก ๋ถ์ฌ
Invoke("Think", time); //๋งค๊ฐ๋ณ์๋ก ๋ฐ์ ํจ์๋ฅผ time์ด์ ๋๋ ์ด๋ฅผ ๋ถ์ฌํ์ฌ ์ฌ์คํ
}
์ ์ฒด ์ฝ๋
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MonsterTest : MonoBehaviour
{
Rigidbody2D rigid;
public int nextMove; //๋ค์ ํ๋์งํ๋ฅผ ๊ฒฐ์ ํ ๋ณ์
float time; // ์๊ฐํ๋ ์๊ฐ์ ๋๋ค์ผ๋ก ๋ถ์ฌ
void Start()
{
rigid = GetComponent<Rigidbody2D>();
// ๋งจ ์ฒ์๋ถํฐ ์๊ฐ
Invoke("Think", 1f);
}
void Update()
{
rigid.velocity = new Vector2(nextMove, rigid.velocity.y); // nextMove ๋ฉ์ถค(0), ์ผ์ชฝ(-1), ์ค๋ฅธ์ชฝ(1)์ผ๋ก ์ด๋
// ๋ญ๋ ๋ฌ์ง ์ ์ด
Vector2 frontVec = new Vector2(rigid.position.x + nextMove, rigid.position.y); // ์์ ์ฒดํฌ
Debug.DrawRay(frontVec, Vector3.down, new Color(0, 1, 0)); // ๋น ์์ ์์น, ์๋ ๋ฐฉํฅ, ์๊น
RaycastHit2D rayHit = Physics2D.Raycast(frontVec, Vector3.down, 1, LayerMask.GetMask("Platform")); // Platform Layer์ ๋น ์๊ธฐ
if (rayHit.collider == null) // ๊ฐ ๊ณณ์ด ์๋ค๋ฉด
{
nextMove = nextMove * (-1); // ์ง์ ๋ฐฉํฅ์ ๋ฐ๊พธ์ด ์ฃผ์์ผ๋ Think๋ ์ ์ ๋ฉ์ถ์ด์ผ ํจ
CancelInvoke(); // think๋ฅผ ์ ์ ๋ฉ์ถ ํ ์ฌ์คํ
time = Random.Range(2f, 5f); // ์๊ฐํ๋ ์๊ฐ์ ๋๋ค์ผ๋ก ๋ถ์ฌ
Invoke("Think", time);
}
}
void Think()
{
// ๋ชฌ์คํฐ๊ฐ ์ค์ค๋ก ์๊ฐํด์ ํ๋จ (์ผ์ชฝ(-1), ์ค๋ฅธ์ชฝ(1), ๋ฉ์ถค(0) ์ผ๋ก 3๊ฐ์ง ํ๋์ ํ๋จ)
nextMove = Random.Range(-1, 2);
time = Random.Range(2f, 5f); // ์๊ฐํ๋ ์๊ฐ์ ๋๋ค์ผ๋ก ๋ถ์ฌ
Invoke("Think", time); // ๋งค๊ฐ๋ณ์๋ก ๋ฐ์ ํจ์๋ฅผ time์ด์ ๋๋ ์ด๋ฅผ ๋ถ์ฌํ์ฌ ์ฌ์คํ
}
}
์ ๋๋ฉ์ด์ ์ถ๊ฐ
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MonsterTest : MonoBehaviour
{
Rigidbody2D rigid;
public int nextMove; //๋ค์ ํ๋์งํ๋ฅผ ๊ฒฐ์ ํ ๋ณ์
float time; // ์๊ฐํ๋ ์๊ฐ์ ๋๋ค์ผ๋ก ๋ถ์ฌ
Animator animator; // ์ ๋๋ฉ์ด์
void Start()
{
rigid = GetComponent<Rigidbody2D>();
// ๋งจ ์ฒ์๋ถํฐ ์๊ฐ
Invoke("Think", 1f);
animator = GetComponent<Animator>();
}
void Update()
{
rigid.velocity = new Vector2(nextMove, rigid.velocity.y); // nextMove ๋ฉ์ถค(0), ์ผ์ชฝ(-1), ์ค๋ฅธ์ชฝ(1)์ผ๋ก ์ด๋
// ๋ญ๋ ๋ฌ์ง ์ ์ด
Vector2 frontVec = new Vector2(rigid.position.x + nextMove, rigid.position.y); // ์์ ์ฒดํฌ
Debug.DrawRay(frontVec, Vector3.down, new Color(0, 1, 0)); // ๋น ์์ ์์น, ์๋ ๋ฐฉํฅ, ์๊น
RaycastHit2D rayHit = Physics2D.Raycast(frontVec, Vector3.down, 1, LayerMask.GetMask("Platform")); // Platform Layer์ ๋น ์๊ธฐ
if (rayHit.collider == null) // ๊ฐ ๊ณณ์ด ์๋ค๋ฉด
{
nextMove = nextMove * (-1); // ์ง์ ๋ฐฉํฅ์ ๋ฐ๊พธ์ด ์ฃผ์์ผ๋ Think๋ ์ ์ ๋ฉ์ถ์ด์ผ ํจ
CancelInvoke(); // think๋ฅผ ์ ์ ๋ฉ์ถ ํ ์ฌ์คํ
time = Random.Range(2f, 5f); // ์๊ฐํ๋ ์๊ฐ์ ๋๋ค์ผ๋ก ๋ถ์ฌ
Invoke("Think", time);
}
}
void Think()
{
// ๋ชฌ์คํฐ๊ฐ ์ค์ค๋ก ์๊ฐํด์ ํ๋จ (์ผ์ชฝ(-1), ์ค๋ฅธ์ชฝ(1), ๋ฉ์ถค(0) ์ผ๋ก 3๊ฐ์ง ํ๋์ ํ๋จ)
nextMove = Random.Range(-1, 2);
// ์ ๋๋ฉ์ด์
animator.SetFloat("DirX", nextMove);
animator.SetBool("IsWalk", nextMove == 0 ? false : true);
time = Random.Range(2f, 5f); // ์๊ฐํ๋ ์๊ฐ์ ๋๋ค์ผ๋ก ๋ถ์ฌ
Invoke("Think", time); // ๋งค๊ฐ๋ณ์๋ก ๋ฐ์ ํจ์๋ฅผ time์ด์ ๋๋ ์ด๋ฅผ ๋ถ์ฌํ์ฌ ์ฌ์คํ
}
}