Carrot
본문 바로가기
Unity/멋쟁이사자처럼 부트캠프

[멋쟁이사자처럼부트캠프] 유니티 게임 개발 5기(19일차) - Video 심층 학습 및 TV 리모컨 구현부터 게임 빌드까지!

by 독기품은토끼 2025. 6. 11.
✅ 오늘의 학습 목표
1. Video 심층 학습 - 카메라와 비디오의 기능 살펴보기
2. TV와 리모컨 구현으로 Video 실습해보기
3. Cat 횡 스크롤 게임에 적용해보기
4. 게임 빌드

 

1. Frustum(프러스텀)

카메라가 렌더링 할 수 있는 공간의 형태를 나타내는 것

1. Camera

  • Clipping Planes : 카메라가 렌더링 하는 최소/최대 거리 범위
    • Near (가까운 클립 거리): 이 거리보다 가까운 오브젝트는 렌더링 되지 않음
    • Far (먼 클립 거리): 이 거리보다 먼 오브젝트는 렌더링되지 않음.
  • Field of View : 카메라가 한 번에 볼 수 있는 시야의 각도(°)
  • Culling Mask : 카메라가 어떤 레이어에 속한 오브젝트만 볼 것인지 선택하는 설정

 

2. Video

Render Mode 용도 설명 
Far Plane 배경 영상 카메라의 Far Clip Plane에 영상 출력. 다른 3D 오브젝트보다 뒤쪽에 위치.
Near Plane UI 위에 표시되는 영상 카메라의 Near Clip Plane에 출력. 영상이 UI 위에 나타나며 항상 보이게 됨.
Render Texture 영상 텍스처를 자유롭게 활용 영상이 Render Texture로 출력됨. 이를 RawImage, 머티리얼 등에 활용 가능.
Material Override 3D 오브젝트에 직접 영상 출력 선택한 GameObject의 머티리얼 텍스처에 영상이 입혀짐.
API Only 커스텀 처리 Unity에서 자동 렌더링하지 않고, 코드로만 제어 가능. 프레임 데이터 추출 등에 사용.

 

 

2.1. Render Mode 실습

 

Render Texture을 생성해준 후 해상도를 설정해 주고, 이 텍스처를 Video Player의 Target Texture에 지정한다.

 

 

Render Mode는 UI 방식이기 때문에 Raw Image를 생성해주어야 한다.

Raw Image는 Unity UI에서 텍스처를 보여주는 컴포넌트로, Render Texture를 출력하기 위해 사용된다.

 

 

Canvas의 Render Mode를 World Space로 설정하면 UI 요소(Raw Image)를 3D 공간에 배치할 수 있고, 오브젝트 위에 붙이는 것도 가능하다.

 

2.2. Material Override 실습

 

Video의 Render Mode를 Material Override로 설정하면 Renderer에 지정한 오브젝트의 머테리얼에 영상이 출력된다.

이때 영상의 밝기나 색상은 사용된 셰이더의 종류에 따라 달라진다.

 

3. Shader

Shader는 GPU가 오브젝트의 색상, 조명, 질감 등을 계산하는 프로그램이며, Unity에서는 HLSL 언어를 사용한다.

 

위에서 실습한 두 오브젝트를 비교해 보면

Material Override 방식은 기본적으로 Lit Shader를 사용하므로 조명의 영향을 받는다.
반면 Render Texture를 Raw Image에 출력하는 방식은 Unlit Shader 기반이므로 조명의 영향을 받지 않는다.

 

따라서 영상이 어두워지거나 조명에 따라 달라지는 이유는 셰이더 종류 때문이다.

 

 

Material Override 모드에 Unlit 셰이더를 적용시켜 주면 Render Texture 모드와 동일한 빛의 영향을 받는다.

 

2. TV와 리모컨 만들기

🥕 예행 작업
1. Scene 생성 (TV Remote Controller)
2. Script 생성 (RemoteController)

 

1. TV와 리모컨 오브젝트 배치

 

리모컨 이미지를 Resource 폴더에 임포트 해준 후 텍스처 타입과 스프라이트 모드를 설정해 준다.

 

 

3D 모형들을 활용해서 TV 모니터를 생성해 주고, 리모컨과 함께 위치를 적절히 배치해 준다.

 

2. Button - 전원 On/Off  기능 구현

 

우선 리모컨의 On/Off 기능이나 음소거, 채널 이동 등 이벤트를 처리하기 위해서 버튼을 생성해 준 후 버튼의 위치를 적절히 배치해 준다. (배치 후 버튼의 텍스트는 모두 삭제)

 

 

그런 다음 On/Off 구현을 위해 Screen에 실행시킬 Video를 삽입해 준다.

 

using UnityEngine;

public class RemoteController : MonoBehaviour
{
    public GameObject videoScreen;

