2014년 11월 6일 목요일

Unity_IOS 에서 Screen Capture한 이미지를 Social.framework를 활용하여 공유하는 방법정리.

Explain.


IOS 에서 Screen Capture한 이미지를 Social.framework를 활용하여 공유하는 방법정리.


Sequence.


1. Application.CaptureScreenShot() 으로 캡쳐진행.

2. 위의 이미지를 가지고 IOS의 social.framwork를 활용하여 쉐어진행.


In Xcode

아래 2파일[SocialHelper.h , mm]을 현재 프로젝트의 Plugin/IOS 위치 시킨다.

SocialHelper.h
#import &ltFoundation/Foundation.h>

@interface SocialHelper : NSObject
{
    
}

+(void)ImageToTwitter:(NSString*)filePath;

+(void)ImageToFaceBook:(NSString*)filePath;

@end

SocialHelper.mm
#import "SocialHelper.h"

#import &ltSocial/Social.h>
#import &ltUIKit/UIKit.h>
#import &ltUnityAppController.h>

#pragma mark - Unity methods

@implementation SocialHelper


+ (void) ImageToTwitter:(NSString *) filePath
{
    UIViewController *rootViewController = [GetAppController() rootViewController];
    UIImage *image = [UIImage imageWithContentsOfFile:filePath];
    
    if([SLComposeViewController isAvailableForServiceType:SLServiceTypeTwitter]){
        SLComposeViewController *mySLComposeSheet = [SLComposeViewController composeViewControllerForServiceType:SLServiceTypeTwitter];
        
        [mySLComposeSheet setInitialText:@"Let's Play Together!"];
        [mySLComposeSheet addImage:image];
        [mySLComposeSheet addURL:[NSURL URLWithString:@"https://myurl.com"]];
        [mySLComposeSheet setCompletionHandler:^(SLComposeViewControllerResult result) {
            switch (result) {
                case SLComposeViewControllerResultCancelled:
                    NSLog(@"Twtitter - POST Cancel");
                    break;
                    
                case SLComposeViewControllerResultDone :
                    NSLog(@"Twitter - Post Success");
                    break;
                    
                default:
                    break;
            }
        }];
        [rootViewController presentViewController:mySLComposeSheet animated:YES completion:nil];
    }
}

+ (void) ImageToFaceBook:(NSString *) filePath
{
    UIViewController *rootViewController = [GetAppController() rootViewController];
    UIImage *image = [UIImage imageWithContentsOfFile:filePath];
    
    if([SLComposeViewController isAvailableForServiceType:SLServiceTypeFacebook]){
        SLComposeViewController *mySLComposeSheet = [SLComposeViewController composeViewControllerForServiceType:SLServiceTypeFacebook];
        
        [mySLComposeSheet setInitialText:@"Let's Play Together!"];
        [mySLComposeSheet addImage:image];
        [mySLComposeSheet addURL:[NSURL URLWithString:@"https://myurl.com"]];
        [mySLComposeSheet setCompletionHandler:^(SLComposeViewControllerResult result) {
            switch (result) {
                case SLComposeViewControllerResultCancelled:
                    NSLog(@"Facebook - POST Cancel");
                    break;
                    
                case SLComposeViewControllerResultDone :
                    NSLog(@"Facebook - Post Success");
                    break;
                    
                default:
                    break;
            }
        }];
        [rootViewController presentViewController:mySLComposeSheet animated:YES completion:nil];
    }
}

extern "C" {
    void PostImageToTwitter(const char * filePath)
    {
        NSLog(@"Share Iamge Path : %@", [NSString stringWithUTF8String:filePath]);
        [SocialHelper ImageToTwitter:[NSString stringWithUTF8String:filePath]];
    }
    
    void PostImageToFacebook(const char * filePath)
    {
        NSLog(@"Share Iamge Path : %@", [NSString stringWithUTF8String:filePath]);
        [SocialHelper ImageToFaceBook:[NSString stringWithUTF8String:filePath]];
    }
}

@end



In Unity

using UnityEngine;
using System.Collections;
using System.Runtime.InteropServices;


public enum SocialType : int
{
    Twitter,
    Facebook
}

public class SocialHelper : MonoBehaviour
{

    public static IEnumerator PostImage(SocialType type)
    {
        string fileName = getCaptureFileName();

        Application.CaptureScreenshot(fileName);

        yield return new WaitForSeconds(1.0f);

        if (type == SocialType.Facebook)
            PostImageToFacebook(string.Format("{0}/{1}", Application.persistentDataPath, fileName));

        if (type == SocialType.Twitter)
            PostImageToTwitter(string.Format("{0}/{1}", Application.persistentDataPath, fileName));
    }

    static string getCaptureFileName()
    {
        return string.Format("capture_{0}.png", System.DateTime.Now.ToString("yyyy-MM-dd-HH-mm-ss"));
    }



    [DllImport("__Internal")]
    static extern void PostImageToTwitter(string filePath);

    [DllImport("__Internal")]
    static extern void PostImageToFacebook(string filePath);

}

SocialHelper.PostImage(SocialType.Facebook); // 이후 이런식으로 메소드를 호출시 공유가 진행.



Reference.


IOS 에서 Screen Capture한 이미지 Camera Roll에 등록하기.

Social.Framework


2014년 10월 31일 금요일

Unity for IOS_Screen Capture한 이미지 카메라롤 에 등록하기.


Explan.

IOS에서 Application.CaptureScreenShot(path) 메소드를 사용할경우
캡처는 진행되나 해당 app 의 document폴더로 들어가서 카메라 롤에서 확인할수가 없다.
이를 등록해주는 방법 포스팅.


CaptureHelper.h
#import <Foundation/Foundation.h>

@interface CaptureHelper : NSObject

+(CaptureHelper *)sharedInstancs;

@end
CaptureHelper.mm
#import "CaptureHelper.h"

static CaptureHelper * captureHelper = [CaptureHelper sharedInstancs];

