일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 게임 엔진 공부
- 2022년
- todolist
- 1월
- 개인 프로젝트
- 유니티 심화과정
- C/C++
- 개인 프로젝트 - 런앤건
- 5월
- 3월
- 2024년
- 코딩 기초 트레이닝
- 다이나믹 프로그래밍
- c++
- 입문
- 자료 구조
- 수학
- 골드메탈
- 프로그래머스
- 2025년
- 2월
- 기초
- 코딩 테스트
- 4월
- 2023년
- 유니티
- 백준
- 단계별로 풀어보기
- 7월
- 10월
- Today
- Total
기록 보관소
[Unity/유니티] 기초-2D 플랫포머: 몬스터 AI 구현하기[B18] 본문
개요
유니티 입문과 독학을 위해서 아래 링크의 골드메탈님의 영상들을 보며 진행 상황 사진 또는 캡처를 올리고 배웠던 점을 요약해서 적는다.
현재는 영상들을 보고 따라하고 배우는 것에 집중할 것이며, 영상을 모두 보고 따라한 후에는 개인 프로젝트를 설계하고 직접 만드는 것이 목표다.
https://youtube.com/playlist?list=PLO-mt5Iu5TeYI4dbYwWP8JqZMC9iuUIW2
유니티 강좌 기초 채널 Basic
유니티 개발을 처음 시작하시는 입문자 분들을 위한 기초 채널. [ 프로젝트 ] B00 ~ B12 (BE1) : 유니티 필수 기초 B13 ~ B19 (BE2) : 2D 플랫포머 B20 ~ B26 (BE3) : 2D 탑다운 대화형 RPG B27 ~ B37 (BE4) : 2D 종스크롤
www.youtube.com
2D 플랫포머: 몬스터 AI 구현하기[B18]
1. 준비하기
- 애니메이션을 생성하고 나면, 우선 기존에 있던 Enemy_Idle 애니메이션에 우클릭을 해서 Transition을 만들어 Enemy_Walk와 연결한다. Enemy_Walk도 마찬가지로 Enemy_Idle과 연결한다.
- 이후 isWalking이라는 Bool형 Parameter를 생성하고, 각 Transition을 클릭해서 Conditions에 추가한다. isWalking의 작동은 다음과 같다.
- Enemy_Idle -> Enemy_Walk : isWalking이 true일 때
- Enemy_Walk -> Enemy_Idle : isWalking이 false일 때
- 마지막으로 Has Exit Time을 체크 해제하고, Settings의 애니메이션이 겹치는 시간을 작게 만들어 제거하면 애니메이션 전환 준비가 끝난다.
2. 기본 이동
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyMove : MonoBehaviour {
Rigidbody2D rigid;
void Awake() {
rigid = GetComponent<Rigidbody2D>();
}
void FixedUpdate() {
rigid.velocity = new Vector2(-1, rigid.velocity.y); //왼쪽 방향으로 이동
}
}
3. 행동 결정 로직
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyMove : MonoBehaviour {
Rigidbody2D rigid;
public int nextMove; //행동지표를 결정할 변수
void Awake() {
rigid = GetComponent<Rigidbody2D>();
Invoke("Think", 5); //5초 뒤에 Think 함수 실행
}
void FixedUpdate() {
rigid.velocity = new Vector2(nextMove, rigid.velocity.y);
}
void Think() {
nextMove = Random.Range(-1, 2); //-1 ~ 1까지의 랜덤 수 생성
Invoke("Think", 5); //5초 뒤에 Think 함수 실행. 재귀
}
}
- Random : 랜덤 수를 생성하는 로직 관련 클래스
- .Range(최솟값, 최댓값) : 최소 ~ 최대 범위의 랜덤 수 생성(최댓값은 랜덤에서 제외)
- Invoke() : 주어진 시간이 지난 뒤, 지정된 함수를 실행하는 함수. 유니티(MonoBehaviour)에서 제공해주는 함수다.
4. 지능 높이기
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyMove : MonoBehaviour {
Rigidbody2D rigid;
public int nextMove; //행동지표를 결정할 변수
void Awake() {
rigid = GetComponent<Rigidbody2D>();
Invoke("Think", 5); //5초 뒤에 Think 함수 실행
}
void FixedUpdate() {
//이동
rigid.velocity = new Vector2(nextMove, rigid.velocity.y);
//Ray를 사용한 지형 체크
Vector2 frontVec = new Vector2(rigid.position.x + nextMove, rigid.position.y); //Ray를 경로 한칸 앞에 생성
Debug.DrawRay(frontVec, Vector3.down, new Color(0, 1, 0));
RaycastHit2D rayHit = Physics2D.Raycast(frontVec, Vector3.down, 1, LayerMask.GetMask("Platform"));
if (rayHit.collider == null)
Debug.Log("경고 경로에 바닥이 없음");
}
void Think() {
nextMove = Random.Range(-1, 2); //-1 ~ 1까지의 랜덤 수 생성
Invoke("Think", 5); //5초 뒤에 Think 함수 실행. 재귀
}
}
- Ray 테스트를 위한 코드와 실행 결과. 현재 만들려는 몬스터는 다르게 땅에서 떨어지지 않고 움직이는 것이 목표이므로 Ray를 몬스터 중심이 아닌, 몬스터가 가는 경로 한칸 앞에 두어서 해당 경로 앞이 빈 칸인지 아닌지 확인할 수 있어야한다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyMove : MonoBehaviour {
Rigidbody2D rigid;
public int nextMove;
void Awake() {
rigid = GetComponent<Rigidbody2D>();
Think();
}
void FixedUpdate() {
//이동
rigid.velocity = new Vector2(nextMove, rigid.velocity.y);
//Ray를 사용한 지형 체크
Vector2 frontVec = new Vector2(rigid.position.x + nextMove * 0.2f, rigid.position.y);
Debug.DrawRay(frontVec, Vector3.down, new Color(0, 1, 0));
RaycastHit2D rayHit = Physics2D.Raycast(frontVec, Vector3.down, 1, LayerMask.GetMask("Platform"));
if (rayHit.collider == null) {
nextMove *= -1; //경로를 반대로
CancelInvoke();
Invoke("Think", 5);
}
}
void Think() {
nextMove = Random.Range(-1, 2);
float nextThinkTime = Random.Range(2f, 5f);
Invoke("Think", nextThinkTime);
}
}
- CancelInvoke() : 현재 작동 중인 모든 Invoke함수를 멈추는 함수. 마찬가지로 유니티(MonoBehaviour)에서 제공해주는 함수다.
5. 애니메이션 설정
- 기존에 만들어두었던 bool형 매개변수 isWalking을 삭제하고 int형 매개변수 WalkSpeed를 생성한다.
- 이후 각 Transition을 클릭해서 Conditions에 WalkSpeed를 넣는다. WalkSpeed 설정은 다음과 같다.
- Enemy_Idle -> Enemy_Walk : WalkSpeed NotEqual 0
- Enemy_Walk -> Enemy_Idle : WalkSpeed Equal 0
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyMove : MonoBehaviour {
Rigidbody2D rigid;
Animator anim;
SpriteRenderer spriteRenderer;
public int nextMove;
void Awake() {
rigid = GetComponent<Rigidbody2D>();
anim = GetComponent<Animator>();
spriteRenderer = GetComponent<SpriteRenderer>();
rigid.freezeRotation = true; //이동시 굴러가기 방지
Invoke("Think", 5);
}
void FixedUpdate() {
//이동
rigid.velocity = new Vector2(nextMove, rigid.velocity.y);
//Ray를 사용한 지형 체크
Vector2 frontVec = new Vector2(rigid.position.x + nextMove * 0.2f, rigid.position.y);
Debug.DrawRay(frontVec, Vector3.down, new Color(0, 1, 0));
RaycastHit2D rayHit = Physics2D.Raycast(frontVec, Vector3.down, 1, LayerMask.GetMask("Platform"));
if (rayHit.collider == null)
Turn();
}
void Think() {
//행동 지표 결정
nextMove = Random.Range(-1, 2);
//애니메이션 전환
anim.SetInteger("WalkSpeed", nextMove);
//애니메이션 방향 전환
if (nextMove != 0)
spriteRenderer.flipX = nextMove == 1;
//재귀
float nextThinkTime = Random.Range(2f, 5f);
Invoke("Think", nextThinkTime);
}
void Turn() {
//방향 전환
nextMove *= -1;
spriteRenderer.flipX = nextMove == 1;
CancelInvoke();
Invoke("Think", 2);
}
}
- 문제없이 몬스터가 이동했지만, 이전 포스트에서 다뤘던 가끔 스프라이트가 구르는 현상이 발생했다. 플레이어의 경우처럼 rigid.freezeRotation = true;를 추가하고, 기존의 Box Collider 2D에서 Capsule Collider 2D로 변경하니 해당 문제가 해결되었다.
'유니티 프로젝트 > 2D 플랫포머' 카테고리의 다른 글
[Unity/유니티] 기초-2D 플랫포머: 스테이지를 넘나드는 게임 완성하기[BE2] (0) | 2022.02.15 |
---|---|
[Unity/유니티] 기초-2D 플랫포머: 플레이어 피격 이벤트 구현하기[B19] (0) | 2022.02.14 |
[Unity/유니티] 기초-2D 플랫포머: 타일맵으로 플랫폼 만들기[B17] (0) | 2022.02.13 |
[Unity/유니티] 기초-2D 플랫포머: 플레이어 점프 구현하기[B16] (0) | 2022.02.11 |
[Unity/유니티] 기초-2D 플랫포머: 플레이어 이동 구현하기[B15] (0) | 2022.02.10 |