레이블이 Unity3D인 게시물을 표시합니다. 모든 게시물 표시
레이블이 Unity3D인 게시물을 표시합니다. 모든 게시물 표시

2019년 4월 12일 금요일

# 유니티엔진에서 Sqlite 적용 방법 Flow

아래 순서대로 진행하면 될 것이다.. 아마도(?)

1. Create new folder under Assets Folder Rename it Plugins .

2.
Copy sqlite3.def and sqlite3.dll into Assets/Plugins in your unity project .You can download these files here http://www.sqlite.org/download.html for windows (Precompiled Binaries for Windows)


3.
Download SQLite Browser http://sourceforge.net/projects/sqlitebrowser/ or http://sqliteadmin.orbmu2k.de/ download SQLite Administrator tool


4.
Create Database in Assets folder in your unity project using SQLite Browser.


5.
Copy System.Data.dll and Mono.Data.Sqlite.dll from **C:\Program Files (x86)\Unity \Editor\Data\Mono\lib\mono\2.0* and paste them in your Assets/Plugins* folder in your unity project.


6.
Add these namespaces using Mono.Data.Sqlite;
    using System.Data; using System;

아래 그림은, 개인 프로젝트에서 해당 파일들을 어디에다 위치시켰는지 보여준다.
(라이브러리는 Plugins 폴더에 넣어야하니까.)




그리고 다음처럼 이용하고 있다. ( in Code-level)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
using (IDbConnection dbconn = (IDbConnection)new SqliteConnection(conn.ToString()))
            {
                using (IDbCommand dbcmd = dbconn.CreateCommand())
                {
                    dbconn.Open(); //Open connection to the database.
                    string sqlQuery = "SELECT name, type, amount, id FROM USER_ITEM";
                    dbcmd.CommandText = sqlQuery;
                    using (IDataReader reader = dbcmd.ExecuteReader())
                    {
                        while (reader.Read())
                        {
                            USER_ITEM userItem;
                            userItem.name = reader.GetString(0);
                            userItem.type = reader.GetInt32(1);
                            userItem.amount = reader.GetInt32(2);
                            userItem.id = reader.GetString(3);
                            userItemList.Add(userItem);
                        }
                        reader.Close();
                    }
                }
                dbconn.Close();
            }


p.s. 32bit, 64bit 별로 실행파일을 만드려는 경우
     https://docs.unity3d.com/Manual/PluginsForDesktop.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
파일 읽기 가능, 쓰기 불가능


=========================================================================================

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

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

숨겨진 폴더들

점(.)으로 시작하는 폴더(예. “.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

# 페이스북 및 구글 플레이 서비스 연동시 생기는 문제점

일단 상황은 아래와 같다.

-> GPG : GooglePlayGamesPlugin-0.9.32.unitypackage
-> 페이스북 : facebook-unity-sdk-7.5.0

위와 같은 버전상태에서, 테스트 중인 프로젝트에 추가했다.

먼저 GPG에서는 제대로 동작했는데 페이스북 SDK를 추가하면서 dex 파일 생성에 실패가 뜨더라..

에러메세지는 요약하면 -> Unable to convert classes into dex format

일단 아래 레퍼런스 문서를 보자.


내용은, jar 파일이 충돌해서 인데 문제점은 크게 오래걸리지 않고 찾았다.



support-v4-23.2.1 이란 파일이 보이는데 aar확장자를 가지고있다.
이 파일이 GPG와 FaceBook SDK 둘다 각기 다른버전으로 들어있었고
서로 충돌을 일으켰던 것 같다.

세부 원인에 대해서는 좀더 찾아봐야하겠다.

일단 중복되는 파일중 하나를 지우면 동작은 한다.

# 유니티 C# 버전 기본 Behavior Tree ( 가장 기초적인 형태)

참고한 레퍼런스 :
 1) http://kindtis.tistory.com/590
 2) http://www.gamasutra.com/blogs/ChrisSimpson/20140717/221339/Behavior_trees_for_AI_How_they_work.php

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
using UnityEngine;
using System.Collections;
using System;
using System.Collections.Generic;

public abstract class Node
{
    public abstract bool Invoke();
}

public class CompositeNode : Node
{
    public override bool Invoke()
    {
        throw new NotImplementedException();
    }

    public void AddChild(Node node)
    {
        childrens.Push(node);
    }

