2D 오디오는 특정 위치에서 방출되지 않는 비방향성 소리로, 청취자의 위치나 방향에 관계없이 동일하게 유지됩니다. 이는 플레이어가 환경에서 자신의 청취기를 이동할 때 어디서든 2D 오디오를 일관되게 들을 수 있음을 의미합니다.
Gingerbread House - Start .rbxl 파일을 시작점으로 사용하고 Gingerbread House - Complete Audio를 참조하여, 이 튜토리얼에서는 경험에 루핑 및 일회성 2D 오디오를 추가하는 방법을 보여주며 다음에 대한 안내를 포함합니다:
- 플레이어가 서버에 연결되자마자 재생되는 루프 배경 음악.
- 게임 플레이 목표에 따라 플레이어에게 즉각적인 피드백을 제공하기 위한 오디오 트리거.
- 다양한 사용자 인터페이스 상호작용을 나타내기 위해 다양한 소리 활성화.
과정 중에 막히게 되면 Gingerbread House - Complete Audio를 참조하여 진행 상황을 비교할 수 있습니다.
2D 오디오 객체
비방향성 오디오를 생성하려면 이 튜토리얼 전반에 걸쳐 작업할 오디오 객체를 이해하는 것이 중요합니다. 2D 오디오에는 세 가지 주요 오디오 객체가 있습니다:
- AudioPlayer 객체는 오디오 파일을 로드하고 재생합니다.
- AudioDeviceOutput 객체는 스피커나 헤드폰과 같은 물리적 하드웨어 장치입니다.
- Wires는 한 객체에서 다른 객체로 오디오 스트림을 전송합니다.
이러한 모든 오디오 객체는 파일 재생에서 각 플레이어의 하드웨어로 소리를 전달하기 위해 함께 작동합니다. 이제 플레이어가 헤드셋을 착용하고 노트북으로 경험을 즐기는 사례를 통해 이 작동 방식을 살펴보겠습니다:
- AudioPlayer는 127645268874265 오디오 자산 ID를 사용해 통화 구매 소리를 경험에 로드합니다.
- AudioDeviceOutput 객체는 소리를 플레이어의 물리적 스피커, 또는 이 경우는 헤드폰으로 전달합니다.
다음 섹션에서는 루핑과 일회성 2D 오디오를 재생하는 방법을 배울 때 이 객체들을 더욱 심도 깊게 참조합니다. 향후 기술과 함께 이 객체들을 검토하면서 경험에서 플레이어로 소리를 포착하고 전달하는 방법을 보다 정확하게 예측할 수 있습니다.
루핑 오디오
루핑 2D 오디오, 또는 플레이어가 서버에 연결되자마자 즉시 반복되는 비방향성 오디오는 플레이어가 무엇을 하든 어디에 위치해 있든 상관없이 응집력 있는 청각 배경을 제공하는 일반적인 사운드 디자인 기법입니다. 또한, 루핑 2D 오디오는 경험이 완전히 침묵하지 않도록 보장하여, 플레이어가 목표를 잊고 방향을 잃지 않도록 도움을 줍니다.
이 개념을 설명하기 위해, 다음 2D 오디오가 위험한 우주 정거장 샘플의 품질을 어떻게 크게 향상시키는지를 리뷰해 보십시오. 배경 음악이 없으면 우주 정거장은 비어 있고 생명력이 없습니다. 그러나 우주 정거장에 배경 음악이 포함되면 우주 정거장은 불길하고 위험하게 느껴져 전체적인 으스스한 분위기를 더합니다.
루핑 오디오는 플레이어가 경험에 참여하자마자 플레이어에게 어떤 느낌을 주고 싶은지를 설정하는 데 특히 유용합니다. 예를 들어, 샘플은 플레이어에게 겨울 원더랜드에서 즐거운 모험을 제공하려고 하여, 플레이어가 환경에 들어서는 순간 긍정적인 기분을 유도하기 위해 경쾌한 오디오 트랙을 사용합니다. 이 기법이 얼마나 영향을 미칠 수 있는지 보기 위해, 루핑 오디오를 금속 트랙으로 교체했을 경우 플레이어의 경험이 어떻게 달라질지를 고려해 보십시오.
샘플 Gingerbread House - Complete Audio 장소 파일의 루핑 2D 오디오를 재현하려면:
플레이어 캐릭터에 연결된 기본 청취기를 활성화합니다.
- Explorer 창에서 SoundService를 선택합니다.
- Properties 창에서 DefaultListenerLocation을 Character로 설정합니다. 경험을 실행하면 엔진이 자동으로 SoundService 아래에 AudioDeviceOutput을 생성합니다.
Explorer 창에서 SoundService로 이동한 다음:
- 오디오 소스를 생성하기 위해 AudioPlayer 객체를 삽입합니다.
- 경험 전반에 걸쳐 재생되는 스피커를 만들기 위해 AudioDeviceOutput 객체를 삽입합니다.
- 오디오 플레이어에서 스피커까지 스트림을 전송하기 위해 Wire 객체를 삽입합니다.

