이슈 상황 :

대화 스크립트 엑셀 파일에서 Command란에

 

$TokenType : Value $

를 작성하고 역직렬화 할때 , 파싱해서 사용을 하고 있다.

 

하지만 해당 문자열을 파싱할때 문제가 발생했다.

 

"$fade:true$ $duration:0.5$ $easygraph:4$"

 

fadeeasygraph는 토큰으로 인지하여 값을 분리했는데

duration value인 0.5가 값으로 안 읽히고 text 토큰 형태로 읽히는 문제가 발생했다.

 

원인 :

   private static readonly Regex TagRegex =
        new Regex(@"\$(\w+):(\w*)\$", RegexOptions.Compiled);

Command 문자열을 특정패턴으로 분리 할수있게 Regax를 사용하고 있다.

 

(\w*)

원인은 이 정규식(Regax) 때문이였다.

\w는 보통 영문자,숫자,밑줄(_)허용함,

예시 ) abc,A1,test_01 이런것만 정규식으로 인정이 된다는 것

0.5에는 점(.)이 있으므로 매칭이 되지 않는다. 

 

그렇기때문에 앞에 duration 토큰 탐색후 value값 탐색시에 조건이 안맞으므로

예외 처리로 text 토큰 처리를 하게 된것이다. 

 

해결 방법 : 

   private static readonly Regex TagRegex =
      new Regex(@"\$(\w+):([^$]*)\$", RegexOptions.Compiled);

 

(\w*) => ([^$]*)

로 정규식을 변경하였다.

 

[^$]* 해당 정규식을 사용한 의미

1. 값 부분은 $ 아니면 전부 허용

2. * 이므로 빈 문자열(공백)도 허용

 

예시 : 

$tag:0.5$
$tag:-1.25$
$tag:hello world$
$tag:50%$
$tag:test_01$
$tag:abc-def$
$tag:$

 

Value 부분에 어떤 값이 들어와도 해당 토큰에 따라 변경을 해놓게 해결하였다.

camelCase 소문자로 시작, 띄어쓰기 생략, 대문자로 구분
PascalCase 대문자로 시작, 띄어쓰기 생략, 대문자로 구분
snake_Case 소문자만 사용 ,띄어쓰기 대신 _ 사용
kebab-case 소문자만 사용 ,띄어쓰기 대신 - 사용

5-1. [namespace], [class] , [struct] 는 파스칼을 사용
5-2. [함수]는 파스칼을 사용 , 함수 내부는 카멜을 사용
5-3. [Enum]는 파스칼을 사용
5-4. public 변수는 파스칼을 사용 ex) public int Num;
5-5. 나머지 변수는 _ + 카멜을 사용 ex) private int _num;

1.컴퓨터 
저장 장치
하드디스크 -  컴퓨터에 물리적으로 파일을 저장
메모리 - 프로그램/어플리케이션이 실행하는데 필요한 데이터를 저장 
ex) 게임 - 레벨,소유골드 등등...

메모리는 배열처럼 생겼다 (배열은 아님!!!!)
Code : 프로그래머가 작성한 코드를 보관
Data : static,const 어플리케이션 전반에 필요한 데이터 저장
Heap : 참조 데이터 (객체) 저장
Stack : 로컬변수 ,매개변수 저장
어플리케이션 실행 순서에 필요한 데이터 보관

메모리는 2진수로된 주소값이 있다.
해당 메모리에 접근하기 위해 주소값을 가지고 있으면 된다 -> C/C++의 포인터 기능

int x = 10; 
해당 데이터(10)는 stack영역 메모리 첫번째에 저장된다.
메모리 첫번쨰는 0000 0000주소값을 가지고있는데

해당 데이터에 접근하기 위해 0000 0000 주소값을 가져오는게 아닌 x로 변경하여 사용하게 되는것이다.
x도 stack 영역에 저장됨

= : Rvalue를 Lvalue로 옮겨주는것이다.

class는 참조값이기에 heap 영역에 추가됨
heap은 어디에 저장되는지 모름, 그리고 알 필요도 없기에 빈 어딘가에 저장된다고 생각하면 됨

Class Person 
Class Data는 heap영역에
Calss로 만들어진 객체는 Stack 영역에 저장됨

사용할떄 주의할점
Stack 영역
변수할당시 생성
함수가 끝날때 삭제