@implementation CaptureHelper : NSObject

+ (void)initialize{
    if(captureHelper == nil)
        captureHelper = [[CaptureHelper alloc] init];
}

+ (CaptureHelper *)sharedInstancs{
    return captureHelper;
}

- (id)init{
    if(captureHelper != nil){
        return  captureHelper;
    }
    
    self = [super init];
    if(self){
        captureHelper = self;
    }
    
    return self;
}


- (NSString *)getDocumentDirectory {
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    
    return [paths objectAtIndex:0];
}

@end

extern "C"{
    void CaptureToCameraRoll(const char *fileName)
    {
        NSString *file = [NSString stringWithUTF8String:(fileName)];
        NSString *filePath = [[captureHelper getDocumentDirectory] stringByAppendingString:file];
        UIImage *image = [[UIImage alloc] initWithContentsOfFile:filePath];
        
        UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil);
    }
}

위 2개의 파일을 생성후 Plugin/IOS 폴더에 위치 그후

how to used.
[DllImport("__Internal")]
public static extern void CaptureToCameraRoll(String fileName);

// 그이후 스크린 캡처 진행시.

Application.CaptureScreenShot(path);
CaptureToCameraRoll(string.Format("/{0}",path));


이후 카메라롤에서 캡쳐된 이미지를 확인하실수 있다.

2014년 10월 1일 수요일

CSharp_Visual Studio Custom Snippet 적용 시키기. in Visual Studio & Unity

Explain.
Visual Studio를 사용할경우 snippet파일을 직접 만들어서 적용.
Snippet을 적용하여 작업하는 이유는 모든 스크립트의 전체적으로 통일된 디자인을 적용하고 반복되어지는 작업을 최소화 시켜줌.

How to use.
아래처럼 xml형식으로 snippet 파일을 생성후 저장(*.snippet)으로 저장 하여야 한다.
(아래 형식은 제가 사용하는(unity)에서 형식이고 내용은 필요한대로 편집하여 사용하시면됩니다.)
저장경로 : Users\User\Documents\Visual Studio XXXX\Code Snippets\Visual C#\My Code Snippets

그 이후
프로젝트에서 우클릭-> Insert Code Snippet -> My Code Snippet -> 해당 Snippet 선택
해주시면 snippet이 짠 하고 나타나는것을 보실수 있습니다.
ctrl + K + X가 Hotkey이니 애용하시면 될듯합니다. -End.

<?xml version="1.0" encoding="utf-8" ?>
<CodeSnippets  xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
  <CodeSnippet Format="1.0.0">
    <Header>
      <Title>#Classregion</Title>
      <Shortcut>#Classregion</Shortcut>
      <Description>Code snippet for #Classregion</Description>
      <Author>Author Name</Author>
      <SnippetTypes>
        <SnippetType>Expansion</SnippetType>
        <SnippetType>SurroundsWith</SnippetType>
      </SnippetTypes>
    </Header>
    <Snippet>
      <Declarations>
        <Literal>
          <ID>name</ID>
          <ToolTip>Region name</ToolTip>
          <Default>MyRegion</Default>
        </Literal>
      </Declarations>
      <Code Language="csharp">
        <![CDATA[
        #region -------Public Field-------
        $selected$ $end$
        #endregion
    
        #region -------Private Field-------
        $selected$ $end$
        #endregion
    
        #region -------Default Method-------
        $selected$ $end$
        #endregion

        #region -------Public Method-------
        $selected$ $end$
        #endregion
    
        #region -------Private Method-------
        $selected$ $end$
        #endregion
    
        #region -------Property-------
        $selected$ $end$
        #endregion
    ]]>
      </Code>
    </Snippet>
  </CodeSnippet>
</CodeSnippets>

2014년 9월 24일 수요일

Google Play Game Service for Unity In Android_Step2. GPGS Plugin 활용을 위한 Manager Class 생성 & 사용 in Unity


 Explain.
앞서 GPGS활용을위한 셋팅까지 완료한경우
Unity Project에서 GPGS의 기본적은 기능(업적 , 리더보드)를 활용하기위한 Class 생성.
아래 방법은 여러가지 방법중 하나이므로 프로젝트 상황에 맞게끔 구현하여 사용하면 됩니다.


1. Google Game service의 리더보드,업적의 경우 Id값을 기준으로
실행이 이뤄지므로 Developer Console의 업적,리더보드에서
Get Resources를 클릭하여 ID값을 복사

2.위 처럼 텍스트 형식으로 Export하여 복사하여 둡니다.

이후 아래처럼 GPGS Manager Class를 생성한 뒤 Scene에 배치후
이벤트 관련 동작시(게임종료)
업적,리더보드관련 메소드를 호출하여 동작을 확인해 보실수있습니다.
리더보드 , 업적 확인 관련 UI호출은
ShowLeaderBoard, ShowArchievement
메소드를 호출하여 확인가능.
이상으로 GPGS의 기본적인 기능들 활용방법 포스팅 마치겠습니다.


GPGSManager.cs
using UnityEngine;
using System.Collections;
using GooglePlayGames;
using UnityEngine.SocialPlatforms;

public class GPGSManager : MonoBehaviour {

    private static GPGSManager sInstance;

    public static GPGSManager GetInstance
    {
        get
        {
            if(sInstance == null) sInstance = this;

            return sInstance;
        }
    }

    void Start()
    {
        init();
    }

    void init()
    {
        PlayGamesPlatform.DebugLogEnabled = true;

        PlayGamesPlatform.Activate();

        Social.localUser.Authenticate((bool success) =>
        {
            if (success) Debug.Log("Sign in Success");
            else Debug.Log("Sign in Fail");
        });
    }

    public void PostScore(float time)
    {
        Social.ReportScore((long)(time * 1000), IDs.LB, (bool success) => {
            if (success) Debug.Log("Post Success");
            else Debug.Log("Post Fail");
        });
    }

