2014년 4월 24일 목요일

Unity Plugin_Ad@m Sdk를 활용한 Ad@m 광고 달기 for Android - 2


이전 포스팅에서 Jar파일생성후 
Fat Jar Exporter를 활용하여 Jar파일을 생성하는것
까지 진행 했습니다.
1. Plugins/Android 폴더 생성후
AdamPlugin.jar, AndroidManifest파일 생성

2.AndroidManifest.xml파일 내용
-Unity\Editor\Data\PlaybackEngines\androidplayer
안에보시면 기본 AndroidManifest파일이
존재하니 가지고오셔서 위처럼 수정.
(Permission부분또한 동일하게 추가하셔야됩니다.)

3. 여기까지 완료하셨으면 Scene
생성후 Build & Run 해보시면 
 하단에 Banner가 뜨시는것을 확인하실수있습니다.

4. Interstitial Ad(전면형 광고) 관련 스크립트
링크


5.IOS작업또한
 Document참고하셔서 이방식대로 구현하시면
쉽게 구현하실수 있습니다.

추후 IOS작업도 포스팅 하겠습니다.

-End-

Ad@m_Adam.cs

Adam.cs
public class Adam : MonoBehaviour {

    AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
    AndroidJavaObject activity;
    #region Singleton
    private static Adam sInstance;

    public static Adam GetInstance
    {
        get
        {
            if (sInstance == null) sInstance = (Adam)FindObjectOfType(typeof(Adam));

            if (sInstance == null)
            {
                if (GameObject.Find("Singleton"))
                {
                    GameObject goInst = GameObject.Find("Singleton");
                    sInstance = goInst.AddComponent<Adam>();
                }
                else
                {
                    GameObject goInst = new GameObject("Singleton");
                    sInstance = goInst.AddComponent<Adam>();
                }
            }
            return sInstance;
        }
    }
    #endregion

    public void LoadInterstitial()
    {
        activity = jc.GetStatic<AndroidJavaObject>("currentActivity");
        activity.Call("LoadInterstitial");
    }

}
Test.cs
public class Test : MonoBehaviour {

 // Use this for initialization
 void Start () {
        Adam.GetInstance.LoadInterstitial();
 }
}

How to Use.
Adam.cs생성후 다른 스크립트에서 LoadInterstitial() 함수를 호출하면 된다.

Ad@m_UnityPlayerNativeActivity.java

package com.my.app;

import net.daum.adam.publisher.AdInterstitial;
import net.daum.adam.publisher.AdView;
import net.daum.adam.publisher.impl.AdError;
import android.app.NativeActivity;
import android.content.res.Configuration;
import android.graphics.PixelFormat;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.RelativeLayout;

import com.unity3d.player.UnityPlayer;

public class UnityPlayerNativeActivity extends NativeActivity
{
 protected UnityPlayer mUnityPlayer;  // don't change the name of this variable; referenced from native code
 
 // Adam Plugin field
 final String mBannerID = "TestClientId"; // Banner Client ID
 final String mInterstitialID = "InterstitialTestClientId"; // Intersitial Client ID
 private static final String LOGTAG = "Adam";
 private RelativeLayout relativeLayout = null;
 private AdView adView = null;
 AdInterstitial mAdInterstitial = null;
 
 // UnityPlayer.init() should be called before attaching the view to a layout - it will load the native code.
 // UnityPlayer.quit() should be the last thing called - it will unload the native code.
 protected void onCreate (Bundle savedInstanceState)
 {
  requestWindowFeature(Window.FEATURE_NO_TITLE);
  super.onCreate(savedInstanceState);
  
  getWindow().takeSurface(null);
  setTheme(android.R.style.Theme_NoTitleBar_Fullscreen);
  getWindow().setFormat(PixelFormat.RGB_565);

  mUnityPlayer = new UnityPlayer(this);
  if (mUnityPlayer.getSettings ().getBoolean ("hide_status_bar", true))
   getWindow ().setFlags (WindowManager.LayoutParams.FLAG_FULLSCREEN,
                          WindowManager.LayoutParams.FLAG_FULLSCREEN);

  int glesMode = mUnityPlayer.getSettings().getInt("gles_mode", 1);
  boolean trueColor8888 = false;
  mUnityPlayer.init(glesMode, trueColor8888);

  View playerView = mUnityPlayer.getView();
  
  //Adam
  relativeLayout = new RelativeLayout(this);

     initAdam();
     initInterstitial();

     relativeLayout.addView(adView);

     RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
     params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
     
     addContentView(playerView, params);
     addContentView(relativeLayout, params);

     adView.setLayoutParams(params);
     
  setContentView(playerView);
  addContentView(relativeLayout, params);
  
  playerView.requestFocus();
 }
 
