Unity ECS - Hybrid 에 대한 스크립팅 및 사용방법 기록 포스팅입니다.

 

ECS에 대한 설명및 기본세팅은 이전 포스팅으로 - https://zprooo915.tistory.com/66

아래 포스팅은 Unity ECS 샘플 프로젝트의 스크립트를 기준으로 작성하였습니다.

 

이번 포스팅은 아래순서로 진행됩니다.

  1. Hybrid ECS란?
  2. 기본적인 사용방법
  3. 사용 Tip & 오류 및 해결방법

 

1. Hybrid ECS란?

  • 간단하게 기존 Unity의 컴포넌트 방식과 ECS 방식을 융합된 방법입니다.
    • Unity Editor 상에서는 기존 Unity와 동일하게 하이러키와 컴포넌트를 사용하고, 스크립트는 ECS 방식입니다.
  • ECS 방법 자체는 기존 사용자가 바로 사용하기에는 무리가 있을 정도로 에디팅 방식이 많이 바뀌지만, Hybrid 방식으로 둘을 적당히 섞음으로써 ECS 방식을 좀 더 쉽게 접근할 수 있습니다.
  • 장점
    • 진입장벽이 낮다    
    • 기존의 유니티에서 제공하는 시스템을 함께 사용할 수 있다. (ECS만 사용한다면, 새로 만들어야 할 수도 있습니다.) 
  • 단점

 

2. 기본적인 사용방법

 편의상 component - Entity - System 순으로 진행하겠습니다.

 순서는 간단한 설명 후 플레이어블 캐릭터를 예시로 드는 식으로 진행합니다. 

 

Component

 - 하나의 정의에 필요한 데이터 집합

 

  • 아래는 플레이어블 캐릭터의 playerInput 데이터의 집합(컴포넌트) 입니다. 일반 클래스 생성하듯 만듭니다.
public class PlayerInput : MonoBehaviour 
{ 
    public float2 move; 

    public bool jump; 
    public bool seat; 

    public bool attack; 
}

 

  • 그리고 밑은 캐릭터/ 몬스터 누구나 가지고 있을법한 hp에 대한 데이터 집합(컴포넌트) 입니다.
public class Health : MonoBehaviour 
{ 
    public float value;

    public bool isDead;
}

 

  • 위와 같이 하나의 정의에 필요한 데이터 집합들이 component입니다.
    • input에 관련된 요소, Health에 관련된 요소 hp값과 죽었는지 여부입니다.

 

Entity

 - 컴포넌트들의 집합

  •  위의 컴포넌트들을 조합해서 만들어집니다. 기존의 게임오브젝트와 유사하게 생각하시면 됩니다.
  •  위에서 만든 컴포넌트들을 에디터창에서 원하는 오브젝트에 붙이면 됩니다.
    • 각 오브젝트에 필요한 특성과 정의를 부여한다고 생각할 수 있겠네요.
  •  반드시 지켜야 할 것이 ECS의 관리를 받는 오브젝트를 만들려면 Game Object Entity라는 클래스도 오브젝트에 추가해야합니다. (이것을 안하면 System에서 아예 인지를 하지 못합니다.)
  • 아래는 플레이어블 캐릭터의 인스펙터 창입니다.

  • 아래는 체력만 가진 몬스터의 인스펙터 창입니다. ( PlayerInput 빼고는 위와 동일합니다.)

 

 

 System

 - Entity들의 행동로직 

  • 데이터들을 가공하는 곳으로 일반 객체지향의 method와 비슷합니다.
  • 시스템은 방식이 두가지가 있습니다.
    • 두 방식이 어떨 때 좋을지는 정확히는 잘 모르겠네요.
    • 차이점이라면 두번째는 개체수를 얻을 수 있다는 점과 개인적으로 사용했을 때는 여러 엔티티를 가져와 로직을 돌려야 할 경우 두번째 방식이 더 편하고, 하나만 사용할 경우에는 첫번째가 편하더군요.
  • 기본 템플릿

1. 

using UnityEngine; 
using Unity.Entities; 

public class ClassName : ComponentSystem 
{ 
    public struct StructName
    { 
        public ComponentClass name; 
         
    } 
    protected override void OnUpdate() 
    { 
        foreach(var entity in GetEntities<StructName>()) 
        {

            // 로직 진행
            // ex ) entity.name.property = 0;
        } 
    } 
}

 

2.

using Unity.Entities; 
using UnityEngine; 

public class ClassName : ComponentSystem 
{ 
    public struct StructName
    { 
        public readonly int Length; 
        public ComponentArray<ComponentClass > name; 
    } 
    [Inject] 
    private StructName m_data; 

