슈팅게임으로 만들었어요.

소스 다운로드
강의자료

요즘 집필중이라서 정신이 하나도 없습니다. 
부실한 자료지만 참고하시기 바랍니다.


2017/01/04 15:30 2017/01/04 15:30
간만에 유니티3D 관련 글을 쓰네요.

몇 번의 프로젝트에서 사용했던 티끌 같은 팁인데 많은 분들이 모르시는 것같아 소스 하나 남겨둡니다.

using UnityEngine;
using System.Collections;

public class Test : MonoBehaviour {

    // Use this for initialization
    IEnumerator Start () {

        //for Lambda
        StartCoroutine(ReturnValue( (x) => 
        {
            Debug.Log(x);
        } 
        ));

         //for local value
        int a = 0;
        yield return StartCoroutine(ReturnValue( (x) => 
        {
            a = x;
        }
        ));
        Debug.Log(a);
    }
 
    IEnumerator ReturnValue(System.Action<int> callback)
    {
        yield return null;
        callback(10);
    }
}


핵심은 Lambda 식으로 callback 함수를 구현하는 겁니다. =)
로컬 변수에 값을 할당할때는 반듯이 yield return 명령으로 Coroutine 함수가 종료되고나서 값이 할당됐는지 확인되어야 합니다. (그럼 코루틴 쓰는 의미가.. -_-;;)


2016/04/04 17:00 2016/04/04 17:00
1. 먼저 쉐이더를 다음과 같이 입력하자.

Shader "TextureMask"
{
 Properties
 {
 _Mask("Culling Mask", 2D) = "white" {}
 }
 SubShader
 {
 Tags{ "Queue" = "Background" }
 Blend SrcAlpha OneMinusSrcAlpha
 Lighting Off
 ZWrite On
 ZTest Always
 Alphatest LEqual 0
 Pass
 {
 SetTexture[_Mask]{ combine texture }
 }
 }
}


2. Material 하나 만들어서 TextureMask 로 설정하자.
사용자 삽입 이미지
3. 인스펙터에서 다음과 같이 설정해주자.
사용자 삽입 이미지

3. 박스와 Quad를 생성하여 박스와 카메라 사이에 Quad가 위치하도록 한다.

4. Quad에다 위에서 생성한 메터리얼을 Drag&drop하거나, Texture를 Drag&drop 한후에 Shader를 TextureMask 로 설정한다

5. 끝.
사용자 삽입 이미지
* 블랙영역은 컬링되어 렌더링 되지 않는다. =)
2015/10/22 17:56 2015/10/22 17:56
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
출처는 이전의 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
너무나 정신없어서 블로그 관리도 못하고 있네요.
그럼에도 블로그 방문해주신 대부분의 Bot 여러분 및 소수의 열성 독자 여러분께 감사드립니다. (it's joke~!)
Unity3D 에서 안드로이드 개발할때 간혹 다른 앱을 실행하고자 할 때가 있습니다.
물론 이클립스 열어서 플러그인을 만들거나 혹은 쿵짝 쿵짝하는 경우가 있지만, 여기에서는 기본 C#코드로만 하는 방법을 설명할께요.

본 예제는 Android 기본 갤러리를 여는 예제입니다.
 
void LaunchGallery () { 
   AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer"); 
   AndroidJavaObject jo = jc.GetStatic("currentActivity"); 
   AndroidJavaObject pm = jo.Call("getPackageManager"); 
   AndroidJavaObject intent = pm.Call("getLaunchIntentForPackage", "com.android.gallery3d");
   jo.Call("startActivity", intent); 
}
 


처음부터 보자면, 예전 포스팅 (http://www.wolfpack.pe.kr/860)에서 밝혔던, Static 은 AndroidJavaClass, instance는 AndroidJavaObject 원칙에 따라 Class를 먼저 호출했습니다.
그리고나서, 현재 Activity를 호출했구요.
다음줄 부터는 Android API에 따라서,
getPackageManager (상세 여기참조) 와 getLaunchIntentForPackage (상세 여기참조)을 호출하면서 getLaunchIntentForPackage에 인자값으로 패키지명인 "com.android.gallery3d"을 넣어 줬습니다.
이제 준비가 다 끝나고 마지막으로 준비된 과정을 한꺼번에 부어서...
currentActivity Object에서 startActivity로 실행했습니다.
Intent는 다른 앱을 실행하고나서의 통신을 설정하는 부분인데 Intent 정의를 따로 하지 않았으니 통신하지 않겠다는 의미입니다. =)

그럼 수고하세요.
2014/08/19 21:44 2014/08/19 21:44
일전에 NGUI 관련해서는 기초적인 내용부터 강좌한 적이 있어서 기초 내용은 생략합니다.
최근 버전업데이트된 NGUI를 사용하다보니 Scroll부분의 Script 파일명이나 방법들이 바뀌어 있어서 이렇게 포스팅합니다.

우선 사용되는 스크립트는 다음과 같습니다.
- 빨간색 : Panel
- 파란색 : Grid
- 녹색 : Item
사용자 삽입 이미지
생소하다면 생소하겠지만... 예전강좌에서 다룬 부분이므로 참고 이미지만 넣고 넘어가겠습니다.
사용자 삽입 이미지
이제 Panel 부분의 Script설정입니다.
사용자 삽입 이미지
Grid 부분은 다음과 같이 설정합니다.
사용자 삽입 이미지
마지막으로 아이템부분입니다.
사용자 삽입 이미지
바뀐부분에 대해 예전 강좌로 잘 안되시는 분들은 참고하세요 =)
2014/04/20 17:54 2014/04/20 17:54