반응형
SMALL
현재까지 구상한 성소의 기능은 모두 구현, (남은거라면 제작한 UI를 붙이는 것 정도 이다.)
데이터 연동과 그외의 자잘한 마무리들은 씬들을 이어 붙이면서 추가예정.
구현한 부분을 정리해보면
- Main 스크립트로 플레이어 및 기타 오브젝트 관리
- EventDispatcher 를 이용한 이벤트 관리 (버블링 제거)
- 자유롭게 각각의 다른 타이밍에 움직이는 배경 NPC 들
- 씬 전환용 포탈
- NPC 혹은 오브젝트따라 내용이 달라지는 팝업
전체 코드는 아래와 같다.
SanturarySceneMain 스크립트
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using static EventDispatcher;
public class SanturarySceneMain : MonoBehaviour
{
private Camera mainCam;
[SerializeField]
private GameObject player;
[SerializeField]
private UINPCPopupDirector NPCPopup;
[SerializeField]
private SanctuaryPlayerSpawnPoint spawnPoint;
[SerializeField]
private SanctuaryDungeonPortal portal;
private void Awake()
{
this.mainCam = Camera.main;
this.NPCPopup.Init();
this.spawnPoint.Init();
this.portal.onPlayerGoToThePortal = () => { Debug.Log(" 기사단장과의 대화 후 던전 씬으로 전환 "); };
EventDispatcher.Instance.AddListener<Vector2>(EventName.OnUINPCPopup, (NPCPos) =>
{
this.StartCoroutine(this.PopupSetOn(NPCPos));
});
this.player = GameObject.Instantiate(this.player);
this.player.transform.position = this.spawnPoint.transform.position;
}
private void LateUpdate()
{
Vector3 playerPos = new Vector3(this.player.transform.position.x, this.player.transform.position.y, -11);
this.mainCam.transform.position = playerPos;
}
private IEnumerator PopupSetOn(Vector2 NPCPos)
{
this.NPCPopup.gameObject.SetActive(!this.NPCPopup.gameObject.activeSelf);
this.NPCPopup.gameObject.transform.position = new Vector2(NPCPos.x, NPCPos.y + 2);
yield return null;
}
}
UINPCPopupDirector 스크립트
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using static EventDispatcher;
public class UINPCPopupDirector : MonoBehaviour
{
[SerializeField]
private Text txtNPCName;
[SerializeField]
private Button btnPopup;
[SerializeField]
private Text txtSelect;
public void Init()
{
this.btnPopup.onClick.AddListener(() =>
{
Debug.Log("각 NPC에 맞는 팝업 띄우기");
});
EventDispatcher.Instance.AddListener<string>(EventName.UINPCPopupUpdate, this.UpdateNPCPopup);
this.gameObject.SetActive(false);
}
private void UpdateNPCPopup(string NPCName)
{
switch (NPCName)
{
case "Nun":
NPCName = "수녀";
this.txtSelect.text = "대화하기";
break;
case "Merchant":
NPCName = "상인";
this.txtSelect.text = "대화하기";
break;
case "DepoistResult":
NPCName = "이전 전투 결과";
this.txtSelect.text = "확인하기";
break;
}
this.txtNPCName.text = NPCName;
}
}
KnightsController 스크립트
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class KnightsController : MonoBehaviour
{
private Animator anim;
private float moveSpeed = 0.5f;
public float time;
private bool isMoving = false;
private void Awake()
{
this.anim = GetComponent<Animator>();
this.StartCoroutine(this.MoveNPC());
}
private Vector2 previousDirection;
private IEnumerator MoveNPC()
{
float waitTime = Random.Range(2f, 3f);
yield return new WaitForSeconds(waitTime);
while (true)
{
if (!this.isMoving)
{
Vector2 direction = Vector2.zero;
this.isMoving = true;
int randomDirection = Random.Range(0, 4);
switch (randomDirection)
{
case 0: // 상
direction = Vector2.up;
break;
case 1: // 하
direction = Vector2.down;
break;
case 2: // 좌
direction = Vector2.left;
break;
case 3: // 우
direction = Vector2.right;
break;
}
if (direction == -this.previousDirection)
{
direction = this.previousDirection;
}
else if (direction == this.previousDirection)
{
direction = -this.previousDirection;
}
this.previousDirection = direction;
if (direction.x < 0)
{
this.transform.rotation = Quaternion.Euler(0, -180, 0);
}
else
{
this.transform.rotation = Quaternion.Euler(0, 0, 0);
}
float distance = Random.Range(0.5f, 1f);
float elapsedTime = 0;
while (elapsedTime < distance / this.moveSpeed)
{
this.anim.SetInteger("state", 1);
this.transform.Translate(direction * this.moveSpeed * Time.deltaTime);
elapsedTime += Time.deltaTime;
yield return null;
}
this.anim.SetInteger("state", 0);
waitTime = Random.Range(1f, 5f);
yield return new WaitForSeconds(waitTime);
this.isMoving = false;
this.previousDirection = direction;
}
yield return null;
}
}
}
SanctuaryDungeonPortal 스크립트
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SanctuaryDungeonPortal : MonoBehaviour
{
public System.Action onPlayerGoToThePortal;
private void OnTriggerEnter2D(Collider2D collision)
{
if(collision.gameObject.tag == "Player")
{
this.onPlayerGoToThePortal();
}
}
}
NPCController 스크립트 * 실제 컨트롤 하는 건 없다보니 이름을 바꿀지 고민해보아야겠다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class NPCController : MonoBehaviour
{
private void OnTriggerEnter2D(Collider2D collision)
{
if(collision.transform.gameObject.tag == "Player")
{
EventDispatcher.Instance.Dispatch(EventDispatcher.EventName.UINPCPopupUpdate, this.name);
EventDispatcher.Instance.Dispatch<Vector2>(EventDispatcher.EventName.OnUINPCPopup,this.transform.position);
}
}
private void OnTriggerExit2D(Collider2D collision)
{
if (collision.transform.gameObject.tag == "Player")
{
EventDispatcher.Instance.Dispatch<Vector2>(EventDispatcher.EventName.OnUINPCPopup, this.transform.position);
}
}
}
SanctuaryPlayerSpawnPoint 스크립트
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SanctuaryPlayerSpawnPoint : MonoBehaviour
{
[SerializeField]
private List<SpriteRenderer> runes;
private float lerpSpeed = 100;
private Color currentColor;
private Color targetColor;
public void Init()
{
this.currentColor = Color.white;
foreach (var rune in this.runes)
{
rune.color = this.currentColor;
}
this.StartCoroutine(StartGlow());
}
private IEnumerator StartGlow()
{
float elapsedTime = 0f;
float duration = 50f / (2f * this.lerpSpeed);
while (true)
{
elapsedTime += Time.deltaTime;
float alpha = Mathf.Abs(Mathf.Sin(elapsedTime / duration * Mathf.PI));
this.targetColor = new Color(1f, 1f, 1f, alpha);
this.currentColor = Color.Lerp(this.currentColor, this.targetColor, this.lerpSpeed * Time.deltaTime);
foreach (var rune in this.runes)
{
rune.color = this.currentColor;
}
yield return new WaitForSeconds(0.05f);
}
}
}
이중 가장 까다로운게 의외로 KnightsController 스크립트 였다.
NPC가 하나의 스크립트로 모두 다른 타이밍에 움직여야 하며 각각의 기사들은 오브젝트의 바운더리를 벗어나면 안되었고 한번 가는 방향으로는 계속 가지 못하게 하는게 까다로운듯 하다.
아직 좀 더 자연스러우려면 개선 사항이 필요하지만 일단 그럴싸 해 보이는 수준으로 보이니 퀄업 기간에 업데이트를 해야할듯싶다.
반응형
LIST
'프로젝트 > 건즈앤 레이첼스' 카테고리의 다른 글
[유니티 프로젝트] 본 프로젝트용 맵 씬 제작 (0) | 2023.04.20 |
---|---|
[유니티 프로젝트] 카메라 hit shaking 이펙트 & 스크린 flash 이펙트 코루틴 사용 구현 (0) | 2023.04.19 |
[유니티 프로젝트] 간단한이벤트 디스패쳐 EventDispatcher 제작 및 사용 (0) | 2023.04.17 |
[유니티 프로젝트] 로그라이크 허브 마을 (기억의 성소) 제작 .2 (레이어 정리 및 월드 팝업 UI) (0) | 2023.04.16 |
[유니티 프로젝트] 로그라이크 허브 마을 (기억의 성소) 제작 .1 (1) | 2023.04.15 |