    public Stack<Node> GetChildrens()
    {
        return childrens;
    }
    private Stack<Node> childrens = new Stack<Node>();
}

public class Selector : CompositeNode
{
    public override bool Invoke()
    {
        foreach (var node in GetChildrens())
        {
            if (node.Invoke())
            {
                return true;
            }
        }
        return false;
    }
}

public class Sequence : CompositeNode
{
    public override bool Invoke()
    {
        foreach (var node in GetChildrens())
        {
            if (!node.Invoke())
            {
                return false;
            }
        }
        return true;
    }
}

public class MoveForTarget : Node
{
    public MonsterController monController
    {
        set { _monController = value; }
    }
    private MonsterController _monController;
    public override bool Invoke()
    {
        _monController.MoveForTarget();
        return true;
    }
}

public class RotAroundTarget : Node
{
    public MonsterController monController
    {
        set { _monController = value; }
    }
    private MonsterController _monController;
    public override bool Invoke()
    {
        _monController.RotAroundTarget();
        return true;
    }

}

public class StartAttack : Node
{
    public MonsterController monController
    {
        set { _monController = value; }
    }
    private MonsterController _monController;
    public override bool Invoke()
    {
        _monController.StartAttack();
        return true;
    }
}
public class StopAttack : Node
{
    public MonsterController monController
    {
        set { _monController = value; }
    }
    private MonsterController _monController;
    public override bool Invoke()
    {
        _monController.StopAttack();
        return true;
    }
}

public class IsTooCloseTarget : Node
{
    public MonsterController monController
    {
        set { _monController = value; }
    }
    private MonsterController _monController;
    public override bool Invoke()
    {
        if (_monController.IsTooCloseTarget()) return true;
        else return false;
    }
}

public class DeadProcess : Node
{
    public MonsterController monController
    {
        set { _monController = value; }
    }
    private MonsterController _monController;
    public override bool Invoke()
    {
        _monController.DeadPrcoess();
        return true;
    }
}

public class IsDead : Node
{
    public MonsterController monController
    {
        set { _monController = value; }
    }
    private MonsterController _monController;
    public override bool Invoke()
    {
        if (_monController.IsDead())
        {
            return true;
        }
        else
            return false;
    }
}

public abstract class BT_base : MonoBehaviour
{
    public abstract void Init();
    public abstract void StartBT();
    public abstract void StopBT();
    public abstract IEnumerator BehaviorProcess();
}


행동트리의 베이스 클래스이며 아래 클래스는 이를 상속하여 재정의한 클래스이다. ( 노말타입의 몬스터 행동트리 )

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
using UnityEngine;
using System.Collections;
using System;
using System.Collections.Generic;

public class BT_normalAI : BT_base {
    private Sequence root = new Sequence();
    private Selector selector = new Selector();
    private Sequence seqMovingAttack = new Sequence();
    private Sequence seqDead = new Sequence();

    private MoveForTarget moveForTarget = new MoveForTarget();
    private StartAttack startAttack = new StartAttack();
    private StopAttack stopAttack = new StopAttack();
    private IsTooCloseTarget isTooClose = new IsTooCloseTarget();
    private RotAroundTarget rotTarget = new RotAroundTarget();
    private IsDead isDead = new IsDead();
    private DeadProcess deadProcess = new DeadProcess();

    private IEnumerator behaviorProcess;
    private MonsterController monController;

    public override void Init()
    {
        monController = gameObject.GetComponent<MonsterController>();
        monController.Init();

        root.AddChild(selector);

        selector.AddChild(seqMovingAttack);
        selector.AddChild(seqDead);

        moveForTarget.monController = monController;
        startAttack.monController = monController;
        stopAttack.monController = monController;
        isTooClose.monController = monController;
        rotTarget.monController = monController;
        isDead.monController = monController;
        deadProcess.monController = monController;

        seqMovingAttack.AddChild(isTooClose);
        seqMovingAttack.AddChild(moveForTarget);
        seqMovingAttack.AddChild(rotTarget);
        seqMovingAttack.AddChild(startAttack);

        seqDead.AddChild(deadProcess);
        seqDead.AddChild(stopAttack);
        seqDead.AddChild(isDead);

        behaviorProcess = BehaviorProcess();
    }
    public override void StartBT()
    {
        StartCoroutine(behaviorProcess);
    }
    public override void StopBT()
    {
        StopCoroutine(behaviorProcess);
    }

