2013年12月24日火曜日

Unityで複数カメラがあると、OnMouseDownなどが想定外の座標で反応する

Unityでゲーム作成していて、OnMouseDown,OnMouseEnter,OnMouseOver,OnMouseExit が
想定外の場所で反応することがありました。

意味不明な現象でしたが、原因は「カメラが複数あったこと」でした。

私の作り方では、まずシーン遷移をコントロールするために、
DontDestroyOnLoad で決して破棄されないオブジェクトを一つ作ります。
そのオブジェクトにくっついたスクリプトが
Application.LoadLevel や Application.LoadLevelAdditive を行うことで
制御を行っていたのですが、この「非破壊オブジェクト」は、何の気なしにカメラでやってたんですね。

そのおかげで、「制御されたシーンにおいてあるメインカメラ」と「非破壊オブジェクトとなったカメラ」の
ふたつがあることで、まったく想定外の座標でも OnMouseDown などが反応する、ということになってしまいました。



Unityには無害な空オブジェクトを作る Create Empty というメニューがありますので、
ちゃんとこちらを利用しましょう、という反省でした。

2013年12月17日火曜日

UnityでAnimation Event中にSetActive(false)するとフリーズする

UnityにはAnimationとAnimatorってのがあります。
AnimationにはIsPlayingのような再生確認のメソッドが用意されてますけど、
Animatorのほうにはそれらしきものがないんですよね。
スプライトアニメーションを作っても、終了タイミングを検知する方法が
用意されてないようです。


このように、終了フレームのところに Add Event Function して
呼び出すことで、終了検知するようにしてみたのですが、
とんでもないことがおきました。


function End(){
 gameObject.SetActive(false);
}

追加した End() のなかでSetActive(false) を呼び出すと、いきなりUnityがフリーズするのです。
イベントによる関数呼び出し中にオブジェクト自身を停止するのが悪い、ってことなんでしょうけど
いきなりフリーズはないわ~。
エラーメッセージじゃなくてフリーズするせいで、原因特定にかなり時間がかかってしまいました。


function End(){
 Destroy(gameObject);
}

Destroyでは大丈夫。正常にオブジェクト破棄できました。

UnityにVuforiaを入れて画面左下にしか表示されないバグの原因判明

原因がわかりました。
Unity4.3(Windows)、Vuforia2.6.7でやってたのですが、
どうもこれはバージョンの相性が悪かったようです。

Unityはいつでも旧バージョンがDLできてインストール出来るようになっていますが、
Unity4.2.2を入れてみたら…


このように、正常に全画面表示できました。
ARのためにいったんUnityのバージョンを4.2.2に落とすか、
Vuforia側の対応を待てばいいようですね。

2013年12月6日金曜日

UnityにVuforiaを入れてAndroidでARアプリ

http://nvtrlab.jp/column/1-1
この連載コラムの1~5を参考にして、Win7のUnityでAndroidアプリとしてAR機能をつけてみました。


AR機能自体は驚くほどあっさりと実装できたのですが、なにかカメラの範囲がおかしい。





真っ黒の部分は完全に無反応。
左下のエリアだけカメラが機能しているという不思議な状態になりました。

ARカメラの設定項目をひたすらいじりまくっても、一向にカメラ範囲は変化してくれません。
ググっても全く情報がでてきません。

なにこれ!

2013年8月16日金曜日

jQuery mobile のcheckboxでcheckedが固定しちゃうバグらしきもの

jQuery mobile で、 <input type='checkbox' id='check'> なタグに



jQuery('#check').click(function(){
 alert(jQuery('#check').attr('checked'));
});

とすると、そのチェックボックスをクリックするたびに
「checked」「undefined」が交互に返ってきます。

ですが、jQuery mobile のAPIリファレンスにあるように、
スクリプトで初期状態でチェックを入れちゃうと…



jQuery('#check').attr('checked',true).checkboxradio('refresh');
jQuery('#check').click(function(){
 alert(jQuery('#check').attr('checked'));
});

