アプリ開発」タグアーカイブ

[Unity]:Detonator Explosion Frameworkで爆風の影響範囲を限定する

Unityで爆風を表現するのに以下の無料アセットが使える。

Detonator Explosion Framework

炎や煙などの見た目だけでなく、爆風によって周りのオブジェクトを吹き飛ばすことが出来るのだが、爆風の範囲内にあるRigidbodyを持つオブジェクトが全て吹き飛ばされてしまうので、レイヤーの指定により爆風の影響を受けるオブジェクトを限定できるように改造する。

使用バージョン:
Unity 4.5.1f3
Detonator Explosion Framework 1.04

	//tweak the position such that the explosion center is related to the explosion's direction
	_explosionPosition = transform.position; //- Vector3.Normalize(MyDetonator().direction);
//	_colliders = Physics.OverlapSphere (_explosionPosition, radius);
	_colliders = Physics.OverlapSphere (_explosionPosition, radius, -1 - (1 << 4 | 1 << 8));

55行目:Physics.OverlapSphereの第3引数のレイヤーマスクを指定する。
レイヤーマスクの指定は若干複雑だが、

-1 : Everything
0〜 : Tags & Layersで指定した順番

をビット演算して指定する。
上のサンプルコードではEverythingから4番と8番のレイヤーを除外している。

実行速度を考慮するならば、Awake()などであらかじめ計算した値をメンバ変数に保持しておくと良い。

実行結果:左側の球がレイヤー4、右側の球がレイヤー8、キューブはデフォルトレイヤー
Detonator Explosion Framework

[Mac]:アプリの素材づくりに超便利なお絵描きソフト

アプリを開発していると、アプリの中で用いる素材やリリース用のイラストなどたくさんの画像を用意しないといけません。

mokuAppsでは、用途によって主に2種類のグラフィックソフトを使い分けています。

Pixelmator Team
ジャンル:Graphics/Design
リリース日:2011-01-05
価格:3000円
posted with iTunesLinker at 14.01.09

レイヤーや各種エフェクト、パスを使ったベクター画像の作成など、かなりのことが一通り出来るようになってます。AdobeのPhotoshopなどを使ったことがない私には高機能すぎて全ての機能は使い切れてませんが、この後に紹介するArtText2で出来ない部分だけに利用している感じです。

 

Belight Software, ltd
ジャンル:Graphics/Design
リリース日:2010-12-23
価格:2000円
posted with iTunesLinker at 14.01.09

これが超絶便利でとても重宝しています!!

ArtText2メイン

ArtText2 – 作業画面

本来はタイトル通りロゴなどを作成するのに特化したソフトだと思いますが、非常に扱い易く素材の作成にすごく役立っています。

ArtText2 - テンプレート選択

ArtText2 – テンプレート選択

アイコンやロゴなど豊富なテンプレートが用意されていて、文字を差し替えるだけで見栄えの良いものが作れます。また、複数のテンプレートから引っ張ってきたり、分解して切り貼りしたりすることも出来ます。

オブジェクトを組合せて行く感じで、画像を編集したりすることは出来ないので、その場合には上記のPixelMatorを使用しています。

逆に言えばそれ以外は全てArtText2で事足りています。

ArtText2 - レイヤー

ArtText2 – レイヤー

オブジェクト単位で編集していくのが、似通った素材を複数作成する場合に非常に都合が良いのです。

ArtText2 - アイコン・シェイプ

ArtText2 – アイコン・シェイプ

アイコンやシェイプなどのベクター画像のサンプルも豊富に用意されています。

ArtText2 - シェイプ編集

ArtText2 – シェイプ編集

そして、シェイプの編集などは内部で出来たりします。

スクリーンショット 2014-01-09 21.40.46 スクリーンショット 2014-01-09 21.41.19 スクリーンショット 2014-01-09 21.41.38 スクリーンショット 2014-01-09 21.55.21

ベクター画像に対して塗りだけでなく、テクスチャ(画像)を貼付けたり、立体感を付けたり、変形させたりすることもできます。

