이제 John Lemon 프로젝트의 모든 학습을 마쳤다.
하지만 여기에서 멈출 수 없다.
해당 프로젝트는 학회에 제출하기 위한 포트폴리오의 역할을 하는 프로젝트였고,
추가 기능을 (무엇이든) 구현할 수록 가산점을 부여했기 때문에, 이대로 제출하기에는 많이 아쉬웠다.
그래서 학습을 하면서 떠오른 아이디어 여러가지를 구현해보기로 마음먹었다.
John Lemon의 애니메이션이다. 굉장히 소심하고 두려움에 떠는 이미지로 보였다.
이를 보고 다음과 같은 아이디어가 떠올랐다.
"무서움을 많이 타니까, 주변에 귀신들이 많을 수록 더 무서워하면 어떨까?"
귀신과 가까워질 수록 무서워서 발이 얼어붙는다.
▶ 플레이어와 적의 거리가 가까울 수록 공포 수치가 올라가고, 이동속도가 느려진다.
적 오브젝트에 Enemy Tag를 추가한다.
플레이어에 적 오브젝트를 인식할 수 있는 트리거를 추가한다.
적이 플레이어의 트리거와 충돌 중일 경우, 거리를 계산하여 플레이어의 이동 속도를 조절한다.
공포 수치를 시각화하는 UI를 만든다.
Enemies 태그를 생성한 후 Prefab에서 추가해주었다.
콜라이더 오브젝트를 생성하여 플레이어 오브젝트의 자식에 넣어주었다.
public class FearnessCollider : MonoBehaviour {
public GameObject player;
private float distanceFromNeariestEnemy = 3f;
private List<GameObject> nearEnemies = new List<GameObject>();
private void OnTriggerEnter(Collider other) {
if (other.CompareTag("Enemies"))
nearEnemies.Add(other.gameObject);
}
private void OnTriggerStay(Collider other) {
if (other.CompareTag("Enemies"))
distanceFromNeariestEnemy = SetDistanceFromNeariestEnemy();
}
private float SetDistanceFromNeariestEnemy() {
List<float> distances = new List<float>();
Vector3 v;
for (int i = 0; i < nearEnemies.Count; i++) {
v = nearEnemies[i].transform.position - player.transform.position;
distances.Add(v.magnitude);
}
float min = float.MaxValue;
foreach (float f in distances) {
if (f < min)
min = f;
}
return min;
}
private void OnTriggerExit(Collider other) {
if (other.CompareTag("Enemies"))
nearEnemies.Remove(other.gameObject);
}
private void Update() {
if (nearEnemies.Count == 0)
distanceFromNeariestEnemy = 3f;
}
public float GetDistanceFromNeariestEnemy() {
return distanceFromNeariestEnemy;
}
public int GetNumOfEnemies() {
return nearEnemies.Count;
}
}
private void OnTriggerEnter(Collider other) {
if (other.CompareTag("Enemies"))
nearEnemies.Add(other.gameObject);
}
적이 트리거에 들어올(OnTriggerEnter) 경우, nearEnemies 리스트에 적 오브젝트 정보를 저장한다.
private void OnTriggerExit(Collider other) {
if (other.CompareTag("Enemies"))
nearEnemies.Remove(other.gameObject);
}
적이 트리거에서 나갈(OnTriggerExit) 경우, nearEnemies 리스트에 저장된 해당 적 오브젝트 정보를 삭제한다.
private void OnTriggerStay(Collider other) {
if (other.CompareTag("Enemies"))
distanceFromNeariestEnemy = SetDistanceFromNeariestEnemy();
}
적이 트리거에 계속 있는(OnTriggerStay) 경우, 가장 가까운 적과의 거리를 리턴하는 SetDistanceFromNeariestEnemy() 함수로 전역 변수 distanceFromNeariestEnemy 에 값을 저장한다.
private float SetDistanceFromNeariestEnemy() {
List<float> distances = new List<float>();
Vector3 v;
for (int i = 0; i < nearEnemies.Count; i++) {
v = nearEnemies[i].transform.position - player.transform.position;
distances.Add(v.magnitude);
}
float min = float.MaxValue;
foreach (float f in distances) {
if (f < min)
min = f;
}
return min;
}
SetDistanceFromNeariestEnemy() 함수는 nearEnemies 에 저장된 오브젝트들과의 거리를 계산하여 float 리스트 distances 에 저장하고, distances 리스트에 저장된 값 중 가장 작은 값을 리턴한다.
private void Update() {
if (nearEnemies.Count == 0)
distanceFromNeariestEnemy = 3f;
}
Update() 함수에서는 근처에 적이 없을 경우, distanceFromNeariestEnemy 값을 3f로 초기화한다. 값이 3f인 이유는 트리거의 radius를 3으로 설정했기 때문이다.
이렇게 얻은 distanceFromNeariestEnemy 값은 플레이어의 이동 속도를 조절하는 데에 사용된다.
작성한 스크립트는 트리거 오브젝트에 추가하였다.
이동 속도라는 개념을 추가하기 위해 PlayerMovement 스크립트를 수정해야 한다.
Vector3 movement;
float moveSpeed = 1.3f;
private void FixedUpdate() {
...
movement.Set(horizontal, 0f, vertical);
movement.Normalize();
movement *= moveSpeed;
...
}
public void SetMoveSpeed(float speed) {
moveSpeed = speed;
}
moveSpeed 라는 변수를 추가하여 movement 벡터에 값을 곱해주어 이동 속도를 구현하였다.
또한 SetMoveSpeed() 함수를 만들어 외부에서 moveSpeed 를 바꿀 수 있게 했다.
PlayerFearness 스크립트는 플레이어의 이동속도를 조절하는 스크립트이다.
이전에 FearnessCollider 스크립트로 얻은 distanceFromNeariestEnemy 값으로 새로운 moveSpeed 를 계산하고, 이를 PlayerMovement 스크립트의 moveSpeed 에 적용하는 것이 이 스크립트의 역할이다.
코드 전문은 다음과 같다.
public class PlayerFearness : MonoBehaviour {
public FearnessCollider fearnessCollider;
public PlayerMovement playerMovement;
private float fearnessSpeed;
private void Update() {
if (fearnessCollider.GetNumOfEnemies() != 0) {
fearnessSpeed = SetFearnessSpeed();
playerMovement.SetMoveSpeed(fearnessSpeed);
}
else
playerMovement.SetMoveSpeed(1.3f);
}
private float SetFearnessSpeed() {
float fearnessSpeed = 0.5f + fearnessCollider.GetDistanceFromNeariestEnemy() * 0.1f;
return fearnessSpeed;
}
}
만약 FearnessCollider에 충돌한 적이 있으면 fearnessSpeed 를 계산하여 이를 적용한다.
fearnessCollider.GetNumOfEnemies() 은 콜라이더에 충돌 중인 적의 수를 리턴한다.
적이 없으면 기본 속도인 1.3f를 적용한다.
fearnessSpeed 계산 법은 최소 속도가 0.5f가 나오도록 (실제로는 0.7f ~ 1.3f) 설정하였다.
해당 스크립트를 플레이어 오브젝트에 추가한다.
UI에서 슬라이드 바를 생성하였다.
적용할 스크립트는 다음과 같이 작성하였다.
public class FearnessBarUI : MonoBehaviour {
public Slider fearnessBar;
public PlayerMovement playerMovement;
private void Update() {
fearnessBar.value = 1.3f - playerMovement.GetMoveSpeed();
}
}
value 값 설정은 의도했다기 보단, 여러 방법을 시도해보고 자연스러워 보이는 정도를 찾았다.
조금 엉성하지만 마음에 드는 결과를 얻을 수 있었다.
속도가 줄어드니 조금 더 쫄리는 맛이 있어 만족스러웠다. ‡