    public void ProgessiveAchievement(AchievementType type, int value)
    {
        if (type == AchievementType.PlayCount)
        {
            //TODO : 업적 상황 달성시 postAchievement(업적ID);
        }

        if(type == AchievementType.PlayTime)
        {
            //TODO : 업적 상황 달성시 postAchievement(업적ID);
        }
    }

    public void ShowLeaderboard()
    {
        Social.ShowLeaderboardUI();
    }

    public void ShowAchievement()
    {
        Social.ShowAchievementsUI();
    }


    private void postAchievement(string key)
    {
        if (PlayerPrefs.GetInt(key) == (int)AchievementState.UNLOCK) return;
        Social.ReportProgress(key, 100, (bool success) => {
            if (success)
            {
                PlayerPrefs.SetInt(key, (int)AchievementState.UNLOCK);
            }
        });
    }
}

// 업적 , 리더보드 ID Class
public class IDs
{
    public const string LB = ""; // 리더보드 ID
    //TODO : 업적 ID 추가.
}

public enum AchievementType
{
    PlayCount,
    PlayTime
}

public enum AchievementState
{
    LOCK,
    UNLOCK
}

2014년 9월 23일 화요일

Google Play Game Service for Unity In Android_Step1. GPGS Plugin 설치후 기본적인 셋팅


Notice.
 - 이전의 포스팅에서의 준비사항이 모두 준비된 상태에서 진행. (준비사항)
 - Developer console사용법등의 기타 방법은 자세하게 정리X


1. GPGS와 연동할 application 등록.
2.Game Service 또한 등록.
3.Game Service 등록시 출력되는 Application ID값 복사.
4.Unity 프로젝트에 Google Play Service Plugin Import후
메뉴바의 Google Play Services -> Android Setup 실행
위에서 복사해둔 ID값 입력 -> Setup 버튼 클릭

5. 이후 다시 Developer Conlose로 돌아와서
Game Service 메뉴의 업적 추가
기본적으로 최소 5개 이상으로 등록
e.g) 점수 xxx 이상 달성
플레이 횟수 x 회 이상 달성 등.

6. 게임에서 사용할 리더보드 또한 등록.
최소 1개 이상.

여기까지 진행했을경우 기본적으로 GPGS Plugin을 활용하여
업적,리더보드 기능을 활용할수 있게끔 셋팅이 완료된 상태이다.
Very Easy..
이후 리더보드,업적 활용법에 대해 정리하겠습니다.

2014년 9월 3일 수요일

Google Play Game Service for Unity In Android_Step0. Index & 준비 사항.



Summary.
 - Anroid & IOS App 작업진행시 각종 게임 관련 기능들을 제공해주는 플랫폼중 
가장 널리 알려진 Google play game service를 Unity에서 활용하는 기본방법 포스팅.
 - GPGS의경우 기본적은 리더보드 , 업적등의 기능 외에도 Multiplay, cloud game save 등의 기능까지 제공해주므로 여러가지 방면으로 유용한 플랫폼.

Requirements.
 - Unity 4.3 이상(only Pro)
 - Android SDK
 - Google Play Services library 4.2.42 이상 (SDK Manager에서 설치 가능)
 - Google Developer 계정(테스트용으로 진행할 App 등록)
 - GPGS Plugin 최신버전 - 다운로드 

Index
 -step 0 : 준비 사항
 -step 1 : GPGS Plugin 설치후 기본적인 요구사항 셋팅
 -step 2 : GPGS Plugin 활용을 위한 Manager Class 생성 후 사용



이번 포스팅은 여기까지 아주 기본적인 기능들면 정리할 생각 추후 멀티플레이 , 클라우드 게임 세이브 등의 기능도 정리 할 예정.

좋은 글_dum spiro spero



dum spiro spero

내가 숨쉬는동안 나는 희망한다.



Android_Intent 형식으로 미디어 파일 재생하기.

Explain.
친숙한 방식인 Intent형식으로
미디어파일을 재생하기.
간단한 방법이므로 따로 설명은 생략.

Code.
public void OpenMediaFile(String path)
{
 Intent intent = new Intent();
 intent.setAction(android.content.Intent.ACTION_VIEW);
 File file = new File(path);
 String extension = path.substring(path.lastIndexOf("."));

 if(extension.equalsIgnoreCase(".avi") || extension.equalsIgnoreCase(".mp4"))
 {
  intent.setDataAndType(Uri.fromFile(file), "video/*"); 
 }
 else
 {
  intent.setDataAndType(Uri.fromFile(file), "audio/*");
 }

 startActivity(intent); 
}

Android_Intent방식을 활용한 Social 기능 구현_Filter 적용하기.

Explain.
앞서, intent기능을 활용한 social기능 구현하는법을 포스팅하였습니다.
이번에는 각 intent마다 필터링을 하여
각 app 마다 맞춰줘야할 템플릿을 맞춰주는것을 포스팅.


Code.
public void Share()
{
 File videoFile = new File("My Video File Path");

 Intent shareIntent = new Intent(Intent.ACTION_SEND);
 
 shareIntent.setType("video/mp4");
 shareIntent.putExtra(Intent.EXTRA_SUBJECT, "My Video");  
 shareIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(videoFile));
 shareIntent.putExtra(Intent.EXTRA_TEXT, "Enjoy the Video");
 

 PackageManager pm = getPackageManager();
 Intent sendIntent = new Intent(Intent.ACTION_SEND);
 sendIntent.setType("text/plain");
 
 Intent openInChooser = Intent.createChooser(shareIntent, "select app to share"); 
// shareIntent를 기본으로한 chooser 생성.
 
 List<ResolveInfo> resInfo = pm.queryIntentActivities(shareIntent, 0); 
// share intent를 지원하는 resolveinfo 를 받아온다.
 List<LabeledIntent> intentList = new ArrayList<LabeledIntent>(); 
// 추가 intent를 담을 공간.
 
 for(ResolveInfo ri : resInfo) 
