園児ニアのメモ

ただのエンジニア。何でも屋みたいな扱い受けてます。

【Unity】Sceneを再読み込みでライティングが暗くなってしまった時の対応

Unityで簡単なゲームを作っており、そこで起こったライティングの不具合とその解決法です。
f:id:nanokanato:20171116111703p:plain:w300

発生した不具合

Sceneは以下のようになっており、
「Scene_Static」でカメラとコントローラーなどを管理。
「Scene_Game」でゲームをプレイする画面を表示しています。
f:id:nanokanato:20171116110059p:plain:w300

初回のScene_Gameの表示時は以下のような明るいライティングの画面が表示されます。
f:id:nanokanato:20171116110251p:plain:w300

制限時間のカウントが0になるとリザルト画面に遷移し、タイトルなどを挟んでまたScene_Gameに戻ってきます。
その時、以下のような暗いライティングになってしまいました。
f:id:nanokanato:20171116110620p:plain:w300

ちなみにScene遷移はScene_Staticを残したまま行いたいので「SceneManager.LoadScene(nextSceneName, LoadSceneMode.Additive);」を使っています。

解決方法

Unityに「事前計算されたライティングの利用」という解説があるのでそれを利用します。
docs.unity3d.com

メニューバーの「Window > Lighting > Settings」からObject mapsを開きます。
f:id:nanokanato:20171116112624p:plain:w300

Auto GenerateをOffにしてGenerate Lightingを押すことでライティングを事前に計算することができます。

ひとまず、再度Scene_Gameを読み込んでも明るい状態になりました。
f:id:nanokanato:20171116111805p:plain:w300

その他

一度、Generate Lightingをすると以下のようなライティングのデータが作成されるので再度Auto GenerateをOnにしても暗くなることはありませんでした。
f:id:nanokanato:20171116112952p:plain:w300

【Unity】EazyWifiController使ってみた

結構使っているのですが書いていなかったので...
簡単にですが紹介します。

Easy WiFi ControllerというAssetを紹介します。
f:id:nanokanato:20171107110448p:plain:w300

2017年11月現在は$37.80です(4,303.91円)

こちらはiPhone,AndroidをコントローラーにするAssetです。

サンプルにはいろいろなコントローラーとそのコントローラーで動かすUnityのサンプルが入っています。
Client:コントローラー側
Server:ゲーム等のサンプル側

コントローラーは自由にカスタムできるので実機じゃないと繋がらないコントローラーをEazyWifiControllerでUnityEditor用として代用したりすることができます。
もちろん実機同士でも可能です(iPhoneのゲームを別のiPhoneをコントローラーとして操作など)

コントローラーの接続条件

コントローラーの接続条件として
EazyWifiManager.csの値のPeerType以外が同じ、
各ボタン、ジョイスティック等のControllNameが正しい、
回線が安定している同じWifiに接続されていることである必要があります。

EazyWifiManagerのPeerTypeでClientかServerかを指定します。
Serverは同じWifiに接続されているEazyWifiManagerの値が同じClientのアプリを検索して接続します。
接続後はControllNameを元に各ボタンの値を取得します。

問題点など...

  • Wifiが使える環境でないといけないので外でやる場合はテザリング用にもう一台iPhoneなどを用意する必要がある。
  • コントローラーは接続されているがボタンが動作しないなど...
    • ControllNameが違う値だった
  • ジョイスティックの値がズレている
    • コントローラー側が90度回転していた。
      どちらが正位置か分かりづらい。
  • 同じWifiで1人プレイようのゲームを同時に2人で操作しているとコントローラーが入れ替わってしまう場合がある
    • 別のWifiに繋ぐ
    • EazyWifiManagerのApplicationNameを変える

上記、解決法が見つかり次第追記していこうかなと思います。

【AppleScript】ブラウザを開いて自動で入力する

勤怠など毎日入力するのが面倒な部分があるので作ってみました。
AppleScriptを使用しますのでMacのみ対応しています。

●やりたいこと
・勤怠入力で最初から定型文を入力する
・完全自動化もできるが勤怠なので作業内容などは色を変えて入力を指示だけにする

