2019년 11월 26일 화요일

10. 블럭 속도와 효과음 추가하기

이번 장에서는 블럭이 떨어지는 거리에 따라 속도를 다르게 적용해 자연스러운 드롭을 구현하고, 스와이프와 매칭 블럭이 제거되는 경우에 효과음이 플레이되도록 할 것이다.

실행화면은 아래와 같다.

소스 코드 다운로드

https://github.com/ninez-entertain/BingleStarter 에서 소스코드를 다운받을 수 있다. >git clone https://github.com/ninez-entertain/BingleStarter.git 또한, 각 진행 과정은 개별 branch로 제공되며 소스코드 다운로드 후에 각 스텝 별로 브랜치를 이용할 수 있다. 이번 장의 브랜치는 "step-10"으로 아래 git 명령어로 이번 장의 브랜치로 바로 이동할 수 있다. git checkout step-10

이번에 작업할 내용은 다음과 같다.
  • 거리별 블럭 드롭 속도 적용하기
  • 효과음 플레이하기

거리별 블럭 드롭 속도 적용하기

지난 장에서 구현한 실행 화면을 살펴보면 블럭이 떨어지는 모습이 자연스럽지 않다. 거리에 관계없이 블럭이 떨어지는 시간은 항상 300ms로 동일하다. 블럭 한개의 거리(1미터)가 떨어지든 3개의 거리(3미터)가 떨어지든 거리에 관계없이 300ms 동안 해당 거리를 이동한다. 그래서 3미터 떨어지는 경우 아주 빠르게 떨어지고 거리가 멀어질 수록 속도는 더 늘어난다. 그에 비해 한 블럭이 떨어지는 경우에는 느리다는 느낌을 받게 된다.

이번 장에는 거리별로 다른 속도로 떨어지도록 구현해보자.

거리별 속도 설정하기

Scriptable Asset으로 등록한 BlockConfig에 거리별 속도를 설정하는 멤버를 추가하여 BlockActionBehaviour에서 사용할 수 있도록 할 것이다.
public class BlockConfig : ScriptableObject
{
    public float[] dropSpeed;
    public Sprite[] basicBlockSprites;

    // -- 중략 --
}
파일명.cs
3 거리별로 속도를 입력할 수 있도록 배열을 선언한다.
9x9 보드에서 최대 떨어지는 거리가 9 미터이므로 크기 9인 배열을 추가할 것이다.

BlockConfig Asset에 거리별 속도 입력하기 

다음 순서로 거리별 속도를 추가한다.
  1. Assets/Config/BlockConfig 를 선택한다.
  2.  Inspector에서 Drop Speed를 선택한다.
  3.  Size에 9을 입력하고 엔터를 입력한다.
  4. 아래와 같이 Element 0 ~ 8의 값을 입력한다.
    • Element 0 : 0.2
    • Element 1 : 0.3
    • Element 2 : 0.35
    • Element 3 : 0.4
    • Element 4 : 0.425
    • Element 5 : 0.45
    • Element 6 : 0.475
    • Element 7 : 0.5
    • Element 8 : 0.525
'Element 0'에 입력된 값이 떨어지는 거리가 1행인 경우에 사용하는 블럭의 속도이고, 'Element 2'가 3행의 거리를 떨어지는 블럭의 속도이다.

BlockActionBehaviour 멤버 추가하기

BlockActionBehaviour에서 BlockConfig를 참조할 수 있도록 멤버를 추가한다.
public class BlockActionBehaviour : MonoBehaviour
{
    [SerializeField] BlockConfig m_BlockConfig;
    public bool isMoving { get; set; }
    // -- 중 략 --
}
BlockActionBehaviour.cs

BlockActionBehaviour 컴포넌트에 BlockConfig 등록하기

다음과 같이 Block Prefab에 등록된 BlockActionBehaviour의 Block Config 속성에 Block Config Asset을 설정한다.
  1. Assets/Prefabs/Block 을 선택한다.
  2. Inspector에서 [Open Prefab]을 선택한다.
  3. Prefab이 오픈되고, Inspector에서 BlockActionBehaviour 컴포넌트에 [Block Config] 속성이 나타나는 것을 확인한다.
  4. Assets/Config/BlockConfig를 드래그해서, 3에서 선택한 Prefab의 BlockActionBehaviour | Block Config에 드롭한다.

거리별 속도 적용하기

