* 주의 : 이 스크립트는 유니티 에디터에서만 작동하고 런타임 중에는 사용할 수 없습니다.



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
//This script will only work in editor mode. You cannot adjust the scale dynamically in-game!
using UnityEngine;
using System.Collections;
 
#if UNITY_EDITOR 
using UnityEditor;
#endif
 
[ExecuteInEditMode]
public class ParticleScaler : MonoBehaviour
{
    public float particleScale = 1.0f;
    public bool alsoScaleGameobject = true;
 
    float prevScale;
 
    void Start()
    {
        prevScale = particleScale;
    }
 
    void Update()
    {
#if UNITY_EDITOR
        //check if we need to update
        if (prevScale != particleScale && particleScale > 0)
        {
            if (alsoScaleGameobject)
                transform.localScale = new Vector3(particleScale, particleScale, particleScale);
 
            float scaleFactor = particleScale / prevScale;
 
            //scale legacy particle systems
            ScaleLegacySystems(scaleFactor);
 
            //scale shuriken particle systems
            ScaleShurikenSystems(scaleFactor);
 
            //scale trail renders
            ScaleTrailRenderers(scaleFactor);
 
            prevScale = particleScale;
        }
#endif
    }
 
    void ScaleShurikenSystems(float scaleFactor)
    {
#if UNITY_EDITOR
        //get all shuriken systems we need to do scaling on
        ParticleSystem[] systems = GetComponentsInChildren<ParticleSystem>();
 
        foreach (ParticleSystem system in systems)
        {
            system.startSpeed *= scaleFactor;
            system.startSize *= scaleFactor;
            system.gravityModifier *= scaleFactor;
 
            //some variables cannot be accessed through regular script, we will acces them through a serialized object
            SerializedObject so = new SerializedObject(system);
 
            //unity 4.0 and onwards will already do this one for us
#if UNITY_3_5
            so.FindProperty("ShapeModule.radius").floatValue *= scaleFactor;
            so.FindProperty("ShapeModule.boxX").floatValue *= scaleFactor;
            so.FindProperty("ShapeModule.boxY").floatValue *= scaleFactor;
            so.FindProperty("ShapeModule.boxZ").floatValue *= scaleFactor;
#endif
 
            so.FindProperty("VelocityModule.x.scalar").floatValue *= scaleFactor;
            so.FindProperty("VelocityModule.y.scalar").floatValue *= scaleFactor;
            so.FindProperty("VelocityModule.z.scalar").floatValue *= scaleFactor;
            so.FindProperty("ClampVelocityModule.magnitude.scalar").floatValue *= scaleFactor;
            so.FindProperty("ClampVelocityModule.x.scalar").floatValue *= scaleFactor;
            so.FindProperty("ClampVelocityModule.y.scalar").floatValue *= scaleFactor;
            so.FindProperty("ClampVelocityModule.z.scalar").floatValue *= scaleFactor;
            so.FindProperty("ForceModule.x.scalar").floatValue *= scaleFactor;
            so.FindProperty("ForceModule.y.scalar").floatValue *= scaleFactor;
            so.FindProperty("ForceModule.z.scalar").floatValue *= scaleFactor;
            so.FindProperty("ColorBySpeedModule.range").vector2Value *= scaleFactor;
            so.FindProperty("SizeBySpeedModule.range").vector2Value *= scaleFactor;
            so.FindProperty("RotationBySpeedModule.range").vector2Value *= scaleFactor;
 
            so.ApplyModifiedProperties();
        }
#endif
    }
 
    void ScaleLegacySystems(float scaleFactor)
    {
#if UNITY_EDITOR
        //get all emitters we need to do scaling on
        ParticleEmitter[] emitters = GetComponentsInChildren<ParticleEmitter>();
 
        //get all animators we need to do scaling on
        ParticleAnimator[] animators = GetComponentsInChildren<ParticleAnimator>();
 
        //apply scaling to emitters
        foreach (ParticleEmitter emitter in emitters)
        {
            emitter.minSize *= scaleFactor;
            emitter.maxSize *= scaleFactor;
            emitter.worldVelocity *= scaleFactor;
            emitter.localVelocity *= scaleFactor;
            emitter.rndVelocity *= scaleFactor;
 
            //some variables cannot be accessed through regular script, we will acces them through a serialized object
            SerializedObject so = new SerializedObject(emitter);
 
            so.FindProperty("m_Ellipsoid").vector3Value *= scaleFactor;
            so.FindProperty("tangentVelocity").vector3Value *= scaleFactor;
            so.ApplyModifiedProperties();
        }
 
        //apply scaling to animators
        foreach (ParticleAnimator animator in animators)
        {
            animator.force *= scaleFactor;
            animator.rndForce *= scaleFactor;
        }
#endif
    }
 
    void ScaleTrailRenderers(float scaleFactor)
    {
        //get all animators we need to do scaling on
        TrailRenderer[] trails = GetComponentsInChildren<TrailRenderer>();
 
        //apply scaling to animators
        foreach (TrailRenderer trail in trails)
        {
            trail.startWidth *= scaleFactor;
            trail.endWidth *= scaleFactor;
        }
    }
}
 
cs



ParticleScaler.cs


위 스크립트를 ParticleSystem 오브젝트에 갖다붙이고



    



인스펙터에서 ParticleScale 변수를 조절해주면


모든 설정값들이 스케일 1 기준으로 딱딱딱 바뀌는 것을 볼 수 있습니다.


왼쪽은 원본 파티클이고 오른쪽은 ParticleScaler.cs 로 스케일 2배한 파티클입니다.


참 편리한 녀석이죠?


유용하게 쓰세요!





코드 출처 : https://github.com/hypno2000/starcontrol/blob/master/Assets/ParticleScaler/ParticleScaler.cs




[Unity3D] 구글 플레이 게임 서비스 & 애플 게임 센터 연동 (1/2) #설정편 링크 : http://minhyeokism.tistory.com/70



* 본격적으로 진행하기 전에 한 가지 말씀드리자면 


UnityEngine.Social.localUser 는 Androiod와 iOS 두 플랫폼을 모두 지원하고


현재 앱의 Platform을 알아서 판단하여


Android는 구글 플레이 게임 서비스의 api를


iOS는 애플 게임선터의 api를 호출하도록 할 수 있습니다.


다시 말해,



1
2
3
4
    Social.localUser.Authenticate((bool success) =>
    {
 
    });
cs



