We had to meet Drawcall problem in Mobile game developing using unity 3D.
You should solve it by 2 combination way.

1. Make shard material.
2. Combine mesh.

I writing down about "Combine mesh"

1. Make next script as CombineChild on Unity3D

using UnityEngine;
using System.Collections;

[RequireComponent(typeof(MeshFilter))]
[RequireComponent(typeof(MeshRenderer))]
public class CombineChild : MonoBehaviour
{
void Start()
{
Matrix4x4 thisTransform = transform.worldToLocalMatrix; 
MeshFilter[] meshFilters = GetComponentsInChildren<MeshFilter>();
CombineInstance[] combine = new CombineInstance[meshFilters.Length];
int i = 0;
while (i < meshFilters.Length)
{
combine[i].mesh = meshFilters[i].sharedMesh;
combine[i].transform = thisTransform * meshFilters[i].transform.localToWorldMatrix;
meshFilters[i].gameObject.SetActive(false);
i++;
}
transform.GetComponent<MeshFilter>().mesh = new Mesh();
transform.GetComponent<MeshFilter>().mesh.CombineMeshes(combine); //, true, true, true);

transform.GetComponent<MeshFilter>().mesh.RecalculateNormals();
transform.gameObject.SetActive(true);
}
}


2. and Please attach to parent gameobject.

Cheers
2017/10/10 02:42 2017/10/10 02:42
일전에 Mesh생성하는 방법에 대해 써 놓은 적이 있는데,

Link : http://www.wolfpack.pe.kr/853

문득 Hexa로 만드는것도 가능하지 않을까? 고민했습니다.
그래서, 만들어 봤습니다.

먼저 Mesh 만든 절차를 그대로 따라 했습니다.
1. 좌표를 Vector3 array로 만든다.
* Mesh의 경우는 단 4개의 Vertex로 구성되면 되기 때문에, 좌표는 총 4개를 만들면 되죠.
예를 들어, pivot point가 Center에 있는 4각의 Mesh라면,
(-1, -1, 0), (-1, 1, 0), (1, 1, 0), (1, -1, 0) >> 각각 0, 1, 2, 3 번 이라고 합시다.

2. Polygon Array를 만듭니다. 하나 중요한건 Unity3D는 왼손 좌표계이므로 Surface가 -Z축에서 보여지려면 시계방향으로 Array를 생성해야 합니다.
* 0, 1, 2, 0, 2, 3 / 3개씩 잘라서 보면 0,1,2 폴리곤과 0, 2, 3 폴리곤 2개가 있어, 4각으로 보이죠.

자세한건 위의 Link 참조바랍니다.

Hexa의 경우는 간단히 다음과 같이 하면 되겠지하고 덤볐다가.. -_-;; 개피봤습니다.
사용자 삽입 이미지
1개 일때는 문제없이 0, 1, 3, 1, 2, 3, 0, 3, 4, 0, 4, 5 로 폴리곤 셋을 잘라주면 되는데.. 2개 이상만 되도 일정한 규칙을 발견하기 어려웠습니다.
사용자 삽입 이미지
슬슬 멘붕오기 시작하지요. 여기에 Y축까지 추가되면...
사용자 삽입 이미지
규칙은 사라지고 Chaos 만 남습니다. -_-;;
만약, Pivot이 Center에 있다면? 이라는 질문을 하게 되었고 Vetex를 정리해보니 다음과 같이 그런대로 규칙이 생깁니다.
사용자 삽입 이미지
정리하자면 2*2짜리 Hexa는 가로로 n개의 Vertex를 가집니다.
이걸 수식으로 정리하면 Hexa갯수가 x라면 Vertex수는
1 - 3
2 - 5
3 - 7
4 - 9
n - 2 * 헥사갯수 + 1

입니다만, Hexa는 항상 위에서 보는바와 같이 왔다 갔다 하므로 Y축 확장을 위해 우로 Vertex열이 1줄 더 있어야 하고 좌로도 1줄 더 있어야 하는 상황이 됩니다.

그리고, 또 하나 만난 문제는 저렇게 Vertex를 배열하는게 문제였습니다.
6각형은 60도로 나누어져 있는데 소숫점 오차로 인해 제대로 배열이 안되는것도 문제여서...
그냥 다음과 같이 간단히 일렬로 배열한뒤에...
사용자 삽입 이미지
홀수 열만 0.5정도 올려주면...
사용자 삽입 이미지
근사하게 육각으로 바뀌는 군욤. ㅋ

* 여기까지 코드는 다음과 같습니다.

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

public class Hexa : MonoBehaviour {

    private Vector3[] vertexs; 

    void Awake()
    {
        gameObject.AddComponent<MeshFilter>();
        gameObject.AddComponent<MeshRenderer>();
    }

    void Start () {
        SetVertex(5, 6);
    }
    
