iOS」タグアーカイブ

[Unity]:MonoBehaviour.OnApplicationPause()の挙動

[検証バージョン]
Unity 5.1.2p2
iOSシミュレータ 8.4
Android Genymotion 4.2.2 API 17

アプリがバックグラウンドに入った時・および復帰した時の処理を記述するMonoBehaviour.OnApplicationPause()

この中に記述する処理が重く時間がかかり途中でアプリが強制終了された場合、どのような挙動になるのか検証してみた。

・検証方法
OnApplicationPauseでバックグラウンド退避時の処理中に数秒待機してデバッグプリントを行う。

using UnityEngine;

public class GameController : MonoBehaviour {
	void OnApplicationPause(bool isPause) {
		if (isPause) {
			Debug.Log("OnApplicationPause()");
			System.Threading.Thread.Sleep(3000);
			Debug.Log("3 seconds lator..");
		}
	}
}

・iOSの場合
アプリ起動中にホームボタン2度押し→アプリを終了

-> applicationWillResignActive()
OnApplicationPause()
3 seconds lator..
-> applicationDidEnterBackground()
-> applicationWillTerminate()

ちゃんと3秒後にデバッグプリントされた後に、バックグラウンド→終了となっている。

ただし、OnApplicationPauseをコルーチンにした場合はバックグラウンドに入った時点で処理が一時停止してしまうので注意が必要。

・Androidの場合
ハードウェアボタンでアプリの一覧を表示→アプリを終了

OnApplicationPause()のデバッグプリントのみで、処理は途中で中断されてしまった?

Androidの場合は、OnApplicationPause()での処理には注意が必要みたい。

ゆるあぷ!様に「あ〜ちゃ〜の大冒険」をレビューして頂きました!