    protected override void OnUpdate() 
    {         
        for(int i = 0; i < m_data.Length; i++) 
        {

            // 로직 진행
            // ex ) m_data.name[i].property = 0; 
             
        } 
    } 
}

 

  • 사용예시

  • 아래는 인풋제어 컴포넌트를 가진 엔티티들을 불러와 키맵핑을 해주는 시스템입니다.
    • 다른 컴포넌트를 가지고 있든말든 인풋제어만 있으면 다 로직이 돕니다.
using UnityEngine; 
using Unity.Entities; 

//사용자의 입력을 input component에 배정하는 시스템 
// 이동 점프 앉기 공격 등 키입력 맵핑 
public class PlayerInputSystem : ComponentSystem 
{ 
    public struct PlayerData 
    { 
        public PlayerInput input; 
         
    } 
    protected override void OnUpdate() 
    { 
        foreach(var entity in GetEntities<PlayerData>()) 
        { 
            entity.input.move.x = Input.GetAxis("Horizontal"); 

            entity.input.seat = false; 
            entity.input.jump = false; 

            if (Input.GetKey(KeyCode.DownArrow)) entity.input.seat = true; 
            if (Input.GetKey(KeyCode.UpArrow)) entity.input.jump = true; 

        } 
         
    } 
}

 

 

  • 아래는 플레이어블 캐릭터의 컴포넌트들을 가진 엔티티들을 불러와 이동시켜주는 시스템입니다.
    • 마찬가지로 PlayerData에 있는 컴포넌트를 가진 모든 엔티티를 불러옵니다.
using Unity.Entities; 
using UnityEngine; 

// 입력된 input에 맞춰 캐릭터 이동  
public class PlayerMoveSystem : ComponentSystem 
{ 
    public struct PlayerData 
    { 
        public readonly int Length; 
        public ComponentArray<PlayerInput> input; 
        public ComponentArray<Health> health; 
        public ComponentArray<Rigibody2D> rigid; 
    } 
    [Inject] 
    private PlayerData m_data; 

    protected override void OnUpdate() 
    { 
        // 병렬로 전체 캐릭터들이 모두 별도로 이동하고, 나중에 한꺼번에 적용 
        float dt = Time.deltaTime; 
        for(int i = 0; i < m_data.Length; i++) 
        { 
            m_data.rigid[i].AddForce(new Vector2(m_data.input[i].move.x * 4, 0)); 
             
        } 
    } 
}

 

  • 아래는 Health를 가진 모든 오브젝트에 대해 죽음 판별하는 시스템입니다.
    • 캐릭터와 몬스터 모두 Health를 가졌으니 모두를 대상으로 돌겠죠
using UnityEngine; 
using Unity.Entities; 

public class HealthCheckSystem : ComponentSystem 
{ 
    public struct PlayerData 
    { 
        public Health health; 
         
    } 
    protected override void OnUpdate() 
    { 
        foreach(var entity in GetEntities<PlayerData>()) 
        { 
            if(entity.health.value <=0) entity.health.isDead = true;

        } 
         
    } 
} 

 

  • 그래서 필요 기능별로 컴포넌트들을 구분지어야 나중에 시스템을 돌릴때 편해지고 직관적이게 됩니다. 오브젝트를 사용할 때도 컴포넌트만으로 제어하기 편합니다.
    • 예를들어 이 캐릭터는 무적인 특성이 있다 했을 때, 예외처리를 하는게 아니라 단지 그 캐릭터에게 Health 컴포넌트만 제거하면 Health에 관련된 모든 로직을 무시할 수 있게되죠.
    • 마찬가지로, 스킬이나 특성들도 컴포넌트만으로 제어할 수 있겠죠.

 

 

3. 사용 Tip & 오류 및 해결방법

  • 시스템 로직 도는 순서 변경하는 방법

    •  기능별로 시스템이 돌기 때문에 간혹 순서상에 문제가 발생할 수도 있습니다.
      •  물론 이런경우를 발생 안시키는게 베스트이긴 하지만, 부득이할 경우 로직 도는 순서를 변경 시킬 수 있습니다.
      • (디폴트로는 생성 순으로 진행되는 것으로 압니다.)
    • 참고 사이트 - https://docs.unity3d.com/Packages/com.unity.entities@0.0/manual/system_update_order.html
    • 방법 
// 타겟 시스템이 SystemClass 이후에 실행되도록 설정
// 같은 시스템 그룹 내에서 동작
[UpdateAfter(typeof(SystemClass))]

