Shooting 소리나 데미지를 입었을 때 등 효과음을 넣어주기 위해 스크립트에 AudioClip을 부른 뒤, 해당 조건에 재생되도록 한다.
public AudioClip shootingClip;
----------------- 생략 -----------------
if (shootingClip)
SoundManager.PlayClip(shootingClip);
UI
UGUI를 사용하여 UI를 구현하기 위해 Canvas를 생성하고 Scale을 Scale With Screen Size로 바꿔준 뒤, 16:9로 변경한다. Scale With Screen Size는화면이 커질수록 UI 요소도 커져서 멀티 해상도에 대응이 가능하다. 반대로 Constant PhysicalSize는 화면 크기와 해상도에 관계없이 UI 요소가 동일한 물리적인 크기로 유지된다.
UI 배치는 큰 묶음과 앵커에 신경쓰면 좋다.
앵커를 기준으로 크기와 좌표를 정한다.
현재 앵커가 중앙에 걸려있으므로 화면 사이즈가 변할 때, 중앙을 기준으로 위치가 유지된다.
Wave를 띄우기 위해 Canvas 하위에 빈 오브젝트를 생성해서 image를 추가하여 앵커로 위치를 조정한다.
생성한 캔버스를 꽉 채우기 위해서는 alt + 클릭으로 stretch를 선택하면 된다.
UI를 적당히 꾸며준 뒤, UI를 처리하는 스크립트를 GameManager에서 처리한다.
플레이어의 체력을 불러와서 %로 계산해준 뒤, Silder UI에 반영한다.
GameOver가 되면 GameOver UI가 실행되도록 하고, 버튼을 눌렀을 때 각각 게임 Scene화면을 다시 불러오고 어플리케이션을 종료시킨다. 예전에는 Application.LoadScene로 씬을 불러왔는데 이제는 사용되지 않기 때문에 SceneManager로 build Setting에 걸려있는 인덱스 번호로 불러온다.(Scene 이름으로 불러올 수도 있다.)
오브젝트 풀(Object Pool)은 오브젝트를 미리 생성해 놓은 뒤, 삭제하거나 소멸시키지 않고 재사용 하는 것이다. 생성이나 소멸을 최소화하여 메모리를 효율적으로 관리할 수 있다.
빈번히 생성되고 사라지는 총알이나 파티클 같은 사운드 등을 재사용하기 위해 자주 사용되고 있다.
오브젝트 풀 스크립트를 만들어서 ProjectileManager에 달아준다.
지정된 개수를 넘기면 마지막에 사용한 것을 또 사용한다.
[System.Serializable]
public struct Pool
{
public string tag;
public GameObject prefab;
public int size;
}
public List<Pool> pools;
public Dictionary<string, Queue<GameObject>> poolDictionary;
private void Awake()
{
poolDictionary = new Dictionary<string, Queue<GameObject>>();
foreach (var pool in pools)
{
Queue<GameObject> objectPool = new Queue<GameObject>();
for (int i = 0; i < pool.size; i++)
{
GameObject obj = Instantiate(pool.prefab);
obj.SetActive(false);
objectPool.Enqueue(obj);
}
poolDictionary.Add(pool.tag, objectPool);
}
}
public GameObject SpawnFromPool(string tag)
{
if (!poolDictionary.ContainsKey(tag))
return null;
GameObject obj = poolDictionary[tag].Dequeue();
poolDictionary[tag].Enqueue(obj);
return obj;
}
투사체를 달고 20개를 미리 풀링하도록 한다.
이제 ProjectileManager에서 임시로 만들어둔 테스트 오브젝트를 지우고 오브젝트 풀을 불러온다.
애니메이션을 바로 실행하지 않고 트랜지션으로 연결 구조를 만들기 위해 컨트롤러에 애니메이션을 상속시킨다.
Animation을 통해 StringToHash로 값을 변환하는데, StringToHash는 특정 문자열을 일정한 공식에 의해 숫자, Hash 값으로 변환하는 것이다. Animator 안에서 키값을 string으로 제공했을 때, string을 비교하는 연산이 일어나는데 그 비용이 높기 때문에 Hash값으로 변환하여 비교하는 것이다.
유니티로 돌아와서 몹들의 레이어를 Enemy로 바꿔주고 플레이어의 공격 데이터 타겟을 Enemy로 설정한다.
이번엔 죽는 처리에 대한 스크립트를 만들어준다.
OnDeath에 이벤트를 걸기 위해 HealthSystem과 Rigidbody를 가져온다. 죽었을 때, 자리에서 멈추도록 Vector3 값을 0으로 준다. 모든 컴포넌트들을 Behaviour를 상속받고 있기 때문에 Behaviour를 검색하여 죽었을 때 동작하지 않게 한다.
코드카타 알고리즘 '문자열 내림차순으로 배치하기' 문제를 풀다가 ToString()에 대해 궁금증이 생겼다.
문제 내용은 아래와 같다.
문자열을 배열에 넣어 Array.Sort()를 이용하여 풀었는데, 문자열로 출력하는 부분에서 왜 ToString으로 배열을 전환할 수 없는지 궁금해졌다.
using System;
using System.Linq;
public class Solution {
public string solution(string s)
{
char[] arr = s.ToCharArray();
Array.Sort(arr);
Array.Reverse(arr);
string answer = new string(arr);
return answer;
}
}
ToString()을 사용할 경우에 나오는 출력값은 System.Char[]이다.
C#에서 다루는 대부분은 System.Object라는 클래스를 상속받았다. 그 중 ToString()은 '이 개체가 어떤 개체인가'를 string으로 반환하는 문자열이다. Object를 상속받은 모든 클래스는 ToString()을 오버라이딩할 수 있는데, 예시로 System.Int32는 ToString()을 오버라이딩해서 정수값을 string으로 보여주라고 정의 되어있다.
즉, ToString()을 재정의하냐 안하냐에 따라 결과가 달라지는데 배열은 재정의되어 있지 않은 것이다.
쉽게 말하면 배열에서 ToString으로 문자열 변환은 안된다!
ToString을 이것저것 문자열 변환 할 때 사용했다가 안되는 경우가 많아서 그냥 단순히 숫자만 가능한가보다...하고 넘겼는데 상속과 오버라이딩이 관련 있었다. 더 자세한 내용은 아래에서 확인해보면 좋을 것 같다.