본문으로 건너뛰기

"unity3d" 태그로 연결된 43개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 8분
karais89

환경

  • macOS Mojave v10.14.6
  • Unity 2019.2.5f1
  • Github Desktop
  • Rider 2019.2
  • UniRx v7.1.0

원문 : https://qiita.com/toRisouP/items/f6088963037bfda658d3

이 포스팅은 원문을 단순히 구글 번역을 하여 정리한 내용입니다. 일본어를 잘하시는 분은 원문을 보시는게 더 좋으실 것 같습니다.

UniRx에 대한 기사 요약은 여기


Rx의 IObservable<T>는 Hot/Cold라는 큰 두가지 특징이 있습니다.

이러한 성질을 이해하지 않은 채 스트림을 설계하면 의도된 동작을 해주지 않는 경우가 있습니다.

이번에는 이 Hot/Cold의 성격에 대해 간략하게 정리하고자 합니다.


요약


한마디로 말하면?

  • Cold: 스트림의 전후를 연결하는 파이프. 단독으로는 의미가 없다. 대부분의 오퍼레이터는 Cold이다.
  • Hot: 스트림에서 값을 계속 발행하는 수도꼭지. 항상 흐린다. 뒤에 파이프를 많이 연결 할 수 있다.

자세히 설명하면

Cold Observable

  • 자발적으로 아무것도 하지 않는 수동적인 Observable
  • Observer이 등록되어 (Subscribe 되고) 처음 일을 시작한다.
  • 스트림의 전후를 그냥 연결만 한다. 스트림을 분기시키는 기능은 없다.

Hot Observable

  • 자신이 값을 발행하는 능동적인 Observable
  • 후속 Observer의 존재에 관계없이 메시지를 발행한다
  • 자신보다 상류의 Cold Observable을 시작하고 값의 발행을 요구하는 기능을 가진다
  • 하류의 Observer를 모두 묶어, 정리해 같은 값을 발행한다 (스트림을 분기시킨다)

Hot과 Cold 구분법

대부분의 오퍼레이터는 Cold인 성질이며, 자신이 명시적으로 스트림(stream)을 Hot으로 변환하지 않는 한 Cold 그대로 입니다.

Hot 변환용 오퍼레이터는 이른바 Publish 계의 오퍼레이터가 해당 합니다.


Hot에 대해


Hot Observable의 성질


스트림을 가동시키는 성질

Rx 스트림은 기본적으로 Subscribe가 된 순간에 각 오퍼레이터의 작동이 시작하게 되어 있습니다. 하지만 Hot Observable을 스트림 중간에 끼우는 것으로, Subscribe를 샐행 이전에 스트림을 실행시킬 수 있습니다.


스트림을 분기하는 성질

Hot Observable은 스트림을 분기 할 수 있습니다.

Cold에 대해


Subscribe 될 때까지 작동하지 않는 성질

Cold Observable은 Subscribe될때 (또는 Hot 변환될때)까지 작동하지 않습니다. 마음이 없는 Observable 입니다.

작동하지 않는 Cold Observable에 전달된 메시지는 모두 처리 조차 되지 않고 소멸 됩니다.

특히 값의 발행 타이밍이나 전후 관계가 중요한 오퍼레이터를 사용하는 경우는, 어느 타이밍부터 처리가 시작되는지를 충분히 인지하고 사용하지 않으면 안됩니다. 같은 스트림 정의라도, Subscribe 한 타이밍에 따라서 동작이 바뀌어 버립니다. 아래가 그 예 입니다.

ColdExample.cs

var subject = new Subject<string>();

// subject에서 생성된 Observable은 [Hot]
var sourceObservable = subject.AsObservable();

// 스트림에 흘러 들어온 문자열을 연결하여 새로운 문자열로 만드는 스트림
// Scan()은 [Cold]
var stringObservable = sourceObservable.Scan((p, c) => p + c);

// 스트림에 값을 흘린다
subject.OnNext("A");
subject.OnNext("B");

// 스트림에 값을 흘린 후 Subscribe 한다.
stringObservable.Subscribe(Debug.Log);

// Subscribe 후 스트림에 값을 흘린다.
subject.OnNext("C");

// 완료
subject.OnCompleted();

실행결과

C

위의 코드를 실행한 결과 C가 출력 될 것입니다.

이것은 Scan 오퍼레이터가 Cold이기 때문에 Subscribe 전에 발행된 A 그리고 B 가 처리되지 않았기 때문입니다.

만약 여기에서 "Subscribe하기 이전에 발급 된 값을 처리 했으면 좋겠다"는 경우는 어떻게 하면 좋을까요. 이 경우 Hot 변환 오퍼레이터를 끼워 Subscribe하기 이전에 스트림을 시작하면 좋을 것입니다.

var subject = new Subject<string>();

// subject에서 생성된 Observable은 [Hot]
var sourceObservable = subject.AsObservable();

// 스트림에 흘러 들어온 문자열을 연결하여 새로운 문자열로 만드는 스트림
// Scan()은 [Cold]
var stringObservable = sourceObservable
.Scan((p, c) => p + c)
.Publish(); // Hot 변환 오퍼레이터

stringObservable.Connect(); // 스트림 가동 개시

// 스트림에 값을 흘린다
subject.OnNext("A");
subject.OnNext("B");

// 스트림에 값을 흘린 후 Subscribe 한다.
stringObservable.Subscribe(Debug.Log);

// Subscribe 후 스트림에 값을 흘린다.
subject.OnNext("C");

// 완료
subject.OnCompleted();

실행 결과

ABC

Publish 라는 Hot 변환 연산자를 사이에 끼우는 것으로, Subscribe하는 이전에 스트림을 강제로 실행시킬 수 있습니다.


각각의 Observer에 대해 별도의 처리를 한다 (스트림의 분기점이 되지 않는다.)

Cold Observable은 스트림을 분기시키는 성질을 가지고 있지 않습니다.

따라서 Cold Observable을 여러 Subscribe하는 경우 각각 별도의 스트림이 생성되고 할당 될 것입니다.


그러나 스트림에 Hot Observable이 존재하는 경우 가장 말단에 가까운 Hot Observable로 스트림이 분기되어, 또 다른 별도의 스트림이 생성됩니다.


정리

스트림이 어디에서 분기 하는가? 항상 의식하고 설계하는 것이 중요합니다.

"클래스 외에 Observable을 공개 할 때는 Cold인 채로 공개하지 말고, 반드시 말단에서 Hot으로 변환한 후 공개한다"등 의도하지 않은 곳에서 스트림이 분기되어 버리지 않도록 확실하게 제어해 줄 필요가 있습니다.

또한 Cold → Hot 변환에는 적용 오퍼레이터가 준비되어 있으므로, 그 쪽을 이용하면 좋을 것 같습니다.

(Publish, PublishLast, Multicast 등)

자세히 : [Reactive Extensions] Hot 변환은 어떤 때에 필요한가?

· 약 6분
karais89

환경


  • macOS Mojave v10.14.6
  • Unity 2019.2.5f1
  • Github Desktop
  • Rider 2019.2
  • UniRx v7.1.0

원문 : https://qiita.com/toRisouP/items/83fd28b6d4a70a7ed1d2

이 포스팅은 원문을 단순히 구글 번역을 하여 정리한 내용입니다. 일본어를 잘하시는 분은 원문을 보시는게 더 좋으실 것 같습니다.

UniRx에 대한 기사 요약은 여기


Unity 개발에서 플레이어 캐릭터를 만들 때 자주 사용되는 컴포넌트에 CharacterControoler가 있습니다.

이 컴포넌트는 개체를 이동할 때 바닥과 벽의 판정, 언덕의 경사 및 계단 오르기 등의 판정을 쉽게 계산 해주는 매우 유용한 컴포넌트 입니다.

다만 이 Character Controller은 접지 판정의 IsGrounded의 정밀도가 별로 입니다.

언덕길이나 계단을 이동하는 동안 분명히 접지하고 있음에도 불구하고 false 판정이 리턴되곤 합니다.

따라서 IsGrounded의 기준으로 점프 여부를 결정하면 점프를 하지 못하는 경우가 생길 수 있습니다.

(경사면을 이동중에는 isGrounded가 true/false가 격렬하게 변한다)

그래서 이번에는 이 IsGrounded의 판정을 완만하게 하여 개선해 보려고 합니다.

방법 1: Raycast와 병행하여 판정을 정확하게 하기


IsGrounded는 엄격하게 딱 바닥에 접해 있지 않으면 true가 되지 않습니다.

그래서 이 판정을 어느 정도 바닥에 가까우면 "지면에 접하고 있다"로 판정하도록 하려고 합니다.

판정에 Physics.Raycast 라는 개체와의 충돌을 검사하는 API을 이용합니다.

(이름 그대로 점에서 가상의 광선을 쏘아 그것이 물체에 닿았는지 확인 하는 API입니다.)

이 Raycast를 GameObject의 발밑에서 발사하고 Raycast가 지상과 충돌 여부에 지면에 접하고 있는지 판정해 보겠습니다.

(개체의 바닥에서 바로 밑에 Raycast를 쏘아서 충돌 여부를 판단)

CheckGroundedWithRaycast.cs

/// <summary>
/// 땅에 접지되어 있는지 여부를 확인
/// Update에서 실행, _characterController, _fieldLayer의 경우 Start 메서드에서 캐시 처리.
/// </summary>
/// <returns></returns>
private bool IsCheckGrounded()
{
// CharacterController.IsGrounded가 true라면 Raycast를 사용하지 않고 판정 종료
if (_characterController.isGrounded) return true;
// 발사하는 광선의 초기 위치와 방향
// 약간 신체에 박혀 있는 위치로부터 발사하지 않으면 제대로 판정할 수 없을 때가 있다.
var ray = new Ray(this.transform.position + Vector3.up * 0.1f, Vector3.down);
// 탐색 거리
var maxDistance = 1.5f;
// 광선 디버그 용도
Debug.DrawRay(transform.position + Vector3.up * 0.1f, Vector3.down * maxDistance, Color.red);
// Raycast의 hit 여부로 판정
// 지상에만 충돌로 레이어를 지정
return Physics.Raycast(ray, maxDistance, _fieldLayer);
}

이와 같이 CharacterController.ISGrounded과 Raycast를 병행함으로써 경사면의 행동을 개선 한 수 있습니다. 그러나 이 CheckGround()는 실행할 때마다 매번 Raycast를 실행하게 됩니다. 실제로 사용할 때는 Raycast 결과를 프레임별로 캐시하면 더 좋지 않을까 생각합니다. (실제 캐시 처리를 할 수 있는지 의문이 듬)

방법 2: IsGrounded 값의 변동이 안정 될 때까지 IsGrounded 값을 무시


IsGrounded 값을 잘 관찰하면, "점프와 착지 직후" "경사면을 이동하는 동안" true/false 값이 수차례 변동하는 것을 알 수 있습니다. 그래서 이 값이 변동하는 것을 일정 시간 무시하고 값이 안정화 된 이후에 이용하도록 해봅시다. (마지막에 값이 변화하고 n 밀리 초 경과했을 때 그 값으로 결정합니다)

이러한 시간에 관한 판정 처리는 Rx가 적합하므로, Rx의 Unity용 구현 UniRx를 사용해 봅시다.

CheckGroundedComponent.cs

using UniRx;
using UnityEngine;

public class CheckGroundedWithRx : MonoBehaviour
{
private bool _isGrounded;

public bool IsGrounded { get => _isGrounded; }

private void Start()
{
_characterController = GetComponent<CharacterController>();
_characterController
.ObserveEveryValueChanged(x => x.isGrounded)
.ThrottleFrame(5)
.Subscribe(x => _isGrounded = x);
}
}

CharacterController.IsGrounded을 배치하고 값이 마지막으로 변화하고 5 프레임 안정 될때까지 그 값을 무시하도록 해 보았습니다.

이러한 "값의 변화를 감시한다", "시간을 사용하여 판정"등 처리는 Rx를 사용하면 정말 쉽게 작성할 수 있으므로 사용하는 것을 추천 합니다.

다만 학습 비용 (러닝커브)는 상당히 높습니다.

마지막으로


UniRx가 더 유행했으면 좋겠습니다.

· 약 8분
karais89

환경


  • macOS Mojave v10.14.6
  • Unity 2019.2.5f1
  • Github Desktop
  • Rider 2019.2
  • UniRx v7.1.0

원문 : https://qiita.com/toRisouP/items/83fd28b6d4a70a7ed1d2

이 포스팅은 원문을 단순히 구글 번역을 하여 정리한 내용입니다. 일본어를 잘하시는 분은 원문을 보시는게 더 좋으실 것 같습니다.

UniRx에 대한 기사 요약은 여기

Reactive Extensions이란?