참조가 한번 끊어지면 다시 heap영역의 data에 접근이 되지 안흔ㄴ다 -> heap영역의 과잉생산 -> 메모리 누수

데이터가 있지만 활용하지 못하는것 -> 쓰레기(가비지)
데이터가 언제 삭제되는지 모름, 단 한 조건은 있음
데이터가 사용되지 않을때 (참조가 끊겼을때)

GC가 언젠가 쓰지않는 데이터를 호출하여 처리해줌

가비지가 생긴다고 했을때
가비지를 1개만 가져가면 괜찮지만
가비지를 100개 1000개씩 한번에 가져갈때
컴퓨터가 과부하가 걸린다 -> 게임시 렉이 걸리는 이유

효율적으로 쓴다 = 가비지를 최대한 적게 생성시킨다.

Heap영역
new를 통해 생성 GC로 인해 삭제
Unity에서는 Instanitate() , Destroy()
하지만 Destroy()에서는 오브젝트가 삭제되는거지 Data는 남아있어 가비지로 남아있음 

유니티 inspector에 들어간 컴포넌트의 상위는 Class ->Monobehaviour 이다. 즉 ,  Instanitate() 를하면 new를 통해 생성하는것과 똑같은 의미이다.

최적화 -> 반복적으로 new 하는것을 줄인다
(Update,While 등등...)

최적화 1.
데이터를 한번 준비하고 **재활용**
-> 변수들을 미리 선언하여 호출하여 쓰기 

Unity에서 Vector는 구조체이기 때문
C/C++ 에서는 구조체나 class는 비슷하다.
(왜냐하면 프로그래머가 직접 메모리 할당 및 해지 , 포인터를 이용한 주소값 접근을  담당하기 때문)

하지만 C#은 다르다.
구조체는 데이터 자체가 Stack영역에 생성됨
즉, 구조체는 가비지가 남지 않게된다.
Stack 메모리가 heap 속도보다 더 빠르다
즉, Vector는 자주 사용되기 때문에 구조체로 Unity에서 만든것이다.

string str = "유저 레벨"

유 저  레 벨 이라는 5가지 저장됨

문자열은 불변객체 = 변하지 않는다는 뜻

str += "chad" 가 실행되면

내부적으로 간단하게
유저 레벨 뒤에 chad가 붙는것이 아니다.
즉, 유저 레벨 뒤 heap영역 메모리 데이터에 다른 데이터들도 있기에 붙일수가 없기 때문이다. 

유저레벨 + chad를 합친 문자열을 다른 heap영역에 새로 만들어진다. 

즉, heap에는 유저 레벨 , 유저레벨 chad 데이터가 2개가 동시에 존재하는 것이다
즉 string의 += 연산자를 계속 쓸때마다 heap영역을 쓰는 것이고 추가가 되어진다. 

이런 현상을 "파편화"라고 한다.

문자열 대처법
1.string을 한번에 제작 [ 보간문자열,스트링포맷,+연산자]
(추천)1-1.보간 문자열 : 문자열 앞에 $ 표시를 붙여 사용한다.
1-2.string.Format
1-3.+연산자를 한줄에 적어버리기

문자열을 한번에 제작하지 못하는경우
2.StringBuilder 사용하기

근데 왜 StringBuilder는 왜 이렇게 사용해도 괜찮은가??
ex)
유저 레벨 이 5글자이면 , StringBuilder는 추가로 공간을 확보해놓고 해당 확보한 공간을 넘어서는경우 새로 ㅏㄴ들게 된다. -> C#의 List와 유사하다.

stringBuilder는 처음에 16글자
넘어가면 2배인 32글자를 새로운 heap영역에 할당 이런식으로 된다.

stringBuilder는 일반 문자열보다 느리지만 파편화를 덜 방지 할 수 있기에 사용이 추천된다.

StringBuilder도 처음 초기화시 크기를 선언하고 사용 할 수 있다.

웬만한 문자열은 보간문자열을 사용할 수 ㅣㅆ고
중간중간 문자열이나 Data를 실시간으로 조합할때는 StringBuilder를 추천한다.
(개별 입력으로 받아들여서 합치는 경우에는 StringBuilder, 개발자가 처음 부터 값을 넣어주는 경우에는 $""를 쓰는 게 좋겠군요.)

Stack 값 형식 =  value
C#의 기본 타입(int,float,bool,char) , 구조체 , enum
Heap 참조형식 = referance
string class 배열 interface delegate