위 처럼 Social.localUser.~~~ 를 하면 Android 든 iOS 든 알아서! 문제없이! 작동한다는 뜻입니다.


그러나 특별한 상황에서는 구분을 명확하게 해야할 필요가 있습니다. 그럴 때에는


- Android일 경우 PlayGamesPlatform. 을


- iOS일 경우 GameCenterPlatform. 을 


쓰시면 됩니다.


또한, iOS 앱에서도 PlayGamesPlatform.을 사용하면 게임센터가 아닌 구글 플레이에 로그인하도록 할 수도 있습니다.



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
    void Start()
    {
#if UNITY_ANDROID
 
        PlayGamesClientConfiguration config = new PlayGamesClientConfiguration.Builder()
            .EnableSavedGames()
            .Build();
 
        PlayGamesPlatform.InitializeInstance(config);
 
        PlayGamesPlatform.DebugLogEnabled = true;
 
        PlayGamesPlatform.Activate();
 
#elif UNITY_IOS
 
        GameCenterPlatform.ShowDefaultAchievementCompletionBanner(true);
 
#endif
    }
cs



제일 먼저 Initialization과 Configuration 부분입니다.


구글의 저장된 게임 기능을 사용할 것이라면 (for Android)


.EnableSavedGames() 를 config에 추가해주세요.


마지막 줄에 PlayGamesPlatform.Activate() 를 해줘야 모든 기능이 작동합니다.


iOS는 특별한 config 빌드가 없어도 됩니다.



* 한 가지 주의할 점!


iOS 앱에서 안드로이드 용인 PlayGamesPlatform.Activate() 가 호출되면


애플 게임센터 api가 제대로 작동하지 않는 것을 경험했습니다.


때문에 게임센터에 접근하려면 위 처럼 코드를 #if 와 #elif 로 나눠주는 것이 좋습니다.



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    public void SignIn()
    {
        Social.localUser.Authenticate((bool success) =>
        {
            if (success)
            {
                // to do ...
                // 로그인 성공 처리
            }
            else
            {
                // to do ...
                // 로그인 실패 처리
            }
        });
    }
cs



Sign In 부분입니다.


Social.localUser로 Authenticate 하고 있기 때문에


Android와 iOS 둘 다 작동합니다.



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
    public void SignIn()
    {
#if UNITY_ANDROID
 
        PlayGamesPlatform.Instance.Authenticate((bool success) =>
        {
            if (success)
            {
                // to do ...
                // 구글 플레이 게임 서비스 로그인 성공 처리
            }
            else
            {
                // to do ...
                // 구글 플레이 게임 서비스 로그인 실패 처리
            }
        });
 
#elif UNITY_IOS
 
        Social.localUser.Authenticate((bool success) =>
        {
            if (success)
            {
                // to do ...
                // 애플 게임 센터 로그인 성공 처리
            }
            else
            {
                // to do ...
                // 애플 게임 센터 로그인 실패 처리
            }
        });
 
#endif
    }
cs



필요에 따라 이렇게 명확하게 구분하여 구현해도 됩니다.


(GameCenterPlatform 에는 Authenticate가 없기때문에 Social.localUser로 접근함)





플레이 게임 서비스 Sign Out 은 이렇게 하시면 됩니다.


(게임 센터 Sign Out api는 별도로 확인하지 못 함)



다음으로 업적과 리더보드를 구현해보겠습니다.



1
2
3
4
5
6
7
8
9
10
11
    public void UnlockAchievement(int score)
    {
        if (score >= 100)
        {
#if UNITY_ANDROID
            PlayGamesPlatform.Instance.ReportProgress(GPGSIds.achievement_100, 100f, null);
#elif UNITY_IOS
            Social.ReportProgress("Score_100", 100f, null);
#endif
        }
    }
cs



먼저, 업적을 잠금해제하는 코드 예시입니다.


Social.ReportProgress("업적ID", 0f~100f, callback);


첫 번째 파라미터에는 잠금해제 할 업적의 고유 ID를 입력해주고

(설정편에서 생성된 GPGSIds.cs에 있는 static 변수를 가져다 쓸 것)


두 번째 파라미터에는 해당 업적의 진행도(0f~100f) 를 넣어줍니다.

진행도가 존재하는 업적의 경우 진행도를 0과 100사이의 float형으로 환산하여 넣어주면 되고

단순히 조건을 충족하면 바로 잠금해제되는 업적일 경우 바로 100f를 넣어주면 됩니다.


세 번째 파라미터에는 callback 함수를 넣어줍니다.

콜백이 필요 없는 경우에는 null



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
    public void ShowAchievementUI()
    {
        // Sign In 이 되어있지 않은 상태라면
        // Sign In 후 업적 UI 표시 요청할 것
        if (Social.localUser.authenticated == false)
        {
            Social.localUser.Authenticate((bool success) =>
            {
                if (success)
                {
                    // Sign In 성공
                    // 바로 업적 UI 표시 요청
                    Social.ShowAchievementsUI();
                    return;
                }
                else
                {
                    // Sign In 실패 처리
                    return;
                }
            });
        }
 
        Social.ShowAchievementsUI();
    }
cs



업적 UI를 열려면 


Social.ShowAchievementsUI() 를 호출하면 됩니다.


* 소셜에 로그인이 되어있지 않다면 로그인 후 재요청하는 예외처리를 해주는 것이 좋습니다.



    



좌 : Android - Google Play Games Services 업적 UI 스크린샷

우 : iOS - Apple Game Center 목표 달성 UI 스크린샷



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
    public void ReportScore(int score)
    {
#if UNITY_ANDROID
 
        PlayGamesPlatform.Instance.ReportScore(score, GPGSIds.leaderboard_score, (bool success) =>
        {
            if (success)
            {
                // Report 성공
                // 그에 따른 처리
            }
            else
            {
                // Report 실패
                // 그에 따른 처리
            }
        });
 
#elif UNITY_IOS
 
        Social.ReportScore(score, "Leaderboard_ID", (bool success) =>
            {
                if (success)
                {
                    // Report 성공
                    // 그에 따른 처리
                }
                else
                {
                    // Report 실패
                    // 그에 따른 처리
                }
            });
        
#endif
    }
cs



다음은 리더보드에 점수를 기록하는 코드 예시입니다.


Social.ReportScore(점수, "리더보드 고유 ID", callback);


첫 번째 파라미터에는 플레이어가 기록한 점수를 넣어줍니다.


두 번째 파라미터에는 점수를 올릴 리더보드의 고유 ID를 넣어줍니다.