今回はZACという勤怠管理ツールを参考に書きます。
↓普通にブラウザで開いた勤怠入力画面
f:id:nanokanato:20170914121617p:plain:w300
起動時に今日の日付になるのは自動ですが...
毎日、出勤時間,退勤時間,作業時間などはある程度テンプレなど前日のデータを入力していてほしいです。

AppleScriptでテンプレ入力

AppleScriptとは、文字通りAppleが開発したMacOS用のオブジェクト指向スクリプト言語です。

AppleScriptは「Application/Utility/スクリプトエディタ.app」で開発することができます。
実行ボタンを押すことで実際にどんな動作をするか確認できます。

以下は実行することでGoogle Chromeを起動し、勤怠のページを開いて最初から入力していてほしい部分に定型文を入れるソースです。

tell application "Google Chrome" to run
tell application "Google Chrome" to activate
tell application "Google Chrome"
	tell window 1
		set newTab to make new tab with properties {URL:"https://hogehoge.zac.ai/hogehoge/Shinsei/Nippou.asp"}
		repeat while loading of active tab
			delay 1
		end repeat
		tell active tab
			-- 出勤時間の入力
			execute javascript "var start_time_hours = document.body.getElementsByTagName('select')[1].getElementsByTagName('option');"
			execute javascript "for(var i = 0; i < start_time_hours.length; i++){
								if(start_time_hours[i].value == '10'){
									start_time_hours[i].selected = true;
									break;
							 	}
							   }"
			-- 退勤時間の入力
			execute javascript "var end_time_hours = document.body.getElementsByTagName('select')[4].getElementsByTagName('option');"
			execute javascript "for(var i = 0; i < end_time_hours.length; i++){
								if(end_time_hours[i].value == '19'){
									end_time_hours[i].selected = true;
									break;
							 	}
							   }"
			
			-- 作業内容を登録
			execute javascript "var task_time_hours = document.body.getElementsByTagName('select')[57].getElementsByTagName('option');"
			execute javascript "for(var i = 0; i < task_time_hours.length; i++){
								if(task_time_hours[i].value == '8'){
									task_time_hours[i].selected = true;
									break;
							 	}
							   }"
			
			execute javascript "document.body.getElementsByTagName('select')[61].style.backgroundColor = 'skyblue';"
			execute javascript "document.body.getElementsByTagName('textarea')[6].style.backgroundColor = 'skyblue';"
		end tell
	end tell
end tell

AppleScriptを実行して開かれた勤怠入力画面
f:id:nanokanato:20170914121900p:plain:w300
・出勤時間と退勤時間はデフォルトを入力。
・作業時間もデフォルトを入力。
・作業内容の部分に色をつけて編集しないといけないことを指示。

若干ですが、煩わしい選択が少し減ることで毎日続けれる気がします。

スクリプトエディタで「ファイル > 書き出す...」を選択し、フォーマットをアプリケーションにすることで.appのアプリケーションファイルを作成できます。

毎日これをクリックすることで最初からテンプレートが入力された状態のページを開くことができます。

【GAS】RSSのURLを渡すと連想配列を返すライブラリ、ELERSSReaderを公開

RSSとはサイトなどの更新情報を受け取れるフォーマット、またはその仕組みなどを一般的に言います。

RSSについては詳しくは以下をどうぞ。
RSS - Wikipedia

そのRSSをGoogleAppScript(以下GAS)にて簡単に取得するライブラリを作りました。

中でやっていること

どこらへんが簡単になっているのかというと...

  1. 内部で受け取った引数のURLからRSSの情報を取得します。
  2. 取得したRSS情報をテキストからXMLに変換。
  3. XMLはGASでは使いづらいので連想配列の形式に変換。

という処理をやっています。

導入方法

ライブラリの追加に必要なプロジェクトキー

1J1FyM0gHx_X_bXb_qgGHnbzM1IIpGMmOMGaf_dzo7BwX_Cfop2ZieJLr

ELERSSReaderの使用方法

function myFunction() {
    var rss = ELERSSReader.GetRSS("http://www.hogehoge.rss");
    if (rss != null) {
    
    }
}

GetRSS()メソッドにRSSのURLを送るだけで連想配列が取得できます。

実際に「国土交通省 / 気象庁」のRSSを取得した結果が下の画像です。
f:id:nanokanato:20170912190810p:plain:w300