Block Config에 설정한 거리별 속도 정보를 이용해서 블럭 속도를 다르게 적용한다.
IEnumerator DoActionMoveDrop(float acc = 1.0f)
{
    isMoving = true;

    while (m_MovementQueue.Count > 0)
    {
        Vector2 vtDestination = m_MovementQueue.Dequeue();

        int dropIndex = System.Math.Min(9, System.Math.Max(1, (int)Mathf.Abs(vtDestination.y)));
        float duration = m_BlockConfig.dropSpeed[dropIndex-1];

        yield return CoStartDropSmooth(vtDestination, duration * acc);
    }

    isMoving = false;
    yield break;
}
BlockActionBehaviour.cs
9 떨어지는 거리에 해당되는 인덱스를 구한다.
거리는 1행에서 9행까지이며 인덱스 0에서 8의 값을 갖는다.
10 Block Config Asset에서 드롭 속도를 구한다.

계속해서, 더이상 사용하지 않는 상수 DROP_TIME은 Constants에서 삭제한다.
namespace Ninez.Core
{
    public static class Constants
    {
        public static float BLOCK_ORG = 0.5f;           //블럭의 출력 원점
        public static float SWIPE_DURATION = 0.2f;      //블럭 스와이프 애니메이션 시간
        public static float DROP_TIME = 0.3f;           //블럭이 떨어지는 시간
    }
}
Core/Constants.cs

실행하기

플레이버튼(▶)을 클릭해서 블럭이 떨어지는 속도를 확인해보자.
가로줄로 제거되는 경우와 세로줄로 매칭된 블럭이 떨어지는 속도가 다르게 적용되어 조금 더 자연스럽게 보일 것이다.

효과음 적용하기

스와이프 액션과 3매치 블럭이 제거될 때 효과음을 플레이 해보자.

사운드 파일을 아래와 같이 준비한다.
  1. 아래에서 2개의 파일을 다운로드한다.
    1. 위치 :https://github.com/ninez-entertain/BingleStarter
    2. 파일 : BlockClear.wav, Chomp.wav
  2. Assets/Sounds 폴더를 생성하고, 1에서 다운로드한 사운드 파일을 복사한다.

SoundManager 클래스 작성하기

씬의 사운드 플레이를 전담하는 SoundManager 클래스를 아래와 같이 추가한다. (위치 : Utils/SoundManager.cs)
namespace Ninez.Util
{
    public enum Clip
    {
        Chomp       = 0,
        BlcokClear  = 1
    };

    public class SoundManager : MonoBehaviour
    {
        public static SoundManager instance;
        private AudioSource[] sfx;

        /**
         * 시작시 컴포넌트를 구해서 singleton으로 저장한다.
         * 어디에서나 SoundManager.PlayOnShot()으로 사운드를 플레이할 수 있다.
         */
        void Start()
        {
            instance = GetComponent<SoundManager>();
            sfx = GetComponents<AudioSource>();
        }

        public void PlayOneShot(Clip audioClip)
        {
            sfx[(int)audioClip].Play();
        }

        /*
         * 지정된 볼륨으로 AudioClip을 플레이한다.
         */
        public void PlayOneShot(Clip audioClip, float volumeScale)
        {
            AudioSource source = sfx[(int)audioClip];
            source.PlayOneShot(source.clip, volumeScale);
        }
    }
}

Utils/SoundManager.cs
3 - 7 등록된 AudioSource에 접근하기 위한 인덱스를 enum 타입으로 정의한다.
Clip.Chomp는 '0'으로 첫번째 AudioSource 컴포넌트를 가리킨다.
예를 들어, 첫번재 AudioSource를 플레이하려면 SoundManager.PlayOnShot(Clip.Chomp)와 같이 사용한다.
11 static으로 선언하여 싱글톤과 같은 역할을 하도록 한다.
12 AudioSource 컴포넌트를 참조하는 배열. AudioSource 컴포넌트가 여러 개 등록되기 때문에 배열로 참조한다.
20 씬 시작시에 SoundManager 컴포넌트의 참조를 구해서 static 멤버에 저장한다.
이제부터 위치에 관계없이 SoundManager.PlayOnShot(...)으로 사운드를 플레이할 수 있다.
21 GameObject에 등록된 모든 AudioSource 컴포넌트의 배열을 구해서 sfx 멤버가 참조하도록 한다.
24 - 27 사운드를 플레이하는 메소드.
audioClip : 플레이할 AudioSouce의 인덱스 번호에 해당되는 enum 타입
32 - 36 지정된 볼륨으로 사운드를 플레이하는 메소드.
audioClip : 플레이할 AudioSouce의 인덱스 번호에 해당되는 enum 타입
volumeScale : 볼륨

