2014年12月14日日曜日

【Unity、PUN】Photon Unity Networkingのコールバックメソッド一覧


PhotonCloudを使えばUnityで簡単にオンラインゲームが作れますね。

接続に失敗したとき、部屋に誰か入ってきたとき、など
ことあるごとに呼び出されるコールバックメソッドたち。
インテリセンスも効かないし毎度調べるのも面倒なので一覧にしてみた。

誰かが作ってたと思うけど自分用にまとめ直し。
ちなみに超適当和訳なので怪しいと思ったら原文読んでください。
(PhotonNetworkingMessage列挙型のXMLコメントに全て書かれてます)

もうちょい使ったらよく使うもの同士まとめた記事も書こうとおもう。

OnConnectedToPhoton

/// <summary>
/// サーバーへの初期接続が確立したとき呼び出されます。
/// </summary>
/// <remarks>
/// このコールバックはサーバーとの通信が可能かどうかの判断をするためにみ有効です。
/// ほとんどの場合、OnFailedToConnectToPhoton()もしくはOnDisconnectedFromPhoton()で十分です。
/// これが呼び出ばれたときには低レベルでの接続は確率されています。
/// この後、PUNはAppIDやユーザー、その他の情報をバッググラウンドで送信します。
/// また、マスターサーバーからゲームサーバーへ移った際には呼び出されません。
/// </remarks>
void OnConnectedToPhoton(){}

OnLeftRoom

/// <summary>
/// ローカルユーザー、クライアントが部屋を出たときに呼び出されます。
/// </summary>
/// <remarks>
/// 部屋を出るとき、PUNはあなたをマスターサーバーへ移動させます。
/// あなたがロビーを使う前、もしくは部屋に入る(作る)前、
/// OnJoinedLobby() もしくは OnConnectedToMaster() が再度呼び出されます。
/// </remarks>
void OnLeftRoom(){}

OnMasterClientSwitched

/// <summary>
/// マスタークライアントが退場し、新しいマスタークライアントに切り替わった後に呼び出されます。
/// 古いマスタークライアントは既にプレイヤー一覧からは削除されています。
/// </summary>
/// <remarks>
/// このクライアントが部屋に入ったときには、このコールバックは呼び出されません。
/// </remarks>
void OnMasterClientSwitched(PhotonPlayer newMasterClient){}

OnPhotonCreateRoomFailed

/// <summary>
/// CreateRoom()の呼び出しが失敗した場合に呼び出されます。
/// オプションパラメータによってエラーコードおよびエラーメッセージを提供します。
/// </summary>
/// <remarks>
/// よくあるのは「指定した部屋名がすでに使用されている」などです。
/// PhotonNetwork.logLevel を PhotonLogLevel.Informational 以上二している場合、
/// PUNはいくつかの情報をログ出力します。
/// </remarks>
void OnPhotonCreateRoomFailed(){}
void OnPhotonCreateRoomFailed(object[] codeAndMsg){
 //codeAndMsg[0]は エラーコード です。(int)
 //codeAndMsg[1]は デバッグメッセージ です。(string)
}

OnPhotonJoinRoomFailed

/// <summary>
/// JoinRoom()の呼び出しが失敗した場合に呼び出されます。
/// オプションパラメータによってエラーコードおよびエラーメッセージを提供します。
/// </summary>
/// <remarks>
/// よくあるのは「部屋が存在しない」「すでに満員」などです。
/// PhotonNetwork.logLevel を PhotonLogLevel.Informational 以上二している場合、
/// PUNはいくつかの情報をログ出力します。
/// </remarks>
void OnPhotonJoinRoomFailed(){}
void OnPhotonJoinRoomFailed(object[] codeAndMsg){
 //codeAndMsg[0]は エラーコード です。(int)
 //codeAndMsg[1]は デバッグメッセージ です。(string)
}

OnCreatedRoom

/// <summary>
/// このクライアントが部屋を作成し、入室する際に呼び出されます。
/// この後、OnJoinedRoom() は同様に呼び出されます。
/// </summary>
/// <remarks>
/// このコールバックは部屋を作成(PhotonNetwork.CreateRoom)したクライアント上でのみ、呼び出されます。
/// クライアントはいつでも閉じる(もしくは接続を切る)可能性があり、
/// タイミングによってはOnCreatedRoomは呼び出されません。
/// 
/// もし部屋に関する特定のプロパティや"開始信号"が必要な場合、
/// 部屋の状態を確認するためにマスタークライアントを作り、
/// OnMasterClientSwitched()を実装するのが安全です。
/// </remarks>
void OnCreatedRoom(){}

OnJoinedLobby

/// <summary>
/// マスターサーバー上のロビーに入った際に呼び出されます。
/// 部屋のリストが更新されるとOnReceivedRoomListUpdate()が呼び出されます。
/// </summary>
/// <remarks>
/// 注意:PhotonNetwork.autoJoinLobby が false の場合、
/// OnConnectedToMaster() が呼び出され、部屋のリストが利用可能になることはありません。
/// 
/// ロビーに居る間、部屋のリストは自動的に一定間隔で更新されます。
/// (間隔はあなたが変更する事はできません。)
/// </remarks>
void OnJoinedLobby(){}

OnLeftLobby

/// <summary>
/// ロビーを退出した際に呼び出されます。
/// </summary>
/// <remarks>
/// (よくわからんかった。以下原文)
/// When you leave a lobby, [CreateRoom](@ref PhotonNetwork.CreateRoom) and [JoinRandomRoom](@ref PhotonNetwork.JoinRandomRoom) 
/// automatically refer to the default lobby. 
/// </remarks>
void OnLeftLobby(){}

OnDisconnectedFromPhoton

/// <summary>
/// Photonサーバーから切断した際に呼び出されます。
/// </summary>
/// <remarks>
/// いくつかのケースでは、OnDisconnectedFromPhoton()の前に他のコールバックが呼び出されます。
/// 例)OnConnectionFail() や OnFailedToConnectToPhoton()
/// </remarks>
void OnDisconnectedFromPhoton(){}

OnConnectionFail

/// <summary>
/// 何らかの原因で接続が失敗した際に OnDisconnectedFromPhoton() に続いて呼び出されます。
/// </summary>
/// <remarks>
/// サーバーへ到達できない場合は代わりに OnFailedToConnectToPhoton()
/// が呼び出され、エラーの原因はStatusCodeとして提供されます。
/// </remarks>
void OnConnectionFail(DisconnectCause cause){}

OnFailedToConnectToPhoton

/// <summary>
/// Photonサーバーへの接続が確立される前に接続呼び出しが失敗すると呼び出されます。
/// OnDisconnectedFromPhoton() に続いて呼び出されます。
/// </summary>
/// <remarks>
/// (よくわからんかった。以下原文)
/// OnConnectionFail only gets called when a connection to a Photon server was established in the first place.
/// </remarks>
void OnFailedToConnectToPhoton(DisconnectCause cause){}

OnReceivedRoomListUpdate

/// <summary>
/// 部屋のリストが更新された場合に呼び出されます。
/// 新規か更新かの違いはありません。
/// これはマスターサーバー上のロビー内でのみ呼び出されます。
/// </summary>
/// <remarks>
/// 全てのタイプのロビーがクライアントへの部屋リスト通知を提供するわけではありません。
/// 中にはサーバーサイドでのマッチメイキングに特化し、サイレントのものもあります。
/// 
/// PUNでは PhotonNetwork.GetRoomList() による部屋リストの取得が可能です。
/// 取得される各項目はカスタムプロパティを含んだRoomInfoです。
/// これらの情報はロビー上で部屋を作る際に定義したものです。
/// </remarks>
void OnReceivedRoomListUpdate(){}

OnJoinedRoom

/// <summary>
/// 部屋に入った際に呼び出されます。(部屋作成時もしくは参加時)
/// これは全てのクライアント(マスタークライアント含む)で呼び出されます。
/// </summary>
/// <remarks>
/// このメソッドはプレイヤーキャラクタの生成のためによく使われます。
/// もしあなたがより"積極的に"呼び出す必要がある場合は
/// 代わりに PhotonView.RPC を利用する事が出来ます。
/// 
/// これが呼び出されたとき、
/// あなたは PhotonNetwork.playerList を介して
/// 部屋に存在するプレイヤーにアクセスできます。
/// また、Room.customProperties を介して部屋のカスタムプロパティも利用できます。
/// ゲームを開始できる人数がいるかどうかは、Room.playerCount で確認できます。
/// </remarks>
void OnJoinedRoom(){}

OnPhotonPlayerConnected

/// <summary>
/// リモートプレイヤーが部屋に入ったときに呼び出されます。
/// 入室したプレイヤーは既に PhotonNetwork.playerList に追加されています。
/// </summary>
/// <remarks>
/// もしあなたのゲームが特定の人数で始まる場合、このコールバックが役立ちます。
/// Room.playerCount を確認し、開始できるか確認しましょう。
/// </remarks>
void OnPhotonPlayerConnected(PhotonPlayer newPlayer){}

OnPhotonPlayerDisconnected

/// <summary>
/// リモートプレイヤーが部屋を抜けた際に呼び出されます。
/// 退室したプレイヤーは既に PhotonNetwork.playerList から除外されています。
/// </summary>
/// <remarks>
/// あなたのクライアントが PhotonNetwork.leaveRoom を呼び出した場合、
/// PUNは残ったクライアント上でこのメソッドを呼び出します。
/// リモートクライアントの接続が切れると、
/// 数秒後のタイムアウトの後、このコールバックが呼び出されます。
/// </remarks>
void OnPhotonPlayerDisconnected(PhotonPlayer otherPlayer){}

OnPhotonRandomJoinFailed

/// <summary>
/// JoinRandom() の呼び出しが失敗した際に呼び出されます。
/// オプションパラメータによってエラーコードおよびエラーメッセージを提供します。
/// </summary>
/// <remarks>
/// よくあるのは「全ての部屋が満員」「一つも部屋がない」などです。
/// もし(JoinLobbyやTypedLobbyを介して)複数のロビーを使用している場合、
/// 他のロビーは接続可能な部屋を持っているかもしれません。
/// PhotonNetwork.logLevel を PhotonLogLevel.Informational 以上二している場合、
/// PUNはいくつかの情報をログ出力します。
/// </remarks>
void OnPhotonRandomJoinFailed(){}
void OnPhotonRandomJoinFailed(object[] codeAndMsg){
 //codeAndMsg[0]は エラーコード です。(int)
 //codeAndMsg[1]は デバッグメッセージ です。(string)
}