(설정편에서 생성된 GPGSIds.cs에 있는 static 변수를 가져다 쓸 것)


세 번째 파라미터에는 callback 함수를 넣어줍니다.

콜백이 필요 없는 경우에는 null


* ReportScore() 함수 내부에서 플레이어의 기록된 점수와 파라미터로 날아온 점수의 높낮이를 비교하고 필터링하기 때문에

 더 낮은 점수일 경우의 예외처리를 할 필요가 없습니다.



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
    public void ShowLeaderboardUI()
    {
        // Sign In 이 되어있지 않은 상태라면
        // Sign In 후 리더보드 UI 표시 요청할 것
        if (Social.localUser.authenticated == false)
        {
            Social.localUser.Authenticate((bool success) =>
            {
                if (success)
                {
                    // Sign In 성공
                    // 바로 리더보드 UI 표시 요청
                    Social.ShowLeaderboardUI();
                    return;
                }
                else
                {
                    // Sign In 실패 
                    // 그에 따른 처리
                    return;
                }
            });
        }
 
#if UNITY_ANDROID
        PlayGamesPlatform.Instance.ShowLeaderboardUI();
#elif UNITY_IOS
        GameCenterPlatform.ShowLeaderboardUI("Leaderboard_ID", UnityEngine.SocialPlatforms.TimeScope.AllTime);
#endif
    }
cs



리더보드의 UI를 열려면


Social.ShowLeaderboardUI()

PlayGamesPlatform.Instance.ShowLeaderboardUI()

GameCenterPlatform.ShowLeaderboardUI("순위표 고유 ID", TimeScope)


를 상황에 맞게 호출합니다.


* 마찬가지로, 소셜에 로그인이 되어있지 않다면 로그인 후 재요청하는 예외처리를 해주도록 합니다.





리더보드 UI를 열 때


위 처럼 TimeScope 를 정해서 넘겨주면 원하는 기간동안의 순위표를 보게할 수도 있습니다.


AllTime - 전체 기간 순위표

Today - 오늘 순위표

Week - 이번 주 순위표



    



Android - Google Play Games Services 리더보드 UI 스크린샷



    



iOS - Apple Game Center 순위표 UI 스크린샷




이상으로 포스팅을 마치겠습니다.


궁금한 점이 있다면 댓글 남겨주세요.



안녕하세요.


이번 포스팅의 주제는 Google Play Games 플러그인 연동하기 입니다!


유니티 프로젝트에


     Android - 구글 플레이 게임 서비스 (Google Play Games Services)

     iOS - 애플 게임 샌터 (Apple Game Center)


를 연동하고


1. 로그인(SignIn), 2. 로그아웃(SignOut), 3. 업적(Achievement), 4. 리더보드(Leaderboard) 


기능을 구현해보도록 하겠습니다.


물론 Google Play Games plugin for Unity - Github 사이트에 영문으로


업적, 리더보드, 저장된 게임, 퀘스트 등 다양한 기능 연동 가이드가 있습니다.


링크 : https://github.com/playgameservices/play-games-plugin-for-unity





우선 위 링크에서 [Clone or download] -> [Download.ZIP] 하여 플러그인을 다운로드합니다.


압축을 풀고 내용물을 보면





이렇게 되어있는데


맨 위 current-build 폴더에 최신 버전 플러그인.unitypackage가 들어있습니다.


(이 플러그인은 구글 플레이 게임서비스와 애플 게임센터 두 플랫폼 다 지원함)




유니티 프로젝트에 import 해둡니다.




[Android - 구글 플레이 게임 서비스 설정]



먼저, 구글 플레이 개발자 콘솔로 이동하여 (회원가입 후) 로그인 합니다.


플레이 콘솔 사이트 링크 : https://play.google.com/apps/publish/?hl=ko


(Google Play Developer Console의 탐색 기능과 디자인이 업데이트되었으며 이름이 Play Console로 변경되었습니다 - 17년 4월 기준)





우측에 [애플리케이션 만들기] 버튼을 클릭합니다. (플레이 콘솔에 앱을 추가하지 않은 경우만)





언어를 설정하고 제목을 입력한 후 [만들기] 하고


스토어 등록정보 등 기타 내용을 채워서 출시 조건을 맞춰줍니다.





애플리케이션 설정을 완료했다면 [게임 서비스]로 이동하여 [새 게임 추가]를 클릭합니다.





게임 이름을 입력하고 카테고리 선택 후 [계속] 합니다.


애플리케이션 설정과 마찬가지로 [게임 세부정보]에 내용을 채워줍니다.





그 다음 [연결된 앱]으로 이동하고 플랫폼을 선택합니다.





이전에 만들어 두었던 애플리케이션을 게임 서비스와 연결하는 과정입니다.


앱 이름과 패키지 이름, 기타 설정을 마치고 [저장하고 계속] 합니다.





연결이 정상적으로 완료된 모습입니다.







업적과 리더보드를 각각 추가해줍니다.


(참고로, 업적이나 리더보드 아이콘을 넣어주지 않으면 디폴트 아이콘이 보여집니다.)


그러고 나면 하단에 [리소스 받기] 가 보이실텐데요 한번 클릭해 봅시다.





위 처럼 [리소스 내보내기] 라고 뜨는데요


Android 앱은 [ANDROID] 탭을 눌러서 내용을 복사해둡니다.


(iOS 앱은 [OBJECTIVE-C] 탭을 눌러서 복사)


이제 유니티로 돌아옵니다.





플러그인이 정상적으로 임포트 되었다면 Window에 Google Play Games 메뉴가 생깁니다.


그럼 [Window] -> [Google Play Games] -> [Setup] -> [Android setup] 해줍니다.





위와 같은 팝업창이 뜨면 [Resources Definition]


아까 복사해 두었던 리소스를 붙여넣기 합니다.


[Web App Client ID]는 웹 게임용 변수이므로 패스하고 [Setup] 버튼을 누릅니다.





Setup이 완료되었습니다.


[OK]를 누르고 로딩을 기다리면





위 처럼 GPGSIds 라는 .cs 파일이 생성되었을 것입니다.





앞서 만들어둔 업적과 리더보드의 ID를 string 형식으로 담고있는 모습입니다.


업적을 잠금해제하거나 리더보드에 스코어를 올릴 때


static 클래스 GPGSIds의 string 변수를 가져다 쓸 것입니다.




[iOS - 애플 게임센터 설정]



