GameDev/Cubidom

Cubidom - SFX 넣기 1

SMNNMN 2026. 4. 20. 23:34

이번엔 효과음(SFX)을 넣을 것이다.

현재 생각나는 구현해야하는 것은 

버튼 상호작용 효과음, 블럭 파괴,생산,생성,타격,발사 게임종료 효과음

SFX 넣기 1에서는 3D SFX에 대한 것들만 구현할 것이다. 

사용 에셋

https://assetstore.unity.com/packages/audio/sound-fx/free-sound-effects-pack-155776?srsltid=AfmBOood1q-2Scat_MEkHZHilcPcgzexXEFXRquXO7R_lOc3tll1M38z

https://assetstore.unity.com/packages/audio/sound-fx/hints-stars-points-rewards-sound-effects-lite-pack-295538

https://assetstore.unity.com/packages/audio/sound-fx/free-ui-soundpack-239372

 

 

3D SFX 출력 함수 만들기

게임 공간안에서 발생하는 효과음인 3D SFX은 BGM과 다르게 공간적 사운드를 활용할 것이다. 

소리가 나는 3D 공간에 소리가 들어있는 오브젝트를 생성해서 해당 공간에 소리를 발생시키는 식으로 만들 것이다. 

사용할 효과음들을 빈 오브젝트에 넣은채 Prefab으로 사용한다.

AudioManager

       [SerializeField] AudioSource effectAudio; // 효과음 담당 오디오

    public void EffectPlay(GameObject clipObject,Vector3 pos) // 효과음 실행 함수
    {
        GameObject audioClip = Instantiate(clipObject, pos, Quaternion.identity);
        StartCoroutine(EffectFinishCheck(audioClip.GetComponent<AudioSource>()));
        Debug.Log("dd");
    }
    
    IEnumerator EffectFinishCheck(AudioSource source) // 이펙트가 끝이 났는지 확인하는 코드
    {
        while(source != null && source.isPlaying)
        {
            yield return new WaitForSecondsRealtime(0.5f);
        }
        if(source != null)
        {
            Destroy(source.gameObject);
        }
    }

EffectPlay 함수는 소리 오브젝트와 생성 위치를 받아서 소리를 생성시킨다. 

EffectFinishCheck 함수는 생성된 소리가 끝이 나면 해당 오브젝트를 제거하는 코루틴 함수이다. 0.5초마다 판단한다. 

 

Audio Sorce 설정

3D 공간에서 생성되는 소리이기 때문에 설정을 따로 해주어야 한다. 

가장 중요한 설정은 공간 블렌드로, 3D쪽으로 슬라이더하여 공간 소리임을 설정한다. 

도플러 수준의 경우 0이 아닌값이라면 Audio Listener(Main Camera)가 움직이게 될 경우 소리가 이상하게 들리기 때문에 0으로 맞춰준다. 

또한 이 게임에서 카메라는 Orthographic 이기 때문에, 스크롤로 카메라 시점과 오브젝트와 가까워 져도 카메라자체는 움직이지 않기때문에, 볼륨 롤오프를 선형으로 하여, 멀리있어도 잘 들릴수 있게 하였다. 

 

Cannon 관련 효과음 출력

Cannon

    [SerializeField] GameObject cannonCreateClip; // 대포가 생성될 때 나는 효과음
    [SerializeField] GameObject cannonFireClip; // 포탄이 발사될 때
        void Start()
    {
        StartCoroutine(FireBall());
        lineRenderer.positionCount = 2;

        switch(myCaster)
        {
            case InputController: lineRenderer.material = lineMaterials[0]; break;
            case EnemyAI: lineRenderer.material = lineMaterials[1]; break;
        }

        AudioManager.Instance.EffectPlay(cannonCreateClip, transform.position); // 생성될때, 효과음 출력
    }
        IEnumerator FireBall()
    {

        Vector3 center = transform.position + standardDir * 5;
        Vector3 halfExtent = new Vector3(3, 3, 5);
        Quaternion orientation = Quaternion.Euler(standardDir.y * 90, standardDir.x * 90, 0);
        InsRangeCube(center, orientation);
        while (true)
        {

			~~~~~~~~~~~~~~~
            
            
            if (target != null)
            {
            
            
				~~~~~~~~~~~~~~~

                Instantiate(cannonBall, cannonBallPos.position, cannonRotation.rotation);

                AudioManager.Instance.EffectPlay(cannonFireClip, cannonBallPos.position); // 포탄 발사 효과음 출력
            }

        }
    }

대포가 생성될 때 출력되는 효과음은 Start에서 실행하고, 포탄이 발사될 때 출력되는 효과음은 발사이후 포탄 생성위치에서 실행된다. 

 