OnConnectedToMaster

/// <summary>
/// PhotonNetwork.autoJoinLobby が false の状態で、
/// マスターサーバーへ接続および認証が完了した際に呼び出されます。
/// </summary>
/// <remarks>
/// もしあなたが PhotonNetwork.autoJoinLobby に true をセットしている場合、
/// これの代わりに OnJoinedLobby() が呼び出されます。
/// 
/// あなたがロビーにいない場合でも、部屋を作ったり参加したりできます。
/// デフォルトのロビーはその場合に用いられます。
/// PhotonNetwork.joinLobby によってロビーに入っている場合を除いて、
/// 利用可能な部屋のリストが更新される事はありません。
/// </remarks>
void OnConnectedToMaster(){}

OnPhotonSerializeView

/// <summary>
/// PhotonViewが定期的に同期するデータをカスタマイズするために実装します。
/// PhotonViewによってネットワークアップデートが観察されるたびに呼び出されます。
/// </summary>
/// <remarks>
/// このメソッドはPhotonViewによって監視対象にされているスクリプト上で呼び出されます。
/// PhotonNetwork.sendRateOnSerialize はこのメソッドが呼び出される頻度に影響します。
/// PhotonNetwork.sendRate はこのクライアントによってパッケージが送信される頻度に影響します。
/// 
/// このメソッドの実装によって、PhotonViewが定期的に同期するデータをカスタマイズすることができます。
/// あなたのコードは クライアントから何が送信され、何を受信するか、を定義することができます。
/// 
/// 他のコールバックとは異なり、PhotonView.observed に設定されたScriptでのみ呼び出されます。
/// 
/// このメソッドを利用するためには、PhotonStreamが必要不可欠です。
/// 書き込みを行う側のクライアント(PhotonStream.isWriting == true)と
/// 読み取りを行う側のクライアントに別れてデータの送受信を行います。
/// 
/// もし書き込みを行わないと、PUNは更新をスキップします。
/// これを利用すればネットワークの帯域幅を節約する事が出来ます。
/// 
/// 送信を行わない場合、受信側はOnPhotonSerializeViewが呼び出されていないことに注意してください。
/// そのため「x回の受信ごとに」といった処理はできません。
/// </remarks>
void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info){}

OnPhotonInstantiate

/// <summary>
/// PhotonNetwork.Instantiate によって GameObject(とその子供)が生成された際に呼び出されます。
/// </summary>
/// <remarks>
/// PhotonMessageInfoパラメータは「誰が」「いつ」作成したかを提供します。
/// (「いつ」は PhotonNetworking.time に基づきます。)
/// </remarks>
void OnPhotonInstantiate(PhotonMessageInfo info){}

OnPhotonMaxCccuReached

/// <summary>
/// CCUの制限(一時的な物)によってサーバーへの接続が切断された際に呼び出されます。
/// </summary>
/// <remarks>
/// これが発生した場合は、一定時間をおいて再試行してください。
/// これが発生した場合、すでに切断されているため部屋に入ることはできません。
///
/// あなたは新しいライセンスの適用や拡張サブスクリプションによってCCUの制限を上げる事が出来ます。
/// CCU制限に達したとき、PhotonCloudはあなたにメールを送信します。
/// また、Webページ上のダッシュボードでも確認できます。
/// </remarks>
void OnPhotonMaxCccuReached(){}

OnPhotonCustomRoomPropertiesChanged

/// <summary>
/// 部屋のカスタムプロパティが変更された際に呼び出されます。
/// 引数のpropertiesThatChanged には Room.SetCustomProperties() 
/// でセットされた全てのプロパティが含まれます。
/// </summary>
/// <remarks>
/// v1.25以降、このメソッドは1つの引数を持ちます。(Hashtable propertiesThatChanged)
/// ローカル上での変更においても、カスタムプロパティの変更は
/// 必ずRoom.SetCustomPropertiesによって行われる必要があります。
/// </remarks>
void OnPhotonCustomRoomPropertiesChanged(Hashtable propertiesThatChanged){}

OnPhotonPlayerPropertiesChanged

/// <summary>
/// カスタププレイヤープロパティが変更された際に呼び出されます。
/// プレイヤーと変更されたプロパティは object[] として渡されます。
/// </summary>
/// <remarks>
/// v1.25以降、このメソッドは1つの引数を持ちます。(object[] playerAndUpdatedProps)
/// これには2つのエントリが含まれます。
/// [0] は影響を受けた PhotonPlayer です。
/// [1] は変更されたプロパティのハッシュテーブルです。
/// (私たちはUnityのGameObject.SendMessageの仕様により、object[]を使用している。)
/// 
/// ローカル上での変更においても、カスタムプロパティの変更は
/// 必ずPhotonPlayer.SetCustomPropertiesによって行われる必要があります。
/// </remarks>
void OnPhotonPlayerPropertiesChanged(object[] playerAndUpdatedProps){
    PhotonPlayer player = playerAndUpdatedProps[0] as PhotonPlayer;
    Hashtable props = playerAndUpdatedProps[1] as Hashtable;
    //...
}

OnUpdatedFriendList

/// <summary>
/// サーバーが FindFriends リクエストに対してPhotonNetwork.Friendsを更新し、
/// 応答を送信した際に呼び出されます。
/// </summary>
/// <remarks>
/// フレンドリストはPhotonNetwork.Friendsによって
/// 名前の一覧、オンライン状態、(もし入っているなら)部屋などを取得できます。
/// </remarks>
void OnUpdatedFriendList(){}

OnCustomAuthenticationFailed

/// <summary>
/// カスタム認証に失敗し、切断された際に呼び出されます。
/// </summary>
/// <remarks>
/// ユーザーの入力や不正なトークン/シークレットなどによってカスタム認証は失敗することがあります。
/// もし認証に成功した場合、このメソッドは呼ばれません。
/// (一般的には) OnJoinedLobby() か OnConnectedToMaster() を実装します。
/// 
/// ゲームの開発中、それはサーバーサイドの謝った設定により失敗することがあります。
/// そのような場合は、デバッグメッセージをログに記録する事は非常に重要です。
/// 
/// あなたがあなたのアプリに対して(ダッシュボード上で)カスタム認証を設定しない限り、これが呼ばれる事はありません。
/// </remarks>
void OnCustomAuthenticationFailed(string debugMessage){}

OnWebRpcResponse

/// <summary>
/// PUNがWebRPCを受信した際に呼び出されます。
/// PhotonNetwork.WebRPC を参照してください。
/// </summary>
/// <remarks>
/// 重要:もしPhotonがあなたのWebServiceに到達できるならresponse.ReturnCodeは0です。
/// 応答の内容はあなたのWebServiceが送信した物です。
/// あなたはこれを元にWebResponseのインスタンスを生成できます。
///
/// 例)WebRpcResponse webResponse = new WebRpcResponse(operationResponse);
///
/// 注意:OperationResponseクラスは下記名前空間に含まれています。
/// using ExitGames.Client.Photon; 
/// 
/// PhotonからのOperationResponse.ReturnCodeは以下の通りです。
///  0 : OK
/// -3 : Webサービスが未設定です。(ダッシュボード、WebHooksを確認)
/// -5 : Webサービスは現在RPCのためのpath/nameを持っていません。(少なくともAzureのため)
/// </remarks>
void OnWebRpcResponse(OperationResponse response){}

OnOwnershipRequest

/// <summary>
/// PhotonViewの現在の所有者であるあなたに対して、
/// 他のプレイヤーが所有権を要求したときに呼び出されます。
/// </summary>
/// <remarks>
/// viewAndPlayerには2つのエントリが含まれます。
/// [0] は対象のPhotonViewです。
/// [1] は要求を行ったPhotonPlayerです。
/// </remarks>
void OnOwnershipRequest(object[] viewAndPlayer){
 PhotonView view = viewAndPlayer[0] as PhotonView;
 PhotonPlayer requestingPlayer = viewAndPlayer[1] as PhotonPlayer;
 //...
}

2014年12月11日木曜日

【Unity、UniRx】画面の向きが変化したことを検出する


@Baiteen

ReactiveExtensionsがUnityでも使えるってのを今更知ったので、今更ながら使ってみた。その9。

やりたいこと

ReactiveExtensionsを使って画面の向きが変化したことを検出する。

やり方

Input.deviceOrientationで現在のデバイスの画面の向きを取れるので、これが変化した時だけ値を発行させる。
ソース全体はこちらのGithub

これだと実機で試してねってなっちゃうので、せっかくなのでWebPlayerでも雰囲気確認できるようにCube置いてみた。
Hosted by UnityRoom.com

ついでにトースト表示クラスをシングルトンにしてみた。
CanvasとEventSystemをスクリプトからする方法がわからない。

2014年11月30日日曜日

【Berkshelf】The resource at 'hoge' does not appear to be a valid cookbook. Does it have a metadata.rb?

まだまだエラーは続くよー。
この本の通り書いててこの本の通り書いててvendoring cookbookしたらエラーした。
CakePHPで学ぶ継続的インテグレーションCakePHPで学ぶ継続的インテグレーション
渡辺 一宏,吉羽 龍太郎,岸田 健一郎,穴澤 康裕,丸山 弘詩

エラー

デプロイツール?のCapistrano3っていうのを開発用仮想マシンに入れるところ。
下記コマンドでcookbookのひな形を作って
bundle exec knife cookbook create capistrano -o site-cookbooks
できたフォルダのrecipes/default.rbを変更、
Vagrantfileのrun_listに追記して、
Berksfileを修正。
source "http://api.berkshelf.com"

cookbook "apt"
cookbook "phpenv", path: "./site-cookbooks/phpenv"
cookbook "hostsfile"
cookbook "capistrano", path: "./site_cookbooks/capistrano"
で、cookbooksをvendoringしたらエラーした。

naichilabmba:vagrant_book hu$ bundle exec berks vendor cookbooks
Resolving cookbook dependencies...
Fetching 'phpenv' from source at site-cookbooks/phpenv
Fetching 'capistrano' from source at site_cookbooks/capistrano
The resource at '/Users/hu/Documents/vagrant_book/site_cookbooks/capistrano' does not appear to be a valid cookbook. Does it have a metadata.rb?

metadata.rb?