AudioPlayer를 선택한 다음 Properties 창에서,
- AssetID를 rbxassetid://1841461968로 설정하여 경쾌한 오디오 트랙을 재생합니다.
- 오디오가 매끄럽게 반복되도록 Looping을 활성화합니다.
- Volume을 0.2로 설정하여 다른 오디오 소스를 들을 수 있도록 낮은 볼륨으로 재생합니다.
Wire를 선택한 다음 Properties 창에서,
- 이 특정 오디오 플레이어에서 오디오를 전송하도록 SourceInstance를 새 AudioPlayer로 설정합니다.
- 이 특정 스피커로 오디오를 전송하도록 TargetInstance를 새 AudioDeviceOutput으로 설정합니다.
Explorer 창으로 돌아가 AudioPlayer에 Script를 삽입하고 이름을 LoopBackgroundMusic으로 변경한 다음, 스크립트에 다음 코드를 붙여넣습니다:
local audioPlayer = script.ParentaudioPlayer:Play()코드 설명이 스크립트는 스크립트의 부모를 나타내는 변수를 선언하는 것으로 시작합니다 AudioPlayer. 그런 다음 플레이어가 경험에 참여하는 순간부터 종료할 때까지 오디오 소스가 재생되도록 설정합니다.
경험을 플레이테스트하여 루핑 배경 음악을 들어보십시오.
일회성 오디오
일회성 2D 오디오, 또는 특정 시간과 위치에서 한 번 재생되며 플레이어가 다시 트리거하지 않는 한 반복되지 않는 비방향성 오디오는 플레이어가 놓치지 않도록 중요한 맥락을 제공합니다. 이러한 유형의 오디오 피드백을 경험에 사용하는 것은 필수적이며, 주요 게임플레이 기능 및 플레이어 행동에 대한 즉각적이고 명확한 반응을 제공합니다. 예를 들어, 보스 전투를 시작하거나 상당한 양의 체력을 잃기 등의 순간에 필요한 것입니다.
다음 섹션에서는 게임 플레이 피드백, UI 요소 상호작용 및 튜토리얼에 대한 음성 해설 등 플레이어가 시기 적절하고 비방향적인 피드백을 필요로 하는 일반적인 게임플레이 시나리오에 대한 구현 세부정보를 제공합니다.
게임 플레이 피드백
플레이어가 경험을 진행하면서, 점수, 체력 또는 레벨 진행 상황과 같이 게임 플레이에 영향을 미치는 것의 상태에 대한 즉각적인 청각 피드백을 받는 것이 중요합니다. 이러한 유형의 오디오는 플레이어 목표에 직접 연결되므로, 이러한 소리는 명확하고, 들릴 수 있으며, 유용하고 정확한 정보를 전달하는 것이 중요합니다.
이 개념을 설명하기 위해, 다음은 플레이어가 점프하고 동전을 수집하는 동안 재생되는 플랫포머 템플릿의 일회성 2D 오디오입니다:
- 플레이어가 장치에서 점프 키를 누르자마자 경쾌한 수치 스우시 소리가 재생됩니다.
- 플레이어의 캐릭터가 동전과 접촉하는 순간 높은 음의 딩 소리와 동전 소리가 재생됩니다.
이 두 소리는 3D 공간의 맥락 내에서 즉시 인식되며, 플레이어들이 시각적 신호에만 의존하지 않고 자신의 행동의 성공을 이해하는 데 도움을 줍니다.
여러 소리를 동시에 재생할 때, 특정 소리가 플레이어가 듣기 더 중요한 경우가 있습니다. 이 경우 동전 수집 소리는 점프 소리보다 귀중한 게임 플레이 정보를 전달하므로, 경험에서는 약간 더 높은 볼륨으로 재생됩니다. 그러나 뛰어난 믹스를 만들기 위한 기준이나 규칙은 없으므로, 경험의 소리는 중요하다고 생각되는 것들을 반영하도록 조정하십시오.
예를 들어, 샘플은 경험 전반에 걸쳐 다양한 오디오 트리거를 포함하고 있지만, 모두 경험의 주요 목적을 이해하는 데 동일한 수준의 중요성을 갖고 있지는 않습니다: 진저브레드 하우스를 열기 위해 젤리빈을 수집합니다. 플레이어에게 해야 할 일을 알리기 위해 샘플은 플레이어가 젤리빈을 수집할 때마다 청각 피드백의 진폭을 세 배로 증가시킵니다.
샘플 Gingerbread House - Complete Audio 장소 파일에서 일회성 상태 피드백 오디오를 재현하려면:
Explorer 창에서 Workspace ⟩ Gumdrops ⟩ Gumdrop으로 이동한 다음:
- 오디오 소스를 생성하기 위해 AudioPlayer 객체를 삽입합니다.
- 경험 전반에 걸쳐 재생되는 스피커를 만들기 위해 AudioDeviceOutput 객체를 삽입합니다.
- 오디오 플레이어에서 스피커까지 스트림을 전송하기 위해 Wire 객체를 삽입합니다.