    public override IEnumerator BehaviorProcess()
    {
        while (!root.Invoke())
        {
            yield return new WaitForEndOfFrame();
        }
        Debug.Log("behavior process exit");
    }
}

# NGUI 컴포넌트 교체하는 샘플 코드

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
using UnityEngine;
using System.Collections.Generic;
using UnityEditor;
using System.IO;

// NGUI 라이브러리의 경우
// UIButton 컴포넌트는 UIButtonColor 을 상속받는다.
// UIButtonColor에는 tweenTarget 변수가 있으며, 이 변수가 tweening을 할 대상 오브젝트를 가진다.
// 근데, UIButtonColor.OnInit 메소드에 Unity 버전 4.6, 4.3, 4.5등에 대해 tweenTarget에 Light 컴포넌트가 없으면
// tweenTarget을 null로 할당하는 코드가 있다. 우선 이부분을 주석처리하고 하면 에디터상에서 변경한 버튼컴포넌트 프리팹이
// 씬 화면에 로딩되어도 정상적으로 tweenTarget레퍼런스가 끊기지 않는다. 

public class LegacyButtonChanger : Editor
{
 private static UIButtonMessage[] legacyUIButtonsMsgs;
 private static UIEventTrigger[] legacyUIEventTriggers;

 [MenuItem("LegacyButtonChanger/ChangeLegacyButtons")]
 public static void ChangeLegacyButtons()
 {
  Debug.Log("<color=green><b>[Changer_Logging]</b></color>Start Log legacy buttons");
  string[] filesFolderPath = Directory.GetFiles("Assets/Prefabs/Lobby/");

  // 주어진 파일 폴더경로에서 .prefab 확장의 파일들을 검색한다.
  // 해당 파일에 child로 속한 파일들은 얻을 수 없으므로 해당 파일을 root로 하여
  // Legacy한 컴포넌트들을 가져온다.
  foreach (var p in filesFolderPath)
  {
   if (Path.GetExtension(p).Equals(".prefab"))
   {
    GameObject root = AssetDatabase.LoadAssetAtPath(p, typeof(Object)) as GameObject;
    if (root == null) Debug.Log("Loaded Asset is NULL");
    else
    {
     ExtractLegacyBtns(root);
     ChangeBtnComponent();
     //EditorUtility.SetDirty(root);
    }
   }
  }
  Debug.Log("<color=green><b>[Changer_Logging]</b></color>Finish Log legacy buttons");
 }