CannonBall

        [SerializeField] GameObject cannonAttackClip; // 타격 될때 효과음

    private void OnTriggerEnter(Collider other)
    {
        // Debug.Log("충돌");
        Cube contactCube = other.gameObject.GetComponent<Cube>();
        if (contactCube != null)
        {
            // Debug.Log(contactCube.gameObject.name);
            contactCube.OnDamage(damage); // 닿은 큐브의 체력 감소

            contactCube.DamageEffect(transform.position); // 충돌 이펙트 주기

            GameObject popUpText = Instantiate(onDamagePopUp_T, transform.position, Quaternion.identity);
            popUpText.GetComponent<PopUpText>().TextSetUp(damage);

            AudioManager.Instance.EffectPlay(cannonAttackClip, transform.position); // 타격 효과음

            Destroy(gameObject);
        }
        
        if(other.gameObject.layer == wallIndex)
        {
            Destroy(gameObject);
        }
    }

큐브 타격시 실행되는 효과음을 큐브 타격후 소리가 발생하게끔 추가해주었다. 

 

한번에 많은 효과음 처리하기

많은 대포가 생성되면 소리가 중첩되어서 매우 커지기 때문에, 이를 해결하기 위한 몇가지 방법을 사용했다. 

pitch 조절

    public void EffectPlay(GameObject clipObject,Vector3 pos) // 효과음 실행 함수
    {
        GameObject audioClip = Instantiate(clipObject, pos,Quaternion.identity);
        audioClip.GetComponent<AudioSource>().pitch = Random.Range(0.8f, 1.2f);
        StartCoroutine(EffectFinishCheck(audioClip.GetComponent<AudioSource>()));
        Debug.Log("dd");
    }

오디오의 pitch를 랜덤으로 조절하여 같은 주파수가 중첩되지 않도록 한다. 

 

Audio Mixer 사용하기

여러 효과음들을 한 그룹으로 묶어서 특정 데시벨보다 소리가 커지지 못하게 막는 기능을 사용할 것이다. 

Audio Mixer의 Compressor를 사용해서 전체 데시벨의 한계점을 정해준다. 

이후 효과음들의 Output을 새로 만든 SFX 그룹에 넣어준다. 

비교

Compressor가 없는 상태
Compressor가 있는 상태

 

자원 생산 효과음

Resource Producer

    [SerializeField] GameObject resourceClip; // 생산될 때 출력될 효과음

	IEnumerator Produce() // 자원을 생산한다.
    {
        for(int i = 0; i < repeatNum; i++)
        {
            yield return new WaitForSeconds(delayTime);
            float upYPos = 0.75f / repeatNum;
            Vector3 currentPos = transform.position + transform.up * upYPos;
            transform.position = currentPos;
        }

        GameObject popUpText = Instantiate(resourcePopUp_T, transform.position,Quaternion.identity);
        popUpText.GetComponent<PopUpText>().TextSetUp(resourceValue);

        EffectManager.Instance.PlayParticle(resourcePati, transform.position); // 생산 파티클 생성

        AudioManager.Instance.EffectPlay(resourceClip, transform.position);

        myCaster.totalResource += resourceValue;
        // GameManager.Instance.totalResource += resourceValue;
        transform.position = initialPos;
        StartCoroutine(Produce());
    }

자원이 생산될 때, 효과음을 출력한다 .

 

블럭 관련 효과음

CubeCreator

블럭 생성 효과음


    [SerializeField] GameObject blockClip; // 블럭 생성 효과음
	public void InsCube(Vector3 dir, Transform cube, Caster caster) // 큐브가 맞은 방향과 위치를 계산하여 큐브를 생성한다.
    {
		~~~~~~~~~~~~~~~
        
        AudioManager.Instance.EffectPlay(blockClip, prefab.transform.position);

        DefinePos(dir, cube, prefab, prefab.GetComponent<Cube>());
        CasterCheck(prefab, caster);
    }
    public void Conversion(Vector3 dir, Transform cube, Caster caster) // 큐브 우클릭 시 큐브에 기능추가 함수
    {
		~~~~~~~~~~~~~~~~~~~~~
        
        AudioManager.Instance.EffectPlay(blockClip, prefab.transform.position);

        DefinePos(dir, cube ,prefab, prefab.GetComponent<Structure>());
        ConnectingCube(prefab, cube);
        CasterCheck(prefab, caster);
    }

대포를 제외한 block 즉, 큐브와 자원생산자를 생성했을 때, 효과음이 출력되게끔 하였다. 

 

Cube

큐브 파괴 효과음

    void OnDelete() // hp가 0이하 되었을 때 사라지는 하무
    {
        Debug.Log(gameObject.name + "사라짐");
        EffectManager.Instance.PlayParticle(onDestroyPati, transform.position); // 파괴 이펙트 실행

        AudioManager.Instance.EffectPlay(onDestroyClip, transform.position);

        CubeDissapear?.Invoke();
        isdead = true;
        CubeCreator.Instance.DeleteBlock(this, myCaster);
        // Destroy(gameObject);
    }

큐브가 파괴될 때, 효과음이 출력된다.