このような記述をすると、クリックしても毎回「checked」が
返ってくるようになってしまいます。
見た目のオンオフはちゃんと切り替わってるのですけどね。

なお、jQuery 1.9.1 , jQuery mobile 1.3.2 です。
なんだかバグくさい挙動なんですが、これ仕様なんですかね?



jQuery('#check').attr('checked',true).checkboxradio('refresh');
jQuery('#check').click(function(){
 alert(jQuery('#check').next().find(":eq(0) :eq(1)").hasClass("ui-icon-checkbox-on"));
});

しょうがないので、このように、jQuery mobile が構成しているアイコンがON用かどうかで
判断するようにしたら、しっかり「true」「false」が返ってくるようになりました。
バッドノウハウな感じで嫌ですね。
公式APIリファレンスさん、正しいチェック状態の取得方法を書いててくださいよ~。

2013年8月2日金曜日

unity-webview の表示ページを追加スクリプトでコントロールする

unity-webviewはUnity画面上にWeb表示エリアを作り出す無料プラグインです。
このunity-webviewには、javascriptで画面を制御する機能があります。






SampleWebView.csを見ると、こういう記述があります。
この場所の指示で、表示しているWeb画面に対して自由に制御することができます。
ただ、ここにそのまま直接記述すると、javascriptの制御文を文字列で
与えなければならないので、やっかいですよね。
そこで、この文字列を外部ファイル読み込みにします。




function Start(){

 var cb0 : WWW = new WWW("http://yourserver.com/cb0.js");
 yield cb0;
 var cb1 : WWW = new WWW("http://yourserver.com/cb1.js");
 yield cb1;

 var Url = 'https://twitter.com/';
 var webViewObject = new GameObject("WebViewObject").AddComponent(WebViewObject);

 webViewObject.Init(function(msg){
  Debug.Log(String.Format("CallFromJS[{0}]", msg));
 });

 webViewObject.LoadURL(Url);
 webViewObject.SetMargins(20,20,20,Screen.height/2);// このタイミングでサイズ指定
 webViewObject.SetVisibility(true);

 switch (Application.platform) {
 case RuntimePlatform.OSXEditor:
 case RuntimePlatform.OSXPlayer:
 case RuntimePlatform.IPhonePlayer:
  webViewObject.EvaluateJS(cb0.text);
  break;
 }
 webViewObject.EvaluateJS(cb1.text);
}

このように外部ファイルの読み込みという形にすることで、素直なjavascript記述が利用できます。
なお、SampleWebView.csにはなかったのですが、SetMarginsというメソッドで、
Unity内でのweb画面の表示範囲を指定することができます。
SetMarginsの指定はLoadURL以降じゃないと無効のようです。




window.addEventListener('load', function() {
 window.Unity = {
  call:function(msg) {
   var iframe = document.createElement('IFRAME');
   iframe.setAttribute('src', 'unity:' + msg);
   document.documentElement.appendChild(iframe);
   iframe.parentNode.removeChild(iframe);
   iframe = null;
  }
 }
}, false);

cb0.jsの内容はこんな感じで済みます。
cb1.jsも同様にできますが、ここで、ちょっと記述を変えてみました。




window.addEventListener('load', function(){
 var id = setInterval(function(){
  if (document.getElementsByClassName("blue").length){
   Unity.call('clicked');
   clearInterval(id);
   var p = document.getElementsByClassName("black")[0];
   p.parentNode.removeChild(p);
  }
 },300);
}, false);

このように、要素の準備が揃うのを待ち、blackクラスの要素を削除してみますと、






スクリプトの処理によって、黒い「ログインボタン」を削除することができました。


EvaluateJSメソッドを使いこなして、Web表示機能の夢が広がりますね。

2013年7月31日水曜日

Let's Tweet In Unity を Twitter API 1.1 で動かす

Unityでtwitter関連の動作を調査した人は、ほぼ必ず、無料のLet's Tweet In Unityにたどり着いたことでしょう。
しかし、2013/7/31現在、Unityの新規プロジェクトにインポートしても、正常に動きません。
私は2013/6/10にLet's Tweet In Unityの動作確認をしていたのでしたが、
なんとまさにその翌日、2013/6/11にTwitter API 1.0が停止し、
API 1.1に対応していないLet's Tweet In Unityは動かなくなる、という事態になったのです。