 private static void ExtractLegacyBtns(GameObject root)
 {
  legacyUIButtonsMsgs = root.GetComponentsInChildren<UIButtonMessage>();
  legacyUIEventTriggers = root.GetComponentsInChildren<UIEventTrigger>();
 }
 private static void ChangeBtnComponent()
 {
  // UIButtonMessages..
  if (legacyUIButtonsMsgs != null)
  {
   foreach (var msgComp in legacyUIButtonsMsgs)
   {
    string targetFuncName = msgComp.functionName;
    if (msgComp.target == null)
    {
     Debug.Log(string.Format(""));
     continue;
    }
    if(targetFuncName == null)
    {
     Debug.Log(string.Format(""));
     continue;
    }
    if(targetFuncName == "")
    {
     Debug.Log(string.Format(""));
     continue;
    }
    
    MonoBehaviour[] targetMonoComponents = msgComp.target.GetComponents<MonoBehaviour>();
    if (targetMonoComponents != null)
    {
     foreach (var targetMonoComp in targetMonoComponents)
     {
      if (targetMonoComp == null)
      {
       Debug.Log("Target의 컴포넌트가 Missing 이거나 Null 입니다.");
       continue;
      }

      System.Type scriptType = targetMonoComp.GetType();
      if (scriptType.GetMethod(targetFuncName) != null)
      {
       Debug.Log(string.Format("{0} msgComponent :: {1} is, type--> {2}"
               , msgComp.name, targetMonoComp.name, targetMonoComp.GetType()));

       GameObject toBeAttachedObj = msgComp.gameObject;
       // 타겟의 모노비헤이비어와 메소드이름을 주어 생성하면
       // 이벤트델리게이트 클래스 안에서 해당 메소드를 리플렉션하여 파라미터가 필요한지 아닌지를 확인한다.
       //
       EventDelegate ed = new EventDelegate(targetMonoComp, targetFuncName);
       EventDelegate.Parameter param = new EventDelegate.Parameter();
       // 파라미터로, 새로운 UIButton 컴포턴트가 붙어야할 오브젝트를 할당해준다.
       param.obj = toBeAttachedObj;
       // 추출한 타겟의 메소드에서 파라미터를 요구한다면.
       if(ed.parameters != null)
       {
        ed.parameters[0] = param;
       }
       else // 추출한 타겟의 메소드에서는 파라미터가 없다.
       {
        // 파라미터를 할당해 줄 필요가 없다.
       }
       AddUIButtonComp(toBeAttachedObj, ed);
       RemoveUIButtonMessage(toBeAttachedObj);
       break;
      }
      else
      {
       // GetType().GetMethod() 리플렉션에서 메소드를 구해오는건 public만 가능.
       // 실패한다면 해당 메소드 접근자는 private 이다.
       // 실패한 경우, 해당 메소드가 어느 스크립트에 존재하는지 로그를 남겨야한다.
       Debug.Log(string.Format("<color=red><b>[Changer_Alert]</b></color> <b>@Private Accesser Find@"
        +"</b> scriptName : {0}, methodName :{1}",
        scriptType.Name, targetFuncName));
      }
     }
    }
   }
  }

  // UIEventTriggers..
  if (legacyUIEventTriggers != null)
  {
   foreach (var trigger in legacyUIEventTriggers)
   {
    Debug.Log(string.Format("attached {0} prefab's parent : {1}, :: {2} is trigger, {3} (OnClick..) ",
           trigger.gameObject, trigger.gameObject.transform.parent, trigger.name, trigger.onClick));
    GameObject toBeAttachedObj = trigger.gameObject;
    AddUIButtonComp(toBeAttachedObj, trigger.onClick);
    RemoveUIEventTrigger(toBeAttachedObj);
   }
  }
 }

 private static void AddUIButtonComp(GameObject toBeAttachedObj, EventDelegate onClickEvent)
 {
  UIButton uiButtonComp = toBeAttachedObj.GetComponent<UIButton>();
  if (uiButtonComp != null)
  {
   bool isExist = uiButtonComp.onClick.Exists((other) => {
    return other.methodName.Equals(onClickEvent.methodName);
   });
   if (isExist == false)
   {
    uiButtonComp.onClick.Add(onClickEvent);
   }
   // onClick list에 동일한 이벤트가 있던 없던, tween target은 설정해준다.
   uiButtonComp.tweenTarget = toBeAttachedObj;
  }
  else
  {
   UIButton addedComponent = toBeAttachedObj.AddComponent<UIButton>();
   addedComponent.onClick.Add(onClickEvent);
   addedComponent.tweenTarget = toBeAttachedObj;
  }
 }
 private static void AddUIButtonComp(GameObject toBeAttachedObj, List<EventDelegate> onClickEvents)
 {
  UIButton uiButtonComp = toBeAttachedObj.GetComponent<UIButton>();
  if (uiButtonComp != null)
  {
   uiButtonComp.onClick = onClickEvents;
   uiButtonComp.tweenTarget = toBeAttachedObj;
  }
  else
  {
   UIButton addedComponent = toBeAttachedObj.AddComponent<UIButton>();
   addedComponent.onClick = onClickEvents;
   addedComponent.tweenTarget = toBeAttachedObj;
  }
 }

 private static void RemoveUIEventTrigger(GameObject obj)
 {
  if (obj.GetComponent<UIEventTrigger>() != null)
   DestroyImmediate(obj.GetComponent<UIEventTrigger>(), true);
 }
 private static void RemoveUIButtonMessage(GameObject obj)
 {
  if (obj.GetComponent<UIButtonMessage>() != null)
   DestroyImmediate(obj.GetComponent<UIButtonMessage>(), true);
 }


}

# 간단한 장애물 생성기 ( 유니티3D)

아마 유니티3D 맵 생성 관련 튜토리얼을 보고.. 했던걸로 기억한다. 소스만 살짝 수정했었나..
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public struct TileCoord
{
    public int tileX;
    public int tileY;
    public TileCoord(int _x, int _y)
    {
        tileX = _x;
        tileY = _y;
    }

    public static bool operator ==(TileCoord a, TileCoord b)
    {
        if ((a.tileX == b.tileX) && (a.tileY == b.tileY)) return true;
        else return false;

    }
    public static bool operator !=(TileCoord a, TileCoord b)
    {
        if ((a.tileX == b.tileX) && (a.tileY == b.tileY)) return false;
        else return true;
    }
}

