かつて代官山らへんで働いてたengineerのUnityブログ

サーバサイドやってきたエンジニアがUnityとか触って遊ぶだけのブログ

【Unity】セルのところで止まるスクロールビュー【FancyScrollView】

ちょうどセルのところで止めてくれるタイプのスクロールビューってあるじゃないですか
SnapScrollとか呼んだりするみたいなのですが、あれを作りたくて自分で組んでみました。

ゴリラがスクロールするよ!


そして半日かけて作ったあとに
「もしかしてどっかに同じようなスクリプト転がってるんじゃね?(禁忌)」
と思い調べてみたら見事にありましたので、今回はそんな神スクリプトの紹介です。

こちらがその神スクリプト、FancyScrollView!
github.com
https://github.com/setchi/FancyScrollView/raw/master/Documents/screencast1.gif
https://github.com/setchi/FancyScrollView/raw/master/Documents/screencast2.gif
めちゃめちゃスタイリッシュ&ファンシィー!


半日かけて作ったなんちゃってスクロールビューは投げ捨ててFancyScrollViewでゴリラをスクロールさせてみることにしました。
そして完成したのがこちら

前のと一緒!!!


ええ、前のとなにが違うのかわかりませんね。僕のなんちゃってスクロールビューも実はよく出来ていたのかもしれません。
ただ内部の処理は僕が作ったものとはかなり異なっていました。

僕の方はuGuiのScrollViewを改造して作ったのですが、こちらはスクロールの動きはAnimationで管理されていて非常に細かい変更が楽です。スクロールさせつつスタイリッシュなアニメーションを混ぜたい場合などにめちゃめちゃ便利なのではないでしょうか。
あとは無限スクロールなんかにも対応しており、応用の幅が広いです。
表示させているセルも最小限になる作りなっており、ド派手なアニメーションなどさせない限り負荷も問題ない模様。
サンプルも充実しており、実装で特に迷うことはありませんでした。


こんなものが普通に無料で転がっているのだから、恐ろしい時代になったものです。
みんなもFancyScrollViewでスタイリッシュになろう!




そういえばブログ名ちょっとだけ変えました。

【Unity】1週間ゲームジャムに参加してみた【クソゲー】

代官山らへんで働かなくなってはや2年が経とうとしております。くそすけです。
そろそろブログ名変えなきゃなぁ



唐突にUnity1週間ゲームジャムなるものに参加しました。
f:id:chroske:20180611211550p:plain

参加しましたって言ってもゲーム作って投稿するだけなんですが
毎回お題が変わるようなのですが、今回は「ぎりぎり」がお題でした。
作ったのはコチラの神ゲー、「ラスト・グレネード」です。
lastgranadegif
https://unityroom.com/games/pub_nantoka


ラスト1個のグレネードで最後に残った敵を爆殺するだけの簡単なゲームです。焦るなよ?
某島に100人落とされてサバイバルする系のゲームをやってる人は「あー、アレね」ってなると思いますがアレです。

ちなみにアイディアは締切日当日のお昼に浮かんだので開発期間は8時間です!
え?いっしゅうかん?

ビルドは早めにやっておこう!

ゲームジャム的なものには初めて参加したのですが、これ重要ですね。
基本ビルドは上手く通らない!!
今回WebGLビルドが初めてだったのでそれもあるのかもしれませんが、全然上手く行きませんでした。
AssetStoreの無料アセットのパワーに頼りすぎたのも原因だったりします。あいつらめっちゃバグる!
まあUnityは結構バージョン依存するのでアセットがバグっちゃうのは仕方ないことです。力には責任がつきまとうのだ。

ガンガンAssetStoreに頼ろう!

僕らプログラマは基本イラストを書いたりかっこいい3Dモデルを作ったりはできないはずなので、そこはガンガンAssetStoreに頼っていきましょう。無料でもかなりいい素材がゴロゴロしてます。
今回使わせていただいたのはこちら
assetstore.unity.com
assetstore.unity.com
assetstore.unity.com
Environments系のアセットはライティングとかシェーダーとかかなり勉強になります。

