Unity でネイティブプラグインを使って ActivityIndicator の画像変更と画面中央での表示(iOS編)
はじめに
iOS 側のコード
- 以下のスクリプトを Unity の Assets/Plugins/iOS に MyActivityIndicator.mm として保存する
- http://tsubakit1.hateblo.jp/entry/2014/08/19/010141 からの変更点は
- 親ビューの取得がなんかうまくいかなかったので GetAppController から UnityGetGLViewController に
- 背景色を半透明の黒に
- 画像の表示位置を画面中央に
#import "UnityAppController.h" UIImageView *imageView; UIView *backgroundView; NSMutableArray *imageList; const int kImageCount = 4; NSString* const kImageNameFormat = @"anim%02d.png"; extern "C" { void InitMyActivityIndicator(); void StartMyActivityIndicator(); void EndMyActivityIndicator(); } void InitMyActivityIndicator() { imageList = [NSMutableArray array]; for (NSInteger i = 0; i < kImageCount; i++) { NSString *imagePath = [NSString stringWithFormat:kImageNameFormat, i]; UIImage *img = [UIImage imageNamed:imagePath]; [imageList addObject:img]; } UIImage *img = [imageList objectAtIndex:0]; CGSize cs = img.size; CGRect pr = [[UIScreen mainScreen] bounds]; CGRect rect = CGRectMake( (pr.size.width / 2 - cs.width / 2), (pr.size.height / 2 - cs.height / 2), cs.width, cs.height ); imageView = [[UIImageView alloc]initWithFrame:rect]; imageView.image = imageView.image = [imageList objectAtIndex:0]; imageView.animationImages = imageList; imageView.animationDuration = 0.5; imageView.animationRepeatCount = 0; } void CreateBackgroundView () { CGRect pr = [[UIScreen mainScreen] bounds]; backgroundView = [[UIView alloc] init]; backgroundView.frame = CGRectMake(0, 0, pr.size.width, pr.size.height); backgroundView.backgroundColor = [UIColor colorWithRed:0.0 green:0.0 blue:0.0 alpha:0.5]; [UnityGetGLViewController().view addSubview:backgroundView]; } void StartMyActivityIndicator() { InitMyActivityIndicator(); CreateBackgroundView(); [UnityGetGLViewController().view addSubview:imageView]; [imageView startAnimating]; } void EndMyActivityIndicator() { [imageView removeFromSuperview]; [backgroundView removeFromSuperview]; }
Unity 側のコード
- 以下のスクリプトを任意の GameObject にアタッチする
- StartActivityIndicator で表示、StopActivityIndicator で非表示
using UnityEngine; using System.Collections; using System.Runtime.InteropServices; public class ActivityIndicator : SingletonMonoBehaviour<ActivityIndicator>{ #if UNITY_IOS [DllImport("__Internal")] static extern void InitMyActivityIndicator(); [DllImport("__Internal")] static extern void StartMyActivityIndicator(); [DllImport("__Internal")] static extern void EndMyActivityIndicator(); #endif public void Start() { #if UNITY_IOS InitMyActivityIndicator (); DontDestroyOnLoad(gameObject); #endif } public void StartActivityIndicator(){ #if UNITY_IOS StartMyActivityIndicator(); #endif } public void StopActivityIndicator() { #if UNITY_IOS EndMyActivityIndicator(); #endif } }
Unity でネイティブプラグインを使って ActivityIndicator の画像変更と画面中央での表示(Android編)
はじめに
バージョン
- Unity 4.6.3
- Android Studio 1.2
ソースコード
https://github.com/nirasan/UnityPluginAndroidCustomActivityIndicator/tree/master/activityindicator
ネイティブプラグインの作り方
- Android Studio を使い始めたので、http://tech.admax.ninja/2014/09/10/export-jar-by-android-studio/ こちらにしたがってプラグインを作成した。
プラグインからリソースを参照する
- jar 形式のファイルにはリソースが含まれないため、R.id などで参照するとエラーになる
- リソースを参照したい場合は http://sixeight.hatenablog.com/entry/2013/10/12/215456 こちらの通り、Unity の Assets/Plugins/Android/res にファイルを配置し、プラグイン側からは getResources().getIdentifier() を使って参照するとうまくいく
- 今回はこのおかげで表示したい画像を Unity 側で差し替えやすくなったのが嬉しかった
画面中央に任意の画像の Progress Dialog を表示する
Progress Dialog の拡張クラス
- 前記の通り R による参照を使えないので、初期化時に context から取得している
package com.example.activityindicator; import android.app.ProgressDialog; import android.content.Context; import android.graphics.drawable.AnimationDrawable; import android.os.Bundle; import android.widget.ImageView; public class MyCustomProgressDialog extends ProgressDialog { private static int layoutId; private static int animationId; private static int drawableId; private AnimationDrawable animation; public static MyCustomProgressDialog ctor(Context context) { MyCustomProgressDialog dialog = new MyCustomProgressDialog(context); dialog.setIndeterminate(true); dialog.setCancelable(false); layoutId = context.getResources().getIdentifier("view_custom_progress_dialog", "layout", context.getPackageName()); animationId = context.getResources().getIdentifier("animation", "id", context.getPackageName()); drawableId = context.getResources().getIdentifier("custom_progress_dialog_animation", "drawable", context.getPackageName()); return dialog; } public MyCustomProgressDialog(Context context) { super(context); } public MyCustomProgressDialog(Context context, int theme) { super(context, theme); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(layoutId); ImageView la = (ImageView) findViewById(animationId); la.setBackgroundResource(drawableId); animation = (AnimationDrawable) la.getBackground(); } @Override public void show() { super.show(); animation.start(); } @Override public void dismiss() { super.dismiss(); animation.stop(); } }
呼び出し用クラス
package com.example.activityindicator; import android.app.Activity; import com.unity3d.player.UnityPlayer; public class ActivityIndicator { private static MyCustomProgressDialog _dialog; public static void show() { final Activity activity = UnityPlayer.currentActivity; activity.runOnUiThread(new Runnable() { @Override public void run() { if (_dialog == null) { _dialog = MyCustomProgressDialog.ctor(activity); } _dialog.show(); } }); } public static void hide() { if (_dialog == null) return; final Activity activity = UnityPlayer.currentActivity; activity.runOnUiThread(new Runnable() { @Override public void run() { _dialog.hide(); } }); } }
Unity への取り込み
- http://tech.admax.ninja/2014/09/10/export-jar-by-android-studio/ に従い jar を作成し Unity の Plugins にコピー
- プラグインの res も Unity の Plugins にコピー
Unity 側のコード
void StartActivityIndicator () { #if UNITY_ANDROID AndroidJavaClass androidNativePlugin = new AndroidJavaClass("com.example.activityindicator.ActivityIndicator"); androidNativePlugin.CallStatic("show"); #endif } void StopActivityIndicator () { #if UNITY_ANDROID AndroidJavaClass androidNativePlugin = new AndroidJavaClass("com.example.activityindicator.ActivityIndicator"); androidNativePlugin.CallStatic("hide"); #endif }
paperclip_database の導入メモ
はじめに
- ActiveRecord のオブジェクトに画像ファイルを添付する gem の Paperclip で、画像の保存先をデータベースにする gem の paperclip_database の導入メモ。
- Paperclip は導入済みで、User モデルの avatar カラムに添付画像情報を入れているものとする。
バージョン
- Rails 4.2
- Paperclip 4.2.1
- paperclip_database 2.3.1
インストール
- Gemfile
gem "paperclip_database", "~> 2.0"
- インストール
bundle install
マイグレーションファイルの作成と実行
rails generate paperclip_database:migration User avatar rake db:migrate
モデルの設定
- :storage オプションを追加する
has_attached_file :avatar, :storage => :database
画像を返すアクションとルートの作成
ルートの作成
- routes.rb で users リソースが定義済みの場合、member でルートの追加
resources :users do member do get :avatars end end
アクションの作成
- UsersController で paperclip_database のコントローラ拡張ミックスインのインポートと画像返却用のアクション定義メソッドの追記
class UsersController < ApplicationController include Paperclip::Storage::Database::ControllerClassMethods downloads_files_for :user, :avatar
テンプレートで画像の呼び出し
- @user.image.url(:style) だと URL エンコード済みの文字列が出てきてしまうので、普通にルート名で URL を指定する。
<% image_tag(images_user_url(@user, :style => :medium)) %>
NGUI の UITweener の Animation Curve に任意の曲線を設定する
はじめに
- Unity + NGUI で TweenPosition や TweenAlpha などの UITweener を継承したコンポーネントで Animation Curve の曲線をプログラムから設定する方法についてメモ。
バージョン
- Unity 4.6.1f1
- NGUI 3.7.6
設定方法
- Animation Curve の曲線は AnimationCurve クラスで設定する
- コンストラクタは AnimationCurve(Keyframe[])
- Keyframe は曲線上の任意の点で Keyframe(x, y) で x が時間軸、y が変化量になる。
- Keyframe の x は、開始地点が 0 で、終了地点が 1 になる。duration が 3秒 なら「x:0 = 0秒」で「x:1 = 3秒」となる。
- Keyframe の y は、開始地点が 0 で、終了地点が 1 になる。TweenAlpha で変化前が 1 で変化後の値が 0 なら「y:0 = 1」で「y:1 = 0」となる。
コード例
開始から半分の時間まで変化しない場合
GameObject go = GameObject.Find("Path/To/Any/GameObject"); UITweener tw = TweenAlpha.Begin(go, 1f, 0.5f); tw.animationCurve = new AnimationCurve( new Keyframe( 0f, 0f), new Keyframe(0.5f, 0f), new Keyframe( 1f, 1f) );
点滅する場合
GameObject go = GameObject.Find("Path/To/Any/GameObject"); UITweener tw = TweenAlpha.Begin(go, 1f, 0.5f); tw.animationCurve = new AnimationCurve( new Keyframe( 0f, 0f), new Keyframe(0.2f, 1f) new Keyframe(0.4f, 0f), new Keyframe(0.6f, 1f) new Keyframe(0.8f, 0f), new Keyframe( 1f, 1f) );
Keyframe に入る角度と出る角度の指定をする
- Keyframe のコンストラクタで Keyframe(x, y, inTangent, outTangent) というパターンが有り inTangent と outTangent で曲線の入る角度と出る角度を指定できる。
- inTangent, outTangent の単位は tangent なので、角度を指定したい場合は変換する必要がある。
角度からタンジェントへの変換
// 角度からタンジェントへ float DegreesToTan (float degrees) { return Mathf.Tan(DegreesToRadians(degrees)); } // 角度からラジアンへ float DegreesToRadians (float degrees) { return degrees * Mathf.PI / 180f; }
コード例
イーズアウト
GameObject go = GameObject.Find("Path/To/Any/GameObject"); UITweener tw = TweenAlpha.Begin(go, 1f, 0.5f); tw.animationCurve = new AnimationCurve( new Keyframe(0f, 0f, 0f, DegreesToTan(70f)), new Keyframe(1f, 1f, DegreesToTan(10f), 0f) };
イーズイン
GameObject go = GameObject.Find("Path/To/Any/GameObject"); UITweener tw = TweenAlpha.Begin(go, 1f, 0.5f); tw.animationCurve = new AnimationCurve( new Keyframe(0f, 0f, 0f, 0f), new Keyframe(1f, 1f, DegreesToTan(70f), 0f) };
波を描く
GameObject go = GameObject.Find("Path/To/Any/GameObject"); UITweener tw = TweenAlpha.Begin(go, 1f, 0.5f); tw.animationCurve = new AnimationCurve( new Keyframe( 0f, 0f, 0f, 0f), new Keyframe(0.25f, 0.5f, DegreesToTan(-85f), DegreesToTan(-85f)), new Keyframe(0.5f, 0.5f, DegreesToTan(-85f), DegreesToTan(-85f)), new Keyframe(0.75f, 0.5f, DegreesToTan(-85f), DegreesToTan(-85f)), new Keyframe( 1f, 1f, 0f, 0f) };
NGUI で UILabel の文字量に従って縦幅だけ変動させるスクリプト
using UnityEngine; using System.Collections; /// <summary> /// UILabel の横幅を固定して text に従った縦幅に設定するスクリプト /// </summary> public class UILabelHeightFitter : MonoBehaviour { /// <summary> /// リサイズ対象の UILabel /// </summary> public UILabel label; private string beforeText = ""; void Update () { // UILabel.text が更新されるたびにリサイズを実行 if (label.text != beforeText) { ResizeHeight(); beforeText = label.text; } } /// <summary> /// UILabel.text の文字量に従って UILabel.height を変更する。 /// 簡単のため「UILabel.fontSize == 文字の幅 == 文字の高さ」という前提で計算しているので、 /// 半角全角の混在や可変幅フォントの使用によりずれが生じる場合がある /// </summary> void ResizeHeight () { int line = 1; int chara = 0; int maxChara = label.width / (label.fontSize + label.spacingX); string text = label.text; for (int i = 0; i < text.Length; i++) { if (text[i] != '\n') { chara++; if (chara >= maxChara) { chara = 0; line++; } } else { chara = 0; line++; } } label.height = (label.fontSize + label.spacingY) * line; } }
自分用 RubyMine ショートカットメモ
ジャンプ
- 宣言へジャンプ
- Command + b
- 使用箇所のリスト
- Alt + F7
タブ移動
- 左へ
- Command + Shift + @
- 右へ
- Command + Shift + [
NGUI の ScrollView で気持ちのいいスクロールを実装する
気持ちが良いスクロールとは?
- 今回は「ちょっとスワイプするだけで次の要素にスッと切り替わる」ものを実装する。
バージョン情報
- Unity 4.6.1f
- NGUI 3.7.6
ヒエラルキー
UI Root ScrollView Grid
ScrollView の作成
- メニューの [NGUI] > [Create] > [Scroll View] で作成
Grid の作成
- メニューの [NGUI] > [Create] > [Grid] で作成
- UICenterOnChild をアタッチ
オプション設定
- UIGrid の Sorting を None 以外にする
- わからなければ Custom にする
- UICenterOnChild の NextPageThreshold を 1 にする
動作確認
- Grid 配下に任意の UIDragScrollView とコライダーがアタッチされたオブジェクトを追加して動作を確認する。
解説
- UICenterOnChild.NextPageThreshold が 0 より大きく、UIGrid の Sorting が None 以外である時、スワイプした距離が NextPageThreshold より大きければ UICenterOnChild により次あるいは前の要素に切り替わる。
注意点
- ScrollView をループさせるスクリプトの UIWrapContent が有効であると、この処理は正常に行われなかった。