using System.Collections.Generic;
using UnityEngine;
public class DatabaseManager : MonoBehaviour
{
static public DatabaseManager instance;
public string[] var_name;
public float[] var;
public string[] switch_name;
public bool[] switches;
public List<Item> itemList = new List<Item>();
private PlayerStat thePlayerStat;
public GameObject prefabs_Floating_Text;
public GameObject parent;
private void FloatText(int number, string color)
{
Vector3 vector = thePlayerStat.transform.position;
vector.y += 60;
GameObject clone = Instantiate(prefabs_Floating_Text, vector, Quaternion.Euler(Vector3.zero));
clone.GetComponent<FloatingText>().text.text = number.ToString();
if(color == "GREEN")
clone.GetComponent<FloatingText>().text.color = Color.green;
else if(color == "BLUE")
clone.GetComponent<FloatingText>().text.color = Color.blue;
clone.GetComponent<FloatingText>().text.fontSize = 25;
clone.transform.SetParent(parent.transform);
}
private void Awake()//인스턴스 설정
{
if (instance == null)
{
DontDestroyOnLoad(this.gameObject);
instance = this;
}
else
{
Destroy(this.gameObject);
}
}
public void UseItem(int _itemID)
{
switch (_itemID)
{
case 10001:
Debug.Log("HP가 50회복되었습니다.");
//PlayerStat.instance.currentHP += 50;//이렇게 작성하면 플레이어 스텟으로 접근해서 값에 플러스해줌
if (thePlayerStat.hp >= thePlayerStat.currentHP + 50)
//조건문 현제 플레이어 hp가 높거나 같다면 50을 회복
thePlayerStat.currentHP += 50;
else//그게 아니라면(쉽게 말해 HP를 회복시 상한을 넘을 경우)
thePlayerStat.currentHP = thePlayerStat.hp;
FloatText(50, "GREEN");
break;
case 10002:
if (thePlayerStat.mp >= thePlayerStat.currentMP + 15)
thePlayerStat.currentMP += 15;
else
thePlayerStat.currentMP = thePlayerStat.mp;
Debug.Log("MP가 15회복되었습니다.");
FloatText(50, "BLUE");
break;
}
}
// Start is called once before the first execution of Update after the MonoBehaviour is created
void Start()
{
thePlayerStat = FindAnyObjectByType<PlayerStat>();
itemList.Add(new Item(10001, "빨간 포션", "체력을 50 회복시켜주는 기적의 물약", Item.ItemType.Use));
itemList.Add(new Item(10002, "파란 포션", "마나를 15 회복시켜주는 기적의 물약", Item.ItemType.Use));
itemList.Add(new Item(10003, "농축 빨간 포션", "체력을 350 회복시켜주는 기적의 농축 물약", Item.ItemType.Use));
itemList.Add(new Item(10004, "농축 파란 포션", "마나를 80 회복시켜주는 기적의 농축 물약", Item.ItemType.Use));
itemList.Add(new Item(11001, "랜덤 상자", "랜덤으로 포션이 나온다 낮은 확율로 꽝", Item.ItemType.Use));
itemList.Add(new Item(20001, "짧은 검", "기본적인 용사의 검", Item.ItemType.Equip));
itemList.Add(new Item(21001, "사파이어 반지", "1분에 마나 1을 회복시켜주는 마법 반지", Item.ItemType.Equip));
itemList.Add(new Item(30001, "고대 유물의 조각1", "반으로 쪼개진 고대 유물의 파편", Item.ItemType.Quest));
itemList.Add(new Item(30002, "고대 유물의 조각2", "반으로 쪼개진 고대 유물의 파편", Item.ItemType.Quest));
itemList.Add(new Item(30003, "고대 유물", "고대 유적에 잠들어있던 고대의 유물", Item.ItemType.Quest));
}
}
using System.Collections;
using UnityEngine;
public class PlayerStat : MonoBehaviour
{
public static PlayerStat instance;
public int character_Lv;
public int[] needExp;
public int currentEXP;
public int hp;
public int currentHP;
public int mp;
public int currentMP;
public int atk;
public int def;
public string dmgSound;
public GameObject prefabs_Flosting_text;
public GameObject parent;
// Start is called once before the first execution of Update after the MonoBehaviour is created
void Start()
{
instance = this;
}
public void Hit(int _enemyAtk)
{
int dmg;
if (def >= _enemyAtk)
dmg = 1;
else
dmg = _enemyAtk - def;
currentHP -= dmg;
if (currentHP <= 0)
Debug.Log("체력 0 미만, 게임오버");
AudioManager.instance.Play(dmgSound);
Vector3 vector = this.transform.position;
vector.y += 60;
GameObject clone = Instantiate(prefabs_Flosting_text, vector, Quaternion.Euler(Vector3.zero));
clone.GetComponent<FloatingText>().text.text = dmg.ToString();
clone.GetComponent<FloatingText>().text.color = Color.red;
clone.GetComponent<FloatingText>().text.fontSize = 25;
clone.transform.SetParent(parent.transform);
StartCoroutine(HitCoroutine());
}
IEnumerator HitCoroutine()
{
Color color = GetComponent<SpriteRenderer>().color;
color.a = 0;
GetComponent<SpriteRenderer>().color = color;
yield return new WaitForSeconds(0.1f);
color.a = 1f;
GetComponent<SpriteRenderer>().color =color;
yield return new WaitForSeconds(0.1f);
color.a = 0f;
GetComponent<SpriteRenderer>().color = color;
yield return new WaitForSeconds(0.1f);
color.a = 1f;
GetComponent<SpriteRenderer>().color = color;
yield return new WaitForSeconds(0.1f);
color.a = 0f;
GetComponent<SpriteRenderer>().color = color;
yield return new WaitForSeconds(0.1f);
color.a = 1f;
GetComponent<SpriteRenderer>().color = color;//이 부분작성을 빼놓지 말자 투명에서 유색으로 돌아오지 않는다
}
// Update is called once per frame
void Update()
{
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SlimeController : MovingObjcet//플레이어 캐릭터에 넣은 무빙오브젝트를 상속
{
public int atk; //슬라임의 공격력
public float attackDelay; //공격 딜레이(유예)
public float inter_MoveWaitTime; //대기 시간(인스펙터에 표시)
private float current_interMWT; //실질적인 계산시간
public string atkSound; //어택사운드
private Vector2 PlayerPos; //플레이어의 좌표값(옆에 위에 있는지 확인)
private int random_int; //애너미가 랜덤으로 움직이기 위한 변수
private string direction; //UP,DOWN,RIGHT,LEFT 값
// Start is called once before the first execution of Update after the MonoBehaviour is created
void Start()
{
queue = new Queue<string>(); //무빙오브젝트에 필요한 큐
current_interMWT = inter_MoveWaitTime; //시작하자마자 실질적 계산값 적용
}
// Update is called once per frame
void Update()
{
current_interMWT -= Time.deltaTime; //초당 -값으로 적용해서 시간이 지남을 적용
if (current_interMWT <= 0)//0보다 작아지면 행동
{
current_interMWT = inter_MoveWaitTime;//작업 초기화
if (NearPlayer())//NearPlayer이 참이라면
{
Flip();//플립으로 스프라이트 뒤집기 계산
return;
}
RandomDirection(); //랜덤하게 상하좌우 이동
if (base.CheckCollsion()) //충돌체크
return; //뭔가가 진로를 방해할경우 리턴으로 끊어준다
base.Move(direction); //무빙오브젝트 움직임
}
}
private void Flip()//슬라임의 스프라이트를 뒤집어서 오른쪽 공격으로 만들어줌
{
Vector3 flip = transform.localScale; //백터 플립이라는 변수를 만들어주고 스케일을 대입해줌
if (PlayerPos.x > this.transform.position.x)//플레이어 포인트.x의 위치가 클 경우
flip.x = -1; //스프라이트를 뒤집어줌
else
flip.x = 1f; //아니니라면 정상적으로 오른쪽 공격을 실행
this.transform.localScale = flip; //현재 스케일에 계산한 플립을 대입
animator.SetTrigger("Attack"); //어택실행
StartCoroutine(WaitCoroutine()); //코루틴 waitCoroutine를 실행
}
IEnumerator WaitCoroutine()
{
yield return new WaitForSeconds(attackDelay);
AudioManager.instance.Play(atkSound);
if (NearPlayer())//리턴이 true가 된다면 실행
PlayerStat.instance.Hit(atk);
}
private bool NearPlayer()//플레이어가 근처에 있는지 조건을 확인
{
PlayerPos = PlayerManager.instance.transform.position;//플레이어 위치를 받아옴
//MAthf.Abs는 설대값을 반환하는 함수이다 절대값은 음수가 나오면 정수로 바꿔주고 정수는 정수로 출력한다 즉 벡터가 -가 나와도 계산이 꼬일 일이 없다
if (Mathf.Abs(Mathf.Abs(PlayerPos.x) - Mathf.Abs(this.transform.position.x)) <= speed * walkCount)
//가로의 위치 측정 (플레이어포지션.x - 자신의 포지션을 해주고 덤으로 스피드와 워크카운트를 곱한 값을 비교해서 작거나 같을 경우
{
if (Mathf.Abs(Mathf.Abs(PlayerPos.y) - Mathf.Abs(this.transform.position.y)) <= speed * walkCount * 0.5f)
//여기는 세로 플레이어포지션.y - 자신의 포지션.y 그뒤 스피드와 워크카운트 곱하고 절반분의 값을 계산뒤 이 값이 크거나 같을 경우
{
return true;//리턴 true로 끊어줌
}
}
if (Mathf.Abs(Mathf.Abs(PlayerPos.y) - Mathf.Abs(this.transform.position.y)) <= speed * walkCount)//이 부분은 세로와 가로를 반대로 측정해줌
{
if (Mathf.Abs(Mathf.Abs(PlayerPos.x) - Mathf.Abs(this.transform.position.x)) <= speed * walkCount * 0.5f)
{
return true;
}
}
return false;
//위 두가지 조건이 만족하지 않으면 플레이어가 없으므로 false로 거짓으로 리턴한다
}
private void RandomDirection() // 랜덤하게 움직이는 함수
{
vector.Set(0, 0, vector.z); //백터값을 초기화
random_int = Random.Range(0, 4);//0~3개의 난수 설정
switch (random_int) //스위치 문으로 랜덤한 값에 따라 움직임을 적용
{
case 0:
vector.y = 1f;
direction = "UP";//위로이동
break;
case 1:
vector.y = -1f;
direction = "DOWN";//아래로 이동
break;
case 2:
vector.x = 1f;
direction = "RIGHT";//오른쪽으로 이동
break;
case 3:
vector.x = -1f;
direction = "LEFT";//왼쪽으로 이동
break;
}
}
}
오늘은 피격효과와 스텟을 구현하겠다
일단 피격 효과부터 구현한다
일단 스크립트 작성부터 시작한다 PlayerStat 라고 C#스크립트를 만들자
주석으로 충분히 설명이 가능해서 주석으로 설명했다
세이브 한다
이제 SlimeController 스크립트에 내용을 수정한다
지난번에는 Debug.Log로 표현했지만 이제는 재대로 작성해줬다
이제 PlayerStat 스크립트로 다시 돌아와서 로직을 작성해준다
HIt(int _enemyAtk)라고 작성해서 데미지가 인수로 넘어오게 작성한다
int dmg 변수를 선언해주고
데미지가 def보다 작거나 같으면 무조건 데미지가 1이 나오게 조건문을 작성해주고
그게 아니라면 데미지는 _enemyAtk -def로 값을 내주고
40번째 줄에 현재 HP에 계산이 끝난 dmg를 빼준다
그리고 45번째 줄에서 소리를 내준다
그리고 50번째 줄에 지난번에 작성한 플로인 텍스트가 뜨게 설정한다
47번째 줄에 자신의 벡터를 변수에 넣어주고
넣은 벡터에 += 60을 더해서 플레이어 머리 위에서 플로잉텍스트가 뜨게 한다
그리고 50번째 줄에 인스팩터에 공개되게 플로잉 텍스트 프리팹을 슬롯에 넣을 수 있게 설정해주고
51번째 줄에 clone변수로 선언한 플로잉텍스트의 text를 받아와서 dmg.ToString();로 작성하여 숫자를 문자열화 해준다
52번째 줄에는 color을 받아와서 Color.red;로 빨간색이 뜨게 한다
53번째 줄에는 fontSize를 25로 설정하고
54번째 줄에 clone로 선언한 변수의 부모값을 인스펙터에서 받아오게 한다
그뒤 55번째 줄에서 코루틴을 스타트한다
이제 작성한 코루틴을 보자
60번째 줄에 Color color = GetComponent<SpriteRenderer>().color;로
color변수에 스프라이트 컴포넌트를 받아온다
그뒤 알파값을 0으로 만들어주고 color변수에 적용해준다
그뒤 1프레임 뒤에 알파값을 1로 만들어서 없세주고
다시 1프레임뒤에 알파값을 0으로 만들어주고를 반복해서
우리가 보기에는 피격시 깜박깜박 거리는 것 처럼 보인다
이렇게
이제 Player오브젝트에 스크립트를 넣어보자 각종 수치가 나온다
여기서 연결할 프리팹은 지난번에 우리가 만들어준 플로잉 프리팹이고
Parent는 Hierarchy에서 Canvas를 연결해주면 된다
이제 DatabaseManager 스크립트를 열어보자
새로 작성한 부분은 빨갓게 체크해놨다
16번째 줄에는 PlayerStat를 변수화해주고, 17번째 줄은 플로잉 텍스트 프리팹이다, 18번째 줄은 부모오브젝트를 변수화 한 것이다
공개정도를 private로하고 void FloatText함수를 만들어 준다
int _number, string color로 인수를 받아온다
23번째 줄에 vector변수에 플레이어 스텟 트랜스프롬.포지션을 넣어주고
y값에는 +=60을 넣어서 플레이어 머리 위에 텍스트가 뜨게 한다
26번째 줄에는 clone변수에 인스턴시에이드를 넣어주고 프리팹을 생성하게 해준다, 위치는 23번째줄에 지정해준 위치고
쿼터니언 값은 0이다
27번째 줄에 clone변수에서 text인자를 받아온뒤 숫자를 number.ToString();로 문자화해준다
그리고 조건문으로 color인수가 GREEN이면 초록색으로 텍스트가 뜨게해주고
그게아니라 BLUE면 파랑색으로 텍스트가 뜨게 해준다
폰트 사이즈는 25이고, 부모 객체는 인스펙터에서 Canvas로 해준다
이제 UseItem 함수를 수정해주자
이제 조금 숙련됬다면 case 10001의 주석만 봐도 다 이해가 될 것이다
if문에서 플레이어 hp가 회복할 hp보다 높거나 같으면 단순하게 hp를 회복해주고
그게 아니라면 최대 hp와 같게 해준다
즉 이 얘기는 플레이어 최대 hp보다 더 많이 회복을 못하게 하는 기능이다
10002는 마나를 회복시켜주는 것이다
마나도 이와 똑같은 방향으로 작성해주었다
FloatText로 (50, "GREEN")으로 초록색 50이 뜨게 했다
이제 데이터베이스 매니져를 켜주면 다시 이렇게 맨 아래에 연결할 플로링텍스트 프리팹과 Canvas 슬롯이 있다
다 연결해준다
이제 테스트를 하면...
이렇게 체력을 회복할때 텍스트가 뜨게 된다!
'쯔꾸르식 유니티 게임 공부' 카테고리의 다른 글
31.이팩트 (0) | 2025.03.07 |
---|---|
30.몬스터 처치, 레밸 UP (1) | 2025.03.07 |
28.슬라임(몬스터) 구현 (0) | 2025.02.28 |
27.플로팅 텍스트(아이템 습득시 이름 표시) (0) | 2025.02.27 |
26.획득한 아이템 사용 (0) | 2025.02.26 |