// 위에서 받아온 resolveinfo 반복(foreach)
 {
  String packageName = ri.activityInfo.packageName; 
  
  if(packageName.contains("android.email")){ 
// 위에서 생성한 intent는 email 용이므로 intent에 email package를 넣어준다.
   shareIntent.setPackage(packageName);
  } else { // 그외의 의경우
   Intent intent = new Intent();
   intent.setComponent(new ComponentName(packageName, ri.activityInfo.name));
   intent.setAction(Intent.ACTION_SEND);
   intent.setType("video/mp4");
      if(packageName.contains("youtube")) // 현재 youtube filter적용 예시.
      {
       Log.d("SHARE", "In Youtube : " + packageName);
       ContentValues content = new ContentValues(4);
       content.put(Video.VideoColumns.TITLE, "My Video Title");
       content.put(Video.VideoColumns.DATE_ADDED,
        System.currentTimeMillis() / 1000);
       content.put(Video.Media.MIME_TYPE, "video/mp4");
       content.put(MediaStore.Video.Media.DATA, videoFile.getAbsolutePath());
       ContentResolver resolver = getBaseContext().getContentResolver();
       Uri uri = resolver.insert(MediaStore.Video.Media.INTERNAL_CONTENT_URI, content); 
       // Extnernal 형식의 path의 경우 EXTERNAL_CONTENT_URI 사용
       
       intent.setType("video/*");
       intent.putExtra(Intent.EXTRA_STREAM, uri);
      }
      else // Youtube가 아닌 경우.
      {
       Log.d("SHARE", "Other : " + packageName);
       intent.putExtra(Intent.EXTRA_SUBJECT, "Video");  
       intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(videoFile));
       intent.putExtra(Intent.EXTRA_TEXT, "Enjoy the Video");
      }

      intentList.add(new LabeledIntent(intent, packageName, ri.loadLabel(pm), ri.icon));
  }

 }

 LabeledIntent[] extraIntents = intentList.toArray( new LabeledIntent[ intentList.size() ]);

 openInChooser.putExtra(Intent.EXTRA_INITIAL_INTENTS, extraIntents);
 startActivity(openInChooser);

}

2014년 9월 2일 화요일

Android_Intent 방식을 활용하여 Share기능 구현.



Explain.

Android Native기능중 Social관련 기능을 아주 쉽고 빠르게 구현할수있는방법.
Api를 활용하는것보다 완성도적인 측면에서는 떨어짐.
예제는 video파일 공유를 예로 든것이며
따로 text, image도 intent설정만 조금 변경해주고 방식은 동일하다.


Flow.
1. 공유할 정보를 담은 Intent 생성.
2.현재 휴대폰에 설치되어있는 App중 이 Intent형식을 지원하는 App 선택창 제공
3.App 선택후 공유진행.

순으로 진행되는것이 일반적이며 이때 App마다 intent설정 방식이
조금 차이가 있는(대부분 동일) app이 존재하므로 filter를 적용해주는것도 하나의방법.
(추후 포스팅 하겠습니다.)


Code.
public void Share(){
  Intent intent = new Intent(Intent.ACTION_SEND);
  
  intent.putExtra(Intent.EXTRA_SUBJECT, "My Video");  
  intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(videoFile));
  intent.putExtra(Intent.EXTRA_TEXT, "Enjoy the Video");
   
  intent.setType("video/mp4");
  
  startActivity(Intent.createChooser(intent, "Choose an Email client" ));
  }

2014년 7월 9일 수요일

Unity C#_Array Refernce & Copy


Explain.
자주있는 일은 아니지만, 종종
System.Array type의 값, 즉 C#에서 배열로 선언한 값들을 사용할때
orgArr[원본]
destArr[복사본]
즉 Array 에 대한 Reference가 아닌 Copy가 필요한경우

무의식중에 destArr = orgArr;
이런식으로 코딩하고 넘어가는경우(즉, 아직 저처럼 실력이 부족한경우겠지만..)
가 발생할수도 있지만 이경우 destArr가 orgArr의 data를 Copy한것이아닌
reference(참조)하고 있는것이므로
orgArr의 데이터값 변화가 생기면 자동으로 destArr의값또한 변화가 생긴다.

이러한특징은 C#의 System.Array type(int [] , string[] , float [] 등등)
으로 선언된 데이터는 모두 reference형식으로 선언되는점때문에 일어나는 현상이다.

이럴때 copy가 필요한경우
System.Array.Copy 메소드를 사용해서 array자체를 복사해주는것으로 해결하면된다.

자세한내용의 MSDN에 잘명시되어있다. 링크


Code.

System.Array.Copy(orgArray, destArray, orgArray.Length); // prams (원본 배열 , 복사본 배열, 원본배열의 길이)

2014년 7월 8일 화요일

Everyplay in Unity for Android_ Everyplay Record를 활용하여 .mp4 생성.


Explain.
Everyplay사용시 share기능에 특화된 플러그인이기 때문에
자체 디바이스에 저장하는것이 불가능 한것으로 보여짐.
하지만, Everyplay를 활용하여 record진행시
.everyplaycache 라는 폴더에
mp4파일을 생성하는것을 확인.
이파일을 활용한 예를 정리.
(.xxxx등으로 .으로 시작하는 폴더의경우 대부분 캐시데이터를 저장하는용도로 사용.
그로인해 일반적인 방법으로는 접근불가.)

Flow.

Every play를 활용하여 record진행
cache폴더내에 .mp4파일 생성
생성된 .mp4파일 원하는 위치로 복제 후 사용.


How to used.
아래의 코드를 native jar plugin으로 만들어서
현재 Unityproject에 통합후
Everyplay stoprecord 호출 뒤
CopyFileFromEveryPlay를 호출해서 사용.

code.
package com.mycompany.test;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.text.SimpleDateFormat;
import java.util.Date;

import android.os.Environment;
import android.util.Log;

public class EveryPlayHelper {