AudioPlayer를 선택한 다음 Properties 창에서,
- AssetID를 rbxassetid://9113723699로 설정하여 부드러운 씹는 소리 트랙을 재생합니다.
- TimePosition을 0.15로 설정하여 실제 씹는 소리와 더 가까운 시점에서 오디오 트랙이 시작됩니다.
- Volume을 3으로 설정하여 경험 내의 다른 오디오 소리보다 젤리빈 소리가 더 잘 들리도록 높은 볼륨으로 재생합니다.
Wire를 선택한 다음 Properties 창에서,
- 이 특정 오디오 플레이어에서 오디오를 전송하도록 SourceInstance를 새 AudioPlayer로 설정합니다.
- 이 특정 스피커로 오디오를 전송하도록 TargetInstance를 새 AudioDeviceOutput으로 설정합니다.
Gumdrop을 세 개 가지도록 중복시키고, 환경에 흩어 뿌립니다.
Explorer 창으로 돌아가 ServerScriptService로 이동한 후, Script를 삽입하고 이름을 GumdropService로 변경한 다음, 다음 코드를 붙여넣습니다:
-- 서비스 초기화local Workspace = game:GetService("Workspace")local Players = game:GetService("Players")local ServerStorage = game:GetService("ServerStorage")local TweenService = game:GetService("TweenService")-- 모듈local Leaderboard = require(ServerStorage.Leaderboard)local PlayerData = require(ServerStorage.PlayerData)-- 변수local gumdropsFolder = Workspace.Gumdropslocal gumdrops = gumdropsFolder:GetChildren()local GUMDROP_KEY_NAME = PlayerData.GUMDROP_KEY_NAMElocal GUMDROP_AMOUNT_TO_ADD = 1local function updatePlayerGumdrops(player, updateFunction)-- 젤리빈 테이블 업데이트local newGumdropAmount = PlayerData.updateValue(player, GUMDROP_KEY_NAME, updateFunction)-- 젤리빈 리더보드 업데이트Leaderboard.setStat(player, GUMDROP_KEY_NAME, newGumdropAmount)-- 플레이어가 세 개의 젤리빈을 수집할 경우 확인if newGumdropAmount >= 3 then-- 플레이어가 세 개의 젤리빈을 수집할 때 문 이벤트 오디오 재생local audioPlayer = Workspace.Door.AudioPlayeraudioPlayer:Play()-- 문을 아래로 이동시키는 애니메이션local doorPart = Workspace.Doorlocal tweenInfo = TweenInfo.new(2, Enum.EasingStyle.Linear)local tween = TweenService:Create(doorPart, tweenInfo, {Position = doorPart.Position + Vector3.new(0, -15, 0)})tween:Play()endend-- 이벤트 핸들러 정의하기local function onGumdropTouched(otherPart, gumdrop)if gumdrop:GetAttribute("Active") thenlocal character = otherPart.Parentlocal player = Players:GetPlayerFromCharacter(character)if player then-- 플레이어가 젤리빈에 닿음local audioPlayer = gumdrop.AudioPlayeraudioPlayer:Play()gumdrop.Transparency = 1gumdrop:SetAttribute("Active", false)updatePlayerGumdrops(player, function(oldGumdropAmount)oldGumdropAmount = oldGumdropAmount or 0return oldGumdropAmount + GUMDROP_AMOUNT_TO_ADDend)endendend-- 이벤트 리스너 설정for _, gumdrop in gumdrops dogumdrop:SetAttribute("Active", true)gumdrop.Touched:Connect(function(otherPart)onGumdropTouched(otherPart, gumdrop)end)end코드 설명이 스크립트는 Workspace, Players, ServerStorage, 및 Class.TweenService 서비스를 초기화하여 자식 및 기능을 참조할 수 있도록 합니다. 그런 다음 ServerStorage에서 Leaderboard 및 PlayerData 모듈을 요구하며, 이 모듈들은 플레이어가 환경에서 수집한 젤리빈의 양을 추적하는 화면 오른쪽 상단에 리더보드를 만드는 데 책임이 있습니다.
스크립트의 updatePlayerGumdrops 함수는 주로 플레이어가 젤리빈을 수집할 때마다 리더보드를 업데이트하고, 그들이 세 개의 젤리빈을 수집할 때까지 기다리며, 그 시점에 위치 기반 소리 및 애니메이션을 트리거합니다. 이 스크립트 부분에 대한 자세한 내용은 3D 오디오 추가 - 이벤트 피드백을 참조하세요.
스크립트의 onGumdropTouched 함수는 게임 플레이 피드백을 위한 2D 오디오 트리거에 대한 대부분의 작업을 수행하며, 두 개의 인수를 받습니다:
- otherPart - 젤리빈과 충돌하는 파트입니다.
- gumdrop - 젤리빈을 나타내는 구형 파트입니다.
어떤 것이 젤리빈 부품과 충돌하면, 스크립트는 먼저 젤리빈이 활성 상태인지 확인합니다. 그렇다면 충돌하는 객체가 플레이어인지 확인합니다. 그 후 스크립트는 다음을 수행합니다:
- 오디오 플레이어에서 2D 오디오 트랙 재생.
- 젤리빈을 보이지 않게 설정.
- 플레이어가 보이지 않는 젤리빈과 상호작용할 수 없도록 비활성화합니다.
- 젤리빈을 수집한 플레이어를 위해 리더보드 업데이트.
마지막으로, 스크립트는 각 젤리빈에 대해 Touched 이벤트에 함수를 연결하여, 플레이어가 젤리빈을 터치할 때 onGumdropTouched 함수를 호출하도록 리스터를 설정합니다.
경험을 플레이테스트하여 젤리빈을 수집한 후 씹는 소리를 들어보십시오.
UI 상호작용
플레이어가 체크박스를 호버링하거나 구매 버튼을 선택하는 등 사용자 인터페이스(UI) 요소와 상호작용할 때, 즉각적인 피드백을 제공하여 그들이 UI와 어떻게 상호작용하고 있는지를 직관적으로 이해하도록 돕는 것이 중요합니다. 예를 들어, 많은 디자이너는 플레이어가 설정을 활성화하거나 비활성화할 때 UI의 시각적 특성을 변경하여 설정의 상태를 전달합니다.
그러나 시각적 장애가 있는 플레이어의 경우 색상 변화나 애니메이션을 혼자서 해석하기 어려울 수 있으므로, 가능한 한 많은 플레이어가 접근 가능하고 직관적으로 이해할 수 있도록 UI 상호작용에 대한 여러 형태의 감각 피드백을 제공하는 것이 유용합니다. 이 개념을 확장하기 위해, UGC 홈스토어 템플릿의 구매 버튼에 중요한 다음 UI 상태를 고려해 보겠습니다:
- 포커스 상태 - 플레이어가 선택을 위해 UI 요소를 강조하고 있음을 나타냅니다.
- 호버 상태 - 플레이어가 커서로 UI 요소 위에 마우스를 올려놓고 있음을 나타냅니다.
- 선택됨 - 플레이어가 입력을 통해 UI 요소를 선택했음을 나타냅니다.
이 세 가지 상호작용은 템플릿의 주요 사용자 흐름인 플레이어가 아바타 의상과 액세서리를 탐색하고 선택하며 구매하는 데 필수적이므로, 누군가가 UI와 상호작용하는 방법을 혼동하지 않도록 하는 것이 매우 중요합니다. 성공적으로 플레이어를 지원하기 위해, 템플릿에서는 각 UI 상호작용에 대해 시각적 및 청각적 피드백을 제공합니다.




