어몽어스 - 캐릭터 색상 선택 (2)

본 포스트는 베르님의 Make the 어몽어스를 정리한 포스트입니다.
https://www.youtube.com/watch?v=xht2y9op61E&list=PLYQHfkihy4Aw6QjsZqwwbD4ihpwvm7N0U&index=8

지난 포스트에서는 캐릭터 색상 변경 기능에 대한 전반적인 구현을 마쳤습니다. 이번 시간에는 Customize UI 마무리 작업을 진행하겠습니다.

1. Customize UI 열기/닫기 shader 작업

Customzie UI가 열렸을 때는 캐릭터 조작이 불가능해야합니다. 이를 위해 CustomizeUI 스크립트를 킨 후 하단에 다음의 내용을 추가합니다.

  public void Open(){
        AmongUsRoomPlayer.MyRoomPlayer.lobbyPlayerCharacter.isMoveable = false;
        gameObject.SetActive(true); // UI 킴
    }

    public void close(){
        AmongUsRoomPlayer.MyRoomPlayer.lobbyPlayerCharacter.isMoveable = true;
        gameObject.SetActive(false); // UI 끔
    }

Customize UI에 이미지를 추가해 x 표시의 이미지를 소스로 등록하고 왼쪽에 배치합니다. 그 다음 Background에 button 컴포넌트를 추가한 다음 On click() 이벤트로 close 함수를 등록합니다.

  • x 표시 이미지를 hierarchy창에서 background보다 위에 둬야 해당 이미지를 누를 때도 창이 닫힙니다!


잠시 CustomizeUI를 비활성화 시켜놓고 지금부터 노트북 오브젝트와 상호작용을 하여 Customize UI가 열리는 기능을 구현해보겠습니다. 이를 위해 먼저 상호작용 가능한 오브젝트를 위한 shader를 만들어야 합니다. (오브젝트의 일정 거리 안에 들어올 시 외곽선 효과 강조, 사용가능한 거리에 들어오면 오브젝트가 하얀색으로 바뀌며 Customize 버튼이 활성화됨)


먼저 shader graph를 킨 다음 Texture 2D 프로퍼티로 _MainTex를 하나 추가하고, defult에 laptop sprite를 할당합니다. 그 다음 Color 프로퍼티로 _OutlineColor를 추가합니다.

_MainTex 프로퍼티를 배치하고 Sample Texture 2D 노드로 분리합니다.

그 다음 alpha 값만 꺼내서 _OutlineColor를 곱해주고 Sprite Lit Master 노드의 Color 노드에 넣어줍니다. (이때 _OutlineColor 색상이 안보여서 default color를 잠시 노란색으로 변경하였습니다.) 이렇게 셰이더 그래프 작업이 끝나면 저장 후 셰이더 그래프 에디터를 닫습니다.

+) alpha값을 Fragment부분의 Alpha에도 연결합니다.

유니티 버전이 변경되면서 영상과 동일하게 작업했을 때 Scene view에서 다음과 같이 보이는 문제점이 확인되었습니다. Shader graph에서 alpha값 할당을 달리 하니 해결이 되어 해당 부분은 영상과 차이가 있습니다.

동일한 과정으로 새로운 shader graph(Shader_Highlight)를 생성하고 Texture 2D 프로퍼티인 _MainTex와 Float 프로퍼티인 _Highlighted를 추가합니다. 그 다음 _MainTex를 Sample Texture 2D 노드와 연결하고 _Highlighted 프로퍼티의 값을 Comparison 노드로 검사한 다음 branch 노드로 상태에 따라 false일 때는 그냥 색상을 내보내고, true일 때는 원래 색상에 값을 곱해 더 환하게 만든 후 내보내도록 설정합니다.

  1. _MainTex를 Sample Texture 2D 노드와 연결
  2. _Highlighted를 Comparison 노드와 연결 후 NotEqual로 변경
  3. Comparison을 Branch 노드와 연결
  4. Sample Texture 2D의 RGB 값을 Branch의 False와 연결
  5. Sample Texture 2D를 Multiply(a)와 연결 후 Branch의 True와 연결
  6. Branch의 out을 Base Color와 연결