 private static File sdcard = Environment.getExternalStorageDirectory();
 private static String parentDir = "/.EveryplayCache/myPackageName(e.g com.xxx.xxxx)/sessions";
 private static String saveDir = "/myVideo/";

 public static String CopyFileFromEveryPlay() throws IOException {
  File parent = new File(sdcard, parentDir);
  File videoFile = getFile(parent);
  
  FileChannel inputChannel = null;
  FileChannel outputChannel = null;
  

  if(videoFile.exists()){
   String fileName = "myVedio_";
   String currentDateandTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
   fileName += currentDateandTime;
   fileName += ".mp4";
   
   File dest = new File(sdcard, saveDir + fileName);
   
   try {
          inputChannel = new FileInputStream(videoFile).getChannel();
          outputChannel = new FileOutputStream(dest).getChannel();
          outputChannel.transferFrom(inputChannel, 0, inputChannel.size());
  
      } finally {
          inputChannel.close();
          outputChannel.close();
      }
   
   Log.d("EveryPlay", "Copy End");
   
   return saveDir + fileName;
  }
  
  return null;
 }

 private static File getFile(File parentDir) {
  File[] files = parentDir.listFiles();
  File[] inFiles;
  for (File file : files) {
   if (file.isDirectory()) {
    Log.d("EveryPlay", "Found Complete Directory" + file.getName());
    inFiles = file.listFiles();
    for (File mfile : inFiles) {
     if (mfile.getName().endsWith(".mp4")) {
      Log.d("EveryPlay", "Found Complete File(.mp4) file name : " + mfile.getName());
      return mfile;
     }
    }
   }
  }
  return null;
 }
}

Everyplay in Unity with NGUI_Everyplay HUD camera사용시.


삽질ing...

Everyplay를 활용하여 작업시
NGUI를 사용하고있는경우
NGUI의 일부분만 지우고싶어서 그부분에 해당하는 Camera에
Everyplay HUD camera설정을 적용시
NGUI모두 record가 안되는 현상이발생.
이럴경우
record할 UI에만 record용으로 camera를 따로 배치해주어야 
record가 제대로 동작하는것을 확인.

NGUI를 사용시 Everyplay를 함께 사용하면 발생하는 문제로 보이며
Unity자체의 UI system을 사용할때는 발생하지 않는것으로 파악됨.


2014년 6월 17일 화요일

Unity Script_ Debug 전용 Logger 예제


Explain.
C#의 기능중하나인 전처리기를 활용하여 Debug모드시에만 
Log를 출력하게끔 구현해주는 방식.
릴리즈시 전처리기의 해제로 log기능을 끌수있다.
구현방식은 매우 간단.

Logger.cs
#define DEBUG
//#undef DEBUG // realease version

public class Logger {
 public static void Log(string msg)
    {
#if DEBUG
        UnityEngine.Debug.Log(msg);
#endif
    }

    public static void ErrorLog(string msg)
    {
#if DEBUG
        UnityEngine.Debug.LogError(msg);
#endif
    }
}

2014년 5월 30일 금요일

Unity2D_ 2D Object Class


Explain.
Unity 2D Sprite Renderer
+
2D Physics
+
Animator
를 활용하여 오브젝트를 제작시 사용.
AutoDepth, ColorFade 기능구현


using UnityEngine;
using System.Collections;
[RequireComponent(typeof(BoxCollider2D))]
[RequireComponent(typeof(SpriteRenderer))]
[RequireComponent(typeof(Animator))]
[RequireComponent(typeof(Rigidbody2D))]
public class MyObject : MonoBehaviour
{
    private SpriteRenderer mSpriteRenderer;
    private BoxCollider2D mBoxCollider2D;
    private Animator mAnimator;
    private Transform mTransform;

    public void FadeOut()
    {
        StartCoroutine(fadeOutCoroutine());
    }

    public void ONObject()
    {
        _BoxCollider2D.enabled = true;
        _SpriteRenderer.enabled = true;
        _Animator.enabled = true;
    }

    public void OFFOjbect()
    {
        _BoxCollider2D.enabled = false;
        _SpriteRenderer.enabled = false;
        _Animator.enabled = false;
    }

    private IEnumerator fadeOutCoroutine(float duration = 3f)
    {
        float startTime = Time.time;
        while (Time.time - startTime < duration)
        {
            mSpriteRenderer.color = Color.Lerp(Color.white, Color.clear, (Time.time - startTime) / duration);
            yield return true;
        }
        _SpriteRenderer.color = Color.clear;
    }
    public void ColorFade(Color fadeColor)
    {
        StartCoroutine("colorFade", fadeColor);
    }
    public void ColorFade(Color fadeColor, float duration, int rc)
    {
        StartCoroutine(colorFade(fadeColor, duration, rc));
    }

    public void StartAutoDepth()
    {
        StartCoroutine("depthCoroutine");
    }

    public void SetDepth()
    {
        _SpriteRenderer.sortingOrder = (int)(-_LocalPosition.y * 200f);
    }

    public void StopAutoDepth()
    {
        StopCoroutine("depthCoroutine");
    }

    private IEnumerator depthCoroutine()
    {
        yield return new WaitForSeconds(.1f);
        _SpriteRenderer.sortingOrder = (int)(-_LocalPosition.y * 200f);
        StartCoroutine("depthCoroutine");
    }

    private IEnumerator colorFade(Color fadeColor, float duration = 2f, int rc = 3)
    {
        float once = duration / (rc * 2);

        float startTime = Time.time;

        for (int i = 0; i < rc; i++)
        {
            while (Time.time - startTime < once)
            {
                mSpriteRenderer.color = Color.Lerp(Color.white, fadeColor, (Time.time - startTime) / once);
                yield return null;
            }
            mSpriteRenderer.color = fadeColor;
            startTime = Time.time;
            while (Time.time - startTime < once)
            {
                mSpriteRenderer.color = Color.Lerp(fadeColor, Color.white, (Time.time - startTime) / once);
                yield return null;
            }
            mSpriteRenderer.color = Color.white;
            startTime = Time.time;
        }

    }
    public Vector2 _LocalPosition
    {
        get { if (mTransform == null) mTransform = transform; return mTransform.localPosition; }
        set { if (mTransform == null) mTransform = transform; mTransform.localPosition = value; }
    }
    public Vector2 _LocalScale
    {
        get { if (mTransform == null) mTransform = transform; return mTransform.localScale; }
        set { if (mTransform == null) mTransform = transform; mTransform.localScale = value; }
    }