ReactiveExtensions (Rx)는 한마디로 "이벤트 및 비동기 처리에 LINQ와 같은 기술을 적용할 수 있게 하는 C# 라이브러리"입니다. 리액티브 프로그래밍에 대한 설명은 당신이 놓치고 있던 리액티브 프로그래밍에 대한 안내를 보시면 쉽게 이해하실 수 있을 것이라고 생각합니다.

Rx는 원래 Unity에서는 작동하지 않습니다만, neuecc님이 GitHub에 공개한 UniRx는 Unity에서 Rx가 작동할 수 있게 만들었습니다. 이번 포스트에서는 UniRx를 사용한 스크립트를 소개하려고 합니다.

값을 통지하는 Subject<T> / 화면에 객체가 찍혔음을 통지하는 예제


Subject<T>를 사용하여 값을 발행하고 통지할 수 있습니다.

이것은 Event의 상위 호환 기능에 있어서, EventArgs에서 파라미터를 통지하는 것과 같이 감시하고 있는 대상에 값을 통지 할 수 있습니다.

그러면 Subject<T>를 사용해 실제로 카메라에 객체가 찍혔을 때에 통지되도록 해봅니다.

OnVisibleScript.cs

using System;
using UniRx;
using UnityEngine;

/// <summary>
/// 게임오브젝트가 카메라에 찍힌 것을 통지하는 스크립트
/// </summary>
public class OnVisibleScript : MonoBehaviour
{
/// <summary>
/// 카메라에 비친 게임오브젝트를 흐르는 스트림
/// </summary>
private Subject<GameObject> onVisibleStream = new Subject<GameObject>();

/// <summary>
/// 외부에 공개하는 Observable
/// </summary>
public IObservable<GameObject> OnVisibleObservable => onVisibleStream.AsObservable();

/// <summary>
/// 카메라에 찍힐 때 실행되는 Unity 전용 콜백
/// </summary>
private void OnBecameVisible()
{
// OnNext에서 자신의 게임오브젝트를 스트림에 흐르게 한다.
onVisibleStream.OnNext(gameObject);
}
}

ObserveScript.cs

using UniRx;
using UnityEngine;

/// <summary>
/// 대상을 감시하는 측면
/// </summary>
public class ObserveScript : MonoBehaviour
{
// 관측 대상의 GameObject
public GameObject targetCube;

private void Start()
{
// OnVisibleScript를 획득
var targetOnvisibleScript = targetCube.GetComponent<OnVisibleScript>();

// Subscribe에서 값을 구독한다.
targetOnvisibleScript.OnVisibleObservable
.Subscribe(Debug.Log);
}
}

이제 OnVisibleScript가 할당된 게임오브젝트가 카메라에 찍힌 순간에 OnVisibleObservable의 값이 흐릅니다. OnVisibleObservable을 모니터링하는 ObserveScript가 그것을 감지하고 Debug.Log가 실행됩니다.

OnNext()는 값을 통지합니다. "Event를 발화하는 것(Invoke)" 것과 같은 처리에 해당됩니다.

Subscribe()는 기존의 Event에서 "EventHandler 등록"에 해당합니다.

Event를 사용하는 경우 delegate를 정의하거나 EventArgs을 정의하는 등 복잡한 처리가 많았습니다. UniRx는 Subject를 정의하는 것만으로 매우 간결하게 쓸 수 있습니다.

이벤트를 합성 해보자 (merge) / 화면에 동시에 찍힌 숫자를 세어 보는 예제.


Subject<T>를 사용하면 이벤트 통지를 간단하게 쓸 수 있다고 했지만, 그것이 Rx의 전부는 아닙니다.

이 이벤트 통지를 합성, 필터링, 투영 등 유연한 처리를 실시 할 수 있습니다.

예를 들어, "카메라가 이동하여 화면에 객체가 찍혔을 때, 동시에 몇 개의 객체가 카메라에 찍혔는지를 계산한다"를 처리하고 싶다고 가정 합니다.

우선은 사전 준비한 OnVisibleScript를 붙인 Target Cube를 씬 상에 몇개를 배치 하고 Cubes라는 오브젝트의 자식으로 정리해 둡니다.

다음에, 조금전의 ObserveScript.cs를 다시 씁니다.

using System;
using System.Linq;
using UniRx;
using UnityEngine;

public class ObserveScript : MonoBehaviour
{
/// <summary>
/// TargetCube를 묶는 GameObject
/// UnityEditor에서 설정해두자.
/// </summary>
public GameObject cubes;

private void Start()
{
// OnVisibleScript를 획득
var onVisibleScripts = cubes.GetComponentsInChildren<OnVisibleScript>();

// Merge : 여러개의 OnVisibleObservable을 하나로 통합
var allOnVisibleObservable = Observable.Merge(onVisibleScripts.Select((x => x.OnVisibleObservable)));

// 250ms 이내에 화면에 함께 찍힌 GameObject를 계산
allOnVisibleObservable
.Buffer(allOnVisibleObservable.Throttle(TimeSpan.FromMilliseconds(250)))
.Subscribe(x => Debug.Log(x.Count));
}
}

이상입니다. 몇 줄 고쳐 쓴 것만으로 "동시에 화면에 찍힌 객체의 수를 세다"는 복잡한 처리를 할 수 있었습니다.

9개가 동시에 화면에 비치기 때문에 "9으로 표시되어 있습니다.

하나 하나 무엇을 하고 있는지 설명하겠습니다.

우선 Observable.Merge()를 사용하여 여러 스트림을 하나의 스트림인 allOnVisibleObservable로 합성하고 있습니다.

Merge

// Merge : 여러개의 OnVisibleObservable을 하나로 통합
var allOnVisibleObservable = Observable
.Merge(onVisibleScripts.Select((x => x.OnVisibleObservable)));

이어 Buffer를 사용하여 값의 통지를 막습니다.

Buffer는 이벤트를 컬렉션으로 정리하고, 해제 이벤트가 올때 까지 계속 막습니다. Buffer의 해제는 Throttle를 사용해 생성합니다.

Throttle은 마지막으로 값이 와서 일정 기간 경과하면 발화하는 것입니다.

이번에는 마지막에 값이 와서 250ms 초 이상 경과하면 Buffer를 해제 했습니다. (즉, 250ms 이내에 화면에 비친 것은 동시에 비친 것으로 계산 됩니다)

buffer

// 250ms 이내에 화면에 함께 찍힌 GameObject를 계산
allOnVisibleObservable
.Buffer(allOnVisibleObservable.Throttle(TimeSpan.FromMilliseconds(250)))
.Subscribe(x => Debug.Log(x.Count));

이 외에도 Where에서 특정 이벤트만을 필터링 하거나 Select에서 흘러 나오는 값 자체를 바꿔거나 Rx를 사용하는 것으로 LINQ와 비슷한 방식으로 이벤트를 유연하게 취급할 수 있게 됩니다.

· 약 40분
karais89

원문

Extend the Unity3d Editor

이 튜토리얼에서는 유니티 에디터를 확장하여 게임의 필요에 맞게 유니티 에디터를 커스텀 하는법을 배웁니다.

CustomizedEditorPreview2

유니티 에디터는 게임 제작을 위한 멋진 엔진이지만, 실제로는 사용자의 요구를 실제로 충족시킬 수 있습니다. 때로는 "내 뜻대로" 버튼을 원하는 경우가 있을 것입니다.

유니티 (Unity)는 신비한 버튼을 통해 뇌파를 해석 할 수는 없지만, 이 도구를 사용하면 더 유용하고 사용하기 쉽도록 확장 할 수 있습니다.

이 Unity 튜토리얼에서는 엔진을 확장하고 사용자 정의하는 방법을 배웁니다. 도메인 마스터의 성취와는 별개로 프로세스를 약간 (또는 많은) 더 희박하게 만들어 게임 개발 속도를 높이는 도미노 효과가 있어야합니다. :]

특히 다음을 사용하는 방법에 중점을 둡니다.

  • 기본 사용자 정의를 위한 특성
  • 사용자 정의 자산을 만들고 메모리 사용을 줄이기위한 ScriptableObject
  • PropertyDrawer와 Editor를 사용하여 Inspector 사용자 정의
  • 자신의 편집 도구를 제공하는 새 편집기 창 만들기
  • Gizmos가있는 장면에서 그립니다.

유니티 사용자 정의가 도움이 될지 의심 스럽습니까? 가마 수트라에서는 Unity 에디터와 에디터 툴에 대한 훌륭한 기사를 볼 수 있습니다. 이 툴은 커스텀 에디터가 게임 디자인과 제작에 어떻게 도움이 될 수 있는지 보여줍니다.

전제 조건

스타터 프로젝트를 성공적으로 로드하려면 Unity 버전 5.3 이상이 필요합니다. 가지고 있지 않다면 Unity3D.com에서 Unity를 다운로드하십시오.

이 튜토리얼은 고급 Unity 개발자를 대상으로하므로 기본 스크립팅을 알고 Unity Editor에서 편안하게 작업해야합니다. 만약 당신이 거기에 없다면 다른 Unity 튜토리얼로 지식을 연마 해보십시오.

시작하기

스타터 프로젝트를 다운로드하고 zip 파일의 압축을 풀고 Unity를 사용하여 결과로 나오는 TowerDefenseEditor 폴더를 엽니 다. 폴더 이름에서 알 수 있듯이이 프로젝트는 타워 방어 게임입니다.