아이튠즈 커넥트 사이트로 이동하여 회원가입 후 로그인 합니다.


iTunes Connect 사이트 링크 : https://itunesconnect.apple.com





로그인 후 메인 페이지에서 [나의 앱] 으로 이동합니다.





[나의 앱] 페이지 좌상단에 있는 [+ 버튼] 을 누른 후 [신규 앱] 을 클릭합니다.


이미 등록된 앱이 있다면 앱 아이콘을 클릭합니다.





플랫폼, 이름, 기본 언어, 번들 ID, SKU 를 입력하고 [생성] 합니다.





앱이 정상적으로 생성되었다면 앱 아이콘을 눌러 앱 페이지로 이동합니다.


그 다음 [앱 내 추가 기능] -> [Game Center] 메뉴로 이동합니다.





순위표와 목표 달성 (각각 리더보드와 업적을 뜻함) 이


비어있는 모습입니다.


먼저 순위표 옆에 있는 +버튼을 눌러줍니다.





개별 순위표를 [선택] 합니다.





개별 순위표 정보를 채워줍니다.


그리고 하단 순위표 현지화에 있는 [언어 추가] 를 눌러줍니다.





현지화 할 언어를 선택합니다.


나머지 정보를 입력하고 (이미지는 선택사항, 이미지를 올리지 않으면 디폴트 이미지가 보여짐) [Save] 합니다.


이제 리더보드가 준비되었습니다.


다음으로 업적을 추가해 봅시다.





목표 달성 옆에 있는 +버튼을 눌러서 업적을 생성합니다.


업적 정보를 입력하고 하단 목표 달성 현지화에 있는 [언어 추가] 를 눌러줍니다.





마찬가지로 현지화 할 언어를 선택하고 제목과 설명을 입력하고 [Save] 합니다.


위는, 업적 아이콘을 원하는 이미지로 직접 설정해 준 모습입니다.





다시 유니티로 돌아와서


[Window] -> [Google Plat Games] -> [Setup] -> [iOS setup...] 합니다.






팝업 창이 뜨면 플레이 콘솔 [리소스 내보내기] 에서 [OBJECTIVE-C] 를 선택하고 


복사 & 붙여넣기 합니다.


그리고 [Setup] 해줍니다.




자, 이제 정말로 연동할 준비가 다 되었습니다!


다음 편에 계속됩니다!


[Unity3D] 구글 플레이 게임 서비스 & 애플 게임 센터 연동 (2/2) #코드편 링크 : http://minhyeokism.tistory.com/72



이번에는 유니티에 구글 애드몹 SDK를 연동하여 [Banner 광고][Interstitial 광고]를 달아보도록 하겠습니다.



우선 AdMob 홈페이지에 들어가서 회원가입 후 로그인 합니다.


Google AdMob 사이트 링크 : http://www.google.co.kr/admob





홈페이지에서 [수익 창출] -> [+새로운 앱에서 수익 창출] 합니다.





1. 앱 선택


이미 스토어에 올라간 앱이라면 [앱 검색]


개발 중인 앱이라면 [앱 직접 추가]


이미 추가된 앱의 광고를 생성하려면 [추가한 앱 중에서 선택]


본 포스팅에서는 개발 중인 앱이라 가정하고 [앱 직점 추가] 로 진행


앱 이름과 플랫폼을 선택 후 [앱 추가] 를 누릅니다.





이제 ca-app-pub 로 시작하는 앱 ID가 생성되었음을 볼 수 있습니다.


2. 광고 형식 선택 및 광고 단위 이름 지정


먼저 [배너] 를 선택해주고


[광고 유형] [자동 새로고침] [텍스트 광고 스타일] [광고 단위 이름] 모두 설정해주고 [저장] 을 누릅니다.





앱 ID와 마찬가지로 [광고 단위 ID] 도 생성되었습니다.


3. Firebase 애널리틱스 설정(선택사항)


Firebase 애널리틱스 관련 내용은 다음에 포스팅 할 예정이니 [건너 뛰기] 누릅니다.





4. 구현 안내 보기


Banner 광고 생성을 완료하였으니


Interstitial 광고(전면 광고)도 생성하기 위해 [다른 광고 단위 생성] 합니다.





[전면] 을 선택하고 


[광고 유형][게재빈도 설정][광고 단위 이름] 을 설정하고 [저장] 합니다.





배너 광고 단위와 전면 광고 단위가 잘 생성되었음을 볼 수 있습니다.


이제 본격적으로 유니티에 연동할 차례입니다.





우선 아래 링크에서 AdMob Unity Plugin(GoogleMobileAds.unitypackage)을 다운로드 받습니다.


Google AdMob Unity Plugin(v3.4.0) 다운로드 링크 : https://github.com/googleads/googleads-mobile-unity/releases/tag/v3.4.0


다운로드한 unitypackage를 유니티 프로젝트에 import 합니다.



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
using UnityEngine;
using System;
using GoogleMobileAds.Api;
 
public class AdMobManager : MonoBehaviour
{
    public string android_banner_id;
    public string ios_banner_id;
 
    public string android_interstitial_id;
    public string ios_interstitial_id;
 
    private BannerView bannerView;
    private InterstitialAd interstitialAd;
 
    public void Start()
    {
        RequestBannerAd();
        RequestInterstitialAd();
 
        ShowBannerAd();
    }
 
    public void RequestBannerAd()
    {
        string adUnitId = string.Empty;
 
#if UNITY_ANDROID
        adUnitId = android_banner_id;
#elif UNITY_IOS
        adUnitId = ios_bannerAdUnitId;
#endif
 
        bannerView = new BannerView(adUnitId, AdSize.SmartBanner, AdPosition.Top);
        AdRequest request = new AdRequest.Builder().Build();
 
        bannerView.LoadAd(request);
    }
 
    private void RequestInterstitialAd()
    {
        string adUnitId = string.Empty;
 
#if UNITY_ANDROID
        adUnitId = android_interstitial_id;
#elif UNITY_IOS
        adUnitId = ios_interstitialAdUnitId;
#endif
 
        interstitialAd = new InterstitialAd(adUnitId);
        AdRequest request = new AdRequest.Builder().Build();
 
        interstitialAd.LoadAd(request);
 
        interstitialAd.OnAdClosed += HandleOnInterstitialAdClosed;
    }
 
    public void HandleOnInterstitialAdClosed(object sender, EventArgs args)
    {
        print("HandleOnInterstitialAdClosed event received.");
 
        interstitialAd.Destroy();
 
        RequestInterstitialAd();
    }
 