public class MapGenerator : MonoBehaviour {

    [SerializeField]
    private Transform mapTilePrefab;
    private Transform mapTileGroup;

    [SerializeField]
    private Transform obstaclePrefab;
    private Transform obstacleGroup;

    //인스펙터 조절 변수들.
    public int tileNumX;
    public int tileNumY;
    [Range(0, 1)]
    public float outLinePercent;
    [Range(0, 1)]
    public float obstaclesPercent;
    public int randSeed = 1;

    private List<TileCoord> list_tileCoord;
    private Queue<TileCoord> queue_randTileCoord;
    // player spwan position.
    private TileCoord mapCentre; 

 void Start () {
        GenerateMap();
    }
    public void GenerateMap()
    {
        mapCentre.tileX = tileNumX / 2;
        mapCentre.tileY = tileNumY / 2;

        string groupName = "mapTileGroup";
        if (transform.FindChild(groupName))
        {
            DestroyImmediate(transform.FindChild(groupName).gameObject);
        }
        mapTileGroup = new GameObject(groupName).transform;
        mapTileGroup.parent = transform;

        groupName = "obstacleGroup";
        if (transform.FindChild(groupName))
        {
            DestroyImmediate(transform.FindChild(groupName).gameObject);
        }
        obstacleGroup = new GameObject(groupName).transform;
        obstacleGroup.parent = transform;

        GenTileCoords();
        GenRandTileCoords();
        GenTiles();
        GenObstacles();
    }

    private void GenTiles()
    {
        for (int x = 0; x < tileNumX; x++)
            for (int y = 0; y < tileNumY; y++)
            {
                Transform obj = Instantiate(mapTilePrefab,
                    new Vector3(x, 0, y),
                    Quaternion.Euler(90, 0, 0)) as Transform;
                obj.parent = mapTileGroup;
                obj.localScale = Vector3.one * (1 - outLinePercent);
            }
    }

    private void GenTileCoords()
    {
        list_tileCoord = new List<TileCoord>();
        for(int x = 0; x < tileNumX; ++x)
            for(int y = 0; y < tileNumY; ++y)
            {
                TileCoord tileCoord;
                tileCoord.tileX = x;
                tileCoord.tileY = y;
                list_tileCoord.Add(tileCoord);
            }
    }
    private void GenRandTileCoords()
    {
        queue_randTileCoord = new Queue<TileCoord>(Utility.ShuffleArray(list_tileCoord.ToArray(),
           randSeed));
    }

    private TileCoord GetRandomCoord()
    {
        TileCoord tileCoord = queue_randTileCoord.Dequeue();
        queue_randTileCoord.Enqueue(tileCoord);

        return tileCoord;
    }

    private Vector3 CoordToPosition(TileCoord _coord)
    {
        return new Vector3(_coord.tileX, 0, _coord.tileY);
    }

    /// <summary>
    /// Flood fill 알고리즘을 이용한 중복제거.
    /// </summary>
    /// <param name="obstacleMap"></param>
    /// <param name="currentObstacleNum"></param>
    /// <returns></returns>
    private bool IsMapFullyAccessible(bool [,] obstacleMap, int currentObstacleNum)
    {
        bool[,] mapFlags = new bool[obstacleMap.GetLength(0), obstacleMap.GetLength(1)];
        Queue<TileCoord> queue_nodes = new Queue<TileCoord>();
        queue_nodes.Enqueue(mapCentre);
        mapFlags[mapCentre.tileX, mapCentre.tileY] = true;

        int accessibleTileCount = 1;

        while(queue_nodes.Count > 0)
        {
            TileCoord tileCoord = queue_nodes.Dequeue();
            for(int x = -1; x <=1; x++)
                for (int y = -1; y <= 1; ++y){ 
                    int neighborX = tileCoord.tileX + x;
                    int neighborY = tileCoord.tileY + y;
                    if(x == 0 || y == 0){
                        if ((neighborX >= 0) && (neighborX < obstacleMap.GetLength(0)) &&
                           (neighborY >= 0) && (neighborY < obstacleMap.GetLength(1))){
                            if (!mapFlags[neighborX, neighborY] && !obstacleMap[neighborX, neighborY]){
                                mapFlags[neighborX, neighborY] = true;
                                queue_nodes.Enqueue(new TileCoord(neighborX, neighborY));
                                accessibleTileCount++;
                            }
                        }
                    }
                }
        }

        int targetAccessibleTileCount = (tileNumX * tileNumY) - currentObstacleNum;

        return accessibleTileCount == targetAccessibleTileCount;
    }
    