アイディアがでっかくなっちゃう病にはゲームジャム!

僕も結構ゲームのアイディアとか考えるんですが、決まってでっかくなりがちなんですよね。
そんな人にこそゲームジャムかなあと。締切が決まってれば「面白くしよう!」の前に「間に合わせなければ...」っていうのが来るので自然とアイディアがコンパクトになります。多分。
向き不向きはあると思うんですが自分には合ってるような気がしたのでまた機会があれば参加してみようかと思います。


ブログ名なんにしようかなあ

【Unity】NetworkMigrationManagerを試してみる【UNET】

Unity5.3.1p3より追加されたNetworkMigrationManagerですが、
これなにが出来るのかって言うと、ホストマイグレーションができます。
ホストマイグレーションっていうのはつまり通信中にホストが落ちちゃった場合にクライアントの誰かが新しいホストになりかわって通信を繋ぎ直し、ゲームを継続するというもの。
Photonなんかだと普通に実装されている機能ですが、UNETには今までなかったのでこれでちょっとはPhotonの対抗馬となれるのかなーと思い実際に動かしてみました。

やり方

1) 空のプロジェクトを作成します。

2) 空のオブジェクトを作成し、コンポーネントにNetworkManager HUD、NetworkManager、NetworkMigrationManagerを追加してやります。

3) もう一個空のオブジェクトを作成し、NetworkIdentityを追加します。Local Player Authorityにチェックを入れます。

4) 3で作成したオブジェクトをprefab化します。元のは消していいです。

5) 2で作成したオブジェクトのNetworkManagerのPlayer Prefabに4で作成したprefabを追加します。

6) ホストマイグレーションのテストなので、エディタ以外に最低もう一個クライアントが必要となります。PCアプリとしてプロジェクトをビルドします。

7) 6でビルドしたPCアプリを実行して、エディタも実行します。

8) どっちかでLAN Hostします。もう一方でLAN Clientします。つながりましたね?

9) LAN Hostした方をSTOPします。これでホストが落ちたことになります。

10) LAN Clientした方でPick new Host→Start as Hostします。これでこいつが新しいホストになります。
f:id:chroske:20160715150804p:plain

11) STOPした方でPick new Host→Recconect as Clientします。これでさっきホストだった方がクライアントとしてつながりました。
f:id:chroske:20160715150801p:plain

まとめ

とりあえず動かすだけなら簡単に出来ましたね。
実際これを使って開発するとしたら継承してロジックなどを書き足しながら使ってやることになるのかと思います。
あとこれ現在のところUnity MultiplayerなどのMachMakerを使っている場合には使えません。
マニュアルにも書いてあるんですが一番下に書いてあって気づくのに時間かかった。上に書いとけよお!!

IP指定で直接接続してる場合のみ使えるみたいです。
つまりまだまだPhotonのホストマイグレーションほど便利には使えないみたいですね。今後に期待です。

【JavaScript】はてなブログに貼ったgif画像に再生ボタンをつける【Gifffer】

ゲーム系のブログなんかをやっていると、動きを見せるためにgif画像を使いたい時があると思います。
しかしブログみたいな複数ページが連なっているようなところでgifを貼りまくるとそれぞれが勝手に動くのでなかなかにCPUを食いまくります。
そこで紹介したいのがGiffferというgif画像に再生ボタンをつけるJavaScriptライブラリです。
はてなブログ上でも使えそうだなと思い、試してみたらいけたのでそのやり方を書いていきます。

大天使チタンダエルgif
↑こんな感じになるよ!チタンダエル!

やり方

設定→デザイン→カスタマイズ→フッタを開き、下記のhtml及びjavaScriptを貼り付けます。

<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script type="text/javascript" src="http://krasimir.github.io/gifffer/lib/gifffer.js"></script>
<script type="text/javascript">
     window.onload = function() {
        Gifffer();
     }
</script>

あとはgif画像を貼り付ける時に