// 타겟 시스템이 SystemClass 이전에 실행되도록 설정
// 같은 시스템 그룹 내에서 동작.
[UpdateBefore(typeof(SystemClass))]

// 타겟 시스템을 SystemGroup에 귀속시킴.
// 디폴트로 개발자가 만든 System은 모두 SimulationSystemGroup에 속함.
[UpdateInGroup(typeof(SystemGroup))]


// 사용 예시
// PlayerInputSystem 실행 이전에 TestSystem (타켓시스템)을 실행하도록 설정.
[UpdateBefore(typeof(PlayerInputSystem))]
public class TestSystem : ComponentSystem
{
    public struct PlayerData
    {
        public PlayerInput input;
    }
    protected override void OnUpdate()
    {
        foreach (var entity in GetEntities<PlayerData>())
        {
        }
    }
}

 

  • 여러 엔티티를 하나의 시스템에서 써야할 때

    • 그냥 두개 inject 해오면 된다.
    public struct PlayerData 
    { 
        public readonly int Length; 
        public ComponentArray<PlayerInput> input; 
        public ComponentArray<Transform> transform; 

    } 
    [Inject] 
    private PlayerData p_data; 

    public struct MinimapData 
    { 
        public readonly int Length; 
        public ComponentArray<Minimap> minimap; 

    } 
    [Inject] 
    private MinimapData m_data;

 

  • Tag처럼 쓰기

    • Tag도 하나의 컴포넌트로써 사용하면 된다.
      • ex) Component로 Human클래스를 만들고 아무 내용없이 오브젝트에 붙여만 놓으면, 시스템에서 Human으로 걸러낼 수 있죠.

 

 

 

 

 

 

 

 

Posted by 검은거북

* 먼저 해당 포스트는 튜토리얼이 아닌 Firebase 사용하는 과정에서 발생한 오류 및 해결과정, TIP 중심의 글임을 알려드립니다.  

* Firebase 설명 자체가 굉장히 잘 되어있으므로 튜토리얼은 Firebase를 참고하세요. 여기서 발생한 오류/정보도 Firebase 가이드에 있으나 놓쳤을 확률이 높습니다

 

 

- Functions는 그냥 서버에 본인이 쓸 API 함수를 작성하여 업로드하는 것으로 생각하면 편안.

 - 접근할 서버의 URL = https://us-central1-프로젝트ID.cloudfunctions.net/    

 - 본인이 올린 API 함수는 위 URL을 통해 API 호출하듯 호출하면 된다. POST든 GET이든

    - ex ) https://us-central1-myproject.cloudfunctions.net/myfunc 

 

1. Functions에서의 외부 API 호출시 무시되는 현상.

 - Paypal 에 요청 API를 보냈을 때 아예 무시가 되는 경우가 발생.

 - 무료버전(spark)은 구글 외의 다른 url로 API 접근이 안된다.

    - Blaze 버전으로 업데이트 필요.

 

 

 

 

Posted by 검은거북

ECS 개요는 알겠는데, 대체 얼마나 빨라지길래 스크립팅 방식을 바꿔가면서까지 제공을 하는 걸까요?
그래서 한 번 유니티에서 제공해주는 샘플앱을 이용해 간단하게 벤치마크 테스트 해보았습니다.

 

* 아래 결과는 제 컴퓨터에서 18년 12월 28일 기준으로 수행된 결과입니다.

  * 사양 - i5-6600 / GTX 970 / Ram 15GB

* 실행 프로젝트는 유니티 ECS 샘플 프로젝트의 TwoStickShooter 입니다.

* job system과 burst compiler를 고려하지 않고, 단순히 오브젝트 리젠 갯수를 늘린 결과입니다.

 

* 오브젝트 무한리젠 ( 매 업데이트(0.02)마다 오브젝트 10개씩 생성)

* Classic

Profiler
Game Play Scene

 - 약 30FPS 아래로 내려갈 때의 프로파일링 결과입니다.

 - 총 오브젝트 갯수가 6800개를 넘어가자 어마어마하게 끊깁니다.

 

* hybrid ECS

- 약 30FPS 아래로 내려갈 때의 결과입니다.

- 총 오브젝트 갯수가 21000개 정도 이상되니 끊기네요

- 특이한 점은 classic은 끊기다가 다시 조금 회복되다 또 끊기는 반면에 Hybrid는 더더더 느려집니다.

 

*Pure ECS

- 30 FPS 이하로 떨어지질 않아요 하단 이미지가 실행중인 이미지인데 저 상태에서도 굉장히 부드럽습니다.

