본문으로 건너뛰기
HY Devlog
뒤로 가기

포크 세 번 끝에 공식판이 나왔다

게임 프로젝트에 글리치 효과를 연출로 넣으려고 쓰는 법을 찾아보던 중에, URP용 글리치 이펙트 패키지 saimarei/URPGlitchREADME를 스크랩해뒀었다. 설치 방법 세 가지와 렌더러 피처·볼륨 설정 순서가 적혀 있어서 그대로 따라 할 수 있는 문서다.

지금 그 저장소를 열면 README 맨 위에 공지가 하나 붙어 있다.

I have moved on to using the Godot engine and will no longer be updating this repository.

그리고 문제가 생기면 keijiro의 KinoGlitchURP를 보라고 안내한다. 스크랩할 때는 없던 문장이다. 확인해보니 이 패키지가 서 있던 계보 전체가 그런 상태였고, 끝에 공식판이 하나 생겨 있었다.

목차

목차

세 단계 포크의 계보

README가 직접 출처를 밝힌다 — mao-test-h의 프로젝트에 기반하고, 그건 다시 keijiro의 KinoGlitch에서 영감을 받았다고. 하나씩 열어보면 이렇다.

저장소대상라이선스현재 상태
keijiro/KinoGlitch레거시 파이프라인MIT원본
mao-test-h/URPGlitchUnity 2021.3+ / URP 12.1.7MIT“not actively maintained”
saimarei/URPGlitchUnity 6000.0MIT“will no longer be updating”
keijiro/KinoGlitchURPUnity 6000.0+ / URPUnlicense공식 URP판

중간 두 개가 둘 다 유지보수 중단을 명시한다. mao-test-h 쪽 README에는 이렇게 적혀 있다.

Please note that this project was implemented for study purposes and is not actively maintained.

포크가 이어진 이유가 여기 있다. 원본은 레거시 파이프라인용이었고, 공부 삼아 URP로 옮긴 것이 있었고, 그걸 Unity 6에 맞춰 다시 손본 것이 이 클리핑의 패키지다. 그리고 그 사이에 원저자가 URP판을 직접 냈다. 그게 지금 saimarei/URPGlitch가 가리키는 곳이다.

어디에 왜 쓰나

패키지 README는 설치와 설정만 다룬다. 글리치 효과를 언제 꺼내 쓰는지는 적혀 있지 않으니 정리해둔다.

언제 쓰나

공통점은 셋 다 강도를 시간에 따라 바꿔야 한다는 것이다. 인스펙터에서 값 하나 올려두고 끝나는 효과가 아니라, 코드로 흔들었다 가라앉히는 쪽이 대부분이다.

코드로 제어하기

아래는 공식판인 KinoGlitchURP 기준이다. 카메라에 붙는 컨트롤러 컴포넌트를 직접 잡아 쓴다.

using System.Collections;
using KinoGlitch;
using UnityEngine;

[RequireComponent(typeof(Camera))]
public class GlitchFeedback : MonoBehaviour
{
    private const float MIN_INTENSITY = 0f;

    [Header("Damage Flash")]
    [SerializeField, Range(0f, 1f), Tooltip("피격 순간의 최대 강도")]
    private float _peakIntensity = 0.6f;

    [SerializeField, Range(0.05f, 1f), Tooltip("가라앉는 데 걸리는 시간(초)")]
    private float _falloffSeconds = 0.35f;

    private DigitalGlitchController _digital;
    private Coroutine _running;

    private void Awake()
    {
        if (!TryGetComponent(out _digital))
        {
            _digital = gameObject.AddComponent<DigitalGlitchController>();
        }

        _digital.Intensity = MIN_INTENSITY;
    }

    public void PlayDamageFlash()
    {
        if (_running != null)
        {
            StopCoroutine(_running);
        }

        _running = StartCoroutine(FlashRoutine());
    }

    private IEnumerator FlashRoutine()
    {
        _digital.Intensity = _peakIntensity;

        float elapsed = 0f;
        while (elapsed < _falloffSeconds)
        {
            elapsed += Time.deltaTime;
            float t = elapsed / _falloffSeconds;
            _digital.Intensity = Mathf.Lerp(_peakIntensity, MIN_INTENSITY, t);
            yield return null;
        }

        _digital.Intensity = MIN_INTENSITY;
        _running = null;
    }
}