    void SetVertex(int xSize, int ySize)
    {
        //사전처리 부분입니다. 예외처리를 위해 가로 헥사수는 2이상, 세로 헥사수는 짝수로 고정합니다.
        if (xSize < 2) xSize++; //가로 헥사수, 예외 처리를 위해 2이상으로 강제 처리
        if (ySize % 2 == 1) ySize++; //세로 헥사수  //예외 처리를 위해 짝수로 강제 처리

        // 가로와 세로의 버텍스 수를 구합니다.
        int xDotSize = 2 * xSize + 2; //가로 버텍스 수
        int yDotSize = (ySize % 2 == 0) ? (3 * ySize / 2) + 1 : 3 * (ySize / 2 + 1); //세로 버텍스 수

        //For 문으로 자동으로 찍되 홀수 줄은 0.5f만큼 올립니다.
        MeshFilter MF = GetComponent<MeshFilter>();
        vertexs = new Vector3[xDotSize * yDotSize];
        for (int i = 0, y = 0; y > yDotSize * -1 ; y--)
        {
            for (int x = 0; x < xDotSize; x++, i++)
            {
                vertexs[i] = (x % 2 == 0) ? new Vector3(x, 0f, y) : new Vector3(x, 0f, y + 0.5f);
            }
        }
        MF.mesh.vertices = vertexs;
    }

    private void OnDrawGizmos()
    {
        if (vertexs == null) return;
        Gizmos.color = Color.red;
        for (int i = 0; i < vertexs.Length; i++)
            Gizmos.DrawSphere(vertexs[i], 0.1f);
    }
}


이제 문제는 Center를 찾는 겁니다. 의외로 어렵게 해결된 부분인데,
먼저 좌변의 Center Point들을 먼저 찾고 거기에 가로로 +2를 반복하였습니다.
그 이유는 다음의 그림에서 처럼 첫 Center는 2번째, 2번째 Center는 5번째... 이런식으로 나타 났고 이를 위해 수열 공식 찾느라 오래 걸렸습니다.
사용자 삽입 이미지
암튼 위의 결과를 보인 소스는 다음과 같습니다.

        //SetVertex() 함수 밑부분에 넣었습니다.
        //Find Center
        CenterP = new Vector3[xSize * ySize];
        for (int idx =0, y = 0; y < ySize; y++) {
            int verticesIdx = 0;
            verticesIdx = xDotSize * ((y / 2) + 1 + y) + 1 + (y % 2); //버텍스인덱스는 가로크기* ((y / 2) + 1 + y) + 1 + (y % 2), y는 세로변수 y
            CenterP[idx] = vertexs[verticesIdx];
            CenterIdx.Add(verticesIdx);
            idx++;
            for (int x = 1; x < xSize; x++)
            {
                CenterP[idx] = vertexs[verticesIdx + 2 * x]; //세로에서 구한 센터값에 +2 반복
                CenterIdx.Add(verticesIdx + 2 * x);
                idx++;
            }
        }

좀 복잡하지만, CenterP는 OnGizmo에서 디버깅 용으로 만든 변수이고, 실재 사용하는 변수명은 CenterIdx입니다.
클래스 변수는 다음과 같이 위의 2개 변수를 추가했구요.

    private Vector3[] vertexs;
    private Vector3[] CenterP;
    private List<int> CenterIdx = new List<int>();


마지막으로 OnGizmo를 다음과 같은 최종형태로 만들었습니다.
    private void OnDrawGizmos()
    {
        if (vertexs == null) return;
        Gizmos.color = Color.red;
        for (int i = 0; i < vertexs.Length; i++)
            Gizmos.DrawSphere(vertexs[i], 0.1f);

        if (CenterP == null) return;
        Gizmos.color = Color.yellow;
        for (int i = 0; i < CenterP.Length; i ++ )
            Gizmos.DrawSphere(CenterP[i], 0.1f);

    }


마지막으로 Vertex 연결 순서는 짝수 열과 홀수 열이 조금 다르지만, 정리하면,

                //총 6개의 폴리곤이 있어야 6각형이 나오므로...
                triangles[idx] = CenterIdx[x];
                triangles[idx + 1] = CenterIdx[x] - 1;
                triangles[idx + 2] = CenterIdx[x] - xDotSize;
                
                triangles[idx + 3] = CenterIdx[x];
                triangles[idx + 4] = CenterIdx[x] - xDotSize;
                triangles[idx + 5] = CenterIdx[x] + 1;
                
                triangles[idx + 6] = CenterIdx[x];
                triangles[idx + 7] = CenterIdx[x] + 1;
                triangles[idx + 8] = CenterIdx[x] + xDotSize + 1;

                triangles[idx + 9] = CenterIdx[x];
                triangles[idx + 10] = CenterIdx[x] + xDotSize + 1;
                triangles[idx + 11] = CenterIdx[x] + xDotSize;
                
                triangles[idx + 12] = CenterIdx[x];
                triangles[idx + 13] = CenterIdx[x] + xDotSize;
                triangles[idx + 14] = CenterIdx[x] + xDotSize - 1;
                
                triangles[idx + 15] = CenterIdx[x];
                triangles[idx + 16] = CenterIdx[x] + xDotSize - 1;
                triangles[idx + 17] = CenterIdx[x] - 1;

