Game Over 텍스트를 만들어서 게임 오버 환경을 만들겠습니다
하이라키 뷰에 Canvas 를 선택하고 오른쪽 마우스버튼을 눌러 UI -> Text를 만듬니다
새로 생긴 Text 오브젝트를 이름을 tetGameOver 로 바꾸고 속성을 아래 그림과 같이 바꿈니다
txtGameOver 오브젝트를 선택하고 Window -> Animation -> Animation 을 선택합니다
Animation 창이 나오면 Create 버튼을 누릅니다
저장할 Animation 폴더를 만들고 이름을 GameOver 라 하고 저장합니다
Animation 창에 Add Property 를 눌러서 Color + 를 선택하여 애니메이션 의 키를 줍니다
애니며여션 창의 Preview 옆의 빨간 버튼을 누르고 첫 프레임의 키를 선택하여 GameOver 텍스쳐의 알파 값을 0 으로 하고 마지막 프레임을 선택하여 알파값을 255 로 줍니다
그리고 Animator 창으로 돌아가서 GameOver 애니메이션의 Loop Time을 체크 해제 합니다
그리고 빈 State 를 만들고 선택하여 Set as Layer Defult State 를 선택합니다
Animator 창에 Parameters를 선택하여 Trigger를 만들고 이름을 show 라 합니다
Start 와 GameOver State 에 화살푤르 연결하고 has Exit Time 을 체크 해제하고 Conditons를 show를 선택합니다
UIManager 스크립트를 선택하고 스크립트를 수정합니다
public Text gameOver;
Animator animGameOver;
.
.
.
.
private void Start()
{
animGameOver = gameOver.gameObject.GetComponent<Animator>();
gameOver.enabled = false;
}
public void ShowGameOver()
{
gameOver.enabled = true;
animGameOver.SetTrigger("show");
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class UIManager : MonoBehaviour
{
// 언제 어디서나 쉽게 접금할수 있도록 하기위해 만든 정적변수
public static UIManager instance;
public Text playerName;
public Text playerMoney;
public Image playerHPBar;
public Text gameOver;
Animator animGameOver;
private void Awake()
{
if (instance == null)
{
instance = this;
}
}
private void Start()
{
animGameOver = gameOver.gameObject.GetComponent<Animator>();
gameOver.enabled = false;
}
public void ShowGameOver()
{
gameOver.enabled = true;
animGameOver.SetTrigger("show");
}
public void UpdatePlayerUI(PlayerParams playerParams)
{
playerName.text = playerParams.name;
playerMoney.text = "Coin : " + playerParams.money.ToString();
playerHPBar.rectTransform.localScale =
new Vector3((float)playerParams.curHp / (float)playerParams.maxHp, 1f, 1f);
}
void Update()
{
}
}
Canvas의 txtGameOver 오브젝트를 선택하고 UIManager 스크립트의 연결합니다
PalyerFSM 스크립트를 선택하고 수정합니다
UIManager.instance.ShowGameOver(); public void ChangeToPlayerDead()
{
print("player was dead");
ChangeState(State.Dead, PlayerAni.ANI_DIE);
UIManager.instance.ShowGameOver();
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerFSM : MonoBehaviour
{
public enum State
{
Idle,
Move,
Attack,
AttackWait,
Dead
}
//idle 상태를 기본 상태로 지정
public State currentState = State.Idle;
//마우수 클릭 지점,플레이어가 이동할 목적지의 좌표를 저장할 예정
Vector3 curTargetPos;
GameObject curEnemy;
public float rotAnglePerSecond = 360f; //1초에 플레이어의 방향을 360도 회전한다
public float moveSpeed = 2f; //초당 2미터의 속도로 이동
float attackDelay = 2f; // 공격을 한번하고 다시 공격할때 까지의 지연시간
float attackTimer = 0f; //공격을 하고 난 뒤에 경과되는 시간을 계산하기 위한 변수
float attackDistance = 1.5f; // 공격 거리 (적과의 거리)
float chaseDistance = 2.5f; // 전투 중 적이 도망가면 다시 추적을 시작 하기 위한 거리
PlayerAni myAni;
PlayerParams myParams;
EnemyParams curEnemyParams;
void Start()
{
myAni = GetComponent<PlayerAni>();
// myAni.ChangeAni(PlayerAni.ANI_WALK);
myParams = GetComponent<PlayerParams>();
myParams.InitParams();
myParams.deadEvent.AddListener(ChangeToPlayerDead);
ChangeState(State.Idle, PlayerAni.ANI_IDLE);
curEnemy = GameObject.FindGameObjectWithTag("Enemy");
}
public void ChangeToPlayerDead()
{
print("player was dead");
ChangeState(State.Dead, PlayerAni.ANI_DIE);
UIManager.instance.ShowGameOver();
}
public void CurrentEnemyDead()
{
ChangeState(State.Idle, PlayerAni.ANI_IDLE);
print("enemy was killed");
curEnemy = null;
}
public void AttackCalculate()
{
if (curEnemy == null)
{
return;
}
print("Attack" + curEnemy.name +"...");
curEnemy.GetComponent<EnemyFSM>().ShowHitEffect();
int attackPower = myParams.GetRandomAttack();
curEnemyParams.SetEnemyAttack(attackPower);
//플레이어가 공격할때 나는 소리
SoundManager.instance.PlayHitSound();
}
// 적을 공격하기 위한 함수
public void AttackEnemy(GameObject enemy)
{
if (curEnemy != null && curEnemy == enemy)
{
return;
}
//적(몬스터)의 파라미터를 변수에 저장
curEnemyParams = enemy.GetComponent<EnemyParams>();
if (curEnemyParams.isDead == false)
{
curEnemy = enemy;
curTargetPos = curEnemy.transform.position;
GameManager.instance.ChangeCurrentTarget(curEnemy);
ChangeState(State.Move, PlayerAni.ANI_WALK);
}
else
{
curEnemyParams = null;
}
}
void ChangeState(State newState, int aniNumber)
{
if (currentState == newState)
{
return;
}
myAni.ChangeAni(aniNumber);
currentState = newState;
}
//캐릭터의 상태가 바뀌면 어떤 일이 일어날지 를 미리 정의
void UpdateState()
{
switch (currentState)
{
case State.Idle:
IdleState();
break;
case State.Move:
MoveState();
break;
case State.Attack:
AttackState();
break;
case State.AttackWait:
AttackWaitState();
break;
case State.Dead:
DeadState();
break;
default:
break;
}
}
void IdleState()
{
}
void MoveState()
{
TurnToDestination();
MoveToDestination();
}
void AttackState()
{
attackTimer = 0f;
//transform.LookAt(목표지점 위치) 목표지점을 향해 오브젝트를 회전 시키는 함수
transform.LookAt(curTargetPos);
ChangeState(State.AttackWait, PlayerAni.ANI_ATKIDLE);
}
void AttackWaitState()
{
if (attackTimer > attackDelay)
{
ChangeState(State.Attack, PlayerAni.ANI_ATTACK);
}
attackTimer += Time.deltaTime;
}
void DeadState()
{
}
//MoveTo(캐릭터가 이동할 목표 지점의 좌표)
public void MoveTo(Vector3 tPos)
{
//사망 하였을때 움직임 리턴 처리 (여기서 끝냄)
if (currentState == State.Dead)
{
return;
}
curEnemy = null;
curTargetPos = tPos;
ChangeState(State.Move, PlayerAni.ANI_WALK);
}
void TurnToDestination()
{
// Quaternion lookRotation(회전할 목표 방향) : 목표 방향은 목적지 위치에서 자신의 위치를 빼면 구함
Quaternion lookRotation = Quaternion.LookRotation(curTargetPos - transform.position);
//Quaternion.RotateTowards(현재의 rotation값, 최종목표rotation 값, 최대 회전각)
transform.rotation = Quaternion.RotateTowards(transform.rotation, lookRotation, Time.deltaTime * rotAnglePerSecond);
}
void MoveToDestination()
{
//Vector3.MoveTowards(시작지점, 목표지점,최대이동거리)
transform.position = Vector3.MoveTowards(transform.position, curTargetPos,moveSpeed * Time.deltaTime);
if (curEnemy == null)
{
//플레이어의 위치와 목표지점의 위치가 같으면, 상태를 Idle 상태로 바꾸라는 명령
if (transform.position == curTargetPos)
{
ChangeState(State.Idle, PlayerAni.ANI_IDLE);
}
}
else if (Vector3.Distance(transform.position,curTargetPos) < attackDistance)//Vector3.Distance(A,B):A와B사이의 거리
{
ChangeState(State.Attack, PlayerAni.ANI_ATTACK);
}
}
void Update()
{
UpdateState();
}
}
EnemyFSM스크립트를 선택하고 수정합니다
void AttackState()
{
if (player.GetComponent<PlayerFSM>().currentState == PlayerFSM.State.Dead)
{
ChangeState(State.NoState, EnemyAni.IDLE);
}
if (GetDistanceFromPlayer() > reChaseDistance)
{
attackTimer = 0f;
ChangeState(State.Chase, EnemyAni.WALK);
}
else
{
if (attackTimer > attackDelay)
{
transform.LookAt(player.position);
myAni.ChangeAni(EnemyAni.ATTACK);
attackTimer = 0f;
//몬스터가 공격할때 나는 소리
SoundManager.instance.PlayEnemyAttack();
}
attackTimer += Time.deltaTime;
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyFSM : MonoBehaviour
{
public enum State
{
Idle, //정지
Chase, //추적
Attack, //공격
Dead, //사망
NoState //아무 일도 없는 상태
}
public State currentState = State.Idle;
EnemyParams myParams;
EnemyAni myAni;
Transform player;
PlayerParams playerParams;
CharacterController controller;
float chaseDistance = 5f; // 플레이어를 향해 몬스터가 추적을 시작할 거리
float attackDistance = 2.5f; // 플레이어가 안쪽으로 들오오게 되면 공격을 시작
float reChaseDistance = 3f; // 플레이어가 도망 갈 경우 얼마나 떨어져야 다시 추적
float rotAnglePerSecond = 360f; // 초당 회전 각도
float moveSpeed = 1.3f; // 몬스터의 이동 속도
float attackDelay = 2f;
float attackTimer = 0f;
public ParticleSystem hitEffect;
public GameObject selectMark;
//리스폰 시킬 몬스터를 담을 변수
GameObject myRespawnObj;
//리스폰 오브젝트에서 생성된 몇번째 몬스터에 대한 정보
public int spawnID { get; set; }
//몬스터가 처음 생성될때의 위치를 저장
Vector3 originPos;
void Start()
{
myAni = GetComponent<EnemyAni>();
myParams = GetComponent<EnemyParams>();
myParams.deadEvent.AddListener(CallDeadEvent);
ChangeState(State.Idle, EnemyAni.IDLE);
controller = GetComponent<CharacterController>();
player = GameObject.FindGameObjectWithTag("Player").transform;
playerParams = player.gameObject.GetComponent<PlayerParams>();
hitEffect.Stop();
HideSelection();
}
//몬스터가 리스폰 될때 초기화 상태로 함
public void AddToWorldAgain()
{
// 리스폰 오브젝트에서 처음 생성될때의 위치와 같게 함
transform.position = originPos;
GetComponent<EnemyParams>().InitParams();
GetComponent<BoxCollider>().enabled = true;
}
public void HideSelection()
{
selectMark.SetActive(false);
}
public void ShowSelection()
{
selectMark.SetActive(true);
}
// 몬스터가 어느 리스폰 오브젝트로부터 만들었졋는지에 대한 정보를 전달 받을 함수
public void SetRespawnObj(GameObject respawnObj, int spawnID,Vector3 originPos)
{
myRespawnObj = respawnObj;
this.spawnID = spawnID;
this.originPos = originPos;
}
//몬스터가 죽는 순간 처리 명령어
void CallDeadEvent()
{
ChangeState(State.Dead, EnemyAni.DIE);
//몬스터가 죽은후 아이템 및 동전을 생성한다
ObjectManager.instance.DropCoinToPosition(transform.position, myParams.rewardMoney);
player.gameObject.SendMessage("CurrentEnemyDead");
//몬스터가 사망했을때 나는 소리
SoundManager.instance.PlayEnemyDie();
StartCoroutine(RemoveMeFromWorld());
}
IEnumerator RemoveMeFromWorld()
{
yield return new WaitForSeconds(1f);
ChangeState(State.Idle, EnemyAni.IDLE);
//리스폰 오브젝트에 자기 자신을 제거해 달라는 요청
myRespawnObj.GetComponent<RespawnObj>().RemoveMonster(spawnID);
}
public void ShowHitEffect()
{
hitEffect.Play();
}
public void AttackCalculate()
{
playerParams.SetEnemyAttack(myParams.GetRandomAttack());
}
void UpdateState()
{
switch (currentState)
{
case State.Idle:
IdleState();
break;
case State.Chase:
ChaseState();
break;
case State.Attack:
AttackState();
break;
case State.Dead:
DeadState();
break;
case State.NoState:
NoState();
break;
}
}
public void ChangeState(State newState, string aniName)
{
if (currentState == newState)
{
return;
}
currentState = newState;
myAni.ChangeAni(aniName);
}
void IdleState()
{
if (GetDistanceFromPlayer() < chaseDistance)
{
ChangeState(State.Chase, EnemyAni.WALK);
}
}
void ChaseState()
{
//몬스터가 공격 가능 거리 안으로 들어가면 공격 상태
if (GetDistanceFromPlayer() < attackDistance)
{
ChangeState(State.Attack, EnemyAni.ATTACK);
}
else
{
TurnToDestination();
MoveToDestination();
}
}
void AttackState()
{
if (player.GetComponent<PlayerFSM>().currentState == PlayerFSM.State.Dead)
{
ChangeState(State.NoState, EnemyAni.IDLE);
}
if (GetDistanceFromPlayer() > reChaseDistance)
{
attackTimer = 0f;
ChangeState(State.Chase, EnemyAni.WALK);
}
else
{
if (attackTimer > attackDelay)
{
transform.LookAt(player.position);
myAni.ChangeAni(EnemyAni.ATTACK);
attackTimer = 0f;
//몬스터가 공격할때 나는 소리
SoundManager.instance.PlayEnemyAttack();
}
attackTimer += Time.deltaTime;
}
}
void DeadState()
{
GetComponent<BoxCollider>().enabled = false;
}
void NoState()
{
}
void TurnToDestination()
{
Quaternion lookRotation = Quaternion.LookRotation(player.position - transform.position);
transform.rotation = Quaternion.RotateTowards(transform.rotation, lookRotation, Time.deltaTime * rotAnglePerSecond);
}
void MoveToDestination()
{
// transform.position = Vector3.MoveTowards(transform.position, player.position, moveSpeed * Time.deltaTime);
//몬스터의 이동을 캐릭터 컨트롤러로 바꿈 몬스터가 전방으로 moveSpeed 만큼의 빠르기로 이동하게 됨
controller.Move(transform.forward * moveSpeed * Time.deltaTime);
}
//플레이어와 거리을 재는 함수
float GetDistanceFromPlayer()
{
float distance = Vector3.Distance(transform.position, player.position);
return distance;
}
// Update is called once per frame
void Update()
{
UpdateState();
}
}
게임을 실행하고 플레이어가 죽으면 GameOver 텍스쳐가 잘나오는지 확인 합니다
'게임 만들기 강좌 > 유니티 3D RPG 게임만들기' 카테고리의 다른 글
unity 유니티 게임 만들기 RPG 게임만들기 24 XML Manager 만들기 (0) | 2019.07.23 |
---|---|
unity 유니티 게임 만들기 RPG 게임만들기 23 오브젝트 매니저 만들기 아이템 관리 (0) | 2019.07.21 |
unity 유니티 게임 만들기 RPG 게임만들기 22 동전 Coin 아이템 Item 만들기 (0) | 2019.07.19 |
unity 유니티 게임 만들기 RPG 게임만들기 21 적 캐릭터 리스폰 초기화 하기 (0) | 2019.07.17 |
unity 유니티 게임 만들기 RPG 게임만들기 20 적 캐릭터 재생산 하기 게임 매니저 만들기 (0) | 2019.07.13 |