아날로그 쪽은 파라미터가 다섯 개라 조합으로 성격이 갈린다. 체력이 낮을 때 상시로 깔아두는 형태라면 이렇게 된다.

// 체력 비율(0~1)을 받아 낮을수록 화면이 흔들리게 한다
public void ApplyHealthDistortion(float healthRatio)
{
    float severity = Mathf.Clamp01(1f - healthRatio);

    _analog.ScanLineJitter    = severity * 0.35f;  // 주사선이 어긋난다
    _analog.VerticalJump      = severity * 0.10f;  // 화면이 세로로 튄다
    _analog.HorizontalShake   = severity * 0.15f;  // 가로로 떨린다
    _analog.ColorDrift        = severity * 0.30f;  // 색이 분리된다
    _analog.HorizontalRipple  = severity * 0.20f;  // 가로로 물결친다
}

다섯 개를 같은 비율로 올리면 대개 과해진다. ColorDriftScanLineJitter가 “화면이 고장났다”는 인상을 만들고, VerticalJump는 값이 조금만 커도 눈이 피로해진다. 위 계수처럼 축마다 상한을 다르게 두는 편이 낫다.

성능에서 주의할 것

KinoGlitchURP README에 이 문장이 있다.

Digital Glitch still runs at zero Intensity because it must keep updating its internal frame history. Disable the component if you want to eliminate its cost entirely.

강도를 0으로 내려도 비용이 남는다. 직전 프레임을 계속 들고 있어야 하는 구조라서 그렇다. 그래서 위 코routine도 마지막에 0을 넣고 끝나지만, 오래 안 쓸 거라면 컴포넌트 자체를 꺼야 한다.

// 한동안 쓸 일이 없으면 컴포넌트를 끈다
_digital.enabled = false;

아날로그 쪽은 성격이 다르다. README가 “Analog Glitch skips its pass when all properties are set to zero”라고 적는다. 전부 0이면 패스를 건너뛴다. 둘의 0이 다르다는 것만 알아두면 된다.

갈아타면 조작 방식이 바뀐다

공식판으로 옮기는 게 패키지 교체만으로 끝나지 않는다. 효과를 거는 구조가 다르다.

클리핑의 패키지KinoGlitchURP
조작Global Volume + 볼륨 오버라이드카메라에 붙는 컨트롤러 컴포넌트
컴포넌트 이름Analog/Digital Glitch VolumeAnalogGlitchController, DigitalGlitchController
기본 삽입 지점After Rendering Transparents (수동 지정)after post processing (Pass Event로 변경)
설치Git URL스코프드 레지스트리

클리핑 쪽은 URP의 볼륨 시스템을 탄다. 씬에 Global Volume을 만들고 프로파일에 오버라이드를 추가하는 방식이라, 구역별로 다른 값을 주거나 블렌딩하기에 유리하다.

공식판은 카메라에 컴포넌트를 붙인다. 위 예제 코드가 TryGetComponent로 바로 잡을 수 있는 이유가 이것이다. 볼륨을 거치지 않으니 코드에서 다루기는 더 단순한데, 볼륨 블렌딩으로 얻던 것은 직접 만들어야 한다.

둘 중 뭐가 낫다기보다 쓰던 코드가 안 옮겨진다는 게 요점이다. 볼륨 프로파일을 TryGet으로 뒤지던 코드는 전부 다시 써야 한다.

라이선스가 갈린다

계보 표에서 마지막 줄만 다르다. 원본 KinoGlitch와 그 포크들은 MIT인데, 공식 URP판 KinoGlitchURPUnlicense다.

클리핑의 README는 라이선스를 이렇게 적어뒀다.

This project is released under the same license as keijiro/KinoGlitch.

원본을 찾아가야 알 수 있는 서술인데, 확인해보면 MIT가 맞고 GitHub이 저장소에 붙인 표기도 MIT다. 어긋나지는 않는다. 다만 라이선스를 “다른 저장소를 보라”로 적어두면, 그 저장소가 나중에 라이선스를 바꿔도 이쪽은 모른다. 실제로 같은 저자의 새 저장소가 다른 라이선스를 달고 나왔다.