    public void ShowBannerAd()
    {
        bannerView.Show();
    }
 
    public void ShowInterstitialAd()
    {
        if (!interstitialAd.IsLoaded())
        {
            RequestInterstitialAd();
            return;
        }
 
        interstitialAd.Show();
    }
 
}
 
cs



AdMobManager.cs를 생성하고 위 코드를 복사 붙여넣기 합니다.


빈 게임 오브젝트를 생성하고 AdMobManager.cs를 Add Componet 해줍니다.





플랫폼 별로, banner_id 와 interstitial_id 변수 값에 앞서 얻은 id를 각각 입력해줍니다.


AdMob은 RequestAd() 로 광고를 요청하고 요청에 성공하면 ShowAd()로 광고를 송출하는 형식입니다.


배너광고는 이니셜라이즈 시 Request 하고 바로 Show 합니다.


전면광고는 미리 Request 로 캐싱해 두다가 원하는 상황에 Show 한 후 광고가 닫히면 재요청하는 구조입니다.


메모리 누수를 방지하기 위해


HandleOnInterstitialAdClosed 콜백에서 interstitialAd.Destroy(); 를 해주도록 합니다.





BannerView를 생성할 때 파라미터로 사이즈와 위치를 조절할 수 있는데요


AdSize.SmartBanner 로 설정해주면 디바이스의 해상도에 맞춰서 자동으로 크기 조절이 되고


AdPosition 파라미터로 배너의 위치를 조절할 수 있습니다.





위 스크린샷은 배너가 AdPosition.Top 으로 설정된 모습입니다.


전면 광고는 게임 오버나 버프 지급 등 특정 상황에 송출하도록 합니다.


RequestInterstitialAd() 로 캐싱해 두었던 전면광고를


원하는 상황에 ShowAd() 해줌으로써 광고를 송출시킵니다.


광고 시청이 완료되면 HandleOnInterstitialAdClosed 콜백이 호출되는데


콜백 안에 광고 시청에 대한 보상을 지급하고 


RequestInterstitialAd() 로 소모된 광고를 미리 재 요청해놓습니다.





게임 내 애드몹 전면광고가 송출된 모습입니다.


이상으로 포스팅을 마치도록 하겠습니다.



이번에는 Unity 프로젝트에 AdColony SDK 연동을 해보겠습니다! (역시 매우 간단)


AdColony 홈페이지 링크 : https://www.adcolony.com

AdColony SDK 다운로드 링크 : https://github.com/AdColony/AdColony-Unity-SDK-3



위의 AdColony GitHub 링크로 가서


[Clone or download -> Download ZIP]


한 후, 다운로드 받은 애드콜로니 유니티패키지를 프로젝트에 import 합니다.





다음으로 진행하기 전에 기본적인 세팅이 필요한데요


우선 애드콜로니에 회원가입하고 로그인 합니다.


그 다음 [MONETIZATION] 탭에서 [Setup New App] 을 클릭합니다





App의 Platform과 국가, 이름


광고를 Skip 가능하게 할 지와 


13세 미만 어린이에게 지도 감독이 필요한 앱인지 여부를 체크합니다.


그리고 앱의 특성이나 출시 국가에 따라 다르겠지만 웬만한 정책위반을 피하기 위해


되도록이면 [Customize Your Ads] 부분에 정치, 종교, 성인 등의 광고 타입은 체크해제 해주는 것이 좋습니다. 


모든 세팅이 완료되면 [Create] 합니다.





아래는 광고 App의 기본적 Setup이 완료된 모습입니다.


[AdColony App ID] 는 스크립트를 작성할 때 필요하므로 잘 기억해둡니다.