앞선 문제점 해결을 위해 Sampel Texture 2D의 Alpha값을 Fragment의 Alpha값으로 연결해줍니다.

그 다음 두 shader graph를 사용하는 material을 각각 만듭니다.

이렇게 셰이더 작업이 끝나면 Customize 노트북에 셰이더를 넣어야합니다. 먼저 Hierarchy 창에서 노트북을 복제하여 2개로 만든 후 만든 material들을 각각 넣습니다. 그리고, highlight 오브젝트가 조금 더 앞에 나오게 Order in Layer를 수정합니다.

그 다음 outline 오브젝트가 highlight 오브젝트보다 조금 더 크게 만듭니다.

여기까지 outline, highlight 오브젝트를 완성하였습니다. 지금부터 본격적으로 customize UI를 열기 위한 기능을 만들겠습니다. 두 노트북 오브젝트에 Box Collider 2D 컴포넌트를 추가한 후 크기를 조절해줍니다. Outline의 경우 크기를 크게 잡고, Highlight의 경우 크기를 잡게 잡아줍니다.


그 다음 두 collider의 Is Trigger를 모두 체크합니다.

2. Customize UI 열기/닫기 기능 구현

먼저 outline 오브젝트가 플레이어를 감지하는 기능을 먼저 만들겠습니다. OutlineObject 스크립트를 생성한 후 다음과 같이 작성합니다.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class OutlineObject : MonoBehaviour
{
    private SpriteRenderer spriteRenderer;
    [SerializeField]
    private Color OutlineColor;
    // Start is called before the first frame update
    void Start()
    {
        spriteRenderer = GetComponent<SpriteRenderer>(); 
        var inst = Instantiate(spriteRenderer.material); // material 인스턴스화
        spriteRenderer.material = inst;
        spriteRenderer.material.SetColor("_OutlineColor",OutlineColor); // OutlineColor 설정
        
    }

   
    private void OnTriggerEnter2D(Collider2D collision) { 
        var character = collision.GetComponent<CharacterMover>(); // 트리거가 부딪힌 object가 LobbyPlayerCharacter이고 
        if(character != null && character.hasAuthority){ // 해당 캐릭터가 클라이언트 권한 가지고 있다면 
            spriteRenderer.enabled = true; // outline 보이게 만들기
        }

        
    }
    
    // 영역 벗어나면 outline 없앰
    private void OnTriggerExit2D(Collider2D collision) {
        var character = collision.GetComponent<CharacterMover>(); 
        if(character != null && character.hasAuthority){ 
            spriteRenderer.enabled = false; // SpriteRenderer 끄기
        }
        
    }

}

그 다음 커스터마이즈 노트북 기능을 만들기 위해 CustomizeLaptop 스크립트를 생성하고 다음과 같이 작성합니다.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CustomizeLaptop : MonoBehaviour
{
    [SerializeField]
    private Sprite useButtonSprite; // Use 버튼 이미지 변경
    private SpriteRenderer spriteRenderer; // 노트북 이미지 밝게 만들어주기 위함


    // Start is called before the first frame update
    void Start()
    {
        spriteRenderer = GetComponent<SpriteRenderer>();
        var inst = Instantiate(spriteRenderer.material);
        spriteRenderer.material = inst;
        
    }
    private void OnTriggerEnter2D(Collider2D collision) {
        var character = collision.GetComponent<CharacterMover>(); 
        if(character != null && character.hasAuthority){ 
            spriteRenderer.material.SetFloat("_Highlighted",1f); // 스프라이트 밝은 색상으로 변경
        }
    }

    private void OnTriggerExit2D(Collider2D collision) {
        var character = collision.GetComponent<CharacterMover>(); 
        if(character != null && character.hasAuthority){ 
            spriteRenderer.material.SetFloat("_Highlighted",0f); // 스프라이트 원래 색상으로 변경
        }
    }
}