구조체는 Stack에 할당
메모리는 Stack에만 저장
큰 데이터나 외부 데이터를 가져와서 사용할떄 문제 발생

Calss는 Heap에 할당
Class의 객체(변수)는 stack에
Class 객체의 data는 heap영역에
데이터 접근은 Stack에 접근후 데이터의 주소를 파악후 heap영역에 접근한다. 

C#에서는 Class를 만들떄 new 어트리뷰트를 사용하여 생성한다. 유니티는 Instaiate는 new키워드와 유사한다

Static 키워드가 변수 ,메서드가 있으면
외부의 클래스의 이름을 바로 호출하여 바로 사용 할 수 있다.

Static을 메모리 관점으로 봤을때
Data영역에 배치 되어있음 
Data 영역은 프로그램 시작시 생성
프로그램 종료시 삭제

즉 , Static 키워드는 Data영역에 접근하여 언제든지 호출이 가능해진다.

Static 키워드 데이터는 Scene 사이의 데이터가 유지되어있다.

Static Class는 데이터를 세팅해줘야 됨
나머지 int,float은 언제든지 쓸 수 있음 
객체도 Class라서 초기값은 null이다.

Static 키워드들이 heap,stack영역 데이터에 접근해서 사용 할수는 없다.

'C#' 카테고리의 다른 글

Dirty Flag Pattern  (0) 2026.02.03
C# :: 메모리 구조  (0) 2024.10.14

* 타일맵의 정적 오브젝트 충돌 처리시 사용 방법 
composite collider : 타일 하나당 collider 적용되는것을 막기위해(연산 초과)  및 이상한 충돌처리를 막기위해 컴포넌트 추가
=> 리지드바디2D가 자동으로 추가 됨, body type을 static으로 변경 -> 정적으로 변경해서 고정된 얘들끼리는 충돌연산을 하지 않기에 비용이 매우 쌈

* Scene에서 리소스가 꺠져보이는 경우 발생
 원인 :  카메라의 해상도가 맞지 않기에 생기는 문제점 
대처법 : 스프라이트 아틀라스 사용하고 패킹 후 필터모드를 포인트로 변경 하면 부드럽고 리소스가 깨지는것을 방지 가능 

* 게임 중단 및 종료, 오브젝트 파괴시 사용되는 함수 

OnApplocationPause()
:모바일 개발시 많이 사용
home버튼을 누를떄 사용됨(검색할것)
OnApplicationQuit() 
:종료되는 시점을 알려주는 함수
Ondestroy()
:해당 오브젝트를 컴포넌트로 가지고있는경우 파괴 될때 알려줌

* Animator State Tip
Any State에서는 Settings에 Can Transition To Self 체크옵션을 빼주면 자기 자신한테 상태이전은 하지 않는다. 이로서 위처럼 깔끔하게 처리를 할수 있었다.

can Transsition self : 해당 컨디션에서 또 조건이 되면 애니메이션을 재생 

 

* 콜라이더 충돌 오브젝트 통과 판정 만들기
Collider 컴포넌트의 use-effect 체크 -> Surface Arc 사용가능
Surface Arc (표면각도)  : 이 각도 수치가 넘으면  이 오브젝트를 뚫고 지나가는지를 판단 

랜덤 맵 생성 기능
ㄴ하나의 스테이지 안에 맵을 랜덤으로 생성합니다.
ㄴ참고 키워드) 절차적 맵 생성 (Procedural Map Generation)
BigInteger 기능
ㄴ매우 큰 숫자를 처리하기 위한 기능입니다.
ㄴ방치형 게임에서 나오는 1a, 4d 등의 숫자를 구현합니다.
ㄴ참고) https://github.com/keiwando/biginteger

Cinemachine - VirtualCamera Inspector 

더보기

- Lens Vertical FOV
:카메라 컴포넌트에서 사용
: FOV (Field of View) 의 약자, 시야각이란 뜻
: 카메라의 렌즈에서 위에서 아래로 보이는 영역의 각도
:값이 크면 더 많은 영역을 볼 수 있지만, 특정 지점까지 가까워질수록 왜곡이나 물체의 크기 변화가 눈에 띌 수 있습니다. 작은 값은 시야가 제한되어 작은 영역만 볼 수 있지만, 더 멀리 있는 물체가 더 자연스럽게 보일 수 있습니다.