여러 형태의 감각 피드백을 구성할 수 있는 방법의 예를 제시하기 위해, 샘플에서는 플레이어가 화면상의 민트 버튼을 누를 때마다 시각적 및 청각적 피드백을 제공합니다. 플레이어가 버튼과 상호작용하지 않을 때에는 일반 민트 사탕처럼 보이지만, 버튼을 누르면 샘플에서는:
- 즐거운 멜로디 오디오 트랙이 재생됩니다.
- 버튼에 청록색이 입혀집니다.
- 버튼이 화면에서 약간 아래로 이동합니다.
여기에서 이 상호작용을 메뉴를 열거나 아이템을 구매하는 등 유용한 게임 플레이 작업과 연결할 수 있습니다.


샘플 Gingerbread House - Complete Audio 장소 파일에서 일회성 UI 상호작용 오디오를 재현하려면:
Explorer 창에서 StarterGui ⟩ 2DAudioButton으로 이동한 다음:
- 오디오 소스를 생성하기 위해 AudioPlayer 객체를 삽입합니다.
- 경험 전반에 걸쳐 재생되는 스피커를 만들기 위해 AudioDeviceOutput 객체를 삽입합니다.
- 오디오 플레이어에서 스피커까지 스트림을 전송하기 위해 Wire 객체를 삽입합니다.