通常は「data」の中にArray型で入っています。
エラー時は「error」としてエラー内容の参照配列が入っています。

活用方法

RSSを読み取るだけでは活用は難しいですが、SNSと連携することで通知機能として活躍すると思います。

今回これを作った経緯として、
Unity公式のARKitの開発が「BitBucket」というサービスで公開されており、その進捗を自動で通知するために使っています。

Chatworkに投稿

madgenius.hateblo.jp

【Unity】unitypackageのzipを解凍するとフォルダができてしまう

これに関してはMacユーザーのみ、または解凍ソフトの問題だとは思うのですが...

.unitypackageのファイルをZIPに圧縮したものを受け取り、Macアーカイブユーティリティ.appという標準の解凍ソフトを使うと「ZIP → .unitypackage → フォルダ」というように解凍し過ぎてしまいます....

.unitypackageはフォルダになった時には削除されておりゴミ箱にも入っていない状態です。
なんかもう、これもどうせ解凍するんやろ??みたいなところがAppleって感じですね。

解決法としては単純でターミナルで以下を入力し、zipのみ解凍することを指示してやればOKです。

unzip [ファイル名]

【Unity】数字の画像でスコアを表現する

スコアなどを表示するときにフォントがあれば一番ですが、特殊な表示をしたい場合について書きます。

用意した0〜9の数字の画像を使ってそれをUI.Textのように簡単に使えるようにしてみたいと思います。

画像素材の用意

用意するのはこのような画像で0〜9までです。
f:id:nanokanato:20170908135201p:plain:w30

スクリプトの用意

以下のNumberText.csはUI.Textのように簡単な入力で画像を使ったスコアなどの表示をするためのクラスです。

このスクリプトを空のGameObjectに配置することで使用できます。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class NumberText : MonoBehaviour {
	public long num = 1234567890;
	public int digit = 16;
	public bool zeroFill = false;
	private List<Image> NumImageList = new List<Image>();
	[SerializeField] private Sprite[] spriteNumbers = new Sprite[10];
	
	// Update is called once per frame
	void Update () {
		if (NumImageList.Count == digit) {
			//桁数が揃っているので数値を表示する
			long num2 = num;
			int numDigit = 0;
			if (num2 > 0) {
				numDigit = ((int)Mathf.Log10(num2) + 1);
			}
			if (numDigit > digit) {
				//数値が桁数を超えている
				for (int i = 0; i < NumImageList.Count; i++) {
					Image numImage = NumImageList.ToArray () [i];
					if (numImage != null) {
						numImage.color = Color.white;
						numImage.sprite = spriteNumbers [spriteNumbers.Length - 1];
					}
				}
			} else {
				//数値が桁数を超えていない
				int[] numIndexs = new int[numDigit];
				for (int i = 0; i < numDigit; i++) {
					numIndexs [i] = (int)(num2 % 10); 
					num2 = num2 / 10;
				}
				for (int i = 0; i < NumImageList.Count; i++) {
					Image numImage = NumImageList.ToArray()[i];
					if (numImage != null) {
                                                if (numDigit == 0 && i == 0) {
							//数値が0だった時の処理(1桁目は必ず0で表示)
							numImage.color = Color.white;
							numImage.sprite = spriteNumbers[0];
						} else if (i < numIndexs.Length) {
							//数値を反映する
							numImage.color = Color.white;
							numImage.sprite = spriteNumbers[numIndexs[i]];
						} else {
							if (zeroFill) {
								//0埋め
								numImage.color = Color.white;
								numImage.sprite = spriteNumbers[0];
							} else {
								//非表示
								numImage.color = Color.clear;
							}
						}
					}
				}
			}
		} else {
			if (NumImageList.Count < digit) {
				//桁数が足りないので増やす
				GameObject numImageObj = new GameObject();
				if (numImageObj != null) {
					numImageObj.name = "NumberImage"+(NumImageList.Count+1);
					numImageObj.transform.SetParent (this.transform);
					RectTransform thisRect = this.GetComponent<RectTransform> ();
					if (thisRect != null) {
						Image numImage = numImageObj.AddComponent<Image> ();
						if (numImage != null) {
							numImage.color = Color.clear;
							RectTransform numImageRect = numImageObj.GetComponent<RectTransform> ();
							if (numImageRect != null) {
								if (spriteNumbers != null && spriteNumbers.Length > 0) {
									numImageRect.sizeDelta = new Vector2(spriteNumbers[0].bounds.size.x*(thisRect.sizeDelta.y/spriteNumbers[0].bounds.size.y), thisRect.sizeDelta.y);
									if (NumImageList.Count == 0) {
										numImageObj.transform.localPosition = new Vector3 (thisRect.sizeDelta.x / 2 - numImageRect.sizeDelta.x / 2, 0);
									} else {
										Image image = NumImageList.ToArray () [NumImageList.Count - 1];
										if (image != null) {
											numImageObj.transform.localPosition = new Vector3 (image.transform.localPosition.x - numImageRect.sizeDelta.x, 0);
										}
									}
									NumImageList.Add (numImage);
								}
							}
						}
					}
				}
			} else {
				//桁数が多いので減らす
				Image image = NumImageList.ToArray()[NumImageList.Count-1];
				if (image != null) {
					NumImageList.RemoveAt(NumImageList.Count-1);
					Destroy(image.gameObject);
				}
			}
		}
	}
}