- 다만 특이한 점은 총 오브젝트 갯수가 균일하게 약 2000개 밖에 안 찍힙니다. 화면상으로만 봐도 몇 만개는 되보이는데 말이죠. ( 이 부분에 대해서는 좀 더 ECS 시스템에 대한 공부가 필요할 듯하네요)

 

 

 

 

번외로 Pure ECS가 30 FPS 이하로 떨어지는 구간이 대체 언제쯤일지 테스트를 해보았는데, 매 업데이트(0.02초)마다 적 오브젝트를 50개씩 만드니 아래와 같이 떨어지긴 하더군요 (참고로 적오브젝트는 계속 총알 오브젝트를 날립니다)

 

Result

- 거의 성능 차이가 classic을 1이라 했을 때, hybrid가 3, pure가 5~10 정도로 차이가 나는 듯 보이는 것 같습니다.

 

* 다음 포스팅부터는 hybrid ECS를 사용하는 방법 및 사용하면서 발생했던 오류 위주로 포스팅 시작하도록 하겠습니다.

 

 

* 유니티 샘플 프로젝트 경로를 같이 올리려했는데 반년 사이에 많은 변화가 있었는지 git 저장소에 TwoStickShooter를 비롯해 제가 가진 예전 샘플들이 없어졌더군요 ; (너무 바뀌어서 같은 프로젝트인지도 확실치 않지만 최근 샘플을 공유드립니다.)

 - git https://github.com/Unity-Technologies/EntityComponentSystemSamples

* 글을 쓰는 날(19.5.19)을 기준으로는 ECS 벤치마크 데이터가 빈번하게 검색되어 나오는 것 같네요. 더 궁금하시면 영문으로 찾아보시면 만족할만한 자료가 나올 듯 합니다.

 

* 개인이 공부하며 하는 포스팅이기에 잘못된 부분이 있을 수 있습니다.

 혹시 잘못되거나 문제시 되는 부분이 있으면 지적 부탁드립니다.

Posted by 검은거북

* 먼저 해당 포스트는 튜토리얼이 아닌 Firebase 사용하는 과정에서 발생한 오류 및 해결과정, TIP 중심의 글임을 알려드립니다.  

* Firebase 설명 자체가 굉장히 잘 되어있으므로 튜토리얼은 Firebase를 참고하세요. 여기서 발생한 오류/정보도 Firebase 가이드에 있으나 놓쳤을 확률이 높습니다

 

 

1. 사용하면서 느낀 Realtime VS Firestore

  • 우선 전체적으로 사용자 관점에서는 Firestore가 좋습니다.
  • 다만 Firestore의 단점은 베타라는 점과 데이터 옮기기가 조금 더 번거롭다는 점.
  • 쿼리문
    • Realtime
      • 내림차순이 없습니다;;;  ( 내림차순 하나면 처리될 쿼리가 엄청 복잡해질 수 있습니다..)
      • 여기서 트랜젝션 개념은 여러 일의 원자성 보장이 아닌 한가지 일에 대한 동시접근을 못하게 보장 (lock 개념과 비슷하다)
    • Firestore
  • 저장방식
    • Realtime
      • 완전 JSON이라 봐도 무방 - 실제로 import , export도 json 형식으로 연동되서, 데이터를 이동하기 편하다.
    • Firestore
  • 개인적으로 저장방식은 Realtime이나 Firestore나 비슷한 것 같습니다.

 

2. Firestore 복합쿼리가 동작하지 않음 ( 아무것도 찾지 못한다)

 - 서로 다른 필드에 대해서는 색인 등록을 해야한다.

 

3. 규칙을 설정했으나 Function으로 접근시 규칙이 무시된다.

 - 파이어베이스 Functions에서의 DB 접근은 모든 규칙을 무시한다.

 - DB의 규칙은 클라이언트에서의 접근에 적용된다.

 

 

 

 

Posted by 검은거북

* 먼저 해당 포스트는 튜토리얼이 아닌 Firebase 사용하는 과정에서 발생한 오류 및 해결과정, TIP 중심의 글임을 알려드립니다.  

* Firebase 설명 자체가 굉장히 잘 되어있으므로 튜토리얼은 Firebase를 참고하세요. 여기서 발생한 오류/정보도 Firebase 가이드에 있으나 놓쳤을 확률이 높습니다.

 

1. 웹 클라이언트 init 데이터 경로 

auth는 아니지만 init 에 사용되는 데이터 경로입니다.

 - 아래는 웹 클라이언트 측에서 Firebase 연동하여 init할 때 필요한 config 입니다. 