상업 프로젝트에 넣는다면 어느 쪽을 쓰는지에 따라 고지 의무가 달라진다. MIT는 저작권 표시와 라이선스 전문을 함께 배포해야 하고, Unlicense는 퍼블릭 도메인에 가까워 그 의무가 없다.

설치 방법도 다르다

클리핑이 안내하는 방식은 Git URL이다. Package Manager에서 + → Install package from Git URL에 붙여 넣거나, manifest.json을 직접 고친다.

{
  "dependencies": {
    "com.subbu.urp-glitch": "https://github.com/saimarei/URPGlitch.git",
    "com.unity.collab-proxy": "2.5.2"
  }
}

공식판은 스코프드 레지스트리를 쓴다.

Install the KinoGlitch URP package (jp.keijiro.kino-glitch.universal) from the ‘Keijiro’ scoped registry in Package Manager.

차이가 실무에서 드러나는 지점이 있다. Git URL 방식은 버전을 고정하지 않는다. 위 한 줄은 그 저장소의 기본 브랜치를 가리키므로, 저장소가 바뀌면 다음에 패키지를 복원할 때 다른 코드가 온다. 저장소가 사라지면 복원 자체가 안 된다. 유지보수가 끝났다고 선언된 저장소를 Git URL로 물고 있는 건 그 자체로 위험 요소다.

태그를 붙여 고정할 수는 있지만(.git#태그), 스코프드 레지스트리 쪽이 버전 관리는 더 안정적이다.

클리핑에 남아 있는 것들

셰이더를 손으로 지정하는 단계. 설정 안내에 이런 줄이 있다.

Set the Shader field by selecting an appropriate shader (click the eye icon to reveal hidden shaders).

렌더러 피처에 셰이더를 직접 물려야 한다는 뜻이다. 눈 아이콘을 눌러야 보이는 숨김 셰이더를 고르게 하는 구성이라, 빌드에서 셰이더가 빠지는 사고가 나기 쉬운 형태다. 씬의 머티리얼이 참조하지 않는 셰이더는 스트리핑 대상이 되기 때문이다. 에디터에서 되던 게 빌드에서 안 나오면 여기를 먼저 본다.

VR은 되돌려져 있다. 저장소 커밋 목록에 Revert "made texture usage to be compatible with vr"가 남아 있다. VR에서 쓰려고 한 시도가 있었고 되돌려졌다는 뜻이다. XR 쪽에 붙일 생각이었다면 이 한 줄이 답이 된다.

옛 버전은 Releases에 있다. README 맨 위 안내다.

If you are coming from the older URP Glitch video, you need to download the older 2021.3.8f1+ package from the Releases section.

Unity 6 이전 프로젝트라면 Git URL이 아니라 Releases에서 받아야 한다. Git URL 한 줄만 보고 붙였다가 컴파일이 깨지는 경우가 여기서 나온다.

정리

저장소 README를 스크랩하는 건 값이 있다. 설치 명령과 설정 순서가 한자리에 있으니까. 다만 README는 저장소의 현재 상태를 반영하는 문서라, 스크랩하는 순간 그 관계가 끊긴다. 이번 건은 그 사이에 저자가 문을 닫았고, 원저자가 문을 열었다.

그래서 패키지 클리핑에서 먼저 볼 것이 정해졌다. 설치 명령이 아니라 README 맨 위와 마지막 커밋 날짜다. 둘 중 하나만 봐도 그 아래를 읽을지가 정해진다.

연출 하나 넣으려고 시작한 일인데 결론이 패키지 선택으로 끝난 게 좀 얄궂다. 다만 글리치는 이벤트에 맞춰 강도를 흔드는 효과라 코드에서 계속 건드리게 되고, 그러면 그 API가 앞으로도 그대로일지가 실제 문제가 된다. 지금 화면에 어떻게 보이느냐만큼 누가 고치고 있느냐가 고를 기준이 되는 종류였다.

참고


이 글 공유하기:

이전 글
FreezePositionY가 얼리는 건 월드 Y다
다음 글
Read 한 번이 메시지 하나가 아니다