    public bool isOn = false;

    public void OnScreenPower()
    {
        if (!isOn)
        {
            videoScreen.SetActive(true);
            isOn = true; // 현재 티비 On
        }
        else
        {
            videoScreen.SetActive(false);
            isOn = false;
        }

        // NOT을 활용해서 적은 방법
        //isOn = !isOn;
        //videoScreen.SetActive(isOn);

        // GameObject 속성을 활용해서 적은 방법
        //videoScreen.SetActive(!videoScreen.activeSelf);
    }
}

 

버튼을 누르면 영상이 On/Off 되도록 코드를 구현해 주었다.

 

 

Remote Controller 오브젝트를 만들어주어서 Manager처럼 활용하도록 해줄 것이다.

지금 막 작성한 스크립트를 여기에 적용시켜 주고 버튼 오브젝트에서 On Click 메서드를 설정해 주면 끝!

 

 

전원 버튼을 눌렀을 때 화면이 꺼지고, 켜지는 것을 확인할 수 있다.

 

3. Button - 음소거 기능 구현

public Button[] buttonUI;

private VideoPlayer videoPlayer;

 

여러 버튼을 한 개의 스크립트(Remote Controller)로 처리하기 위해서 배열을 사용해 준다.

그리고 동영상의 음향을 조절할 수 있도록 VideoPlayer을 선언해 준다.

 

Video Player 컴포넌트 안에는 오디오를 재생시키는 인스턴스가 있다.

이런 인스펙터 기능을 활용하면 코드를 간략히 줄여 사용할 수 있다.

Mode 설명
None 소리 출력 안 함
Audio Source Audio Source를 통해 소리를 출력 (3D 사운드 지원 등)
Direct VideoPlayer가 직접 오디오를 출력함 (기본적이고 간단한 방식)
API Only 스크립트를 통해서만 오디오 제어 가능 (고급 제어용)

 

 

[TIP ❗] 배열에 오브젝트를 삽입할 때 한 번에 넣는 법

 

  1. 인스펙터 창 우측 상단에 잠금버튼 활용하기
  2. 인스펙터 창을 새로 띄워서 적용하기

 

using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Video;

public class RemoteController : MonoBehaviour
{
    public GameObject videoScreen;

    private VideoPlayer videoPlayer;

    public bool isOn = false;
    public bool isMute = false;

    public Button[] buttonUI;

    void Awake()
    {
        videoPlayer = videoScreen.GetComponent<VideoPlayer>();
    }

    void Start()
    {
        //buttonUI[0].onClick.AddListener(OnScreenPower);
        buttonUI[1].onClick.AddListener(OnMute);
    }
    
    public void OnMute()
    {
        isMute = !isMute;
        videoScreen.GetComponent<VideoPlayer>().SetDirectAudioMute(0, isMute);

        // 현재 영상의 Mute 속성을 활용한 방법
        //videoPlayer.SetDirectAudioMute(0, !videoPlayer.GetDirectAudioMute(0));
    }
}

 

이제 버튼의 동작을 buttonUI[0].onClick.AddListener(...) 식으로 코드에서 직접 등록하고 있으므로

유니티 에디터의 Button 컴포넌트 → OnClick() 부분에 따로 메서드를 넣어주면 중복 실행으로 다시 꺼져버린다!

그러니 이 부분은 꼭 삭제해주자.

 

4. Button - 채널 변경 기능 구현

public class RemoteController : MonoBehaviour
{
    public VideoClip[] clips; // 영상 파일 배열
    public int currClipIndex = 0; // 현재 영상 Index

    private VideoPlayer videoPlayer;

    void Awake()
    {
        videoPlayer.clip = clips[0]; // Default 영상 설정
    }
}

 

다음 또는 이전 영상으로 전환하기 위해 VideoClip 배열을 사용한다.
배열에서 현재 인덱스에 해당하는 클립을 VideoPlayer의 clip 속성에 할당하면, 해당 영상이 화면에 출력된다. 

 

 

유니티 에디터에서 송출할 영상들을 배열에 넣어준다.

 

void Start()
{
    buttonUI[2].onClick.AddListener(OnPrevChannel);
    buttonUI[3].onClick.AddListener(OnNextChannel);
}

//public void OnChangeChannel(bool isNext)
//{
//    int value = isNext ? 1 : -1;
//    currClipIndex += value;

//    if (currClipIndex > clips.Length - 1)
//        currClipIndex = 0;

//    if (currClipIndex < 0)
//        currClipIndex = clips.Length - 1;

//    videoPlayer.clip = clips[currClipIndex];
//    videoPlayer.Play();
//}

public void OnNextChannel() // 오른쪽 버튼
{
    currClipIndex++;
    if (currClipIndex > clips.Length - 1)
        currClipIndex = 0;

    videoPlayer.clip = clips[currClipIndex];
    videoPlayer.Play();
}