metadata.rbはどう見てもあるんだよなぁ。
naichilabmba:vagrant_book hu$ ls site-cookbooks/capistrano/
CHANGELOG.md README.md attributes definitions files  libraries metadata.rb providers recipes  resources templates
なぜだ…
phpenvの方のvendoringは問題なく成功してるし…。うーむ。

phpenvの方

capistranoの方

phpenvの方は1行追加してあるけど他は全く一緒だよなぁ。
そもそも「knife cookbook create」で作ったテンプレートのままだし。

ためしにphpenvの方のmetadata.rbをリネームしてberks vendorしてみた。
naichilabmba:vagrant_book hu$ bundle exec berks vendor cookbooks
/Users/hu/.rbenv/versions/1.9.3-p547/lib/ruby/gems/1.9.1/gems/ridley-4.1.0/lib/ridley/chef/cookbook.rb:40:in `from_path': no metadata.json or metadata.rb found at /Users/hu/Documents/vagrant_book/site-cookbooks/phpenv (IOError)
 from /Users/hu/.rbenv/versions/1.9.3-p547/lib/ruby/gems/1.9.1/gems/berkshelf-3.1.5/lib/berkshelf/locations/path.rb:21:in `cached_cookbook'
 from /Users/hu/.rbenv/versions/1.9.3-p547/lib/ruby/gems/1.9.1/gems/berkshelf-3.1.5/lib/berkshelf/dependency.rb:126:in `cached_cookbook'
 from /Users/hu/.rbenv/versions/1.9.3-p547/lib/ruby/gems/1.9.1/gems/berkshelf-3.1.5/lib/berkshelf/lockfile.rb:387:in `block in reduce!'
 from /Users/hu/.rbenv/versions/1.9.3-p547/lib/ruby/gems/1.9.1/gems/berkshelf-3.1.5/lib/berkshelf/lockfile.rb:372:in `each'
 from /Users/hu/.rbenv/versions/1.9.3-p547/lib/ruby/gems/1.9.1/gems/berkshelf-3.1.5/lib/berkshelf/lockfile.rb:372:in `reduce!'
 from /Users/hu/.rbenv/versions/1.9.3-p547/lib/ruby/gems/1.9.1/gems/berkshelf-3.1.5/lib/berkshelf/installer.rb:32:in `run'
 from /Users/hu/.rbenv/versions/1.9.3-p547/lib/ruby/gems/1.9.1/gems/berkshelf-3.1.5/lib/berkshelf/berksfile.rb:371:in `install'
 from /Users/hu/.rbenv/versions/1.9.3-p547/lib/ruby/gems/1.9.1/gems/berkshelf-3.1.5/lib/berkshelf/berksfile.rb:590:in `vendor'
 from /Users/hu/.rbenv/versions/1.9.3-p547/lib/ruby/gems/1.9.1/gems/berkshelf-3.1.5/lib/berkshelf/cli.rb:387:in `vendor'
 from /Users/hu/.rbenv/versions/1.9.3-p547/lib/ruby/gems/1.9.1/gems/thor-0.19.1/lib/thor/command.rb:27:in `run'
 from /Users/hu/.rbenv/versions/1.9.3-p547/lib/ruby/gems/1.9.1/gems/thor-0.19.1/lib/thor/invocation.rb:126:in `invoke_command'
 from /Users/hu/.rbenv/versions/1.9.3-p547/lib/ruby/gems/1.9.1/gems/thor-0.19.1/lib/thor.rb:359:in `dispatch'
 from /Users/hu/.rbenv/versions/1.9.3-p547/lib/ruby/gems/1.9.1/gems/berkshelf-3.1.5/lib/berkshelf/cli.rb:52:in `dispatch'
 from /Users/hu/.rbenv/versions/1.9.3-p547/lib/ruby/gems/1.9.1/gems/thor-0.19.1/lib/thor/base.rb:440:in `start'
 from /Users/hu/.rbenv/versions/1.9.3-p547/lib/ruby/gems/1.9.1/gems/berkshelf-3.1.5/lib/berkshelf/cli.rb:27:in `execute!'
 from /Users/hu/.rbenv/versions/1.9.3-p547/lib/ruby/gems/1.9.1/gems/berkshelf-3.1.5/bin/berks:5:in `<top (required)>'
 from /Users/hu/.rbenv/versions/1.9.3-p547/bin/berks:23:in `load'
 from /Users/hu/.rbenv/versions/1.9.3-p547/bin/berks:23:in `<main>'
naichilabmba:vagrant_book hu$ 
IOエラーに変わっただと…?
一旦phpenvの方のmetadata.rbを戻してcapistrano側のmetadata.rbをリネームしてみた。
naichilabmba:vagrant_book hu$ bundle exec berks vendor cookbooks
Resolving cookbook dependencies...
Fetching 'phpenv' from source at site-cookbooks/phpenv
Fetching 'capistrano' from source at site_cookbooks/capistrano
The resource at '/Users/hu/Documents/vagrant_book/site_cookbooks/capistrano' does not appear to be a valid cookbook. Does it have a metadata.rb?
naichilabmba:vagrant_book hu$ 
結果変わらず。 どういう状態だろ。
metadata.rbを読み込みに行く前に無いと判断されてる?
設定ファイルミスったかなぁ。

原因発見

改めてBerksfile眺めてたら見つけた。
ひどいミス。
source "http://api.berkshelf.com"

cookbook "apt"
cookbook "phpenv", path: "./site-cookbooks/phpenv"
cookbook "hostsfile"
#cookbook "capistrano", path: "./site_cookbooks/capistrano"
cookbook "capistrano", path: "./site-cookbooks/capistrano"
まぁそんなもんだよね…。
pathが含まれるエラーはとりあえずそのpathの存在見ないとな…すぐ分かったはずなのに。

2014年11月29日土曜日

【nginx】nginxがうまく起動しなくてログおっかけて修正するだけで数時間かかった。


相変わらず下記の本を読みながらCI環境構築を進めてるんだけどnginxがうまく起動しなかった。
CakePHPで学ぶ継続的インテグレーションCakePHPで学ぶ継続的インテグレーション
渡辺 一宏,吉羽 龍太郎,岸田 健一郎,穴澤 康裕,丸山 弘詩

エラー

本では vagrant provision するとChefがnginxの設定や起動までやってくれてすぐにindex.phpに繋がるよ!って書いてあるんだけどどうにもこうにも繋がらない。

vagrant sshで対象の仮想マシンに繋いでnginxの状態を調べてみると…
vagrant@develop:~$ service nginx status
 * nginx is not running
nginx起動してないじゃん…
あと良くわからないのがstartしても何も応答がないんだよね・・・
vagrant@develop:~$ service nginx start
vagrant@develop:~$ service nginx start
vagrant@develop:~$ service nginx status
 * nginx is not running
vagrant@develop:~$ 

ログ確認

じゃあnginxの起動ログ見てみようってことで探した。
参考:http://heartbeats.jp/hbblog/2012/02/nginx03.html
エラーログは「/var/log/nginx/error.log」ってとこにあるらしい。

ログを見ると…ファイルはあるけど現在時刻では何も残ってない…
もしかしてssh接続したユーザーって起動コマンド打てない?って思ってsudoで再実行。
vagrant@develop:~$ sudo service nginx start
vagrant@develop:~$ 
そうしたらログに行が増えてた
2014/11/29 09:20:15 [emerg] 12499#0: invalid number of arguments in "root" directive in /etc/nginx/sites-enabled/test:5

testって設定ファイルがおかしーよ!って言われてるっぽいなぁ。

原因

testってファイルは
VagrantからChef経由でサーバーに作ったファイル。
見てみる。

test
server {
        listen 80;

        root /var/www/application/current/app/webroot
        index index.php index.html index.htm;

        server_name test.localhost;

        location / {
                try_files $uri $uri/ /index.php?$args;
        }

        location ~ \.php$ {
                try_files $uri =404;
                include /etc/nginx/fastcgi_params;
                fastcgi_pass unix:/var/run/php5-fpm.sock;
                fastcgi_index index.php;
                fastcgi_intercept_errors on;
                fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
                fastcgi_param CAKE_ENV test;
        }
}
4行目…セミコロンないよねorz

修正

ホストマシンのtest.erbを修正し、
「rm -rf cookbooks」でcookbooksフォルダを一旦削除、
「bundle exec berks vendor ./cookbooks」でBerkshelfを使ってcookbookを再作成
「vagrant provision」で仮想マシンへ再度反映

結果

動いたーーーーーーーー
こんな凡ミスで数時間持ってかれた…。
まぁコマンド試行錯誤しててちょっとUNIXに慣れた気がするしいいか。

【MVNO】docomoを辞めてIIJmioに乗り換えました。乗り換え前後の料金とか公開。

長い事使ってきた(13年ぐらい?)docomoをやめてIIJmioに乗り換えました。
乗り換えるにあたって調べたことをメモしておきます。

この記事を書いたのは 2014/11/28 です。
もし乗り換えを検討してる方がいれば参考にどうぞ。

乗り換え理由

まぁ当たり前ですけど料金ですね。あまりにも高い。マンション光が2500円/月なのにスマホに1万円ってどうかしてる。もっと早く乗り換えとけばよかった。
後述しますけど最近iPadMiniを買ったんで2台持ち運用の料金を比較したら乗り換え一択でした。

料金

スマホ+iPadMiniの2台運用を前提に下記料金比較を行いました。
①docomo継続(スマホのみ)
②docomo継続(iPad用にSIM追加)
③IIJmioにMNP(SIM2枚契約)

①docomo継続(スマホのみ)

だいたい月8000円+端末代2000円で10000円ちょいぐらい。

ざっくり内訳。
まずは定額分が
 ・基本使用料+Xiカケホーダイ1500円
 ・SPモード+パケット定額6000円
従量課金が
 ・通話料500〜1000円
端末代が
 ・端末代(分割) 2000円ぐらい
こんな感じ。

これで受けられるサービスは
 ・無料通話なし(docomoへの通話は無料)
 ・通信量7G
ですね。

端末代はいいとして
電話しないし7Gの通信量で8000円は高いなぁ。

②docomo継続(iPad用にSIM追加)

docomoが(というかS社もA社もだけど)新料金プランを発表して、通信有無にかかわらず端末1台で3000円(タブレットなら大体2000円)かかるようになりましたね。
 参考:3社出揃う!ドコモに追随したauとソフトバンク新料金プランの違いとは?

ちょうどdocomoのサイトにシミュレータがありました。
「カケホーダイ&パケあえる」かんたんシミュレーション 家族でご利用の場合
・スマホ基本使用料3000円
 ・タブレット基本使用料2000円
 ・通信量10G(シェア) 9200円

サービスは
 ・docomoだけでなく他社・固定電話にもカケホーダイ
 ・通信量10G

合計14200円
た、高い、、、かけホーダイとかいらないし…
ちょっとボリすぎじゃないですかね

③IIJmioにMNP(SIM2枚契約)

最後はIIJmioのみおふぉん
(他にもMVNOいっぱいあるけど評判とか見てったら自然とIIJに落ち着きました)

いろいろプランあるけどちょうど良さそうな組み合わせはこんな感じ。

 ・ファミリーシェアプラン 2560円/月
  ↑通信量7G、SIM最大3枚
・音声通話機能付きSIMを追加 700円/月
  ↑スマホ用
 ・SMS機能付きSIMを追加 140円/月
  ↑iPadMini用

  合計 3400円 安い。

これでサービスは
 ・無料通話無し(30秒20円)
 ・通信量7G(SIM2枚で共有)

十分すぎる…
インフラはドコモを使ってるらしく、ドコモのスマホならSIMロック気にせず使えるようです。ドコモの回線ならエリア気にせず使えていいですねー。
これに決めた!

乗り換え前後の料金

月々10000円(端末代含む)→3400円なので6600円安くなった。
docomoの端末代が残り3回残ってて、違約金かからず解約できるのが来年4月だった。
無理にやめると端末サポートも切れるから22000円ぐらい払うことになるけど4ヶ月で元が取れる計算。
テザリング運用よりも楽だし今乗り換えた方がお得と判断して乗り換え決行。

MNPの流れ

自分の行った手続きとスケジュール感をざっくり。

11/20(木)
 ・ドコモのサイトでMNP番号発行しようとしたが蹴られる
  どうも主回線(親と家族契約してた)だとダメみたい。
 ・ドコモに電話してどうやったら主回線切り替えられるか質問
  ドコモショップに出向く以外に方法はないらしい。めんどい。
11/22(土)
 ・ドコモショップへ
 ・1時間ぐらい待ってから手続き開始。簡単に主回線の変更&MNP発行できた。
 ・正直に高いからMNPするって言ってしまったけどニコニコ対応してくれて好印象だった。
11/25(火)
 ・13:00ごろ
  ・IIJmioのサイトで申し込み開始
  ・始めてなのでmioIDっていうものを新規に取得。
  ・プランとSIMの形態を選んで申し込み完了。
  ・すぐにメールが来た。本人確認できる写真を送ってくれって。
 ・13:05ごろ
  ・スマホで免許証の写真とってそのままアップロード
  ・めっちゃ簡単
  ・すぐにメールが来た。受付完了したよって。
 ・18:45
  ・メール来た。本人確認完了したよって。
   ・到着予定日が決まり次第再度メールしますって書かれてた。
 ・22:09
  ・またメール来た。転入手続き進めるから数時間〜1日以内に今の電話番号は使えなくなるよって。
11/26(水)
 ・11:00ごろ
  ・気づいたら圏外になってた。docomo解約されたのね。
 ・21:00帰宅
  ・ちょっと期待してたけどやっぱり届いてなかった。
11/27(木)
 ・06:12
  ・メール来た。利用開始&お届け予定日のお知らせ。
   ・音声SIMは27日
   ・SMSSIMは28日
 ・23:00帰宅
  ・嫁に聞いたら22:00すぎに届けにきたらしい。がんばるねヤマトさん。
  ・スマホにSIMさしてなんなく開通。
11/28(金)
 ・22:00帰宅
  ・SMSSIMも届いてた。お知らせ通り。
  ・iPadにSIMさして接続確認完了

スマホが使えなかったのは30時間ぐらい。
郵送の時間も入ってることを考えても結構速いんじゃないかな。
どうしても使えないと困るって人は店頭でやってもらうといいかも。

このまましばらく使ってみよ。
もし質問とかあればTwitterでもコメントでもお気軽にどうぞー。

追記

Twitterとかで質問あった内容を書いておきます。

2014/11/29 どうしてみおふぉんにしたの?

たくさんあるMVNO業者の中で最初にIIJに目をつけた理由は自分の中でほかの業者に比べて企業イメージがよかったから。それだけ。
少し調べたら通信料シェアや翌月繰り越し、高速・低速通信の切り替えアプリとか十分すぎるサービスだったのであまりほかの業者は調べてない。
ちゃんと調べればもっとお得なMVNO業者はあるかもですね~。

2014年11月26日水曜日

【Vagrant、Chef-Solo】The cookbook path 'hogehoge/.cookbooks' doesn't exist. Ignoring...


この本の通り書いててvagrantからchef-soloを呼び出したんだけどエラーした。
CakePHPで学ぶ継続的インテグレーションCakePHPで学ぶ継続的インテグレーション
渡辺 一宏,吉羽 龍太郎,岸田 健一郎,穴澤 康裕,丸山 弘詩

エラー

naichimba:vagrant_book hu$ vagrant provision develop
==> develop: The cookbook path '/Users/hu/Documents/vagrant_book/.cookbooks' doesn't exist. Ignoring...

エラー全文

naichimba:vagrant_book hu$ vagrant provision develop
==> develop: The cookbook path '/Users/hu/Documents/vagrant_book/.cookbooks' doesn't exist. Ignoring...
==> develop: Chef 11.16.4 Omnibus package is already installed.
==> develop: Configuring cache buckets...
==> develop: Running provisioner: chef_solo...
Generating chef JSON and uploading...
==> develop: Running chef-solo...
==> develop: stdin: is not a tty
==> develop: [2014-11-25T16:30:57+00:00] INFO: Forking chef instance to converge...
==> develop: [2014-11-25T16:30:57+00:00] DEBUG: Fork successful. Waiting for new chef pid: 2306
==> develop: [2014-11-25T16:30:57+00:00] DEBUG: Forked instance now converging
==> develop: [2014-11-25T16:30:57+00:00] WARN: 
==> develop: * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
==> develop: SSL validation of HTTPS requests is disabled. HTTPS connections are still
==> develop: encrypted, but chef is not able to detect forged replies or man in the middle
==> develop: attacks.
==> develop: 
==> develop: To fix this issue add an entry like this to your configuration file:
==> develop: 
==> develop: ```
==> develop:   # Verify all HTTPS connections (recommended)
==> develop:   ssl_verify_mode :verify_peer
==> develop: 
==> develop:   # OR, Verify only connections to chef-server
==> develop:   verify_api_cert true
==> develop: ```
==> develop: 
==> develop: To check your SSL configuration, or troubleshoot errors, you can use the
==> develop: `knife ssl check` command like so:
==> develop: 
==> develop: ```
==> develop:   knife ssl check -c /tmp/vagrant-chef-2/solo.rb
==> develop: ```
==> develop: 
==> develop: * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
==> develop: [2014-11-25T16:30:57+00:00] INFO: *** Chef 11.16.4 ***
==> develop: [2014-11-25T16:30:57+00:00] INFO: Chef-client pid: 2306
==> develop: [2014-11-25T16:30:57+00:00] DEBUG: Chef-client request_id: 45494694-3ef0-4c40-abfc-faa48c83b4b8
==> develop: [2014-11-25T16:31:03+00:00] DEBUG: Building node object for develop
==> develop: [2014-11-25T16:31:03+00:00] DEBUG: Extracting run list from JSON attributes provided on command line
==> develop: [2014-11-25T16:31:03+00:00] INFO: Setting the run_list to ["recipe[apt]", "recipe[phpenv::default]"] from CLI options
==> develop: [2014-11-25T16:31:03+00:00] DEBUG: Applying attributes from json file
==> develop: [2014-11-25T16:31:03+00:00] DEBUG: Platform is ubuntu version 14.04
==> develop: [2014-11-25T16:31:03+00:00] INFO: Run List is [recipe[apt], recipe[phpenv::default]]
==> develop: [2014-11-25T16:31:03+00:00] INFO: Run List expands to [apt, phpenv::default]
==> develop: [2014-11-25T16:31:03+00:00] INFO: Starting Chef Run for develop
==> develop: [2014-11-25T16:31:03+00:00] INFO: Running start handlers
==> develop: [2014-11-25T16:31:03+00:00] INFO: Start handlers complete.
==> develop: [2014-11-25T16:31:03+00:00] DEBUG: Re-raising exception: ArgumentError - You must specify at least one cookbook repo path
==> develop: /opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-11.16.4/lib/chef/cookbook_loader.rb:43:in `initialize'
==> develop:   /opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-11.16.4/lib/chef/policy_builder/expand_node_object.rb:60:in `new'
==> develop:   /opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-11.16.4/lib/chef/policy_builder/expand_node_object.rb:60:in `setup_run_context'
==> develop:   /opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-11.16.4/lib/chef/client.rb:265:in `setup_run_context'
==> develop:   /opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-11.16.4/lib/chef/client.rb:429:in `do_run'
==> develop:   /opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-11.16.4/lib/chef/client.rb:213:in `block in run'
==> develop:   /opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-11.16.4/lib/chef/client.rb:207:in `fork'
==> develop:   /opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-11.16.4/lib/chef/client.rb:207:in `run'
==> develop:   /opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-11.16.4/lib/chef/application.rb:236:in `run_chef_client'
==> develop:   /opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-11.16.4/lib/chef/application/solo.rb:226:in `block in run_application'
==> develop:   /opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-11.16.4/lib/chef/application/solo.rb:218:in `loop'
==> develop:   /opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-11.16.4/lib/chef/application/solo.rb:218:in `run_application'
==> develop:   /opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-11.16.4/lib/chef/application.rb:55:in `run'
==> develop:   /opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-11.16.4/bin/chef-solo:25:in `<top (required)>'
==> develop:   /usr/bin/chef-solo:23:in `load'
==> develop:   /usr/bin/chef-solo:23:in `<main>'
==> develop: [2014-11-25T16:31:03+00:00] ERROR: Running exception handlers
==> develop: [2014-11-25T16:31:03+00:00] ERROR: Exception handlers complete
==> develop: [2014-11-25T16:31:03+00:00] FATAL: Stacktrace dumped to /var/chef/cache/chef-stacktrace.out
==> develop: [2014-11-25T16:31:03+00:00] DEBUG: ArgumentError: You must specify at least one cookbook repo path
==> develop: /opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-11.16.4/lib/chef/cookbook_loader.rb:43:in `initialize'
==> develop: /opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-11.16.4/lib/chef/policy_builder/expand_node_object.rb:60:in `new'
==> develop: /opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-11.16.4/lib/chef/policy_builder/expand_node_object.rb:60:in `setup_run_context'
==> develop: /opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-11.16.4/lib/chef/client.rb:265:in `setup_run_context'
==> develop: /opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-11.16.4/lib/chef/client.rb:429:in `do_run'
==> develop: /opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-11.16.4/lib/chef/client.rb:213:in `block in run'
==> develop: /opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-11.16.4/lib/chef/client.rb:207:in `fork'
==> develop: /opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-11.16.4/lib/chef/client.rb:207:in `run'
==> develop: /opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-11.16.4/lib/chef/application.rb:236:in `run_chef_client'
==> develop: /opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-11.16.4/lib/chef/application/solo.rb:226:in `block in run_application'
==> develop: /opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-11.16.4/lib/chef/application/solo.rb:218:in `loop'
==> develop: /opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-11.16.4/lib/chef/application/solo.rb:218:in `run_application'
==> develop: /opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-11.16.4/lib/chef/application.rb:55:in `run'
==> develop: /opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-11.16.4/bin/chef-solo:25:in `<top (required)>'
==> develop: /usr/bin/chef-solo:23:in `load'
==> develop: /usr/bin/chef-solo:23:in `<main>'
==> develop: [2014-11-25T16:31:04+00:00] ERROR: You must specify at least one cookbook repo path
==> develop: [2014-11-25T16:31:04+00:00] FATAL: Chef::Exceptions::ChildConvergeError: Chef run process exited unsuccessfully (exit code 1)
Chef never successfully completed! Any errors should be visible in the
output above. Please fix your recipes so that they properly complete.

対応

なんのこっちゃない、自分の書いたVagrantfileにミスがありました。
  config.vm.define :develop do |develop|
    develop.omnibus.chef_version = :latest
    develop.vm.hostname = "develop"
    develop.vm.box = "opscode-ubuntu-14.04"
    develop.vm.box_url = "http://opscode-vm-bento.s3.amazonaws.com/vagrant/virtualbox/opscode_ubuntu-14.04_chef-provisionerless.box"
    develop.vm.network :private_network, ip: "192.168.33.10"

    develop.vm.synced_folder "application", "/var/www/application/current",
      id: "vagrant-root", :nfs => false,
      :owner => "vagrant",
      :group => "www-data",
      :mount_options => ["dmode=775,fmode=775"]

    develop.vm.provision :chef_solo do |chef|
      chef.log_level = "debug"
#      chef.cookbooks_path = ".cookbooks"
      chef.cookbooks_path = "./cookbooks"
      chef.json = {
        nginx: {
          docroot: {
            owner: "vagrant",
            group: "vagrant",
            path: "/var/www/application/current/app/webroot",
            force_create: true
          },
          default: {
            fastcgi_params: { CAKE_ENV: "development" }
          },
          test: {
            available: true,
            fastcgi_params: { CAKE_ENV: "test" }
          }
        }
      }
      chef.run_list = %w[
        recipe[apt]
        recipe[phpenv::default]
      ]
    end
  end
16行目のスラッシュが抜けてただけっていうね。凡ミスでした。

【Vagrant、Ubuntu】stdin: is not tty


vagrantでubuntu立ち上げてchef-soloでprovisionしたときにエラーした。

エラー

==> develop: stdin: is not a tty

対応

ここに載ってた
https://github.com/mitchellh/vagrant/issues/1673

Yeah this is a known "thing."
It is not really a problem because it doesn't really cause a problem.
Actually, the reason this error message even appears is due to a bug
in Ubuntu not checking whether stdin is a TTY, and just assuming it is.
ほっとけばよさそうね。

2014年11月21日金曜日

【Vagrant】Failed to mount folders in Linux guest.


vagrant upしたときにこんなエラーがでることがある。

エラー

Failed to mount folders in Linux guest.

エラー全文

c:\vm\vagrant>vagrant up
Bringing machine 'default' up with 'virtualbox' provider...
==> default: Importing base box 'centos7'...
==> default: Matching MAC address for NAT networking...
==> default: Setting the name of the VM: vagrant_default_1416540764834_66350
==> default: Clearing any previously set network interfaces...
==> default: Preparing network interfaces based on configuration...
    default: Adapter 1: nat
==> default: Forwarding ports...
    default: 22 => 2222 (adapter 1)
==> default: Booting VM...
==> default: Waiting for machine to boot. This may take a few minutes...
    default: SSH address: 127.0.0.1:2222
    default: SSH username: vagrant
    default: SSH auth method: private key
    default: Warning: Connection timeout. Retrying...
    default: Warning: Connection timeout. Retrying...
==> default: Machine booted and ready!
==> default: Checking for guest additions in VM...
==> default: Mounting shared folders...
    default: /vagrant => C:/vm/vagrant
Failed to mount folders in Linux guest. This is usually because
the "vboxsf" file system is not available. Please verify that
the guest additions are properly installed in the guest and
can work properly. The command attempted was:

mount -t vboxsf -o uid=`id -u vagrant`,gid=`getent group vagrant | cut -d: -f3` vagrant /vagrant
mount -t vboxsf -o uid=`id -u vagrant`,gid=`id -g vagrant` vagrant /vagrant

The error output from the last command was:

/sbin/mount.vboxsf: mounting failed with the error: No such device

対応

下記記事を参考に
http://qiita.com/osamu1203/items/10e19c74c912d303ca0b

すでに仮想環境’default’は起動しているようで、
$ vagrant ssh
で起動した仮想マシンへ接続
[vagrant@localhost ~]$ sudo /etc/init.d/vboxadd setup
これがリビルドなのかな?

[vagrant@localhost ~]$ exit
sshを抜けて
Connection to 127.0.0.1 closed.
naichilabmba:vagrant hu$ vagrant halt
==> default: Attempting graceful shutdown of VM...
vagrant haltで仮想マシン停止。もう一度vagrant upで成功した。

2014年11月20日木曜日

【Vagrant】仮想環境構築が超絶簡単になる(らしい)Vagrantを試してみた。

vagrantのロゴ。かっこいい。

はじめに

お久しぶりです。@naichilabです。
最近「けいぞくてきいんてぐれーしょん」ってのに興味を持ったので下記本を買ってみました。
CakePHPで学ぶ継続的インテグレーションCakePHPで学ぶ継続的インテグレーション
渡辺 一宏,吉羽 龍太郎,岸田 健一郎,穴澤 康裕,丸山 弘詩
少しずつ読み進めてたんですが知らん言葉やソフトが出てくる出てくる。

・バージョン管理システム - Git
・デプロイツール - Capistrano
・プロビジョニングツール - Chef
・サーバ仮想化ツール - VirtualBox
・環境構築ツール - Vagrant
・テストツール - PHPUnit
・インスペクションツール - PHP_CodeSniffer
・ドキュメント自動生成ツール - phpDocumentor
・継続的インテグレーションツール - Jenkins
・パッケージ管理ツール - Composer

設定はRubyで書いたり(Ruby触った事ない)仮想環境はUNIXだったり(UNIX触った事ない)と頭パンクしそう。
でもなんとなくVagrantが重要そうだなーと思ったのでさきにそっちで遊んでみました。


参考

ぐぐって見つけた下記本を購入。
Vagrant入門ガイドVagrant入門ガイド
新原雅司
400円!!52ページ!!なんと手頃な。
とても分かりやすかったしオススメ。

Vagrantって?


  • 開発環境を自動で構築するためのツール
  • OSXやWindows、Linuxなど幅広い環境で動く
  • VirtualBoxやVMWareといった仮想化ツールへの環境構築を自動化する
  • ChefやPuppetといったプロビジョニングツールも組み合わせる事で極端な話コマンド1行で環境構築終わり。といったことが可能になる。
っていう夢のようなソフト。らしい。実際まだ良くわかってない。

やってみる

環境

2014/11/19現在
MacBookAir 2013モデル
MacOSX 10.9.4
VirtualBox 4.3.18(最新版)
Vagrant 1.6.5(最新版)

インストール

以下からVirtualBoxをインストール
  https://www.virtualbox.org/wiki/Downloads
以下からVagrantをインストール
  https://www.vagrantup.com/
RubyはVagrantのパッケージに付いてくるらしい。

Vagrantのバージョンの確認

ターミナルで下記を実行。バージョンが出ればOK。
$ vagrant -v
Vagrant 1.6.5

Vagrantのコマンド

Vagrantは
$ vagrant ○○
というコマンドで操作する。
コマンド一覧は
$ vagrant -h
で確認できる。少ないし使ってればすぐに覚えられそう。

Boxファイル

Vagrantでは仮想マシンのベースとなるイメージファイル(Boxファイル)を使う。
やさしい人たちが色んなパターンを用意してくれてるみたい。

http://www.vagrantbox.es/
とりあえずぐぐって見つけたこのサイトからCentOSの一番新しそうなのを使ってみる。
CentOS7.0 x86_64 minimal (VirtualBoxGuestAddtions 4.3.14)ってやつ

Boxファイルの追加

$ vagrant box add [名前] [BOXファイルのURL]
これでvagrantがboxファイルをダウンロードしてくれて付けた名前で記憶してくれる。
こんな感じ
$ vagrant box add centos70 https://f0fff3908f081cb6461b407be80daf97f07ac418.googledrive.com/host/0BwtuV7VyVTSkUG1PM3pCeDJ4dVE/centos7.box
==> box: Adding box 'centos70' (v0) for provider: 
    box: Downloading: https://f0fff3908f081cb6461b407be80daf97f07ac418.googledrive.com/host/0BwtuV7VyVTSkUG1PM3pCeDJ4dVE/centos7.box
==> box: Successfully added box 'centos70' (v0) for 'virtualbox'!

Boxファイルの確認

vagrant box listコマンドで追加されているBOX一覧を確認できる。
$ vagrant box list
centos70             (virtualbox, 0)
ちゃんと出てくる。あら素敵。


基準となるフォルダを決める

適当にフォルダを作る。
適当な場所に"vagrant"ってフォルダを用意した。
ターミナルのカレントフォルダをそこに移動しておく。
naichilabmba:vagrant hu$ 
PC名やユーザー名は適当に読み替えて。

Vagrantfileの作成

VagrantではVagrantfileというファイルに起動する仮想マシンの構成などを記載する。
とりあえずboxだけを指定したシンプルなVagrantfileを生成する。
$ vagrant init [box名]
これでVagrantfileが作られる。
$ vagrant init centos70
A `Vagrantfile` has been placed in this directory. You are now
ready to `vagrant up` your first virtual environment! Please read
the comments in the Vagrantfile as well as documentation on
`vagrantup.com` for more information on using Vagrant.
できたVagrantfileはこんな感じ。(長いrubyファイルなんだけどコメント部分を削除するとこれだけ)
# -*- mode: ruby -*-
# vi: set ft=ruby :

VAGRANTFILE_API_VERSION = "2"

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
  config.vm.box = "centos70"
end
boxファイルの名前を指定してるだけですね。

仮想マシンの起動

ではお待ちかね。起動してみます。
$ vagrant up
これだけ。これだけで起動する…はずがエラーした。

エラー対処

別記事にまとめました。
【Vagrant】Failed to mount folders in Linux guest.

これで起動完了。

SSH接続

作成した仮想マシンへのSSH接続するのは
$ vagrant ssh
これだけ。仮想マシン側にsshdが起動している必要があるらしいけどたぶんboxファイルにする前に設定済みなんだろうね。
naichilabmba:vagrant hu$ vagrant ssh
Last login: Wed Nov 19 11:15:42 2014 from 10.0.2.2
[vagrant@localhost ~]$ exit
ログアウト
Connection to 127.0.0.1 closed.
めっちゃ楽。

状態確認

仮想マシンがどういう状態かは
$ vagrant status
で確認可能。
vagrant upした後ならこんな感じ。
$ vagrant status
Current machine states:
default      running (virtualbox)

仮想マシンの停止と破棄

upした仮想マシンを停止したければ
$ vagrant halt
これだけ。またupすれば使える。
もし完全に破棄したければ
$ vagrant destroy
こんな感じ。
破棄してもまたupすればすぐに使えるんだけどね。

まとめ

とりあえずboxの追加とそれを使った仮想マシンの作成・起動・停止・破棄だけやってみた。Vagrantfileさえ用意しておけば数個のコマンドを覚えるだけで仮想マシンの使い捨てができる。すげー楽。ほんと楽。

Vagrantfileを編集すれば複数の仮想マシンを一気に起動したり(webサーバ、dbサーバ、開発サーバとか)、起動後の更新を自動化したり、色々できるらしい。

ぼっちCI(継続的インテグレーション)を回せるように少しずつ覚えよう。

とりあえず今日はここまで。

2014年11月10日月曜日

【Unity、UniRx、Photon】PUNをRx対応してみた


@Baiteen

↓のツイートを見て、気になったので作ってみた。

やりたいこと

ReactiveExtensionsを使ってPhotonのコールバック地獄から脱出する。

環境

Unity(v4.6 beta)
UniRx(v4.5)
PUN(v1.28.3)

やったこと

1.UniRxインポート。
2.【Unity,Photon】PhotonCloudでオンラインゲームつくっちゃおーを参考にPUNをインポート&設定
3.PhotonRx.ObservableMonoBehaviourクラス作成。
これだけ。
ソースコードは長いので一番下。
ホントはGuthubとかに上げれればいんだけど、時代の流れについていけれない。

(追記 ここから)

GithubにGistっていうお手軽なのがあったので、そっちにあげてみました。

(追記 ここまで)

一応使ってみて機能としてはできたっぽいんだけど、Photonの使い方を知らないので確認できず。。。
誰か使ってみてくれたら嬉しいな。。。

参考サイト

neuecc/UniRx - Github
Photon Cloudのイベント一覧 - テラシュールブログ
Public API - Photon Unity Networking v1.27


ソースコードと使い方はUniRx.ObservableMonoBehaviourを真似してこんな感じ。



2014年11月1日土曜日

【Unity、UniRx】クリック回数が偶数なら赤を、奇数なら青を動かす


@Baiteen

ReactiveExtensionsがUnityでも使えるってのを今更知ったので、今更ながら使ってみた。その8。

やりたいこと

ReactiveExtensionsを使ってクリック回数が偶数なら赤のCubeを、奇数なら青のCubeを動かす。
前回の記事からちょっとしか変わってないので、変更分だけ書くことにした。

変更したところ

SphereScriptのOnMouseDownAsObservableの文を以下のように変更する。
    //クリック回数のストリームとして変数に保持
    var clickCountStream = OnMouseDownAsObservable ()
      .Buffer (OnMouseDownAsObservable ().Throttle (System.TimeSpan.FromMilliseconds (250)))
      .Select (xs => xs.Count);

    //クリック回数が偶数なら、クリック回数に比例した右向きの力を赤い四角に加える
    clickCountStream
      .Where(cnt => cnt % 2 == 0)
      .Do(cnt => redRigidbody.AddForce(Vector3.right * cnt * 10))
      .Subscribe (t=>Debug.Log(t));

    //クリック回数が奇数なら、連打イベントを発生させる
    clickCountStream
      .Where(cnt => cnt % 2 != 0)
      .Do(cnt => OnBarrage(new BarrageEventArgs(cnt)))
      .Subscribe (t=>Debug.Log(t));
    
    //クリック回数をテキストにセット
    clickCountStream
      .Do(cnt => text.text = string.Format ("{0} clicks", cnt))
      .SelectMany(cnt => 
        //1秒かけて0→1、また1秒かけて1→0ってなってほしいんだけど、0→1、1→2ってなる。意味不明。
        TakeSec(1).Do(t=>text.setA(t)).Last().SelectMany(_=>TakeSec(1).Do(t=>text.setA(1-t)))
        )
      .Subscribe (t=>Debug.Log(t));

これで完成。
今回はうに部屋に投稿するのは辞めました。ランキングにノイズが入ってしまうので。。。
適当に実行してください。


2014年10月2日木曜日

【Unity、UniRx】クリック回数をトースト表示しつつ、オブジェクトを動かす


@Baiteen

ReactiveExtensionsがUnityでも使えるってのを今更知ったので、今更ながら使ってみた。その7。

やりたいこと

ReactiveExtensionsを使ってクリック回数をトースト表示しつつ、左右にあるCubeオブジェクトを動かす。
せっかくなので、左のCubeはRigitbodyにAddForceで、右のCubeはイベント経由で動かすようにした。

前回の記事が分かりづらいと評判だったので、前回の補足も兼ねて。

やったこと

(UniRxはインポート済み)
※前回からUnityのバージョンが 4.6 beta になってます。(uGUIを使ってみたかっただけ)

前回のなごりでPlaneを追加(Planeじゃなくてもいい)
暗いのでDirectional lightを追加
クリック用のSphereを追加
トースト表示するためのText追加(GameObject→UI→Text)。Textを追加すると勝手にCanvas、EventSystemも追加されるはず。
動かすためのCubeを2つ追加。左をBlueCube、右をRedCubeとしました。両方共にRigitbodyを追加。
Cubeに色を付けるためのMaterialを作成。BlueMaterialとRedMaterial。それぞれBlueCube,RedCubeにドラッグドロップでCubeに色がつくはず。
※Sphere、Cubeの位置は適当に調整してください。

C#Scriptを作成して名前をSphereScriptに変更
今回はもう一つC#Scriptを作成して名前をCubeScriptに変更

ここまででこんな感じになってるはず。

SphereScript、CubeScriptをそれぞれ以下のように変更
・SphereScript
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UniRx;

static class ColorExt
{
  /// 
  /// Textのアルファ値をセットする拡張メソッド
  /// 
  /// uGUIのText
  /// アルファ値
  public static void setA (this UnityEngine.UI.Text text, float a)
  {
    text.color = new Color(text.color.r, text.color.g, text.color.b, a);
  }
}

public class SphereScript : ObservableMonoBehaviour
{
  /// トースト表示用オブジェクト
  public GameObject Toast;

  /// 赤い四角オブジェクト
  public GameObject RedCube;

  /// 
  /// 指定秒間だけEveryUpdateを発行する
  /// 
  /// 
  /// 秒
  private IObservable TakeSec (double value)
  {
    //0秒〜経過秒が値として発行される
    return Observable.EveryUpdate().Scan(
        new { time=0f, offset=Time.time }
        , (a, t)=>new { time=Time.time - a.offset, offset=a.offset }
      ).Select(delta => delta.time)
      .TakeUntil(Observable.Timer(System.TimeSpan.FromSeconds(value)));
  }

  #region 連打イベント関連 ここはC#のイベントの説明読んで
  public class BarrageEventArgs : EventArgs
  {
    public int ClickCount;
    public BarrageEventArgs(int clickCount){ this.ClickCount = clickCount; }
  }
  public event EventHandler Barrage;
  private void OnBarrage (BarrageEventArgs e)
  {
    if (Barrage != null) Barrage(this, e);
  }
  #endregion

  ///  RXのサンプルがAwakeだったので、ここに書きました。 
  public override void Awake ()
  {
    //参考サイト:http://neue.cc/2010/07/28_269.html

    //トースト用オブジェクトからuGUIのTextを取り出して、透明にする
    var text = Toast.GetComponent ();
    text.setA(0);

    //赤い四角オブジェクトからRigidbodyを取り出して、設定(重力無視&自然に止まって欲しいので空気抵抗を与える)
    var redRigidbody = RedCube.GetComponent();
    redRigidbody.useGravity = false;
    redRigidbody.drag = 5;

    //nueuccさんのアドカレ記事をパクりました
    //クリック回数をトースト表示
    OnMouseDownAsObservable ()
      .Buffer (OnMouseDownAsObservable ().Throttle (System.TimeSpan.FromMilliseconds (250)))
      .Select (xs => xs.Count)
      //クリック回数をテキストにセット
      .Do(cnt => text.text = string.Format ("{0} clicks", cnt))
      //クリック回数に比例した右向きの力を赤い四角に加える
      .Do(cnt => redRigidbody.AddForce(Vector3.right * cnt * 10))
      //連打イベントを発生させる
      .Do(cnt => OnBarrage(new BarrageEventArgs(cnt)))
      .SelectMany(cnt => 
        TakeSec(1).Do(t=>text.setA(t)).Last().SelectMany(_=>TakeSec(1).Do(t=>text.setA(1-t)))
      )
      .Subscribe (t=>Debug.Log(t));

    // If you use ObservableMonoBehaviour, must call base method ← サンプルにこう書いてますので、そのまま。
    base.Awake ();
  }
}

・CubeScript
using UnityEngine;
using System.Collections;

public class CubeScript : MonoBehaviour {

  // Use this for initialization
  void Start () {
    //自分からRigidbodyを取り出して、設定(重力無視&自然に止まって欲しいので空気抵抗を与える)
    var rigidbody = this.GetComponent();
    rigidbody.useGravity = false;
    rigidbody.drag = 5;

    //Sphereを探し出して、SphereScriptを取り出して、連打イベントが発生したら自分に力を加える
    var sphereScript = GameObject.Find("Sphere").GetComponent();
    sphereScript.Barrage += (sender, e) => { 
      rigidbody.AddForce(Vector3.left * e.ClickCount * 10); 
    };
  }
 
}

SphereScriptをSphereに、CubeScriptをBlueCubeにドラッグドロップ
Sphereに追加したSphereScriptに変数Toast、RedCubeがあるので、それぞれText、RedCubeをドラッグドロップ

これで完成。
Hosted by UnityRoom.com


2014年9月28日日曜日

【Unity, NCMB】nifty cloud mobile backendを使ってみた。けどWebPlayerでConnectFailureする。

今日参加したLT大会で「ニフティクラウド Mobile Backend」というものの存在を知った。
機能の詳細については公式をみて。
公式サイト → http://mb.cloud.nifty.com/

正式名は「NIFTY Cloud mobile backend」略して「NCMB」
呼びづらい、覚えづらい、書きづらい…どうにかならんもんか。

簡単に言うと
・プッシュ通知やユーザー管理、データストレージなどが使える
 ・プラットフォームに依存しないランキング実装とかに使える
・国内サーバー
 ・海外サーバーに比べレスポンスが速い
・無料でもかなり使える
 ・最大でも月3万円
こんな感じ。

かなり便利そうなので試してみる事にした。

ここのクイックスタートを見ながら試してみたんだけど
http://mb.cloud.nifty.com/doc/quickstart_unity.html

下記エラーが出た。
Error occurred: Error: ConnectFailure (Unexpected error while trying to call method_GetSecurityPolicyBlocking : System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.Net.WebException: The remote server returned an error: (404) Not Found.
  at System.Net.HttpWebRequest.CheckFinalStatus (System.Net.WebAsyncResult result) [0x00000] in <filename unknown>:0 
  at System.Net.HttpWebRequest.SetResponseData (System.Net.WebConnectionData data) [0x00000] in <filename unknown>:0 
  --- End of inner exception stack trace ---
  at System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x000eb] in /Users/builduser/buildslave/monoAndRuntimeClassLibs/build/mcs/class/corlib/System.Reflection/MonoMethod.cs:232 
  at System.Reflection.MethodBase.Invoke (System.Object obj, System.Object[] parameters) [0x00000] in /Users/builduser/buildslave/monoAndRuntimeClassLibs/build/mcs/class/corlib/System.Reflection/MethodBase.cs:115 
  at UnityEngine.UnityCrossDomainHelper+WebRequestPolicyProvider.GetPolicy (System.String policy_url) [0x00000] in <filename unknown>:0 
  at UnityEngine.UnityCrossDomainHelper.GetSecurityPolicy (System.String requesturi_string, IPolicyProvider policyProvider) [0x00000] in <filename unknown>:0 
  at UnityEngine.UnityCrossDomainHelper.GetSecurityPolicyForDotNetWebRequest (System.String requesturi_string, System.Reflection.MethodInfo policyProvidingMethod) [0x00000] in <filename unknown>:0 
  at (wrapper managed-to-native) System.Reflection.MonoMethod:InternalInvoke (object,object[],System.Exception&)
  at System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x000d0] in /Users/builduser/buildslave/monoAndRuntimeClassLibs/build/mcs/class/corlib/System.Reflection/MonoMethod.cs:222 
  --- End of inner exception stack trace ---
  at System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x000eb] in /Users/builduser/buildslave/monoAndRuntimeClassLibs/build/mcs/class/corlib/System.Reflection/MonoMethod.cs:232 
  at System.Reflection.MethodBase.Invoke (System.Object obj, System.Object[] parameters) [0x00000] in /Users/builduser/buildslave/monoAndRuntimeClassLibs/build/mcs/class/corlib/System.Reflection/MethodBase.cs:115 
  at System.Net.WebConnection.CheckUnityWebSecurity (System.Net.HttpWebRequest request) [0x00000] in <filename unknown>:0 ) 
 with: System.Collections.Hashtable ; 
   at System.Net.HttpWebRequest.EndGetRequestStream (IAsyncResult asyncResult) [0x00000] in <filename unknown>:0 
  at System.Net.HttpWebRequest.GetRequestStream () [0x00000] in <filename unknown>:0 
  at NCMB.Internal.NCMBConnection._sendRequest (System.Net.HttpWebRequest req, NCMB.NCMBException& error) [0x00000] in <filename unknown>:0 
まさかの404エラー。
でも「method_GetSecurityPolicyBlocking」みたいに書かれてる。
WebPlayer向けに設定してるしもしやと思ってAndroid向けに変えてみたらこのエラーは消えて通信成功した。

案の定WebPlayerのセキュリティポリシーによるものみたいやなぁ…
http://docs-jp.unity3d.com/Documentation/Manual/SecuritySandbox.html

ランキングロジックとかをWebPlayer、iOS、Androidで全て統一できないかと考えたんだけどなぁ。うーむ。

Unity系記事まとめ

発表してきました。【アセットまみれのLT大会 in 大阪 】

はじめに

今日はこれに参加し、初めてLTしてきました。

【9/27】【Unity】アセットまみれのLT大会 in 大阪 【関西】
http://unityassetjp.doorkeeper.jp/events/13003

※LT : Lightning Talk (3~5分の短いプレゼン)

・スケジュール通りのLTが6件(上記URL参照)
・飛び入りLTが4件
・そして少し時間が余ったのでポケット・クエリーズ佐々木様による発表が1件
計2時間ちょいでした。

全体の感想

Unityさんやゼンリンさん、ポケットクエリーズさんなど企業として来ている方の発表は聞きやすかったなぁ。話し手が落ち着いているので聞く側もとても楽だった。

今回メモは全く取ってないので他の方の発表内容に関するコメントはできません〜。
はじめて発表したのでここからは感想でも書いておこうと思います。

自分の発表の感想

自分は下記資料で発表しました。



資料作りで気をつけた点
・初LTだし緊張するから読むだけでいい資料にした
 →結果的に良かったと思う。自分はこれぐらいが楽。
・文字いっぱい書いても後ろの人読めないしMAX5行ぐらいにした
 →細かくても読まないし。
・5分から逆算して30枚程度(1枚10秒)ぐらいにした。
 →ちょうどよかったと思う。

話してるとき考えてた事
・いつも早口になっちゃうのでとにかくゆっくり話す
 →懇親会で聞いたら大丈夫でしたよって言ってもらえた。よかった。

しゃべってる最中は結構緊張しててあんまり覚えてません。
緊張に関してはあと2〜3回こういう機会があれば余裕も出てくるかな。
終わってみると5分はあっという間だったので
言いたい事をもっと絞らないとあかんなーと感じた。
特に今回の資料はアセットにこじつけたので少し話しづらかった。

そいえばMacの外部出力が快適でひじょーにやりやすかった。
プロジェクターにプレゼン再生中は手元のMacに次スライドも表示されてる。
原稿も書いておけば表示されるらしい。

おみやげ

  • UnityさんからUnityちゃんタオル
  • UnityさんからUnityPro90日トライアルライセンス
    なぜか発表を褒めていただけた…!うに部屋もっとやれってことっぽい。
  • ポケット・クエリーズさんよりクエリちゃんステッカー×2
いただきもの
ライセンスは真夏のアドベントカレンダーの参加賞でもいただいたので180日分になりました!イヤッホウUnityProで遊ぶぜ!

おわりに

とにかくやってよかった。
LTはプレゼンの練習にいいなー。
5分ぐらいならミスっても許されそうだし。
またやりたい。

2014年8月29日金曜日

【Unity、UniRx】クリック回数をトースト表示する


@Baiteen

ReactiveExtensionsがUnityでも使えるってのを今更知ったので、今更ながら使ってみた。その6。

やりたいこと

ReactiveExtensionsを使ってクリック回数をトースト表示する。

やったこと

(UniRxはインポート済み)
まずは、クリックするためのSphereを追加(Sphereじゃなくてもいい)
前回のなごりでPlaneを追加(Planeじゃなくてもいい)
トースト表示するためのText追加(せっかくなのでuGUI使いました)
それからC#Scriptを作成してコードを以下のように変更(今回もTestScriptにしました)
そしてTestScriptをSphereにドラッグ&ドロップしたら出来上がり
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UniRx;

static class ColorExt
{
  /// 
  /// Textのアルファ値をセットする拡張メソッド
  /// 
  /// uGUIのText
  /// アルファ値
  public static void setA (this UnityEngine.UI.Text text, float a)
  {
    text.color = new Color(text.color.r, text.color.g, text.color.b, a);
  }
}

public class test1script : ObservableMonoBehaviour
{
  /// トースト表示用オブジェクト
  public GameObject Toast;

  /// Scanで匿名クラス使うとなぜかエラーするので、クラス定義
  private class Delta
  {
    public float time{ get; set; }
    public float offset{ get; set; }
    public Delta(float t, float o){ time = t; offset = o; }
  }

  /// 
  /// 指定秒間だけEveryUpdateを発行する
  /// 
  /// 
  /// 秒
  private IObservable TakeSec (double value)
  {
    //0秒〜経過秒が値として発行される
    return Observable.EveryUpdate().Scan(
        new Delta( 0, Time.time)
        , (a, t)=>new Delta( Time.time - a.offset, a.offset)
      ).Select(delta => delta.time)
      .TakeUntil(Observable.Timer(System.TimeSpan.FromSeconds(value)));
  }

  ///  RXのサンプルがAwakeだったので、ここに書きました。 
  public override void Awake ()
  {
    //参考サイト:http://neue.cc/2010/07/28_269.html

    //トースト用オブジェクトからuGUIのTextを取り出して、透明にする
    var text = Toast.GetComponent ();
    text.setA(0);

    //nueuccさんのアドカレ記事をパクりました
    //クリック回数をトースト表示
    OnMouseDownAsObservable ()
      .Buffer (OnMouseDownAsObservable ().Throttle (System.TimeSpan.FromMilliseconds (250)))
      .Select (xs => xs.Count)
      .Do(cnt => text.text = string.Format ("{0} clicks", cnt))
      .SelectMany(cnt => 
        //1秒かけて0→1、また1秒かけて1→0ってなってほしいんだけど、0→1、1→2ってなる。意味不明。
        Observable.Concat(TakeSec(1).Do(t=>text.setA(t)), TakeSec(1).Do(t=>text.setA(2-t)))
      ).Subscribe (t=>Debug.Log(t));

    // If you use ObservableMonoBehaviour, must call base method ← サンプルにこう書いてますので、そのまま。
    base.Awake ();
  }

}

こんな感じ。
Hosted by UnityRoom.com

どうにか思ったとおりの動きになったんだけど、理解できない部分がいくつか。
・Scanのseedを匿名クラスにすると、なぜかCannot implicitly convert type `anonymous type' to `anonymous type'って怒られる
・1秒で終わるObservableをConcatで2つ繋げると、なぜか単体で動かすのと違う動きになる、気がする、、、