var config= {
apiKey:""
authDomain:""
databaseURL:""
projectId:""
storageBucket:""
messagingSenderId:""
 };

  - confing에는 연동하고자 하는 프로젝트의 각종 값이 필요합니다. 아래는 그 값을 찾을 수 있는 위치입니다.

  • apiKey = 좌측탭 상단 - 톱니바퀴 -  프로젝트 설정 - 일반 - 웹 API 키
  • projectId = 프로젝트 설정 - 일반 - 프로젝트 ID
  • authDomain =  프로젝트ID.firebaseapp.com   이거나 Authentication - 로그인 방법 - 승인된 도메인 중 localhost 외의 경로
  • databaseURL = 좌측 탭의 Database의 데이터베이스 이름 또는 디폴트로 https://프로젝트ID.firebaseio.com 이거나  프로젝트 설정 - 서비스 계정 - Admin SDK 구성 스니펫의 databaseURL
  • storageBucket = 디폴트로는 gs://프로젝트ID.appspot.com 
  • messagingSenderId = 프로젝트 설정 - 클라우드 메시징 - 발신자 ID

 

2. FirebaseUI의 데이터 값

 - FirebaseUI는 로그인 UI가 완성된 버전을 붙이는 것입니다. 개발자가 따로 UI를 짤 필요가 없죠. 저는 Facebook, google play game, 익명 을 사용해봤습니다.

  • 익명

    • 그냥 사용 설정

 

 

3. FirebaseUI에 게스트 로그인 추가하기

 - signInOptions 에 "anonymous" 추가

 

 

Posted by 검은거북

Firebase 란?

 - 구글에서 제공하는 통합 플랫폼 서비스. 인증 / 서버 / 저장소 / DB 등 앱,웹 개발에 필요한 인프라 전반을 제공해 줌과 동시에 수익창출/ 사용 및 크래시 통계 / 테스트 등의 품질 관리 도구를 제공해주는 플랫폼입니다. 

 개발을 하다보면 인프라 / 환경 구축이 정말 귀찮은데 이것을 대신 해주는 것이죠. 개발자들이 해야할 일은 코드 몇 줄로 사용하고자 하는 기능을 웹/앱에 연동시켜주고 쓰고자 하는 내용을 밀어넣는 것입니다. 

 (대표적인 예로 서버로 쓸 수 있는 Firebase Cloud Functions는 API로 사용할 함수만 작성하고 디플로이하면 바로 API로 활용 가능합니다.)

 이러한 방식을 '서버리스' 방식이라고 합니다. 실제로 서버를 사용은 하지만 유저가 서버를 직접 관리(환경 구성, 확장 등)하지 않기 때문입니다. 특히 Firebase의 경우에는 백엔드 기능 전반을 서비스로 제공해주는 Baas(Backend as a Service) 중 하나입니다. 

 

가격 정책

 - https://firebase.google.com/pricing?authuser=0

 

장/단점

 장점

 장점은 위에서 계속 말한바와 같이 인프라 전반의 환경 구성과 확장 등을 알아서 해주기 때문에 사용자는 그저 필요한 기능을 연동시켜 사용하면 된다는 점입니다.

 그리고 기본적인 기능은 다 제공되는 점도 장점이라 할만 할 것 같습니다. 복잡하지 않은 서버가 필요한 상황이라면 충분히 활용 가능할 것입니다. (복잡한 기능도 비용이 많이 들뿐 사용 가능할 것 같습니다.)

 

 단점

 단점으로는 관리를 개발자가 하지 못하기 때문에 제공되는 기능과 사용방법은 제한적일 수 밖에 없습니다. Firebase가 제공하는 서버 기능만 사용할 수 있기도 하고, 복잡한 기능이나 쿼리를 구현하기 위해서는 추가적인 교육 비용이 많이 들 수 있습니다. DB만을 봐도 RDB가 아닌 NoSQL DB만을 제공합니다. 그렇기 때문에 RDB에 익숙하신 분에게는 따로 공부가 필요할 수 있습니다. (다행히 나름의 쿼리를 제공하긴 합니다.)

 

Firebase 링크

 - https://firebase.google.com/

 - Firebase에서 제공하는 기능은 위 링크에서 확인 가능합니다.

 

향후 포스팅 방향

 * Firebase 홈페이지 자체가 샘플도 많고, 튜토리얼이 굉장히 친절한 편입니다. 그렇기 때문에 앞으로 실행 과정이나 사용 방법보다는 파이어베이스 연동 과정에서 발생한 에러 / 해결방법 위주로 포스팅할 예정입니다.

 * 포스팅은 플랫폼 / Firebase 기능 별로 페이지를 만들고, 에러 및 해결방법을 쌓아가는 방식이 될 예정입니다.

 

 

