일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 프로그래머스
- 입문
- 백준
- 수학
- 2025년
- 2024년
- c++
- 6월
- 개인 프로젝트
- 2023년
- 유니티
- 1월
- 10월
- 개인 프로젝트 - 런앤건
- 2월
- C/C++
- 골드메탈
- 다이나믹 프로그래밍
- 단계별로 풀어보기
- 자료 구조
- 게임 엔진 공부
- 4월
- 2022년
- 5월
- 코딩 테스트
- 3월
- todolist
- 유니티 심화과정
- 코딩 기초 트레이닝
- 기초
- Today
- Total
기록 보관소
[Unity/유니티] 기초-탑다운 2D RPG: 대화창 UI 구축하기[B22] 본문
개요
유니티 입문과 독학을 위해서 아래 링크의 골드메탈님의 영상들을 보며 진행 상황 사진 또는 캡처를 올리고 배웠던 점을 요약해서 적는다.
현재는 영상들을 보고 따라하고 배우는 것에 집중할 것이며, 영상을 모두 보고 따라한 후에는 개인 프로젝트를 설계하고 직접 만드는 것이 목표다.
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 RPG: 대화창 UI 구축하기[B22]
1. 대화창 UI
- 대화창 UI는 대부분의 탑다운 2D RPG 게임에서 화면 아래쪽에 조금 가득 채우듯이 존재한다. 그래서 UI로 사용할 스프라이트에 Sprite Editor를 사용해 Border 값을 주고 Image Type을 Sliced로 변경해서, 이미지 크기를 변경했을때 깨지지 않도록 한다.
- 그 후에 앵커를 활용해 Alt 키와 Shift키를 눌려 아래쪽으로 고정시키고, 위 캡처의 버튼처럼 채우기 모양의 버튼을 클릭하면 이미지가 변경된다. 이때 포지션 값이 Left와 Right로 바뀌는데, 이는 양쪽 여백을 뜻한다. 또한 Pos Y 값을 주면 아래쪽에도 여백이 생긴다.
- 텍스트에 사용할 폰트도 다운받아서 사용했다. 폰트는 유튜브 설명란의 넥슨 메이플스토리 서체를 사용했다. 다운 받은 후에는 압축을 풀고 그냥 폰트 파일들을 유니티 에셋창에 넣어두었다.
- 이후 텍스트의 Font 항목에 사용할 폰트를 끌어다 넣고, 색상과 크기를 약간 바꾸었다. 그리고 일반적인 탑다운 2D RPG에서는 대화창의 텍스트가 왼쪽 상단부터 출력되므로, 앵커와 Alt키, Shift키를 사용해서 중심을 왼쪽에 배치하여 여백과 크기를 설정했다.
2. 데이터 전달
//GameManager 스크립트 파일
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class GameManager : MonoBehaviour {
public Text talkText;
public GameObject scanObject;
public void Action (GameObject scanObj) {
scanObject = scanObj;
talkText.text = "이것의 이름은 " + scanObject.name + "이라고 한다.";
}
}
//PlayerAction 스크립트 파일
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerAction : MonoBehaviour {
public float Speed;
public GameManager manager;
Rigidbody2D rigid;
Animator anim;
Vector3 dirVec;
GameObject scanObject;
float h;
float v;
bool isHorizonMove;
void Awake () {
rigid = GetComponent<Rigidbody2D>();
anim = GetComponent<Animator>();
}
void Update () {
//이동 값 설정
h = Input.GetAxisRaw("Horizontal");
v = Input.GetAxisRaw("Vertical");
//이동 버튼 확인
bool hDown = Input.GetButtonDown("Horizontal");
bool vDown = Input.GetButtonDown("Vertical");
bool hUp = Input.GetButtonUp("Horizontal");
bool vUp = Input.GetButtonUp("Vertical");
//이동 방향 체크
if (hDown || vUp) //수평
isHorizonMove = true;
else if (vDown || hUp) //수직
isHorizonMove = false;
else if (hUp || vUp)
isHorizonMove = h != 0;
//애니메이션 전환
if (anim.GetInteger("hAxisRaw") != h) {
anim.SetBool("isChange", true);
anim.SetInteger("hAxisRaw", (int)h);
}
else if (anim.GetInteger("vAxisRaw") != v) {
anim.SetBool("isChange", true);
anim.SetInteger("vAxisRaw", (int)v);
}
else
anim.SetBool("isChange", false);
//레이 방향 판단하기
if (vDown && v == 1) //위쪽 방향
dirVec = Vector3.up;
else if (vDown && v == -1) //아래쪽 방향
dirVec = Vector3.down;
else if (hDown && h == -1) //왼쪽 방향
dirVec = Vector3.left;
else if (hDown && h == 1) //오른쪽 방향
dirVec = Vector3.right;
//오브젝트 스캔 출력
if (Input.GetButtonDown("Jump") && scanObject != null)
manager.Action(scanObject);
}
void FixedUpdate() {
//수평, 수직 이동 결정
Vector2 moveVec = isHorizonMove ? new Vector2(h, 0) : new Vector2(0, v);
rigid.velocity = moveVec * Speed;
//레이 사용하기
Debug.DrawRay(rigid.position, dirVec * 0.7f, new Color(0,1,0));
RaycastHit2D rayHit = Physics2D.Raycast(rigid.position, dirVec, 0.7f, LayerMask.GetMask("Object"));
if (rayHit.collider != null) {
scanObject = rayHit.collider.gameObject; //RayCast된 오브젝트를 변수로 저장
}
else
scanObject = null;
}
}
- 지난 글에서 Ray를 사용해 플레이어가 조사 가능한 오브젝트(레이어를 Object로 설정)의 이름을 콘솔에 출력했었다. 이번 시간에는 대화창 UI를 만들었으니 이를 사용해서 출력하게 만들었다.
- 참고로 이전에 픽셀 퍼펙트를 적용하면서 해상도를 키웠더니 화면 크기가 작아져서 확대한채로 진행했었는데, 이번에 추가된 대화창은 확대에 영향을 받지 않아서 결국 메인 카메라의 해상도를 1/3정도로 낮춰서 크기가 맞게 했다.(341 x 256)
3. 상태 전환
//GameManager 스크립트 파일
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class GameManager : MonoBehaviour {
public GameObject talkPanel;
public Text talkText;
public GameObject scanObject;
public bool isAction; //대화창 활성화 여부 체크
public void Action (GameObject scanObj) {
if (isAction) { //대화창 비활성화
isAction = false;
}
else { //대화창 활성화
isAction = true;
scanObject = scanObj;
talkText.text = "이것의 이름은 " + scanObject.name + "이라고 한다.";
}
talkPanel.SetActive(isAction);
}
}
//PlayerAction 스크립트 파일
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerAction : MonoBehaviour {
public float Speed;
public GameManager manager;
Rigidbody2D rigid;
Animator anim;
Vector3 dirVec;
GameObject scanObject;
float h;
float v;
bool isHorizonMove;
void Awake () {
rigid = GetComponent<Rigidbody2D>();
anim = GetComponent<Animator>();
}
void Update () {
//이동 값 설정
h = manager.isAction ? 0 : Input.GetAxisRaw("Horizontal");
v = manager.isAction ? 0 : Input.GetAxisRaw("Vertical");
//이동 버튼 확인
bool hDown = manager.isAction ? false : Input.GetButtonDown("Horizontal");
bool vDown = manager.isAction ? false : Input.GetButtonDown("Vertical");
bool hUp = manager.isAction ? false : Input.GetButtonUp("Horizontal");
bool vUp = manager.isAction ? false : Input.GetButtonUp("Vertical");
//이동 방향 체크
if (hDown || vUp) //수평
isHorizonMove = true;
else if (vDown || hUp) //수직
isHorizonMove = false;
else if (hUp || vUp)
isHorizonMove = h != 0;
//애니메이션 전환
if (anim.GetInteger("hAxisRaw") != h) {
anim.SetBool("isChange", true);
anim.SetInteger("hAxisRaw", (int)h);
}
else if (anim.GetInteger("vAxisRaw") != v) {
anim.SetBool("isChange", true);
anim.SetInteger("vAxisRaw", (int)v);
}
else
anim.SetBool("isChange", false);
//레이 방향 판단하기
if (vDown && v == 1) //위쪽 방향
dirVec = Vector3.up;
else if (vDown && v == -1) //아래쪽 방향
dirVec = Vector3.down;
else if (hDown && h == -1) //왼쪽 방향
dirVec = Vector3.left;
else if (hDown && h == 1) //오른쪽 방향
dirVec = Vector3.right;
//오브젝트 스캔 출력
if (Input.GetButtonDown("Jump") && scanObject != null) {
manager.Action(scanObject);
}
}
void FixedUpdate() {
//수평, 수직 이동 결정
Vector2 moveVec = isHorizonMove ? new Vector2(h, 0) : new Vector2(0, v);
rigid.velocity = moveVec * Speed;
//레이 사용하기
Debug.DrawRay(rigid.position, dirVec * 0.7f, new Color(0,1,0));
RaycastHit2D rayHit = Physics2D.Raycast(rigid.position, dirVec, 0.7f, LayerMask.GetMask("Object"));
if (rayHit.collider != null) {
scanObject = rayHit.collider.gameObject; //RayCast된 오브젝트를 변수로 저장
}
else
scanObject = null;
}
}
- 대화창은 원래 기본적으로 대화나 조사 같은 특정 이벤트 때만 활성화되어야 하므로, 비활성화한다. 이후 스크립트 파일에 코드를 작성해서 isAction이라는 bool 변수를 활용해서 특정 오브젝트 앞에서 스페이스바를 눌렸을때 true가 되어 대화창이 뜨고, 다시 누르면 대화창이 꺼지면서 false가 되도록 한다.
- 또한 대화할 때 플레이어가 움직여서 이탈할 가능성이 있으므로, 대화창이 활성화 되었을 때는 PlayerAction 스크립트 파일의 이동 값과 이동 버튼 확인 변수들을 변경하여 플레이어 이동을 제한하도록 했다.
4. UI 애니메이션
- 대화창 UI의 오른쪽 아래에 있는 커서는 따로 애니메이션 스프라이트가 존재하지 않고, 단순하게 위 아래로 움직이는 것이므로 이전과는 달리 직접 애니메이터 컨트롤러와 애니메이션을 생성했다.
'유니티 프로젝트 > 탑다운 2D RPG' 카테고리의 다른 글
[Unity/유니티] 기초-탑다운 2D RPG: 대화 애니메이션 느낌있게 만들기[B25] (0) | 2022.02.21 |
---|---|
[Unity/유니티] 기초-탑다운 2D RPG: 퀘스트 시스템 구현하기[B24] (0) | 2022.02.20 |
[Unity/유니티] 기초-탑다운 2D RPG: 대화 시스템 구현하기[B23] (0) | 2022.02.19 |
[Unity/유니티] 기초-탑다운 2D RPG: 쯔꾸르식 액션 구현하기[B21] (0) | 2022.02.17 |
[Unity/유니티] 기초-탑다운 2D RPG: 도트 타일맵으로 쉽게 준비하기[B20] (0) | 2022.02.17 |