    public Animator _Animator
    {
        get {
            if (mAnimator == null) mAnimator = GetComponent <Animator>();
            return mAnimator; }
        set { mAnimator = value; }
    }

    public SpriteRenderer _SpriteRenderer
    {
        get
        {
            if (mSpriteRenderer == null) mSpriteRenderer = GetComponent<SpriteRenderer>();
            return mSpriteRenderer;
        }
        set { mSpriteRenderer = value; }
    }


    public BoxCollider2D _BoxCollider2D
    {
        get
        {
            if (mBoxCollider2D == null) mBoxCollider2D = GetComponent<BoxCollider2D>();
            return mBoxCollider2D;
        }
        set { mBoxCollider2D = value; }
    }
}

2014년 5월 15일 목요일

Unity_Pannel Fade In&Out With NGUI

Explan.
NGUI이용시
Script상에서 UIPannel 의 alpha scale을 조정하면서
페이드 인,아웃 효과를 주는 스크립트.
Tween Alpha를 활용하여 줄수도 있으나
개인적으로 직접 코딩하여 진행하는것이 더편하기에...
UIPannel뿐만아니라 그외의 컴포넌트라도
Alpha 속성이 구현되어있는 놈이라면
동일하게 이용할수있다.
Timescale값은 원하는값으로 수정하면된다.


FadeInOut.cs

using UnityEngine;
using System.Collections;

public class PannelFadeInOut : MonoBehaviour {

    private UIPanel mPannel;

    private float mFadeTime = 1f;
    private float mWaitTime = 1f;
    private bool on = false;
    void Start()
    {
        mPannel = GetComponent<UIPanel>();
        mPannel.enabled = false;
    }

    public void StartFade()
    {
        if(!on)
            StartCoroutine("fadeCoroutine");
    }

    public void StopFade()
    {
        StopCoroutine("fadeCoroutine");
    }

    IEnumerator fadeCoroutine()
    {
        mPannel.enabled = true;
        float startTime = RealTime.time;
        Time.timeScale = .2f; // slow Time scale
        while(RealTime.time - startTime < mFadeTime) // fade in
        {
            mPannel.alpha = Mathf.Lerp(.1f, .95f, (RealTime.time - startTime) / mFadeTime);
            yield return null;
        }

        startTime = RealTime.time;
        while (RealTime.time - startTime < mWaitTime) // wait
            yield return null;
        
        startTime = RealTime.time;
        while (RealTime.time - startTime < mFadeTime) // fade out
        {
            mPannel.alpha = Mathf.Lerp(.95f, 0, (RealTime.time - startTime) / mFadeTime);
            yield return null;
        }
        Time.timeScale = 1f;
        mPannel.alpha = 0; // default 0
    }
}

2014년 5월 13일 화요일

Unity_NGUI Sprite Auto Depth Script with NGUI

Explanation.
NGUI를 사용하여 게임제작시
자동으로 Depth를 조정해주는 스크립트
depth의 범위를 지정안하고 그냥 
포지션의 y값을 가지고와서 depth로 적용.

Todo.
정해진 depth 범위내에서 적용되도록 변경.

기본

Auto Depth 적용전

Auto Depth 적용후

using UnityEngine;
using System.Collections;

public class AutoDepth : MonoBehaviour {

    private UISprite mSprite;
    private Transform mTransform;
    private int waitFrameCount = 15;
    
 void Start () {
        mSprite = GetComponent();
        mTransform = transform;
        StartCoroutine("depthCoroutine");
 }
 
 IEnumerator depthCoroutine()
    {
        for (int i = 0; i < waitFrameCount ; ++i)
            yield return null;
        mSprite.depth = -(int)mTransform.localPosition.y;
        StartCoroutine("depthCoroutine");
    }

}

Unity_2D Movement(Click to move) with NGUI

Explanation.

NGUI로 게임제작시
UI Root        
└Camera
└Pannel
            └2d Object(Sprite)
의 구조로 게임제작을 진행할때 사용하는 Movement
제목처럼 화면클릭시 목표좌표를 계산(NGUI에 맞게)한뒤
움직이는 방법.(Click to Move)
Movement2D.cs
using UnityEngine;
using System.Collections;

//click to move with NGUI
public class Movement2D : MonoBehaviour {
    private int mUIWidth = 720; // UI Root width size
    private int mUIHeight = 1280; // UI Root height size
    private int mScreenWidth = Screen.width;
    private int mScreenHeight = Screen.height;
    private int mSpeed = 200; // pixel per sec

    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            clickToMove(Input.mousePosition);
        }
    }

    void clickToMove(Vector2 mousePos)
    {
        StopCoroutine("move");
        StartCoroutine("move", calcurateTargetPos(mousePos));
    }
    
    IEnumerator move(Vector2 endPos)
    {
        Vector2 startPos = transform.localPosition;
        float startTime = Time.time;
        float movedTime = Vector2.Distance(startPos, endPos) / mSpeed;
        while (Time.time - startTime < movedTime)
        {
            transform.localPosition = Vector2.Lerp(startPos, endPos, (Time.time - startTime) / movedTime);
            yield return null; // wait frame
        }
        transform.localPosition = endPos; // end Position set
    }

    Vector2 calcurateTargetPos(Vector2 mousePos)
    {
        Vector2 result;

        result.x = Mathf.Lerp(-mUIWidth / 2, mUIWidth / 2, mousePos.x / mScreenWidth);
        result.y = Mathf.Lerp(-mUIHeight / 2, mUIHeight / 2, mousePos.y / mScreenHeight);

        return result;
    }
}

