반응형


Object.Destroy

Other Versions
public static void Destroy(Object obj, float t = 0.0F);

Parameters

objThe object to destroy.
tThe optional amount of time to delay before destroying the object.

Description

Removes a gameobject, component or asset.

The object obj will be destroyed now or if a time is specified t seconds from now. If obj is a Component it will remove the component from the GameObject and destroy it. If obj is a GameObject it will destroy the GameObject, all its components and all transform children of the GameObject. Actual object destruction is always delayed until after the current Update loop, but will always be done before rendering.

using UnityEngine;

public class ScriptExample : MonoBehaviour { void DestroyGameObject() { Destroy(gameObject); }

void DestroyScriptInstance() { // Removes this script instance from the game object Destroy(this); }

void DestroyComponent() { // Removes the rigidbody from the game object Destroy(GetComponent<Rigidbody>()); }

void DestroyObjectDelayed() { // Kills the game object in 5 seconds after loading the object Destroy(gameObject, 5); }

// When the user presses Ctrl, it will remove the // BoxCollider component from the game object void Update() { if (Input.GetButton("Fire1") && GetComponent<BoxCollider>()) { Destroy(GetComponent<BoxCollider>()); } } }

Destroy is inherited from the UnityEngine.Object base class. Javascript users should consider making a call to UnityEngine.Object.Destroy, rather than Object.Destroy to avoid references being resolved to the .Net System.Object class.


반응형
반응형

Unity JIT Spike (JIT 강제로 컴파일 하기) Just In Time




[방법.1]


var preJitCompile =typeof(MyComponent).GetMethod("MethodName");


//preJitCompile != null 체크 생략


preJitCompile.MethodHandle.GetFunctionPointer();




[방법.2]


using System.Reflection;



var method = typeof(A).GetMethod("MethodName", BindingFlags.NonPublic | BindingFlags.Instance);







회사 프로젝트를 최적화 하던 중 몇몇 함수에서 메모리 할당이나 테이블 로드등의 


최초 수행 시 시간이 소모되는 작업이 전혀 없음에도

함수의 최초 1번째 수행시간과 2번째부터의 수행시간이 10:1 이상 차이나는것을 보고 검색과 질문을 해보던 중

JIT Spike라는 개념을 찾았습니다.



- 인터프리터 방식에서 최초 번역시에 만들어진 기계어를 캐싱해 놓는 인터프리터와 컴파일 방식의 혼용입니다.


JIT Spike란 최초번역과 기계어를 캐싱되는 시간에 의한 딜레이 발생을 의미합니다.

해결방법으로는 특정 코드를 미리 캐싱되게 하는 것으로 3가지의 방법이 있는데

1. system.runtime.compilerservices.runtimehelpers.preparemethod("methodname")

 - 이것은 Mono에선 작동하지 않으며 
   (관련 링크- http://www.slideshare.net/flashgamm/unity-internals-memory-and-performance  35, 36페이지)
   Method.MethodHandle.GetFunctionPointer()를 권장하고 있습니다.


2. Method.MethodHandle.GetFunctionPointer()

 - 포럼의 글을 찾다보니 
   효과가 있다고 나왔지만
   에디터&안드로이드 세팅을 한 상태에서 사용 시 수행시간의 변화를 찾지 못했습니다.
   (휴대폰에서 로그를 확인 할 수 있는 플러그인을 사용해 PC에선 변화가 없어도 휴대폰에선 변화가 있는지 보려고 했으나 
    최근에 빌드에 계속 문제가있어서 확인해보지 못했습니다.)

3. fake 로직 수행

 - 최초전투 발생시 렉이 생기는것이 문제였으므로 fake parameter등으로 가짜로 먼저 로직을 수행해놓게 했습니다.


현재 저는 3번방식으로 해결한 상태입니다.

PC에서 확인시에는 그 차이가 10:1임에도 시간 자체가 매우 작지만

휴대폰에서는 수행시간의 차이가 체감되는 수준이었습니다.

안드로이드에서만 적용되며 IOS는 작동방식이 다르므로 신경쓸 필요가 없습니다.

틀린부분도 있을 수 있지만 혹시 도움되는 분들이 있으실 것 같아서 용기내서 올립니다.





---- 이 이하는 2번방법에 관심이 있으신 분들만 보시면 됩니다 ----

현재진행형인 부분으로 저는 typeof(클래스네임).GetMethods() (반환값 MethodInfo[])를 사용해서 

휴대폰에서 작동이 잘 되는지 확인해보려고 하고있습니다.

이때 주의점은 MethodInfo.DeclaredType 이 UnityEngine.Component인 함수를 캐싱하려고하면 유니티가 죽어버리고

또한 오버라이딩 함수의 경우에도 죽게 되 있습니다.

때문에 GetMethods를 사용해서 이름을 얻어와 오버라이딩 함수의 이름을 제거 후 DeclaredType은 if문으로 걸러낸뒤에

적용시키는 코드를 작성해놓았습니다. 빌드문제가 해결되는대로 테스트해볼 예정입니다.


http://lab.gamecodi.com/board/zboard.php?id=GAMECODILAB_Lecture&page=1&sn1=&divpage=1&sn=off&ss=on&sc=on&select_arrange=hit&desc=asc&no=421

반응형
반응형






using UnityEngine; using UnityEditor;

public class SimpleRecorder : EditorWindow { string fileName = "FileName";

string status = "Idle"; string recordButton = "Record"; bool recording = false; float lastFrameTime = 0.0f; int capturedFrame = 0;

[MenuItem ("Example/Simple Recorder")]         //상단 메뉴 툴바에 Example 서브 메뉴로 Simple Recorder 메뉴가 생김 static void Init () { SimpleRecorder window = (SimpleRecorder)EditorWindow.GetWindow(typeof(SimpleRecorder)); }

void OnGUI () { fileName = EditorGUILayout.TextField ("File Name:", fileName);//File Name 레벨 과 파일명 얻어옴

if(GUILayout.Button(recordButton)) {                           //Record 버튼 생성 및 이벤트 가져옴

if(recording) { //recording status = "Idle..."; recordButton = "Record"; recording = false; } else { // idle capturedFrame = 0; recordButton = "Stop"; recording = true; } } EditorGUILayout.LabelField ("Status: ", status);             //Status: 라벨 추가 }

void Update () { if (recording) { if (EditorApplication.isPlaying && !EditorApplication.isPaused){ RecordImages(); Repaint();                                //에디터 custom window 에 변경 사항이 생길경우 다시 그려주는 작업 } else status = "Waiting for Editor to Play"; } }

void RecordImages() { if(lastFrameTime < Time.time + (1/24f)) { // 24fps status = "Captured frame " + capturedFrame; Application.CaptureScreenshot(fileName + " " + capturedFrame + ".png");        //현재 실행화면 캡쳐 capturedFrame++; lastFrameTime = Time.time; } } }

https://docs.unity3d.com/ScriptReference/EditorWindow.OnGUI.html

반응형
반응형




테스트한 기기에 따라 결과가 다를 수 있음

[윈도우 에디터]
Application.persistentDataPath : 사용자디렉토리/AppData/LocalLow/회사이름/프로덕트이름
파일 읽기 쓰기 가능
Application.dataPath : 프로젝트디렉토리/Assets
Application.streamingAssetsPath : 프로젝트디렉토리/Assets/StreamingAssets
파일 읽기 쓰기 가능


[윈도우 응용프로그램]
Application.persistentDataPath : 사용자디렉토리/AppData/LocalLow/회사이름/프로덕트이름
파일 읽기 쓰기 가능
Application.dataPath : 실행파일/실행파일_Data
Application.streamingAssetsPath : 실행파일/실행파일_Data/StreamingAssets
파일 읽기 쓰기 가능

[맥 에디터]
Application.persistentDataPath : 사용자디렉토리/Library/Caches/unity.회사이름.프로덕트이름
파일 읽기 쓰기 가능
Application.dataPath : 프로젝트디렉토리/Assets
Application.streamingAssetsPath : 프로젝트디렉토리/Assets/StreamingAssets
파일 읽기 쓰기 가능

[맥 응용프로그램]
Application.persistentDataPath : 사용자디렉토리/Library/Caches/unity.회사이름.프로덕트이름
파일 읽기 쓰기 가능
Application.dataPath : 실행파일.app/Contents
Application.streamingAssetsPath : 실행파일.app/Contents/Data/StreamingAssets
파일 읽기 쓰기 가능

[웹 플랫폼]
웹에서는 명시적인 파일 쓰기 불가능. 애셋번들로 해야함
Application.persistentDataPath : /
Application.dataPath : unity3d파일이 있는 폴더
Application.streamingAssetsPath : 값 없음.

[안드로이드 External]
Application.persistentDataPath : /mnt/sdcard/Android/data/번들이름/files
파일 읽기 쓰기 가능
Application.dataPath : /data/app/번들이름-번호.apk
Application.streamingAssetsPath : jar:file:///data/app/번들이름.apk!/assets 
파일이 아닌 WWW로 읽기 가능

[안드로이드 Internal] 
Application.persistentDataPath : /data/data/번들이름/files/
파일 읽기 쓰기 가능
Application.dataPath : /data/app/번들이름-번호.apk
Application.streamingAssetsPath : jar:file:///data/app/번들이름.apk!/assets
파일이 아닌 WWW로 읽기 가능

[iOS]
Application.persistentDataPath : /var/mobile/Applications/프로그램ID/Documents 
파일 읽기 쓰기 가능
Application.dataPath : /var/mobile/Applications/프로그램ID/앱이름.app/Data
Application.streamingAssetsPath : /var/mobile/Applications/프로그램ID/앱이름.app/Data/Raw 
파일 읽기 가능, 쓰기 불가능


http://memocube.blogspot.kr/2014/04/blog-post.html







유니티 예약 폴더이름 



유니티 에셋 폴더의 예약된 이름들

유니티에서 몇몇 폴더 이름은 특별한 속성을 가지고 있다.

숨겨진 폴더들

점(.)으로 시작하는 폴더(예. “.UnitTests/”, “.svn/”)는 유니티에 의해 무시된다. 이 폴더에 들어 있는 어떠한 에셋도 임포트되지 않으면 어떠한 스크립트도 컴파일되지 않는다. 또한 프로젝트 뷰에서도 보이지 않는다.

“Standard Assets”

이곳에 위치한 스크립트들은 항상 가장 먼저 컴파일된다. 스크립트는 언어에 따라 Assembly-CSharp-firstpass, Assembly-UnityScript-firstpass, Assembly-Boo-firstpass 중의 하나로 출력된다.

Standard Assets 폴더의 스크립트들은 다른 스크립트들보다 먼저 컴파일 된다. 그로므로 스크립트를 Standard Assets 폴더에 두는 것은 C# 스크립트가 .js 스크립트에 접근할 수 있게 하거나 또는 .js 스크립트가 C# 스크립트에 접근할 수 있게 하기를 위한 한 방법이다.

“Pro Standard Assets”

Standard Assets과 같지만 이곳의 파일들은 Pro 버전에서만 의미가 있다. 이는 여기에 있는 에셋들은 Render Texture나 Screen-space 효과 등과 같은 Pro-only 기능에서만 사용된다는 것을 의미한다.

이 곳의 스크립트들은 먼저 컴파일되며, Pro Standard Assets 폴더 밖의 다른 스크립트(어떠한 언어라도)에서 접근이 가능하다.

“Editor”

Editor는 여러분이 작성한 스크립트가 유니티 에디터 스크립팅 API에 접근할 수 있도록 허가해주는 특별한 폴더 이름이다. 만약 여러분의 스크립트가 UnityEditor 네임스페이스의 클래스나 기능을 사용한다면, Editor라는 폴더 안에 위치해야 한다.

Editor 폴더 안의 스크립트들을 게임 빌드에는 포함되지 않을 것이다. 오직 유니티 에디터에서만 사용된다. 프로젝트에 여러개의 Editor 폴더가 존재해도 된다.

주의: 특수 폴더에 위치하지 않은 Editor 폴더는 프로젝트 어디에서 위치/포함 될 수 있다. 하지만 “Standard Assets”, “Pro Standard Assets”, “Plugins” 안에 위치할 경우, 이 폴더들의 직접적인 자식이어야 한다. 그렇지 않으면 정상적으로 처리되지 않는다. 예를 들어 “My Entension/Scripts/Editor”는 OK이다. 하지만 특수 폴더에 위치할 경우 반드시 ‘Standard Assets/Editor/MyExtension/Scripts”, 또는 “Pro Standard Assets/Editor/My Extension/Scripts”, 또는 “Plugins/Editor/My Extension/Scripts”이어야 한다.

“Plugins”

“Plugin” 폴더는 스크립트에서 접근하기를 원하는 네이티브 플러그인들이 위치하는 곳이다. 이것들은 자동으로 빌드에 포함될 것이다. 이 폴더는 어떠한 다른 폴더 안에 들어가 있으면 안된다( Assets 폴더 최상위에 위치해야 한다)

Windows에서는 네이티브 플러그인들이 .dll 파일들로 존재한다. Mac OS X에서는 .bundle 파일들로 존재한다. Linux에서는 .so 파일들로 존재한다. Standard Assets 폴더와 같이 이 곳의 스크립트들은 먼저 컴파일되며, Plugins 폴더 밖의 스크립트(어떠한 언어라도)에서 접근이 가능하다.

“Plugins/x86”

만약 32-bit나 유니버셜(32와 64 bit 모두) 플랫폼으로 빌드를 하고, 이 하위 폴더가 존재한다면, 이 폴더의 모든 네이티브 플러그인 파일들이 자동으로 빌드에 추가 될 것이다. 만약 이 폴더가 존재하지 않는다면, 유니티는 대신 부모인 Plugin 폴더에서 네이티브 플러그인들을 찾을 것이다.

“Plugins/x86_64”

만약 64-bit이나 유니버셜(32와 64 bit 모두) 플랫폼으로 빌드를 하고, 이 하위 폴더가 존재한다면, 이 폴더의 모든 네이티브 플러그인 파일들이 자동으로 빌드에 추가 될 것이다. 만약 이 폴더가 존재하지 않는다면, 유니티는 대신 부모인 Plugin 폴더에서 네이티브 플러그인들을 찾을 것이다.

만약 유니버셜 빌드를 생성한다면, x86과 x86_64 하위 폴더를 모두 만드는 것을 권장한다. 그리고 32-bit와 64-bit 버전의 네이티브 플러그 인을 적절한 하위 폴더에 넣어 준다.

“Plugins/Android”

Java-based 플러기인에 사용될 안드로이드 프로젝트에 포함시키고 싶은 Java .jar 파일들을 이곳에 위치시킨다. 모든 .so 파일(Android NDK-based 플러그인들)도 역시 포함될 것이다. See http://docs.unity3d.com/Documentation/Manual/PluginsForAndroid.html

“Plugins/iOS”

생성된 Xcode 프로젝트에 어떠한 .a, .m, .mm, .c 또는 .cpp 파일들을 자동으로 (심볼릭 링크로써) 추가하는 제한적이고 간단한 벙법이다. See http://docs.unity3d.com/Documentation/Manual/PluginsForIOS.html

만약 Xcode 프로젝트에 자동으로 파일들을 추가하는 방법에 대해 더 많은 제어를 하고 싶다면, PostprocessBuildPlayer 기능을 사용해야 한다. 이렇게 하는 것은 파일들을 Plugins/iOS 폴더에 위치시키지 않아도 된다. See http://docs.unity3d.com/Documentation/Manual/BuildPlayerPipeline.html

“Resources”

Resources 폴더는 스크립트에서 일반적인 (그리고 권유하는) 직접 참조 (유니티 에디터에서 드래그&드롭하는 스크립트의 변수 등) 하는 대신 파일 경로나 이름으로 에셋들에 접근할 수 있게 해주는 특수 폴더이다.

이러한 이유로 사용할 때 주의 사항이 있다. Resources 폴더의 모든 에들은 비록 사용되지 않더라도 여러분의 빌드에 포함된다. 그 이유는 이 에셋들은 스크립트에 의해 사용되기 때문에 유니티는 리소스 기반의 에셋들이 사용되는지 사용되지 않는지 판단할 수 없기 때문이다.

프로젝트에 여러개의 Resources 폴더를 가질 수 있다. 한 Resources 폴더의 어떠한 이름을 가지는 에셋을 넣어두고 또 다른 Resources 폴더에 같은 이름을 가지는 에셋을 넣어두는 것은 추천하지 않는다.

게임이 한번 빌드되고 나면 모든 Resources 폴더의 모든 에셋들이 에셋을 위한 아카이브로 패킹된다. 이는 기술적으로 최종 빌드에서는 더이상 Resources 폴더가 존재하지 않는 다는 것을 의미한다. 비록 여러분의 코드에서 이들을 마치 프로젝트에 존재하던 경로를 통해 접근하긴 하지만 말이다.

에셋들이 MonoBehavior 스크립트의 변수로 접근될 경우, 이 에셋들은 MonoBehaviour 스크립트가 Instantiate될 때 (즉, 게임 오브젝트나 프리팹이 씬에 존재하게 될 때) 메모리에 한번에 로드 된다는 것을 주목하자. 만약 에셋이 너무 크고 이것이 메모리에 로딩될 때 여러분이 이에 대한 더 많은 제어를 하고 싶다면 이렇게 동작하기를 원치 않을 것이다.

그럴 경우 큰 에셋들을 Resources 폴더에 넣고 Resources.Load를 통해 불러오는 것을 고려해보자. 에셋이 더 이상 사용되지 않을 경우에는 오브젝트에 대해 Object.Destory를 호출한 후 Resources.UnloadUnusedAsset를 호출함으로써 메모리를 해제할 수 있다.

“Editor Default Resources”

이 폴더는 Resources 폴더와 유사하지만 에디터 스크립트들 에서만 의미를 가진다. 만약 에디터 플러그 인에 에셋들(예를 들어 아이콘, GUI 스킨 등)을 로드해야 하지만 빌드에는 포함되어야 하지 않다면 이 폴더를 사용해라 (이러한 파일들은 그냥 Resources 폴더에 넣는다면 빌드에도 포함된다는 것을 의미한다).

에디터 스크립트들은 MonoBehavior 스크립트가 아니기 때문에 에셋에 접근하는 일반적인 방법(즉, 인스펙터를 통한 드래그 & 드랍)을 사용할 수 없다. “Editor Default Resources”는 이를 위한 편리한 방법이다.

“Editor Default Resources”안에 위치한 에셋들에 접근하기 위해서는 EditorGUIUtility.Load를 사용해야 한다.

Resources.Load와 달리 EditorGUIUtility.Load는 로드 하려고 하는 에셋의 파일 이름 확장자를 명시할 필요가 있다. 그러므로 “myPlugin/mySkin” 대신 “myPlugin/mySkin.guiskin”이어야 한다.

EditorGUIUtility.Load에 의해 사용된 메모리를 해제하기 위해서는 오브젝트에 대해 Object.Destroy를 호출한 후 EditorUtility.UnloadUnusedAssets를 호출하라.

알고 있는 바로는 오직 하나의 “Editor Default Resources” 폴더만이 존재할 수 있으며, Assets 폴더 바로 아래에 위치해야 한다.

“Gizmos”

Gizmos.DrawIcon에 사용되는 모든 텍스쳐/아이콘들을 가지는 폴더. 이 폴더의 텍스쳐 에셋들은 이름으로 불려질 수 잇으며, 에디터에서 기즈모로써 화면에 그려진다.

“WebPlayerTemplates”

웹 빌드에 사용되는 디폴트 웹페이지를 교체하기 위해 사용된다. 여기에 위치하는 스크립트들은 전혀 컴파일 되지 않을 것이다. 이 폴더는 Assets 폴더의 최상위에 위치해야 한다.

“StreamingAssets”

이곳에 위치한 파일들은 어떠한 변경도 없이 빌드 폴더에 복사된다(최종 빌드 파일에 포함되어야 하는 모바일과 웹빌드는 제외). 이들의 경로는 플랫폼 마다 다를 수 있으며 Application.streamingAssetsPath((http://docs.unity3d.com/Documentation/ScriptReference/Application-streamingAssetsPath.html))를 통해 접근할 수 있다. Also see http://docs.unity3d.com/Documentation/Manual/StreamingAssets.html


link # 1 : http://wiki.unity3d.com/index.php/Special_Folder_Names_in_your_Assets_Folder

link # 2 : http://www.devbb.net/viewtopic.php?f=37&t=1191

반응형
반응형





일정 loop 에 대한 시간을 측정 한다고했을 경우 JIT 컴파일은 


첫 실행에서만 효과를 누릴 수 있는 만큼 평균치에선 효과가 드러나지 않는다




JIT 컴파일(just-in-time compile) 또는 동적 번역(dynamic translation)은 프로그램을 실제 실행하는 시점에 기계어로 번역하는 컴파일 기법이다. 이 기법은 프로그램의 실행 속도를 빠르게 하기 위해 사용된다.

전통적인 입장에서 컴퓨터 프로그램을 만드는 방법은 두가지가 있는데, 인터프리트 방식과 정적 컴파일 방식으로 나눌 수 있다. 이 중 인터프리트 방식은 실행 중 프로그래밍 언어를 읽어가면서 해당 기능에 대응하는 기계어 코드를 실행하며, 반면 정적 컴파일은 실행하기 전에 프로그램 코드를 기계어로 번역한다.

JIT 컴파일러는 두 가지의 방식을 혼합한 방식으로 생각할 수 있는데, 실행 시점에서 기계어 코드를 생성하면서 그 코드를 캐싱하여, 같은 함수가 여러 번 불릴 때 매번 기계어 코드를 생성하는 것을 방지한다.

최근의 자바 가상 머신에서는 JIT 컴파일을 지원한다. 즉, 자바 컴파일러가 자바 프로그램 코드를 바이트코드로 변환한 다음, 실제 바이트코드를 실행하는 시점에서 가상 머신이 바이트코드를 기계어로 변환한다.


http://linuxism.tistory.com/258




좀 더 Jit 에 대한 자세한 설명




1. 닷넷의 이해, 컴파일과 실행

 

1.1 닷넷 (.NET)

1.1.1 C#이란

▣ Visual Basic

​  ◈ 프로그램을 쉽고 빠르게 할 수 있었다.

​  ◈ 문제점

    - 완벽한 객체지향을 지원해주지 않았다.

    - 다른 언어와의 호환성 지원이 미흡했다.

​▣ C#의 가장 큰 이슈

  ​◈ 더 쉽고 빠르게 프로그램하는 것이다.

​  ◈ C#에서 할 수 없는 것을 완벽하게 지원해주자는 것이다.

​​▣ C#의 파워

  ​◈ 2줄이면 C언어로 만들어진 함수 라이브러리를 사용할 수 있다.

​▣ 변화​

  ​◈ 많은 개발자들이 C#을 이해하지 못하고 있었기에 C#에 대한 혼돈 상태에 있었다.

  ◈ C#을 이해하기 시작하면서 C#은 MS의 대체 언어가 되고 있으며, 개발자의 주 개발 언어가 되고 있다.

1.1.2 닷넷의 의미와 특징

​▣ 닷넷이란(프로그래머 입장)

  ◈ 모든 것을 포괄하고 있는 이상적인 개발환경

▣ 이상적인 개발환경

  ◈ 닷넷은 개발에 필요한 개발 언어, 개발 툴, 라이브러리, 기반 기술 등을 제공하는 이상적인 개발환경이다.​

​​▣ 개발환경의 통일

  ◈ 개발자는 닷넷 환경하에서 다양한 언어로 개발

▣ 실행환경의 통일

  ◈ 다양한 언어로 개발된 프로그램을 하나의 방식으로 번역한 후 닷넷에서 하나의 방식으로 동작

▣ C#​​

  ◈ 닷넷을 대표하는 언어

1.1.3 닷넷 프레임웍(.NET Framework)

1.1.4 닷넷의 특징

▣ 플랫폼 독립성(Platform Independent)

  ◈ 닷넷 프레임웍만 설치되어 있다면 어떠한 운영체제에서도 닷넷 애플리케이션이 동작할 수 있다.

▣ 다양한 언어의 지원

  ◈ 개발자는 다양한 언어로 개발, 닷넷에서 하나의 방식으로 동작

  ◈ CLS(Common Language Specification)

    - 닷넷 프레임웍에서 동작하기 위해서 언어들이 지켜야 하는 표준 스펙

▣ 상호 운용성(Interoperability)

  ◈ 서로 다른 언어끼리의 호환성

    - C#에서의 COM 사용

    - C#에서의 Win32 API 사용

  ◈ 닷넷 지원 언어들끼리의 호환성

1.2 ​닷넷 언어의 핵심

​1.2.1 중간언어

1.2.2 동적 컴파일러

​▣ 중간언어(Intermediate Language)

  ◈ 기계어로 변환하기 쉬운 상태의 중간 단계의 언어

  ◈ 닷넷에서 번역되고 실행되기 위해서는 중간언어 형태로 컴파일되어야 한다. 

▣ 중간언어의 단점

  ◈ 완전한 기계어가 아니기 때문에 언젠가는 완전한 기계어로 번역해야 한다.

  ◈ 속도가 느리다.​

▣ 중간언어의 장점

  ◈ 중간언어를 기계어로 번역하는 번역기만 제공된다면 어떠한 플랫폼에서도 실행 가능하다.​

▣ 중간언어와 동적 컴파일​​​ 

  ◈ 중간언어

     - C#의 최종 결과물(컴파일한 결과파일)

  ◈ 재컴파일

     - 중간언어는 완벽한 기계어로 컴파일되어야만 실행될 수 있다.

  ◈ JIT(Just In Time Compiler)

     - 중간언어를 동적으로 컴파일하는 컴파일러


1.2.3 CLR







1.2.4 컴파일과 실행

​▣ 닷넷의 컴파일과 실행

  ◈ 컴파일

    - 중간언어(IL) 형태로 만든다.

  ◈ 실행

    - .NET 환경의 CLR은 IL형태의 언어를 JIT가 재컴파일한 후 시행시킨다.

​▣ C#의 어셈블리

  ◈ 중간언어 형태의 .exe나 .dll파일들을 C#에서는 어셈블리(Assembly)라고 한다.

​▣ 어셈블리의 종류 ​

  ◈ 사설 어셈블리

    - Copy & Paste 개념의 프로그램

    - 복사해서 사용하면 된다.

  ◈ 공용 어셈블리

    - 레지스터 개념에서 발전된 형태

    - 공용 어셈블리를 등록하는 것은 레지스터와 동일하다.

    - 버전별로 독립적으로 관리하며 복사버젼을 보관한다.

    - CLSID 대신 디지털 서명 방식으로 관리한다.   ​

1.3 .NET과 언어​

1.3.1 .NET과 C#

▣ .NET과 C#의 호환

  ◈ .NET을 위해서 새롭게 만들어진 언어

  ◈ .NET의 특징은 곧 C#의 특징이다.

▣ C#언어의 특징

  ◈ 기존 언어들의 장점만을 살려서 만든 언어

  ◈ C#은 C++의 문법을 표준화하고 정리하고 발전시킨 언어이다.

  ◈ C#에서 C문법을 사용할 수 있다. (포인터)

  ◈ C#에서 기존의 COM 컴포넌트를 사용할 수 있다.

▣ C#의 라이브러리​

  ◈ XML.NET

  ◈ ADO.NET

  ◈ ASP.NET

  ◈ XML Web Services

​1.3.2 C#과 자바



[출처] [C#] 1.닷넷의 이해, 컴파일과 실행


반응형
반응형




VSync는 모니터의 화면 갱신에 맞추는 것인데

이게 생각외로 프로세스를 잡아 먹는다

 

 

첫번째 사진은 VSync옵션을 킨 상태인데 

보시다시피 대부분의 프로세스를 VSync가 사용하고 있다.



 

두번째 사진은 VSync옵션을 끈 상태인데

FPS가 2배로 오르고 그래프자체도 더 보기 편해졌다.

 

 

이제 이 옵션을 끄는 방법인데

Edit > ProjectObtion > QualitySetting 을 눌러보면

아래쪽에 VSync Count라는 항목이 있는데 Dont Sync로 바꿔주면 된다

 

 

 

아이폰의 경우 VSync를 기기에서 자체적으로 해주기 때문에

처음부터 Dont Sync를 해주면 되고

 

안드로이드의 경우 자체적으로 해주지 않아서 

키던 안 키던 그건 개발자가 결정하면 될듯 하다

 

 

참고로 이 옵션을 끄게 되면 베터리 소모가 많아 진다고 한다 ㅇㅇ

 

 

깨알팁 

- 맨위 옵션도 최적화에 도움이 되는 건데 그냥 텍스쳐의 질을 결정하는 거라 보면 된다.



반응형
반응형

MonoBehaviour

유니티 상에서 스크립트를 생성하게 되면 해당 클래스는 항상 MonoBehaviour의 상속을 받게 만들어진다.

기본 스크립트를 생성하면 MonoBehaviour를 상속받는 것을 알 수 있다.
MonoBehaviour는 Behaviour를 상속받는다

위와 같이 상속받는 대상을 파고 들어가본다면 
MonoBehaviour -> Behaviour -> Component -> Object 로 상속형태가 이루어져있다.
그리고 MonoBehaviour를 상속받지 않는 스크립트를 inspector창에서 컴포넌트로 붙힐 수는 없다.

컴포넌트로 붙히려면 이런 Error가 뜬다

또한 MonoBehaviour를 상속받는 클래스는 코드상 new로 할당 받을 수 없다.
아래의 코드가 쓰여진다면 newTest는 null이 들어간다.

public Class Test : MonoBehaviour { } Test newTest = new Test();

그리고 MonoBehaviour에는 Unity의 중요하며 기본적인 함수들을 지원한다.
 - Awake() : Start()함수와 같이 실행될 때 제일 먼저 한번만 호출된다. 게임오브젝트가 비활성화 상태라도 호출이 된다.
 - Start() : Awake()함수와 같이 실행될 때 Awake()함수 뒤에 한번만 호출된다. 하지만 비활성화 상태라면 호출되지 않는다.
 - Update() : 유니티 내부의 프레임 속도에 맞춰 매 프레임마다 한번씩 호출되는 함수이다.
 - LateUpdate() :  Update()함수 뒤에 호출되는 함수로서 Update()함수와 마찬가지로 프레임마다 한번씩 호출된다.
 - FixedUpdate() : 프레임마다 진행되는 시간은 일정하지 않은데 이 함수는 고정된 일정 시간을 간격으로 한번씩 호출된다.

중요한 것은 만약 스크립트가 여러개 존재한다면 각 스크립트마다 Awake(), Start(), Update()함수들이 언제 호출되는지
정해져 있지 않기 때문에 신경써서 코딩을 해주어야 한다.

위의 함수들이 잘 실행이 되는지 확인해 보는 방법으로 Console창에 글을 띄우는 방법이 있다.
'Debug.Log("띄울 글")' 코드를 통해 Console창에 내용을 출력할 수 있다.

GameObject 이동방법
게임오브젝트의 transform에는 오브젝트의 위치값, 각도, 스케일등을 가지고 있다.
게임오브젝트를 이동시키려면 transform의 위치값을 변경해 주면 된다.
Start()함수나 Update()함수에 transform.position = new Vector3(0, 0, 5); 를 해준다면 z값이 5인 위치로 변경된다.
position에는 localPosition도 따로 존재하기때문에 로컬좌표를 변경하기 위해선 localPosition을 사용해주면 된다.
오브젝트의 각도를 조절하기 위해선 Quaternion을 사용해주면 된다.


데이터를 Import하는 방법
유니티 프로젝트를 생성하면 Assets 폴더가 자동 생성되는데 
외부에서 리소스 파일들을 받아오기위해선 이 폴더 안에 리소스들을 넣어주면 된다.

Unity 프로젝트 폴더 내부에 존재하는 Assets폴더

Assets폴더 내부에 리소스를 넣고 유니티 화면으로 전환하면 자동으로 Import 로딩이 뜨며 임포트가 완료된다.

Material 변경
만약 임포트한 파일의 텍스쳐가 마음에 들지 않아 사용자 임의로 바꾸기를 원한다면 material 파일을 만들어서
게임오브젝트에 컴포넌트로 넣어주면된다.

현재 Material을 넣어주기 전 plane의 색은 회색이다.
Plane의 색이 초록색으로 바뀌었다

Material을 만들고 원하는대로 변경한 후 변경할 게임오브젝트에 드래그만 해준다면 위의 두번째 사진처럼
Material이 New Material로 변화되는것을 확인할 수 있다.


Layer 추가 방법

게임오브젝트를 클릭하면 컴포넌트 위에 Tag란과 Layer를 볼 수 있다.
Layer를 설정하기 위해선 Add Layer... 를 선택하여 2번째 사진처럼 원하는 Layer 이름을 작성해준다.
그러면 바로 위 사진처럼 원하는 Layer 네임을 가지는 것을 볼 수 있다.



반응형
반응형

늦어서 죄송합니다. 그동안 잠시 다른 일이 생겨서 마지막 부분 업로드가 늦었습니다.
해당 글 원문은 http://unitygems.com/memorymanagement/에서 보실수 있습니다.
번역중에 이상한 부분은 댓글 남겨주시면 확인 후에 수정하겠습니다.
(저도 공부하면서 올리는 거라 미흡한 점이 많이 있습니다. 미흡한 부분이 있더라도 조금만 양해부탁드립니다^^;)


Static Variables
정적 변수


많은 초보자들은 이 간단한 실수를 한다:

1
2
3
4
public static int health;
void Start(){
    health=100;
}

다른 스크립트에서 우리는 다음과 같은 코드를 발견할 것이다.

1
2
3
4
5
6
void OnCollisionEnter(Collision other){
    if (other.gameObject.tag == “Enemy”){
        EnemyScript.health-=10;
        if(EnemyScript.health <=0)Destroy(other);
    }
}

그들은 적을 죽이려고 할 것이고, 그를 죽이는데 성공한다. 그러나 그때 더 많은 적들이 추가되고, 모두 갑자기 죽는것에 의아해 한다.

이유는 간단하다. 당신은 많은 enemy 클래스 인스턴스를 가지고 있다.(그것은 static이 아니다) 그러나 그들은 모든 적에 대해 오직 하나의 health 인스턴스를 가진다. 하나를 죽이는 것은 모두를 죽이는 것이다!

사실, 클래스에 선언된 정적 변수는 객체들에 속해 있는 것이 아니라 클래스 그 자체에 속해 있다.
이것이 바로 우리가 정적 변수에 접근할 때 인스턴스 이름이 아닌 클래스 이름을 사용하는 이유이다.

그것은 적의 체력에 대해서 올바르게 실행되지 않았다. 왜냐하면 각각의 적들은 그들 스스로의 health 변수를 가져야 하기 때문이다.

정적 클래스와 같은 방법으로, 정적변수를 선언한 클래스가 첫번째로 접근될 때 정적 변수는 인스턴스화된다.
이것은, 게임에서 클래스의 어떤 인스턴스도 없더라도, 우리가 정적변수에 접근하려고 할 때 정적 변수는 존재한다는 것을 의미한다.

적 카운터를 고려할 때, 우리는 게임 안에서 적들의 숫자를 추적하기를 원한다.

static var counter;
 
public void CreateEnemy(){
    GameObject obj=Instantiate(enemyPrefab, new Vector3(0,0,0).Quaternion.identity);
 
    if(obj)counter++;
}
 
public void DestroyEnemy(){
    Destroy(gameObject);
    counter--;
}

이 스크립트는 각 적들에게 첨부될 것이다. 모든 적들이 다른 스크립트를 가지고 있더라도, 모든 적들은 같은 counter 변수를 공유한다

우리의 게임에 이 함수를 사용하는 것은 우리가 계속해서 적들의 숫자를 추적 가능하게 한다.
Instantiate가 객체를 만드는 것을 실패했을 경우에(예를 들면, 힙 파편화로 인한), Instantiate는 null 참조를 반환하기 때문에, 참조 변수 사용에 주의해야 한다. 

만약 어떤 이유로 인스턴스 생성이 실패되었을 경우, 다음과 같은 if문의 확인없이 적 카운트를 증가시킨다면, 우리는 잘못된 counter를 가질 것이다.

지금 만약 다음과 같이, counter변수를 사용하여 레벨을 끝내고, 새로운 레벨을 불러오려고 한다면,
if(counter<=0)Application.LoadLevel(nextLevel);
만약 확인을 하지 않고 만약 인스턴스화를 실패했다면, 모든 적을 죽인 후에, 우리가 counter가 1이고 우리의 게임은 버그가 생긴 것이다.

우리의 플레이어는 어떤가, 정적 health를 사용할 수 있는가? 그렇다. 우리는 우리의 플레이어의 체력을 위해서 정적 변수를 사용할 수 있다. 사실 정적 변수에 접근하는 것은 심지어 인스턴스 맴버보다 더 효율 적이다.

인스턴스의 멤버를 호출할 때, 컴파일러는 객체의 존재를 확인하기 위한 작은 함수 요청이 필요하다. 왜냐하면 존재하지 않은 데이터를 수정할 수 없기 때문이다.


Static Functions
정적 함수

정적 함수는 비정적 클래스에서 구현될 수 있다. 이 경우에 있어서, 오직 그 클래스의 정적 변수만이 정적함수안에서 접근될 수 있다. 정적 함수는 인스턴스가 아닌 클래스를 통해 호출되기때문에, 
존재하는 것이 확신되지 않는 맴버에 접근하는 것은 문제가 될 수 가 있다.

반면에, 정적 함수에 매개변수를 전달할 수 있고, 당신은 아마 이런 함수를 꽤 많이 사용하고 있을 것이다.
1
2
3
4
5
6
Vector3.Distance(vec1.vec2);
vec1.Normalize();
 
Application.LoadLevel (1);
 
if (GUI.Button(Rect(10,10,50,50),btnTexture)){}

그 목록들을 다 말하기에는 너무 많다. 두번째 라인은 정적 함수가 아니다. 그것은 vector3타입의 vec1 인스턴스를 통해 호출된다. 그 밖의 예는, 클래스가 정적 함수를 호출하기위해 사용되는 것을 보여 준다.

정적 함수는 비정적함수보다 더 빠른 경향이 있다. 컴파일러는 인스턴스함수의 호출이후에 인스턴스가 존재하는지 확인을 하기위해 작은 오버헤드가 발생하기 때문이다. 정적 함수는 존재의 유무가 확실한 클래스를 사용한다. 그렇지만 시간의 이득은 작다.


정적 클래스의 또 다른 이점은 그들의 존재가 영구적이고, 컴파일러에게 알려져 있으며,
따라서 C#에서 정적 클래스는 확장메소드 를 정의하기 위해 사용되어 질 수 있다.
확장메소드는 다른 타입의 변수에 첨가된 추가적인 함수처럼 보인다.
이는 syntactic sugar이다. 이런 식으로 확장된 클래스는 사실 열려있지 않지만, 
(그들의 private과 protected 변수는 확장메소드에서 접근이 불가능하다.)
이는 겉보기에 기존의 객체에 새로운 함수를 주는 깔끔한 방법 - 코드의 가독성을 향상시키고, 개발자들에게 더 쉬운 API를 제공하는 - 이다.

예를 들어 표면상으로 다음과 같이 Transform에 새로운 함수를 추가할 수 있다.

transform.MyWeirdNewFunction(x);
확장 메소드의 예:


기존의 함수(OrderBy, Sort,...)를 확장하는 배열에 함수를 만들기를 원한다고 생각해보자.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
using UnityEngine;
using System.Collections;
 
public static class ExtensionTest {
 
    public static int BiggestOfAll(this int[] integer){
        int length = integer.Length;
        int biggest = integer[0];
        for(int i = 1;i<length;i++){
            if(integer[i]>biggest)biggest = integer[i];
        }
        return biggest;
    }
}


우리는 정적 함수를 정의한다. 전달된 매개변수가 어떻게 함수를 호출하는 인스턴스가 되는지를 봐라.
매개변수의 타입을 수정하는 것은 다른 객체에서 작동하도록 한다.


이제 선언해서 배열을 채워보자:

01
02
03
04
05
06
07
08
09
10
11
using UnityEngine;
using System.Collections;
 
public class Test : MonoBehaviour {
 
        int[] array = {1,25,12,120,12,6};
 
    void Start () {
        print(array.BiggestOfAll());
        }
}

배열 이름 뒤에 .(점)을 입력하면, 위에서 입력한 새로운 함수가 나타나는 것을 보게 될 것이다.

그들의 타입과 상관없는 다른 배열들을 받아들이는 더 제너릭한 함수를 만들기를 원한다면, 
정적 제너릭 확장 메소드를 구현해야 할 것이다.


01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
 
public static class ExtensionTest {
 
    public static T TypelessBiggestOfAll<T>(this T[] t){
        int length = t.Length;
        var biggest = t[0];
        for(int i = 1;i<length;i++){
            biggest =((Comparer<T>.Default.Compare(t[i], biggest)>0)?t[i]:biggest);
        }
        return biggest;
    }
}


using System.Collections.Generic의 추가를 주목해라. 컴파일러가 T타입이 무슨타입인지 알지 못하기 때문에, 우리는 간단하게  < 또는 >를 통해서 값을 비교할 수 없다. 우리는 Comparer<T>를 사용해야 한다.
예를 들어,  삼항연산자가 사용된다. 우선 약간 혼란스러운 코딩을 만드는 경향이 있지만, 또한 약간의 라인을 줄여준다.


삼항 연산자는 다름과 같에 생겼다.  a ? b : c;

a는 수행된다. 그리고 만약 참이면 b가 명령문에서 내보내진다. 만약 거짓을 경우 c가 보내진다. 
일반적인 if문과 비교해봤을 때 장점은 비교후 값을 리턴한다는데 있다. 
result = a ? b : c; result는 a에 의존하여 b 또는 c값을 받는다.
이제 다른 타입의 배열을 선언한 뒤 같은 함수를 사용하는 것이 가능하다.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
using UnityEngine;
using System.Collections;
 
public class Test : MonoBehaviour {
 
    int[] arrayInt = {1, 25, 12, 120, 12, 6};
    float[] arrayFl = {0.5f, 52.456f, 654.25f, 41.2f};
    double[]arrayDb = {0.1254, -15487.258, 654, 8795.25, -2};
 
    void Start () {
        print(arrayInt.TypelessBiggestOfAll());
        print (arrayFl.TypelessBiggestOfAll());
        print (arrayDb.TypelessBiggestOfAll());
       }
}

이는 값 120, 654.25, 8798.25를 출력한다.

이제 나는 당신에게 이것이 유용하다는 것을 보여주고싶다.  많은 유니티 유저는 객체 내부의 지역을 통제하는 방법에 대해서 묻는다. 예를 들어, 화면 내부에 객체를 유지하고 싶기를 원한다면, 당신은 이 함수를 사용하여 그 객체의 Transform을 고정해야 한다.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
using UnityEngine;
using System.Collections;
 
public static class GameManager{
 
    public static void ClampTransform(this Transform tr,Vector3 move, Vector3 min, Vector3 max){
        Vector3 pos = tr.position + move;
        if(pos.x < min.x ) pos.x = min.x;
        else if(pos.x > max.x) pos.x = max.x;
 
        if(pos.y  < min.y ) pos.y = min.y;
        else if(pos.y> max.y) pos.y = max.y;
 
        if(pos.z < min.z) pos.z = min.z;
        else if(pos.z > max.z) pos.z = max.z;
 
        tr.position = pos;
    }
}

그리고 당신이 필요한 어떤 곳이든 사용하면 된다:

1
2
3
Vector3 minVector = new Vector3(-10,0,-10);
Vector3 maxVector = new Vector3(10,0,10);
transform.ClampTransform(move,minVector,maxVector);

이 예에서,  객체는 (0, ,0, 0)을 중심으로한 20*20 사각형의 2D 환경에 제약이 걸려 있다.

Heap Fragmentation, Garbage Collection and Object Pooling
힙 파편화, 가비지 컬렉션, 오브젝트 풀링


힙은 데이터가 무작위로 저장되어 있는 큰 메모리 공간이다.  사실 그렇게 무작위는 아니다. OS는 메모리를 조사할 것이고, 요구된 데이터에 적합한 크기의 첫번째 메모리 지역을 찾을 것이다. 
힙의 크기와 위치는 당신이 선택한 플랫폼에 따라 달라진다.

예를 들어, 정수에 대해 요청할 때, OS 연속적인 4바이트의 메모리공간을 찾는다. 사실은, 프로그램이 실행될 때, 메모리 지역은 실제 규칙없이 사용되고, 해제된다. 그리고 당신의 프로그램은 힙 파편화가 발생될 것이다.


이 사진은 힙 파편화의 예를 보여준다. 명백하게 이것은 좀 지나치게 강조하고 있지만, 본질은 틀리지 않다.

어떤 데이터도 추가 되지 않았다는 것을 명심해라. state1에서의 같은 양의 데이터가 있다.
오직 움직임이 힘 파편화를 만들었다.



유니티는 .NET managed 메모리를 사용한다. C 프로그램에서 힙 파편화는 메모리 블럭을 할당하는 것이 불가능해지는 상황을 초래할 수 있다. 왜냐하면 사실 충분한 메모리 공간이 있더라도 적합한 크기의 인접한 블록이 없기 때문이다. managed 메모리는 이 제한으로 부터 영향을 받지 않는다. 만약 메모리 블록 발견되지 않는다면, 실제 .NET 메모리 관리와 가비지 컬렉션 시스템은 메모리를 움직임으로써 힙의 파편화를 제거할 수 있다. 유니티에서는 가비지 컬렉션의 실행으로 힙 파편화 현상이 일어나지 않는다.
한번에 하나 이상의 객체가 제거됨으로써 공간을 찾을 것이다. 그러나 이것은 그들이 적합할 때까지 블록을 움직이는 것만큼 효율적이지는 않다.
프로그래머의 해답은 오브젝트 풀링을 사용하는 것이다. state1에서 data3를 파괴하는 대신에 우리는 간단하게 나중의 사용을 위해서 그것을 비활성화 시키면 어떻게 될까? 아마 힙 파편화를 피했을 것이다.

우리는 data를 파괴하지 않는다. 우리는 그것을 잠자게 할 것이고, 필요할 때 우리는 그것을 깨울 것이다.

이 해법에 많은 장점이 있다. 우선 우리는 위에서 봤던 경우를 피할 것이고, 우리는 또한 Instantiate과 Destroy 함수 호출을 피할 것이다. 우리는 오직 gameobject.SetActiveRecursevely(true/false)를 사용하면 된다.

마지막으로 우리는 파괴하지 않기 때문에, 우리는 비용이 비싼 행위인 가비지 컬렉터를 호출하지 않는다.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
ObjectScript.cs
using UnityEngine;
using System.Collections;
 
public class Test : MonoBehaviour {
    public GameObject prefab;
    GameObject[] objectArray = new GameObject[5]; 
        public static int counter;
 
         void Start(){
        for(int i = 0;i<objectArray.Length;i++){
            objectArray[i] = (GameObject)Instantiate(prefab,new Vector3(0,0,0),Quaternion.identity);
            objectArray[i].SetActiveRecursively(false);
        }
        }
        void Createobject(){
        if(counter < objectArray.Length){   
                // iterate through array
            for(int i = 0;i<objectArray.Length;i++){ 
                                // Find an inactive object                                    
                if(!objectArray[i].active){   
                               // Increase the counter, activate the object, position it                                             
                    counter++;                                             
                    objectArray[i].SetActiveRecursively(true);            
                    objectArray[i].transform.position = new Vector3(0,0,0); 
                    return;
                }
            }return;
        }else return;
    }
}
1
2
3
4
void OnCollisionEnter(CollisionEnter){
    if(other.gameObject.tag=="Object"){
        other.gameobject.SetActiveRecursively(false);       //Deactivate the object
        ObjectScript.counter--;                                             //Decrease counter

이 씬에서는 한번에 절대 5개의 객체를 가지지 않는다. 1개가 비활성화 될 때, 우리 원하는 곳에 다시 재활성화 시킬 수 있다. 가비지 컬렉션은 필요하지 않는다.


만약 당신 앞에 나타나는 적들의 웨이브를 가진다면, 죽은 적들을 파괴하는 대신에, 당신의 적에 대해서 이 트릭을 사용할 수 있고, 그들을 다시 재위치 시킬 수 있다. 


탄약에서도 똑같이, 각각의 총알을 파괴하는 대신에 그것을 비활성화 시킨다.
만약 당신의 총 앞의 위치와 적절한 속도로 다시 발사할 때, 그것을 다시 활성화 시키면 된다.


Note that pooling at the memory level is a different process managed by the OS consisting in reserving always the same amount  of memory whatever size the object is. As a result there will never be tiny memory locations lost all over the memory.
메모리 단계에서 풀링은 객체의 사이즈가 얼마이든 간에, 항상 같은 양의 메모리를 확보하는 OS에 의해 관리되는 다른 프로세스이다.

Array Reuse
배열 재사용

객체를 재사용하는 것에 더하여, 우리는 각 호출 또는 같은 호출에서 반복적인 코드를 재사용하는 버퍼를 생성하는 것은 생각해볼 만한 가치가 있다.

01
02
03
04
05
06
07
08
09
10
11
12
void Update()
{
      if(readyToFire && Input.GetKeyDown(KeyCode.F))
      {
          var nearestFiveEnemies = new Enemy[5];
 
          //Do something to fill out the list finding the nearest
          //enemies
 
          TargetEnemies(nearestFiveEnemies);
      }
}

이 예제에서, 우리는 가장 가까운 5개의 적을 타겟팅 할 것이고, 자동적으로 그들에게 발사할 것이다.
문제는 발사 버튼을 누를 때 마다 메모리가 할당된다는 점이다. 대신에 만약 우리가 오랫동안 유지되는 적들의 배열을 만들었다면, 우리는 코드 어디에서든 사용할 수 있을 것이고, 우리는 메모리를 할당하지 않아도 되며 느려지는 현상을 피할 수 있을 것이다.

이것은 간단한 케이스가 될 수 있다:

01
02
03
04
05
06
07
08
09
10
11
12
Enemy[] _nearestFiveEnemies = new Enemy[5];
 
void Update()
{
      if(readyToFire && Input.GetKeyDown(KeyCode.F))
      {
          //Do something to fill out the list finding the nearest
          //enemies
 
          TargetEnemies(_nearestFiveEnemies);
      }
}

그러나 어쩌면 우리가 종종 적들의 배열이 필요할 때,  우리는 그것을 더 일반적으로 만들 수 있을 것이다.

01
02
03
04
05
06
07
08
09
10
11
12
Enemy[] _enemies = new Enemy[100];
 
void Update()
{
      if(readyToFire && Input.GetKeyDown(KeyCode.F))
      {
          //Do something to fill out the list finding the nearest
          //enemies
 
          TargetEnemies(_enemies, 5);
      }
}

카운트를 가지는 TargetEnemies에 대한 코드를 재작성함으로써, 우리는 효율적으로 우리의 코드 어떤 곳에서든 일반적인 적의 목적 버퍼를 사용할 수 있을 것이고, 훨씬 더 할당의 필요성을 피할 수 있을 것이다.


두번째 예제는 꽤 지나친 경우이다. 그리고 만약 실제로 메모리 문제를 야기시키는 거대한 집합을 가진다면 반드시 이것을 해야한다. - 일반적으로 우리는 언제나 읽기쉽고, 약간의 메모리를 아낄 수 있는 이해할 수 있는 코드를 작성해야 한다.



출처: http://unityindepth.tistory.com/14 [UNITY IN DEPTH]

반응형
반응형

원문이 약간 길어 3~4개로 나눠어서 올릴 예정입니다. 
해당 글 원문은 http://unitygems.com/memorymanagement/에서 보실수 있습니다.
번역중에 이상한 부분은 댓글 남겨주시면 확인 후에 수정하겠습니다.
(저도 공부하면서 올리는 거라 미흡한 점이 많이 있습니다. 미흡한 부분이 있더라도 조금만 양해부탁드립니다^^;)






Struct vs Class

그래서 어떤 상황에서 어느 것을 사용해야 하는가? 클래스를 사용하는 것은 추가적인 변수(참조변수)가 추가 된다는 것을 알았다. 만약 수천 개의 객체를 만든다면, 수천 개의 추가적인 변수를 얻을 것이다. 하지만 구조체는 이들을 생성하지 않는다.

밑의 예제를 보자:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public class DogC{
    public string name { get; set;}
    public int age { get; set;}
}
 
public struct DogS {
    public string name;
    public int age;
}
 
using UnityEngine;
using System.Collections;
 
public class Memory : MonoBehaviour {
    DogC dogC = new DogC();
    DogS dogS = new DogS();
 
    void Update () {
        if(Input.GetKeyDown(KeyCode.Alpha1))
            Print (dogS);
        if(Input.GetKeyDown(KeyCode.Alpha2))
            Print (dogC);
        if(Input.GetKeyDown(KeyCode.Alpha3))
            print ("Struct: The dog's name is "+dogS.name +" and is "+dogS.age);
        if(Input.GetKeyDown(KeyCode.Alpha4))
            print ("Class: The dog's name is "+dogC.name +" and is "+dogC.age);
     }
 
    void Print(DogS d){
        d.age = 10;
        d.name = "Rufus";
        print ("Struct:The dog's name is "+d.name +" and is "+d.age);
     }
 
    void Print(DogC d){
        d.age = 10;
        d.name = "Rufus";
        print ("Class:The dog's name is "+d.name +" and is "+d.age);
    }
}

우선, 세번째 입력은 우리가 예상했던 값을 출력하지 않을 것이다. 구조체의 name과 age는 값이 사라졌다. 
반면에 클래스는 값을 유지한다.
(역주: 유니티에서 테스트해보시면 쉽게 이해하실 수 있습니다. 업데이트문에 있는 순서대로 입력을 해보시기 바랍니다. 참고로 KeyCode.Alpha1은 숫자1키를 말합니다.)  

우리가 말했던 것을 기억해라. 클래스는 참조 타입이고 구조체는 값 타입이다. 구조체에 대해서 함수를 호출 할 때, 구조체의 복사본을 전달하지만, 그 복사본은 원래의 데이터가 아니다. 그 데이터를 수정할 때, 우리는 단지 스택에 있는 복사본의 데이터를 수정하는 것이다. 이들은 함수의 마지막 부분에서 사라진다.

클래스를 매개변수로 받는 함수는 원래의 데이터로 접근할 수 있는 참조값을 전달 받는다. 
그리고 함수안에서 수정도 가능하다. 함수가 끝나더라도 수정한 것은 그대로 유지된다.

우리가 생각해야 하는 것은 무엇인가? 만약 구조체가 10개의 변수를 포함할정도로 크다면, 우리가 함수에 구조체를 전달할 때 스택에는 10개의 공간이 생성될 것이다. 지금 내가 이 구조체 50개를 가지고 있는 구조체배열을 전달한다면 스택에는 500개보다 더 큰 공간이 발생할 것이다. 스택의 사이즈는 제한되어 있고, 만약 구조체와 배열의 크기가 더 크다면 스택 오버플로우가 발생할 수도 있다.



클래스를 전달 할 때는, 오직 하나의 변수만 전달한다. 50개의 클래스 인스턴스를 전달할 때는 스택에 50개의 변수에 대한 공간만 생성될 것이다.

종합해보면, 클래스는 메모리를 절약하지만 구조체는 대신 더 빠르다. 핵심은 2개의 균형을 알아내는 것이다. 
5개~8개의 멤버변수를 가질 때 구조체를 사용하는 것을 고려해라. 그 보다 위에는 클래스를 사용해라.


You must also bear in mind that every time you access a variable which is a structure (for example by doing transform.position) you are creating a copy of that structure on the stack - that copy takes time to create.
구조체 변수에 접근할 때마다(예를 들어 transform.position를 사용할 때) 스택에 구조체의 복사본을 만들고, 복사하는데도 시간이 든다는 것을 명심해야 한다.
(역주: 구조체 변수에 접근할 때마다 스택에 구조체의 복사본을 만든다는게 잘 이해가 안되서
원문과 같이 나뒀습니다. 혹시 이글을 보시고 부연설명이 가능하신분은 댓글로 좀 부탁드리겠습니다.)

클래스를 만들때 마다, 힙에 메모리를 할당할 것이다. 이는 빠른속도로, 버려진 클래스들을 위한 가비지 컬렉션 사이클로 이어진다. 구조체는 항상 스택에 할당되며 이런 이유로 가비지 컬렉션을 절대 유발시키지 않는다.

유니티에서 구조체는 종종 3~4개의 변수를 가진다. 
(예를 들어, position, color, quaternion, rotation, scale...이들 모두다 구조체이다)


오래가지 못하는, 자주 할당되는 객체는 구조체의 좋은 후보가 된다. 왜냐하면 이들은 가비지 컬렉션을 유발시키지 않기 때문이다. 오래가고 큰 객체들은 클래스의 좋은 후보들이다. 왜냐하면 그들은 접근될 때 마다 복사되지 않으며, 그들의 지속된 존재는 가비지 컬렉션을 유발시키지 않는다. 
Vector3, RayCastHit같이 많은 작은 객체들은 바로 이 이유때문에 유니티에서 구조체인 것 이다.
다음을 넘어가기전에 마지막 핵심 하나가 있는데,
함수에서 구조체를 수정하고 값을 계속 유지하기 위해서, 구조체를 참조로써 전달하는 것이 가능하다.


구조체 변수를 정의한 함수가 종료되었을 때 구조체에 대한 참조를 유지할 수 없다.이 같은 문제를 해결하기 위해 클로저의 사용(람다 함수 또는 인라인 함수에 의해서 생성된)처럼 많은 방법이 있지만 그것은 꽤 고급 기술에 속한다.
1
2
3
4
5
void PrintRef(ref DogS d){
    d.age = 10;
    d.name = "Rufus";
    print ("Structure:The dog's name is "+d.name +" and is "+d.age);
}

우리는 위에 것을 다음처럼 호출할 것이다:

1
2
3
4
void Update(){
    PrintRef(ref dogS);
    print ("Structure:The dog's name is "+dogS.name +" and is "+dogS.age);
}

당신은 키워드 ref에 대해서 알았다. 이것은 컴파일러에게 스택에 구조체를 복사하지 말고 구조체에 대한 참조를 만들라고 말하는 것이다. 함수 밖에서 구조체는 함수 안에서 받은 값들이 유지된다.
이 키워드는 어떤 값타입 형식과 사용될 수 있다.


Creating a Reference
참조 만들기


우리는 방금 전에 값타입 변수에 참조를 만드는 것이 가능하다는 것을 봤다. 우리는 함수 안에 정수형 변수를 만들고, 그것이 후에 사용되기 위해서 계속해서 유지되는 것을 원한다고 생각해보자.
간단하게, 함수내부에 변수를 선언하는 것은 함수의 끝에 사라지는 automatic 변수를 만드는 것이다.


우리는 동적으로 할당되는 변수를 만들 수 있다. 이는 참조와 함께 힙에 저장될 것이다.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
using UnityEngine;
using System.Collections;
 
public class Test:MonoBehaviour{
    int number;
 
    void Update () {
        if(Input.GetKeyDown (KeyCode.C))
            CreateVariable();
        if(Input.GetKeyDown (KeyCode.Space))
            PrintVariable(number);
    }
 
    void CreateVariable(){
         //int number = new int();
         number = new int();
         number = 10;
        print ("In function "+number);
    }
 
    void PrintVariable(int n){
        print (n);
        n+=10;
    }
}

첫 번째로, 스크립트에 전역으로 정수형 변수 number를 선언한다. 우리는 이 변수를 CreateVariable함수에서 사용하고, new int()를 사용하여 number에 정수형 변수 참조를 대입한다.



이 상황을 명확히 하기 위해서, new 키워드는 힙영역에 만들어진 변수에 대한 참조를 반환한다.
이 참조는 number 변수안에 저장되고, 이제 number 변수는 새롭게 생성된 변수의 주소를 가지고 있다. number를 사용하는 것은 간접적으로 이 변수를 사용하는 것이다. 우리가 새롭게 생성한 변수는 함수 밖에서도 여전히 존재한다.










주석으로 된 부분을 고려해보면, (//int number = new int();)
만약 이 같은 방법으로 사용했다면, 우리는 함수의 끝에서 참조를 잃었을 것이다.
다음 청소 라운드에서, 가비지 컬렉터는 참조가 없는 데이터(함수 안에 선언된 number)를 찾을 것이고 그것을 메모리로부터 제거할 것이다.
이 같이 우리가 함수 내부에서 했던 것처럼 지역 변수를 선언하는 것을 생각해 보면, int number은 전역 int 
number 변수를 숨긴다는 것을 알 수 있다.


참조안에서 int같은 원시 변수를 유지할 때,  컴파일러는 boxing(박싱)이라고 불리는 함수를 수행한다. 이는 기본적으로 원시 변수를 클래스 안으로 싸는 것이다.
이런 식으로 선언된 변수는 힙에 할당되고, 이들이 해제될 때 가비지 컬렉션이 발생하도록 한다. 이들은 기본적인 원시 변수보다 더 많은 메모리를 차지한다.
(역주: 박싱에 대한 그림 참조)

기본적인 박싱 상황을 생각해보자. Debug.Log(object)함수를 사용할 때, 매개변수는 object이다. 
이는 어떤 변수의 객체도 함수로 전달할 수 있다는 의미이다. 그러나 정수형을 전달할 때, 컴파일러는 정수형을 오브젝트 안으로 넣어 버린다. 이렇게 처리되는 것을 예상함으로인해 박싱을 최대한 적절하게 사용하는 것이 가능하다.



1
2
3
4
5
void PrintingManyTimes(int n){
    object obj = n;
    for(int i = 0;i<500;i++)
        Debug.Log(obj);
}
이 상황에서 컴파일러는 매번 Debug.Log를 호출할 때마다 박싱을 수행하는 것이 필요가 없다. 박싱은 한번 일어나고 500번 호출될 뿐이다.






Static
정적

우리는 이제 튜토리얼의 마지막 부분으로 가고 있다. 한번 더 말하지만, 당신이 C에 대한 배경지식을 가지고 있다면, 당신이 static(정적)에 대해서 아는 것은 잊어 버려라. 
여기서의 static의 사용은 다르다.


.NET의 정적 변수는 High Frequency Heap이라고 불리는 Heap의 특별한 공간에 저장된다.

(역주: High Frequency Heap에 대한 추가 설명)
정적 변수가 참조타입이든 값타입이든 상관없이 모든 정적 변수는 힙에 저장된다.
얼마나 많이 인스턴스가 만들어졌는지 상관없이 통틀어서 오직 하나의 슬롯(공간)만이 있다.
(그렇지만 그 공간을 채우기 위해 인스턴스를 만들필요는 없다.)
이 힙은 "High frequency heap"으로 알려져 있으며, 이 힙은 보통의 가비지 컬렉션되는 힙으로 부터 분리되어 있다. 이는 어플리케이션 도메인당 하나씩 존재한다.


앞에서 언급한바와 같이, 우리는 튜토리얼에서 종종 정적변수를 만나며, 초심자는 단지 쉽게 사용할 수 있기를 기도한다. 점잖은 교사는 종종 정적변수의 이익과 위험에 대해서 충분히 설명하는데 시간을 쓰지 않는다. 우리는 이제 정적 객체가 무엇인지, 어떻게 사용하는지, 어디에 그것을 사용하고, 사용하지 않는지에 대해서 명확히 하려고 한다.

여기 정적 변수의 개념을 소개한 빠른 비디오 영상이 있다.(영어)


Static classes
정적 클래스


정적 클래스는 객체 인스턴스를 가지지 않는 클래스이다. 당신은 정적 클래스의 객체 인스턴스를 만들 수 없을 것이다.

그 결과로, 오직 하나의 인스턴스를 가진다. 방금 내가 "그것의 어떤 인스턴스도 만들 수 없다"고 말한 것때문에 혼란스러울수도 있다.
프로그램은 Heap에 이 정적객체를위해 메모리를 할당할 것이다. 그리고 유저의 정적객체에 대한 어떤 인스턴스화도 허용하지 않을 것이다.

정적 객체는 그들의 첫번째 호출에서 생성되며, 프로그램이 끝날 때 파괴될 것이다.
(혹은 크래쉬가 일어나거나..)


1
2
3
4
public static class GameManager{
    public static int score; 
    public static float amountOfTime;
}

당신은 GameManager타입의 객체를 선언할 수 없을 것이다.
GameManager GM = new GameManager();
이는 에러를 리턴할 것이다.


우리는 간단하게 다음의 명령어를 통해서 정적 클래스의 멤버에 접근할 수 있을 것이다.
GameManager.score = 10;
이는 게임안에서 언제 어디서나 가능하다. 정적 클래스 또는 멤버는 모든 파일안에서 접근가능하고 수명또한 전체 프로그램의 수명과 같다.


An example for using static class
정적 클래스 사용 예시

새로운 씬을 불러올 때, 이전 씬의 모든 변수는 파괴된다. 만약 예를들어 score같은 변수를 보존하기를 원한다면 다음과 같은 해야한다.
DontDestroyOnLoad(score);
정적 클래스의 유용한 장점은 그들의 수명에 있다. 정적 클래스는 죽지 않기때문에, 만약 씬을 불러올 때 score변수가 살아남기를 원한다면, 우리는 간단하게 정적 클래스를 사용할 수 있다. 레벨의 끝부분에 저장되기 원하는 값을 정적 멤버에 저장하고, 새로운 씬을 시작할 때 그 값을 복원한다.

if(newLevel){
    GameManager.score = tempScore;
    Application.LoadLevel(nextLevel);
}

그리고 새로운 레벨에 대한 스크립트에서:

1
2
3
void Start(){
    scoreTemp = gameManager.score;
}

여기서 작은 트릭은, 끝날때까지 GamaManager.score를 사용하지 않는 것이다.

한 레벨당 최대 200포인트를 얻을수 있는 레벨이 10개가 있는 게임을 생각해보자.

한 유저는 한번에 게임을 끝냈다. 그리고 2000포인트를 얻었다.

다른 유저는 여러번 플레이해서 게임을 끝냈고, 5000포인트를 얻었다.

두번째 플레이어는 좋지 않을 뿐만 아니라 더 많은 포인트를 얻었다. 왜냐하면 그는 각 레벨을 여러번 시도했고 그 포인트가 축적되었다. 만약 다음 레벨을 불러올 때 정적변수에 포인트를 전달한다면, 레벨을 재시도할때는 반드시 축적된 포인트를 취소해야 한다.

게임을 나가는 동안, 정적 변수의 데이터는 저장공간으로 보내질 수 있다.


정적 클래스와 정적 변수는 프로그램 실행중에 그들이 처음으로 맞닥뜨릴때 할당된다.
당신이 실제로 A 정적 클래스의 변수나 함수에 한번도 접근하지 않는다면 A 정적 클래스는 절대 만들어 지지 않는다.



출처: http://unityindepth.tistory.com/12 [UNITY IN DEPTH]

반응형
반응형

원문이 약간 길어 3~4개로 나눠어서 올릴 예정입니다. 
해당 글 원문은 http://unitygems.com/memorymanagement/에서 보실수 있습니다.
번역중에 이상한 부분은 댓글 남겨주시면 확인 후에 수정하겠습니다.



참조형식과 힙


참조형식 변수는 참조형으로 메모리에 저장된 객체를 말한다. 새로운 클래스 인스턴스를 만들 때,
이들의 데이터 위치의 참조변수를 만들뿐만 아니라 데이터도 힙에 저장된다.
이 참조는 값형식으로 객체의 주소값을 가지는 변수이다.(참조변수) 클래스의 인스턴스를 만들기 위해서 new 키워드를 사용하는데, 이는 OS에게 객체의 타입에 맞는 메모리 공간과 객체를 초기화 하는데 필요한 코드를 수행하도록 요청한다. 밑에 Dog타입의 객체의 예시를 보자.

(역주: 위에서 말한 참조형식 변수는 Class, Object, String같은 클래스를 의미한다. 이는 힙에 저장되며
참조변수는 객체의 주소값을 의미하는데, 이는 스택에 저장된다. 
참조 형식 변수와 참조변수가 햇갈리지 않도록 주의할 것)



1
2
3
4
5
6
7
8
public class Dog{
     public string name { get; set;}
     public int age { get; set;}
     public Dog(string s, int n){
           name = s;
           age = n;
    }                         
}
01
02
03
04
05
06
07
08
09
10
11
public class Test : MonoBehaviour {
    Dog dog1 = new Dog("Rufus",10);
    Dog dog2 = new Dog("Brutus",8);
 
    void Update (){                                                 
        if(Input.GetKeyDown(KeyCode.Alpha1))
            print ("The dog1 is named "+dog1.name+" and is "+dog1.age);
        if(Input.GetKeyDown(KeyCode.Alpha2))
             print ("The dog2 is named "+dog2.name+" and is "+dog2.age);
    }
}

dog1과 dog2 변수는 메모리안에서 이들 객체와 연관된 데이터가 저장된 위치를 참조한다.



밑에 Instantiate 함수와 관련된 행동을 봐라. 
우리는 3D모델이 포함된 Enemy 프리팹과 약간의 컴포넌트와 스크립트를 가지고 있다고 생각하자.

01
02
03
04
05
06
07
08
09
10
11
using UnityEngine;
using System.Collections;
 
public class Test : MonoBehaviour{
    public GameObject enemyPrefab;
 
    void Update(){
        if(Input.GeyKeyDown(KeyCode.Space))
               Instantiate(enemyPrefab,new Vector3(0,0,0), Quaternion.identity);
    }
}

우리는 새로운 enemyPrefab의 인스턴스를 만들었고, 그것은 사용되고 있다. 우리는 새로 생성한 enemyPrefab의 인스턴스를 참조하지 않고 있기 때문에, 만약 인스턴스에 접근하기 원한다면 문제가 생길 것이다. 우리가 새롭게 생성한 객체에 어떤 행동을 적용하길 원한다면 우리는 그 객체를 참조하는 변수를 만들어야 한다.

1
2
3
4
5
6
7
void Update(){
    if(Input.GeyKeyDown(KeyCode.Space))
       GameObject enemyRef = Instantiate(enemyPrefab, new Vector3(0,0,0), Quaternion.identity);
        enemyRef.AddComponent(Rigidbody);
        Destroy(enemyRef);
     }
}

예를 들어, enemyRef는 선언되었고, 새로 생성된 객체의 주소값을 할당받았다. enemyRef는 이제 그 객체를 참조한다. 어떻게 우리가 Componet를 추가하는지 봐라. 이 변수는 객체의 위치를 알기때문에 객체의 public변수에 접근하거나 추가하는 것이 가능하다. 마지막 줄을 보면 객체가 파괴되는데 이런 방식으로 거의 코드를 짜지 않지만, 이것은 단지 예시를 위한 목적이다.

참조변수는 오직 if문{ } 안에서만 생존하는 automatic 변수이다.
그 밖에서는 참조변수는 더 이상 존재하지 않으며, enemy 객체를 다시 찾기위해서는
우리는 다음과 같은 방법을 사용할 수도 있다.
GameObject enemyRefAgain = GameObject.Find(“Enemy”);

참조형식 변수는 힙공간에 저장된다. 그들은 다음과 같다. 
(역주: 위에서도 말했지만 참조형식 변수의 주소값을 가지는 참조변수는 스택에 저장된다.)

  • class   
  • object 
  • string 
    (string이 변수처럼 보일지 몰라도, 사실은 Class String의 실제 객체이다.)
값형식 변수와 참조형식 변수의 주된 차이는 데이터가 저장된 곳을 가리키는 참조변수의 추가이다.
참조변수는 우리가 데이터를 찾기전에 첫번째로 접근해야한다.
이것은 클래스가 직접적으로 바로 접근할 수 있는 구조체보다 느리게 만든다.


다른 주요 문제는 다음과 같은 연산이다.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
using UnityEngine;
using System.Collections;
 
public class Memory : MonoBehaviour {
    DogC dogC1 = new DogC();
    DogC dogC2 = new DogC();
    int number1;
    int number2;
 
    void Update () {
        if(Input.GetKeyDown(KeyCode.Space)){
           number2=number1 = 10;
           number1 = 20;
           print (number1+" "+number2);
           dogC1.name = "Rufus";
           dogC1.age = 12;
           dogC2 = dogC1;
           dogC1.name = "Brutus";
           print (dogC1.name+" "+dogC1.age+" "+dogC2.name+" "+dogC2.age);
        }
    }
}

우리는 하나가 다른 하나를 수정하지 못한다는 것을 보았을 때 두개의 number변수는 독립적이라는 것을 알게 될 것이다.

반면에, 클래스에 다른 클래스를 대입할 때
하나의 수정은 다른 하나에 영향을 미친다.

이것은 dogC2 = dogC1를 할때 우리는 dogC1의 주소를 dogC2 참조 변수에 대입하기 때문이다.
메모리에서 dogC2의 원래 주소값을 잃어 버리고
dogC1또는 dogC2를 바꾸는 것은 같다.
우리는 하나의 지역을 가리키는 2개의 참조변수를 가지고 있다.


수명과 범위 그리고 가비지 컬렉션

참조형식 변수인 경우에, 수명을 가비지 컬렉터(GC)가 담당하기 때문에 불분명하다.
C와 C++의 옛시절로 돌아가보면, 동적으로 할당된 변수가 메모리로부터 제거되는 것은 프로그래머의 의무였다. 
(C에서는 Free(), C++에서는 ~(파괴자)에서 담당한)


C#에서, 가비지 컬렉터는 변수가 아직 사용되고 있는지, 그것(실행코드안에 있는)을 참조하고 있는 것이 있는지 없는지 확인할 것이다. 그리고는 자유롭게 사용가능한 메모리 지역을 표시할 것이다.
COM인터페이스 또는 다른 레퍼런스 카운팅 시스템에 익숙한 프로그래머는 종종 가비지 컬렉션의 이상한 개념을 발견한다. 왜냐하면 레퍼런스 카운팅 시스템에서 서로를 참조하는 두 객체를 가지는 것은
(부모가 자식을 참조하고, 자식이 부모를 참조하는것 같은) 메모리가 절대 해제되지 않을 수 있기 때문이다. 가비지 컬렉터는 이러한 제약을 가지지 않는다.


가비지 컬렉터는 큰 주제이고, 유니티는 콜렉션 프로세스의 수행에 관한 많은 특이점을 가지고 있다. - 만약 당신이 전통적인 C#에 익숙하다면 기본적으로 GC는 짧은 수명을 가지는 오브젝트에 대해 처리비용이 싸야된다고 예상하겠지만, 유니티에서는 그렇지 않다.

메모리 블럭이 가득찰 때마다, 시스템은 힙에 할당된 모든 객체에 대한 도달가능성(객체에 대한 참조가 있는지 없는지)을 확인해야 한다. 이것은 그들이 다른 코드로 부터 접근되고 있는지 혹은 실행되고 있는지를 확인하는 것을 의미한다. 만약 그들이 실행되고 있거나 접근되어지고 있다면 메모리에 유지될 것이며, 그렇지 않으면 해제가 될 것이다.
일반적인 .Net 가비지 컬렉션은 제너레이션(세대)안에서 발생한다. 이는 도달가능성 테스트와 관련된 처리를 최소화하도록 하는데, 새롭게 만들어진 객체(1세대)를 먼저 검사하고, 그래도 여전히 메모리가 부족하다면 오래된 객체(2세대, 3세대)를 검사한다.
유니티는 항상 모든 객체를 검사하고 그래서 가비지 컬렉션은 주된 과부하 요소이며,
일반적으로 .NET 언어에서 예상되는 것보다 훨씬 크다.

접근성 테스트는 가비지 컬렉션 뒤에서 작용하는 마법같은 것이다. - 
이는 컬렉터가 현재 어떤 코드가 실행되고 있는지 혹은 실행될 것인지를 알아내도록 한다. 
왜냐하면 컬렉션의 후보가 되는 객체를 참조하는, 라이브 참조변수가 있을 수도 있기 때문이다.
이는 함수안에서 클로저의 사용을 포함한다. - 그것은 매우 복잡하고, 매우 강력하며, 그러나 게임을 빠르게 하는 방법은 아니다.
클로저는 지역 변수에 대한 참조 또는 함수 내부 안에 정의된 익명의 함수안의 파라미터에 의해 만들어 진다. 익명의 함수가 호출될 때, 그 루틴이 실행했을 때와 동일하게 하기 위해서 변수의 값이 유지된다 

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
List<Action> actionsToPerformLater = new List<Action>();
 
void DisplayAMessage(string message)
{
      var t = System.DateTime.Now;
      actionsToPerformLater.Add(()=>{
          Debug.Log(message + " @ " + t.ToString());
      });
}
 
void SomeOtherFunction()
{
      DisplayAMessage("Hello");
      DisplayAMessage("World");
      SomeFunctionThatTakesAWhile();
      DisplayAMessage("From Unity Gems");
 
      foreach(var a in actionsToPerformLater)
      {
           a();
      }
 
}

코드내부에서 DisplayAMessage 함수를 호출할 때 마다, 전달 받은 message와 현재 시스템 타임(t)에 대한 클로저를 생성한다. 우리는 끝에 있는 foreach 루프를 실행할 때마다.
디버그 메시지는 전에 리스트에 추가하기 위해 함수를 호출했을 때 전달된 파라미터와 함께 기록될 것이다.
클로저는 매우 강력한 도구이며 그것은 다음 글에 대한 주제가 될 것이다.

가비지 컬렉션은 시스템이 연산을 위해 메모리가 충분치 않다고 판단될 때 일어난다. 이는 버려진 객체가 상당한 기간 동안 수거되지 않을 수도 있다는 것을 의미한다. - 그러므로 객체가 더 이상 필요하지 않을 때 명시적으로 외부의 연결을 끊는 것 또는 외부 리소스를 해제하는 것은 흔히 있는 일이다.
(역주: 언제 가비지 컬렉션이 발동될지 모르기 때문에 수동으로 메모리를 해제한다는 의미이다.)

 명시적으로 내부의 객체(당신의 프로젝트에 있는)를 해제할 필요는 없다. 그러나 스트림(파일)과 시스템에 영향을 주는 데이터베이스 연결을 가지는 프로젝트의 외부에서 사용할 경우에는 필요하다.
예를 들어 파일을 닫지 않는 것은 다음에 파일에 접근할 때 실패하게 만든다.
파일은 여전히 버려진 객체에 의해 열려있기 때문이다.
가비지 컬렉션은 매우 비싼 연산이며, 게임에 영향이 없을 때 - 레벨을 로드할 때, 플레이어가 일시정지 메뉴로 들어갔을 때,  플레이어가 알지 못하는 많은 경우 - 수동적인 가비지 컬렉션의 가동은 적절하다. 
System.GC.Collect(); 명령어를 통해서 수동적으로 가비지 컬렉션을 작동시킬 수 있다.

참조타입 변수에 관해서, GameObject.Find()를 사용하는 것은 어떤 스크립트에서든 객체에 대한 참조를 리턴한다. 이처럼 객체를 찾기 위한 적절한 행동을 한다면, 프로그램안에서 어디서든 접근이 가능하다. 
게임 플레이 동안 클래스의 인스턴스를 찾는 방법에 대한 좀 더 자세한 설명은 GetComponent 튜토리얼에 나와있다.




출처: http://unityindepth.tistory.com/10 [UNITY IN DEPTH]

반응형
반응형


원문이 약간 길어 3~4개로 나눠어서 올릴 예정입니다.
해당 글 원문 http://unitygems.com/memorymanagement/에서 보실수 있습니다.
번역중에 이상한 부분은 댓글 남겨주시면 확인 후에 수정하겠습니다.



만약 당신의 게임이 실행되고 있는 컴퓨터나 장치의 

메모리에서 떤 일이 일어나는지 궁금했던 적이 있다면 

이 글은 당신에게  좀 더 효율적인 게임을 만들고 클래스와

함수의 메모리사용을 최적화하는데 도움을 줄 것이다.

이번 글에서는
  • 메모리 구역
  • 값타입과 스택
  • 참조타입과 힙
  • 구조체와 클래스
  • 참조타입을 만드는 것
  • 정적 클래스
  • 정적 변수
  • 정적 함수
  • 힙 파편화, 오브젝트 풀링, 가비지 컬렉션
이 튜토리얼은 유니티 개발을 위해 C#을 사용하게된 C/C++ 프로그래머에게 특별히 유용하다. C#은 처음에는 혼란과 마법같은 방식으로 메모리를 다루고, 그것은 그것 자체의 위험과 특이한 방식을 가지고 있다.
유니티 프로그래밍을 시작할 때 그리고 일반적인 프로그래밍을 시작할 때 변수 타입의 용도에 많이들  골머리를 앓게 될 것이다. 예를 들어, 왜 어떤때는 변수들이 업데이트되고 다른 변수들은 똑같은 값으로 유지되며, 왜 내가 여전히 필요로 하는 변수는 사라지는지 말이다.

또 다른 초심자의 공통적인 문제는 정적 변수의 사용이다. 인터넷의 다양한 튜토리얼에서 정적 변수를 종종 만나지만, 강사는 시간을 들여 명확하게 설명을 해주지 않으며, 오직 "변수에 대한 쉬운 접근"이라고만 말한다.

문제는 정적 변수(다르게 클래스 변수라고도 불리는)는 쉬운 주제가 아니라는 것이다.

몇몇은 심지어 정적 변수로 작성된 것은 다른 타입으로 처리가 될 수 있다며, 그것을 사용하는 것을 피하라고 한다. 우리는 그것이 진실인지, 그것이 어쩔수 없는 최고의 해법이 아닌지에 대해 알아 볼 것이다.


시작하기에 앞서, 당신이 C 프로그래밍에 대한 지식을 가지고 있거나 객체지향 프로그래밍에 대해 잘 알지 못한다면, 당신이 메모리 관리와 저장 키워드에 대해 알고 있는 것은 OOP(객체지향 프로그래밍)에서는 전체적으로 유사하지 않다는 것을 명심해라.
C#의 C를 연관시키지 않으려고 노력해라.
이 튜토리얼에서, 우리는 각각의 메모리 지역을 다룰것이며, 값형식, 참조형식, 동적메모리 할당 그리고 정적 변수도 다룰것이다.


메모리 지역

프로그램이 시작할 때, 운영체제는 메모리의 일정부분을 프로그램에 할당한다.  할당된 메모리에는 4개의 메모리 지역이 존재하는데, 콜 스택, 힙, 레지스터 그리고 정적 메모리 지역이다.

C#언어 개발자는 친절하게도 4개의 메모리 지역을 2개로 좁혔는데, 2개의 지역이 바로 
스택과 힙 영역이다. 스택은 순서가 있고 빠르지만 제한적이다. 힙은 무작위이고, 크지만 느리다.

프로그래머는 각각의 변수에 어느 메모리 영역을 사용할 것인지를 변수를 사용하는 목적과 변수의 생명주기를 고려하여 결정할 수 있다.

메모리 할당을 위한 3개의 키워드가 있다. 바로 auto, static(우리가 이제 막 다룰려고 하는 것), extern이다.
(C언어에서는 여기에 register라는 키워드가 추가되어 총 4개의 키워드가 존재한다.)

extern 키워드는 아마 본적이 있을 것이다. 그것은 당신의 프로젝트에 있는 다른 코드에 선언된 변수를 선언한다는 의미이다. 
예를 들면 .NET 언어가 아닌 다른 언어로 쓰여진 DLL파일에 포함된 변수가 있다.


만약 당신이 C 프로그래머라면, extern의 의미는 C#과 미묘하게 다르다는 것을 알아야 한다. C#에서는 managed memory에 할당되지 않는 변수를 참조한다.

*(역주: managed memory와 unmanaged memory에 대해)
이것은 모두 똑같은 물리적 메모리이다. 차이는 단지 누가 그것을 제어하는가이다.
마이크로소프트는 managed memory를 Garbage Collector(GC)에 의해서 정리된다고 정의한다. 예를 들어, 정기적으로 물리적인 메모리의 어떤 부분이 사용되고 사용되지 않는지를 알아내는 프로세스가 여기에 해당된다.

unmanaged memory는 그밖의 다른 것에 의해서 정리된다.
예를 들어 당신의 프로그램자체내에서 또는 운영체제가 여기에 해당된다.
1
extern int number;

컴파일러는 number를 메모리에 할당하지 않는다. 그리고 변수는 다른 어느 곳에서 발견 될 것이라고
예상한다. 우리는 더이상 이 주제에 대해서 연연에 할 필요가 없을 것이다.
이 링크에서 더 많은 정보를 찾을 수 있을 것이다.


값형식과 스택

auto는 automatic 변수를 나타낸다. 하지만 C#에서 더이상 사용되지 않기 때문에 당신은 이 키워드를
한번도 보지 못했을지도 모른다.

1
2
int variable = 10;
auto int variable = 10;

두 라인은 정확하게 같다.

automatic 변수의 특징은 콜 스택에 할당된다는 것이다. 
스택에서는 오직 맨윗부분에 변수를 추가하거나 제거할 수 있고, 맨 아랫 부분에는 변수를 추가하거나 삭제를 할 수가 없다.  이것은 접시 한무더기와 비슷하다. 밑에 그림을 봐라


가장 위에 있는 스택을 알기위해서 프로그램은 스택 포인터를 사용한다. 스택 포인터는 CPU안에 있으며, 위 그림에서 보이는 검은색 화살표에 의해서 나타나는 현재 위치를 추적한다. 변수를 추가할 때 마다 스택 포인터는 증가된다.(또는 자신이 사용하는 OS의 메모리 관리에 따라 감소될수도 있다.)

스택은 함수 호출을 위해 사용되며, 전체 프로그램은 다른 함수들을 차례로 호출하는 Update함수를 호출하는 메인 함수이다. 
그래서 당신의 스크립트안에 있는 변수는 다른 어떤 것으로 정의되어 있지않다면 stack에 저장되는
automatic 변수이다.

또한 스택 프레임(Stack frame)에 대한 개념을 만날 것이다. 간단히 말해서 이것은 현재 실행된 함수에 의해 지역적으로 할당된 변수들의 집합이다.(또한 return 주소값을 포함하는데, 이것은 해당 함수가 리턴되고 난후에 바로 다음에 실행될 명령 주소를 뜻한다.)
컴파일러는 스택 프레임의 끝을 가리키는 포인터를 유지하며, 실제로는 당신의 로컬 변수가 차지하는 메모리를 찾기위해 이 포인터로부터 -offest을 사용한다.
이것의 실제적인 결말은 함수가 리턴되고 난 후이다. 그리고 그 함수의 로컬 변수가 사용된 공간들중 어느 것도 사용되거나 할당되지 않는다. (다음 함수 호출을 위한 여분의 공간을 제외하고는)

만약 재귀 프로그램(함수가 자기자신을 호출하는) 작성한다면, 어쩌면 스택의 공간을 모두 써버릴수도 있다.
각 함수 호출은 모든 지역변수와 리턴 주소값이 스택에 존재하기 위한 공간을 요청한다.
만약 당신의 코드에 그들 스스로 멈추지 않고 실행되는 루틴이 존재한다면, 아마 스택 오버플로우 메시지를 보게 될 것이다.

그래서 생명주기와 automatic 변수의 범위는 서로 관련되어 있다. 변수는 중괄호{와 }안에서 존재한다.
이것은 설명이 필요하다.


01
02
03
04
05
06
07
08
09
10
11
void Update(){
    int number = 10;
    Fct(number);
    print(number);
}
 
void Fct(int n){
    int inside=20;
    n = n + inside;
    print(n);
}


업데이트는 호출되고 첫번째 명령어는 변수 선언이다. 스택포인터는  밀어올려지고, number의 값(10)은  메모리 장소에 위치한다.
두번째 명령어는 함수 호출이다. 함수를 호출할 때 프로그램은 멈추고, 함수의 주소로 이동하며,
프로그램의 원래의 위치로 되돌아 가기위한 약간의 데이터는 스택에 전달된다.
매개변수는 원래의 값을 복사하여 스택에 저장된다.
변수 number는 Fct함수에서 사용되기위해 스택에 저장되지 않고 number의 값을 복사한 새로운 변수 n이 만들어져 스택에 저장된다.
제 Fct함수안에는 number 변수의 값을 가지고 있는 변수 n을 가지고 있다. 그렇다고 Fct함수에 number함수가 존재하는 것은 아니기때문에 number변수에는 접근할 수가 없는 것이다.
함수안에서 새로운 변수 inside가  선언되었다. 스택은 다음과 같은 상태이다.(그림에서는 스택의 상황을 매우 단순화 시켰다는 것을 명심하라)




함수의 끝부분에, n은 출력되고 30의 값을 가진다. 함수 내부에서 만들어진 모든 변수는 파괴되고 사라진다. 그것은 n과 inside을 포함한다. 변수가 여전이 메
모리에 존재할지라도, 그들은 간단하게 무시되고 시스템에 의해 더 이상 고려되지 않는다.

Update함수로 돌아왔을 때, 원래 값 10을 가지는 number변수를 출력하는데, Fct함수에서 사용된 것은 number변수의 복사값이기 때문이다.
스택 포인터는 n과 inside의 위치를 잃어버리는데, 만약 Update함수로 돌아와서 다시 n과 inside함수에 접근하려고 한다면 컴파일러는 그 변수는 현재 지역에 존재하지 않는다는 에러메시지를 반환할 것이다.

우리는 automatic 변수는 범위는 중괄호{, }에의해 정의 된다는 것을 보았다. 그 밖에서는, 변수는 존재하지 않고 보이지 않는다. 중괄호는 함수, if문또는 반복문의 범위를 표시할 수 있다.

변수의 수명과 범위

수명은 프로그램안에서 변수가 존재하는 시간을 의미하며, 범위는 프로그램에서 변수가 보이는 지역을 정의한다. automatic 변수의 경우에는 수명과 범위가 유사하다.

값타입의 변수는 스택에 저장된다. 그들은 다음과 같다

  • int
  • unsigned
  • float
  • double
  • char
  • struct
  • bool
  • byte
  • enum
  • long
  • short
그 당시에 존재하지 않았던 bool형을 제외한 거의 모든 타입을 C로부터 상속받았다.

automatic 변수에 대해서, 그것의 생명은 compiler에 의해 관리된다. 선언된 범위의 끝은 변수의 끝을 표시한다. 프로그래머는 메모리 지역을 해제하는데 신경쓰지 않아도 된다. 그것은 자동으로 처리된다.




(다음 글에서 계속)




반응형
반응형

DontDestroyOnLoad 사용시 주의 점 


전부터 저 함수를 자식 오브젝트에 사용하면 장면이 교체되어도 자식 포함해서 부모까지 전부 남는 건지 궁금했었는데 당장 사용하질 않아서 당시에는 그냥 넘기고 잊고 있다가 이번에 ios로 테스트하려니 켜자마자 그냥 죽길래 뭐가 문제인가 찾아보다가 이것 때문인 걸 알게 되어서 몇 자 적습니다.

 

5.3버전부터 경고를 했었나 보네요. DontDestroyOnLoad only work for root GameObjects or components on root GameObjects. 라고 뜨는데요. 말 그대로 최상위 게임오브젝트나 최상위 게임오브젝트에 붙어있는 컴포넌트에서만 작동한다는 경고 메시지입니다. 그런데 이 메시지를 저만 그런지는 모르겠으나 에디터에서는 안 띄워주고 빌드 했을 때만 띄워줘서 전혀 모르고 있다가 게임 내 콘솔 창을 만들어서 띄우고 윈도우즈에서 테스트할 때 알았습니다. 경고로만 뜨고 플레이에 전혀 문제가 없어 큰 문제 아니구나 하고 알고만 있고 넘어갔는데 ios에서는 빌드해서 보니 유니티 스플래시 뜨기도 전에 켜자마자 바로 죽네요.

 

xcode 로그가 딱 저렇게만 나오는데 놓치고 있는 부분이 있는지 모르겠지만 봐도 어디가 문제인지는 알 수가 없어서 뭐를 잘못했을까 생각하다가 문득 저 경고가 생각나서 메시지 말대로 최상위가 아닌 얘들한테 사용하는 곳을 찾아 수정하고 빌드를 해서 보니 문제없이 잘 됩니다. (그런데 며칠 전까지도 같은 코드를 사용하고 있었는데 그때는 왜 잘 됐었을까요 ?ㅁ?)

 

5.3버전 넘어가면서 내부적으로 어디가 바뀌어서 그런 건지 정확히 원인이 이것인지 이것으로 인해 사이드 이펙트가 생기는 건지는 모르겠지만, 관련이 된 것은 맞는 것 같네요. 혹시 ios 테스트하시다가 그냥 죽는 경우가 생기면 이쪽도 한 번 봐보시기 바랍니다. 조금 불만인 것은 변경 점이나 이런 문제가 생길 수 있으면 레퍼런스에 적어 놓았으면 좋을 텐데 저 경고에 해당하는 부분은 내용이 아직 없네요. 함수도 UnityEngine.Object 형을 인자로 받고 있어서 넣을 수 있으면 다 되는 줄 알고 머티리얼에 사용하고 있었는데 원래라면 사용하면 안 되는 거였군요. 저처럼 사용자가 실수할 수 있는 부분인데 UnityEngine.GameObject나 UnityEngine.Component 형만 넣을 수 있게 했으면 더 좋지 않았을까 합니다.

 

여담으로 파괴되지 않아야 하는 해당 오브젝트가 자식인 경우에는 해당 오브젝트의 transform.root.gameObject를 인자로 넣어서 함수를 실행시키면 자신을 포함하는 최상위 오브젝트가 효과를 받아 밑으로 있는 자식까지 파괴되지 않습니다.

 

참고 - http://answers.unity3d.com/questions/996282/wierd-console-errors.html


출처 : https://fetchinist.com/blogs/?p=2061





반응형
반응형

리소스 로딩하기



유니티IDE의 Assets 안 에는 수 많은 객체를 정의해 둘 수 있습니다. 이미지나 사운드를 비롯하여 스크립트와 프리펩까지 모두 들어갑니다.

이러한 에셋은 컴파일타임에 특정 게임오브젝트와 바인딩되지만 런타임에 스크립트를 통해 사용하려면 리소스를 로딩하는 과정을 거쳐야 합니다.


 

특별한 이름의 폴더top

유니티의 Assets 폴더 밑에는 서브폴더를 자유롭게 만들 수 있지만 미리 지정된 이름으로 폴더를 만들면 특별한 작동을 합니다.

http://wiki.unity3d.com/index.php/Special_Folder_Names_in_your_Assets_Folder

위 목록에서 다양한 특수 폴더명을 볼 수 있습니다. 대소문자는 구별하지 않습니다. 이 중 이번 포스팅에서 눈여겨 볼 폴더명은 Resources 입니다.

Assets/resources

에셋폴더 밑에 일단 resoureces 라는 이름으로 폴더를 만들면 이 폴더를 기준으로 여러 에셋을 런타임에 로딩하여 사용할 수 있게 됩니다.

예를들어 프리펩을 만들어 로딩하고 싶다면 다음과 같은 구조로 서브폴더를 만들어봅시다.

Screenshot_1

resources/block 폴더 안에 들어있는 R1~R5까지의 프리펩을 런타임에 불러오려면 어찌 해야할까요?

 

Resources.Load와 Resources.LoadAlltop

각각의 자원을 부르는 방법은 Resources.Load를 사용하는 것입니다.

GameObject temp1 = Resources.Load( "block/R1" as GameObject;
GameObject temp2 = Resources.Load( "block/R2" as GameObject;
GameObject temp3 = Resources.Load( "block/R3" as GameObject;
GameObject temp4 = Resources.Load( "block/R4" as GameObject;
GameObject temp5 = Resources.Load( "block/R5" as GameObject;

귀찮기 때문에 그저 서브폴더만 넘겨주면 전부 처리해주는 LoadAll을 제공합니다.

object[] temp = Resources.LoadAll( "block" );

LoadAll의 경우는 as GameObject[] 로 형 변환할 수 없습니다. 하지만 개별 요소에 대해서는 형변환할 수 있으므로 큰 문제는 없습니다.

object[] temp = Resources.LoadAll( "block" );
 
forint i = 0 ; i < temp.Length ; i++ ){
  GameObject go = temp[i] as GameObject;
  //go...
}

 

제네릭컬렉션으로 캐쉬하기top

일단 로딩한 후에는 해당 객체를 반복해서 사용하는 경우가 많습니다. 예를 들어 R1(녹색큐브)를 10개 복제한다고 하면 GameObject.Instantiate를 사용하기 위해 개별 객체를 인식해두지 않으면 안됩니다.

로딩된 배열을 해시테이블(HashTable)로 잡아두면 편리하게 사용할 수 있지만 제네릭 타입으로 정리하면 형변환도 필요없게 되므로 더욱 안정적이고 간단히 사용할 수 있을 것 입니다.

일단 제네릭컬렉션을 사용하려면 네임스페이스를 불러와야겠죠.

using System.Collections.Generic;

다음은 RSC클래스에 간단히 정적 필드로 캐쉬 공간을 만들어 둡시다.

class RSC {
  static private Dictionary<string,GameObject> _cache =
      new Dictionary<string,GameObject>();

 

로딩하기와 캐쉬에서 꺼내기top

그럼 이제 서브폴더를 로딩하는 함수를 일반화하여 캐쉬에 곧장 넣어버리죠.

static public void Load( string subfolder ){
  object[] t0 = Resources.LoadAll( subfolder );
  forint i = 0 ; i < t0.Length ; i++ ){
    GameObject t1 = (GameObject)(t0[i]);
    _cache[t1.name] = t1;
  }
 }
 
//서브폴더 로딩하기
RSC.Load( "block" );

그럼 그 뒤로는 캐쉬에서 직접 꺼내가던가 아니면 인스턴스를 반환하는 함수를 생각해 볼 수 있습니다. 우선 간단히 가져가는 함수는 다음과 같습니다.

static public GameObject Get( string key ){
  return _cache[key];
}
 
// R1을 가져오자
GameObject temp = RSC.Get( "R1" );

인스턴스를 직접 반환받고 싶다면 다음과 같은 함수로 편리하게 사용합시다.

static public GameObject Instance( string key, string name,
    float x, float y, float z ){
  GameObject t0 = (GameObject)(GameObject.Instantiate(
      _cache[key], new Vector3( x, y, z ), Quaternion.identity
    ));
  t0.name = name;
  return t0;
}
 
//R1으로부터 R1Clone을 가져오자.
GameObject temp = RSC.Instance( "R1""R1Clone", 0, 0, 0 );

 

캐쉬를 메모리에서 삭제top

메모리 절약을 위해 다양한 리소스를 로딩하려면 필요없는 리소스는 다시 삭제해줘야합니다. params인자를 통해 간단히 삭제하는 함수를 구현합시다.

static public void Remove( params string[] arg ){
  forint i = 0 ; i < arg.Length ; i++ ){
    string key = arg[i];
    _cache.Remove( key );
  }
}
 
//R1~R3까지 삭제
 
RSC.Remove( "R1""R2""R3" );

이를 통해 간단히 메모리에서 몇 개라도 한꺼번에 삭제할 수 있게 되었습니다[1. 참고로 GameObject의 경우 Resources.UnloadAsset 으로 삭제되지 않고 더 이상 사용되지 않으면 GC가 될 뿐입니다.]

 

간단한 샘플top

이상의 내용을 바탕으로 간단히 큐브를 화면에 채워봅시다.

Load ("block");
forint i = 0 ; i < 10 ; i++ ){
  forint j = 0 ; j < 10 ; j++ ){
    Instance( "R" + Random.Range(1,6), i+":"+j, i-5, j-5, 0 );
  }
}
Remove( "R1""R2" );

다음과 같은 화면이 된다면 정상입니다.

Screenshot_2

 

결론top

이번 포스팅에서는 간단히 런타임 에셋로딩을 알아봤습니다. 실제 소스는 거의 GameObject 로딩 중심으로 가볍게 설명했으나 이를 바탕으로 다양한 에셋로더를 구현하는 것은 또 다른 기회에 해보겠습니다(과연..)

종합 소스는 아래에 있습니다만, resources/block 폴더를 만들고 R1~R5까지의 색깔별 큐브를 프리펩으로 만들지 않으면 실행되지 않습니다.



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

public class bside002 : MonoBehaviour {
	
	static private Dictionary<string,GameObject> _cache = new Dictionary<string,GameObject>();
	
	static public void Load( string subfolder ){
		object[] t0 = Resources.LoadAll( subfolder );
		for( int i = 0 ; i < t0.Length ; i++ ){
			GameObject t1 = (GameObject)(t0[i]);
			_cache[t1.name] = t1;
		}
	}
	static public GameObject Get( string key ){
		return _cache[key];
	}
	static public GameObject Instance( string key, string name, float x, float y, float z ){
		GameObject t0 = (GameObject)(GameObject.Instantiate( _cache[key], new Vector3( x, y, z ), Quaternion.identity ));
		t0.name = name;
		return t0;
	}
	static public void Remove( params string[] arg ){
		for( int i = 0 ; i < arg.Length ; i++ ){
			string key = arg[i];
			_cache.Remove( key );
		}
	}

	void Start () {
		Load ("block");
		for( int i = 0 ; i < 10 ; i++ ){
			for( int j = 0 ; j < 10 ; j++ ){
				Instance( "R" + Random.Range( 1, 6 ), i + "_" + j, i - 5, j - 5, 0 );
			}
		}
		Remove( "R1", "R2" );
	}
}




http://www.bsidesoft.com/?p=215





반응형
반응형

아래 내용을 구글링 하다가 알게된 것인데

유니티를 지우지 않고 유니티를 끈 상태에서 C:ProgramData/Unity 경로에 폴더를 제외한 

ulf  및  기타 라이센스 파일들을 지운다 (약 3개 정도 인가 있음)

그런 후 다시 유니티를 키면 제인증 하는 화면이 나타남





Give the steps below a try. The files may be hidden so ensure you show hidden files.

  1. Uninstall Unity.

  2. Delete This file:

  • WinXP/Vista: C:/Documents and Settings/All Users/Application Data/Pace Anti-Piracy/License Files

  • Win7: C:/ProgramData/PACE Anti-Piracy/License Files

  • Mac: HD/Library/Application Support/PACE Anti-Piracy/License Files

  • Windows: C:ProgramData/Unity

  • Mac: Library/Application Support/Unity

    1. Restart

    2. Download Unity again from the website, since your original install might be corrupt.http://unity3d.com/unity/download

    3. Reinstall and test




http://answers.unity3d.com/questions/516259/license-information-is-invalid-.html

반응형
반응형

check Player setting => Resolution => (Run In Background) 


OnApplicationPause/OnApplicationFocus has been all over the place for me. In the Editor, it seems to be different on PC and MAC. On MAC it seems to get called on press Play. On PC it doesn't. On iOS it seems like one of them doesn't work. Can somebody tell me the different scenarios that they work?

And can somebody explain what they mean in each scenario? Editor? Standalone? iOS? Android? (like does OnApplicationFocus fire when you minimize or when you just click on another window in the Editor)

I know about the documentation, I read the documentation. But the behavior doesn't match it. It's ridiculous. If somebody has figured it out I'd greatly appreciate it.






Since this UnityAnswer is one of the first (if not the first) to be returned on a search for OnApplicationFocus/Pause & iOS, an important update in Unity 4.6.1 has changed the behavior for iOS.

As of 4.6.1, both OnApplicationFocus and OnApplicationPause will be called in iOS.

The order is :

App initially starts:

  • OnApplicationFocus(true) is called

App is soft closed:

  • OnApplicationFocus(false) is called

  • OnApplicationPause(true) is called

App is brought forward after soft closing:

  • OnApplicationPause(false) is called

  • OnApplicationFocus(true) is called

Hope that helps




http://answers.unity3d.com/questions/496290/can-somebody-explain-the-onapplicationpausefocus-s.html

반응형
반응형



*Start와 Awake와 OnEnable


기본은 유니티 매뉴얼(https://docs.unity3d.com/Manual/ExecutionOrder.html ) 과동일한 순서라고 생각하면 됨.





Awake는 게임오브젝트가 유니티 내부에서 Instantiate될때 첫 호출되고, Start는 모든 오브젝트의 Instantiate가 마무리된 후에 호출된다.




그런데, 오브젝트 풀링 등을 이용하기 시작하면서 헷갈리는 문제가 발생하기 시작함.


오브젝트 풀링은 보통 gameObject.setActive(true) 와 setActive(false)를 이용하여 필요한 오브젝트를 여러번 Instantiate 하지 않고 재활용하는게 요점인데,


여기서부터 Awake, Start, OnEnable, OnDisable 호출 순서가 점점 골치아파지기 시작하는거지.



------------------------------------


본론으로 들어가기 전에 확실하게 짚고 넘어가야 하는 부분은 이거임.


1. gameObject.SetActive(false)

2. this.enabled = false


는 완전히 다르다는 점이다!


뭐가 다른 것인가 하면





gameObject.SetActive와 this.enabled는 위 캡쳐의 1번 2번 각각에 대해 대응된다고 생각하면 됨.


게임 오브젝트 자체를 disable시키느냐, 게임오브젝트 속의 스크립트 컴포넌트 하나를 disable시키느냐의 차이라고 이해하자.


그럼 이게 Awake, Start, OnEnable과 무슨 상관일까?


-----------------------------------------


경우 1) 게임오브젝트가 Disabled 상태일때


위 스샷의 1번 체크박스를 Disable시켜둔 프리팹, 또는 씬 속의 게임오브젝트가 있다고 치자. 

예를 들어 이펙트를 씬에다 갖다놓고, disable 상태로 게임을 시작했다가, 플레이어가 적을 히트하는 등의 특정 행동에 다다르면 이펙트를 enable시켜서 재생하는 경우를 생각할 수 있겠다.


이 경우에는 게임오브젝트가 활성화되지 않은 것으로 간주, Awake가 호출되지 않는다. 즉, Awake는 게임오브젝트의 활성화 여부에 따라 호출된다.


게임 오브젝트가 애초에 비활성화 상태니, 그 하위 컴포넌트들의 초기화 함수인 Start 역시 호출되지 않는다. 


즉, 

A) 게임오브젝트를 비활성화한 상태로 게임을 시작하면, 또는

B) 프리팹 설정에서 1번 체크박스를 꺼놓은 프리팹을 게임 중에 Instantiate(prefab) 으로 생성할 경우,


Awake도, Start도, OnEnable도 모두 호출되지 않는다.


경우 2)게임오브젝트가 Enabled지만 컴포넌트는 Disabled 상태일때


위 스샷의 1번 체크박스는 켜져 있지만, 2번 체크박스는 꺼진 상태를 뜻한다.


이 경우 일단 게임오브젝트 자체는 활성화되어 있기에, Awake는 호출된다.


그러나 OnEnable() 과 Start()는 호출되지 않는다. 이 두 함수는 컴포넌트의 활성화 여부에 따라 호출된다.


게임이 돌아가는 도중에 체크박스를 켜거나 GetComponent<Component>().enabled = true 설정을 하면, 그 이후 OnEnable과 Start가 호출되게 된다.




전체적으로 재정리하면 다음 그림과 같다.








http://blog.naver.com/1mi2/220928872232 중에서



반응형
반응형

Unity를 C#으로 개발하다보면 “리플렉션”, 즉 reflection 이라는 단어를 종종 듣는다. 이는 C/C++에서 넘어온 개발자들이라면 다소 생소한 단어일 수 있다. Java는 java.lang.reflection 이라는 패키지로 reflection을 지원하고, C#에서는 System.reflection 네임스페이스를 통해서 지원한다.

Reflection의 한글 뜻은 “거울에 비친 상” 이라는 뜻이다. Language에서 “상” 이라는 의미가 뭘까 라고 떠올렸을 때 언뜻 잘 떠오르지 않는 것이 Reflection 개념에 대한 1차 Hurdle이다.

Programming Language에서 Reflection이라는 말은 Runtime 시에 Object 객체의 형태나 타입에 대한 정보를 읽어 내는 것을 말한다. C++에서도 제한적이지만 RTTI(Run-time type information) 메커니즘을 통해서 Reflection 기능을 제공하고 있다. C++ RTTI 메커니즘 중 대표적인 것이 dynamic_cast와 typeid 이다. 아래는 dynamic_cast의 예를 보자. (Refrence)

위 코드를 실행하면 “basePointer is pointing to a Derived class object” 이 출력된다.

스크린샷 2015-03-03 오전 12.18.54

즉, dynamic_cast는 부모 클래스의 포인터 변수에서 자식 Instance의 Address를 저장하고 있더라도, 실제 자식의 타입이 특정 타입이 맞는지 체크해 준다. 맞으면 해당 Instance의 address를 틀리면 NULL을 리턴한다. 사실 C++에서 Parent Class에 virtual function을 선언하여 파생 클래스들을 다룬다면 굳이 RTTI를 사용하지 않고도 Runtime시에 실행되는 Method들을 Polymorphism(다형성)의 특성을 이용하여 제어할 수는 있다.
아래의 코드는 C++ RTTI의 다른 형태인 typeid의 용례다. (Refrence)

실행 시 출력은 다음과 같다. 원 Class 이름과 다른 runtime 시의 type name이 적절하게 출력이 된다. (Class 와 이름이 정확히 일치하지 않는 이유 : Reference)

스크린샷 2015-03-03 오전 12.20.39

즉, Class Pointer들이 담고 있는 진짜 Instance들을 알아낼 때 사용 한다.

C#은 Source Code를 .Net Assembly, 즉 CIL(Common Intermediate Language) 형태로 컴파일 한 후에 .Net Runtime(CLR(Common Language Runtime))으로 실제 실행 시키는데, 이 때 CIL 안에 Meta Data 정보를 담고 있다. Unity에서는 ScriptAssemblies 폴더 아래에 보면, Assembly-CSharp.dll 등 여러개의 컴파일된 .NET Assembly 파일들이 있다. 이 .Net Assembly 안의 저장된 Meta Data를 통해서 내부 Class들의 정보들을 알아 냄으로써 Reflection 기능이 동작할 수 있게 된다. 이를 통해서 Method Invocation, Serialization, Object Dump, Database Interface 등의 작업이 가능하다. Type을 묻고, Class Descriptor를 얻은 다음에 해당 클래스의 Method와 Field Descriptor들을 통해서 세부 접근할 수 있게 되는 것이다. C++의 Reflection 기능이었던 RTTI가 단순 타입 체크 정도 수준으로 지원한 반면에, C#의 Reflection은 Instance의 Type을 체크하거나, Field 정보를 알아오는 것은 물론, Method를 실행할 수도 있고, Field나 Method를 동적으로 추가할 수도 있다. 아래는 C#의 기본적인 Reflection의 예이다.

출력은 아래와 같다.

ReflectionTest라는 Static Class가 자기 안에 있는 3개의 Int32 Field와 1개의 String Field의 정보를 Runtime 시에 Reflection 형태로 접근하는 것을 보여준다. typeof operator로 읽어서 FieldInfo Class를 통해서 GetFields, GetValue Method들을 Access하고 있다.

Unity 엔진의 C#에서도 동일하게 Reflection을 사용할 수 있다. 그러나, iOS의 경우에는 자체 저수준 가상 머신인 LLVM 환경 에서 제한적으로 Reflection을 허용한다. 그 LLVM 상에서 돌아가는 Mono Runtime에 맞도록 미리 컴파일하는 AOT(Ahead of Time) 컴파일 방식을 채용하고 있어서 Reflection 기능 중에 매우 큰 강점인 동적인 코드 생성을 할 수 없다. 즉, Reflection.Emit 같이 JIT(Just In Time) Compile되어서 동적으로 코드를 생성하여 Native Code로 변환시키는 동작을 보안상 할 수 없게 된 것이다. 또한 C# Reflection 기능을 이용하는 Linq 쿼리는 동적 코드 생성이 발생하는 지뢰밭이므로 사용은 가능하나 섣불리 사용했다가는 iOS에서 낭패를 볼 수 있다. 다만 AOT를 요구하는 iOS 플랫폼이라도 Field 타입과 Method 리스트를 받아오거나, Method를 실행시키는 등의 기본적인 Reflection 기능들은 모두 동작한다.

이 Reflection의 용도는 다양하다. Unity에서 커스텀 Inspector를 만들 수도 있다. .NET Assembly Dll 파일을 Reflection 통해 분석하면, C++에서 Access할 수 있는 Interface Class 들을 자동으로 만들 수도 있다. 전반적으로, 해당 클래스의 타입을 알고 싶을 때, 속도와 크게 관련이 없을 때는 Reflection을 이용해서 정보들을 당겨와서 이용하면 된다.

다음은 Inspector에서 선택된 Game Object에 Attach되어 있는 스크립트의 Method들을 Inspector에서 Reflection을 이용하여 알아내고, 실행할 수 있게 해주는 Custom Inspector 스크립트의 예이다. (Reference)

custom_inspector

아래 코드는 Object에 Attach 시키는 스크립트이다.

아래는 Scripts 폴더에 정의하는 스크립트이고,

마지막으로 Editors 폴더에 정의하여 실행시에 버튼을 표시하고, 클릭 시에 Method를 Invoke시키는 Reflection 구현이 포함된 클래스이다.

Reflection 기능을 잘 이용한 Asset을 만들 수도 있다. vBug는 Reflection 이용의 끝판왕 이라고 할 만한 Unity Asset Tool 이다.

다만, Reflection 코드의 Runtime이라는 특성을 보면 알 수 있듯이 실시간으로 무엇을 체크한다는 것은 효율적인 면에서 Static보다는 느리다는 것을 짐작할 수 있다. 실제로 Reflection은 속도면에서는 Direct Access하는 것보다 수천배 느리다. GetComponent를 Update() 구문에서 남발하지 않듯이 Reflection 기능들은 Performance를 요하는 부분에서는 사용하지 않거나 매우 주의해서 사용해야 한다. (Benchmark Reference)

 


출처 : http://rapapa.net/?p=2550


반응형
반응형

처음 호출은 Awake, OnEnable, Start 이 순으로 호출이 되는데

Object가 Enable = true (활성화 ) 될 때마다 OnEnable() 이 호출 된다



If a script is attached to more than one gameObject, will Start() & Awake() run more than once?

The same script. Different gameObjects disabled at runtime. Enabled at different time periods throughout the game.

Will Start() & Awake() run for each gameObject becoming enabled?

Or do I need to use OnEnable()?

2 Replies

 · Add your reply
avatar image
1

Answer by Bodrp 

Start() and Awake() will be called when the component becomes active for the first time only. They will act on their own instance of the component (since they are not static methods). OnEnable() gets called everytime the component becomes active. By "acting on their own instance" I mean that for every instance of the component, they will be called and will have access to their instance's attributes, whatever may be their access level.

Awake() will be called first, then OnEnable(), then Start(). If multiple components are activated at the same time, Awake() and OnEnable() methods will be called jointly for each instance. When that's done, each instance's Start() method will be called.

For more information about the methods Unity calls with reflection on your components, you can refer to the execution order page of the manual.



http://answers.unity3d.com/questions/1346838/if-a-script-is-attached-to-more-than-one-gameobjec.html

반응형
반응형




"Tag"and "Layer"

there are two parameters in the window of inspector named "Tag" and "Layer".can anybody tell me what is their the meaning

2 Replies

avatar image
5

Answer by Mike 3 

Tags are used for finding objects with a specific tag ( e.g. FindWithTag(str) ).

Layers are used for determining which objects are raycast to, which get renderered, lit, and which objects collide together

Check the manual for more info:

http://unity3d.com/support/documentation/Components/Layers.html

http://unity3d.com/support/documentation/Components/Tags.html

avatar image
1

Answer by blueLED 

Layers can be used to identify objects at a high level. Some examples would be:

  • level collision

  • pick up item

  • water

  • enemy

So when your code is checking for where the player can walk, you would only check for objects in the "level collision" layer and ignore the rest. If you didn't want the player to collide against something, say, a hologram, then you wouldn't set its layer to "level collision" and the player could pass right through it.

Tags could then be used to define the object at a finer level, for example, if the object is set to the "level collision" layer, its tag could then be set to:

  • dirt

  • metal

  • wood

So your code can use the tag to determine which sound effect to play when the player walks over or touches the object. Or it could be used to determine what kind of bullet mark should be left on the surface if you shoot it.

Another use for layers is to decide which objects get lit by which lights. For example, make a layer called "RedLightObjects". Then make a red light and set its culling mask to "RedLightObjects". Now, the red light will only light up those objects in the scene set to that layer and ignore the rest. Tags would not be efficient for this purpose.



http://answers.unity3d.com/questions/31892/tagand-layer.html



반응형
반응형

Resources.Load() 를 쓰시면 됩니다. 
using UnityEngine; 
using System.Collections; 

public class example : MonoBehaviour { 
    void Start() { 
        GameObject instance = Instantiate(Resources.Load("enemy")); 
    } 


주의할 점은 파일이 Resources 하위에 있어야 합니다. 


http://www.devkorea.co.kr/bbs/board.php?bo_table=m03_qna&wr_id=26416

http://docs.unity3d.com/ScriptReference/Resources.Load.html

반응형
반응형

※ 현재 유니티에서 만든 게임을 홈페이지에 올리는 방법을 몰라서 고전하고 있다. 이점 양해바람.



게임을 진행하다보면 플레이어와 물체가 부딫쳐야 하는 경우가 발생한다. 필자 같은 경우 크게 4가지를 고려한다.



1. OnCollisionEnter



충돌체 간의 충돌을 감지하여 충돌시 이벤트를 발생시키게끔 할 수 있다. Tag를 입히는 것 외에는 별다른 조건 없이 작동한다.


이해력을 돕기위해서 예시 소스를 준비하였다.



1
2
3
4
5
6
7
8
9
10
void OnCollisionEnter(Collision other){
        if(other.transform.tag == "cube" || other.transform.tag == "door" ){  //충돌한 tag가 cube 또는 door 라면 JumpB가 참.
            JumpB = true;
        }
         
        if(other.transform.tag == "JumpUp"){
                        /* 1.5와 jump변수값을 곱한 힘의 백터 위의 방향으로 힘을 가함.*/
            rigidbody.velocity = Vector3.up * jump * 1.5f *Time.deltaTime;
        }
}


전 설명에서 bool 변수 JumpB이 참이되는 조건을 생각해보라고 했었다. 다르게 생각할 수도 있지만 필자는 간단한 방식으로 JumpB의 참 조건을 유도해냈다. (물론 간단하다고 다 좋은 것은 아닌 것 같다.)



2.OnTriggerEnter



충돌체 간의 충돌을 감지하여 충돌시 이벤트를 발생시키게끔 할 수 있다. Tag와 Rigidbody 를 입히는 것, 그리고 Collider를 is Trigger 하는 것이 이 명령어의 조건이 된다. 충돌체간의 충돌시 서로를 통과하게끔 해야하기 때문에 투명한 Collider를 이용하여 사용한다.


아래 예시를 보자.



1
2
3
4
5
void OnTriggerEnter(Collider other){
        if(other.transform.tag == "Player"){ //충돌한 물체 tag가 Player라면, End 씬으로 씬을 넘긴다.
            Application.LoadLevel("End");
        }
    }


is Trigger가 체크된 물체에 Player가 접근하면 이루어 지는 명령어이다.



3. Raycast



위에 두개와 더불어 많이 사용한 소스이다. 거리를 판단하고 명령어에 알맞은 수행을 하는 것부터 시작해서 응용분야가 매우 다양하다. 


아래 예시를 보자. 생각보다 복잡하기 때문에 반드시 각자가 사용해서 그 의미를 정확하게 알기를 바란다.



1
2
3
4
5
6
7
8
9
10
11
12
13
14
RaycastHit hit_0; // RaycastHit 선언.
void Update () {
        Vector3 ray_position = transform.position; //현재 위치.
        Vector3 ray_direction_R = transform.TransformDirection(1, 0, 0); //x값이 1, y값이 0,  z값이 0 방향.
 
            if(Physics.Raycast(ray_position, ray_direction_R, out hit_0, 0.51f)) // 0.5거리가 떨어져있는 위치에 닿는 다면.{
 
                if(hit_0.collider.tag == "Block_R"){ //Block_R tag라면 -0.75 위치로 이동한다. (접근을 제한한 것.).
                    float pos_x = hit_0.collider.transform.position.x -0.75f;
                    transform.position = new Vector3(pos_x, 0, transform.position.z);
                    transform.Translate (Vector3.right * h * Speed * Time.deltaTime);
                }
            }
}


Raycast는 응용 분야가 (필자가 생각하기에) 다양하기 때문에 국한되어 사용하는 실수를 범해서는 안된다고 생각한다. 


그렇다면 위 소스에서 왜 접근을 제한한 것일 까? 

물체간의 충돌시 Rigidbody 로 충분히 충돌을 감지하여 멈추게(접근을 제한하게) 할 수 있다. 

그렇지만 실제로 유니티 안에서 Rigidbody을 사용할 경우, 부자연스러운 충돌이 연속적으로 발생하는 것을 확인할 수 있을 것이다.



4. AddComponent for Rigidbody



물체에 Rigidbody를 넣지않고 필요할 때만 잠깐 동안 Rigidbody를 물체에 넣어주는 것이다. 게임 실행시 시스템 리소스를 조금이나마 아낄 수 있다.


아래 예시를 보자.



1
2
3
void AddRigidbody(){ //함수가 호출되면 Hierarchy 창 안에 있는 Box_s 안에 있는 box0~ 물체에 Rigidbody를 넣어준다.
GameObject.Find("Box_s/box"+i).AddComponent("Rigidbody");
}


이 것 또한 응용분야가 다양할거라 생각되기 때문에 사용자의 참신한 아이디어를 기대해본다.



http://daslyee.tistory.com/39

반응형
반응형

unity3d의 씬내에 표시되는 객체들은 모두 GameObject로 구성되고,

세부 항목들이 Component로 포함되어 계층을 이루고 있다.

   

빈 GameObject를 만들고, 여러 항목을 추가해 사용할 수 있어

필요한 오브젝트들을 관리하기 편리하다.

   

하지만 스크립트상에서 각 오브젝트에 접근하기가 애매할 수 있는데,

이와 관련해 아는 내용만 포스팅한다.

   

1. Hierarchy 에서 게임오브젝트 찾기

전역메쏘드인 GameObject.Find() / GameObject.FindWithTag() 로 찾는다.

GameObject obj = GameObject.Find( typeof(MyObject) );

GameObject obj = GameObject.FindWithTag("MyObjectTag");

   

2. 부모 자식 오브젝트 접근

GameObject 는 자식이나 부모 객체 정보를 가지고 있는데, 실제론 GameObject가 가지고

있다기 보다 GameObject의 컴포넌트인 Transform 객체가 가지고 있다.

Transform은 월드좌표, 로칼좌표등을 관리하는 녀석이라

이녀석을 통해 부모나 자식 객체를 얻어올 수 있다.

   

GameObject child = transform.Find("ChildObject").gameObject;

GameObject parent = transform.parent.gameObject;

GameObject child2 = transform.Find("Child/ChildObject").gameObject;

   

3. 스크립트에 접근

게임 오브젝트까지 접근을 했으면, 포함된 컴포넌트는 GetComponent로 구한다.

MyBehaviour myScript = (MyBehaviour) child.GetComponent( typeof( MyBehaviour ) );

myScript.Something();

   

4. 자식 오브젝트 리스트

자식 오브젝트를 배열로 가져와 순차적으로 어떤 작업을 할 때 사용한다.

Transform[] objList = gameObject.GetComponentsInChildren(typeof(Transform));

foreach( Transform child in objList )

{

child.gameObject.GetComponent( ....);

   

}

   

   

5. 오브젝트 활성화/비활성화

초기에는 비활성화 되어있다가, 어느 순간에 각 오브젝트들을 활성화 할 필요가 있다.

이경우 각 오브젝트를 찾아 active = true; 를 해주어야 하는데,

   

gameObject.SetActiverecursively( true );

   


http://liveupdate.tistory.com/15


반응형
반응형


http://cafe.naver.com/unityhub/25942


아래 개미개발자님의 코드 스타일의 최적화 이슈의 문제에 질답이 오가던 중, 
메테오님의 지적으로 동안 제가 잘 못 알고 있었던게 있었더군요. 

그래서 많은 분들이 알고 계시지만, 혹시나 다른 분들도 같은 오해에 빠져 계시지 않은까 해서 글을 남깁니다. 

논쟁은 new Vector3()를 하면 할당 되는 곳이 힙이냐 스택이냐 였습니다.

일단 제가 정보를 얻은 곳은,

===========================================================
1.

"vector야 말로 string 과 더불어 가장 많이 생성되는 클래스가 아닐까;

특히 vector3 v= new vector3(1,2,3) 이런거,.. 근데 이 벡터를 new로 할당하게 되면 heap에서 메모리 할당이 일어난다고 한다. 뭐 당연하 이야기인데..."

2.

"메소드 안에서 new 

Vector3를 메소드에서 new로 생성하는 코드들. 변수가 가비지로 생성한다. 
자주 호출되는 경우. 멤버변수로 선언해서 사용하자. 
메소드 안에서만 사용되는 인스턴스. 
구조체로 바꾸자. 스택에 생성되니까 "
========================================================


보통 c++, java에서 new를 하면 구조체도 힙에 할당되는데, 아마도 이 고정 관념때문에 생긴게 아닌가 합니다.

메테오님이 스택에 생성된다는 말을 듣고 찾아보니,


========================================================
using System;
struct SimpleStruct
{
    (생략)
}

class TestClass
{
    public static void Main()
    {
        SimpleStruct ss = new SimpleStruct();
       (생략)
    }
}

Heap or Stack?
When you call the New operator on a class, it will be allocated on the heap. 
However, when you instantiate a struct, it gets created on the stack. 
This will yield performance gains. Also, you will not be dealing with references 
to an instance of a struct as you would with classes. 
You will be working directly with the struct instance. 
Because of this, when passing a struct to a method, 
it's passed by value instead of as a reference.
========================================================

그리고 유니티 코리아 질답에

transform 의 position 을 변경하고자 set 함수를 사용하였는데 값이 반영이 되지 않더라고요,

그런데 transform.position = new Vector3( x, y, z); 처럼 새로 할당하니 값이 반영 되고요.

인터넷에 뒤져봐도 다들 새로 할당하는 것으로 코딩이 되더라고요.

단순 값만 변경하고자 하는데 할당을 해야하는지 의문이 듭니다.


"new로 할당한다고 하더라도 구조체는 여전히 스택에 생성되는 값 타입이다. 
C#이 이 문법을 지원하는 이유는 통일성과 생성자 호출을 위해서이다."

라는 답글이 달려있었더군요.

뭐, 공식 문서에서 스택에 저장 된다고 하니 믿어야겠지만, 유니티가 뒤통수 치는거 잘하니 한번 검증 해봤습니다.
공포의 업데이트문에 

case1 클래스 생성과 
case2 Vector, Color 구조체 생성

을 각각 10만번 루트를 돌려봤습니다.

결과는 case1 의 경우는 5.3MB의 가비지가 생기는데, case2는 가비지 자체가 생기기 않는 군요.


결론은 

"걱정 없이 new Vector3() 쓰세요."

입니다. 


-------------검증 코드---------------
public class NewBehaviourScript : MonoBehaviour {

public class Test{
int i,j,k,m,q,e,f,g,x,c,h,y;
}
void Start () {
}
void Update () {

for (int i=0; i<100000; i++) {
// case 1 클래스 할당 소스
Test t=new Test();

// case 2 구조체 할당 소스
Vector3 v = new Vector3 (); 
v.x = 1f;
v.y = 1f;
v.z = 1f;

Color c=new Color();
c.r=1f;
c.g=1f;
c.b=1f;
c.a=1f;
}
}

case 1 프로 파일러 (GC Alloc 5.3MB)




case 2 프로파일러 (GC Alloc 0B)



참고로 메모리풀관려 질문이 많이 올라와서 위 내용을 찾다가 좋은 글이 있어 링크 남깁니다.


반응형
반응형

유니티에서는 미리 사용할 게임 오브젝트를 만들어 놓은 다음,
Prefab으로 만들어서, 게임 상에서 재사용을 용이하게 함


http://metalkim.tistory.com/342


[Unity 유니티] Prefab(프리펩) 이란?


1. Prefab ( 프리펩 ) 이란?


Prefab은, 유니티의 핵심 기능 중 하나로써 게임 오브젝트를 하나의 형틀로 만들어 언제든지 인스턴스화 할 수 있도록 만드는 것이다.

예를 들어보면,


Cube 1 이란 오브젝트를 만들고, 프리펩화 하면, 게임 도중 간단한 호출만으로 Cube 1을 양산할 수 있게 된다.

이것은 현실의 도장과도 비슷한 개념이다.


만들어진 도장만 있으면, 잉크만으로도 언제든지 도장에 파여진 문양을 새길 수 있는 것과 같다.


이 프리펩은 언제든지 수정이 가능하고, 모든 프리펩에 적용이 가능하다. ( 도장의 모양을 바꾸는것을 생각하면 된다. )






2. Prefab (프리펩) 화 하는 방법


프리펩 화 하는 방법은 매우 간단하다.


계층 뷰 ( Hierarchy ) 에서 프로젝트 뷰 ( Project ) 뷰로 오브젝트를 드래그 하는것만으로도 프리펩 화가 가능하며, 미리 만들어 놓은 프로젝트 뷰의 프리펩에 드래그 해도 해당 오브젝트가 프리펩 화 된다.


마찬가지로 프로젝트 뷰에서 계층 뷰나 씬 뷰에 드래그를 하는 것 만으로도 오브젝트를 찍어낼 수 있다.


혹은 함수 Instantiate 로도 프리펩을 오브젝트로 불러낼 수 있다.



반응형
반응형

https://www.youtube.com/watch?v=hTdTiIEluR4





ppt 자료

http://www.slideshare.net/MrDustinLee/ss-33346625



반응형
반응형

https://msdn.microsoft.com/ko-kr/library/58918ffs.aspx


형식에 대한 System.Type 개체를 얻는 데 사용됩니다. typeof 식의 형식은 다음과 같습니다.

System.Type type = typeof(int); // type 을 얻어와 인스턴스를 만들 수도 있음 -> http://3dmpengines.tistory.com/1507

식의 런타임 형식을 얻으려면 다음 예제와 같이 .NET Framework 메서드 GetType을 사용합니다.

int i = 0;
System.Type type = i.GetType();

typeof 연산자는 오버로드되지 않습니다.

typeof 연산자는 열린 제네릭 형식에도 사용할 수 있습니다. 형식 매개 변수가 둘 이상인 형식을 지정할 때는 적절한 수의 쉼표를 사용해야 합니다. 다음 예제에서는 메서드의 반환 형식이 제네릭 IEnumerable<T>인지 여부를 확인하는 방법을 보여 줍니다. 메서드는 MethodInfo 형식의 인스턴스라고 가정합니다.

string s = method.ReturnType.GetInterface
    (typeof(System.Collections.Generic.IEnumerable<>).FullName);

public class ExampleClass
{
   public int sampleMember;
   public void SampleMethod() {}

   static void Main()
   {
      Type t = typeof(ExampleClass);
      // Alternatively, you could use
      // ExampleClass obj = new ExampleClass();
      // Type t = obj.GetType();

      Console.WriteLine("Methods:");
      System.Reflection.MethodInfo[] methodInfo = t.GetMethods();

      foreach (System.Reflection.MethodInfo mInfo in methodInfo)
         Console.WriteLine(mInfo.ToString());

      Console.WriteLine("Members:");
      System.Reflection.MemberInfo[] memberInfo = t.GetMembers();

      foreach (System.Reflection.MemberInfo mInfo in memberInfo)
         Console.WriteLine(mInfo.ToString());
   }
}
/*
 Output:
    Methods:
    Void SampleMethod()
    System.String ToString()
    Boolean Equals(System.Object)
    Int32 GetHashCode()
    System.Type GetType()
    Members:
    Void SampleMethod()
    System.String ToString()
    Boolean Equals(System.Object)
    Int32 GetHashCode()
    System.Type GetType()
    Void .ctor()
    Int32 sampleMember
*/





http://www.lionheart.pe.kr/?document_srl=883&mid=board_uFoa63&listStyle=viewer



유니티 엔진에서  C# 싱글톤

 

public class MyHttp : MonoBehaviour

{


      private static MyHttp s_Instance = null;
 
      public static MyHttp instance 
      {
            get 
            {
                   if (s_Instance == null) 
                   {
                         s_Instance = FindObjectOfType(typeof(MyHttp)) as MyHttp;
                   }

                   // 여전히 null일 경우 새로운 인스턴스 생성
                   if (s_Instance == null) 
                   {
                         GameObject obj = new GameObject("MyHttp");
                         s_Instance = obj.AddComponent(typeof (MyHttp)) as MyHttp;
                   }
                   return s_Instance;
             }
      }
 
      void OnApplicationQuit() 
      {
           s_Instance = null;
      }


}

 

 

유니티 엔진에서  인스턴스 중복 방지

 

public class GameMgr : MonoBehaviour 
{
 
        public static GameMgr instance;
 
        void Awake()
        {
                  if(instance != null)  //  DontDestroyOnLoad(this.gameObject); 인해 해당 오브젝트가 계속 쌓이는 것을 방지
                  {
                            //Destroy(this); // 해당 스크립트를 삭제
                           Destroy(this.gameObject); // 해당 오브젝트를 삭제
                           return;
                  }

                  instance = this;
                 //DontDestroyOnLoad(this);
                 DontDestroyOnLoad(this.gameObject);
                 Application.targetFrameRate = 60; // 최대 프레임은 60으로 지정
        }


}


 

반응형
반응형


http://gilverlight.net/3439



유니티는 스크립트 언어로 C#을 지원하고 있습니다.

 

윈도우 운영체제 사용자들 중

"C# 언어를 이용한 개발은 비주얼 스튜디오에서 하는 것이 가장 편하다"는데에

이견을 가지시는 분이 없으실 것입니다.

 

저도 그렇습니다.

나쁘게 말하면 타성에 젖었다라고 할 수 있지만,

뭔가 쓰던 가락이 있어서라고 할까요?

 

자, 그럼 유니티 편집기에서 비주얼 스튜디오를 기본 스크립트 편집기로

지정하는 방법을 알아보겠습니다.

 

 

1. 유니티 편집기를 띄웁니다.

 

2. 메뉴 중 [Edit - Preferences...]를 선택합니다.

 

 

 

3. 왼쪽 목록에서 [External Tools]를 선택합니다. 그리고 오른편의 [External Script Editor]에서

Microsoft Visual Studio(설치 버전은 다를 수 있습니다.)를 선택합니다.

 

만약, 목록에서 Microsoft Visual Studio가 보이지 않으시면, [Browse...]를 선택하시고,

Visual Studio가 설치된 장소를 찾아가 devenv.exe를 선택해 주시면 됩니다.

 

 

 

4. 이렇게 설정하고 나면, 유니티 편집기에서 스크립트에 더블 클릭을 했을 때,

자동으로 비주얼 스튜디오가 그 스크립트를 열어 줍니다.

 

반응형
반응형

http://unitykoreawiki.com/



유니티 공식문서

edit SideBar

안녕하세요. 유니티코리아 공식문서 사이트에 오신 것을 환영합니다.

본 페이지는 www.unitykoreawiki.com 과 연결되어 있습니다.

더 큰 페이지에서 보시려면 위의 링크를 눌러주세요.

본 페이지에서는 유니티 유저 매뉴얼과 레퍼런스 매뉴얼 등의 유니티 공식문서들을 보실 수 있게 컨텐츠들을 제공하고 있습니다.

목차








유니티 매뉴얼

유니티에 오신 것을 환영합니다.

유니티는 사용자 분들이 최고의 인터랙티브 환경과 멀티미디어의 경험을 창조하는 것을 돕기 위하여 만들어졌습니다. 이 매뉴얼은 여러분이 유니티를 어떻게 이용하는지 기초부터 고급 테크닉까지 모두 배울 수 있게 해드립니다. 처음부터 끝까지 읽으셔도 좋고 참조용으로 사용하셔도 좋습니다.

이 매뉴얼은 몇가지 다른 섹션으로 나뉘어 있습니다. 첫 섹션, 유저 가이드(User Guide) 는 유니티 인터페이스, 에셋 작업방식(워크플로우), 그리고 게임을 만드는 기초에 대한 소개입니다. 만약 여러분이 유니티를 처음 접하시는 것이라면 유니티 기초(Unity Basics) 안의 섹션들을 읽고서 배우시는 것을 추천해드립니다.

iOS 가이드 (iOS Guide) 는 iOS 특정 스크립팅 API, 최적화, 일반 플랫폼 개발 질문들 등의 iOS 관련 토픽들을 보여줍니다.

안드로이드 가이드 (Android Guide) 는 안드로이드 SDK 셋업과 일반 개발 질문들 등의 안드로이드 관련 토픽들을 보여줍니다.

다음 섹션인 FAQ 는 몇몇 절차들을 밟아야 해결 되는 일반적인 작업 수행 방법들에 관련 흔히 물어보는 질문들을 묶어논 것입니다.

마지막 섹션인 고급 (Advanced) 섹션은 게임 최적화, 셰이더, 파일 크기, 배치 (deployment) 등의 토픽들을 보여줍니다.

그리고 유니티를 사용한 게임 제작의 다양한 가능성들을 자세하게 살펴보기 위해 레퍼런스 매뉴얼 (Reference Manual) 과 스크립팅 레퍼런스 (Scripting Reference) 도 읽어보시기 바랍니다.

만약 찾는 답을 구하지 못하셨을 경우, www.unity3dkorea.com 의 유니티 Q&A 섹션을 이용하시거나 Unity Answers , Unity Forums 등을 이용하시면 답을 찾으실 수 있습니다..

즐겁게 읽어보시기 바랍니다, 
유니티 팀

유니티 매뉴얼 가이드는 특정 플랫폼에만 해당되는 섹션들로 구성되어 있습니다. 보시길 원하는 플랫폼 섹션으로 이동하셔서 보시기 바랍니다. 특정 플랫폼 관련 정보는 각 페이지에 보여주기 삼각형을 누르시면 보실 수 있습니다. 

    

목차


반응형
반응형

http://unitykoreawiki.com/index.php?n=KrMain.UnityHotkeys


반응형

+ Recent posts