오른쪽에서 왼쪽으로 오는 노트를 스페이스로 리듬에 맞춰 누르면 플레이어가 헤엄치는 간단한 리듬게임을 기획했다.
Note 구현
빈 캔버스에 노트 컨테이너 UI를 만든다.
일단 노트가 오른쪽에서 왼쪽으로 지나가도록 하기 위해 캔버스에 스크립트를 추가한다.
public class Note : MonoBehaviour
{
private float noteSpeed = 500f;
private void Update()
{
transform.localPosition += Vector3.left * noteSpeed * Time.deltaTime;
}
}
좌표를 local로 한 이유는 캔버스 내의 좌표에서 생성되어야하기 때문이다. position으로 하면 월드좌표에 생성되기 때문에 엉뚱한 곳에 생성될 것이다.
노트의 충돌을 감지하기 위해 Rigidbody와 Collider를 붙여준다.
충돌로 인한 물리 효과는 필요 없기 때문에 Is Trigger를 체크하고 Dynamic이 아닌 Kinematic을 선택한다. 노트는 계속해서 생성할 것이기 때문에 Prefab으로 뺀다.
유니티에서 노트를 생성할 Transform을 지정하는 오브젝트를 만들어준 뒤, 비트 당 시간마다 노트를 생성시킨다.
현재 시간을 0으로 초기화하지 않는 이유는 float나 doubl은 부동 소수점 표현 방식을 사용하기 때문에 오차가 발생하여 60 / bpm을 빼줌으로써 오차를 줄이는 것이다.
public class NoteManager : MonoBehaviour
{
public float bpm;
private double currentTime; // 오차를 줄이기 위해서 double로 선언
[SerializeField] Transform noteCreate;
[SerializeField] GameObject note;
private void Update()
{
currentTime += Time.deltaTime;
if (currentTime >= 60 / bpm) // 1beat 당 시간
{
GameObject touchNote = Instantiate(note, noteCreate.position, Quaternion.identity); // 노트 생성
touchNote.transform.SetParent(this.transform); // 부모인 UI 캔버스 내에서 생성
currentTime -= 60 / bpm;
}
}
private void OnTriggerEnter(Collider other) // 오브젝트 간 충돌 감지
{
if (other.tag == "Note")
{
Destroy(other.gameObject); // 카메라 밖의 Collider와 만나면 파괴
}
}
}
노트가 계속해서 생성되고 파괴되기 때문에 나중에 오브젝트 풀 방식 으로 바꿔줄 예정이다.
노트가 나오면 음악이 나오도록 임시로 오디오 소스를 넣는다. 자동으로 재생하지 않도록 Play On Awake를 꺼준다.
오디오 소스가 붙은 오브젝트에 스크립트를 추가한다. 첫 오브젝트가 바의 Collider와 충돌하면 음악이 재생된다.
public class Audio : MonoBehaviour
{
private AudioSource audio;
private bool isMusicStart;
private void Start()
{
audio = GetComponent<AudioSource>();
}
private void OnTriggerEnter2D(Collider2D other)
{
if (!isMusicStart)
{
if (other.tag == "Note")
{
audio.Play();
isMusicStart = true;
}
}
}
}
그런데 노트가 Collider와 충돌하면 파괴되도록 했는데 왜 바와 충돌해도 파괴되지 않을까?
OnTriggerEnter 같은 이벤트 함수들은 자신->자식 순서로 이벤트가 일어나는지 확인 후, 상호작용하기 때문에 자식에 Collider가 붙어있어도 이미 자기 자신에 Collider가 붙어있기 때문에 파괴되지 않는 것이라고 예상하고 있다. 자세한건 테스트 해봐야겠다.