中にコメントが書いているので何をしているかわかると思いますが説明。

主な設定項目
  • num - 表示したい数値
  • digit - 表示する最大桁数
    • numがdigitの桁数を超えた場合はdigitの桁数の最大の数値を表示する。(digitが4の時、9999など)
  • zeroFill - numがdigitの桁数より小さい場合に0埋めするかどうか
    • デフォルトはfalse、0埋めしない。
  • spriteNumbers - 0〜9までの数値の画像。
    • 0〜9の順番に合うように設定してください。
中でやっている処理について
  1. NumImageList(UI.Imageの配列)の数がdigit(最大桁数)と違う時、同じ桁になるようにUI.Imageを生成したり消したりする。
  2. NumImageListとdigitの桁数が同じになった時、numの桁数を計算する(計算結果:numDigit)
  3. numDigitがdigitより大きい時、digitの桁数で最大の数値を表示。
  4. numDigitがdigit以下の時、桁ごとの数値を取得する。(123の場合は1桁目が3、2桁目が2、3桁目が1など)
  5. 1桁目からNumImageListの同じ桁の位置のUI.Imageに画像を反映する。
  6. numDigitより大きい桁の場合、zeroFillがtrueなら0埋めする。falseなら非表示にする。

実際の表示

最大桁数内で0埋めしないパターン

f:id:nanokanato:20170908141411p:plain:w300
num:1234567890
digit:13
zeroFill:false

最大桁数が3桁分余ったので0埋めしたパターン

f:id:nanokanato:20170908141356p:plain:w300
num:1234567890
digit:13
zeroFill:true

最大桁数が少ないため最大値を表示したパターン

f:id:nanokanato:20170908141421p:plain:w300
num:1234567890
digit:9
zeroFill:false

【GAS】指定の日が何の日かをWikiから取得するライブラリ、ELEWikiDateを公開

はじめに

GoogleAppScript(以下GAS)でWikiからその日が何の日かを取得するライブラリを作りましたので公開します。

要望やバグ、不明点があればコメントにてよろしくお願いします。

ELEWikiDateの導入方法

2017/09/07のバージョンではアニメキャラの誕生日のみになってますが、今後有名人の誕生日や記念日などにも対応する予定です。

ライブラリの追加に必要なプロジェクトキー

1B9gyeBky02pDM_D0Y1wENia1qRLr5xmxUxVuZ3QzMet5OujEDPA_yALW

ELEWikiDateの使用方法

誕生日のキャラクターの情報を取得
function myFunction() {
    var birthdays = ELEWikiDate.GetBirthDayChara(new Date());
    if (birthdays != null) {

    }
}

「ELEWikiDate.GetBirthDayChara([日付])」で指定した日付が誕生日のキャラクター情報の配列が取得できます。

誕生日の人物の情報を取得
function myFunction() {
    var birthdays = ELEWikiDate.GetBirthDayNonFiction(new Date(), "声優");
    if (birthdays != null) {

    }
}

「ELEWikiDate.GetBirthDayChara([日付],[カテゴリ])」で指定したカテゴリに該当する指定の日付が誕生日の人物の情報の配列が取得できます。