목차

반응형

목차

1. 개요

2. 프로토타입 게임

3. 핵심 소스코드 및 설명

4. 유니티 세팅 방법

 

1. 개요

게임 중에 음성을 인식하여 진행하는 방식의 하이퍼 캐주얼 게임이 있다.

주목할만한 점은 게임성만을 놓고 봤을때 그렇게 중독성이 있거나 하지는 않지만 유튜버들이 이목을 끌기 좋은 콘텐츠라서 유튜버들이 올리면 사람들이 많이 본다.

인플루언서들(influncer) 마케팅을 통하여 해당 게임이 성장하였다고 추측된다.

 

유튜버들이 해당 게임을 플레이하는 영상 시청률이 적지 않다.
음성인식 게임, 1000만 다운로드 이상에 반응도 매우 좋다.

 

이번 글에서는 음성인식 게임의 핵심 부분을 다뤄보고자 한다.

 

2. 프로토타입 게임

 

음성인식 프로토타입 게임

캐릭터는 계속해서 이동하고 소리를 내면(어디 아픈 사람 같지만 아니다.) 내 공의 색이 바뀐다. 공의 색이 바뀐동안 상대방 공을 때린다고 생각하면 된다.

소리를 내서 펀치나 발차기를 하는 게임이라고 상상하자

 

음성인식 프로토타입 게임 2

엄마는 게임기를 숨겼다. 느낌의 게임이라고 생각하면 된다.

소리를 내서 불량배들을 쫓아내는 게임이다.

 

3. 핵심 소스코드 및 설명

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Audio;
using UnityEngine.UI;

public class MicrophoneListener : MonoBehaviour
{
    [SerializeField] private Image _imageSound;
    [SerializeField] private Text TextVol;
    public float sensitivity = 100;
    public float loudness = 0;
    public float pitch = 0;
    AudioSource _audio;

    public float RmsValue;
    public float DbValue;
    public float PitchValue;
 
    private const int QSamples = 1024;
    private const float RefValue = 0.1f;
    private const float Threshold = 0.02f;
 
    float[] _samples;
    private float[] _spectrum;
    private float _fSample;

   //Written in part by Benjamin Outram
 
     //option to toggle the microphone listenter on startup or not
     public bool startMicOnStartup = true;
 
     //allows start and stop of listener at run time within the unity editor
     public bool stopMicrophoneListener = false;
     public bool startMicrophoneListener = false;
 
     private bool microphoneListenerOn = false;
 
     //public to allow temporary listening over the speakers if you want of the mic output
     //but internally it toggles the output sound to the speakers of the audiosource depending
     //on if the microphone listener is on or off
     public bool disableOutputSound = false; 
 
     //an audio source also attached to the same object as this script is
     AudioSource src;
 
     //make an audio mixer from the "create" menu, then drag it into the public field on this script.
     //double click the audio mixer and next to the "groups" section, click the "+" icon to add a 
     //child to the master group, rename it to "microphone".  Then in the audio source, in the "output" option, 
     //select this child of the master you have just created.
     //go back to the audiomixer inspector window, and click the "microphone" you just created, then in the 
     //inspector window, right click "Volume" and select "Expose Volume (of Microphone)" to script,
     //then back in the audiomixer window, in the corner click "Exposed Parameters", click on the "MyExposedParameter"
     //and rename it to "Volume"
     public AudioMixer masterMixer;
 
 
     float timeSinceRestart = 0;
 
     void Start() {
         //start the microphone listener
         if (startMicOnStartup) {
             RestartMicrophoneListener ();
             StartMicrophoneListener ();
             
             _audio = GetComponent<AudioSource> ();
             _audio.clip = Microphone.Start (null, true, 10, 44100);
             _audio.loop = true;
             while (!(Microphone.GetPosition(null) > 0))  {}
             _audio.Play();
             _samples = new float[QSamples];
             _spectrum = new float[QSamples];
             _fSample = AudioSettings.outputSampleRate;
             //유니티 5.x 부터는 audio source에서 mute를 하면 정상적으로 음성이 안나온다.
             //audio mixer에서 master volume의 db를 -80으로 하여 소리 출력만 안되도록 하면 된다.
             //_audio.mute = true;
         }
     }
 
     void Update(){    
         //can use these variables that appear in the inspector, or can call the public functions directly from other scripts
         if (stopMicrophoneListener) {
             StopMicrophoneListener ();
         }
         if (startMicrophoneListener) {
             StartMicrophoneListener ();
         }
         //reset paramters to false because only want to execute once
         stopMicrophoneListener = false;
         startMicrophoneListener = false;
 
         //must run in update otherwise it doesnt seem to work
         MicrophoneIntoAudioSource (microphoneListenerOn);
 
         //can choose to unmute sound from inspector if desired
         DisableSound (!disableOutputSound);
 
         loudness = GetAveragedVolume() * sensitivity;
         GetPitch();
         
         //아래는 커스텀 한 소스
         //소리가 
         if (loudness > 5f)
             _imageSound.fillAmount = 1f;
         else
         {
             _imageSound.fillAmount = 0.65f;             
         }

         FindObjectOfType<GameManager>().currentLoud = loudness;
         //TextVol.text = "vol:" + loudness;
     }
 