-Body :  가상 카메라의 위치와 회전을 결정하는 대상(Transform)을 지정
1.Transposer : Follow 대상에 대해 고정돤 관계로 움직입니다.
2.Do Nothing : 카메라가 움직이지 않음
3.Framing Transposer : Follow 대상에 대해 고정된 화면 공간관계로 이동 
4.Orbital Transposer : Follow 에 대한 변수 관계로 이동하며 선택적으로 플레이어 입력을 수락합니다.
5.Tracked Dolly : 사전 정의된 경로를 따라 이동합니다. 
6.Hard Lock to Target : Follow에서 동일한 위치를 사용

Body - Binding Mode : 가상 카메라의 위치와 회전을 어떻게 대상(Transform)과 연결할지 결정 - Follow 
1.World Space : 가상카메라의 위치와 회전이 월드 공간에서 대상과 동일하게 설정,
가상카메라 = 대상 와 동일시됨
2.World Space Offset : 위의 옵션과 동일하나
오프셋을 설정하여 오프셋 위치나 회전에 고정이나 이동가능해짐
3.Follow Target : 
Cinemachine의 추적 시스템을 활용하여 대상을 따라가도록 함, 카메라의 움직임에 대상의 속도,감속도,회전등 적용 가능
4.Follow Target With World Up :
위의 옵션과 유사하나 대상의 상단을 월드 업 벡터로 정렬하여 카메라 회전을 제어 

Aim : 가상카메라의 회전을 결정, - Look At
1.Composer :카메라 프레임에 Look At  대상을 유지
2.Group .Composer : 카메라 프레임에 여러 Look At 대상을 유지
3.Do Nothing : 가상 카메라를 회전시키지 않음
4. POV : 사용자 입력에 따라 회전시킴
5. Same As Follow Target : 카메라의 회전을 Follow Target 의 회전으로 설정
6.Hard Look At : 시선 대상을 카메라 프레임 중앙에 유지

Unity Animatior Controller Inspector

더보기

Animator controller (Weight : 가중치)
해당 레이어의 가중치를 몇% 만큼 합칠거냐라는 뜻( 투명도 x)
->서있는 포즈(1) , 총들고있는포즈(0.5)로 설정시  두 애니메이션이 합쳐져 어중간하게 서있는 포즈가 된다.  

Animator controller ( Kinemetics : 운동학(키네매틱))
애니메이션이 움직이면 하이어라키(계층구조)에 따라 부모가 움직이면 자식들이 영향을 받아 움직이는 구조를 나타냄 

하지만, 자식이 움직이면 부모가 따라 움직이는 구조를 IK(Iverese Kinematics) 라 한다.
-> 즉, 특정 위치를 중심으로 관절의 위치를 역(자식->부모)로 계산
IK Pass : ( IK기능을 활성화하는 기능)
-> 비용이 매우큼(지양적)
->오브젝트의 위치가 어디에 있든, 거기에 맞추어 시선이나 팔 등의 위치를 결정할 때 사용
-> 컴퓨터 그래픽스 이해목적으로 사용

 

 

* for문은 종료 될떄까지 메모리를 계속 쌓음 => for문이 다중으로 쓰일때마다 메모리가 계속 쌓임

* 재귀함수는 함수가 종료될때 함수 메모리가 쌓이지 않음

결론 : 메모리 확보 목적으로는 재귀함수 / 속도를 중시하면 for문

'기술면접질문' 카테고리의 다른 글

기술 면접 리스트  (0) 2024.12.12
기술 면접 질문과 답변(1)  (0) 2024.12.09

>작업 진행도 

더보기
# 대화 플로우 작업 근황

## 개요
- **1차 최적화 리팩토링 이후 중단되었던 대화 씬(Dialog Scene) 작업 재개**
- 기존 작업 기록을 확인한 결과, **기존 코드를 이어서 작업하기보다는 리팩토링 기반으로 재구성하는 방향으로 진행**

### 진행 방향 변경 이유

**1. 스토리 모드 프레임워크 피드백 반영**
- 팀장과 스토리 모드 프레임워크 회의를 진행
- 여러 피드백 및 레퍼런스를 기반으로 **기존 코드 구조를 확장성을 고려한 구조로 업그레이드**

**2. 기능 확장성 고려**
- 기존 코드에 기능을 덧붙이는 방식보다  
- **로직 구조 자체를 업그레이드하는 방식이 이후 기능 추가 및 유지보수에 더 유리하다고 판단**