* 개인이 공부하며 하는 포스팅이기에 잘못된 부분이 있을 수 있습니다.

 혹시 잘못되거나 문제시 되는 부분이 있으면 지적 부탁드립니다.

Posted by 검은거북

* 작년부터 개인 프로젝트를 진행하며 공부한 내용을 정리하는 포스팅입니다.

 

ECS란 무엇인가

Entity - Component - System 의 줄임말로, 각 용어에 대해 간단히 설명하자면 아래와 같습니다.

 

  • Component  - 데이터 집합
    • 요소를 이루는 데이터 집합
    • ex
      • position - 위치의 데이터만 가짐 (x,y,z)
      • rotation - 방향의 데이터만 가짐 (x,y,z)
  • Entity  - 컴포넌트 집합
    • 하나의 오브젝트를 이루는 데이터 집합의 집합 
    • ex
      • character - position, rotation 컴포넌트를 가짐.
  • System - 데이터들을 가공하는 작업 ( method 개념)
    • ex
      • MoveSystem - position, rotation 컴포넌트를 가진 엔티티들을 대상으로 키 입력시 position의 데이터를 변경하기만 한다.

 

기존의 객체 지향 개념(OOP) 이 아닌 데이터 지향 방식(DOD)의 개념으로, 데이터 지향은 저장되는 데이터의 위치를 집적시켜 캐시 히트율을 높여 속도를 끌어올리는 목적의 개념으로 알고있습니다.

 

 * 데이터 지향 부가 설명 및 운영체제 지식

  - 프로그램에서 데이터 필요시 CPU 캐시공간을 먼저 검사하고, 거기 없으면 메모리(램)에서 해당 데이터를 가져와서 사용합니다. (메모리에서 가져올 때는 필요 데이터 주변의 데이터를 뭉태기로 캐시에 가져옵니다. 그 주변 데이터를 다음에 사용할 확률이 크기때문이죠) 이때 CPU 캐시에서 바로 가져오느냐, 메모리에서 가져와서 사용하느냐의 속도차이는 아마 몇 십배일거에요. 즉, 캐시에 미리 사용할 데이터가 없을 수록 속도는 몇십배씩 느려진다는 거죠.  그래서 데이터 지향 방식은 데이터들을 한 곳에 몰아 넣어서 캐시 적중률을 늘려 속도를 올릴 수 있는거죠. (객체 지향은 객체에 따라 데이터가 위치가 분산될겁니다.) 

 

Unity Scripting 방법

* 현재 unity에서 제공하는 방식은 3가지 입니다.

  1. Classic ( 현재 일반적으로 사용하는 컴포넌트 방식입니다.)
  2. Hybrid ECS 
    • Component + ECS 방식입니다.
    • 에디터상의 작업은 기존의 Classic 방식을 따르고, 코딩은 ECS 방식으로 하는 형식입니다. 아마 ECS의 진입장벽을 낮추고, ECS의 속도면 이점도 어느정도 갖추기 위해 만들어졌을거 같네요.
  3. Pure ECS
    • 완전한 ECS 방식입니다.
    • 기존의 Classic 방식과는 객체 생성및 등록 방식이 확 바뀝니다. 거의 모든 걸 스크립트상에서 처리하기에 진입장벽이 많이 높습니다.

 

