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()
{
}
}
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();
}
}
파일을 저장하는데 이름을 적고 확장자를 xml로 바꾸고 인코딩 방식을 UTF-8 코드로 하고 저장합니다
저장하면 모양이 아래 그림과 같이 됩니다 Windows 7 환경
저장한 파일을 유니티에 Resources폴더를 만들고 드래그하여 옮깁니다
하이 라키 뷰에 XMLManager 오브젝트를 만들고 XMLManager 스크립트를 만들어 붙입니다
XMLManager 스크립트 작성
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Xml;
using System;
public class XMLManager : MonoBehaviour
{
public static XMLManager instance;
//xml 파일
public TextAsset enemyFileXml;
//여러개의 변수들을 넣어서 구조체 하나를 한개의 상자처럼 간주하고 사용할수 있음
struct MonParams
{
//xml 파일로 부터 각각의 몬스터의 대해서 이들 파라미터 값을 읽어 들이고 구조체 내부 변수에 저장하고 구조체를 이용하여 각 몬스터에게 파라미터 값으 전달함
public string name;
public int level;
public int maxHp;
public int attackMin;
public int attackMax;
public int defense;
public int exp;
public int rewardMoney;
}
//딕셔너리의 키값으로 적의이름을 사용할 예정이므로 string타입으로 하고 데이터 값으로는 구조체를 이용함 MonParams로 지정
Dictionary<string, MonParams> dicMonsters = new Dictionary<string, MonParams>();
void Awake()
{
if (instance == null)
{
instance = this;
}
}
private void Start()
{
MakeMonsterXML();
}
//XML로부터 파라미터 값 읽어 들이기
void MakeMonsterXML()
{
XmlDocument monsterXMLDoc = new XmlDocument();
monsterXMLDoc.LoadXml(enemyFileXml.text);
XmlNodeList monsterNodeList = monsterXMLDoc.GetElementsByTagName("row");
//노드 리스트로부터 각각의 노드를 뽑아냄
foreach ( XmlNode monsterNode in monsterNodeList)
{
MonParams monParams = new MonParams();
foreach (XmlNode childNode in monsterNode.ChildNodes)
{
if (childNode.Name == "name")
{
//<name>smallspider</name>
monParams.name = childNode.InnerText;
}
if (childNode.Name == "level")
{
//<level>1</level> Int16.Parse() 은 문자열을 정수로 바꿔줌
monParams.level = Int16.Parse( childNode.InnerText);
}
if (childNode.Name == "maxHp")
{
monParams.maxHp = Int16.Parse(childNode.InnerText);
}
if (childNode.Name == "attackMin")
{
monParams.attackMin = Int16.Parse(childNode.InnerText);
}
if (childNode.Name == "attackMax")
{
monParams.attackMax = Int16.Parse(childNode.InnerText);
}
if (childNode.Name == "defense")
{
monParams.defense = Int16.Parse(childNode.InnerText);
}
if (childNode.Name == "exp")
{
monParams.exp = Int16.Parse(childNode.InnerText);
}
if (childNode.Name == "rewardMoney")
{
monParams.rewardMoney = Int16.Parse(childNode.InnerText);
}
print(childNode.Name + ": " + childNode.InnerText);
}
dicMonsters[monParams.name] = monParams;
}
}
//외부로부터 몬스터의 이름과, EnemyParams 객체를 전달 받아서 해당 이름을 가진 몬스터의
//데이터(XML 에서 읽어 온 데이터)를 전달받은 EnemyParams 객체에 적용하는 역할을 하는 함수
public void LoadMonsterParamsFromXML(string monName, EnemyParams mParams)
{
mParams.level = dicMonsters[monName].level;
mParams.curHp = mParams.maxHp = dicMonsters[monName].maxHp;
mParams.attackMin = dicMonsters[monName].attackMin;
mParams.attackMax = dicMonsters[monName].attackMax;
mParams.defense = dicMonsters[monName].defense;
mParams.exp = dicMonsters[monName].exp;
mParams.rewardMoney = dicMonsters[monName].rewardMoney;
}
void Update()
{
}
}
XMLManager 스크립트에 xml 파일 enemy를 Enemy File Xml에 드래그하여 붙입니다