참고 : 그것이 어디서 왔는지, 어떻게 만들어야 하는지 보려면 여기를 보십시오.
튜토리얼 : [유니티에서 타워 디펜스 게임을 만드는 법, Part 1.](https://www.raywenderlich.com/269-how-to-create-a-tower-defense-game-in-unity-part-1)

게임이 원활하게 실행 되더라도 편집하기가 어렵습니다. 예를 들어, 새로운 "탑"을 배치 할 목적으로 새로운 open Spot을 추가하는 과정은 약간 짜증납니다. 또한 적들이 이동하는 경로를 쉽게 볼 수 없습니다.

이 튜토리얼에서는 유니티 에디터를 확장하여 더 많은 기능을 추가하여 다음과 같이 보입니다 :

CustomizedEditorFinal

이 튜토리얼은 작은 변화에서 큰 변화로 이동합니다. 각 섹션은 독립적이므로 귀하의 관심을 끄는 Editor 확장 부분만 작업 할 수 있습니다.

특성을 사용한 기본 사용자 지정

Unity3D 편집기를 대폭 재구성 할 수 있는 힘을 제공하지만 필드 숨기기, 툴팁 표시 또는 헤드 라인 추가 등 약간의 조정 만 필요한 경우도 있습니다. 원치 않는 필드를 숨기고 다른 것을 보호하는 것은 속성에 대한 두 가지 유스 케이스입니다.

공개 필드 숨기기 - [HideInInspector]

기본값에 따라 MonoBehavior의 모든 public 필드는 Inspector에 표시되지만 때로는 모든 것을 원하지 않는 경우도 있습니다. 가끔은 다음과 같이하고 싶을 때가 있습니다.

  • Inspector에서 너무 많은 정보 제거
  • 게임을 엉망으로 만들 수있는 실수로 인한 변수 변경 방지

broken

손을 더럽힐 시간!

Assets/Prefabs 폴더로 이동하여 Bullet1을 선택한 다음 Inspector를 봅니다. 다음을 보아야 합니다.

screenshot1

Target, Start Position 및 Target Position 필드를 볼 수 있습니다. 이러한 변수는 ShootEnemies 스크립트에 의해 지정되어야하기 때문에 public입니다.

그러나 총알은 항상 몬스터에서 타겟으로 날아가므로 인스펙터에서 편집 할 필요는 없습니다. 이 필드를 노출시키지 않아도 됩니다.

Assets/Scripts 폴더에서 BulletBehavior.cs 스크립트를 엽니다. using System;을 라인 2에 삽입합니다.

Before:

using UnityEngine;
using System.Collections;

After:

using UnityEngine;
using System;
using System.Collections;

target, startPosition 및 targetPosition 필드의 정의를 포함하는 행 앞에 [HideInInspector][NonSerialized] 속성을 추가하십시오. [NonSerialized]는 System NameSpace의 일부이므로 위에서 System을 사용하여 삽입 하였습니다.

Before:

public GameObject target;
public Vector3 startPosition;
public Vector3 targetPosition;

After:

[HideInInspector]
[NonSerialized]
public GameObject target;
[HideInInspector]
[NonSerialized]
public Vector3 startPosition;
[HideInInspector]
[NonSerialized]
public Vector3 targetPosition;

[HideInInspector]는 Inspector에서 필드를 제거하고 필드를 성공적으로 숨깁니다. 그러나 사물을 숨기 전에 Inspector에서 변수 값을 편집하면 Unity가 사용자가 설정 한 값을 기억합니다. 혼란스러울 수 있습니다.

이를 방지하려면 [NonSerialized] 속성을 사용하여 Unity가 변수의 값을 기억하지 못하도록 합니다.

screenshot2-480x106

참고 : 속성 사용은 간단합니다. 
필드, 메서드 또는 클래스에 영향을 주어야하며, 여러 속성을 결합 할 수도 있습니다.

[RequireComponent] : 실수로 Component 제거 방지

OpenSpot 프리팹에서 이것을 시도해 배치가 항상 예상대로 작동하게 하십시오.

Assets/Scripts 폴더에서 PlaceMonster.cs 파일을 엽니다. 다음과 같이 코드를 편집하십시오.

Before:

using System.Collections;

public class PlaceMonster : MonoBehaviour {

After:

using System.Collections;

[RequireComponent (typeof(AudioSource))]
[RequireComponent (typeof(Collider2D))]

public class PlaceMonster : MonoBehaviour {

Inspector에서 Assets/Prefabs 폴더에있는 OpenSpot 프리팹에서 Collider2D 또는 AudioSource 구성 요소를 제거하십시오. RequireComponent 때문에 삭제하려고하면 대화 상자가 나타납니다. 제거 할 수 없다는 메시지가 표시되고 필요한 스크립트를 알려줍니다. 아주 산뜻합니다!

screenshot2-480x106

기타 흥미로운 태그들

위에서 작업 한 것들 외에도 멋진 속성이 많이 있습니다. 간략한 개요는 다음과 같습니다.

용법속성
Unity의 메뉴에 항목 추가하기MenuItem, AddComponentMenu, ContextMenu, ContextMenuItem, CreateAssetMenu
정보 표시 또는 숨기기HeaderHelpURL, Tooltip, HideInInspector
Inspector의 레이아웃 표시Multiline, Range, Space, TextArea
구성 요소 사용에 대한 제약 조건 정의RequireComponent, DisallowMultipleComponent
필드의 값을 기억할지 여부를 지정합니다.NonSerialized, Serializable
Editor가 playmode에 있지 않을 때에도 콜백 함수를 실행합니다.ExecuteInEditMode
ColorPicker 사용 구성ColorUsage

ScriptableObject : 사용자 정의 자산 만들기 및 메모리 사용 줄이기

이 섹션에서는 GameObject 내부가 아닌 ScriptableObject에 총알 구성을 저장합니다.

당신은 아마 생각할 것입니다. (GameObject로 완벽하게 작동했습니다! 왜 그것을 바꿔야합니까?) 왜 이 작업을 하면 좋을지 이해하려면 스크립팅 가능 객체의 기본 사항을 살펴보십시오.

스크립팅 가능 객체 : 기본 사항

ScriptableObject의 의도 된 사용 사례는 값의 복사본을 피함으로써 메모리 사용을 줄여야 할 때를 위한 것입니다.

어떻게 그럴 수 있죠? GameObject를 만들면 Unity는 값과 연결된 모든 기본 유형을 저장합니다.

총알에는 speed와 damage이라는 두 개의 정수 필드가 있습니다. 하나의 정수에는 2 바이트가 필요하므로 총 4 바이트가 필요합니다. 이제 총알이 100개라고 가정하십시오. 이미 최소 400 바이트가 필요합니다. speed와 damage에 변화가 없습니다. 리소스를 빠르게 소비하는 가장 좋은 방법 입니다!!!!!!

landfill-879437_640

ScriptableObject는 이러한 값을 참조로 저장하므로 다음과 같은 경우에 사용할 수 있습니다

  • 특정 유형의 객체에 대해 많은 데이터를 저장합니다.
  • 값은 많은 게임 객체간에 공유됩니다.

ScriptableObject의 주요 가치는 메모리 요구 사항을 줄이는 것이지만 대화 상자, 레벨 데이터, 타일 세트 등에 대한 사용자 정의 자산을 정의하는 데 사용할 수도 있습니다. 이렇게하면 데이터와 GameObject를 분리 할 수 있습니다.

ScriptableObject 구현하기

이 하위 섹션에서는 스크립트 가능한 첫 번째 객체를 만듭니다. Unity 내부와 프로젝트 브라우저에서 Assets/Scripts 폴더를 열고 마우스 오른쪽 버튼으로 클릭 한 다음 Create>C# Script를 선택하십시오. BulletConfig 스크립트의 이름을 지정하고 열고 연 다음과 같이 내용을 변경하십시오.

using UnityEngine;

[CreateAssetMenu(menuName = "Bullet", fileName = "Bullet.asset")]
public class BulletConfig : ScriptableObject {
public float speed;
public int damage;
}

그리고 저장해라.

CreateAssetMenu를 사용하면 Assets 메인 메뉴 옵션에서 ScriptableObject를 만들 수 있습니다.

Unity로 다시 전환하고 프로젝트 탐색기로 이동하여 Assets/Bullets 폴더로 이동하십시오. 기본 메뉴에서 Assets/Create/Bullet을 클릭하십시오. 생성 된 애셋의 이름을 Bullet1 - 이제는 멋진 ScriptableObject를 가집니다.

create-scriptable-object-380x500

bullet-asset

Bullet2 및 Bullet3이라는 두 개의 에셋을 추가로 만듭니다. 이제 Bullet1을 클릭하고 Inspector로 이동하십시오. speed를 10으로 설정하고 damage 10으로 설정합니다.

Bullet2 및 Bullet3에 대해 다음 값을 사용하여 반복합니다.

  • Bullet2: speed = 10, damage = 15
  • Bullet3: speed = 10, damage = 20

이제 BulletBehavior의 speed 및 damage 필드를 BulletConfig로 대체합니다. BulletBehavior를 엽니다. speed와 damage 필드를 제거하고 다음 필드를 추가하십시오.

before:

public float speed = 10;
public int damage;

After:

public BulletConfig bulletConfig;

다른 모든 스크립트는 여전히 speed와 damage에 대한 읽기 액세스 권한이 필요 하므로 두 변수에 대한 getter를 만들어야합니다.

클래스의 닫는 중괄호 앞에 다음을 추가하십시오.

private int damage {
get {
return bulletConfig.damage;
}
}

private float speed {
get {
return bulletConfig.speed;
}
}

변경 사항을 저장하십시오.

다른 스크립트를 건드리지 않고도 글 머리 기호 내부의 변수를 스크립팅 가능 객체로 대체하여 메모리 사용을 향상 시켰습니다.

마지막으로 스크립트 가능한 객체를 총알 프리 팹에 할당 해야합니다.

프로젝트 탐색기에서 Prefabs/Bullet1을 선택합니다. Inspector에서 Bullet Config를 Bullet1.asset으로 설정합니다. 그에 따라 Bullet2 및 Bullet3에 대해 이를 반복합니다. 이것이 끝입니다!

addconfigs

게임을 실행하여 모든 것이 여전히 작동하는지 확인하십시오.

PropertyDrawer 및 편집기로 인스펙터 맞춤 설정

지금까지는 사소한 일만 변경했지만 Inspector에서는 거의 모든 것을 다시 그릴 수 있었습니다. 이 섹션은 다시 그리는 근육을 구부리기위한 것입니다.

Editor

Editor를 사용하면 MonoBehavior에 대한 Inspector GUI를 대체 할 수 있습니다. 구현은 슬라이더를 사용하여 각 적의 상태를 표시하는 것입니다. 슬라이더의 길이는 적의 최대 건강 상태에 따라 다르므로 유효하지 않은 값을 입력 할 수 없습니다.

MoveEnemy-before-after

Assets/Editor 폴더에서 EnemyDataEditor라는 새 C# 스크립트를 만듭니다. MonoDevelop에서 스크립트를 엽니다. 내용을 다음으로 교체하십시오.

using UnityEditor;

// 1
[CustomEditor(typeof(MoveEnemy))]
// 2
public class EnemyDataEditor : Editor {
// 3
public override void OnInspectorGUI() {
// 4
MoveEnemy moveEnemy = (MoveEnemy)target;
HealthBar healthBar = moveEnemy.gameObject.GetComponentInChildren<HealthBar> ();
// 5
healthBar.maxHealth = EditorGUILayout.FloatField ("Max Health", healthBar.maxHealth);
// 6
healthBar.currentHealth = EditorGUILayout.Slider("Current Health", healthBar.currentHealth, 0, healthBar.maxHealth);
// 7
DrawDefaultInspector ();
}
}

변경 사항을 저장하십시오.

이것이 각 단계의 작동 방식입니다.

  1. 이 속성에서 Unity가 MoveEnemy 용 CustomEditor를 생성하도록 지정하십시오.
  2. EnemyDataEditor를 OnInspectorGUI와 같은 메서드에 액세스 할 수 있도록 Editor의 하위 클래스로 만듭니다.
  3. 편집기를 그릴 때 호출되는 OnInSpectorGUI 메서드를 추가하십시오.
  4. 편집기에서 대상을 통해 프로젝트 탐색기 또는 계층 구조에서 현재 선택된 인스턴스에 액세스 할 수 있습니다. 그런 다음 MoveEnemy로 캐스팅합니다. 마지막으로, 당신은 유일한 자식 인 HealthBar를 얻습니다.
  5. EditorGUILayout.FloatField를 사용하여 float 값만 허용하는 maxHealth 필드를 추가하십시오.
  6. EditorGUILayout.Slider를 사용하여 실제 현재 상태에 대한 슬라이더를 만듭니다. 변수에 따라 슬라이더의 범위를 조정할 수 있기 때문에 [Range] 속성을 사용하는 것과는 다릅니다. 이 경우 healthBar.maxHealth입니다.
  7. 사용자 정의 편집기는 보통 Inspector에 나타나는 모든 것을 바꿉니다. 기존보기 만 확장하려는 경우 DrawDefaultInspector를 호출합니다. 통화를 추가 한 위치에 나머지 기본 콘텐츠를 그립니다.

Unity로 다시 전환하고 프로젝트 브라우저에서 Prefabs/Enemy를 선택하고 결과를 즐깁니다.

MoveEnemy

게임을 실행하여 모든 것이 여전히 작동하는지 확인하십시오. 게임이 진행되는 동안 Hierarchy에서 Enemy (Clone)를 선택하고 Inspector에서 슬라이더의 상태를 변경할 수 있어야합니다. 부정 행위는 결코 그렇게 재미 있지 않았습니다. :]

blue-1005940_640

Property Drawers

Property Drawers을 사용하면 다음을 수행할 수 있습니다.

  • Wave와 같은 Serializable 클래스의 모든 인스턴스에 대해 GUI를 사용자 정의
  • Range와 같은 사용자 정의 PropertyAttributes가있는 필드의 GUI를 사용자 정의

Hierarchy 에서 Road를 선택하십시오. Inspector에서 모든 Waves를 확인하십시오.

Enemy Prefab 외에도 해당 프리팹의 미리보기 이미지를 표시하려고합니다. 웨이브용 PropertyDrawer로이 작업을 수행 할 수 있습니다.

drawerbeforeandafter-650x381.jpg

먼저 프로젝트 브라우저의 Assets/Editor 폴더에 WavePropertyDrawer라는 새 C# 스크립트를 만듭니다. MonoDevelop에서 열고 다음과 같이 전체 내용을 바꿉니다.

//1
using UnityEditor;
using UnityEngine;

//2
[CustomPropertyDrawer(typeof(Wave))]
//3
public class WavePropertyDrawer : PropertyDrawer {
//4
private const int spriteHeight = 50;
}

변경 사항을 저장하십시오.

이 코드 블록에서 다음을 수행합니다.

  1. UnityEditor를 임포트하여 에디터의 라이브러리에 액세스 할 수 있게 하십시오.
  2. Attribute을 사용하여 클래스에 Wave용 CustomPropertyDrawer를 알립니다.
  3. PropertyDrawer의 서브 클래스로 만듭니다.
  4. 적의 스프라이트가 그려지는 높이를 저장하기위한 상수를 생성하십시오.

Unity로 돌아가서 Hierarchy에서 Road를 선택하십시오. Inspector에 No GUI implemented 메시지가 표시됩니다.

noguiimplementednew-480x297.jpg

이것을 변경하고 WavePropertyDrawer에 OnGUI를 추가하십시오 :

public override void OnGUI (Rect position, SerializedProperty property, GUIContent label) {
EditorGUI.PropertyField(position, property, label, true);
}

OnGUI를 재정의하면 고유 한 GUI를 구현할 수 있습니다. 이 메서드는 세 가지 인수를 취합니다.

  • position : GUI를 그리는 직사각형
  • property : 그릴 속성
  • label : 속성에 레이블을 붙이는 텍스트

EditorGUI.PropertyField 메서드를 사용하여 속성의 기본 GUI를 그립니다. OnGUI에서 가져 오는 모든 변수가 필요합니다.

마지막 매개 변수는 Unity에게 속성 자체와 그 자식을 그리도록하는 bool 변수입니다.

OnGUI 외에도 GetPropertyHeight()를 구현해야합니다.

public override float GetPropertyHeight(SerializedProperty property, GUIContent label) {
return EditorGUI.GetPropertyHeight (property);
}

이 메서드는 속성의 높이를 반환하며 속성이 확장되는지 여부에 따라 높이를 변경할 수 있는 속성에 특히 중요합니다.

당신이 그것을 구현하지 않으면 Unity는 기본 높이를 가정합니다. 귀하의 재산이 더 높은 경우, Inspector의 다른 필드 위에 그려집니다. 이것은 보통 당신이 원하는 것이 아닙니다. :]

Keep-calm-and-implement-GetPropertyHeight.png

이제 기초가 마련되었으므로 GUI를 자유롭게 사용자 정의 할 수 있습니다. OnGUI ()에 다음 코드를 추가합니다.

// 1
if (property.isExpanded) {
// 2
SerializedProperty enemyPrefabProperty = property.FindPropertyRelative ("enemyPrefab");
GameObject enemyPrefab = (GameObject) enemyPrefabProperty.objectReferenceValue;
// 3
if (enemyPrefab != null) {
SpriteRenderer enemySprite = enemyPrefab.GetComponentInChildren<SpriteRenderer> ();
// 4
int previousIndentLevel = EditorGUI.indentLevel;
EditorGUI.indentLevel = 2;
// 5
Rect indentedRect = EditorGUI.IndentedRect (position);
float fieldHeight = base.GetPropertyHeight (property, label) + 2;
Vector3 enemySize = enemySprite.bounds.size;
Rect texturePosition = new Rect (indentedRect.x, indentedRect.y + fieldHeight * 4, enemySize.x / enemySize.y * spriteHeight, spriteHeight);
// 6
EditorGUI.DropShadowLabel(texturePosition, new GUIContent(enemySprite.sprite.texture));
// 7
EditorGUI.indentLevel = previousIndentLevel;
}
}

변경 사항을 저장하십시오.

단계별 분석 :

  1. 예를 들어 사용자가 Inspector에서 전개 할 때 속성의 세부 정보가 표시되어야 할 때만 적 스프라이트를 그립니다. 이 경우 property.isExpanded는 true입니다.
  2. enemyPrefab을 얻는다. FindPropertyRelative를 먼저 호출하여 이름별로 다른 속성에 연결된 속성을 검색합니다. 그런 다음 enemyPrefab가 포함 된 실제 GameObject를 저장하는 objectReference를 가져옵니다.
  3. enemyPrefab이 설정되면 Sprite를 얻습니다.
  4. indentLevel을 올바르게 설정하십시오. 이 레벨은 Unity가 그리기를 시작하는 정도까지 지정합니다. 그리기가 끝나면 원래 값을 복원해야하므로 변수에 먼저 저장 한 다음 indentLevel을 2로 설정합니다.
  5. indentedRect의 위치를 가져 와서 속성 서랍 안쪽에 그리는 데 필요한 좌표를 계산할 수 있습니다. 그런 다음 GetPropertyHeight를 사용하여 하나의 정상 필드 높이를 얻습니다. 적의 스프라이트 크기도 필요합니다. 이 세 가지 값을 사용하여 텍스처를 그리는 rect를 계산할 수 있습니다. 원점은 indentedRect의 원점입니다. 그러나 fieldHeight의 4 배를 추가하여 그려진 필드의 공간을 예약하십시오. 높이가 같아야하므로 적의 실제 크기와 관계없이 너비를 적절하게 조정합니다.
  6. 지정된 위치에 텍스처를 그리려면 DropShadowLabel을 호출하십시오.
  7. 결국 들여 쓰기 수준을 다시 설정합니다.

unity으로 돌아가 적의 스프라이트이 올바른 위치에 있는지 확인하십시오. 거기에 있지만 다음 속성과 겹칩니다. 왜?

원래 프로퍼티의 높이와 스프라이트의 높이를 반환하도록 GetPropertyHeight ()의 구현을 변경해야합니다.

public override float GetPropertyHeight(SerializedProperty property, GUIContent label) {
SerializedProperty enemyPrefabProperty = property.FindPropertyRelative ("enemyPrefab");
GameObject enemyPrefab = (GameObject)enemyPrefabProperty.objectReferenceValue;
if (property.isExpanded && enemyPrefab != null) {
return EditorGUI.GetPropertyHeight (property) + spriteHeight;
} else {
return EditorGUI.GetPropertyHeight (property);
}
}

변경 사항을 저장하십시오! GetPropertyHeight에서 적의 프리팹을 검색합니다. 속성이 확장되어 enemyPrefab이 null이 아닌 경우 spriteHeight를 추가합니다. 그렇지 않으면 정상적인 속성 높이 만 반환합니다.

Unity로 돌아가 Hierarchy에서 Road를 선택하십시오. Inspector에서 이제 적의 스프라이트를 올바르게 볼 수 있습니다.

하나 이상의 스프라이트를 추가하는 대신 Inspector를 더 크게 변경할 수 있습니다. 한 가지 예가 PropertyDrawer 설명서에서 직접 가져온 것입니다.

CustomPropertyDrawer_Class

Property Drawers을 사용하면 Serializable 클래스의 속성에 대해 Inspector가 그려지는 방식을 사용자 정의 할 수 있습니다. Inspector의 나머지 부분을 변경하지 않고 MonoBehavior의 특정 속성을 가져 오는 방법을 지정할 수 있습니다.

Property Drawers은 배열의 항목을 다시 그리려면 특히 유용합니다.

그러나 몇 가지 심각한 단점이 있습니다. 즉, EditorGUILayout()에 대한 액세스 권한이 없으므로 각 필드의 위치를 직접 계산을 해야 합니다. 또한 속성 또는 해당 하위에 저장된 정보에만 액세스 할 수 있습니다.

나만의 편집 도구를 제공하는 새 편집기 창 만들기

Unity에는 Animator, Inspector 및 Hierarchy와 같은 멋진 편집기 창이 있습니다. 그리고 물론, 당신도 당신 자신을 만들 수 있습니다.

이 섹션에서는 클릭 한 번으로 OpenSpot을 배치하고 삭제할 수있는 편집기보기를 구현합니다.

LevelEditorWindow

프로젝트 탐색기에서 Assets/Editor로 이동 한 다음 LevelEditorWindow라는 새 C# 스크립트를 만듭니다. MonoDevelop에서 엽니다.

using UnityEngine;
//1
using UnityEditor;

//2
public class LevelEditorWindow : EditorWindow {
//3
int selectedAction = 0;
}
  1. UnityEditor 임포트
  2. LevelEditorWindow를 EditorWindow의 하위 클래스로 만들어 EditorWindow에서 사용할 수있는 메서드 및 필드에 액세스 할 수 있도록 합니다.
  3. 사용자가 현재 selectedAction 변수를 사용하여 선택한 작업을 추적합니다.

지금 당장은, 당신의 삶이 그것에 달려있다 할지라도 유니티 안에서 이 레벨 편집기를 열 수 없으니, 다음과 같은 방법으로 변경하십시오 :

[MenuItem ("Window/Level Editor")]
static void Init () {
LevelEditorWindow window = (LevelEditorWindow)EditorWindow.GetWindow (typeof (LevelEditorWindow));
window.Show();
}

첫 번째 줄에서는 Unity에게 Window 메뉴 아래에 Level Editor라는 MenuItem을 만들고, Init를 실행하기 위해 그것을 클릭하기만 하면된다.

내부에 GetWindow를 사용하면 기존 열린 창을 가져 오거나 존재하지 않는 창을 새로 만들 수 있습니다. 그런 다음 Show와 함께 표시합니다.

변경 사항을 저장하고 Unity로 돌아갑니다. 경고가 표시됩니다. 괜찮습니다. 잠시 후에 고칠 것입니다. 메인 메뉴 인 Window/Level Editor를 클릭하면 빈 편집기보기가 나타납니다.

empty-level-editor-window-copy

이제 빈 뷰에 내용을 채우십시오. MonoDevelop로 다시 전환하고 LevelEditorWindow에 다음 코드를 추가합니다.

void OnGUI() {
float width = position.width - 5;
float height = 30;
string[] actionLabels = new string[] {"X", "Create Spot", "Delete Spot"};
selectedAction = GUILayout.SelectionGrid (selectedAction, actionLabels, 3, GUILayout.Width (width), GUILayout.Height (height));
}

먼저 너비와 높이를 얻습니다. 너비에 대해서는 약간의 공간을 남기기 위해 5를 공제합니다. 다음으로 각 동작에 대한 레이블이있는 문자열 배열을 만듭니다. "X"는 아무런 작업도하지 않습니다. 이렇게하면 열린 지점을 만들거나 삭제하는 대신 GameObject를 선택할 수 있습니다.

이 배열에서 SelectionGrid를 만들면 한 번에 하나만 선택할 수 있는 버튼 격자를 만듭니다. SelectionGrid의 반환 값은 현재 선택된 버튼의 인덱스이므로 selectedAction에 저장합니다.

변경 사항을 저장하고 Unity에서 작성한 내용을 살펴보십시오.

LevelEditorWindow-1

장면의 선택된 버튼에 의존하는 클릭에 대해 다른 반응을 나타내려고 하므로 다음 메소드를 추가하십시오.

void OnScene(SceneView sceneview) {
Event e = Event.current;
if (e.type == EventType.MouseUp) {
Ray r = Camera.current.ScreenPointToRay (new Vector3 (e.mousePosition.x, Camera.current.pixelHeight -e.mousePosition.y));
if (selectedAction == 1) {
// TODO: Create OpenSpot
Debug.Log("Create OpenSpot");
} else if (selectedAction == 2) {
// TODO: Delete OpenSpot
Debug.Log("Delete OpenSpot");
}
}
}

먼저 현재 이벤트를 변수에 저장합니다. 유형이 MouseUp과 같으면 마우스 클릭에 반응합니다.

이를 위해 어떤 GameObject가 선택되었고 Raycasting을 사용하고 있는지 알아야합니다. 그러나 먼저 장면을 가리키는 광선을 만들어야합니다.

광선을 만들려면 Helper 메서드 인 ScreenPointToRay()를 사용합니다. 이 메서드는 이벤트에 저장된 마우스 위치를 호출합니다. 카메라와 이벤트 좌표계 사이에서 y 좌표가 역전되므로 현재 뷰의 높이에서 원래의 y 좌표를 차감하여 변환합니다.

마지막으로, 당신은 SelectedAction과 짧은 메시지로 나누었습니다.

메소드가 실제로 다음과 같이 호출되는지 확인하십시오.

void OnEnable() {
SceneView.onSceneGUIDelegate -= OnScene;
SceneView.onSceneGUIDelegate += OnScene;
}

OnScene을 대리자로 추가합니다.

변경 사항을 저장하고 Unity로 다시 전환하십시오. LevelEditorWindow 안의 Create Spot 또는 Delete Spot 버튼을 선택한 다음 Scene 내부를 클릭하십시오. 로그 메시지가 콘솔에 나타납니다.

Log-on-click-700x97.png

Hierarchy에서 Background를 선택하십시오. Inspector에서 레이어를 배경으로 설정합니다. 그런 다음 Physics 2D / Box Collider 2D 구성 요소를 추가하십시오.

background

Prefabs/Openspot의 레이어를 Spots으로 설정하십시오. 이제 광선이 배경 또는 열린 지점과 충돌 한 곳을 확인할 수 있습니다.

openspot

TODO: Create OpenSpot을 다음 코드로 대체하십시오.

RaycastHit2D hitInfo2D = Physics2D.GetRayIntersection (r, Mathf.Infinity, 1 << LayerMask.NameToLayer ("Background"));
if (hitInfo2D) {
GameObject spotPrefab = AssetDatabase.LoadAssetAtPath<GameObject> ("Assets/Prefabs/Openspot.prefab");
GameObject spot = (GameObject)PrefabUtility.InstantiatePrefab (spotPrefab);
spot.transform.position = hitInfo2D.point;
Undo.RegisterCreatedObjectUndo (spot, "Create spot");
}

여기 당신이하는 일이 있습니다 :

ray가 배경 이미지와 교차하는지 여부를 확인하여 시작합니다. 그렇다면 hitInfo2D가 null이 아니면 이 위치에 새 OpenSpot을 만듭니다.

LoadAssetPath로 프리팹을 얻습니다. 이 인스턴스에서 PrefabUltility.InstantiatePrefab을 사용하여이 프리 팹의 새 인스턴스를 만듭니다.

InstantiatePrefab()은 프리팹 (prefab) 연결을 생성합니다. 이 경우 프리 팹 연결은 이 경우 더 적절하게 만들고 인스턴스화합니다. 그런 다음 지점의 위치를 충돌 지점으로 설정합니다.

Unity는 항상 작업을 실행 취소 할 수 있으므로 사용자 정의 편집기 스크립트를 만들 때 기억하십시오. 사용자 지정 작업으로 실행 취소를 허용하려면 Undo.RegisterCreatedObjectUndo를 사용하여 GameObject를 만드는 데 사용 된 동작을 등록하기만 하면 됩니다.

이제 지형지물 삭제를 처리 할 수 있습니다. /// TODO: Delete OpenSpot with this:

RaycastHit2D hitInfo2D = Physics2D.GetRayIntersection (r, Mathf.Infinity, 1 << LayerMask.NameToLayer ("Spots"));
if (hitInfo2D) {
GameObject selectedOpenSpot = hitInfo2D.collider.gameObject;
Undo.DestroyObjectImmediate (selectedOpenSpot);
}

이렇게하면 광선과 Spots 레이어 안의 모든 오브젝트 사이의 충돌이 감지됩니다. 그렇다면, 맞았던 GameObject를 얻습니다. 그런 다음 Undo.DestroyObjectImmediate (Undo.DestroyObjectImmediate)로 삭제하면 자리를 제거하고 실행 취소 관리자에 작업을 등록합니다.

UsingTheEditor2

Gizmos로 Scene에 그림 그리기

Gizmos를 사용하면 Scene View로 그리면 시각적인 디버깅 또는 편집 보조 도구로 사용할 정보를 제공 할 수 있습니다.

예를 들어 게임에는 적을 따라갈 수있는 경유지가 포함되어 있지만 게임 중에나 편집 중에는 보이지 않습니다. 진지한 조사 작업을 하지 않고도 어떤 길을 따라갈 지 알 수 없습니다. 다른 레벨을 만들고 싶다면 여전히 경로를 볼 수 없습니다.

Road-1

다행스럽게도 Gizmos를 사용하면이 보이지 않는 경로를 볼 수 있습니다.

모든 웨이 포인트를 저장하는 Assets/Scripts/SpawnEnemy를 열고 다음 메소드를 추가합니다.

// 1
void OnDrawGizmos() {
// 2
Gizmos.color = Color.green;
// 3
for (int i = 0; i < waypoints.Length - 1; i++) {
// 4
Vector3 beginPosition = waypoints[i].transform.position;
Vector3 endPosition = waypoints[i+1].transform.position;
// 5
Gizmos.DrawLine(beginPosition, endPosition);
// 6
UnityEditor.Handles.Label (beginPosition, i + "");
}
// 7
Vector3 lastWaypointPosition = waypoints[waypoints.Length - 1].transform.position;
UnityEditor.Handles.Label (lastWaypointPosition, (waypoints.Length - 1) + "");
}
  1. 기즈모 그리기는 OnDrawGizmos() 또는 OnDrawGizmosSelected()에서만 사용할 수 있습니다. 경로를 항상 그려야하므로 OnDrawGizmos ()를 구현합니다.
  2. 기즈모의 그림에 대한 색상을 설정합니다. 모든 그리기 호출은 변경할 때까지 이 색상을 사용합니다.
  3. 라인을 그릴 후계자가 없는 마지막 웨이 포인트를 제외한 모든 웨이 포인트를 반복하므로 웨이 포인트 길이-1일때 멈춥니다.
  4. 웨이 포인트 i의 위치와 그 후속 i + 1을 저장합니다. 모든 반복과 함께 변수로.
  5. 정적 메서드 Gizmos.Drawline (Vector3, Vector3)을 사용하면 현재 웨이 포인트에서 다음 웨이 포인트까지 선을 그립니다.
  6. UnityEditor.Handles.Label (string)을 사용하면 경로를 표시하고 적들이 이동하는 방향을 시각화하기 위해 인접한 웨이 포인트 인덱스를 작성합니다.
  7. 마지막 웨이 포인트의 위치를 얻고 그 옆에 인덱스를 표시합니다.

경로를 보려면 Unity로 다시 전환하십시오. 경유지를 따라 이동하면 경로가 따라옵니다.

DrawLine () 외에도 Gizmos는 여러 그리기 기능을 제공합니다. 전체 목록은 설명서에서 찾을 수 있습니다. 심지어 DrawRay가 있습니다.

이제 앞으로 뭘 하면 될까요??

이 튜토리얼에서는 Unity Editor의 일부를 직접 만드는 방법을 배웠습니다. 최종 결과를 보려면 여기에서 완료된 프로젝트를 다운로드하십시오. 아마도 짐작할 수 있듯이 Unity를 확장하는 더 많은 방법이 있습니다. 다음과 같은 몇 가지 추가 지침이 있습니다.

이제 당신은 맨 아래에 도달 했으므로 자신 만의 목적과 프로젝트를 위해 Unity를 커스터마이징하는 방법에 대한 지식을 시작해야합니다. 포럼에 참여하여 아이디어와 예제를 둘러보고 Unity를 사용자 정의하는 방법에 대해 질문하십시오. 나는 네가 생각해내는 것을보기를 고대하고있다.

· 약 3분
karais89

유니티에서 버전 관리 컨트롤1 사용하기 버전 관리 컨트롤은 git을 사용한다.

환경

  • Windows 10
  • Unity 2018.3.0f2
  • Git
  • Source tree

설정 순서

  1. ".gitignore" 파일 설정
  2. Edit - Project Settings - Editor설정

2가지 설정만 하면 모든 설정이 완료 된다.

1. ".gitignore" 파일 설정

.gitignore 설정 방법

gitignore folder

gitignore.io 사이트에서 unity를 타이핑 한 후 create 버튼을 누르면 아래와 같은 텍스트가 만들어진다.

# Created by https://www.gitignore.io/api/unity
# Edit at https://www.gitignore.io/?templates=unity

### Unity ###
[Ll]ibrary/
[Tt]emp/
[Oo]bj/
[Bb]uild/
[Bb]uilds/
Assets/AssetStoreTools*

# Visual Studio cache directory
.vs/

# Autogenerated VS/MD/Consulo solution and project files
ExportedObj/
.consulo/
*.csproj
*.unityproj
*.sln
*.suo
*.tmp
*.user
*.userprefs
*.pidb
*.booproj
*.svd
*.pdb
*.opendb
*.VC.db

# Unity3D generated meta files
*.pidb.meta
*.pdb.meta

# Unity3D Generated File On Crash Reports
sysinfo.txt

# Builds
*.apk
*.unitypackage

# Crashlytics generated file
Assets/StreamingAssets/crashlytics-build.properties


# End of https://www.gitignore.io/api/unity

위 텍스트를 복사하여 .gitignore 파일을 생성한다. (visual studio code 같은 텍스트 편집기를 사용해서 생성한다.)

.gitignore 파일을 Assets 폴더와 같은 폴더에 위치 시킨다.

gitignore folder

2. Edit - Project Settings - Editor설정

  • Version Control Mode를 Visible Meta Files 변경
  • Asset Serialization Mode를 Force Text로 변경

gitignore folder

참고 사항

  • 예전 버전(2018 이전)에서는 위와 같이 설정하고 바로 커밋을 하는 경우 제대로 Scene 파일이 존재하지 않아 문제가 생겼었다. (제대로 프로젝트가 불려오지 않는 현상) 그래서 더미 Scene 파일까지 만들어서 커밋을 하였다. 2018 버전에서는 자동으로 Sample Scene이 만들어지기 때문에 그런 문제는 없을 것 같다.
  • Edit - Project Settings - Editor 설정 화면이 바뀌어서 조금 해맸다. 처음에 설정 할 수 있는 셀렉트 박스가 안보였는데. Project Settings 세팅 창을 최대한 늘리면 보이게 된다.

  1. 버전 관리 시스템은 파일의 변화를 시간에 따라 기록하여 과거 특정 시점의 버전을 다시 불러올 수 있는 시스템이다.

· 약 4분
karais89

유니티 IDE 선택에 대한 잡소리 입니다.

윈도우 환경에서는 두말 할 것 없이 visual stduio 쓰시면 됩니다. 모두가 인정하는 최고의 ide 입니다.

맥에서의 유니티 IDE 선택에 대한 이야기 입니다.

사실 초창기에는 어쩔 수 없이 mono IDE를 쓸 수 밖에 없었습니다.

일단 기본적으로 mono의 경우 맥에서는 한글 입력이 되지 않는 현상이 존재하며, 초창기 mono의 경우 화면 분할 또한 가능하지 않았습니다. 이러한 문제들 때문에 서브라임 텍스트를 사용한다던가 윈도우 환경을 가상 머신으로 돌려 비쥬얼 스튜디오를 사용하시는 분들도 계셨습니다.

사실은 제가 가장 추천해주고 싶은 ide는 visual stduio code 입니다. (ide라기 보다는 텍스트 에디터이지만..)

일단 가볍고, 자동 완성 기능 또한 준수하고, 디버깅 기능또한 제공되기 때문에 사실 제가 볼때는 가장 괜찮은 선택일 것 같습니다.

사실 맥에서 나온 visual stduio의 경우 자마린 기반이라. 제가 처음에 생각했던 멋진 모습의 ide가 아니라 실망을 많이 했던 기억이 있습니다.

그이외에 추천하는 툴은 젯브레인사의 Rider입니다.

일단 기본적으로 젯브레인사의 모든 ide는 유료 입니다.

트라이얼 버전이나 학생의 경우는 무료로 사용할 수 있으니 그 기간동안 관련 ide를 사용해 보시고 괜찮으시면 사용해 보는 것도 좋은 선택 인 것 같습니다.

가장 큰 장점은 제가 보기엔 크로스 플랫폼 지원인 것 같습니다.

윈도우와 맥에서 같은 환경에서 같은 ide로 작업하는 것 자체가 큰 메리트로 다가 왔습니다.

그리고 젯브레인사의 제품답게 리팩토링 관련 기능들이 강력한 것 같습니다.

또한 유저의 편의성을 생각하는 ide의 기능들이 눈에 띄었습니다.

예를들어 유니티 자체 함수의 경우 옆에 유니티 아이콘으로 표시해 주는 점.

또한 제일 신기했던 부분은 인보크나 스타트 코루틴에 스트링으로 함수를 호출해주는 부분이 있는데 그 부분 조차 리팩토링을 지원해 줘서 이름을 바꿀 수 있다는 점입니다.

어쨌든 저도 무료 기간동안은 계속해서 사용할 예정이고, 만족하면서 사용 중입니다.

한번 쯤 맥 환경에서 ide를 고민하시는 분들은 사용해 보시는 것을 추천 드립니다.

참조

· 약 11분
karais89

김포프님 C# 코딩 스탠다드

포프님이 올리신 C# 코딩 스탠다드의 번역 글입니다.

번역 자체는 구글 번역에서 이상한 부분은 제가 수정 하였습니다. 번역 자체의 문제가 있을 수 있습니다.

코딩 스탠다드는 코드 몽키 레벨에서는 왜 이렇게 작성했는지에 대한 설명을 할 필요도 없고 그냥 코딩 스탠다드에 주어진 내용 대로 코드를 짜면 된다고 합니다.

하지만 아래 45가지의 코딩 스탠다드에는 분명히 그렇게 해야 되는 이유가 존재 합니다.

꼭 아래의 코딩 스탠다드를 사용하여 코드를 작성하실 필요는 없습니다.

경험에 바탕을 둔 방법

  1. 읽기 좋은 코드가 우선(대부분의 경우 사용 설명서가 항상 문서화되어야 함)
  2. 그렇지 않은 이유가 있는 경우가 아니라면 IDE의 자동 형식 지정 방식을 따르십시오. (VisualStudio의 Ctrl+K+D)
  3. 기존 코드로부터 배우기

참조

이 코딩 스탠다드는 이러한 코딩 표준에 의해 영감을 받는다.

이름 지정 규칙 및 스타일

1. 클래스 및 구조체에 파스칼 케이스1 사용

class SomeClass;    
struct SomeStruct;

2. 지역 변수 이름과 함수 매개 변수에 카멜 케이스2 사용

void SomeMethod(int someParameter)
{
int someNumber;
}

3. 모든 메소드 이름은 파스칼 케이스 사용

public uint GetAge()
{
// function implementation...
}

4. 메소드 이름은 동사 + 명사 사용

public uint GetAge()
{
// function implementation...
}

5. non-public 메서드면 post fix로 "Internal"을 붙인다

Private uint GetAgeInternal()
{
// function implementation...
}

6. 상수는 ALL_CAPS_SEPARATED_BY_UNDERSCORE를 사용한다.

const int SOME_CONSTANT = 1;

7. namespaces는 파스칼 케이스를 사용한다.

namespace System.Graphics

8. boolean 변수 앞에는 b를 붙이고 프로퍼티에는 앞에 is를 붙인다.

bool bFired;    // for local variable
Private bool mbFired; // for private class member variable
Public bool IsFired { get; private set; }

9. 인터페이스 앞에는 I를 붙인다.

interface ISomeInterface 
{

}

10. enum 앞에는 E를 붙인다.

public enum EDirection
{
North,
South
}

11. private 멤버 변수 앞에 m을 붙입니다. 나머지 멤버 변수에는 파스칼 케이스를 사용하십시오.

Public class Employee
{
public int DepartmentID { get; set; }
private int mAge;
}

12. 반환 값이있는 메소드는 반환 된 값을 설명하는 이름을 가져야합니다.

public uint GetAge();

13. 설명적인 변수 이름을 사용하십시오. 예를 들어 루프에 사용되는 사소한 변수가 아니라면 i 또는 e 대신 index이나 employee이 될 수 있습니다.

14. 두개의 문자만 있는 경우 머리 글의 모든 문자를 대문자로 구분합니다.

public int ID { get; private set; }

15. 두개의 문자 이상인 경우 머리 글자로 된 첫 번째 문자 만 대문자로 표시하십시오.

public string HttpAddress { get; private set; }

16. getter setter 함수보다 프로퍼티를 선호해라.

이거 대신에:
public class Employee
{
private string mName;
public string GetName();
public string SetName(string name);
}

이렇게 사용해라:
public class Employee
{
public string Name {get; set;}
}

17. 4 개의 스페이스와 동일하게 탭을 사용해라

18. 로컬 변수가 사용되는 첫 번째 행에 최대한 가깝게 선언하십시오.

19. 새로운 줄에 항상 여는 중괄호 "{" 를 넣으십시오.

20. scope에 단 하나의 줄이 있어도 중괄호를 추가하십시오.

if (bSomething)
{
return;
}

21. 부동 소수점 값에 대한 정밀도 지정은 명시 적으로 double이 필요하지 않는 한 사용하십시오.

float f = 0.5F;

22. switch 문에는 항상 default가 있어야 합니다.

switch (number)
{
case 0:
...
break;
default:
break;
}

23. case 문에 코드가없는 경우를 제외하고는 switch case에 대한 fallthrough 설명을 추가하십시오.

switch (number)
{
 case 0:
   DoSomething();
   //fallthrough
 case 1:
   DoFallthrough();
   break;
 case 2:
 case 3:
   DoNotFallthrough();
break;
 default:
   break;
}

24. 스위치 문에서 default 문이 발생해야되지 않아야 되는 경우에는 항상 Assert(false)를 더해라. assert 구현에서, 이것은 릴리즈 빌드를위한 최적화 힌트를 추가 해라.

switch (type)
{
case 1:
...
break;
Default:
Debug.Fail("unknown type");
break;
}

25. 재귀 함수의 이름은 "Recursive"로 끝내라.

void FibonacciRecursive();

26. 클래스 변수와 메소드의 순서는 다음과 같아야합니다.

  1. public variables
  2. internal variables
  3. protected variables
  4. private variables
  5. public methods
  6. Internal methods
  7. protected methods
  8. private methods

27. 함수 오버로딩은 대부분의 경우 피해야합니다.

이거 대신에:
Anim GetAnim(int index);
Anim GetAnim(string name);

이렇게 사용해라:
Anim GetAnimByIndex(int index);
Anim GetAnimByName(string name);

28. 여러 개의 작은 클래스를 그룹화하는 것이 합리적이지 않으면 각 클래스는 별도의 소스 파일에 있어야합니다.

29. 파일 이름은 대문자와 소문자를 포함한 클래스의 이름과 동일해야합니다.

class Anim {}

Anim.cs

30. 너가 가지고 있는 주장에는 assert를 사용해라. (예, 모든 함수는 Debug.Assert( not null parameters) 를 가져야 한다.)

31. bigflag 열거 형의 이름은 Flags로 끝나야합니다.

public enum EVisibilityFlags
{
}

32. 기본 매개변수보다 오버로딩을 더 선호한다.

33. 기본 매개 변수가 사용되면 null, false 또는 0과 같은 자연스러운 상수로 제한하십시오.

34. 지역 변수 숨김은 허용하지 않는다.

public class SomeClass
{
public int Count {get;set;}
public void Func(int Count)
{
for (int count = 0; count != 10; ++count)
{
// Use Count
}
}
}

35. 항상 System.Collections의 컨테이너보다 System.Collections.Generic의 컨테이너를 사용하십시오. 순수 배열을 사용하는 것도 좋습니다.

36. var 키워드 대신 실제 타입을 사용하는 걸 선호합니다. Enumerator같은 경우에는 var를 사용할 수 있습니다.

37. 싱글톤 패턴이 아닌 static class를 사용해라.

38. async void 보다 async Task를 사용해라. async void는 오직 이벤트 핸들러에서만 사용해라.

39. 경계에서 외부 데이터의 유효성을 검사하고 데이터를 함수에 전달하기 전에 반환하십시오. 즉,이 시점 이후에 모든 데이터가 유효하다고 가정합니다.

40. 따라서 우리 함수 내부에서 예외를 던지지 마십시오. 이것은 경계에서만 처리되어야합니다.

41. public 함수에서는 특히 매개 변수에 null을 허용하지 않는 것이 좋습니다.

42. 만약 매개변수에 null을 사용해야 하는 경우라면, 매개변수 이름의 postfix로 OrNull을 붙이십시오.

43. 어떤 함수, 특히 public 함수에서는 null을 반환하지 않기를 바랍니다. 하지만 예외를 throw 하지 않도록 null을 반환해야 하는 경우가 있습니다.

44. 만약 함수에서 null을 반환해야 된다면, 함수 이름에 Postfix로 OrNull을 붙입니다.

public string GetNameOrNull();

45. 객체 initializer를 사용하지 말자. 대신에 명명 된 매개 변수와 함께 명시적 생성자를 사용합시다. 아래 두가지 예외 상황은 제외 합니다.

a. 오브젝트가 한 곳에서만 작성된 경우. (예를 들어, 1 회의 DTO)
b. 객체가 소유 클래스의 정적 메서드 내에서 만들어 질 때 (: 팩토리 패턴)

  1. 첫 단어를 대문자로 시작하는 표기법. 예) BackgroundColor, TypeName, PowerPoint
  2. 각 단어의 첫문자를 대문자로 표기하고 붙여쓰되, 맨처음 문자는 소문자로 표기함. 예) backgroundColor, typeName, iPhone