위의 코드를 좀 짧게 축약하면 다음과 같습니다.

                triangles[idx] = triangles[idx + 3] = triangles[idx + 6] = triangles[idx + 9] = triangles[idx + 12] = triangles[idx + 15] = CenterIdx[x];
                triangles[idx + 1] = triangles[idx + 17] = CenterIdx[x] - 1;
                triangles[idx + 2] = triangles[idx + 4] = CenterIdx[x] - xDotSize;
                triangles[idx + 5] = triangles[idx + 7] = CenterIdx[x] + 1;
                triangles[idx + 8] = triangles[idx + 10] = CenterIdx[x] + xDotSize + 1;
                triangles[idx + 11] = triangles[idx + 13] =  CenterIdx[x] + xDotSize;
                triangles[idx + 14] = triangles[idx + 16] = CenterIdx[x] + xDotSize - 1;


이제 이걸 돌려 보면... 두둥..
사용자 삽입 이미지
전체 코드는 다음과 같습니다.

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

public class Hexa : MonoBehaviour {

    private Vector3[] vertexs;
    private Vector3[] CenterP;
    private List<int> CenterIdx = new List<int>();

    void Awake()
    {
        gameObject.AddComponent<MeshFilter>();
        gameObject.AddComponent<MeshRenderer>();
    }

    // Use this for initialization
    void Start () {
        SetVertex(5, 6);
    }
    
    void SetVertex(int xSize, int ySize)
    {
        if (xSize < 2) xSize++; //x 헥사수
        if (ySize % 2 == 1) ySize++; //y 헥사수
        int xDotSize = 2 * xSize + 2; //가로 버텍스 수
        int yDotSize = (ySize % 2 == 0) ? (3 * ySize / 2) + 1 : 3 * (ySize / 2 + 1); //세로 버텍스 수
        MeshFilter MF = GetComponent<MeshFilter>();
        vertexs = new Vector3[xDotSize * yDotSize];
        for (int i = 0, y = 0; y > yDotSize * -1 ; y--)
        {
            for (int x = 0; x < xDotSize; x++, i++)
            {
                vertexs[i] = (x % 2 == 0) ? new Vector3(x, 0f, y) : new Vector3(x, 0f, y + 0.5f);
            }
        }
        MF.mesh.vertices = vertexs;

        //Find Center
        CenterP = new Vector3[xSize * ySize];
        for (int idx =0, y = 0; y < ySize; y++) {
            int verticesIdx = 0;
            verticesIdx = xDotSize * ((y / 2) + 1 + y) + 1 + (y % 2);
            CenterP[idx] = vertexs[verticesIdx];
            CenterIdx.Add(verticesIdx);
            idx++;
            for (int x = 1; x < xSize; x++)
            {
                CenterP[idx] = vertexs[verticesIdx + 2 * x];
                CenterIdx.Add(verticesIdx + 2 * x);
                idx++;
            }
        }

        int[] triangles = new int[18 * (xSize * ySize)];

        for (int idx = 0, x = 0; x < CenterIdx.Count; x++)
        {
            if ((x / xSize) % 2 == 0)
            {
                triangles[idx] = CenterIdx[x];
                triangles[idx + 1] = CenterIdx[x] - xDotSize - 1;
                triangles[idx + 2] = CenterIdx[x] - xDotSize;

                triangles[idx + 3] = CenterIdx[x];
                triangles[idx + 4] = CenterIdx[x] - xDotSize;
                triangles[idx + 5] = CenterIdx[x] - xDotSize + 1;

                triangles[idx + 6] = CenterIdx[x];
                triangles[idx + 7] = CenterIdx[x] - xDotSize + 1;
                triangles[idx + 8] = CenterIdx[x] + 1;

                triangles[idx + 9] = CenterIdx[x];
                triangles[idx + 10] = CenterIdx[x] + 1;
                triangles[idx + 11] = CenterIdx[x] + xDotSize;

                triangles[idx + 12] = CenterIdx[x];
                triangles[idx + 13] = CenterIdx[x] + xDotSize;
                triangles[idx + 14] = CenterIdx[x] - 1;

                triangles[idx + 15] = CenterIdx[x];
                triangles[idx + 16] = CenterIdx[x] - 1;
                triangles[idx + 17] = CenterIdx[x] - xDotSize - 1;

            }
            else
            {
                triangles[idx] = CenterIdx[x];
                triangles[idx + 1] = CenterIdx[x] - 1;
                triangles[idx + 2] = CenterIdx[x] - xDotSize;
                
                triangles[idx + 3] = CenterIdx[x];
                triangles[idx + 4] = CenterIdx[x] - xDotSize;
                triangles[idx + 5] = CenterIdx[x] + 1;
                
                triangles[idx + 6] = CenterIdx[x];
                triangles[idx + 7] = CenterIdx[x] + 1;
                triangles[idx + 8] = CenterIdx[x] + xDotSize + 1;

                triangles[idx + 9] = CenterIdx[x];
                triangles[idx + 10] = CenterIdx[x] + xDotSize + 1;
                triangles[idx + 11] = CenterIdx[x] + xDotSize;
                
                triangles[idx + 12] = CenterIdx[x];
                triangles[idx + 13] = CenterIdx[x] + xDotSize;
                triangles[idx + 14] = CenterIdx[x] + xDotSize - 1;
                
                triangles[idx + 15] = CenterIdx[x];
                triangles[idx + 16] = CenterIdx[x] + xDotSize - 1;
                triangles[idx + 17] = CenterIdx[x] - 1;
            }
            idx += 18;
        }

        MF.mesh.triangles = triangles;
        MF.mesh.RecalculateNormals();

    }