Androidゲームアプリのレビューサイト「ゆるあぷ!」(http://android.yuruapp.com)様に、「あ〜ちゃ〜の大冒険」を取り上げて頂きました!

ゆるふわ天使「あ〜ちゃ〜の大冒険」をあそんでみた

プレイ感などをかなり詳しく、ゆるふわな素敵な感じでご紹介いただきまして、とても嬉しいです!
カメラの移動に関しては他のユーザ様からも要望がありまして、どのような仕様にするか検討中であります。
今後のアップデートで対応できればと考えております。

今後ともどうぞよろしくお願い致します!

[Unity]:iOSシミュレータでネイティブプラグインを動かす

UnityからiOSビルドをする時に、「PlayerSettings」-「Other Settings」-「Optimization」-「SDK Version」をデフォルトの「Device SDK」から「Simulator SDK」に変更する事により、iOS Simulator用のXcodeプロジェクトを吐き出し、iOS Simulator上で動作を確認することができます。

ただUnityのiOS Simulator向けビルドはネイティブプラグインの呼び出しをサポートしておらず、ネイティブプラグイン呼び出し時に以下ようなのエラーを吐いて、動作しません。

EntryPointNotFoundException: initAdBanner_
  at (wrapper managed-to-native) Binding:initAdBanner_ (string)
  at Binding.initAdBanner () [0x00000] in <filename unknown>:0

このエラーを回避し、ネイティブプラグインをシミュレータ上で動作させる方法がこちらのサイトで紹介されていました。

「Jason Lu Game Programmer –
Unity4: Test Unity iOS plug-in on iOS simulator」

画像付きですし分かり易いと思いますが、日本語で分かり易く紹介しておきます。
Unityのバージョンは4.5.5f1で試しています。

変更はUnityからiOS Simulatorビルドで吐き出されたXcodeプロジェクトの以下のソース中の2カ所に対して行います。

 Librariesフォルダ – RegisterMonoModules.cpp

1.mono_dl_register_symbolの宣言を#ifディレクティブの外に出します。

extern "C"
{
	typedef void* gpointer;
	typedef int gboolean;
#if !(TARGET_IPHONE_SIMULATOR)
	const char*			UnityIPhoneRuntimeVersion = "4.5.5f1";
	void				mono_dl_register_symbol (const char* name, void *addr);
	extern int 			mono_ficall_flag;
	void				mono_aot_register_module(gpointer *aot_info);
	extern gboolean		mono_aot_only;
	extern gpointer*	mono_aot_module_Assembly_CSharp_firstpass_info; // Assembly-CSharp-firstpass.dll
	extern gpointer*	mono_aot_module_Assembly_CSharp_info; // Assembly-CSharp.dll
	extern gpointer*	mono_aot_module_Assembly_UnityScript_info; // Assembly-UnityScript.dll
	extern gpointer*	mono_aot_module_Boo_Lang_info; // Boo.Lang.dll
	extern gpointer*	mono_aot_module_Mono_Security_info; // Mono.Security.dll
	extern gpointer*	mono_aot_module_PlayMaker_info; // PlayMaker.dll
	extern gpointer*	mono_aot_module_System_Core_info; // System.Core.dll
	extern gpointer*	mono_aot_module_System_info; // System.dll
	extern gpointer*	mono_aot_module_UnityEngine_info; // UnityEngine.dll
	extern gpointer*	mono_aot_module_UnityScript_Lang_info; // UnityScript.Lang.dll
	extern gpointer*	mono_aot_module_mscorlib_info; // mscorlib.dll
#endif // !(TARGET_IPHONE_SIMULATOR)
	void	initAdBanner_();
	void	moveOffBanners_();
	void	share_();
	void	shareWithImage_();
	void	showAppStore_();
	void	UnityNSObject_RetainObject();
	void	UnityNSObject_ReleaseObject();
	void	UnityNSError_Code();
	void	UnityNSError_Description();
	void	UnityNSError_Reason();
	void	UnityNSNotification_Name();
}
extern "C"
{
	typedef void* gpointer;
	typedef int gboolean;
#if !(TARGET_IPHONE_SIMULATOR)
	const char*			UnityIPhoneRuntimeVersion = "4.5.5f1";
	extern int 			mono_ficall_flag;
	void				mono_aot_register_module(gpointer *aot_info);
	extern gboolean		mono_aot_only;
	extern gpointer*	mono_aot_module_Assembly_CSharp_firstpass_info; // Assembly-CSharp-firstpass.dll
	extern gpointer*	mono_aot_module_Assembly_CSharp_info; // Assembly-CSharp.dll
	extern gpointer*	mono_aot_module_Assembly_UnityScript_info; // Assembly-UnityScript.dll
	extern gpointer*	mono_aot_module_Boo_Lang_info; // Boo.Lang.dll
	extern gpointer*	mono_aot_module_Mono_Security_info; // Mono.Security.dll
	extern gpointer*	mono_aot_module_PlayMaker_info; // PlayMaker.dll
	extern gpointer*	mono_aot_module_System_Core_info; // System.Core.dll
	extern gpointer*	mono_aot_module_System_info; // System.dll
	extern gpointer*	mono_aot_module_UnityEngine_info; // UnityEngine.dll
	extern gpointer*	mono_aot_module_UnityScript_Lang_info; // UnityScript.Lang.dll
	extern gpointer*	mono_aot_module_mscorlib_info; // mscorlib.dll
#endif // !(TARGET_IPHONE_SIMULATOR)
	void	mono_dl_register_symbol (const char* name, void *addr);
	void	initAdBanner_();
	void	moveOffBanners_();
	void	share_();
	void	shareWithImage_();
	void	showAppStore_();
	void	UnityNSObject_RetainObject();
	void	UnityNSObject_ReleaseObject();
	void	UnityNSError_Code();
	void	UnityNSError_Description();
	void	UnityNSError_Reason();
	void	UnityNSNotification_Name();
}

2.RegisterMonoModules()中のネイティブ呼び出しするメソッドの定義を#ifディレクティブから外す

void RegisterMonoModules()
{
    gEnableGyroscope = false;
#if !(TARGET_IPHONE_SIMULATOR)
	mono_aot_only = true;
	mono_ficall_flag = true;
	mono_aot_register_module(mono_aot_module_Assembly_CSharp_firstpass_info);
	mono_aot_register_module(mono_aot_module_Assembly_CSharp_info);
	mono_aot_register_module(mono_aot_module_Assembly_UnityScript_info);
	mono_aot_register_module(mono_aot_module_Boo_Lang_info);
	mono_aot_register_module(mono_aot_module_Mono_Security_info);
	mono_aot_register_module(mono_aot_module_PlayMaker_info);
	mono_aot_register_module(mono_aot_module_System_Core_info);
	mono_aot_register_module(mono_aot_module_System_info);
	mono_aot_register_module(mono_aot_module_UnityEngine_info);
	mono_aot_register_module(mono_aot_module_UnityScript_Lang_info);
	mono_aot_register_module(mono_aot_module_mscorlib_info);

	mono_dl_register_symbol("initAdBanner_", (void*)&initAdBanner_);
	mono_dl_register_symbol("moveOffBanners_", (void*)&moveOffBanners_);
	mono_dl_register_symbol("share_", (void*)&share_);
	mono_dl_register_symbol("shareWithImage_", (void*)&shareWithImage_);
	mono_dl_register_symbol("showAppStore_", (void*)&showAppStore_);
	mono_dl_register_symbol("UnityNSObject_RetainObject", (void*)&UnityNSObject_RetainObject);
	mono_dl_register_symbol("UnityNSObject_ReleaseObject", (void*)&UnityNSObject_ReleaseObject);
	mono_dl_register_symbol("UnityNSError_Code", (void*)&UnityNSError_Code);
	mono_dl_register_symbol("UnityNSError_Description", (void*)&UnityNSError_Description);
	mono_dl_register_symbol("UnityNSError_Reason", (void*)&UnityNSError_Reason);
	mono_dl_register_symbol("UnityNSNotification_Name", (void*)&UnityNSNotification_Name);
#endif // !(TARGET_IPHONE_SIMULATOR)
}
void RegisterMonoModules()
{
    gEnableGyroscope = false;
#if !(TARGET_IPHONE_SIMULATOR)
	mono_aot_only = true;
	mono_ficall_flag = true;
	mono_aot_register_module(mono_aot_module_Assembly_CSharp_firstpass_info);
	mono_aot_register_module(mono_aot_module_Assembly_CSharp_info);
	mono_aot_register_module(mono_aot_module_Assembly_UnityScript_info);
	mono_aot_register_module(mono_aot_module_Boo_Lang_info);
	mono_aot_register_module(mono_aot_module_Mono_Security_info);
	mono_aot_register_module(mono_aot_module_PlayMaker_info);
	mono_aot_register_module(mono_aot_module_System_Core_info);
	mono_aot_register_module(mono_aot_module_System_info);
	mono_aot_register_module(mono_aot_module_UnityEngine_info);
	mono_aot_register_module(mono_aot_module_UnityScript_Lang_info);
	mono_aot_register_module(mono_aot_module_mscorlib_info);
#endif // !(TARGET_IPHONE_SIMULATOR)

	mono_dl_register_symbol("initAdBanner_", (void*)&initAdBanner_);
	mono_dl_register_symbol("moveOffBanners_", (void*)&moveOffBanners_);
	mono_dl_register_symbol("share_", (void*)&share_);
	mono_dl_register_symbol("shareWithImage_", (void*)&shareWithImage_);
	mono_dl_register_symbol("showAppStore_", (void*)&showAppStore_);
	mono_dl_register_symbol("UnityNSObject_RetainObject", (void*)&UnityNSObject_RetainObject);
	mono_dl_register_symbol("UnityNSObject_ReleaseObject", (void*)&UnityNSObject_ReleaseObject);
	mono_dl_register_symbol("UnityNSError_Code", (void*)&UnityNSError_Code);
	mono_dl_register_symbol("UnityNSError_Description", (void*)&UnityNSError_Description);
	mono_dl_register_symbol("UnityNSError_Reason", (void*)&UnityNSError_Reason);
	mono_dl_register_symbol("UnityNSNotification_Name", (void*)&UnityNSNotification_Name);
}

以上の2カ所を修正することにより、ネイティブプラグインがiOSシミュレータ上でも動作するようになります。

にゃんこといっしょ♪

にゃんこといっしょ♪
にゃんこと仲良くなって鳴き声を集めよう!

にゃんこといっしょ♪#1
1.にゃんこにタッチしてみよう!にゃおんがもらえるよ!
にゃんこをタッチした時にもらえるにゃおんは、アイテムを購入・強化すると増えるよ!

にゃんこといっしょ♪#2
2.アイテムを購入してみよう!
自動的ににゃおんが貯まるようになるよ!
アイテムは買う度に値段が高くなっていくよ!

にゃんこといっしょ♪#3
3.アイテムをレベルアップしよう!
もっとにゃおんが貯まるようになるよ!

にゃんこといっしょ♪#4
4.たまに落ちてくるボールを取るとにゃおん大量ゲットのチャンス!
ボールの効果はアイテムをたくさん持っていると、より強力になるよ!

にゃんこといっしょ♪#5
5.新しいアイテムが買いにくくなってきたら、一度「メニュー」ボタンから「リセット」をしてみよう!
リセットをするとリセットボーナスがもらえるよ!

Moku

Moku
にゃんこといっしょ
☆☆☆☆☆
0件の評価
App Store

moku Apps

moku Apps
にゃんこといっしょ♪

[iPhone&iPad]:まちがい探し3D アップデート

たくさんのレビューありがとうございます!
ご要望にお応えしまして、新たに2つのステージを追加して
まちがい探し3D アップデートver.1.1.0を配信開始しました。

4ステージ×5パターン=計20パターンのまちがい探しを3Dでお楽しみいただけます!

Screenshot 2013.12.18 07.56.46 Screenshot 2013.12.18 07.57.09 Screenshot 2013.12.18 07.57.40

今後も引き続き、応援をよろしくお願い致します!

Moku

Moku
まちがい探し3D
★★★★☆
28件の評価
App Store

Unity NEWSにKick Far Away!!が掲載されました!

ユニティ・テクノロジー・ジャパン様のホームページで、Unity3D使用タイトルを紹介しているUnity NEWSのコーナーにKick Far Away!!を掲載して戴きました。

Android / iOS 向け Unity タイトル『Kick Far Away!!』配信開始

Moku

Moku
Kick Far Away!!
☆☆☆☆☆
0件の評価
App Store

moku Apps

moku Apps
Kick Far Away!!

[Unity]:ドラッグ&ピンチイン・ピンチアウトの入力コントロール

先日リリースした「まちがい探し3D」では、少し特殊な入力インターフェースを利用しています。

Moku

Moku
まちがい探し3D
★★★★☆
28件の評価
App Store

  • ドラッグで視点の移動
  • ピンチイン・ピンチアウトで視点の縮小・拡大
  • タップでオブジェクトの選択

これらの入力をコントロールするためのスクリプトを公開します。

using UnityEngine;
using System.Collections;

public class CameraController : MonoBehaviour {
	const float ROTATION_SPEED =  5.0f;
	const float ZOOM_SPEED = 200.0f;
	
	private Vector3 original;
	private float angle_x = 0.0f;
	private float angle_y = 0.0f;
	
	private bool isDragging = false;
	private bool isDragged = false;
	private bool isPinched = false;
	private float interval = 0.0f;

	// Use this for initialization
	void Start () {
		Vector3 angles = transform.eulerAngles;
		angle_x = angles.y;
		angle_y = angles.x;
		original = transform.position;
	}
	
	void OnGUI() {
		if (Input.touchCount == 1 && !isPinched) {
			if (Event.current.type == EventType.MouseDrag) {
				isDragging = true;
				isDragged = true;
			} else {
				isDragging = false;
				if (Event.current.type == EventType.MouseUp) {
					if (!isDragged) Tap(Input.mousePosition);
					else isDragged = false;
				}
			}
		} else if (Input.touchCount == 0) {
			isPinched = false;
		}
	}
	
	void Update () {
		if (Input.touchCount == 2) {
			if (Input.touches[0].phase == TouchPhase.Began || Input.touches[1].phase == TouchPhase.Began) {
				interval = Vector2.Distance(Input.touches[0].position, Input.touches[1].position);
			}
			float tmpInterval = Vector2.Distance(Input.touches[0].position, Input.touches[1].position);
			original.z += (tmpInterval - interval) / ZOOM_SPEED;
			if (original.z > 0) original.z = 0;
			interval = tmpInterval;

			transform.position = transform.rotation * original;
			isPinched = true;
		} else if (isDragging) {
			angle_x += Input.GetAxis("Mouse X") * ROTATION_SPEED;
	        angle_y -= Input.GetAxis("Mouse Y") * ROTATION_SPEED;
	        
	        transform.rotation = Quaternion.Euler(angle_y, angle_x, 0);
	        transform.position = transform.rotation * original;
		}
	}
	
	private void Tap(Vector3 point) {
		// タップ時の処理を記述
	}
}

若干、難解なコードになってしまっていますが、元々ドラッグのみを実現するスクリプトにタップとピンチイン・ピンチアウトを無理くり付け足したため、このような状態になってしまいました。
OnGUI()を使わずにUpdate()でまとめて書き直した方がスッキリするでしょう。

画面にタッチされた時に触れた点が2カ所ならピンチイン・アウトの開始として、その2点間の距離をintervalに保持しておくと同時にisPinchedフラグを立てておきます。
このisPinchedフラグは、指が離れる時に指1本だけが残り不正なドラッグ・タップが起こらないよう判定するためのもので、タッチが検出されなくなると寝かせるようにしています。
ピンチイン・アウト中は前回のintervalと比較計算しズームイン・アウトの処理を行います。

触れた点が1カ所の場合、Event.current.type == EventType.MouseDragによりドラッグ中であると判定されたらisDraggingとisDraggedフラグを立てます。
isDraggingフラグが立っている場合には回転の処理を行います。
originalに初期位置、angle_x, angle_yに初期位置での向きを保持しており、ドラッグによりangle_*を変更し、その角度に基づき位置を計算します。
原点を中心に回転し、常に原点の方を向く事になります。

指が離れた場合にはisDraggedフラグが寝ていればタップの処理を行います。
つまりタップに関しては指が離れた時点での処理ということになりますが、ドラッグも実現するために選択の余地はありませんでした。

[iOS]:アプリから画像付きのツイートをする

iOSアプリから、ツイッター・Facebookなど各種SNSやメール・メッセージなどで、画像付きのシェアをする方法です。

SLComposeViewControllerを使う場合はTwitter・Facebookなどの限られたSNSにしかポストできず、SNSを指定する方法も自前で実装する必要があります。

一方、UIActivityViewControllerを使うと、利用出来るSNS・ツールなどの選択画面が自動的に用意されます。

UIActivityViewControllerを使用した場合

UIActivityViewControllerを使用した場合

いずれの方法もiOS6.0以降で、Social.frameworkが必要になります。

#import <Social/Social.h>

@interface SNSController : NSObject <UIActionSheetDelegate>

-(void) share:(UIViewController *)viewController : (NSString *)_txt : (NSString *)_hashTag : (NSString *)_url : (NSString *)_path;
@end

@implementation SNSController {
    UIViewController *viewController;
    NSString *txt, *hashTag;
    NSURL *url;
    UIImage *image;
}

-(void) share:(UIViewController *)_viewController : (NSString *)_txt : (NSString *)_hashTag : (NSString *)_url : (NSString *)_path {
    viewController = _viewController;
    txt = _txt;
    hashTag = _hashTag;
    url = [NSURL URLWithString:_url];
    if (_path) image = [UIImage imageWithContentsOfFile:_path];
    
    UIActionSheet *actionServiceType = [[UIActionSheet alloc] initWithTitle:@"Share result on ..." delegate:self cancelButtonTitle:@"Cancel" destructiveButtonTitle:nil otherButtonTitles:@"Twitter", @"Facebook", nil];
    [actionServiceType showInView:viewController.view];
}

#pragma mark - UIActionSheetDelegate
- (void)actionSheet:(UIActionSheet *)actionSheet didDismissWithButtonIndex:(NSInteger)buttonIndex {
    NSString *serviceType = nil;
    if (buttonIndex == 0) serviceType = SLServiceTypeTwitter;
    else if (buttonIndex == 1) serviceType = SLServiceTypeFacebook;
    
    if (serviceType) [self compose:serviceType];
}

- (void)compose:(NSString *)serviceType {
    NSString *str;
    if (serviceType == SLServiceTypeTwitter) str = [txt stringByAppendingFormat:@" %@", hashTag];
    else if (serviceType == SLServiceTypeFacebook) str = txt;
    else str = txt;

    SLComposeViewController *controller = [SLComposeViewController composeViewControllerForServiceType:serviceType];
    if (image) [controller addImage:image];
    [controller addURL:url];
    [controller setInitialText:str];
    [viewController presentViewController:controller animated:YES completion:nil];
}
@end
#import <Social/Social.h>

@interface SNSController : NSObject <UIActivityItemSource>

-(void) share:(UIViewController *)viewController : (NSString *)_txt : (NSString *)_hashTag : (NSString *)_url : (NSString *)_path;
@end

@implementation SNSController {
    NSString *txt, *hashTag;
}

-(void) share:(UIViewController *)viewController : (NSString *)_txt : (NSString *)_hashTag : (NSString *)_url : (NSString *)_path {
    txt = _txt;
    hashTag = _hashTag;
    NSURL *url = [NSURL URLWithString:_url];

    NSArray *array;
    float version = [[[UIDevice currentDevice] systemVersion] floatValue];
    if (version < 7.0) {
        array = [NSArray arrayWithObjects:self, url, nil];
    } else {
        txt = [txt stringByAppendingFormat:@" %@", url];
        array = [NSArray arrayWithObjects:self, nil];
    }

    if (_path) {
        UIImage *image = [UIImage imageWithContentsOfFile:_path];
        if (image) array = [NSArray arrayWithObjects:self, image, nil];
    }
    
    UIActivityViewController *avc = [[UIActivityViewController alloc] initWithActivityItems:array applicationActivities:nil];
    [viewController presentViewController:avc animated:YES completion:nil];
}

#pragma mark - UIActivityItemSource
-(id) activityViewController:(UIActivityViewController *)activityViewController itemForActivityType:(NSString *)activityType {
    if ([activityType isEqualToString:UIActivityTypePostToTwitter]) {
        return [NSString stringWithFormat:@"%@ %@", txt, hashTag];
    }
    return txt;
}

-(id) activityViewControllerPlaceholderItem:(UIActivityViewController *)activityViewController {
    return txt;
}
@end

iOS6.x台とiOS7.0以降で分岐させているのは、iOS7.0ではUIActivityViewControllerにNSURLを放り込むと、実行時にブラウザに飛んでしまうようになったためです。

Kick Far Away!!

タップ操作だけでボールを遠くに飛ばすゲームです。
プレイする度にキック力が強くなり、遠くにボールを飛ばせるようになります。

サッカーボールを遠くに蹴飛ばそう!
ゲームセンター対応で、世界中のライバル達と飛距離を競えるぞ!

遠くへ飛ばすコツ
1.真ん中を蹴る
端の方を蹴るとボールにしっかり力が伝わらないぞ!

2.早く蹴る
助走が終わってボールがズームアップになったらすぐに蹴ることができる。
助走の流れで蹴った方が飛距離が伸びるぞ!

3.繰り返しプレイ
プレイする度にキック力が増加して、遠くにボールを蹴れるようになるぞ!

Moku

Moku
Kick Far Away!!
☆☆☆☆☆
0件の評価
App Store

moku Apps

moku Apps
Kick Far Away!!

 
1 2 3 4 5