· 약 23분
karais89

Unity Cloud Build 소개

자습서

01. Unity Cloud Build 소개

이 단원을 마치면 Unity Cloud Build가 무엇이며 어떻게 작동하는지 이해하게 될 것입니다.

Unity Cloud Build란 무엇입니까?

Unity 게임을위한 빌드 파이프 라인1을 자동화하는 서비스입니다.

Cloud Build를 사용해야 하는 이유는 무엇인가?

클라우드 빌드 자동화 서비스를 사용하면 시간을 절약 할 수 있습니다.

빌드는 자동으로 컴파일되고 배포되므로 수작업과 개입이 최소화됩니다.

여러 플랫폼이있는 게임을 단일 빌드 프로세스로 통합 할 수 있습니다.

아래와 같은 장점이 있습니다.

  1. 품질 향상.

    • 변화가 감지되면 지속적으로 게임을 제작하여 프로젝트에 도입 된 문제를 감지 할 수 있습니다.
  2. 더 빠른 배포.

    • 클라우드 기반 인프라 스트럭처는 빌드를 컴파일합니다. 다중 플랫폼 프로젝트의 경우 병렬로 실행됩니다. 완성 된 빌드는 Cloud Build의 웹 사이트를 통해 팀 구성원이 다운로드 할 수 있습니다.