追記

neueccさんからアドバイス頂いてScanの理解できない件解決しました。
timeの型がintとfloatになってるだけでした。
seedをnew {time=0f, offset=Time.time}にしたらOKでした。はずかし。

Concatの件もいろいろ試してたら思った感じの動きになりました。
Observable.Concat(TakeSec(1).Do(t=>text.setA(t)), TakeSec(1).Do(t=>text.setA(2-t)))

TakeSec(1).Do(t=>text.setA(t)).Last().SelectMany(_=>TakeSec(1).Do(t=>text.setA(1-t)))
てしたらそれっぽくなりました。全然わからん。



2014年8月22日金曜日

【Unity、uGUI】チュートリアル見ながら触ってみる。01.Canvas

ついにUnity4.6のベータ版が来ましたね。

Unity 4.6オープンβスタート。新GUI「uGUI」の試用が可能に | テラシュールブログ

待ちに待ったuGUIがやっと使えます。

ここにチュートリアルがあるようなのでのんびり見てみようと思います。
UI チュートリアル | Unity 公式

01. UI CANVAS

動画はここ 01. UI CANVAS
実際に動画見ながら試してたらむっちゃ時間かかった

Canvas

キャンバスはUI要素を表示するための重要なコンポーネント
Hierarchy->Create->UI->Canvasで作成可能。
すべてのUI要素はキャンバスの子供にする必要がある