<img data-gifffer="http://チタンダエル.gif">

こんな感じに指定してやると再生ボタンが勝手につけられます。超簡単!

捕捉

Gifffer.jsはHTML5Canvasを利用してサムネイルを作ってくれているみたいですね。
読み込みに若干時間はかかるけどなんとなくページスクロールが軽くなったような気がします。
あと上のスクリプトに書いてあるURLはgifffer.js開発元から勝手に引っ張ってきたものなので、あっちで変更があると動かなくなる可能性があります。
下の開発元サイトからダウンロードしてきて自前サーバなりDropBoxなりに置いて、そこから引っ張ってくることをオススメします。
それとはてなブログスマートフォンサイト版だとヘッダとフッタをいじれるのはPro版かららしい、さすがはてな汚い課金しよ...



Gifffer
http://krasimir.github.io/gifffer/

【Unity】ScrollViewを魔改造してTwitter風ひっぱりリロード【uGUI】

スマホアプリなんかでよくある引っ張ってリロードするScrollViewをuGUIを改造して作ってみました。
twitterクライアントアプリとかに使われてるアレです。iOSとかandroidだとUIRefreshControlなんて言われてるやつですね。

ひっぱりリロードデモgif
↑こんなの

Unity側作業

1. UI→ScrollViewでスクロールビューを生成します。ScrollRectのHorizontalのチェックは外しておきましょう。

2. ScrollView/ViewPortの下に空のオブジェクトを作成します。名前は「OffsetGroup」としました。

3. ViewPort下にあるContentにVertical Layout Groupコンポーネントを設定しOffsetGroup下に移動します。

4. Content下にReloadPanelを作成します。リロードのクルクルが出てくるところです。

5. ReloadPanelにLayoutElementコンポーネントを設定し、PrefrredHeightを150にします。これがReloadPanelの高さになります。

6. ReloadPanel下にUI→ImageからLoadArrowImage(引っ張ると出てくる矢印)とLoadingImage(ロード中クルクマまわる画像)を生成し、テキトーな矢印とクルクルましたくなる画像をそれぞれに設定します。LoadingImageはactiveのチェックを外しておきます。

7. UI→PanelからPanelを生成し色をつけたらScrollView内のReloadPanelの部分がちょうど隠れるように被せます。上のgif画像デモでいうUnityマークがついた青い部分です。

以上でUnityでの作業は終わりです。こんな感じになってますでしょうか。
f:id:chroske:20160525161656p:plain

Script

ScrollViewに貼り付けるスクリプトです。
実は結構面倒なことをやっています。
ScrollView内のContentを下に引っ張った時に、Contentが動いた分と同じだけScrollViewも動かします。
しかしContentはScrollViewの子要素なので、同じだけ動かすとContentは2倍動いてしまう。
そこでOffsetGroupをContentが動いた方向とは逆方向に同じだけ動かすことで相殺し、Contentの動きにScrollViewが追従している感じになります。
多分動かして見たほうがわかると思うので、理解する前に動かすこと推奨。
ReloadPanelが隠していたPanelから出きったところでScrollViewの追従を止め、リロードを行います。
リロード完了したら追従を再開、elasticityで勝手に上に引っ張り戻る。

[SerializeField]
private GameObject content;
[SerializeField]
private GameObject offsetGroup;
[SerializeField]
private GameObject loadIcon;
[SerializeField]
private GameObject loadArrowIcon;

private bool listReloadFlag = true; //リロード可能フラグ
private bool listReloadEndFlag = false; //リロード終了確認用フラグ
private int scrollViewReloadHeight = -50; //ScrollViewがこのポジション以下まで下がったら更新開始 ReloadPanelの高さに準ずる
private int scrollViewDefaultHeight = 0; //ScrollViewのデフォルト位置
private float offsetGroupMoveRate = 0.80f; //戻るはやさに関係する
private float scrollRectDefaultElasticity; //デフォルトのelasticityを入れておく
private float pullBackscrollRectElasticity = 0.01f; //リロード開始までの引っ張って戻る速さに関係する