Unity Cloud Build는 어떻게 작동합니까?

Unity Cloud Build는 소스 제어 저장소 (예 : Git, Subversion, Mercurial, Perforce)를 모니터합니다.

변경 사항이 감지되면 Cloud Build가 자동으로 빌드를 생성합니다.

빌드가 완료되면 당신과 당신의 팀에게 이메일로 보냅니다.

당신과 당신의 팀 동료를 위해 당신의 빌드가 Unity에 의해 호스팅됩니다.

만약 빌드가 실패하면 문제를 해결하기 위해 로그가 제공됩니다.

Unity Cloud Build를 사용하려면 무엇이 필요합니까?

소스 제어 저장소. 다음 소스 제어 관리 ("SCM") 시스템을 지원합니다.

  • Git
  • Subversion
  • Mercurial
  • Perforce

02. 당신의 첫 번째 소스 컨트롤 저장소 만들기

이 수업이 끝나면 로컬 Git repo가 생깁니다. 유니티 프로젝트가 포함되어 있으며, Git Sever와 연결될 것입니다.

프로젝트에 대한 소스 컨트롤(Git, Subversion, Perforce 또는 Mercurial)이 있는 리포지토리가 이미 있거나 Unity 프로젝트 빌드를 테스트하기 위해 샘플 프로젝트를 사용하려는 경우 3 단원 : 첫 클라우드 빌드 프로젝트 만들기로 진행할 수 있습니다 .