キャンバスは2つ以上配置することもできる

新規UI要素を配置する際、シーンに一つもキャンバスが存在しない場合は自動的に作られる。
こんな感じでいきなりボタンなどを配置するとキャンバスも作られる

RenderMode

Canvasごとに異なるRenderModeを設定できる

Screen Space Overlay : RenderMode

もっとも基本的なRenderModeであり、規定値。
これを選択するとUIはシーン上にオーバーレイ表示され、最前面に描画される。
カメラなしでも表示できる。
このモードの場合、Canvasのサイズは自動的にスクリーンサイズに合うように変更される。
また、スクリーンサイズが変更になる場合も自動的に追従する。
よくあるGUI(操作ボタンとか)はこれで問題なさそうね。

Rect Transform

Canvasを含むすべてのUI要素はこのコンポーネント(RectTransformComponent)を持ってる。
UI要素はこのコンポーネントに基づいて動作する。(らしい、よく理解できんかった)
RenderModeにScreen Space Overlayを選んでいる場合、Canvasのこのコンポーネントの値はすべて自動的に入力される。(画面サイズとかに基づいて)
Rect Transformの詳細についてはこちら

CanvasのRectTransformを変更する必要があるのは後述するWorldSpaceに置いたときだけかな?
Buttonなどの子要素を移動する場合はこのコンポーネントを操作すればいいみたい。