 public void LoadInterstitial()
   {
     mAdInterstitial.loadAd();
   }

   public void initInterstitial()
   {
     mAdInterstitial = new AdInterstitial(this);

     mAdInterstitial.setClientId("8ec8Z0kT14572d5fa2c");

     mAdInterstitial.setOnAdLoadedListener(new AdView.OnAdLoadedListener() {
       public void OnAdLoaded() {
         Log.i("MSGAdam", "광고가 로딩되었습니다.");
       }
     });
     mAdInterstitial.setOnAdFailedListener(new AdView.OnAdFailedListener()
     {
       public void OnAdFailed(AdError error, String errorMessage) {
         Log.i("MSGAdam", errorMessage);
       }
     });
   }

   public void initAdam()
   {
     adView = new AdView(this);

     adView.setOnAdClickedListener(new AdView.OnAdClickedListener() {
       public void OnAdClicked() {
         Log.i(LOGTAG, "광고를 클릭했습니다.");
       }
     });
     adView.setOnAdFailedListener(new AdView.OnAdFailedListener() {
       public void OnAdFailed(AdError arg0, String arg1) {
         Log.w(LOGTAG, arg1);
       }
     });
     adView.setOnAdLoadedListener(new AdView.OnAdLoadedListener() {
       public void OnAdLoaded() {
         Log.i(LOGTAG, "광고가 정상적으로 로딩되었습니다.");
       }
     });
     adView.setOnAdWillLoadListener(new AdView.OnAdWillLoadListener() {
       public void OnAdWillLoad(String arg1) {
         Log.i(LOGTAG, "광고를 불러옵니다. : " + arg1);
       }
     });
     adView.setOnAdClosedListener(new AdView.OnAdClosedListener() {
       public void OnAdClosed() {
         Log.i(LOGTAG, "광고를 닫았습니다.");
       }
     });
     adView.setClientId(mBannerID);

     adView.setRequestInterval(30); // default 60 , 배너 갱신 간격

     adView.setAnimationType(AdView.AnimationType.NONE); 
     adView.setVisibility(0);
   }
 
 protected void onDestroy ()
 {
  mUnityPlayer.quit();
  super.onDestroy();
 }

 // onPause()/onResume() must be sent to UnityPlayer to enable pause and resource recreation on resume.
 protected void onPause()
 {
  super.onPause();
  mUnityPlayer.pause();
 }
 protected void onResume()
 {
  super.onResume();
  mUnityPlayer.resume();
 }
 public void onConfigurationChanged(Configuration newConfig)
 {
  super.onConfigurationChanged(newConfig);
  mUnityPlayer.configurationChanged(newConfig);
 }
 public void onWindowFocusChanged(boolean hasFocus)
 {
  super.onWindowFocusChanged(hasFocus);
  mUnityPlayer.windowFocusChanged(hasFocus);
 }
 public boolean dispatchKeyEvent(KeyEvent event)
 {
  if (event.getAction() == KeyEvent.ACTION_MULTIPLE)
   return mUnityPlayer.onKeyMultiple(event.getKeyCode(), event.getRepeatCount(), event);
  return super.dispatchKeyEvent(event);
 }
}

Unity Plugin_Ad@m Sdk를 활용한 Ad@m 광고 달기 for Android - 1


Summary
기존의 모바일 광고 지원 솔루션이 많지만 한국유저를 타겟으로한 게임이면
수익률이 어느정도 보장이되는 Ad@m SDK + Unity 통합작업 관련 포스팅.
저는 Unity 4.3.3f pro + Eclipse로 작업진행.
UnityNativeActivity를 수정하여 Ad를 붙이는 방법으로 진행.

총 2편으로 진행하겠습니다.

Requirements.

Unity Pro License
Ad@m Android SDK - Ad@m HomePage
Eclipse Fat Jar Exporter Plugin - Fat Jar Homepage
Ad@m 사이트,앱관리 에서 앱등록.

Process.

1. Empty Android Project 생성
- pakage Name은 Unity의 pakage명과 동일하게.

2. 불필요한 icon, activity또한 전부 Off
3.해당 Project->Properties->JavaBuildPath
Class.jar파일(Unity 폴더내의)
AdamSDK.jar
2개의 jar파일 Import

4. Unity\Editor\Data\PlaybackEngines\
androiddevelopmentplayer\src\com\unity3d\player안의
UnityNativeActivity.java파일 복사해서
현재프로젝트안에 배치.