이 단원에서는 다음을 사용하여 리포지토리를 만듭니다.

  • Bitbucket: 소규모 프로젝트를위한 무료 저장소
  • Git: 인기있는 소스 제어 시스템, Bitbucket과 통합되었습니다.
  • SourceTree: Git 및 Bitbucket에서 작동하는 사용자 친화적인 GUI 툴.

이 도구는 빠른 설정과 손쉬운 사용을 위해 SCM 초보자에게 유용합니다.

시작하려면 로컬 컴퓨터에 기존 Unity 프로젝트가 있어야합니다.

가지고 있지 않은 경우 다음 자습서를 완료하십시오.

Space Shooter Tutorial

01. Download SourceTree

SourceTree를 다운로드하여 설치하려면 www.sourcetreeapp.com을 방문하십시오.

Download Sourcetree

02. Bitbucket 계정 만들기

bitbucket.org를 방문하여 무료 개인 계정에 가입하십시오.

첫 번째 저장소를 만들지 묻는 메시지가 표시되면 '아니요'라고 표시됩니다.

우리는 SourceTree로 저장소를 생성 할 것입니다.

Create Your Repo

03. SourceTree를 사용하여 Bitbucket에 저장소를 만듭니다.

SourceTree를 열고 Bitbucket 로그인 사용자 이름과 암호를 입력하라는 메시지가 나타나면 입력하십시오.

Create a repository on bitbucket

로그인하면 repo를 "Clone"하라는 메시지가 나타납니다 ("Clone a Repo"는 모든 관련 파일을 저장소에서 로컬 시스템으로 복사하는 것입니다).

Bitbucket 계정과 연결된 Repo를 만들지 않은 경우 목록이 비어 있어야합니다. "Skip Setup"을 클릭하십시오.

Skip Setup

"Skip Setup"을 클릭하면 Repository Browser 창이 나타납니다.

Repository Browser

이전에 생성 한 Bitbucket 계정을 사용하여 원격 리포지토리를 만듭니다.

Repository Browser에서 "+ New Repository"를 클릭하십시오. 드롭 다운 목록이 나타납니다. '원격 저장소 만들기'를 선택하십시오.

New Repository Options

"Create a remote repository" 창이 나타납니다.

Create A Remote Repository

아래와 같은 설정으로 세팅합니다:

  • Account: Bitbucket 계정
  • Owner: Bitbucker 유저 이름
  • Name: 무엇이든지 상관 없음
  • Description: 무엇이든지 상관 없음
  • Type: Git
  • This is a private repository: Checked

“Create”를 클릭.

당신은 "Repository Window"로 되돌아 갈 것입니다. "Remote"을 클릭하면 방금 만든 목록의 이름이 목록에 표시됩니다.

04. 당신의 Repo Clone

Repository Window에서 | Remote window에서 이전 단계에서 생성 한 Bitbucket 저장소를 클릭하십시오. 다음 프롬프트가 나타납니다.

Clone A Repository

  • Source URL: 있는 그대로 유지하고 나중에 기록해 두십시오. 나중에 Unity Cloud Build를 구성 할 때 사용하게됩니다.
  • Destination Path: 저장소 파일이 저장 될 로컬 시스템의 위치를 선택하십시오. 대상 경로는 항목이 없는 디렉토리를 가리켜야 합니다.
  • Name: 무엇이든지 상관 없음.
  • Advance Options: 있는 그대로 둡니다.

“Clone”을 클릭.

그러면 Repository Window로 돌아 가게 됩니다. 로컬, 방금 작성한 저장소의 로컬 사본이 표시됩니다. 저장소는 비어 있습니다. 당신은 다음 단계에서 그것을 채울 것입니다.

Empty Repository

05. 유니티 프로젝트로 로컬 레포 채우기

Repository Window에서 | 로컬에서 repo를 더블 클릭하십시오. 메시지가 나타나면 이름과 이메일을 입력하십시오.

User Details

이제 로컬 저장소를 채 웁니다.

로컬 유니티 프로젝트를 찾고 모든 파일을 4단계에서 정의한 repo 대상 경로로 이동하십시오.