    float GetAveragedVolume() {
        float[] data = new float[256];
        float a = 0;
        _audio.GetOutputData (data, 0);
        foreach(float s in data) 
        {
            a+=Mathf.Abs(s);
        }
        return a/256;
    }

    void GetPitch() {
        GetComponent<AudioSource>().GetOutputData(_samples, 0); // fill array with samples
        int i;
        float sum = 0;
        for (i = 0; i < QSamples; i++)
        {
            sum += _samples[i] * _samples[i]; // sum squared samples
        }
        RmsValue = Mathf.Sqrt(sum / QSamples); // rms = square root of average
        DbValue = 20 * Mathf.Log10(RmsValue / RefValue); // calculate dB
        if (DbValue < -160) DbValue = -160; // clamp it to -160dB min
        // get sound spectrum
        GetComponent<AudioSource>().GetSpectrumData(_spectrum, 0, FFTWindow.BlackmanHarris);
        float maxV = 0;
        var maxN = 0;
        for (i = 0; i < QSamples; i++)
        { // find max 
            if (!(_spectrum[i] > maxV) || !(_spectrum[i] > Threshold))
                continue;
            maxV = _spectrum[i];
            maxN = i; // maxN is the index of max
        }
        float freqN = maxN; // pass the index to a float variable
        if (maxN > 0 && maxN < QSamples - 1)
        { // interpolate index using neighbours
            var dL = _spectrum[maxN - 1] / _spectrum[maxN];
            var dR = _spectrum[maxN + 1] / _spectrum[maxN];
            freqN += 0.5f * (dR * dR - dL * dL);
        }
        PitchValue = freqN * (_fSample / 2) / QSamples; // convert index to frequency
    }
 
     //stops everything and returns audioclip to null
     public void StopMicrophoneListener(){
         //stop the microphone listener
         microphoneListenerOn = false;
         //reenable the master sound in mixer
         disableOutputSound = false;
         //remove mic from audiosource clip
         src.Stop ();
         src.clip = null;
 
         Microphone.End (null);
     }
 
 
     public void StartMicrophoneListener(){
         //start the microphone listener
         microphoneListenerOn = true;
         //disable sound output (dont want to hear mic input on the output!)
         disableOutputSound = true;
         //reset the audiosource
         RestartMicrophoneListener ();
     }
     
     
     //controls whether the volume is on or off, use "off" for mic input (dont want to hear your own voice input!) 
     //and "on" for music input
     public void DisableSound(bool SoundOn){
         
         float volume = 0;
         
         if (SoundOn) {
             volume = 0.0f;
         } else {
             volume = -80.0f;
         }
         
         masterMixer.SetFloat ("MasterVolume", volume);
     }
 
 
 
     // restart microphone removes the clip from the audiosource
     public void RestartMicrophoneListener(){
 
         src = GetComponent<AudioSource>();
         
         //remove any soundfile in the audiosource
         src.clip = null;
 
         timeSinceRestart = Time.time;
 
     }
 
     //puts the mic into the audiosource
     void MicrophoneIntoAudioSource (bool MicrophoneListenerOn){
 
         if(MicrophoneListenerOn){
             //pause a little before setting clip to avoid lag and bugginess
             if (Time.time - timeSinceRestart > 0.5f && !Microphone.IsRecording (null)) {
                 src.clip = Microphone.Start (null, true, 10, 44100);
                 
                 //wait until microphone position is found (?)
                 while (!(Microphone.GetPosition (null) > 0)) {
                 }
                 
                 src.Play (); // Play the audio source
             }
         }
     }}

여기 업데이트 부분을 보면 loudness가 소리 크기를 받아오는 부분이고 GetPitch 함수를 통해 PitchValue에 소리의 높낮이 값을 저장한다.

 

4. 유니티 세팅 방법

위의 소스코드만 넣는다고 해서 바로 적용이 되지는 않는다.

인터넷에 돌아다니는 소스 코드를 적용해보았더니 내가 내는 소리가 출력이 되어야지 작동을 했었다.

(audio source에 mute를 넣으면 작동을 안함)

 

프로젝트 우클릭 > Create > Audio Mixer 생성

 

생성된 Audio Mixer 더블클릭 > Groups 새로 생성

 

새로 생성한 그룹의 이름을 microphone이라고 지정

 

새로 생성한 그룹 클릭 > 우측에 Volume이 보이는데 우클릭

 

첫 번째 메뉴 선택

 

우측 상단에 Exposed Parameters 생성됨 > 클릭

 

이미 이름이 지정되어 있는데 Volume으로 이름 변경

 

Master 그룹의 dB를 -80으로 변경

 

유니티 인스펙터에서 MicrophoneListener 컴포넌트에 다음과 같이 등록

반응형