SoundManager를 플레이 씬에서 사용해보자.

Sound GameObject 추가하기

아래 과정으로 씬에 SoundManager와 AudioSouce를 추가한다.
  1. 씬에 Empty GameObject를 추가하고 이름을 'Sound'로 변경한다.
  2. Inspector에 새로추가한 Sound GameObject 컴포넌트의 정보가 출력되는지 확인한다.
  3. SoundManager 클래스를 Sound GameObject의 컴포넌트로 등록한다.
    Utils/SoundManager.cs를 드래그해서 Sound GameObject의 Inspector에 드롭한다.
  4. Inspector 하단의 [Add Component] 버튼을 클릭해서 'Audio Source' 컴포넌트를 2개 추가한다.
  5. 첫번째 Audio Source의 [Audio Clip] 속성에 Chomp.wav를 설정한다.
  6. 두번째 Audio Source의 [Audio Clip] 속성에 BlockClear.wav를 설정한다
  7. Audio Source의 [Play On Awake] 속성은 반드시 체크상태를 해제한다.


위의 과정을 진행하면 아래와 같이 등록된 AudioSouce 컴포넌트를 볼 수 있다.
볼류(Volume)이나 Loop 같은 세부 속성을 설정할 수 있기 때문에 변경 및 관리가 용이하다.

효과음 플레이하기

스와이프 효과음과 블럭 제거시 효과음을 플레이 해보자.

스와이프 액션 효과음 적용하기

ActionManager에서 스와이프 액션을 시작할 때 효과음을 아래와 같이 플레이 한다.
IEnumerator CoDoSwipeAction(int nRow, int nCol, Swipe swipeDir)
{
    if (!m_bRunning)  //다른 액션이 수행 중이면 PASS
    {
        m_bRunning = true;    //액션 실행 상태 ON

        SoundManager.instance.PlayOneShot(Clip.Chomp);

        //1. swipe action 수행
        Returnable bSwipedBlock = new Returnable(false);
        yield return m_Stage.CoDoSwipeAction(nRow, nCol, swipeDir, bSwipedBlock);

        //2. 스와이프 성공한 경우 브드를 평가(매치블럭삭제, 빈블럭 드롭, 새블럭 Spawn 등)한다.
        if (bSwipedBlock.value)
        {
            Returnable bMatchBlock = new Returnable(false);
            yield return EvaluateBoard(bMatchBlock);

            //스와이프한 블럭이 매치되지 않은 경우에 원상태 복귀
            if (!bMatchBlock.value)
            {
                yield return m_Stage.CoDoSwipeAction(nRow, nCol, swipeDir, bSwipedBlock);
            }
        }

        m_bRunning = false;  //액션 실행 상태 OFF
    }
    yield break;
}
ActionManager.cs

블럭 제거 효과음 적용하기

매칭된 블럭이 발견되는 경우에 Block Clear 효과음을 플레이한다. 블럭이 드롭되기 전에 플레이한다.
IEnumerator EvaluateBoard(Returnable matchResult)
{
    while (true)    //매칭된 블럭이 있는 경우 반복 수행한다.
    {
        //1. 매치 블럭 제거
        Returnable bBlockMatched = new Returnable(false);
        yield return StartCoroutine(m_Stage.Evaluate(bBlockMatched));

        //2. 3매치 블럭이 있는 경우 후처리 싱행 (블럭 드롭 등)
        if (bBlockMatched.value)
        {
            matchResult.value = true;

            SoundManager.instance.PlayOneShot(Clip.BlcokClear);

            // 매칭 블럭 제거 후 빈블럭 드롭 후 새 블럭 생성
            yield return StartCoroutine(m_Stage.PostprocessAfterEvaluate());

            //yield return new WaitForSeconds(0.2f);
        }
        //3. 3매치 블럭이 없는 경우 while 문 종료
        else
            break;  
    }

    yield break;
}
ActionManager.cs

실행하기

플레이버튼(▶)을 클릭해서 사운드가 플레이 되는지 확인해보자.

다음 장에서는 블럭이 사라질 때 효과를 출력하도록 하겠습니다.

문의사항 및 잘못된 부분은 댓글 및 메일(ninez.entertain@gmail.com)으로 부탁드립니다.
감사합니다.

댓글 없음:

댓글 쓰기