다음 스크린 샷에서 단일 프로젝트 "MyFirstProject"가 로컬 리포지토리 폴더 "MyFirstUnityRepo"로 이동합니다

Populate Your Local Repository

로컬 repo를 채울 때 SourceTree는 자동으로 변경 사항을 감지하고 다음과 같이 업데이트합니다 :

First Repsoitory

06. Commit 과 Push 로컬 Repo를 원격 Repo로 푸시합니다.

Unity Cloud Build는 원격 저장소를 모니터링합니다. 이렇게하려면 원격 repo에 파일을 COMMIT하고 PUSH해야합니다.

"Unstaged files"확인란을 선택하여 서버에 푸시 될 모든 파일을 선택합니다.

참고 : 각 파일 이름 오른쪽에 세 개의 점 (•••)이 있음을 알 수 있습니다.이 점을 클릭하면 '자세히'메뉴에 액세스 할 수 있습니다. 이 메뉴를 사용하여 Unity 'Library'폴더에 대해 '파일 무시'를 선택하십시오 (Unity Cloud Build는 빌드 프로세스 중에 Library 폴더를 생성합니다). 프로젝트를 빌드하기 위해 Unity가 필요로하지 않는 다른 파일도 '무시'할 수 있습니다. 이 단계를 완료하지 않으면 필요한만큼 빌드 프로세스가 느려집니다.

화면 상단의 "Commit"을 클릭하십시오. 메시지가 나타나면 이름과 이메일을 입력하십시오.

First Commit

Comment를 입력하고 "Commit"을 클릭하십시오.

First Commit 2

다음 메시지가 나타납니다. "There is nothing left to commit"

Nothing to commit

커밋 된 모든 파일을 원격 저장소에 복사하려면 "Push"를 클릭하십시오.

Push

"확인"을 클릭하십시오. 업로드 프로세스가 시작됩니다.

07. 원격 저장소 확인

bitbucket.org로 이동하여 너의 아이디로 로그인하십시오. 첫 번째 화면에 저장소 이름이 표시되어야합니다. Repo에 대한 페이지로 이동하려면 이름을 클릭하십시오.

repo 페이지에서 브라우저의 주소 표시 줄로 이동하여 표시되는 URL / 주소를 복사하십시오.

Repote Repository

이 URL은 새 프로젝트를 추가 할 때 Unity Cloud Build에 제공하는 URL입니다.

이 페이지에서 'Git' 탭을 클릭하거나 Unity Cloud Build 설정 마법사의 지침에 따라 Bitbucket 계정에 SSH 키를 추가하여 액세스를 허용하십시오.

03. 첫번째 Cloud Build 프로젝트

이 레슨이 끝나면 코드 리포지토리에 커밋 할 때 자동으로 컴파일하고 알려주는 Unity Cloud Build 프로젝트를 갖게됩니다.

시작하려면 다음이 필요합니다.

  1. 소스 제어 저장소. Unity Cloud Build는 Git, Subversion, Mercurial 및 Perforce를 지원합니다. 강의 1을 참조하십시오.
  2. Unity Cloud Build 계정.

01. 프로젝트 만들기

https://build.cloud.unity3d.com으로 이동하여 Unity Developer Account로 로그인하십시오. Cloud Build 홈페이지로 이동하게됩니다.

Cloud Build Home Page

Cloud Build 홈 페이지에서 "Add New"를 클릭하십시오. Cloud Build는 프로젝트의 이름과 프로젝트가 속한 조직의 이름을 선택하도록 요청합니다.

Create New Project

  • Organization: 드롭 다운 메뉴를 클릭하고 사용자 이름을 선택하십시오.
  • Project Name: 무엇이든지 상관 없음.

02. Unity Cloud Build Project를 원격 저장소에 연결하십시오.

repo URL을 제공 한 후 다음을 클릭하십시오.

Connect To Repository

03. 액세스 권한 구성

repo가 비공개 인 경우 Cloud Build는 파일에 액세스하여 빌드 할 수 있도록 SSH 키를 추가하라는 메시지를 표시합니다 (공개 인 경우 자동으로 다음 단계로 건너 뜁니다)

이전 강의를 사용하여 BitBucket Repo를 만든 경우 Unity Cloud Build에서 제공하는 SSH 키를 BitBucket Repo에 첨부해야합니다

이 작업을 수행하지 않으면 빌드 시도가 실패하게됩니다. Bitbucket에 로그인하고 Cloud Build SSH 키를 Bitbucket 계정에 추가하십시오.

이전 단계의 샘플 프로젝트를 사용한 경우에는 액세스 권한을 구성하라는 메시지가 표시되지 않습니다. 프로젝트는 공개입니다.

Connect To Repository

클라우드 빌드에서 제공하는 SSH 키 복사

Cloud Build에서 제공하는 SSH 키를 리포에 연결합니다. 만약 레슨 2를 진행 하였다면, BitBucket 레포에 SSH 키를 추가할 수 있을 것입니다.

Connect To Repository

저장소 구성을 마치면 다음을 클릭하여 설정을 구성하십시오.

04. Unity Cloud Build 프로젝트 구성하기

플랫폼을 선택하라는 메시지가 나타납니다. 이 레슨에서는 Web Player를 선택하십시오.

Select Build Targetb Update

자세한 구성 정보를 제공하라는 메시지가 나타납니다. 빌드 할 Repo에서 지점을 선택해야합니다. 레슨 2의 Bitbucket 레포를 사용하는 경우 "마스터"를 선택하십시오.

Select Build Branch

05. 빌드!

"Next"를 클릭하면 Unity Cloud Build가 자동으로 컴파일을 시작합니다.

Select Build Branch

빌드 프로세스가 완료되면 자동으로 통지됩니다.

04. 부록 : 빌드 프로세스 기본 사항

소개

이 섹션에서는 게임 빌드 프로세스에 대한 높은 수준의 개요를 제공합니다. 이 단원을 마치면 빌드 프로세스와 구성 요소가 무엇인지 알 수 있습니다.

빌드 란 무엇입니까?

게임의 사용 가능한 버전.

플레이어는 장치를 사용하는 빌드를 중요하게 생각합니다.

종종 공개, 라이브 또는 프로덕션 빌드라고도합니다.

주어진 프로덕션 빌드의 경우 게임 개발자가 생성 한 빌드 수는 수백 개는 아니지만 수천 개에 달합니다.

게임이 제대로 작동하도록하기 위해 만들어졌습니다. 이러한 빌드는 모두 구성, 컴파일, 유효성 검사 및 배포에 시간이 필요합니다.

빌드 프로세스 (또는 빌드 파이프 라인) 란 무엇입니까?

게임 개발자가 빌드를 만드는 데 사용하는 일련의 도구 및 단계.

솔로 개발자라면 프로세스가 간단합니다.

코드, 자산, 버전, 빌드 환경 및 시스템은 모두 로컬 컴퓨터에 있습니다.

그러나 팀 환경에서는 팀이 개선해야하는 "권위있는"버전의 게임을 결정하기 위해 도구와 프로세스가 필요합니다.

빌드 프로세스는 무엇으로 구성됩니까?

일반적인 요소는 다음과 같습니다.

  • 자산 및 코드가 저장되는 저장소 (또는 "Repo"). 전문적인 환경에서 프로젝트의 모든 파일은 소스 제어 관리("SCM") 시스템에 저장되므로 여러 팀 구성원이 최소한의 충돌로 동일한 리소스로 작업 할 수 있습니다.
  • Build Environment는 저장소에서 코드와 자산을 가져 와서 게임의 실제 버전으로 컴파일합니다.
  • 배포 서비스는 사용자에게 빌드를 가져옵니다. 대부분의 게임 개발 라이프 사이클에서 빌드는 유효성이 확인되면 내부적으로 배포됩니다.
  • 자동화 엔진은 빌드 프로세스의 일부를 모니터하고 자동 조치를 취합니다. 보고서, 알림 및 배포뿐만 아니라 빌드 컴파일. 자동화 엔진이 없으면 팀원 중 한 명 이상이 수동으로 프로세스를 실행합니다.

일반적인 빌드 파이프 라인은 다음과 같습니다.

Build Pipeline

소스 제어는 무엇입니까?

소스 제어 (일반적으로 버전 제어라고도 함)는 여러 파일의 변경 사항을 '실행 취소'하는 방법을 제공하고 팀과 함께 소프트웨어 프로젝트를 공동 작업하는 시스템입니다.

소스 컨트롤을 사용하면 실수로 게임 시간을 잃지 않도록 보호하고 코드로 다른 것을 시도해 보는 것이 더 쉽지만보다 안정적인 버전의 프로젝트로 롤백하기 때문에 게임 개발에있어 '모범 사례'로 간주됩니다 당신이 당신의 접근 방식을 재고 할 필요가 있다면.

소스 제어는 어떻게 작동합니까?

특정 소스 관리 관리 시스템에 따라 다릅니다. 소프트웨어 개발을 위해 Git을 사용하기위한 워크 플로는 일반적으로 다음과 같이 작동합니다.

  1. 당신은 repo (어딘가에서 호스팅)를 만들고 Unity 프로젝트를 넣습니다.
  2. 변경 사항을보고 추적 할 수 있는 인터페이스를 제공하는 Git 클라이언트 (소프트웨어)를 선택합니다.
  3. Git에게 라이브러리 폴더와 같은 특정 파일을 무시하도록 명령한다.
  4. Unity 프로젝트를 약간 변경하고 Unity에 저장합니다.
  5. Git 클라이언트로 전환하면 변경된 파일을 알려줍니다.
  6. 그 파일을 선택하고 '커밋'하면됩니다 - 이것은 프로젝트의이 버전을 표시하는 것과 근본적으로 유사하므로 나중에 참조 할 수 있습니다.
  7. 1 (또는 그 이상) 커밋이 끝나면 커밋을 Gitl 호스트 / 서버로 'push' 때문에 이제는 백업되고 프로젝트에서 작업중인 다른 사람도 볼 수 있습니다. 마지막 'push' 단계는 Unity Cloud Build가 프로젝트를 시청할 때 보는 단계입니다. 그러면 자동으로 새 빌드를 시작합니다.

Cloud Build에서 지원하는 소스 제어 시스템은 무엇인가요?

Unity Cloud Build는 다음 소스 제어 관리 시스템 ("SCM")을 지원합니다.

  • Git
  • Subversion("SVN")
  • Perforce
  • Mercurial

특정 시스템에 대한 자세한 내용은 다음 리소스를 참조하십시오.

솔로 개발자는 빌드 파이프 라인이 필요합니까?

예, 직접 작업하는 개발자조차도 소스 제어 및 파이프 라인 구축의 이점을 누릴 수 있습니다.

소스 컨트롤은 하드 드라이브가 죽거나 실수로 프로젝트를 삭제하거나 중단 한 경우 복구 할 수있는 프로젝트의 백업을 제공합니다.

Unity Cloud Build와 같은 빌드 파이프 라인을 사용하면 여러 플랫폼에 대한 빌드를 한 번에 만들 수 있으므로 시간을 절약 할 수 있습니다.


  1. 시스템의 효율을 높이기 위해 명령문을 수행하면서 몇 가지의 특수한 작업들을 병렬 처리하도록 설계된 하드웨어.

· 약 19분
karais89

메모리와 리스소를 최적화 해야 되는 이유

  1. 용량이 50MB를 넘게 되면 다운로드하는 유저 수가 절반으로 줄어든다.
  2. 메모리를 많이 쓰면 게임 유저 수에 제약이 생긴다. (저사양 핸드폰에서는 돌아가지 않는다)
  3. 최적화를 하지 않을 시 발생하는 오버헤드는 일반 앱과 비교가 되지 않을 정도로 크다.

02. 스크립트 연산 최적화

01. 유니티 게임 오브젝트를 찾지 말고 캐싱하라.

FindObject 계열 함수들은 매우 느립니다.

찾은 오브젝트는 꼭 변수에 할당해서 캐싱해 놓은 후에 사용합시다.

02. 오브젝트 풀링 기법을 사용하라.

유니티에서 반복적으로 등장하고 제거되는 오브젝트들을 관리하려면 오브젝트 풀링은 필수입니다. Instanitate와 Destory 함수를 이용한 프리팹의 생성/해제는 비용이 크기 때문입니다.

03. Update 함수 보다는 Coroutine을 활용하자.

Update는 매 프레임마다 호출됩니다.

매 프레임마다 호출되야 되는 경우가 아닌 경우에는 코루틴을 활용해 봅시다.

아래는 코루틴이 성능이 좋은 이유입니다.