private ScrollRect scrollViewScrollRect;
private RectTransform scrollViewRectTransform;
private RectTransform contentRectTransform;
private RectTransform offsetGroupRectTransform;

void Start () {
	scrollViewScrollRect = GetComponent<ScrollRect> ();
	scrollViewRectTransform = GetComponent<RectTransform> ();
	contentRectTransform = content.GetComponent<RectTransform> ();
	offsetGroupRectTransform = offsetGroup.GetComponent<RectTransform> ();
	scrollRectDefaultElasticity = scrollViewScrollRect.elasticity;
}

void Update () {
	if (contentRectTransform.anchoredPosition.y < 0.0f && scrollViewRectTransform.anchoredPosition.y > scrollViewReloadHeight) {
		//ScrollViewのポジションが下がっているので戻る速度をあげてすばやく上にひっぱり戻す
		scrollViewScrollRect.elasticity = pullBackscrollRectElasticity;
		//ContentにScrollRectを追従させる
		scrollViewRectTransform.anchoredPosition = new Vector2 (0, contentRectTransform.anchoredPosition.y + scrollViewDefaultHeight);
		//offsetGroupをContentが移動した分だけ反対に移動させて子の追従を打ち消す
		offsetGroupRectTransform.anchoredPosition = new Vector2 (0, -contentRectTransform.anchoredPosition.y * offsetGroupMoveRate);
	} else if(scrollViewRectTransform.anchoredPosition.y <= scrollViewReloadHeight) {
		//reloadしてもいい状態か確認
		if (listReloadFlag) {
			listReloadEndFlag = false;
			listReloadFlag = false;

			//矢印を消してローディングを出す
			loadArrowIcon.SetActive(false);
			loadIcon.SetActive(true);

			//ScrollViewのポジションが下がらなくなるので戻る速度をデフォルトに戻す
			scrollViewScrollRect.elasticity = scrollRectDefaultElasticity;
			//ズレるので決め打ちで値を入れておく
			scrollViewRectTransform.anchoredPosition = new Vector2 (0, scrollViewReloadHeight);
			//リスト取得通信(デモなので実際の通信はしないよ)
			StartCoroutine (PullBackScrollView ());
		} else {
			//reload通信が終了していれば引き戻す処理
			if(listReloadEndFlag){
				scrollViewScrollRect.elasticity = pullBackscrollRectElasticity;
				scrollViewRectTransform.anchoredPosition = new Vector2 (0, contentRectTransform.anchoredPosition.y + scrollViewDefaultHeight);
				offsetGroupRectTransform.anchoredPosition = new Vector2 (0, -contentRectTransform.anchoredPosition.y * offsetGroupMoveRate);
			}
		}
	}
	//reloadした状態で上まで戻ったら再度reloadが行えるフラグをtrueにする
	if(!listReloadFlag){
		if(scrollViewRectTransform.anchoredPosition.y >=  scrollViewDefaultHeight-1){
			listReloadFlag = true;
		}
	}
	//loadIconを監視してactiveなら回す
	if(loadIcon.activeSelf){
		loadIcon.transform.eulerAngles += new Vector3 (0f, 0f, -8f);
	}
}

IEnumerator PullBackScrollView(){
	//とりあえず2秒クルクルさせる
	yield return new WaitForSeconds(2);

	/* ここらへんで通信してデータ取って来てリストに追加する */

	//ローディングから矢印に切り替え
	loadArrowIcon.SetActive(true);
	loadIcon.SetActive(false);
	listReloadEndFlag = true;

	yield break;
}

以上をスクリプトをScrollViewに貼り付けて各子オブジェクトとのひも付けを設定してやってください。

まとめ

今回はなるべくコードでアニメーションを制御せず、uGUIの動き利用しようということでこんな方法になりました。
もしかしたらScrollViewのmovement typeをUnrestrintedにして全部自前で制御した方が今後のメンテナンスを考えるといいのかもしれませんが、パパっと作るならこんなもんでもいいのかなーと
パパっとできてる感もあんまりないけど...
気が向いたらAssetStoreにでもサンプルを出してみようと思います。
需要あんのかなー