하단에 디폴트로 생선된 Zone [Ad Zone #1] 이 보일텐데 Zone 세팅을 위해 클릭해줍니다.





[Zone is active] - Yes 해주고


[Zone ID] 도 [AdColony App ID]  마찬가지로 스크립트 작성에 필요하므로 잘 기억해둡니다.


Zone Name과 Creative Type을 설정해줍니다. (우리는 비디오 광고를 연동할 것이기 때문에 Video로 설정)





그 다음 [Zone Type] 을 아래와 같이 


Value Exchange/V4VC 로 설정해줍니다.


모든 설정을 마치면 [Save] 합니다.





여기까지 되었다면 다시 유니티로 돌아와서


AdColonyManager 라는 스크립트를 만들고


아래의 코드를 복사 붙여넣기 합니다.


애드콜로니 사이트에서 App 세팅할 때 보았던


[AdColony App ID] 와 [Zone ID] 를 각각 플랫폼별 id 변수에 넣어줍니다.


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
using UnityEngine;
using System.Collections.Generic;
 
public class AdColonyManager : MonoBehaviour
{
    private const string android_appId = "xxxxxxxxxxxxxxxxxxx";
    private const string android_zoneId = "xxxxxxxxxxxxxxxxxxx";
 
    private const string ios_appId = "xxxxxxxxxxxxxxxxxxx";
    private const string ios_zoneId = "xxxxxxxxxxxxxxxxxxx";
 
    private string appId = string.Empty;
    private string zoneId = string.Empty;
 
    private AdColony.InterstitialAd ad = null;
 
    void Start()
    {
        Initialize();
    }
 
    private void Initialize()
    {
#if UNITY_ANDROID
        this.appId = android_appId;
        this.zoneId = android_zoneId;
#elif UNITY_IOS
        this.appId = ios_appId;
        this.zoneId = ios_zoneId;
#endif
 
        AdColony.Ads.OnConfigurationCompleted += (List<AdColony.Zone> zones_) =>
        {
            Debug.Log("AdColony.Ads.OnConfigurationCompleted called");
 
            if (zones_ == null || zones_.Count <= 0)
            {
                Debug.Log("Configure Failed");
            }
            else
            {
                Debug.Log("Configure Succeeded.");
            }
        };
 
        AdColony.Ads.OnRequestInterstitial += (AdColony.InterstitialAd ad_) =>
        {
            Debug.Log("AdColony.Ads.OnRequestInterstitial called");
 
            ad = ad_;
 
            // to do ...
            // 광고 요청에 성공했을 때 처리
 
            ShowAd();
        };
 
        AdColony.Ads.OnRequestInterstitialFailed += () =>
        {
            Debug.Log("AdColony.Ads.OnRequestInterstitialFailed called");
 
            // to do ...
            // 광고 요청에 실패했을 때 처리
        };
 
        AdColony.Ads.OnOpened += (AdColony.InterstitialAd ad_) =>
        {
            Debug.Log("AdColony.Ads.OnOpened called");
        };
 
 
        AdColony.Ads.OnClosed += (AdColony.InterstitialAd ad_) =>
        {
            Debug.Log("AdColony.Ads.OnClosed called, expired: " + ad_.Expired);
        };
 
        AdColony.Ads.OnExpiring += (AdColony.InterstitialAd ad_) =>
        {
            Debug.Log("AdColony.Ads.OnExpiring called");
        };
 
        AdColony.Ads.OnRewardGranted += (string zoneId, bool success, string name, int amount) =>
        {
            Debug.Log(string.Format("AdColony.Ads.OnRewardGranted called\n\tzoneId: "
                + "{0}\n\tsuccess: {1}\n\tname: {2}\n\tamount: {3}",
                zoneId, success, name, amount));
 
            if (success)
            {
                // to do ...
                // 광고 시청이 완료되었을 때 처리
                // 광고 시청에 대한 보상 지급 등 ...
            }
        };
 
        AdColony.AppOptions appOptions = new AdColony.AppOptions();
        appOptions.AdOrientation = AdColony.AdOrientationType.AdColonyOrientationAll;
 
        AdColony.Ads.Configure(this.appId, appOptions, this.zoneId);
    }
 
    public void RequestAd()
    {
        Debug.Log("**** Request Ad ****");
 
        AdColony.AdOptions adOptions = new AdColony.AdOptions();
        adOptions.ShowPrePopup = false;
        adOptions.ShowPostPopup = false;
 
        AdColony.Ads.RequestInterstitialAd(this.zoneId, adOptions);
    }
 
    public void ShowAd()
    {
        Debug.Log("**** Show Ad ****");
 
        if (this.ad != null)
        {
            AdColony.Ads.ShowAd(this.ad);
        }
    }
 
}
 
cs



RequestAd() 함수로 광고 요청을 보낸 후


요청에 성공했다면 OnRequestInterstitial 콜백을 타고


ShowAd() 함수를 호출되는 구조입니다. (요청에 실패하면 OnRequestInterstitialFailed 콜백)


그리고 ShowAd() 로 광고 시청이 완료되면 


OnRewardGranted 콜백이 호출되므로 (success == true)


콜백 안에 광고 시청에 대한 보상을 지급하는 코드를 작성해주면 됩니다.



* 광고 procedure


Initialize() -> RequestAd() -> OnRequestInterstitial -> ShowAd() -> OnRewardedGranted



위와 같은 절차로 여러분의 프로젝트에 코드를 녹여주시고 


OnRequestInterstitialFailed 나 OnExpiring 등 기타 콜백함수도 입맛에 맞게 정의해주시면 되겠습니다.


참고로 AdColony는 UnityAds와 다르게 Editor 상에서는 테스트가 불가능하므로 모바일에서 테스트해보시길 바랍니다!



Git을 통해 버전 관리를 할 때


Gitignore를 이용하면 특정 파일을 버전 컨트롤에서 제외시킬 수 있습니다.


이는 쓸모없는 파일이나 원하지 않는 파일을 commit에서 제외시켜 merge crash를 예방하기 위함인데요


아래는 Unity 프로젝트의 gitignore 목록입니다.



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
# =============== #
 
# Unity generated #
 
# =============== #
 
Temp/
 
Obj/
 
UnityGenerated/
 
Library/
 
 
 
# ===================================== #
 
# Visual Studio / MonoDevelop generated #
 
# ===================================== #
 
ExportedObj/
 
*.svd
 
*.userprefs
 
*.csproj
 
*.pidb
 
*.suo
 
*.sln
 
*.user
 
*.unityproj
 
*.booproj
 
 
 
# ============ #
 
# OS generated #
 
# ============ #
 
.DS_Store
 
.DS_Store?
 
._*
 
.Spotlight-V100
 
.Trashes
 
Icon?
 
ehthumbs.db
 
Thumbs.db
 
GraphicsSettings.asset
 
ProjectSettings.asset
 
ProjectVersion.txt
cs


유니티 프로젝트 루트 폴더안에 


.gitignore 라는 파일을 만들고





위 Text를 복사하여 붙여넣고 저장합니다.


그리고나면 Unity에서도 git을 무리없이 사용할 수 있습니다.



이번에는 Unity 프로젝트에 Unity Ads를 연동해보겠습니다. (매우 간단 주의)



우선 Unity에서 [Window -> Services] 하면 인스펙터 뷰 옆에 아래와 같은 화면이 나타나는데


Ads 항목이 OFF(디폴트)로 되어있는 것을 볼 수 있습니다.





Ads 항목을 클릭하면 아래와 같이 나오는데


토글 버튼을 눌러 비활성화되어있는 Ads를 활성화 시킵니다.





13세 미만 아이들에게 지도감독이 필요한 앱인지 여부를 설정하고 Continue 합니다.





활성화가 완료되면 아래와 같이 나옵니다.


원하는 Platform을 체크합니다.


Enable test mode를 체크하면 유니티에서 제공하는 짧은 테스트 광고영상만 송출됩니다.


(런칭 시 반드시 위 항목 체크를 해제할 것)





여기까지 완료했다면, 


using UnityEngine.Advertisements; 


를 할수있게 됩니다!


UnityAdsHelper라는 C# 스크립트를 하나 생성하고


아래의 코드를 복사해서 붙여 넣습니다.


그 다음, 빈 게임 오브젝트를 하나 생성하고 UnityAdsHelper.cs를 AddComponent 해줍니다.



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 UnityEngine.Advertisements;
 
public class UnityAdsHelper : MonoBehaviour
{
    private const string android_game_id = "xxxxxxx";
    private const string ios_game_id = "xxxxxxx";
 
    private const string rewarded_video_id = "rewardedVideo";
 
    void Start()
    {
        Initialize();
    }
 
    private void Initialize()
    {
#if UNITY_ANDROID
        Advertisement.Initialize(android_game_id);
#elif UNITY_IOS
        Advertisement.Initialize(ios_game_id);
#endif
    }
 
    public void ShowRewardedAd()
    {
        if (Advertisement.IsReady(rewarded_video_id))
        {
            var options = new ShowOptions { resultCallback = HandleShowResult };
 
            Advertisement.Show(rewarded_video_id, options);
        }
    }
 
    private void HandleShowResult(ShowResult result)
    {
        switch (result)
        {
            case ShowResult.Finished:
                {
                    Debug.Log("The ad was successfully shown.");
 
             // to do ...
             // 광고 시청이 완료되었을 때 처리
 
                    break;
                }
            case ShowResult.Skipped:
                {
                    Debug.Log("The ad was skipped before reaching the end.");
 
             // to do ...
             // 광고가 스킵되었을 때 처리
 
                    break;
                }
            case ShowResult.Failed:
                {
                    Debug.LogError("The ad failed to be shown.");
 
             // to do ...
             // 광고 시청에 실패했을 때 처리
 
                    break;
                }
        }
    }
}
 
cs



위 코드는


ShowRewardedAd() 함수로 비디오 광고 송출 요청을 하고


HandleShowResult 콜백으로 요청 결과에 따라 Finished, Skipped, Failed 처리하는 구조로 되어있습니다.


* 여기서 androiod game id와 ios game id 그리고 rewarded video id가 필요한데


Services의 우측 상단에 있는 Go to Dashboard 를 누르면





아래와 같은 화면이 뜨는데


여기서 android 게임 ID와 ios 게임 ID를 확인할 수 있습니다.





UnityAdsHelper.cs의 android_game_id 변수와 ios_game_id 변수에 Game Id 숫자 7자리를 각각 넣어주시면 됩니다.


그 다음, 구글 플레이 스토어 또는 애플 앱 스토어 플랫폼을 클릭하여 Ad placements 정보를 확인합니다.





기본적으로 Video와 Rewarded Video 두 가지가 생성되어있는데


첫 번째에 있는 Video는 Skip이 가능한 광고이고


두 번째에 있는 Rewarded Video는 Skip이 불가능한 보상형 광고입니다. 


수익을 많이 내려면 당연히 Skip이 불가능한 보상형 광고를 써야겠죠?


위 스크린샷 처럼 Rewarded Video를 Enabled 하고 Default로 설정합니다. (우측에 EDIT 버튼으로 세부설정이 가능함)


그리고 Rewarded Video의 PLACEMENT ID를 복사하여


UnityAdsHelper.cs의 rewarded_video_id 변수에 넣어줍니다.



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



자, 모든 준비가 끝났습니다!


Unity Editor에서 ShowRewardedAd()로 광고를 요청했을 때 아래와 같은 화면이 나온다면


정상적으로 연동된 것입니다.





이제 원하는 상황에 ShowRewardedAd() 함수를 호출하고 (주로 게임 오버될 때 혹은 특정 버프를 받을 때)


HandleShowResult 콜백에서 원하는 처리를 하여 마무리해줍니다. (캐릭터 부활 혹은 버프효과 적용 등)





Fab Lab Seoul과 서울 디자인재단 주최로 진행된 [2016 패션웨어러블 메이커톤]에 

개발자로 참가했었습니다.

옷에 달린 단추를 누르거나 소매에 달린 슬라이더를 조절하여 

스마트폰 뮤직플레이어를 컨트롤하는 웨어러블 디바이스를 개발하는 것이 저희 팀의 목표였고

옷과 스마트폰의 사이에 블루투스 통신할 수 있는 뮤직플레이어 App을 만드는 것이 제 역할이였습니다.



여기서 

▶MAKE A THON 이란? 

의상 제작이 가능한 패션 디자이너와 기획자, 개발자, 2D/3D 디자이너 등 다양한 분야의 메이커들이 팀을 이루어 사전행사와 무박2일의 본행사동안 “패션 웨어러블”을 주제로 Ideation부터 Prototyping 까지 진행하는 메이킹 마라톤입니다.



제가 만든 간단한 뮤직플레이어 App을 소개해드리겠습니다!



Unity3D로

 - 플레이리스트

 - 음악 재생/정지

 - 볼륨 조절

 - 재생 위치 조절


등의 기능이 구현된 간단한 뮤직플레이어를 만들었는데요.

그 중에, 현재 재생되고 있는 음악의 강약을 옷에 달린 LED에 그대로 표현해주기 위해 비쥬얼라이저를 구현했습니다.

화면 가운데에는 PointLight가 있고 하단에는 막대 그래프가 있는데

영상에서 보이는 것 처럼 재생 중인 음악의 세기에 따라 PointLight의 밝기가 반응하고 하단의 막대그래프도 요동치게끔 하였습니다.


이처럼 AudioListener.GetOutputData() 함수를 이용하면 사운드 비쥬얼라이저를 쉽게 구현할 수 있습니다.


https://docs.unity3d.com/ScriptReference/AudioListener.GetOutputData.html



아래는 코드의 일부입니다.



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
using UnityEngine;
using System.Collections.Generic;
 
public class Visualizer : MonoBehaviour
{
    public List<UISprite> target_sprites;
 
    public Light pointLight;
 
    public int detail;
    public float amplitude;
    public float intensityFactor;
 
    private float baseIntensity = 0.1f;
 
    void Update()
    {
        GetOutputData();
    }
 
    private void GetOutputData()
    {
        float[] data = new float[detail];
        float packagedData = 0.0f;
 
        AudioListener.GetOutputData(data, 0);
 
        for (int i = 0; i < data.Length; ++i)
        {
            packagedData += Mathf.Abs(data[i]);
        }
 
        float resultHeight = packagedData * amplitude;
        float resultIntensity = baseIntensity + packagedData * amplitude;
 
        for (int i = 0; i < target_sprites.Count; ++i)
        {
            target_sprites[i].height = (int)(resultHeight * ((i + 1* 0.4f));
        }
 
        pointLight.intensity = (baseIntensity + packagedData) / (detail / intensityFactor);
    }
 
}
cs


여기서 target_sprites 는 아래의 5개 막대를 담은 변수이고

AudioListner.GetOutputData()로 추출된 데이터(packagedData)를 통해 막대그래프의 height와 PointLight의 밝기를 조절하는 코드입니다.




아래는 메이커톤 당시 시연영상입니다~



영상 초중반 부는 팀원의 프로젝터 맵핑 연출이고 후반부 조금은 음악에 LED가 반응하는 모습을 짧게! 볼 수 있습니다.

시연 영상을 제대로 찍지 못해서 아쉬움이 많이 남네요 ㅠㅠ




AndroidManifest에 다음 줄을 추가하면

시작하자마자 뜨는 퍼미션 요청을 스킵할 수 있습니다.


<meta-data android:name="unityplayer.SkipPermissionsDialog" android:value="true" />



우선, 유니티에서 Window -> Services 합니다.



그러고 나면 Inspector 옆에 Services라는 텝이 생기는데 

Select Organization 하고 Create 해줍니다.



그러면 유니티에서 제공하는 Service들이 보일 것입니다.

그 중에, OFF상태인 In-App Purchasing 텝을 클릭합니다.



우측상단에 있는 토글을 눌러 OFF되어있던 상태를 ON으로 만들어줍니다.

그리고 13세 이하 어린이에게 지도감독이 필요한 지 여부를 선택하고 Save Changes 합니다.



이제, 프로젝트에 In-App Purchasing이 활성화 된 모습을 보실 수 있을 것입니다.

여기서 잊지말고 Import 버튼을 꼬옥 눌러줍니다!



프로젝트 뷰에 Unity IAP 플러그인이 제대로 임포트 된 모습입니다.



이제 기본 세팅이 다 되었습니다. 그렇다면 코딩을 해볼까요?

아래와 같이 Script를 작성합니다.


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
using System;
using UnityEngine;
using UnityEngine.Purchasing;
 
public class InAppPurchaser : MonoBehaviour, IStoreListener
{
    private static IStoreController storeController;
    private static IExtensionProvider extensionProvider;
 
    #region 상품ID
    // 상품ID는 구글 개발자 콘솔에 등록한 상품ID와 동일하게 해주세요.
    public const string productId1 = "gem1";
    public const string productId2 = "gem2";
    public const string productId3 = "gem3";
    public const string productId4 = "gem4";
    public const string productId5 = "gem5";
    #endregion
 
    void Start()
    {
        InitializePurchasing();
    }
 
    private bool IsInitialized()
    {
        return (storeController != null && extensionProvider != null);
    }
 
    public void InitializePurchasing()
    {
        if (IsInitialized())
            return;
 
        var module = StandardPurchasingModule.Instance();
 
        ConfigurationBuilder builder = ConfigurationBuilder.Instance(module);
 
        builder.AddProduct(productId1, ProductType.Consumable, new IDs
        {
            { productId1, AppleAppStore.Name },
            { productId1, GooglePlay.Name },
        });
 
        builder.AddProduct(productId2, ProductType.Consumable, new IDs
        {
            { productId2, AppleAppStore.Name },
            { productId2, GooglePlay.Name }, }
        );
 
        builder.AddProduct(productId3, ProductType.Consumable, new IDs
        {
            { productId3, AppleAppStore.Name },
            { productId3, GooglePlay.Name },
        });
 
        builder.AddProduct(productId4, ProductType.Consumable, new IDs
        {
            { productId4, AppleAppStore.Name },
            { productId4, GooglePlay.Name },
        });
 
        builder.AddProduct(productId5, ProductType.Consumable, new IDs
        {
            { productId5, AppleAppStore.Name },
            { productId5, GooglePlay.Name },
        });
 
        UnityPurchasing.Initialize(this, builder);
    }
 
    public void BuyProductID(string productId)
    {
        try
        {
            if (IsInitialized())
            {
                Product p = storeController.products.WithID(productId);
 
                if (p != null && p.availableToPurchase)
                {
                    Debug.Log(string.Format("Purchasing product asychronously: '{0}'", p.definition.id));
                    storeController.InitiatePurchase(p);
                }
                else
                {
                    Debug.Log("BuyProductID: FAIL. Not purchasing product, either is not found or is not available for purchase");
                }
            }
            else
            {
                Debug.Log("BuyProductID FAIL. Not initialized.");
            }
        }
        catch (Exception e)
        {
            Debug.Log("BuyProductID: FAIL. Exception during purchase. " + e);
        }
    }
 
    public void RestorePurchase()
    {
        if (!IsInitialized())
        {
            Debug.Log("RestorePurchases FAIL. Not initialized.");
            return;
        }
 
        if (Application.platform == RuntimePlatform.IPhonePlayer || Application.platform == RuntimePlatform.OSXPlayer)
        {
            Debug.Log("RestorePurchases started ...");
 
            var apple = extensionProvider.GetExtension<IAppleExtensions>();
 
            apple.RestoreTransactions
                (
                    (result) => { Debug.Log("RestorePurchases continuing: " + result + ". If no further messages, no purchases available to restore."); }
                );
        }
        else
        {
            Debug.Log("RestorePurchases FAIL. Not supported on this platform. Current = " + Application.platform);
        }
    }
 
    public void OnInitialized(IStoreController sc, IExtensionProvider ep)
    {
        Debug.Log("OnInitialized : PASS");
 
        storeController = sc;
        extensionProvider = ep;
    }
 
    public void OnInitializeFailed(InitializationFailureReason reason)
    {
        Debug.Log("OnInitializeFailed InitializationFailureReason:" + reason);
    }
 
    public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs args)
    {
        Debug.Log(string.Format("ProcessPurchase: PASS. Product: '{0}'", args.purchasedProduct.definition.id));
 
        switch (args.purchasedProduct.definition.id)
        {
            case productId1:
 
                // ex) gem 10개 지급
 
                break;
 
            case productId2:
 
                // ex) gem 50개 지급
 
                break;
 
            case productId3:
 
                // ex) gem 100개 지급
 
                break;
 
            case productId4:
 
                // ex) gem 300개 지급
 
                break;
 
            case productId5:
 
                // ex) gem 500개 지급
 
                break;
        }
 
        return PurchaseProcessingResult.Complete;
    }
 
    public void OnPurchaseFailed(Product product, PurchaseFailureReason failureReason)
    {
        Debug.Log(string.Format("OnPurchaseFailed: FAIL. Product: '{0}', PurchaseFailureReason: {1}", product.definition.storeSpecificId, failureReason));
    }
}
 
cs



결제를 진행해야하는 부분에서 


위의 BuyProductID 함수의 파라미터로 상품ID를 넣고 콜(구매요청)하면


product가 initialize된 후 ProcessPurchase로 결과가 넘어오는데


args.purchasedProduct.definition.id로 구매요청된 상품ID를 판별하여


상품에 맞는 보상을 지급해주면 됩니다. 참 쉽죠?


유니티 5.3버전 이전에는(유니티가 자체 IAP를 지원하기 전) Android와 iOS 각각 따로 결제시스템을 만들어야했는데


지금은 Android와 iOS를 동시에 지원해주기 때문에 정말 편하답니다~


    


결제된 정보는 구글 Payments 판매자 센터에서 확인하실 수 있습니다. (https://wallet.google.com/merchant)



+ Recent posts