장점 & 단점

  • 장점

    • 재사용성이 높다. / 상호 의존도가 낮다
      • 위 부분은 객체지향의 장점에도 포함되지만 개인적으로 사용 경험상, 데이터 지향 방식이 더 활용도가 높다고 느껴집니다.
      • 데이터 지향의 시스템은 자기가 필요한 엔티티 집합을 검색해 로직을 돌리는 방식이고, 엔티티는 시스템의 검색에 찾아지도록 필요 컴포넌트들을 포함시켜 만들어진 집합입니다. 즉, 특정 엔티티가 특정 시스템에 돌아가게 하고싶다면, 시스템이 필요로하는 컴포넌트만 엔티티에 추가만 하면 끝인거죠.
        • 이런 방식으로 시스템과 컴포넌트를 구성해놓으면 엔티티는 자기가 원하는 것만 골라 재사용하기 용이하게 느껴집니다.
        •  그리고 실제 설계 / 코딩을 할 때도 필요한 컴포넌트를 개별로 시스템에서 가져다 사용하기때문에 다른 시스템/ 엔티티에 영향도가 적습니다. (상호 의존성 낮음)
    • 유지보수가 용이하다.
      • 위의 장점과 일맥상통합니다.
        • 시스템 별로 필요 엔티티만 집약되어있기에 시스템별로 찾고 고치기가 쉽습니다. 
    • 실행 속도가 빠르다
      • 네, 엄청 빠릅니다. (이건 다음 포스팅에서 검증하도록 하겠습니다)
  • 단점

    •  진입장벽이 많이 크다.
      • Pure가 특히 높습니다. Hybrid는 기존의 방식을 어느정도 쓰기때문에 사실 ECS 개념만 좀 숙달되면 금방하는데, Pure는 스크립트로 거의 모든 걸 진행하여 에디터 UI상의 이점을 많이 뺏깁니다.
    •  디버깅이 힘들다.
      • Pure의 단점으로, 위의 단점과 일맥상통합니다.
      • Pure는 에디터상의 하이러키창에 오브젝트가 안나옵니다. 그래서 하이러키창을 자주 활용하여 디버깅하시는 분은 꽤 당황스러울 수 있습니다.
    • 베타 버전 (개인적으로 최고 단점으로 뽑습니다.)
      • Unity에서 기존에 제공하던 컴포넌트들을 직접 구현해야 할 수 있습니다. 
        • 일부 컴포넌트가 ECS형태로 아직 제공이 안됩니다 ㅠㅠ  자주 사용하는 Physics, animation관련 컴포넌트도 없는게 꽤 있습니다.

 

저는 아직 Pure ECS는 많이 어색하고 어렵더군요. 그래서 앞으로의 포스팅은 Hybrid ECS 위주로 진행하겠습니다. 애초에 개인 프로젝트도 Hybrid ECS를 이용해 만들기도 했거든요.

 

다음 포스팅은 유니티에서 제공해주는 샘플 프로젝트를 이용해 실험해본 간단한 성능 벤치마크를 하도록 하겠습니다.

대체 얼마나 기존에 비해 성능이 빨라질지 간단하게 실험한 결과입니다.

 

* 개인이 공부하며 하는 포스팅이기에 잘못된 부분이 있을 수 있습니다.

 혹시 잘못되거나 문제시 되는 부분이 있으면 지적 부탁드립니다.

Posted by 검은거북

 이미지 공유하기 기능을 구현하려다가 이미 구현되어있는게 있을법해서 찾아보니까 JNI로 깔끔하게 구현된게 있더군요! IOS까지!

https://github.com/tejas123/general-sharing-in-android-ios-in-unity


 문제는 Android N 부터는 정책이 달라져서 저 코드의 이미지 공유하기를 실행시키면,

FileUriExposedException: exposed beyond app through ClipData.Item.getUri()

이런 에러코드가 뜹니다. 



해결방법

1. 타겟 API를 안드로이드 6.0 이하로 내린다.

 - 아주아주 간단하지만, 찝찝합니다. 권장하지 않아요. 

 - 안드로이드에서는 앞으로 위 정책으로 진행할텐데 계속 유지보수하는한 언젠가는 결국 수정해야하잖아요?


2. FileProvider 방식으로 변경한다.

 - 안드로이드에서 권장하는 방법입니다. 

 - Android Native에서 구현 방법은 공식문서에서 이미 가이드를 하고 있으므로, 해당 가이드를 유니티에서 구현하도록 합니다.

 - https://developer.android.com/reference/android/support/v4/content/FileProvider

 - 가이드를 고대로 JNI로 unity에 구현하고, Manifest와 res만 수정하면 되겠네요.  아래부터 수정한 내역을 공유하도록 하겠습니다.


 - Code 수정 (GIT의 코드를 가이드에 맞게 일부 수정한 내용입니다.)

