Android devices with a notch (display cutout) have recently become quite common. However, if you make a Unity application and deploy it to such device, by default Android OS will letter-box your app so that it the notch doesn't interfere with your UI.
If you want to make use of the whole display surface area and render in the area "behind" the notch, you need to add code to your application. Fortunately, since Unity 2018.3 there is a special option in the Player settings called "Render outside safe area" which does exactly this. If you want to have the same option in earlier versions of Unity - this plugin was made for you!
One advantage of this plugin over the built-in Unity solution is that it allows changing the setting in runtime if needed, by callingpublic void SetRenderBehindNotch(bool enabled)inRenderBehindNotchSupport.
If you are planning to make use of "rendering behind the notch" feature, you'll also need another feature which returns you the area of the screen which is outside of the notch area (and is safe to render to). The API is equivalent to whatScreen.safeAreaAPI does in newer Unity versions, and returns aRect.
This plugin is targeted towards Unity 2017.4, however I see no reasons why it shouldn't work with earlier versions too.
System Requirements
Tested on Unity 2017.4.28f1. Should work on any Unity version out there, but make sure your target API is set to 28 or higher. There is no point in using this plugin in Unity 2018.3 or later because these versions have notch support out of the box.
An Android device with Android 9 Pie or later. Some devices with earlier Android versions have notch/cutout, but Google has added a corresponding API only in Android 9. Feel free to add other vendor-specific bits of code to add support on earlier Androids at your own risk.
Usage
Copy the contents ofAssetsdirectory to your project
Attach theAssets/Scripts/RenderBehindNotchSupport.csscript to a game object of your choice in your first scene to make sure the plugin is loaded as early as possible
The script has a public boolean property so that you can tick/untick the checkbox to enable or disable rendering behind the notch with a single click
If you want to change the setting in runtime, callpublic void SetRenderBehindNotch(bool enabled)inRenderBehindNotchSupportclass.
Attach theAssets/Scripts/AndroidSafeArea.csscript to a game object of your choice if you need the safe area API. The propertyAndroidSafeArea.safeAreais returning aRect, use it to layout your UI.
Enjoy
Alternative solution
Instead of using the script (or if you want to apply the "render behind the notch" flag as early as possible), you could modify the theme used by Unity. To do so, please create a file at the pathAssets/Plugins/Android/res/values-v28/styles.xmlwith the following contents:
Unity 편집기에서 화면 크기를 iPhoneX와 같은 크기 (1125 × 2436)로 설정하면 Unity Player에서 디버그 할 수도 있습니다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Runtime.InteropServices;
public class SafeAreaScreen {
#if UNITY_IOS
[DllImport("__Internal")]
private extern static void GetSafeAreaImpl(out float x, out float y, out float w, out float h);
#endif
static public Rect GetSafeArea()
{
float x, y, w, h;
//初期値設定
x = 0;
y = 0;
w = Screen.width;
h = Screen.height;
#if UNITY_IOS && !UNITY_EDITOR
//iOSSafeAreasPluginを呼び出す
GetSafeAreaImpl(out x, out y, out w, out h);
#elif UNITY_ANDROID && !UNITY_EDITOR
//Androidの縦長端末対応が必要になったときのために
#else
//UnityエディタでiPhoneXのテストできるように値を設定する
if(Screen.width == 1125 && Screen.height == 2436 ){
y = 102;
h = 2202;
}
#endif
return new Rect(x, y, w, h);
}
static public int GetTopOffsetY(){
int offset = 0;
//画面の高さとセーフエリアの高さの差分
Rect screenSize = GetSafeArea();
if (Screen.height != screenSize.height) {
offset = Screen.height - (int)screenSize.height - (int)screenSize.y;
}
return offset;
}
static public int GetBottomOffsetY(){
int offset = 0;
//セーフエリアのyの位置取得
Rect screenSize = GetSafeArea();
if (Screen.height != screenSize.height) {
offset = (int)screenSize.y;
}
return offset;
}
}
NGUI의 SafeArea
NguiSafeAreaPatch.cs
SafeAreaScreen.cs에서 얻은 안전 영역을 바탕으로 Anchor의 위치를 조정합니다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class NguiSafeAreaPatch : MonoBehaviour {
void Awake()
{
// UIAnchorを使ってAnchorを設定している場合
UIAnchor anchor = gameObject.GetComponent();
if(anchor != null){
//offsetの値を取得する
SetUIAnchorOffset(anchor);
return;
}
// UIRectをAnchorを使っている場合
UIRect rect = gameObject.GetComponent();
if (rect != null) {
//現在,NGUIのソースコードを解析中
//完成しだい公開予定
return;
}
}
private void SetUIAnchorOffset(UIAnchor anchor){
//現在のoffsetを取得する
Vector2 nowOffset = anchor.pixelOffset;
//TOP側の補正値取得
int topCorrectionOffset = SafeAreaScreen.GetTopOffsetY ();
//Bottom側の補正値取得
int bottomCorrectionOffset = SafeAreaScreen.GetBottomOffsetY ();
switch (anchor.side) {
case UIAnchor.Side.Top:
case UIAnchor.Side.TopLeft:
case UIAnchor.Side.TopRight:
anchor.pixelOffset.Set(nowOffset.x, nowOffset.y - topCorrectionOffset);
break;
case UIAnchor.Side.Bottom:
case UIAnchor.Side.BottomLeft:
case UIAnchor.Side.BottomRight:
anchor.pixelOffset.Set(nowOffset.x, nowOffset.y + bottomCorrectionOffset);
break;
}
}
}
사용법은이 소스 코드를 UIAnchor가 부착되어있는 게임 객체에 첨부 할뿐.
소스에서 무슨 일을하는지는 동작 환경이 iPhoneX 판정되면 스크린과 안전 영역의 높이의 차이를 계산하여 차등 분 게임 오브젝트를 낮 춥니 다.
델타 값은 UIAnchor의 "Pixel Offset"에 할당됩니다.
소스 코드를 반영한 결과
NguiSafeAreaPatch.cs을 UIAnchor에 연결시킨 결과를 기재합니다.
■ 반영 전
■ 반영 후
그 결과 안전 영역을 기준으로 할 수있었습니다.
이것으로 UIAnchor 대해서는 SafeArea의 대처가되었습니다.
그러나, 그 밖에도 「UIRect의 Anchor '에 대해서도 SafeAreas 대응이 있습니다.대응이 끝나는대로 수시 문서에 추가하는 것입니다.
배포시 가장 큰 골치덩어리중 하나는 AndroidManifest.xml 파일 수정문제일 것이다. Android Plugin을 만들어서 넣자니 짜증나고... 그럴때 간단하게 AndroidManifest.xml 파일을 수정할 수 있는 방법을 공개한다.
프로젝트 Root폴더에 보면 "Temp" 폴더가 생성되어 있을텐데 거길 가만히 보면 "StagingArea"라는 폴더가 보인다. 여기로 들어가면 다음과 같이 폴더가 구성되어 있다.
빌드에서 사용될 각종 Resource 파일들이 보일텐데 이중에 필요한건 AndroidManifest.xml 파일과 res 폴더 두개이다. 이 2개를 선택해서 CTRL+C 해서 복사하고 유니티로 돌아와서 "Plugins" 폴더를 만든다음 다시 "Android"폴더를 만들고 거기에 복사해 넣자.
이제 복사한 AndroidManifest.xml 파일을 열어서 마음대로 주무르면 됨. 끝!
최종 화면에 영향을 미치지 않는 물체를 파악하고 이를 렌더링 대상에서 제외시켜 필요한 물체만 그리는 기법
프러스텀 컬림(Frustum Culling)
가장 기본적이면서도 효과가 좋은 방법
카메라 영역내의 공간 정보만 살려두고 영역 외부에 설정된 정보는 모두 버림
카메라에 프로젝트에 설정된 레이어별로 차단 거리(Per-Layer Cull Distances)를 지정하면 이 값을 사용해 레이어에 속한 게임 오브젝트들을 컬링함
오클루젼 컬링(Occlusion Culling)
기존의 절두체를 이용한 컬링 기법의 한계를 보완하고 효율적으로 공간 정보를 관리해 빠르게 렌더링을 수행할 수 있게 도와주는 기법
Unity의 Windows => Occlusion Culling 메뉴를 통해 설정
오클루젼 컬링 제작방법
오클루젼 영역 설정
오클루젼 컬링 데이터 제작
최종 결과 시뮬레이션
오클루젼 포털(Occlusion Portal)
오클루젼 영역을 스크립트로 조절하는 기능
드로우 콜 배칭(Draw Call Batching)
텍스쳐 아틀라스(Texture Atlas)를 사용하여 콜수를 줄임
성질이 동일한 물체들을 하나의 메시와 재질을 사용하게 통합
여러 게임 오브젝트의 메시를 하나의 메시로 통합해주는 CombineChildren 컴포넌트를 제공
최상위 게임 오브젝트에 이 컴포넌트를 사용하면 자식 오브젝트들이 갖는 메시들을 분석한 후 재질별로 메시를 통합해 재가공함
컴바인 메시(Combined Mesh)를 통한 작업시 주의할 점
게임 오브젝트들을 통합하면 게임 오브젝트별로 움직일 수 없게 됨
관련된 빛의 계산이 통합된 모든 메시를 대상으로 수행됨
메터리얼(Materials)
다른 텍스쳐에 있는 두개의 독립적인 메터리얼을 하나의 큰 텍스쳐로 결합 할 수 있음(텍스처 아틀라스)
일단 텍스처를 동일한 아틀라스로 묶으면 단일 메터리얼로 사용할 수 있게 됨
배칭(Batching)
현재 씬의 상황을 판단해 자동으로 드로우 콜을 줄이게 최적화하는 기능
정적 배칭 Static Batching (Pro required)
Static 옵션이 설정된 게임 오브젝트에서 동일한 재질을 사용하는 물체가 있는 경우 유니티는 이들을 자동으로 하나의 메시로 통합 관리함
움직이지 않는 물체에 대해 지정만 하면 자동으로 수행
동적 배칭 Dynamic Batching
움직이는 물체를 대상으로 동일한 재질을 사용하는 경우 이를 판단해 자동으로 메시를 통합해 드로우 콜을 줄여주는 기능
총 정점의 데이터 수가 900개 미만인 메시를 지닌 게임 오브젝트만을 대상으로 수행됨
메시 데이터가 위치 외에도 노멀, UV, 라이트맵 UV 등 총 3개의 속성을 갖고 있는 경우 정점의 수가 225개를 넘으면 동적 배칭이 적용되지 않음
최적의 퍼포먼스를 위한 캐릭터 모델링(Modeling Characters for Optimal Performance)
싱글 스킨 메쉬 렌더러 사용
각 캐릭터에 스킨 메쉬 렌더러(class-SkinnedMeshRenderer)는 하나여야 합니다.
Unity는 시야 컬링 및 경계 볼륨의 업데이트를 사용하여 애니메이션을 최적화하고, 이러한 최적화는 하나의 애니메이션 컴포넌트 및 하나의 스킨 메쉬 렌더러를 사용하는 경우에만 활성화됩니다.
모델의 렌더링 시간은 두개의 스킨 메쉬를 사용할때 하나의 스킨 메쉬를 사용 할 때보다 대략 2배 정도 이며, 여러개의 메쉬를 사용하여 실질적인 메리트가 있는 일은 드뭅니다.
가능한 메터리얼을 적게 사용
각 메쉬의 메테리얼의 수를 최대한 적게 해야 합니다.
여러 메테리얼을 캐릭터에서 사용하는 이유는, 다른 부분에서 다른 쉐이더가 필요한 경우 뿐 입니다(예를 들면 눈 부분은 특수 쉐이더).
그러나 캐릭터에서 두 개 또는 세 개의 메테리얼로 거의 모든 케이스에 충분 합니다.
가능한 한 본(Bones)를 적게 사용
전형적인 데스크톱 게임의 본 계층은 15개에서 60개 사이의 본을 사용합니다.
본의 수가 적을수록 퍼포먼스가 좋아집니다.
30개 정도의 본으로 데스크톱 플랫폼에서는 아주 좋은 품질을 얻을 수 있고, 모바일 플랫폼에서도 비교적 좋은 품질을 얻을 수 있습니다.
이상적으로는 모바일 장치에서는 30개보다 적게 하고, 데스크톱 게임에서도 30개를 크게 초과하지 않도록 합니다.
폴리곤 갯수를 적게 사용
폴리곤 수는 필요한 품질 및 대상 플랫폼에 의존합니다.
모바일 장치는 메쉬 당 300에서 1500 폴리곤으로 좋은 결과를 얻을 수 있으며, 데스크톱 플랫폼에서의 이상적인 범위는 1500에서 4000 정도입니다.
반면, 게임에 많은 캐릭터가 있을 때 메쉬 당 폴리곤의 수를 줄일 필요가 있을 것 입니다.
예를 들어, Half Life 2는 캐릭터에 2500 - 5000 삼각형 수였습니다.
현재 PS3와 XBox 360의 최고 랭크 게임은 보통 캐릭터에 5000에서 7000 삼각형 수입니다.
포워드 및 역운동학을 분리
애니메이션이 임포트 될 때, 모델의 역운동학(IK) 노드는 포워드 운동학(FK)에 베이크(baked)된 결과로 Unity는 IK 노드가 전혀 필요 없습니다.
그러나 모델에 남아있으면 애니메이션에 영향을 미치지 않음에도 불구하고 CPU 오버 헤드가 발생합니다.
선호에 따라 Unity 또는 모델링 도구, 어느 것이든 중복 IK 노드를 제거할 수 있습니다.
이상적으로는 IK 및 FK 계층을 모델링 시에 별도로 보관하여 필요할 때 IK 노드를 쉽게 제거할 수 있도록 해야 합니다.
렌더링 통계 창(Rendering Statistics Window) 확인
Time per frame and FPS
하나의 게임 프레임을 처리 및 렌더링하는데 걸린 시간(프레임/초)
이 숫자는 프레임 업데이트 및 게임 뷰의 렌더링에 걸린 시간만 포함되는 것에 유의
에디터가 씬 뷰 인스펙터의 그리기 및 에디터 전용 작업을 수행하는데 걸린 시간은 포함되지 않음
Batches
“Batching” is where the engine attempts to combine the rendering of multiple objects into a chunk of memory in order to reduce CPU overhead due to resources switching.
Saved by batching
Number of batches that was combined. To ensure good batching, you should share materials between different objects as often as possible.
Changing rendering states will break up batches into groups with the same states.
Tris and Verts 그려진 삼각형과 정점의 수. 이것은 optimizing for low-end hardware에서 중요합니다.
Screen 화면 크기, 안티 앨리어싱 레벨 및 메모리 사용량.
SetPass 렌더링 패스의 수. 각 패스에 대해서 유니티 런타임은 CPU 오버헤드를 가져올 수 있는 새로운 쉐이더를 바인딩한다.