今はデモを動作させても、こんなエラーが出るようになってしまいました。

原因は、Let's Tweet In Unityの内部コードがAPI 1.0を利用していたせいです。
Let's Tweet In Unity内部にOAuth認証のコードはもともと揃っているので、
API 1.1を使用するように書きなおしてみました。

Twitter.csの176行目に、URL指定があります。

private static readonly string PostTweetURL = "http://api.twitter.com/1/statuses/update.xml?status={0}";

これを、API 1.1用に変更します。

private static readonly string PostTweetURL = "https://api.twitter.com/1.1/statuses/update.json?status={0}";

httpがhttpsになり、バージョン指定の数字が1.1になり、最後に拡張子がxmlからjsonに変わります。
これだけで動くようになれば楽なのですが、残念ながらそうはいきませんでした。




さらに、193~201行目の


// Need to fill body since Unity doesn't like an empty request body.
byte[] dummmy = new byte[1];
dummmy[0] = 0;

// HTTP header
Hashtable headers = new Hashtable();
headers["Authorization"] = GetHeaderWithAccessToken("POST", url, consumerKey, consumerSecret, response, parameters);

WWW web = new WWW(url, dummmy, headers);

を、こう変更します。

WWWForm form = new WWWForm();
string str = GetHeaderWithAccessToken("POST", url, consumerKey, consumerSecret, response, parameters);
str = str.Replace("\"","");
string[] p = str.Split(',');
p[0] = "status=" + UrlEncode(text);
foreach (string obj in p){
 string[] tmp = obj.Split ('=');
 form.AddField(tmp[0], tmp[1]);
}
WWW web = new WWW("http://yourserver.com/tweet.php", form);

原型を留めてないぐらい変えました。
唐突に出てきた yourserver.com というのは、自前でサーバを用意する必要があるということです。
API 1.1 での投稿に必要なパラメータは揃っているにも関わらず、なぜか直接
api.twitter.comへの送信を行なっても、どうしても成功しませんでした。
そこで、自前サーバにパラメータを送り、phpでtwitterにパラメータを送信してもらうという形をとりました。







$str = "";
foreach ($_POST as $key => $value){
 $str .= $key."=".$value."&";
}
$str = substr($str,0,strlen($str)-1);
$opts = array(
  'http'=>array(
    'method'=> 'POST',
    'content'=> $str
  )
);

$context = stream_context_create($opts);
file_get_contents("https://api.twitter.com/1.1/statuses/update.json",0,$context);

これが、そのサーバ側のtweet.phpの内容です。
パラメータは揃っているので、形を整えてapi.twitter.comに送信するだけですね。



Array
(
    [status] => test2
    [oauth_consumer_key] => zUAiZ5W0sAnLTrbwjEibGA
    [oauth_nonce] => 178919DE
    [oauth_signature_method] => HMAC-SHA1
    [oauth_timestamp] => 1375233480
    [oauth_token] => 126559571-GQoRFeEkeum8kB1PqIfJiBjj0L8MH36Iumsy1Vgg
    [oauth_version] => 1.0
    [oauth_signature] => 3ZpEhBQDv0rYmPCNapxbd2yoWdY%3D
)

phpに届いた $_POSTの内容を print_r($_POST); で表示すると、こうなります。
こういうデータになってれば大丈夫ということです。





Array
(
    [http] => Array
        (
            [method] => POST
            [content] => status=test2&oauth_consumer_key=zUAiZ5W0sAnLTrbwjEibGA&oauth_nonce=178919DE&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1375233480&oauth_token=126559571-GQoRFeEkeum8kB1PqIfJiBjj0L8MH36Iumsy1Vgg&oauth_version=1.0&oauth_signature=3ZpEhBQDv0rYmPCNapxbd2yoWdY%3D
        )
)