【Google Play】Developer Consoleでサポート対象端末をまとめて設定するスクリプト【JavaScript】

Google Playにアプリを申請時に非対象端末を設定しておいて、その端末からはアプリを検索&表示させないみたいなことが出来るようなのですが、
Android端末って現在のところ1万機種以上あるらしく、手作業でやるとか死ねるんでなんとかならんかと依頼されて書いたフニャフニャスクリプトがなんだかすごい感謝されてしまい
"是非全世界の人に広めるべき"とのお言葉をいただいたので"全世界に広めるべく"ここに載っけておきます(広がるとは言ってない)

↓配列のデバイスを非対応にしてそれ以外を対応にするスクリプト

//この配列に非対象端末名を入れる
var disableDeviceListStr = ["picasso_m","DA220HQL","HMB4213H","octopus-masu","hoge","hoge2"];

var jq = document.createElement('script');
jq.src = "//ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js";
document.getElementsByTagName('head')[0].appendChild(jq);

$('[data-device-id]').each(function(){
    var deviceId = $(this).attr("data-device-id");
    if(jQuery.inArray(deviceId, disableDeviceListStr) != -1){
		$(this).children("checkbox").click();
		console.log("disabled="+deviceId);
	}
});

手順

1. Google Play Developer Consoleからこの画面にいく
f:id:chroske:20160516170633p:plain:w500

2. 上のスクリプトの配列disableDeviceListStrに非対象にしたい端末を羅列する。
端末名は一覧の灰色の文字の方。

3. ブラウザのデベロッパツールのConsoleを開いて「>」マークのとこにコピペしてエンター
f:id:chroske:20160516171549p:plain
Chromeだとここ

4. 完了するのを待ってお終い。端末数によっては結構時間かかる模様。

捕捉

わざわざjQueryを取ってきてるのはどうしてもinArrayが使いたかったからです。二重ループとか書きとうない。
今後GooglePlayがhtmlの構成を変えたりすると動かなくなる可能性があります。というかさすがに本家が機能追加するのでは。

↓配列のデバイスを対応にしてそれ以外を非対応にするスクリプト(上とは逆バージョン)

var enableDeviceListStr = ["picasso_m","DA220HQL","HMB4213H","octopus-masu","hoge","hoge2"];

var jq = document.createElement('script');
jq.src = "//ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js";
document.getElementsByTagName('head')[0].appendChild(jq);

$('[data-device-id]').each(function(){
    var deviceId = $(this).attr("data-device-id");
    if(jQuery.inArray(deviceId, enableDeviceListStr) == -1){
		$(this).children("checkbox").click();
	} else {
		console.log("enabled="+deviceId);
	}
});


誰かが救われますように。

【Unity2D】角度と距離からベクトルを作ってAddForceする【Vector2】

AddForceは引数にベクトルを渡し、そのベクトルにしたがってObjectに力を加えるメソッドですが
ベクトルってなんぞ...って人ももしかしたらいるのかなーと思うので角度と距離からベクトル(Vector2)を作ってAddForceする方法のご紹介です

float angle = 70; //角度70度 基準は↑
float distance = 1500 //距離1500

float rad = angle * Mathf.Deg2Rad; //角度をラジアン角に変換

//rad(ラジアン角)から発射用ベクトルを作成
double addforceX = Math.Sin((rad) * distance;
double addforceY = Math.Cos((rad) * distance;
Vector2 shotVector = new Vector2((float)addforceX, (float)addforceY);

//Rigidbody2Dを取得してから
Rigidbody2D rigidbody2d = transform.GetComponent<Rigidbody2D> ();
//発射ァ!!
rigidbody2d.AddForce(shotVector);


これにRigidbody2Dまわりの摩擦係数やら空気抵抗やらを調整してやると
モンスト風gif
なんとか〜ストライクみたいのができました
アァ!バックベアード様!