---

# 현재 작업 진행 상황

## 1. DialogManager 외부 데이터 로딩 구조 설계
**Excel → JSON 역직렬화 구조 설계**

- `Type` 헤더 값을 기준으로 각 상황에 맞는 동작을 수행하도록 설계
- Type 카테고리는 아직 최종 확정 단계는 아님

### 예정된 Type 카테고리

| Type | 설명 |
|-----|-----|
| talk | Actor(캐릭터)가 대사를 출력할 때 사용 |
| narration | 나레이션 대사 출력 |
| emote | 감정 표현 Spine Animation 실행 |
| skin | Actor 의상 / 얼굴 Spine Skin 변경 |
| choice | 선택지 제공 |
| answer | 선택지 결과 처리 |
| move | 대상 오브젝트(캐릭터, 배경 등)의 좌표 이동 |
| fade | 대상 오브젝트 Fade In / Fade Out |
| transition | 화면 전환 처리 |
| play | BGM / SFX 재생 |
| effect | 화면 특수효과 (애니메이션 / 파티클) |

---

# 2. Asset 구조 분리

대화 시스템에서 사용하는 리소스를 **Common Asset** 과 **Dialog Asset** 기준으로 분리

## Common Asset
프로젝트 전체에서 공통적으로 사용되는 리소스

- BGM
- SFX
- Effect (화면 연출용 파티클 및 애니메이션)
- 나레이션 UI 연출 프리팹

## Dialog Asset
특정 Dialog Script에서만 사용되는 리소스

- 캐릭터 Spine Skeleton Asset (스탠딩 캐릭터 애니메이션 포함)
- 배경 이미지
- 특정 CG 이미지

---

# 3. DialogManager 신규 기능

## 3-1 Actor Spine기능
- Actor **Spine Skin 조합 및 애니메이션 재생**

## 3-2 Actor 이동
- Actor 캐릭터 **좌표 이동 애니메이션**

## 3-3 대상 오브젝트 좌우반전 처리
- Actor / Emote / TextBox **좌우 반전 기능**

## 3-4 Emote 처리
- Emote 애니메이션 재생
- Actor 대상에 따른 **Emote 위치 자동 설정**

## 3-5 TextBox 처리
- 대사 출력
- Actor 대상에 따른 **TextBox 위치 자동 설정**

## 3-6 배경 및 CG
- BG / CG Image 설정 기능
- CG **비활성화 기능**

---

# 4. DialogManager 추가 예정 기능

## 4-1 Effect
- Effect 애니메이션 재생
- 화면 파티클 연출 기능

 

 

해당 작업을 하며 배운 기술 리스트 공유

- Spine 객체 스킨 조합 

더보기

Spine Asset에 포함되어있는 Skin을 한번에 여러개 출력하려면 
Skin 출력시마다 해당 스킨 조합을 만들어서 적용해야된다. 

using UnityEngine;
using Spine.Unity;
using Spine;

public class SpineController 
{
    public SkeletonGraphic Graphic { get => graphic; set => graphic = value; }
    public TrackEntry TrackEntry => trackEntry; 

    private TrackEntry trackEntry;

    private SkeletonDataAsset dataAsset;
    private SkeletonGraphic graphic;

    public void CombineSkin(params string[] skinNames)
    {
        var skeleton = graphic.Skeleton;
        var data = skeleton.Data;

        Skin combinedSkin = new Skin("CombinedSkin");

        for (int i = 0; i < skinNames.Length; i++)
        {
            string skinName = skinNames[i];

            if (string.IsNullOrWhiteSpace(skinName))
                continue;

            var skin = data.FindSkin(skinName);

            if (skin == null)
            {
                Debug.LogWarning($"[SpineSkinCombiner] Skin not found: {skinName}");
                continue;
            }

            combinedSkin.AddSkin(skin);
        }

        skeleton.SetSkin(combinedSkin);
        skeleton.SetSlotsToSetupPose();
        graphic.Update(0);
    }
}

 

프로젝트L에 사용하는 코드 일부를 가져옴.

 

얼굴에서 사용되는 face/Idle과 outpit/School 이라는 스킨을 동시에 사용하려면

spineController.CombineSkin("face/Idle", "outpit/School");

이런식으로 사용

 

p.s new 할때마다 Heap 메모리에 쌓이고 GC가 동작할 여지를 주는데

