Enemy Animator Controller
Assets>Animations>AnimatorControllers 폴더에서 Enemy1의 컨트롤러를 클릭합니다.
그리고 Enemy1Idle과 Enemy1Attack 사이에 서로 트랜지션을 생성합니다.
그 중 Idle에서 Attack으로 향하는 트랜지션 화살표를 선택합니다.
Parameters에서 + 버튼을 눌러 전과 마찬가지로 Trigger 타입의 파라미터 enemyAttack을 생성하고,
인스펙터의 Conditions를 enemyAttack으로 바꿔줍니다. 적이 공격시 바로 애니메이션이 바뀔 수 있도록 Has Exit Time에 체크 해제하고 transition Duration도 0으로 합니다.
이제 Attack에서 Idle로 가는 트랜지션을 선택합니다.
Has Exit Time은 체크한 채로 두고, Exit Time은 1, Transition Duration은 0으로 바꿉니다.
Enemy2는 Enemy1을 오버라이드 했으므로 같은 작업을 반복할 필요가 없습니다.
이제 Scripts 폴더로 가서 GameManager를 에디터로 엽니다.
변수 선언부에 다음의 변수들을 추가합니다.
public float turnDelay = 0.1f; // 플레이어 턴 간의 딜레이
private List<Enemy> enemies; // 적들의 리스트
private bool enemiesMoving; // 적이 움직이는지 체크
| cs |
그리고 Awake()안에서 enemies를 초기화합니다.
private void Awake()
{
...
enemies = new List<Enemy>();
boardScript = GetComponent<BoardManager>();
InitGame();
}
| cs |
InitGame()에서는 게임이 끝났을 때 enemies를 지워야 합니다. 레벨이 다시 시적되었다고 해서 게임 매니저가 리셋되는 것은 아니기 때문입니다.
이제 여기에 적들을 움직이는 코루틴을 작성합니다.
Update() 메소드에서 코루틴을 시작합니다.
그리고 리스트에 적을 추가하는 AddEnemyToList라는 메소드를 작성합니다.
여기까지 작성하고 저장한 후 유니티로 돌아갑니다.
Scripts에서 이번에는 Enemy를 선택하고 에디터로 엽시다. 몇 가지 수정할 것이 있습니다.
Start() 메소드의 가장 첫줄에
를 적어 넣어 게임매니저의 리스트에 이 스크립트를 포함시켜 움직임이 가능하도록 합니다.
또, OnCantMove 가장 아래 줄에
를 넣어 애니메이터 상태를 설정합니다.
저장하고 다시 유니티로 돌아갑니다.
Prefabs 폴더에서 Enemy1, Enemy2를 동시 선택하고 Component>Scripts>Enemy를 선택해서 한번에 추가합니다. 그리고 Blocking Layer 프로퍼티를 BlockingLayer로 설정합니다.
그리고 나서 이번에는 Enemy1만 선택하고 Player Damage 프로퍼티를 10, Enemy2는 20으로 설정해서 두 적이 서로 다른 크기의 데미지를 줄 수 있도록 합니다.
여기까지 해서 테스트를 해보았는데 에러가 나는군요. 뭔가 한참 찾아봤는데 제가 Player스크립트를 만들고 Player프리팹에 추가하는 것을 잊었군요. 글에는 추가하라고 썼는데 정작 저 자신은 빼 먹는 실수를 저질렀습니다. 뒤늦게 추가하고 움직여 보니 움직이긴 하는데 애니메이션이 이상합니다. 확인해 보니 제가 SetTrigger 사용할 때 "playerChop", "playerHit"이라고 써야할 것을 대문자로 "PlayerChop", "PlayerHit"이라고 썼군요. 하아...
수정 후 테스트 하니 일단 지금까지 작동해야 할 것들이 다 잘 작동됩니다.
마지막으로 지금까지 작성된 Enemy와 GameManager 스크립트를 올리고 다음 순서로 넘어가겠습니다.
Enemy.cs
GameManager.cs
void InitGame()
{
enemies.Clear();
boardScript.SetupSecene(level);
}
| cs |
이제 여기에 적들을 움직이는 코루틴을 작성합니다.
IEnumerator MoveEnemies()
{
enemiesMoving = true;
// turnDelay만큼 기다림
yield return new WaitForSeconds(turnDelay);
// 적이 없다면
if (enemies.Count == 0)
{
// 움직이는 대신 기다리게 함
yield return new WaitForSeconds(turnDelay);
}
for(int i = 0; i < enemies.Count ; i++)
{
// 적들을 움직이게 함
enemies[i].MoveEnemy();
// 다음 적이 움직일 때까지 잠시 대기
yield return new WaitForSeconds(enemies[i].moveTime);
}
// 적들이 움직였으면 이제 플레이어 차례
playersTurn = true;
enemiesMoving = false;
}
| cs |
Update() 메소드에서 코루틴을 시작합니다.
private void Update()
{
// 플레이어의 차례이거나 이미 적이 움직이는 중이라면 메소드 빠져나감
if (playersTurn || enemiesMoving)
return;
StartCoroutine(MoveEnemies());
}
| cs |
그리고 리스트에 적을 추가하는 AddEnemyToList라는 메소드를 작성합니다.
public void AddEnemyToList(Enemy script)
{
enemies.Add(script);
}
| cs |
여기까지 작성하고 저장한 후 유니티로 돌아갑니다.
Scripts에서 이번에는 Enemy를 선택하고 에디터로 엽시다. 몇 가지 수정할 것이 있습니다.
Start() 메소드의 가장 첫줄에
GameManager.instance.AddEnemyToList(this);
| cs |
또, OnCantMove 가장 아래 줄에
animator.SetTrigger("enemyAttack");
| cs |
저장하고 다시 유니티로 돌아갑니다.
Prefabs 폴더에서 Enemy1, Enemy2를 동시 선택하고 Component>Scripts>Enemy를 선택해서 한번에 추가합니다. 그리고 Blocking Layer 프로퍼티를 BlockingLayer로 설정합니다.
그리고 나서 이번에는 Enemy1만 선택하고 Player Damage 프로퍼티를 10, Enemy2는 20으로 설정해서 두 적이 서로 다른 크기의 데미지를 줄 수 있도록 합니다.
여기까지 해서 테스트를 해보았는데 에러가 나는군요. 뭔가 한참 찾아봤는데 제가 Player스크립트를 만들고 Player프리팹에 추가하는 것을 잊었군요. 글에는 추가하라고 썼는데 정작 저 자신은 빼 먹는 실수를 저질렀습니다. 뒤늦게 추가하고 움직여 보니 움직이긴 하는데 애니메이션이 이상합니다. 확인해 보니 제가 SetTrigger 사용할 때 "playerChop", "playerHit"이라고 써야할 것을 대문자로 "PlayerChop", "PlayerHit"이라고 썼군요. 하아...
수정 후 테스트 하니 일단 지금까지 작동해야 할 것들이 다 잘 작동됩니다.
마지막으로 지금까지 작성된 Enemy와 GameManager 스크립트를 올리고 다음 순서로 넘어가겠습니다.
Enemy.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Enemy : MovingObject
{
public int playerDamage; // 플레이어를 공격 시 줄어드는 포인트
private Animator animator; // 애니메이터 레퍼런스
private Transform target; // 플레이어의 위치
private bool skipMove; // 해당 턴에 이동할 것인지 결정
protected override void Start ()
{
GameManager.instance.AddEnemyToList(this);
animator = GetComponent<Animator>();
target = GameObject.FindGameObjectWithTag("Player").transform;
base.Start();
}
protected override void AttemptMove<T>(int xDir, int yDir)
{
// 움직임을 건너뛰어야 한다면
if (skipMove)
{
// 다음 턴을 위해 false로 만들고 건너 뜀
skipMove = false;
return;
}
base.AttemptMove<T>(xDir, yDir);
// Enemy가 이미 움직였으므로 true로 설정
skipMove = true;
}
public void MoveEnemy()
{
// 1또는 -1로 각 축의 방향을 정할 변수
int xDir = 0;
int yDir = 0;
// 둘의 x포지션이 같을 때
if (Mathf.Abs(target.position.x - transform.position.x) < float.Epsilon)
// y축 상에서 타겟 쪽으로 한칸 움직여 줌
yDir = target.position.y > transform.position.y ? 1 : -1;
else // 둘의 x 포지션이 다르다면
// x축 상에서 타겟 쪽으로 한 칸 움직여 줌
xDir = target.position.x > transform.position.y ? 1 : -1;
// Player를 대상으로 AttemptMove를 호출
AttemptMove<Player>(xDir, yDir);
}
protected override void OnCantMove<T>(T component)
{
// component를 Player로 캐스트하여 저장
Player hitPlayer = component as Player;
// 받은 데미지만큼 음식포인트를 줄여줌
hitPlayer.LoseFood(playerDamage);
animator.SetTrigger("enemyAttack");
}
}
| cs |
GameManager.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GameManager : MonoBehaviour
{
public float turnDelay = 0.1f; // 플레이어 턴 간의 딜레이
public static GameManager instance = null;
public BoardManager boardScript; // 보드매니저 스크립트의 레퍼런스
public int playerFoodPoints = 100;
[HideInInspector] public bool playersTurn = true;
private int level = 3; // 레벨, 3부터 적이 나타나므로 3으로 초기화
private List<Enemy> enemies; // 적들의 리스트
private bool enemiesMoving; // 적이 움직이는지 체크
private void Awake()
{
if (instance == null)
instance = this;
else if (instance != this)
Destroy(gameObject);
DontDestroyOnLoad(gameObject);
enemies = new List<Enemy>();
boardScript = GetComponent<BoardManager>();
InitGame();
}
private void Update()
{
// 플레이어의 차례이거나 이미 적이 움직이는 중이라면 메소드 빠져나감
if (playersTurn || enemiesMoving)
return;
StartCoroutine(MoveEnemies());
}
public void AddEnemyToList(Enemy script)
{
enemies.Add(script);
}
public void GameOver()
{
enabled = false;
}
void InitGame()
{
enemies.Clear();
boardScript.SetupSecene(level);
}
IEnumerator MoveEnemies()
{
enemiesMoving = true;
// turnDelay만큼 기다림
yield return new WaitForSeconds(turnDelay);
// 적이 없다면
if (enemies.Count == 0)
{
// 움직이는 대신 기다리게 함
yield return new WaitForSeconds(turnDelay);
}
for(int i = 0; i < enemies.Count ; i++)
{
// 적들을 움직이게 함
enemies[i].MoveEnemy();
// 다음 적이 움직일 때까지 잠시 대기
yield return new WaitForSeconds(enemies[i].moveTime);
}
// 적들이 움직였으면 이제 플레이어 차례
playersTurn = true;
enemiesMoving = false;
}
}
| cs |
댓글 없음:
댓글 쓰기