    private void OnDrawGizmos()
    {
        if (vertexs == null) return;
        Gizmos.color = Color.red;
        for (int i = 0; i < vertexs.Length; i++)
            Gizmos.DrawSphere(vertexs[i], 0.1f);

        if (CenterP == null) return;
        Gizmos.color = Color.yellow;
        for (int i = 0; i < CenterP.Length; i ++ )
            Gizmos.DrawSphere(CenterP[i], 0.1f);

    }
}


그럼~ 새해 복 많이 받으세요!
2016/02/09 17:17 2016/02/09 17:17
* Resume carpet bombing 좌표
* 2015. 12. 17 현재 총 16개 회사 (즉시 가서 즉시 성과 낼수 있는 영역만 필터링, Unity3D/C#)

- Activision
1. https://activision.jibeapply.com/activision/jobs/PRO0001D6/Albany-New-York-C-Tools-Engineer-Vicarious-Visions?lang=en-US

- Amazon
2. http://www.amazon.jobs/jobs/305853/pri ··· engineer
3. http://www.amazon.jobs/jobs/345743/sen ··· ix-games


- Blizzard
4. http://us.blizzard.com/en-us/company/c ··· D1500013
5. http://us.blizzard.com/en-us/company/c ··· D15000jt
6. http://us.blizzard.com/en-us/company/c ··· D15000i9
- BeRedbutton
7. http://brbent.com/careers/

- Infinity ward
8. https://activision.jibeapply.com/infinityward/jobs/PRO0001EJ/Woodland-Hills-California-Tools-Engineer-Infinity-Ward?lang=en-US

- Jump Start
9. http://www.jumpstart.com/aboutus/careers

- Magic Leap
10. http://www.magicleap.com/#/job-post/106654

- Seismic Games Job
11. http://www.seismicgames.com/jobs/unity-developer/

- SGN
12. http://www.sgn.com/jobs/

- Wayforward
13. https://wayforward.com/jobs/

- Sticky
14. http://www.stickystudios.com/careers/

- Zinga
15. https://www.zynga.com/careers/positions/client-side-engineer

- Zindagi
16. http://www.zindagigames.com/Careers.html

총 16개





2015/12/18 16:21 2015/12/18 16:21
TAG ,
BMFONT로 Font를 뽑으면 데이터가 커진하고 믿겠지만, RGBA 4개 채널에 폰트데이터를 넣으면 용량이 매우 작아진다.
단적인 예로 우리가 흔히 쓰는 나눔폰트나 Noto 폰트의 경우 각각 용량이 4M, 15~16M에 달한다.

* 나눔폰트 용량
사용자 삽입 이미지
* Google Noto sans CJK Kor 
사용자 삽입 이미지

그런데, Packed Font로 뽑으면...
사용자 삽입 이미지
1024*1024 텍스쳐 한장으로 딸랑 782KB 이다. 
(주의 : 유니티에 Import 하는순간 True Color Texture는 1024에서 4M가 소요된다. 하지만, 512라면 1M에 불과해진다. 폰트사이즈 32정도면 512에 떨어지니 실망하지 말자)
특히, Font 파일을 앱속에 탑재함으로써 발생하는 기타 여러 잡다한 문제도 없고 형좋고 매부 좋고, 암튼 사용하는 방법에 대해 알아보자.

먼저, BM Font 설치하고 한글 파일을 다운로드받자.

- BMFONT : http://www.angelcode.com/products/bmfont/
- 한글텍스트파일 : http://www.wolfpack.pe.kr/attachment/1142498515.txt

이제 BM Font를 실행하고 다음과 같이 설정한다.
* 자세한 BM Font 사용법은 http://www.wolfpack.pe.kr/806 

1. 폰트 셋팅에서 Size를 62정도 입력했을때 1024사이즈에서 공간이 남았었다. (Super Sampling을 켜서 폰트가 이쁘게 나오게 하자.)
사용자 삽입 이미지
2. 익스포트 옵션에서 Bit Depth를 32로 변경하고 "Pack chars in multiple channels" 옵션을 켜자.
사용자 삽입 이미지
3. 이제 익스포트하고 Unity3d로 옮겨 놓자.
사용자 삽입 이미지
4. Texture Import 옵션을 다음과 같이 Advance로 바꾼후 변경하자.
사용자 삽입 이미지
5. NGUI Font Maker를 열어서 폰트를 만든다.
사용자 삽입 이미지
6. 파일이 생성된 모습. 하지만, 하나 더 남았다.
사용자 삽입 이미지
7. NotoFont 메터리얼을 Inspector 창에서 확인하면 쉐이더가 "Unlit/Transparent Colored"로 되어 있을텐데 이걸 "Unlit/Transparent Packed"로 변경해야 한다.
사용자 삽입 이미지
8. 적용결과
사용자 삽입 이미지

* 물론 Dynamic Font로 적용하면 편하다! 하지만, Texture Resize 될때 종종 스냅드래곤을 채용한 핸드폰에서 폰트가 사라지거나 별별 문제가 다 발생하고 그거 잡는 시간에 BMFont로 뽑는게 더 빠르다.
* Packed Font는 외곽 효과라던가... 이팩트 먹이기가 다음과 같이 잘 안된다. 
사용자 삽입 이미지


2015/08/28 17:11 2015/08/28 17:11
유니티 3D로 개발하다보면, 파편화된 데이터때문에 유지보수나 관리에 어려움을 겪게 되는데,
그러다 보니 Entity Class를 만들고 Dontdestroy 옵션으로 삭제되지 않는 불멸의 데이터 클래스를 생성해서 사용하는 개발자 분들이 많이 보인다.
그런데, 이렇게 하다보면 결국 소스가 꼬여서 오작동 하거나 관리상 문제로 인해 불편함을 가지고 가는 경우도 많은데 그럴 필요 없이 Static 으로 설정하여 Scene을 넘어가더라도 Data가 살아 있는 Class를 만들고 관리하는 것을 목표로 한다.
(물론 Json을 Serialize 하거나 Deserialize 하는건 다음 편에서 보자)

EntityMaster.cs파일을 하나 만들어서 다음과 같이 추가해두자.
(폴더위치는 관계없다.)

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

//테이블과 같은 클래스이다.
public class TestEntity
{
 public static List<SubEntity> _sub = new List<SubEntity>();
}

//1개 Row라고 생각하면 비교가 쉽다.
public class SubEntity
{
 public string _x;
 public string _y;
//Row안에 또 하나의 테이블을 가질 수 있다.
 public List<Sub2Entity> _sub = new List<Sub2Entity>();
}
//SubEntity에 포함된 클래스이다.
public class Sub2Entity
{
 public int _z;
}


이제 Scene0를 만들어서 MainCamera에 다음과 같은 Script를 만들어 Attach하자.
using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class Scene0Behavior : MonoBehaviour {
 // Use this for initialization
 void Start () {
//새로운 Sub2Entity 객체를 만든다.
 List<Sub2Entity> _sub2 = new List<Sub2Entity>();
//Sub2Entity 타입의 객체에 데이터를 넣는다.
_sub2.Add ( new Sub2Entity { _z = 1 });
//테스트엔티티 테이블에 Row를 추가하고 다음 씬을 호출한다.
 TestEntity._sub.Add(new SubEntity { _x = "a", _y = "b", _sub = _sub2 });
 Application.LoadLevel(1);
 }

}


이제 2번째 씬을 만들고 다음과 같이 코딩한후 MainCamera에 Attach하자.
* 위에서 데이터를 추가한 것이라 별도 설명은 생략한다.
using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class Scene1Behavior : MonoBehaviour {

 // Use this for initialization
 void Start () {
 Debug.Log(TestEntity._sub[0]._x);
 List<Sub2Entity> _sub2 = new List<Sub2Entity>();
 _sub2.Add(new Sub2Entity { _z = 2 });
 TestEntity._sub.Add(new SubEntity { _x = "c", _y = "d", _sub = _sub2 });
 Application.LoadLevel(2);
 }
 
}


마지막으로 3번째 씬을 만들어서 다음과 같이 코딩후 Attach하자.
using UnityEngine;
using System.Collections;

public class Scene2Behavior : MonoBehaviour {

 // Use this for initialization
 void Start () {
//시작하면 데이터를 찍는 역할이 전부다.
 Debug.Log(TestEntity._sub[1]._x);
 Debug.Log(TestEntity._sub[1]._sub[0]._z);
 }
 
}


최종 결과는 다음과 같다.

* 씬을 다음과 같이 3개로 구성했고, MainCamera에는 각각의 Script가 Attach되어 있다.
사용자 삽입 이미지
* 최종 결과는 Debug.Log로 다음과 같이 출력된다.
사용자 삽입 이미지
2015/08/07 02:14 2015/08/07 02:14
유니티3D는 사전 정의된 폴더(Special Folder)를 가지고 있다.

Assets : 기본적으로 소스를 포함하고 있는 폴더
Editor : 유니티에서 사용되는 사용자 정의 에디터 폴더
Gizmos : 기즈모 리소스
Plugins : 플러그인 파일들 하위 폴더로는 "iOS"와 "Android"가 Native Path로 설정됨
Resources : Resources.load로 런타임에서 호출가능한 리소스 폴더
Standard Assets / Pro Standard assets : 일반적인 유니티 패키지를 import했을때 사용하는 폴더
StreamingAssets : 비디오 파일이나 원화 같이 프로젝트 소스가 참조하는 리소스를 넣는다고 되어 있으나, 데스크탑 계열에서는 읽고 쓰기가 가능하지만, 모바일은 읽기만, 웹플레이어는 아에 설정이 불가.
(왜 필요한지...?)
WebPlayerTemplates : 이건 그림으로 설명하자면, 웹플레이어의 경우 템플릿을 설정할 수 있는데...
사용자 삽입 이미지
이때 사용하는 폴더로 구조는 다음과 같음.
사용자 삽입 이미지

중요한건 빌드 순서일터,
(왜냐하면 플러그인을 import하고 많은 분들이 그냥 내버려 둠으로써 Compile 순서가 꼬이는 등의 문제가 있을 수 있기 때문)

총 4단계를 거치는데, 

Phase 1: Standard Assets,Pro Standard Assets,Plugins.
Phase 2: Standard Assets/Editor,Pro Standard Assets/Editor,Plugins/Editor.
Phase 3: Editor 폴더 밖에 있는 스크립트
Phase 4: 나머지 스크립트

끝.
2015/02/21 03:47 2015/02/21 03:47
출처는 이전의 AES 방식(링크 : http://www.wolfpack.pe.kr/828) 과 같이.. 까먹었슴돠..

using UnityEngine;
using System.Collections;
using System.Security.Cryptography;
using System.Text;

public class MainTester : MonoBehaviour {

 string _x = "";
 UTF8Encoding ByteConv = new UTF8Encoding();
 RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
 
 // Use this for initialization
 void Start () {
 _x = "Hello!!!";
 byte[] _encText = Encryption(ByteConv.GetBytes(_x), RSA.ExportParameters(false), false);
 Debug.Log( ByteConv.GetString(_encText) );
 Debug.Log(ByteConv.GetString(Decryption(_encText, RSA.ExportParameters(true), false)));
 }
 
 static public byte[] Encryption(byte[] Data, RSAParameters RSAKey, bool DoOAEPPadding)
 {
 try
 {
 byte[] encryptedData;
 using (RSACryptoServiceProvider RSA = new RSACryptoServiceProvider())
 {
 RSA.ImportParameters(RSAKey);
 encryptedData = RSA.Encrypt(Data, DoOAEPPadding);
 }
 return encryptedData;
 }
 catch (CryptographicException e)
 {
 Debug.Log(e.Message);
 return null;
 }
 }

 static public byte[] Decryption(byte[] Data, RSAParameters RSAKey, bool DoOAEPPadding)
 {
 try
 {
 byte[] decryptedData;
 using (RSACryptoServiceProvider RSA = new RSACryptoServiceProvider())
 {
 RSA.ImportParameters(RSAKey);
 decryptedData = RSA.Decrypt(Data, DoOAEPPadding);
 }
 return decryptedData;
 }
 catch (CryptographicException e)
 {
 Debug.Log(e.ToString());
 return null;
 }
 }
}


암호화 결과 : 0BB@�{8n��*�b"�FN?s�
복호화 결과 : Hello!!!
2015/01/20 15:40 2015/01/20 15:40
최근에 모 대기업 스마트폰에 Preinstall 하는 프로젝트에 참여하면서 영혼이 갈려 나가고 있습니다.
(8월 23일부터 처음 코딩들어가기 시작했으니 벌써 2달이 가까워지네요.)

TDD와 같이 개발하고 Fix Patch하면서 하나하나 고쳐나가다보니 별별 문제를 다 만납니다.

현재환경 : Unity3D 4.5.4
Vuforia : 3.09

가장 큰 문제는 Vuforia의 카메라를 On/Off 할때 Object (특히 GUI)가 많이 떠 있으면 Crash 가 발생합니다. 
이건 답이 없어서 On/Off하는 상황에서 GUI를 꺼주고 Object들도 다 꺼주고 다시 카메라를 Turn On할때는 카메라가 들어오는걸 확인하고 다시 다 띄워줍니다. (이런 미친...)

정리하자면, 전면카메라와 후면카메라 전환시 카메라 Turn Off -> Turn On 되는데, 이때 

- CameraDevice.instance.stop()
- 모든 Object 제거
CameraDevice.instance.start()
- Object 재생성

이렇게 해야 반복적인 On/Off문제에서 해결이 가능하더군요. (Fucking Vuforia)

그리고, Unity3D 4.5.4f1 버전에서 안드로이드 커버를 덥었다 열었다를 반복하면 먹통이 되는 현상이 발생됩니다.
이 경우는 Unity3D 4.5.4 Patch 3 또는 4.5.5로 업데이트 / 업그레이드하면 많은 부분이 해소됩니다만, 
위의 Vufoira Crash 대응작업을 위해서는 Queue를 사용해서 중간에 들어온 Events를 무시하는 겁니다.

예를 들어, Pause Event에서는 
- CameraDevice.instance.stop()
- 모든 Object 제거
를 해야하고 Resume Event에서는 
CameraDevice.instance.start()
- Object 재생성
를 수행해야하는 상황이라면, 다음과 같이 Queue를 처리 할 수 있습니다.


* 다음은 대소문자 무시하고 기억나는대로 Typing한겁니다.

//먼저 List객체로 만들 Class를 하나 선언합니다.
public class Queue {
     public bool PauseType;
     public float EventTime;
}

//List를 하나선언합시다. using System.Collection.Generic; 선언 잊지 맙시다.
List<Queue> _q = new List<Queue>();
void OnApplicationPause(_paused){
    //Pause 또는 Resume Event가 들어오면 Queue에다 넣어둡니다.
     _q.add ( new Queue { PauseType = _paused, EventTime = Time.realtimeSinceStartup } );
   // 처음 들어온 Event는 일단 Pause이므로 처리하도록 합시다.
     if (_q.Count <= 1)  SetPause();
}

void FixedUpdate(){
    if (_q.Count < 1) return; //비어있을때는 무시
    //큐의 마지막이 Resume 이면서 반복 입력이 1초이상 없으면 Resume 실행
    if (_q[_q.Count -1].
PauseType == false && _q[_q.Count -1].EventTime + 1f <  Time.realtimeSinceStartup) {
       SetResume();
       _q.Clear()
    }
}

만들어 놓고 보니 굳이 List 객체로 처리해야하는지 조금은 궁금증이 들기는 하지만, 조금이라도 안드로이드 덮개 때문에 고통받는 개발자들과 나누고 싶네요.
(참고로 삼성제품에서는 괜찮다고 하더라구요. T_T)
2014/10/18 14:33 2014/10/18 14:33
Unity3D의 신묘한 기능중에 하나는 안드로이드 SDK를 C#으로 불러서 쓸수 있다는 점.
그걸 이용해서 Soft Navigation bar를 없애보았다.

* 게임할때  겁나 걸리적 거리는 요녀석!
사용자 삽입 이미지

* 원문은 여기
아래 "2"가 왜 "2"인지에 대한 자료는 여기
 
 
#if UNITY_ANDROID
 // 안드로이드 자바오브젝트를 3개 선언한다.
 static AndroidJavaObject activityInstance;
 static AndroidJavaObject windowInstance;
 static AndroidJavaObject viewInstance;
 //중요한건 바로 여기 "2"로 설정된 "SYSTEM_UI_FLAG_HIDE_NAVIGATION"값이 중요

 const int SYSTEM_UI_FLAG_HIDE_NAVIGATION = 2;
 const int SYSTEM_UI_FLAG_LAYOUT_STABLE = 256;
 const int SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION = 512;
 const int SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN = 1024;
 const int SYSTEM_UI_FLAG_IMMERSIVE = 2048;
 const int SYSTEM_UI_FLAG_IMMERSIVE_STICKY = 4096;
 const int SYSTEM_UI_FLAG_FULLSCREEN = 4;
 
 public delegate void RunPtr();
 
 public static void Run()
 {
 if (viewInstance != null)
 {
 viewInstance.Call("setSystemUiVisibility",
 SYSTEM_UI_FLAG_LAYOUT_STABLE
 | SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
 | SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
 | SYSTEM_UI_FLAG_HIDE_NAVIGATION
 | SYSTEM_UI_FLAG_FULLSCREEN
 | SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
 }
 
 }
 #endif
 


일단 이렇게 선언이 끝났으면 다음의 코드를 추가한다.

 public static void DisableNavUI()
 {
 if (Application.platform != RuntimePlatform.Android)
 return;
 using (AndroidJavaClass unityPlayerClass = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
 {
 activityInstance = unityPlayerClass.GetStatic<AndroidJavaObject>("currentActivity");
 windowInstance = activityInstance.Call<AndroidJavaObject>("getWindow");
 viewInstance = windowInstance.Call<AndroidJavaObject>("getDecorView");
 
 AndroidJavaRunnable RunThis;
 RunThis = new AndroidJavaRunnable(new RunPtr(Run));
 activityInstance.Call("runOnUiThread", RunThis);
 }
 }
 


이제 이녀석을 호출할 녀석을 추가한다.
 void Start()
 {
 DisableNavUI();
 }
 

이제 상단 실행시켜보면 상단 또는 하단 엣지 부분을 가운데 방향으로 드레그 하기전에는 메뉴가 보이지 않는다.
물론 단점도 존재한다.

- 화면해상도 새로 잡아야 한다. T_T 
- 지금까지 알려진 해상도가 아니라는 점...
2014/09/02 10:46 2014/09/02 10:46
작년 이맘때쯤에 JAVA와 유니티3D를 연동하는 예제를 포스팅하였다.
짜증나는자바1편짜증나는자바2편

자바연동 2편에서 대충 Jar파일을 만들어서 Java에서 호출하는 부분을 만들었었지만, 1편에서 밝힌 바와 같이 Jar를 유니티3D로 끌고 와서 연동하는 예제는 다루지 않았다.

어쩌면... 짜증나는 자바시리즈 3편으로 봐도 될 정도로 오늘 다룰 주제는 자바로 만들어진 Jar파일을 유니티로 가져와서 연동하는 예제이다.

( 출처 밝히면 펌 자유이지만, 출처를 밝히지 않으신다면 고소크리 들어갑니다.)

1. 이클립스를 연다.
2. Android Application Project를 새로 만든다.
사용자 삽입 이미지
3. 이름과 프로젝트명을 지정하고 패키지이름을 지정한다.
사용자 삽입 이미지
4. 이제부터 다음, 다음, 다음, 다음 4연타.
사용자 삽입 이미지
사용자 삽입 이미지
사용자 삽입 이미지
사용자 삽입 이미지

5. 프로젝트 생성완료. (쉽죠?)
사용자 삽입 이미지
6. 디버그 설정을 Android Application으로 설정
사용자 삽입 이미지
7. 이제 만들어진 프로젝트명에서 마우스 우클릭후 속성을 클릭
사용자 삽입 이미지
8. "Is Library"체크하고 "Apply" 한번 눌러 준뒤 "OK"
사용자 삽입 이미지
9. 프로젝트 폴더 밑에 /bin 폴더에 보면 "Plugin.jar"파일이 생성되어 있다. (나중에 유니티에서 이녀석을 가져다 쓸거임)
사용자 삽입 이미지
10. /src/com.lingo.plugin을 클릭한 후 다시 우클릭하여 New, Class를 차례대로 클릭
사용자 삽입 이미지
11. 클래스명을 Test로 하고 Finish 클릭
사용자 삽입 이미지
12. 다음과 같이 정적(Static) 메소드와 인스턴스(Static이 안붙은) 메소드를 작성한다.
사용자 삽입 이미지

13. 다음과 같이 Build Automatically 선택되어 있으면 저장과 동시에 자동으로 Build 됨.
     * Console에서 에러가 발생하지 않았다면 문제없이 사용가능한 형태가 됨.
사용자 삽입 이미지

14. 저장하고 빌드가 완료되면 /bin 폴더의 속성을 클릭해서 실제 경로 확인
사용자 삽입 이미지
15. 이제 유니티로 와서 코딩부터...
     * 주의점은 Static은 "AndroidJavaClass"로, 인스턴스는 "AndroidJavaObject"로 선언되어야 함.
사용자 삽입 이미지
18. 다음과 같이 Main Camera와 GUI Text만 배치한다.
사용자 삽입 이미지
19. PluginTest 파일을 Main Camera에 Attach한후 GUI Text를 다시 Asign한다.
     - 물론 /Pluings/Android 밑에 "9"에서 언급한 plugin.jar 파일을 넣어두는걸 잊으면 골룸.
사용자 삽입 이미지
20. GUI Text Default
사용자 삽입 이미지

21. Editor 상에서는 이렇게 보임. (문구가 장난이지만... 진짜 작동하는거 맞다. ㅜㅜ)
사용자 삽입 이미지
이제 마지막으로 Build&Run하면, 안드로이드 폰에서 실행된 결과를 확인할 수 있다.

2014/04/10 18:16 2014/04/10 18:16