$optsの内容を print_r($opts); で表示すると、こうなります。





以上の作業で、このように無事にツイートできるようになりました。

2013年7月25日木曜日

ハイスコアをデータベースに記録&更新

もぐらたたきゲーム系のアプリを開発していますが、
得点をプレイヤー全員で共有するため、
プレイヤーごとの最高得点をデータベースに保存しようとしています。

この場合、プレイヤーのIDを判定して、初めて見るIDならinsert文、
既にあるIDなら得点の判定をして高得点ならupdate文を実行、となるわけです。
mysqlにはIDが既知かどうかに基づいて自動的にinsertとupdateを実行する構文があります。
と言っても、普通のinsert文とupdate文を、 on duplicate key というキーワードで
つないだだけで、ほぼ2個書いてるようなものですが。


insert into もぐらハイスコア set id=3,スコア=100
 on duplicate key
 update id=3,スコア=greatest((select スコア from もぐらハイスコア where id=3),300)

IDが3,初回得点が100点、2回めの得点が300点という仮定でSQL文を作ってみました。

これをphpMyAdminで実行してみると…





このようなエラーが出てしまいます。
テーブル名に日本語を使うと、エラーメッセージで文字化けが起きるんですよね。
なんかうまい設定ないんでしょうか?

で、このエラー自体は、update中にそのテーブルにselectを使用してるのがまずい、ということでした。





このように、副問い合わせのselectでのテーブルに仮名をつけてあげる事で、
エラーなく実行できるようになりました。

2013年7月24日水曜日

外務省の要人往来記録



外務省サイト各国地域情勢というメニューから国名を選んで、二国間関係というリンクをクリックすると、
主要要人往来というデータが見れます。





例えばインドなら、
往(日本からインド) 2012年4月に自見金融・郵政改革担当大臣が行ったのが最後です。
来(インドから日本) 2013年4月にチダムバラム財務相が来たのが最後です。

アジア各国の、最後に行った要人往来の日付をまとめ、グラフ化してみました。





まず、単純に日付を入力していきます。
パキスタンとバングラデシュは、残念ながら月が不明だったので、除外します。
データは集まりましたが、このままだと取り扱いづらいと気づいたので、加工していきます。





シートの計算を利用して、年度の4桁の数字を取り出します。





同様に、月のデータを取り出します。





2013年7月から見て何ヶ月前だったのか、計算していきます。





往・来 の両方をこのように計算して、データが揃いました。





散布図グラフにすると、こうなりました。
ブルネイとタイが全く同じ数値だったので、タイが見えなくなってしまいました。

韓国と中国との要人往来が当分ない、つまり現日本政府はこの2国を
重視していないことが伺えますね。

2013年7月22日月曜日

参議院選挙結果のグラフ


自民大勝で衆参ねじれ解消 民主大敗


こんなニュースがありましたが、結果を文字だけで表示されてたので、グラフ化してみました。

まず、数字の報告が非常に見づらいので、改行します。


 ▽自民党は選挙区が47議席、比例代表が18議席で、合わせて65議席、
 ▽民主党は選挙区が10議席、比例代表が7議席で、合わせて17議席、
 ▽日本維新の会は選挙区が2議席、比例代表が6議席で、合わせて8議席、
 ▽公明党は選挙区が4議席、比例代表が7議席で、合わせて11議席、
 ▽みんなの党は選挙区が4議席、比例代表が4議席で、合わせて8議席、
 ▽共産党は選挙区が3議席、比例代表が5議席で、合わせて8議席、
 ▽社民党は比例代表で1議席、
 ▽沖縄社会大衆党は選挙区で1議席、
 ▽無所属は選挙区で2議席でした。



こうなります。このデータを、Googleスプレッドシートに打ち込みます。





こうなります。与党勢力がまとまっている方がわかりやすいので、
1行目を自民、2行目を公明にしました。





データの範囲を選択した状態で、上のメニューの挿入からグラフを選ぶと、
グラフが作成できます。





円グラフを選択したら、このようなグラフになりました。

2013年7月20日土曜日