AudioPlayer를 선택한 다음 Properties 창에서 AssetID를 rbxassetid://3422389728로 설정하여 복고풍의 멜로디 오디오 트랙을 재생합니다.
Wire를 선택한 다음 Properties 창에서,
- 이 특정 오디오 플레이어에서 오디오를 전송하도록 SourceInstance를 새 AudioPlayer로 설정합니다.
- 이 특정 스피커로 오디오를 전송하도록 TargetInstance를 새 AudioDeviceOutput으로 설정합니다.
Explorer 창으로 돌아가 2DAudioButton에 LocalScript를 삽입하고 이름을 PlayAudioWhenPressed로 변경한 다음, 스크립트에 다음 코드를 붙여넣습니다:
local TweenService = game:GetService("TweenService")local buttonGui = script.Parentlocal buttonImageButton = buttonGui.ButtonFrame.ButtonImageButtonlocal buttonAudioPlayer = buttonGui.AudioPlayerlocal tweenInfo = TweenInfo.new(.2, Enum.EasingStyle.Exponential)local buttonTweenByIsPressed = {-- 버튼 눌림[true] = TweenService:Create(buttonImageButton, tweenInfo, {Position = buttonImageButton.Position + UDim2.fromScale(0, .1),ImageColor3 = Color3.fromRGB(117, 255, 255),}),-- 기본 상태[false] = TweenService:Create(buttonImageButton, tweenInfo, {Position = buttonImageButton.Position,ImageColor3 = Color3.fromRGB(255, 255, 255),}),}local function onIsPlayingChanged()local isPlaying = buttonAudioPlayer.IsPlayinglocal tween = buttonTweenByIsPressed[isPlaying]tween:Play()endonIsPlayingChanged()buttonAudioPlayer:GetPropertyChangedSignal("IsPlaying"):Connect(onIsPlayingChanged)buttonAudioPlayer.Ended:Connect(onIsPlayingChanged)buttonImageButton.Activated:Connect(function(_hit)buttonAudioPlayer:Play()end)코드 설명스크립트는 다음을 가져오는 것으로 시작합니다:
- UI 요소를 애니메이션화하기 위해 TweenService.
- 스크립트의 부모인 2DAudioButton ScreenGui 객체.
- 민트 사탕 ImageButton.
- 관련 오디오 플레이어와 멜로디 오디오 트랙.
그런 다음 스크립트는 다음을 정의합니다:
- 버튼 애니메이션이 기하급수적인 애니메이션 스타일로 재생될 것임을 지정하는 TweenInfo 객체.
- 버튼이 눌린 상태 또는 눌리지 않은 상태를 나타내는 두 개의 트윈.
- true 눌린 상태는 버튼을 화면에서 약간 아래로 이동시키고, 청록색으로 칠합니다.
- false 눌리지 않은 상태는 버튼을 화면에서 원래 위치로 되돌리고 이전 그라데이션을 제거합니다.
나머지 스크립트는 2D UI 상호작용 피드백을 트리거하는 데 대부분의 작업을 수행하며, onIsPlayingChanged 함수와 이벤트 리스터가 UI 사용자 흐름을 위해 어떻게 함께 작동하는지를 살펴보겠습니다:
- buttonImageButton.Activated는 플레이어가 버튼을 클릭하기를 기다린 다음 오디오 플레이어에서 관련 오디오를 재생하기 위해 Play() 함수를 호출합니다. 이 과정은 AudioPlayer.IsPlaying 속성을 false에서 true로 전환합니다.
- buttonAudioPlayer:GetPropertyChangedSignal("IsPlaying")는 오디오 플레이어의 IsPlaying 속성의 변화를 감지하고, 그 후 onIsPlayingChanged 함수가 호출됩니다.
- onIsPlayingChanged 함수는 이 정보를 사용하여 화면에서의 시각적 변화를 트리거하는 개체를 유도합니다.
- 플레이어가 빠르게 버튼을 클릭해 오디오를 우연히 다시 시작하는 것을 방지하기 위해, buttonAudioPlayer.Ended는 오디오 플레이어가 재생을 종료한 후 다시 onIsPlayingChanged 함수를 호출하도록 설정합니다.
onIsPlayingChanged 이벤트는 오직 false에서 true로 변경될 때만 발생하며, 이는 서버에서 클라이언트로 속성을 복제하는 타이밍 때문에 발생하는 복잡성으로 인한 의도된 동작입니다. 따라서 이 예제에서는 두 가지 케이스를 모두 다루기 위해 Ended 이벤트가 제공됩니다.
경험을 플레이테스트하여 화면의 버튼을 누를 때마다 멜로디를 들어보십시오.