    private void GenObstacles()
    {
        bool[,] obstacleMap = new bool[tileNumX, tileNumY];

        int curObstacleNum = 0;
        int obstacleNum = (int)(tileNumX * tileNumY * obstaclesPercent);
        for (int idx = 0; idx < obstacleNum; ++idx)
        {
            TileCoord tileRandCoord = GetRandomCoord();
            obstacleMap[tileRandCoord.tileX, tileRandCoord.tileY] = true;
            curObstacleNum++;

            if ((tileRandCoord != mapCentre) && 
                IsMapFullyAccessible(obstacleMap, curObstacleNum))
            {
                Vector3 obstaclePos = CoordToPosition(tileRandCoord);
                obstaclePos.y += 0.5f;
                Transform newObstacle = Instantiate(obstaclePrefab,
                                                    obstaclePos,
                                                    Quaternion.identity) as Transform;
                newObstacle.parent = obstacleGroup;
            }
            else
            {
                obstacleMap[tileRandCoord.tileX, tileRandCoord.tileY] = false;
                curObstacleNum--;
            }
        }
    }   
}
그리고 이건 배열 요소 셔플링 하는 간단한 메서드
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
using UnityEngine;
using System.Collections;

/// <summary>
/// Fisher–Yates shuffle 알고리즘을 이용한 셔플.
/// </summary>
public static class Utility {

 public static T[] ShuffleArray<T>(T[] arr, int seed)
    {
        System.Random rand = new System.Random(seed);
        
        for(int idx = 0; idx < arr.Length-1; idx++)
        {
            int randNum = rand.Next(0, arr.Length);
            T tmp = arr[randNum];
            arr[randNum] = arr[idx];
            arr[idx] = tmp;
        }
        return arr;
    }
}

# 유니티3D 안드로이드 빌드 에서 Sqlite 사용방법















유니티엔진에서 안드로이드 빌드시에 Sqlite를 사용하고 싶다면

위 그림과 같이 플러그인 폴더에 Sqlite DLL 파일들을 설정해야 한다.


# 유니티3D 두 벡터의 사잇각 구하기

두 벡테사이의 사잇각은 방향에 따라 각이 다르게 측정된다.
자세한 내용은 참고 문서를 보도록하자.

 # ref 1 : http://bowbowbow.tistory.com/14
 # ref 2 : http://darkpgmr.tistory.com/121
 
1
2
3
4
5
6
7
8
9
public float GetAngleBetween3DVector(Vector3 vec1, Vector3 vec2)
    {
        float theta = Vector3.Dot(vec1, vec2) / (vec1.magnitude * vec2.magnitude);
        Vector3 dirAngle = Vector3.Cross(vec1, vec2);
        float angle = Mathf.Acos(theta) * Mathf.Rad2Deg;
        if (dirAngle.z < 0.0f) angle = 360 - angle;
        Debug.Log("사잇각 : " + angle);
        return angle;
    }
Acos() 함수의 경우, 0 ~ 180' 의 해당하는 radian 값을 돌려준다. 0~360' 의 범위로 계산하고 싶다면 두 벡터사이의 수직인 벡터를 구해 이 벡터의 z 값을 이용하면된다. 3차원 좌표의 x,z평면은 2차원 에서의 x,y 좌표계로 볼 수 있다. 따라서, z의 값이 2차원 좌표계의 y값으로 볼 수 있고 이는 결국 z의값이 사잇각을 구하는 벡터가 1~4사 분면에서 어디에 위치한지를 알 수 있다. ( = 사잇각의 방향성을 알 수 있다. ) 결국 z값(=2차원에서의 y값)이 음수라면 3, 4 분면에, 양수라면 1, 2 분면에 위치함을 알 수 있다. 만약 z값이 0 이라면 두 벡터의 사잇각은 180' -> 평행임을 의미한다.