예를 들어 코루틴에서 yield return new WaitForSeconds(10)이라는 명령을 수행하면 코루틴은 유니티 엔진에게 WaitForSeconds(10)이라는 데이터를 보내고 쉬기 시작합니다. 유니티 엔진은 이를 받고 기록해두었다가 묵묵하게 자기 할 일을 진행 하면서 10초가 지나면 쉬고 있는 코루틴을 깨웁니다. 코루틴이 없이 일반적으로 이를 구현 한다면 Update 구문에서 Time.deltaTime을 사용하여 매 프레임마다 시간을 더해서 10초가 지났는지 감지해야 하는데, 프레임의 평균 소요 시간이 0.01초라고 한다면, 아이러니하게 10초 동안 대기하기 위해 스크립트는 Update 함수를 1000번 호출해야 합니다. 코루틴을 사용하면 10초 동안 스크립트가 쉬는데 말이지요~

04. 문자열을 연결할 땐 StringBuilder를 쓰자.

일반 String + String을 쓰면 임시 문자열이 생성됩니다. (가비지 컬렉터 생성)

StringBuilder.Append() 함수를 사용하여 병합합시다.

05. 나누기 10보단 곱하기 0.1

나눗셈보다 곱셈의 연산 속도가 몇십 배 빠릅니다. 곱하기로도 가능한 나눗셈 연산은 곱하기로 표현하는게 좋습니다.

06. 가비지 컬렉션에서 벗어나자.

문자열은 readonly 혹은 const를 사용하여 가비지 컬렉션에서 벗어나도록 합니다.

07. 객체 캐싱을 활용하라

컴포넌트 참조 GetComponent() 함수는 한 번만 호출하여 객체를 캐싱해 놓습니다.

ex) transform 컴포넌트 캐싱

private Transform _tr;
void Awake()
{
_tr = GetComponent<Transform>();
}

08. 빈 콜백 함수는 제거

Start()나 Update() 같은 콜백함수는 비어있어도, 성능에 영향을 끼칩니다. 사용하지 않는 경우에는 제거 해 줍시다.

03. 리소스 최적화로 메모리 사용량 줄이기

[텍스처 압축 방식별 차지하는 메모리 용량]

[standalone & WebGL]

압축 방식메모리 사용량 (bytes/pixel)
RGB crunched DXT1variable
RGBA Crunched DXT5variable
RGB Compressed DXT10.5 bpp
RGBA Compressed DXT51 bpp
RGB 16bit2 bpp
RGB 24bit3 bpp
Alpha 8bit1 bpp
RGBA 16bit2 bpp
RGBA 32bit4 bpp

[iOS]

압축 방식메모리 사용량 (bytes/pixel)
RGB Compressed PVRTC 2 bits0.25 bpp (bytes/pixel)
RGBA Compressed PVRTC 2 bits0.25 bpp
RGB Compressed PVRTC 4 bits0.5 bpp
RGBA Compressed PVRTC 4 bits0.5 bpp
RGB 16bit2 bpp
RGB 24bit3 bpp
Alpha 8bit1 bpp
RGBA 16bit2 bpp
RGBA 32bit4 bpp

[Android]

압축 방식메모리 사용량 (bytes/pixel)
RGB Compressed DXT10.5 bpp (bytes/pixel)
RGBA Compressed DXT51 bpp
RGB Compressed ETC10.5 bpp
RGB Compressed PVRTC 2 bits0.25 bpp
RGBA Compressed PVRTC 2 bits0.25 bpp
RGB Compressed PVRTC 4 bits0.5 bpp
RGBA Compressed PVRTC 4 bits0.5 bpp
RGB 16bit2 bpp
RGB 24bit3 bpp
Alpha 8bit1 bpp
RGBA 16bit2 bpp
RGBA 32bit4 bpp

텍스쳐 압축은 메모리 사용량과 관련이 깊습니다.

이미지 파일 사이즈와 상관없이 이미지가 메모리상에서 차지하는 용량 계산 방식은 다음과 같습니다.

메모리 사용량 = 가로 픽셀 세로 픽셀 압축 방식의 메모리 사용량(bpp)

이미지 메모리 사용량을 잘 관리하는 방법은 눈에 보이는 이미지는 압축하지 않고, 잘 보이지 앟는 이미지는 압축을 많이 하는 것입니다.

특히나 유저 인터페이스의 경우네는 압축을 많이 하면 이미지가 깨지기 때문에 게임의 퀄리티가 상당히 낮아 보입니다. 이런 이유로 유저 인터페이스는 압축을 피하는 것이 좋습니다.

하지만 3D 몬스터 캐릭터의 텍스쳐 등은 조금 압축을 하더라도 퀄리티에 크게 영향을 주지 않습니다. 스마트폰 게임 특성상 제한된 리소스를 효율적으로 써야 하기에 퀄리티를 강조하고 싶은 영역에는 보다 화질 좋은 텍스쳐를 쓰는 것이 현명한 선택이라 할 수 있습니다.

01. 디바이스별로 권장하는 압축 텍스쳐 포맷

  • 아이폰(powerVR) : PVRCT
  • 안드로이드(Tegra) : DXT
  • 안드로이드(Adreno) : ATC
  • 안드로이드(공통) : ETC!

02. 이미지 가로세로 사이즈는 무조건 2의 제곱

게임에서 사용하는 이미지 가로세로 사이즈는 무조건 2의 제곱으로 되어야 한다(Power Of Two).

컴퓨터에서 이미지를 사용할 때에는 개념적으로 “[1번] 디스크에서 이미지 불러오기→[2번] 이미지 압축 포맷 압축 해제→[3번] 1024×1024×32비트 메모리 블록에 해당 이미지 할당” 과정을 거친다.

1024×1024×32비트 RGBA 기준으로 이미지를 압축한 png 용량은 313KB에 불과하다. 하지만 압축을 해제하면 메모리상 이미지 사이즈는 4MB나 된다. 2048×2048이라면 16MB에 이른다.

이렇게 가로세로 사이즈가 2의 제곱으로 된 이미지가 아닌 경우에는 상당한 메모리 낭비가 일어나게 된다. 예를 들어 900×900 사이즈 이미지가 있다고 하자. 메모리상에서 900×900 사이즈 이미지를 도르할 때 해당 이미지를 똑같이 1024×1024로 변환해 다시 메모리에 저장하게 된다. 다시 말해 거의 배에 가까운 이미지 메모리가 낭비되는 것이다.

이런 이유 때문에 유니티3D를 비롯한 여러 게임 개발 엔진에서 아틀라스(Atlas)라는 리소스 단위를 사용하게 된다. 이미지를 POT(2의 제곱) 방식으로 바꿔서 항상 활용하기를 권한다.

03. 압축된 텍스처와 밉맵 활용

32bit가 아닌 16bit 텍스쳐도 상황에 맞게 적절히 활용하는 것이 좋습니다. 밉맵은 렌더링 속도를 향상시키기 위해 기본 텍스쳐와 이를 연속적으로 미리 축소시킨 텍스쳐들로 이루어진 비트맵 이미지의 집합입니다. 메모리와 리소스 최적화를 위해서는 이런 밉맵을 사용하는 것도 도움이 됩니다.

04. 오디오는 92kb 모노 인코딩으로

모바일에서 스테레오는 의미가 없습니다. 그러니 모두 92kb 모노로 인코딩하는 것이 좋습니다. 92kb 모노 인코딩은 유니티 엔진에서 간편하게 설정할 수 있습니다. 또한 사운드 파일을 임포트하면 디폴트 값으로 3d 사운드가 설정됩니다. 이를 2d 사운드로 설정 변경하는 것도 리소스 낭비를 줄이는데 도움이 됩니다.

05. 오디오 파일은 .wav 형식으로 저장

오디오 파일은 용량을 줄일려고 굳이 .mp3 형식으로 임포트할 필요가 없습니다. 왜나혀면 .wav 파일을 임포트해도 자체 인코더가 용량을 원하는 데로 압축해주기 때문입니다. 음향 손실을 피하려면 wav로 저장하여 유니티 내부 인코더를 활용하는 편이 낫습니다.

04. 캐싱 활용법

페이스북이나 카카오톡과 게임을 연동하게 되면 프로필 이미지를 자주 불러오게 됩니다. 프로필 이미지를 매번 다운로드해서 표시한다면 프로필 이미지가 뜨기까지 시간이 아주 길어집니다. 이를 매끄럽게 하기 위해서는 꼭 캐싱 기법을 사용해야 합니다.

캐싱은 자주 사용하는 데이터를 디스크나 메모리에 저장해두는 기법입니다. 이를 활용하면 네트워크 대역폭을 크게 줄일 수 있습니다. 인터넷에서 다운로드한 이미지를 디스크에 저장해 활용하는 캐싱 기법에 대해 알아봅시다. 이 과정은 총 두번의 스텝으로 이루어집니다.

  • 다운로드할 이미지가 디스크에 있는지 체크 (void LoadProfile)
  • 디스크에 없으면 인터넷에서 이미지를 받아서 디스크에 저장(IEnumerator DownloadProfile)

과정 중간에 에러나 예외가 발생하면 null을 반환합니다.

null을 반환시 사용자는 null 처리를 해주시며 됩니다(ex) 디폴트 이미지를 뿌려준다던지..)

예제를 통해 확인해 봅시다.

캐싱 기법을 활용한 메모리/리소스 최적화 방법은 단편적인 솔루션입니다.

하지만 게임에서 가장 많은 용량과 메모리를 차지하는 이미지 리소스 최적화에서는 아주 기본적인 단계이기도 합니다.

05. 그래픽스 최적화 주요 지표

최적화의 기본은 병목현상을 파악하고 제거하는 것이 핵심입니다.

에디터에서 Game-Stats 메뉴를 클릭하면 통계 수치를 확인할 수 있습니다.

항목설명
Time per frame and FPS하나의 프레임을 처리 및 렌더링 하는 데 걸린 시간 및 상호 관계에 있는 프레임/초, 이 숫자는 프레임 업데이트 및 게임 뷰의 렌더링에 걸린 시간만 포함되는 것에 유의합니다. 에디터가 신 뷰 인스펙터의 그리기 및 에디터 전용 작업을 수행하는 데 걸린 시간은 포함되지 않습니다.
Batches(배칭)배칭이란 여러 오브젝트를 메모리 덩어리로 결합시키는 작업을 말합니다.
Saved by batching(결합된 배칭 수)결합된 배칭의 수입니다. 좋은 배칭을 이끌어내기 위해서는 가능한 한 많은 매터리얼을 공유하도록 하는 게 좋습니다.
Tris and Verts(삼각형의 정점 수)그려진 삼각형과 정점 수
Screen화면 크기, 안티 앨리어신 레벨 및 메모리 사용량
SetPass드로우 콜(Draw Call)과 같은 단어라고 보면 됩니다. 렌더링 패스의 수, 각 패스에 대해서 유니티 런타임은 CPU 오버헤드를 가져올 수 있는 새로운 쉐이더를 바인딩합니다.
Visible Skinned Meshes렌더링 스킨 메시 수
Animations재생 애니메이션 수

06. 배칭

배칭이란 3D 오브젝트들을 하나의 메모리 덩어리로 만들어서 한 번에 그리도록 도와주는 작업을 말합니다. 유니티 메뉴의 [Window > Frame Debugger]를 사용하면 배칭 연산이 이루어지는 단계별로 스냅샷을 볼 수 있습니다.

유니티에서 지원하는 배칭에는 스태틱 배칭과, 다이내믹 배칭 두 가지가 있습니다.

유니티 메뉴에서 Edit-Project Setting-Player로 들어가면 스태틱 배칭을 설정할 수 있습니다.

1) 스태틱 배칭

변하지 않는 오브젝트의 인스펙터탭에서 static 체크박스를 선택함으로써 스태틱 배칭이 일어나도록 설정할 수 있습니다.

2) 다이내믹 배칭

동적으로 비슷한 재질의 오브젝트를 하나의 연산 단위로 묶는 방식을 말합니다.

3D 오브젝트이 정점 수에 영향을 받습니다.

07. 오버드로우와 세이더

1) 오버 드로우

오버 드로우 : 한 픽셀에 여러 번 그리는 행위

화면의 한 픽셀에 두번 이상 그리게 되는 경우에, 오버 드로우가 발생한다고 합니다.

한 픽셀에 어러 번 그리는 만큼 그래픽 부하도 증가하는 건 당연합니다.

2d게임에서 여러 반투명 이미지들을 겹쳐서 표현하는 과정에서 발생합니다.

꼭 반투명이 필요한 이미지만 반투명 옵션을 주고, 굳이 반투명 옵션이 필요하지 않은 경우에는

최대한 옵션 선택을 줄이는 형식으로 반투명 오브젝트에 개수 제한을 걸면 오버드로우 문제를 해결할 수 있습니다.

2) 유니티 세이더

기본 세이더는 모바일용 세이더를 사용합니다. 가장 빠른 세이더는 VertexLit 입니다.

Mobile-VertexLit로 선택하면 됩니다. 화질 차이가 매우 중요한 오브젝트 외에는 모바일에 최적화된

세이더를 쓰는 것이 좋습니다.

참조