2014년 5월 1일 목요일

Unity_Screen Capture.cs _ Screen Shot


Explanation.
현재 게임 화면 캡쳐기능.
현재 스크린 그대로를 readpixel하는 방식이라
OnGUI로 그려지는 UI는 캡쳐X
스크린 사이즈 그대로 RGB24 포멧으로 저장.

ScreenCapture.cs
using UnityEngine;
using System.Collections;
using System.IO;

public class ScreenCapture : MonoBehaviour
{
    int height = Screen.height;
    int width = Screen.width;

    Texture2D captureTexture;
    float startTime;
    string saveDir = "ScreenShot"; // 저장 폴더 이름.

    bool draw = false;
    void OnGUI()
    {
        if (GUI.Button(new Rect(10, 50, 100, 50), "Capture"))
        {
            StartCoroutine(screenCapture());
        }
    }

    IEnumerator screenCapture()
    {
        yield return new WaitForEndOfFrame();
        captureTexture = new Texture2D(width, height, TextureFormat.RGB24, true); // texture formoat setting
        captureTexture.ReadPixels(new Rect(0, 0, width, height), 0, 0, true); // readPixel
        captureTexture.Apply(); // readPixel data apply

        byte[] bytes = captureTexture.EncodeToPNG();

        File.WriteAllBytes(getCaptureName(), bytes);
        Debug.Log(string.Format("Capture Success: {0}", getCaptureName()));

    }
    string getCaptureName()
    {
        string dirPath = Application.dataPath + "/" + saveDir;
        if (!Directory.Exists(dirPath))
        {
            Directory.CreateDirectory(dirPath);
        }
        return string.Format("{0}/capture_{1}.png",
                             dirPath,
                             System.DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss"));
    }
    
}

Unity_Effect Manager.cs


Explanation.

이전에 프로젝트에서 사용했던 Effect Manager 저장용
Effect 전용 카메라 생성후 
카메라 내부에 Effect Manager Script Add

카메라기준 이펙트 스폰 위치의 local position값을 기준으로
이펙트 생성.


TODO.
Wolrd position값을 기준으로 effect생성?
PlayType별로 effect play


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

public class EffectManager : MonoBehaviour
{
    #region Singleton
    private static EffectManager sInstance;

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

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

    public EffectObject[] _Effects;

    public Dictionary<EffectType, EffectObject&g; Effects = new Dictionary<EffectType, EffectObject>();


    public void Spwan(EffectType type, Vector2 pos)
    {
        Effects[type].Spawn(pos);
    }

    private void Awake()
    {
        Initialize();
    }

    private void Initialize()
    {
        for (int i = 0; i < _Effects.Length; i++)
        {
            _Effects[i].Initialize(this.transform);
            Effects.Add(_Effects[i]._Type, _Effects[i]);
        }
    }
}

[System.Serializable]
public class EffectObject
{
    public GameObject _Effect;
    public EffectType _Type;
    public int _Length; // 한화면에 보여질 최대 이펙트 숫자.
    private List<GameObject> mList = new List<GameObject>();
    private int mIndex = 0;


    public void Spawn(Vector2 pos)
    {
        mList[mIndex].transform.localPosition = pos;
        mList[mIndex].particleSystem.Play();
        mIndex++;
        if (mIndex == _Length) mIndex = 0;
    }

    public void Initialize(Transform trans)
    {
        GameObject parent = new GameObject(_Type.ToString());
        parent.transform.parent = trans;
        for (int i = 0; i < _Length; i++)
        {
            GameObject tmp = MonoBehaviour.Instantiate(_Effect) as GameObject;
            tmp.name = i.ToString();
            tmp.transform.parent = parent.transform;
            tmp.transform.localScale = new Vector2(1, 1);
            mList.Add(tmp);
        }
    }
}

public enum EffectType
{
    Once,
    Loop,
    Move,
    Last
}
(e.g)

Unity3D_Screen Capture And Mosaic


Explanation.
현재 화면을 capture한뒤 mosaic처리.
잠깐 시간이나서 구현.
최적화 X(개선필요)
모자이크 처리방식 매우 비효율적.(개선필요)
어디 쓰일지도 의문.
정리해두고 추후 필요시 사용.


CaptureAndMosaic.cs
using UnityEngine;
using System.Collections;

public class CaptureAndMosaic : MonoBehaviour
{
    int height = Screen.height;
    int width = Screen.width;

    Texture2D captureTexture;
    float startTime;

    bool draw = false;
    void Start()
    {
        startTime = Time.realtimeSinceStartup;
        StartCoroutine(getScreenAndMosaic());
    }
    void OnGUI()
    {
        if (captureTexture != null)
            GUI.DrawTexture(new Rect(0, 0, captureTexture.width, captureTexture.height), captureTexture);

        if (!string.IsNullOrEmpty(log))
            GUI.Label(new Rect(100, 100, 100, 100), log);
    }
    bool end = false;
    IEnumerator getScreenAndMosaic() 
    {
        yield return new WaitForEndOfFrame();
        captureTexture = new Texture2D(width, height, TextureFormat.RGB24, true);
        captureTexture.ReadPixels(new Rect(0, 0, width, height), 0, 0, true);
        captureTexture.Apply();
        StartCoroutine(makeNoise());
    }

    IEnumerator makeNoise()
    {
        yield return StartCoroutine(makeNoiseTexture(40));
    }