IEnumerator SaveAndShare () { yield return new WaitForEndOfFrame (); #if UNITY_ANDROID byte[] bytes = MyImage.EncodeToPNG(); string path = Application.persistentDataPath + "/MyImage.png"; File.WriteAllBytes(path, bytes); AndroidJavaClass intentClass = new AndroidJavaClass("android.content.Intent"); AndroidJavaObject intentObject = new AndroidJavaObject("android.content.Intent"); intentObject.Call<androidjavaobject>("setAction", intentClass.GetStatic<string>("ACTION_SEND")); intentObject.Call<androidjavaobject>("setType", "image/*"); intentObject.Call<androidjavaobject>("putExtra", intentClass.GetStatic<string>("EXTRA_SUBJECT"), "Media Sharing "); intentObject.Call<androidjavaobject>("putExtra", intentClass.GetStatic<string>("EXTRA_TITLE"), "Media Sharing "); intentObject.Call<androidjavaobject>("putExtra", intentClass.GetStatic<string>("EXTRA_TEXT"), "Media Sharing Android Demo"); AndroidJavaClass unity = new AndroidJavaClass("com.unity3d.player.UnityPlayer"); AndroidJavaObject currentActivity = unity.GetStatic<androidjavaobject>("currentActivity"); //AndroidJavaClass uriClass = new AndroidJavaClass("android.net.Uri"); AndroidJavaClass uriClass = new AndroidJavaClass("android.support.v4.content.FileProvider"); AndroidJavaClass fileClass = new AndroidJavaClass("java.io.File"); AndroidJavaObject fileObject = new AndroidJavaObject("java.io.File", path);// Set Image Path Here AndroidJavaObject stringObject = new AndroidJavaObject("java.lang.String", "com.myproject.project.share.fileprovider");// Set Image Path Here //AndroidJavaObject uriObject = uriClass.CallStatic<androidjavaobject>("fromFile", fileObject); AndroidJavaObject uriObject = uriClass.CallStatic<androidjavaobject>("getUriForFile", currentActivity, stringObject, fileObject); // string uriPath = uriObject.Call<string>("getPath"); bool fileExist = fileObject.Call<bool>("exists"); Debug.Log("File exist : " + fileExist); if (fileExist) intentObject.Call<androidjavaobject>("putExtra", intentClass.GetStatic<string>("EXTRA_STREAM"), uriObject); currentActivity.Call("startActivity", intentObject); #endif }

  - 작은 설명을 붙이자면, 기존에 Uri.fromFile 함수를  FileProvider.getUriForFile 함수로 함수와 매개변수를 수정한게 끝입니다.  



 - AndroidManifest.xml 추가 

   - 경로 - \Assets\Plugins\Android\Share\AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.myproject.project.share" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="25" /> <application> <provider android:name="android.support.v4.content.FileProvider" android:authorities="com.myproject.project.share.fileprovider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/filepaths" /> </provider> </application> </manifest>


 - res 추가

  - 경로 - \Assets\Plugins\Android\Share\res\xml\filepaths.xml

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
  <external-path name="storage/emulated" path="."/>
</paths>



진행과정에서 발생했던 에러

 android.content.pm.PackageItemInfo.loadXmlMetaData(android.content.pm.PackageManager, java.lang.String)' on a null object reference

  - 코드 상의 패키지 이름과 Provider의 authorities 오타 수정하여 통일

Posted by 검은거북

간혹 코딩을 잘못해서 무한루프가 돌거나, 유니티가 강제로 꺼질 때가 있습니다. 근데 이 때 씬을 저장 안했다면....그대로 날라가죠...ㅠㅠ  비슷한 경험이 있어서 알아보니까 유니티에서  씬이 변경될 때 별도로 백업을 해준다고 하더라고요. 


만약 무한루프나 모종의 이유로 강제종료가 되었다면, 백업된 씬의 확장자를 Unity로 변경해서 실행시키면 됩니다.

단, 위의 작업은 유니티를 재실행하기 전에 해야합니다. 재실행하면 백업도 날라가요.


백업 파일 경로 - [UnityProject] - [Temp] - [_Backupscenes]


예 ) 해당 파일 경로의 0.backup -> 0.Unity로 이름 변경

   0.Unity를 실행시켜 종료되기 이전의 씬을 불러오고, 다시 기존의 씬에 저장.

Posted by 검은거북

폭발시 카메라 흔들림 효과 스크립트


흔들림 효과를 넣고 싶은 카메라에 스크립트 추가하여 사용


    
    Vector3 originPos;
    
    void Start () {
        originPos = transform.localPosition;
    }
	
    public IEnumerator Shake(float _amount,float _duration)
    {
        float timer=0;
        while(timer <= _duration)
        {
            transform.localPosition = (Vector3)Random.insideUnitCircle * _amount + originPos;

            timer += Time.deltaTime;
            yield return null;
        }
        transform.localPosition = originPos;

    }



참고 링크 : https://gist.github.com/ftvs/5822103


Random.insideUnitCircle - 반경 1의 원 내부의 랜덤한 값을 vector로 반환하는 함수

 https://docs.unity3d.com/kr/530/ScriptReference/Random-insideUnitCircle.html



ShakeCamera.cs

Posted by 검은거북
이전버튼 1 2 이전버튼

블로그 이미지
프로그래밍 공부 요약 및 공부하며 발생한 궁금증과 해결과정을 포스팅합니다.
검은거북

공지사항

Yesterday
Today
Total

달력

 « |  » 2024.5
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31

최근에 올라온 글

최근에 달린 댓글

글 보관함