5.해당 파일 수정
하단 링크 참고.
UnityPlayerNativeActivity.java

6. Activity 수정완료후 Project Export
Fat Jar Exporter옵션 선택.


7.use extern jar-name 체크후 경로지정


8. Import설정에서 필요한 Activity와 adamSDK만 포함한뒤
FInish!



Unity Plugin_Ad@m Sdk를 활용한 Ad@m 광고 달기 for Android - 1(END)

Unity2D_ Generic Object Pool Class v1.2


Summary.
기존의 Object Pooling을 사용하기위해 사용하던것을 Generic Class화 하여 봤습니다.
코드의 문제가 군데군데 보이지만 이후 조금씩 보완해 나가겠습니다.

Pool.cs
using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class Pool<T> : MonoBehaviour where T : MonoBehaviour {

    public GameObject _PoolObject;
    public int _PoolLength;

    private int mPoolLength;
    private List<T> mPoolList;
    private int? mIndex = null;

    void Awake()
    {
        Initialize(_PoolLength);
    }


    private void Initialize(int length)
    {
        mPoolLength = length;
        mPoolList = new List<T>();
        for (int i = 0; i < mPoolLength; ++i)
        {
            GameObject tmp = Instantiate(_PoolObject) as GameObject;
            tmp.transform.parent = transform;
            tmp.name = typeof(T).ToString() + i;
            mPoolList.Add(tmp.GetComponent<T>());
        }
    }

    public T _GetObject
    {
        get 
        {
            if (mIndex == null)
                mIndex = 0;
            else
                mIndex++;

            return mPoolList[(int)mIndex];
        }
    }

    public int? _Index
    {
        get { return mIndex; }
        set { mIndex = value; }
    }

 public List<T> _PoolList
    {
        get { return mPoolList; }
        set { mPoolList = value; }
    }

    public int Length
    {
        get { return mPoolLength; }
    }
}

How to Use.

Enemy.cs
using UnityEngine;
using System.Collections;

public class Enemy : MonoBehaviour
{
    public void Spawn(Vector2 pos)
    {
        Debug.Log("Spawn : " + name + "pos :" + pos);
    }
    public void Die()
    {
        Debug.Log("Die : " + name);
    }
}
EnemyPool.cs
using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class EnemyPool : Pool<Enemy>
{
    public void AllEnemySpawn()
    {
        foreach (Enemy ene in _PoolList)
            ene.Spawn(Vector2.zero);
        _Scope = 0;
    }

    public void AllEnemyDie()
    {
        foreach (Enemy ene in _PoolList)
            ene.Die();
        _Scope = 0;
    }

    public void EnemySpwan(Vector2 pos)
    {
        _GetObject.Spawn(pos);
    }

    public void EnemySpawn()
    {
        _GetObject.Spawn(Vector2.zero);
    }

    public void EnemyDie(Enemy target)
    {
        _PoolList.Find(
            delegate(Enemy enemy)
            {
                if (enemy == target)
                {
                    enemy.Die();
                    return true;
                }
                else return false;
            }
            );
    }
}
fix-
v1.0 - 첫 업데이트. 기본적인 Object Pool 구조 작성.
v1.1 - value name modify
v1.2 - method name modify

2014년 4월 23일 수요일

Unity2D_Pixel Perfect _ Camera Size Setting




Intro. 

오랜만의 포스팅 입니다.

Unity 4.3.x 버전 이후 크게 달라진 작업환경(Unity2D 지원)

NGUI , Facebook , GPGS 등 Plugin의 업데이트..

등을 이유로 이런저런 작업환경 셋팅,테스트로 이전의 포스팅(Facebook)관련

또한 이후 정리하여 새로 처음부터(봐뀐것이 너무많아서..)포스팅 하겠습니다.


Purpose. 
 Unity 2D 환경에서 작업전 
이미지 사이즈(Pixel)과 Camera에서의 이미지사이즈(Unit)를 일치 시킴.


e.g.
기본적으로 Unity Sprite생성시
Pixel to Unit 의 값은 100입니다.
이를기준으로 진행하겠습니다.


화면해상도를 720 * 1280(9 : 16)를 기준으로 잡고
작업한 이미지를 Unity내에서 사용할때

Camera Size = (ScreenHeight / 2) / PixelsToUnits

i.e)camera size = 1280 / 2 / 100

result = 6.4


이런식으로 Size를 셋팅 하시면 아주 간단하게
작업할때 사용했던 Size 그대로 Unity안에서 사용할수있습니다!