無料版もありますので、お試しは↓こちらから。

Belight Software, ltd
ジャンル:Graphics/Design
リリース日:2011-04-15
無料
posted with iTunesLinker at 14.01.09

ただし無料版では付いてくる素材が少ないので、気に入ったらぜひ有料版を使ってみて下さい。

Belight Software, ltd
ジャンル:Graphics/Design
リリース日:2010-12-23
価格:2000円
posted with iTunesLinker at 14.01.09

[Unity]:NGUIで各種画面サイズに対応する方法(NGUI ver.3.0.7f1)

以前に似たようなエントリを書いたのですが、
[Unity]:NGUIで各種画面サイズに対応する方法
NGUI ver.3.0.7で大きな変更があったので、新しい方法をご紹介。

前回のUIAnchorとUIStretchを使う方法でも実現可能です。

スクリーンショット 2013-12-10 10.46.00

使用バージョン:
Unity 4.2.2f1
NGUI 3.0.7f1

今回、SpriteなどのWidgetを追加すると従来の「Widget」以外に「Anchors」という項目が付いてきます。

スクリーンショット 2013-12-10 10.53.24 スクリーンショット 2013-12-10 10.56.06

ここでPaddedやRelativeといった項目を設定することで、以前のUIAnchor+UIStretchのような配置とサイズを設定出来ます。

各設定の仕方はざっとこんな感じ。

  • Padded – pixel単位での指定
  • Relative – 相対サイズでの指定
  • Unified – pixelと相対サイズの組合せでの指定
  • Advanced – Unifiedと同様だが、親コンテナを個別に指定できる

pixcel単位の指定では本末転倒なので、今回はRelativeで。
先ずTargetに親コンテナとなるオブジェクトを指定。
そして前回と同様に各Spriteの配置とサイズを親コンテナに対する比率で指定していきます。

スクリーンショット 2013-12-10 11.08.15 スクリーンショット 2013-12-10 11.09.58 スクリーンショット 2013-12-10 11.10.15 スクリーンショット 2013-12-10 11.10.28 スクリーンショット 2013-12-10 11.10.36

すると…

スクリーンショット 2013-12-10 11.17.49 スクリーンショット 2013-12-10 11.17.41 スクリーンショット 2013-12-10 11.17.30

こんな感じで画面サイズに合わせて各Spriteの配置・サイズが調整されて表示されます。
前回のUIAnchor+UIStretchを各Spriteにいちいちアタッチする方法よりはスッキリしますね!