이제 상호작용 오브젝트 근처로 이동 시 USE 버튼의 이미지를 변경하고 버튼을 눌렀을 때 동작하도록 하는 함수를 만들겠습니다. 먼저 지난 시간에 작성했던 LobbyUIManager 스크립트를 열어 다음 내용을 추가합니다.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI; // 선언 (Button)
using UnityEngine.Events; // 선언 (UnityAction)

public class LobbyUIManager : MonoBehaviour
{
                .
                .

    [SerializeField]
    private Button useButton; // USE 버튼 담을 변수
    [SerializeField]
    private Sprite originalUseButtonSprite; // USE 버튼의 원본 스프라이트
                .
                .
    public void SetUseButton(Sprite sprite, UnityAction action){ 
        useButton.image.sprite = sprite; // 버튼 스프라이트 이미지 세팅 
        useButton.onClick.AddListener(action); // 버튼 눌렀을 때 동작 기능 담당할 함수를 버튼의 onclick 이벤트에 세팅
        useButton.interactable = true; // 버튼 활성화
    }

    // 상호작용 오브젝트에서 멀어졌을 때 Use 버튼 원상복구
    public void UnsetUseButton(Sprite sprite, UnityAction action){ 
        useButton.image.sprite = originalUseButtonSprite; 
        useButton.onClick.RemoveAllListeners(); 
        useButton.interactable = false; 
    }
}

그 다음 CustomizeLaptop 스크립트를 다시 켜 다음 내용을 추가합니다.

 private void OnTriggerEnter2D(Collider2D collision) {
                             .
                             .
            LobbyUIManager.Instance.SetUseButton(useButtonSprite,OnClickUse); // 교체할 sprite와 OnclickUse 함수를 USE 버튼에 세팅하도록 함
        }
    }

    private void OnTriggerExit2D(Collider2D collision) {
                             .
                             .
            LobbyUIManager.Instance.UnsetUseButton(); // Use 버튼 세팅 초기화
        }
    }

    public void OnClickUse(){ // Customize UI Open 함수 호출
        LobbyUIManager.Instance.CustomizeUI.Open();
    }

에디터로 돌아와 outline 오브젝트에 OutlineObject 스크립트를 컴포넌트로 붙인 후 Highlighted Color를 하얀색으로 설정한 다음 Sprite Renderer를 꺼줍니다.

그 다음 노트북 본체에는 CustomizeLaptop 스크립트를 컴포넌트로 붙여주고 Use Button Sprite에는 Customize sprite를 할당합니다.

Canvas의 LobbyUIManager 스크립트 컴포넌트에 hierarchy 창의 Use button 오브젝트와 USE 버튼의 기본 sprite 이미지를 할당하고 Use button 오브젝트의 interactable 옵션을 꺼줍니다.

게임을 실행하면 대기실에서 캐릭터의 색상 변경 기능이 제대로 구현된 것을 확인할 수 있습니다.

게임을 실행하니 오류가 몇 가지 있어 추가로 정리해봅니다.

<확인된 오류 목록>
1. laptop에 접근하여 customize 버튼을 누르면 NullReferenceException 오류 발생
2. 캐릭터 색상 변경을 눌러도 preview 캐릭터 이미지의 색상이 변경되는 게 보이지 않음

  1. 오브젝트에 lobby character prefab을 할당하여 해결하였습니다.


2. 할당되어 있는 image가 잘못되어 있어 변경하여 해결하였습니다. (Character Preview 오브젝트 자체를 할당해줍니다.)

오류가 나면 먼저 코드 상의 오타가 있는지 한번 더 확인하고, 누락된 코드 줄이 있는지 확인합니다. 코드에 오류가 없다면 inspector 부분에서 할당된 오브젝트들을 다시 한번 확인해봅시다!

이번 시간까지 오류 없이 진행한다면 다음과 같은 플레이 화면을 확인할 수 있습니다.

좋은 웹페이지 즐겨찾기