PixelPerfectオプションがある。選択するとUI要素は描画時に一番近いPixelに合うように調整される。より鮮明にUI要素を描画したいときに選択するといい。
NGUIにもPixelPerfectってあったけど正直いつ使うのかわからない。

Screen Space Camera : RenderMode

ScreenSpaceOverlayによく似たRenderMode。
特定のカメラに連動して動作する。
だけどカメラなしでも表示できるっぽい。
(Render Cameraをセットしない場合Overlayと同じ動作なのかな?)


こんな感じでRenderCameraにカメラをセットして使う
Perspectiveカメラとの連動が分かりやすい。
ボタンのRectTransformComponentのRotationYを30度傾けてみた。

ScreenSpaceOverlay(上)とScreenSpaceCamera(下)を並べて
同じように30度傾けてみた。違いがわかりやすい。

もちろん連動したカメラのViewPort設定などが反映される。

カメラとの距離はPlaneDistanceで設定可能
カメラごとのClearFlagsやCallingMask、カメラ深度なども適用される。
カメラについての詳細はこちら

World Space : RenderMode

これを選択するとUI要素はシーン内に他のオブジェクト同様に配置される
これはカメラ必須。
このモードのときはRectTransformを自由に書き換えることができ、Scene内の好きな場所に配置できる