[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を放り込むと、実行時にブラウザに飛んでしまうようになったためです。

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

AndroidアプリからIntentを使って、各種SNSに画像付きでシェアする実装例です。
一部の機種でしか動作確認できておらず、不完全であることをご了承下さい。

public void share(final Activity activity, final String text, final String path) {
    Intent intent = new Intent(Intent.ACTION_SEND);
    intent.setType("text/plain");
    intent.putExtra(Intent.EXTRA_TEXT, text);
    if (path != null) intent.putExtra(Intent.EXTRA_STREAM, Uri.parse("file://" + path));

    try {
        activity.startActivity(intent);
    } catch (ActivityNotFoundException e) {
        Toast.makeText(activity, "Client not found.", Toast.LENGTH_LONG).show();
    }
}

“text/plain”の内容を開く事ができるアプリの一覧が表示され、選択することにより各アプリにテキスト・画像がプリセットされた状態で開きます。

Intent一覧  Intent-Twitter  Intent-Facebook

ただしFacebookに関しては、仕様によりURLしかシェアできません。
またGmailに関しては、送信画面では画像が添付されているように見えますが、
実際に送信するとメールに画像は添付されません。

Gmailで画像が添付されるようにするには以下のように実装します。

public void share(final Activity activity, final String text, final String path) {
    Intent intent = new Intent(Intent.ACTION_SEND);
    intent.setType("text/plain");
    intent.putExtra(Intent.EXTRA_TEXT, text);

    try {
        String filepath = Images.Media.insertImage(activity.getContentResolver(), path, "screenshot.png", null);
        intent.putExtra(Intent.EXTRA_STREAM, Uri.parse(filepath));

        activity.startActivity(intent);
    } catch (FileNotFoundException e1) {
        e1.printStackTrace();
    } catch (ActivityNotFoundException e) {
        Toast.makeText(activity, "Client not found.", Toast.LENGTH_LONG).show();
    }
}

ただし、こうすると画像のコピーが作成され、明示的に削除をしない限りストレージに残り続けてしまいます。

Androidネイティブの開発に関しては全くの無知な私に、正しい実装法をご教授下さる方、お待ちしております!

[Unity]:NGUIで各種画面サイズに対応する方法

Unity 4.2
NGUI 2.6.4
(NGUI 3.0.7の場合はこちらを参照→「[Unity]:NGUIで各種画面サイズに対応する方法(NGUI ver.3.0.7f1)」)

画面下部に5枚のSpriteを並べて表示する場合を考えます。
スクリーンショット 2013-08-09 8.31.43スクリーンショット 2013-08-09 8.33.21

綺麗に並べてありますが、画面のサイズが変わると…

スクリーンショット 2013-08-09 8.35.12スクリーンショット 2013-08-09 8.35.34

Spriteがはみ出してしまったり、余計なスペースが空いたりしてしまいます。
そこで、Spriteにアンカーを付与して位置を調整します。

Hierarchyビューで目的のSpriteを選択して…
スクリーンショット 2013-08-09 8.39.50

メニューから Component – NGUI – UI – Anchorを選択すると、
各Spriteに対してUIAnchorコンポーネントが付加されます。
スクリーンショット 2013-08-09 8.41.24スクリーンショット 2013-08-09 8.50.43

左下に配置したいSpriteを選択しInspectorビューで
UISpriteのPivotを左と下に設定して、UIAnchorのSideをBottomLeftとします。

スクリーンショット 2013-08-09 8.53.08 スクリーンショット 2013-08-09 8.47.59

左から2番目に配置したいSpriteの場合は、
例えばPivotを中央と下、SideをBottomとした上で、
UIAnchorのRelative OffsetのXを-0.2と設定します。
この辺りは色々な設定値の組合せで自由に出来ますので、目的の位置にSpriteが配置されるように他のSpriteに関しても設定してみて下さい。

すると…
スクリーンショット 2013-08-09 9.01.25スクリーンショット 2013-08-09 9.01.41

配置に関しては問題無さそうですが、依然として余計なスペースが空きますし、
今度は画面の横幅を縮めた時にSprite同士が重なるようになってしまいました。

そこで今度はストレッチの設定をして、
Spriteが画面サイズに合わせて拡大・縮小するようにします。

アンカーの時と同じように対象のSpriteを選択して、
今度は Component – NGUI – UI – Stretchを選択すると、
各Spriteに対してUIStretchコンポーネントが付加されます。

スクリーンショット 2013-08-09 9.06.36スクリーンショット 2013-08-09 9.11.00

UIStretchの設定は全てのSpriteに関して同じですので、
全てのSpriteを選択したままInspectorビューでUIStretchの項目を
StyleはHorizontalに、Relative SizeのXは0.2と設定します。

スクリーンショット 2013-08-09 9.18.10

すると今度は…

スクリーンショット 2013-08-09 9.21.55 スクリーンショット 2013-08-09 9.21.48

画面サイズが変わってもSpriteが自動的に拡大・縮小されて画面下部に並ぶようになりました。

Sprite自体のアスペクト比が変わってしまうのが都合悪い場合は、
UIStretchのStyleやRelative Sizeの設定で色々と変更することができます。

また今回は水平方向に並べる設定を紹介しましたが、
垂直方向でも同様の設定で対応が可能です。
試しに画面いっぱいに5×5=25枚のSpriteを敷き詰めてみました。

左がiPhoneの4インチで、右がiPadです。
スクリーンショット 2013-08-09 7.39.33 スクリーンショット 2013-08-09 7.38.16

応用すれば、色々と柔軟なデザインが出来そうですね!