대화씬이기에 최적화가 필요한 구간이 아니라 그냥 사용하기로 하였다.

- Spine 애니메이션 완료 이벤트 구독 및 실행

더보기

Spine 애니메이션이 재생이 완료가 됬으면 동작시킬수 있는 로직을 추가하고싶을때 사용하는 메서드이다.

 

Spine API에 기록되어있는 TrackEntry의 델리게이트와 이벤트 목록들
해당 이벤트 등록시 매개변수로 TrackEntry를 필요로함

 

해당 코드는 Spine 라이브러리에 있는거라 캡쳐해서 가져옴

 

우선 사용 로직은 이렇다.

//track = 애니메이션 트랙 ( 코드상에서 설정 가능)
//animationName = 애니메이션 이름
//loop = 반복 여부

private TrackEntry trackEntry;

// 1.이벤트 구독을 하고싶은 애니메이션을 설정 후 trackEntry에 초기화한다.
trackEntry = graphic.AnimationState.SetAnimation(track, animationName, loop);

//--- 

// 2. 이벤트를 구독할 함수를 만든다. (함수명은 자유 , 매개변수는 무조건 TrackEntry 선언)

    private void OnAnimationComplete(TrackEntry trackEntry)
    {
        // 재등록/중복호출 방지용 해제
        trackEntry.Complete -= OnAnimationComplete;

		//애니메이션 재생이 완료되면 해당 Spine 객체를 비활성화
        gameObject.SetActive(false);
    }

//---

// 3. 해당 애니메이션에 이벤트를 구독 
spineController.TrackEntry.Complete += OnAnimationComplete;

 

필요한 메서드들만 떼서 작성했는데 , 이 로직을 따라가면 Spine API가 이벤트를 재생 시켜준다. 

 

- 부모가 다른 RectTransform 위치 좌표 이동시 월드좌표 수정

더보기

당연하다면 당연한건데,

부모가 다른 객체를 해당 객체를 그대로 좌표 이동하면 위치가 다른 현상이 있다.

부모 객체가 다른 UI에 해당 좌표로 이동을 시켜야 될때

 

부모가 같으면 해당 코드를 그대로 사용해도 문제 없다.

textbox.GetComponent<RectTransform>().anchoredPosition = ActerPosList[0].anchoredPosition;

 하지만 계층구조로 인해 부모가 다를 경우 위치뿐만 아니라 크기,회전 등 전부다 틀어질 가능성이 생긴다.

 

이런 상황일때는 실제로 렌더링되서 비춰지는 월드 좌표를 스크린 좌표로 바꿔주고 해당 스크린 좌표를 

옮기려는 좌표의 부모 기존 로컬 좌표로 변환해줘야한다.

 /// <summary>
    /// 부모가 다른 RectTransform 의 위치값을 변경할때 사용
    /// </summary>
    /// <param name="source"></param>
    /// <param name="target"></param>
    public static void MatchUIPosition(RectTransform source, RectTransform target)
    {
        RectTransform targetParent = target.parent as RectTransform;
        Canvas canvas = target.GetComponentInParent<Canvas>();

        Camera cam = null;
        if (canvas != null && canvas.renderMode != RenderMode.ScreenSpaceOverlay)
            cam = canvas.worldCamera;

        // source의 pivot 월드 위치 -> 스크린 좌표
        Vector2 screenPoint = RectTransformUtility.WorldToScreenPoint(cam, source.position);

        // 스크린 좌표 -> target 부모 기준 로컬 좌표
        RectTransformUtility.ScreenPointToLocalPointInRectangle(
            targetParent,
            screenPoint,
            cam,
            out Vector2 localPoint
        );

        target.anchoredPosition = localPoint;
    }

좌표 변환(월드좌표->스크린 좌표 , 스크린 좌표-> 부모기준 로컬좌표)은 Unity 라이브러리에서 제공되는 RectTransformUntility 를 사용

RectTransfrom 사용시 도움이 되는 API

 

 

- Spine 객체 좌우 반전 

더보기

Skeleton의 ScaleX값을 음수화 해주면된다...

너무 간단해서 놀램

 private SkeletonGraphic graphic;

 graphic.Skeleton.ScaleX = -1 ; //(좌/우 반전)
 graphic.Skeleton.ScaleX = 1 ; //(좌/우 반전 해제)

 

+ Recent posts