Bloggerで画像の回り込みを制御する

Bloggerで画像の回り込みの解除方法を探すと、こんな記事が見つかります。

Bloggerの記事で画像の回り込みを解除



<div style="clear: both;"></div>

とタグを打つことで、文章の途中から
いつでも回り込み解除できるよ、という解説ですね。



ですが実際にブログ記事を書く時は、

画像の回り込みをさせたまま文章を書くか、
最初から回りこみなしで文章を書くか、

の2択ではないでしょうか。
ググっても、Blogger自身の機能でこの違いをコントロールする方法のことを
解説してある記事は見当たりませんでした。



実は、Bloggerではこの2つを、HTMLタグなど打たずにコントロール可能です。





このように編集画面で画像を貼り付けると、デフォルトで中央表示となっています。





画像を選択して、画像下部に現れたメニューのを押すと…






このように、文字が回りこみます。





さきほどの画像中央の状態で、上部にあるメニューの文字揃えで左揃えにすると…






回りこみは発生しません。


どうしてほぼ同じような操作で結果が違ってしまうのかわかりませんが、
とにかくこれを知っていれば、画像の回り込みで苦労することはなくなるでしょう。

2013年7月19日金曜日

Bloggerにtwitterウィジェットを追加する方法

このスクリプトグラフというブログはGoogleが提供している
Bloggerというブログシステムを使っているのですが、
Bloggerにtwitterウィジェットを追加する方法を検索したら、
こんなページが出てきます。

BloggerのガジェットにTwitterのウィジェットを追加してみた

ですが、実際やろうとすると、そのリンクがなくて、出来ません。
Web界隈じゃよくあることですね。
ページ構成が変わったせいで、昔の情報では出来なくなってしまったのです。


というわけで、2013年7月現在で出来る方法を書いておきます。
大雑把な流れは、

1.twitterの公式ウィジェットを作成する。
2.BloggerのガジェットHTML/JavaScriptでtwitterウィジェットのコードを入力する。

これだけです。
ガジェットとウィジェットってなんで呼称が微妙に違うんでしょうね?
英語は苦手なんでわかりません。





まずは、twitterのホーム画面から。
右上のギヤマークのメニューから、設定をクリックします。





左のメニューでウィジットを選び、右上の新規作成ボタンをクリックします。





ウィジェットの設定画面になりますので、見た目を色々調整したら、
左下のウィジェットを作成ボタンをクリックします。



右下にコードが出てきますので、コードをコピーしておきます。

twitter側の作業はここまで。
ここからBlogger側です。





Bloggerのマイブログ画面です。左のメニューでレイアウトを選び、
追加したい場所のガジェットを追加をクリックします。





ガジェット選択するウィンドウがポップアップしてきます。
この中の、基本ガジェットが選択状態になっているはずです。





HTML/JavaScriptのガジェットを選択します。




コピーしていたtwitterのウィジェットコードを貼り付けたら、
保存をクリックします。






これで完了です。無事にサイドバー部分にtwitterウィジェットが入りましたね。

2013年7月18日木曜日

スクエニアカウントハッキングの疑いのあるメール

GMailの迷惑メールフォルダをなんとなしに見てたら、こんなメールが来てました。




赤丸で示したリンクですが、マウスカーソルを合わせると、
矢印のように、よく見たらスクエニ公式とドメインが違う場所へのリンクになっています。

公式ドメインにそっくりな偽ドメインを用意して、IDとパスワードを盗む…。
これが、アカウントハッキングの典型的な手法です。


ちょっと前に、楽天銀行のインターネットバンキングで不正送金が行われた
というニュースがありました。

これもおそらく、まったく同じ手口だったはずです。
不正アクセスする人は、オンライン銀行のような、いかにもセキュリティが
堅そうなところを直接狙うわけではありません。
そこのアカウント所有者と思わしき人に、この手の危機感を煽るメールを
送りつけ、間抜けが引っかかるのを待っているのです。


こういった手口があることを知ることで、自分自身のセキュリティ強化になります。
知識として蓄えておきましょう。