public void OnPrevChannel() // 왼쪽 버튼
{
    currClipIndex--;
    if (currClipIndex < 0)
        currClipIndex = clips.Length - 1;

    videoPlayer.clip = clips[currClipIndex];
    videoPlayer.Play();
}

 

리모컨의 이전(◀) 또는 다음(▶) 버튼을 누르면, 현재 재생 중인 영상 인덱스를 하나씩 이동시켜 새로운 영상으로 전환한다.

만약 인덱스가 배열의 범위를 초과하거나 음수가 되는 경우, 다시 처음 또는 마지막 영상으로 순환되도록 처리하여 끊김 없는 채널 전환이 가능하도록 구현하였다.

 

 

 

3. Cat 횡 스크롤 게임에 실습해보기

 

Cat 씬에서도 Video 작업을 몇 개 넣어놨는데 이 부분을 이제 VideoManager로 관리해 보자

 

 

기존에는 게임 성공/실패 여부에 따라 각각에 해당하는 패널을 불러왔다.

이번에는 하나의 오브젝트를 통해서 패널을 가져와보겠다.

 

🥕 예행 작업
1. Render Texture 생성 (Video Render Texture)
2. Video Manager 오브젝트 생성

 

using UnityEngine;
using UnityEngine.Video;

namespace Cat
{
    public class VideoManager : MonoBehaviour
    {
        public GameObject videoPanel;

        public VideoPlayer vPlayer;
        public VideoClip[] vClips;

        void Start()
        {
            vPlayer = GetComponent<VideoPlayer>();
        }

        public void VideoPlay(bool isHappy)
        {
            videoPanel.SetActive(true);

            var endingClip = isHappy ? vClips[0] : vClips[1];

            vPlayer.clip = endingClip;
            vPlayer.Play();
        }
    }
}

 

VideoPlay() 메서드는 인자로 전달된 bool isHappy 값에 따라 재생할 영상 클립을 선택하고

이를 VideoPlayer에 설정한 뒤 즉시 재생하도록 구현하였다.

 

 

isHappy가 true일 경우에는 성공 엔딩 영상(vClips[0])을, false일 경우에는 실패 엔딩 영상(vClips[1])을 재생하도록 Clips을 배치하였다.

영상은 반복재생되도록 Loop 값을 켜주었다.

화면에 영상이 나타나도록 Video Manager와 Ending Panel 오브젝트에 만들어둔 Render Texture을 지정해 준다.

 

 

이전과 똑같이 영상이 잘 실행되는 것을 확인할 수 있다.

이렇게 관리가 원활하도록 Manager을 활용하는 습관을 갖도록 하는 것이 좋겠다.

 

4. Build

Cat 횡 스크롤 게임을 exe 파일로 만들어보자.

 

유니티 에디터 좌측 상단에 File - Build Profiles를 클릭한다. (Ctrl + Shift + B)

 

 

우선 빌드해 줄 씬을 추가해 준다.

 

 

그런 다음 씬이 제대로 삽입된 것을 확인하고 Build And Run을 클릭한다.

 

 

이렇게 빌드까지 완료!

 

 

개인 데스크탑 해상도에 따라서 게임 월드가 보일 수 있는데, 이럴 때에는 카메라의 해상도 옵션을 조절해주면 된다.

 

 


 

 

Cat 횡스크롤 게임이 슬슬 마무리되어 간다!

하드 코딩이나 충돌 때문에 발생하는 속도 문제 등, 몇 가지 어색한 부분이 있긴 하지만… 아직은 실습 단계이고 배우자는 의미로 진행 중이라 큰 부담 없이 공부하고 있다.

에디터 사용은 확실히 천재들이 만들어서 그런지(?) 딱히 어려운 점은 없지만 나중에 직접 코드 구현할 때 잘 해낼 수 있을지 살짝 걱정되긴 한다.
C# 언어 공부를 좀 해야 하나 싶기도 하고… 근데 또 가만 보면 유니티에서 이미 만들어둔 메서드들이 워낙 많다 보니 딱히 언어 자체를 깊게 파고들 필요는 없을 것 같기도 하다..
취업하려면 코딩 테스트 준비도 해야 하는데 유니티만 해서 언어 실력을 향상 시킬 수 있을지는 잘 모르겠다!!..

뭐 하다 보면 실력이 늘겠지 하고 그냥 넘겼는데… 점점 개인 공부가 목을 졸라오는군!!! 으윽!!~~!!!

얼른 교재와라!! 그걸로라도 공부하게!!! 우엑!!! 난 말하는 감자!!! 우엑!!!ㅠㅠㅠㅠㅠㅠㅠㅠㅠ