イベントカメラはどのカメラがイベントを受け取るかの設定に必要。
主にクリックイベントの取得とか。
※Receives Events って項目ないな。

キャラクター上にHPバーを表示したりとか、そういうときに使うのかな。

Sorting Layer

よくわからんかった。
描画順を制御できるらしい。
詳細はこちら

描画順

キャンバス内での描画順は上から。
これは直感的でいいね



今日はここまで~。

Unity系記事まとめ

2014年8月21日木曜日

【うに部屋】Unityのゲーム投稿サイトにアセット検索機能を付けてみた

はじめに

みなさんこんにちは、@naichilab(ないち)と申します。
今回の投稿は「Unity アセット真夏のアドベントカレンダー 2014 Summer!」の記事になります。
昨日はLekさんの「スマフォで本当にオープンワールド」でした。すんごいですね!

本日の記事は
【うに部屋】Unityのゲーム投稿サイトにアセット検索機能を付けてみた
ということで、私が管理しているUnityのゲーム投稿サイトを紹介させていただきたいと思います。

※結果的にかなり露骨な宣伝になってしまいました…ごめんなさい!

Unityでゲームを作って投稿しよう

うに部屋 - UnityRoom
現在136本のゲームが公開されています。
うに部屋」というUnityWebPlayerのゲーム投稿サイトを作りました。
おかげさまで投稿されたアプリケーションも100本を超えました!
100MBまでのアプリが投稿できます。公開先に迷った際はぜひご利用ください。