    int index = 0;
    IEnumerator makeNoiseTexture(int dist)
    {
        int distDiv = dist;
        int nBoxSize = width / distDiv;
        int x = 0;
        int y = 1;
        Color[] colors = new Color[nBoxSize * nBoxSize];
        Color preColor = Color.white;
        int mid = height / (nBoxSize * y);
        while (nBoxSize * y < height)
        {
            preColor = captureTexture.GetPixel(0 + x * nBoxSize, height - nBoxSize * y);

            for (int i = 0; i < colors.Length; ++i)
                colors[i] = captureTexture.GetPixel(0 + x * nBoxSize, height - nBoxSize * y);
            if (height - nBoxSize * y <= 0)
                captureTexture.SetPixels(0 + x * nBoxSize, 0, nBoxSize, nBoxSize, colors);
            else
                captureTexture.SetPixels(0 + x * nBoxSize, height - nBoxSize * y, nBoxSize, nBoxSize, colors);

            ++x;

            if (x * nBoxSize > width - nBoxSize)
            {
                x = 0;
                ++y;
                if (y % 3 == 0) // 3행 처리후 apply
                {
                    captureTexture.Apply();
                    yield return null;
                }
            }
        }
        yield return null;
        captureTexture.Apply(); 

        log = "mosaic ended Time : " + (Time.realtimeSinceStartup - startTime);

        Debug.Log("end");
    }
    string log = string.Empty;

}
Result.

Before

After

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안에서 사용할수있습니다! 

2014년 1월 27일 월요일

Facebook In Unity_Step2. Unity에서 Facebook SDK import후 테스트 하기.

1. Unity 용 Facebook SDK를 다운로드한다[저는 4.3.3버전을 사용]


2. Facebook SDK import

3. 이전에 생성해둔 facebook App 대쉬보드에서
앱 ID를 복사해 둔다. Ctrl + C

4. Facebook -> Edit Setting

5. App Name에 사용할 이름을 적고
App ID에 위에 복사해둔 ID 입력 Ctrl + V

6. 그이후 Android build Facebook Setting 메뉴가 제대로 나오는지 확인
  • 이때 Openssl 미 설치시 KeyHash가 출력되지 않는다.
  • Openssl 설치 후 시스템 환경변수에 설정을 해주셔야 출력이 됩니다.


7. 그후 Facebook app Page로 돌아가서
설정 -> 플랫폼 추가 -> Android 추가를 해주시면

8. 이처럼 Android tab이 나타나게 되는데
여기에 위에서(6) 나왔던 내용들을 적어주시면 됩니다.

9. 그이후 SDK내의 InteractiveConsole을 메인으로 Build하여 실행

10. 위그림처럼 FB.Init클릭시 Init이 비활성화되고 아래의 FB Method Call관련 버튼들이
활성화 되면 기본적인 연동이 성공한것이다.

10. 시험삼아 CallAPI 창의 API에 me를 입력하고
CallAPI를 클릭하여 나의 페이스북 정보가(Json형태) 받아와지는지 확인
(아래의 흰색 TextArea에 출력됩니다.)

End.
위 그림에서는 현재 UserID, Access Token등이 나와있지않지만[컴퓨터로 테스팅]
핸드폰으로 빌드하여 테스트해보면 UserID, Access Token이 제대로 출력되어야 
정상적으로 작동하는것이다.

Next.. 페이스북 내정보 받아와서 Unity에서 파싱하여 사용하기.






Facebook In Unity_Step1. Facebook SDK를 활용하기위한 Facebook App만들기


1. 페이스북에 로그인 후 개발자 페이지접속 



2. 상단의 메뉴에서 앱 -> Create New App 클릭




3. App 설정후 어플리케이션 만들기 실행
  • Display Name - 만들 App이름
  • Namespace - App이 사용할 NameSpace(선택사항)

3. 그후 생성된 App DashBoard 확인.
 
End. 
여기까지하시면 Facebook SDK를 활용하기위한 facebook app을 등록을 하셨다고
보시면됩니다. 다음 Step에서 이후 기본적인 연동법을 정리 해보겠습니다.

2014년 1월 21일 화요일

Facebook In Unity_Index. Facebook SDK for unity Plugin in Android


1. 포스팅 환경

  - Unity 4.2.1 Pro Version

  • Unity 버전은 상관없으나 Facebook plugin 자체가 Android native(jar파일) 형식으로 되어있기때문에 Pro version Only 라고 생각하시면 됩니다.

  - Facebook SDK for Unity 4.3.3 pakage

  • Facebook SDK 4.3.4 버전 부터는 Unity 4.3 이상을 요구하니 주의 하시길 바랍니다.
  • 4.2.1 버전 이후로 사용하시기를 적극 추천드립니다. 3.x 버전의 SDK(초기)도 사용해봤었는데 FB callback 메소드 delegate 타입 자체가 void() 형식이라 event 처리하는게 많이 많이 까다로웠던것으로 기억납니다. 4.x이후에는 void(FBresult) 형식으로 봐껴서 콜백시 파싱하시가 훨씬 수월하더군요.(3.x 버전의 경우에는 잠깐 테스트 씬만 실행해보고 소스좀 살펴본게 전부라 정확하게 기억나지는 않는다는 함정...)... 아무튼 최신버전을 애용합니다 ^^
  • 참조 : https://developers.facebook.com/docs/unity/downloads
  
 - Simple Json Library

  • Json 파서중 하나로 이번에 포스팅을 진행시 사용하게될 라이브러리 기존의 Facebook SDK를 설치하면 MiniJson이 포함되어있고 JsonFX, LitJson등 수많은 Json 파서들이 존재하나 개인적인 생각으로 게임을위한 Facebook Json 파서로써는 SimpleJson만큼 작고 간편한놈이 없는듯하여 선택하였음. (Json 파서 별로 특징들이 뚜렷하게 존재하니 조금씩 살펴보시는것도 큰도움이 될듯 합니다.)
  • 참초 : http://wiki.unity3d.com/index.php/SimpleJSON

2. Index
  • SDK 활용전 기본적으로 꼭 해야될 Facebook 개발자 셋팅
  • Facebook 내정보 받아오기(이름 , ID 등)
  • Facebook Score API를 활용하여 리더보드 구현하기
  • Facebook 친구초대 구현하기
  • Facebook 내 타임라인에 Feed 올리기
  • 기타 등등++