うに部屋の目的

Unityは初心者向けの本が多く、無料で始めることができるので新しいユーザーさんがすごい勢いで増えていますね!
そんな方たちに気軽に作品を公開できる場所を提供しようとサイトを立ち上げました。

みんなでゲームを楽しみたい」のはもちろんですが、
純粋なゲーム公開サイトは星の数ほどあるのでちょっと方向性を変えて
投稿者自身にメリットがあり楽しめるサイト」を目指しています。

各開発者さんにとって
  • 少なからず宣伝になったり
  • モチベーション維持に役立てたり
  • 他の開発者と繋がることができたり
といったメリットを提供できたら嬉しいですね。

【新機能】アセットを探す(β版)

さて、そんな「うに部屋」ですが
つい先日「アセットを探す」という機能をリリースしました。
まだ機能が不足してるので少しずつ作っていきます
ゲームに使われているアセットの詳細みたり、
使われているアセットからゲームを探したりできます。

  現在登録されているアセットは→こちら

アセット開発者さんであれば、デモの公開場所としても利用いただけますね!
いくつかアセットを販売されている@LycoRadiさんにはこんな形で利用いただいています。
販売中のアセットを使ったデモアプリを公開
ちょっと現時点では登録が面倒だったり、閲覧できる情報が少なかったりでイマイチなのでそのあたりは少しずつ改善していきます。AssetStoreへの導線が無いとか致命的…ゴメンナサイ

おわりに

うに部屋は特にUnityを始めたばかりの初心者さんに使っていただきたいサイトです。
アップロード場所に困った際はぜひ使ってみてください!


明日は@neueccさんの「A Beginners Guide to Reactive Extensions with UniRx」です。
個人的にめちゃめちゃ楽しみです!ではよろしくおねがいします。

2014年8月18日月曜日

【google+】google+(picasa)に15分を超える動画をアップロードしても、保存容量にカウントされない

@Baiteen

前から使用容量の表示がおかしいなぁとは思ってたんだけど、間違いじゃなかったみたい。

Picasaの保存容量の上限を確認するのページには
容量にカウントされるアイテム
  • Gmail: メール内のすべてのメッセージと添付ファイル([スパム] や [ゴミ箱] フォルダ内のアイテムも含む)。
  • ドライブ: ドライブに保存しているファイル(PDF、画像、動画など)。Google ドキュメント、スプレッドシート、スライドで作成したアイテムは、保存容量にカウントされません。
  • Google+ フォト: 2048x2048 ピクセルを超える写真と15分を超える動画のみが、保存容量にカウントされます。
ってなってるんだけど、Google+ フォトに15分を超える動画をアップロードしても容量にカウントされませんでした。

やり方

1.まずはgoogle+にログイン

2.google+のホーム画面にある”最近の出来事を共有しましょう”の「動画」をクリック

3.「動画をアップロード」をクリックして、ドラッグドロップかファイル選択

4.アップロードが終わったら「動画を追加」をクリック

5.共有画面が開くので、”ユーザーを追加”のとこにログインアカウントのメールアドレスを入力し「共有」をクリック

6.google+の設定画面で使用容量を確認すると、0GBのまま

PICASAウェブアルバムでも確認してみる

7.追加したアルバムのページを開くと右側にアルバムの容量が表示されてる(14817 MB)

8.PICASAウェブアルバムの設定画面で使用容量を確認しても、0GBのまま


具体的にどんな条件でこうなるのかは分からないが、とりあえず約15GBの動画をアップロードしても使用容量が0GBのままになってる。
PCの容量を気にせず、動画が取り放題だね。


Related Posts Plugin for WordPress, Blogger...