Phalcon Migrations Tips
ちょっとPhalconを使わなければならない状態になったので、使ってみているのですが、 Phalcon結構素直でいいFWだと思います。
Phalconはだんだん好きになってきたのですが、
この案件今まで一人でやっていたらしく、migrationとか使ってないといわれました。
そんじゃPhalconMigrationでもつかうかと気軽におもったのですが
資料がない。
あってもup系の記事や記述ばかりでdownどうすんねん!!
ってのがわからなかったのでしょうがないからソースを読んでやっとわかったので、
同じ轍を踏んでいる人が少しでも参考になるように記載しておきます。
とりあえずソースよませろや!って方はこちらを読めば大体挙動がわかると思います。
GitHubは偉大
github.com
・テーブルはわかりやすいようにmigration_testを指定していますが指定しなければALLになります
・configを環境ごとに分けている場合は--configオプションにconfigのパスを指定してください
・初期generateします
$ phalcon migration generate --config=./app/config/config_local.php ~skip Success: Version 1.0.0 was successfully generated
app/migrations/1.0.0が出来上がりました
これはすべてのテーブル情報が含まれています。
・migration_testテーブルを作成します
こんなテーブルにしました。
・migration_testテーブルだけgenerateします
$ phalcon migration generate --config=./app/config/config_local.php --table=migration_test ~skip Success: Version 1.0.1 was successfully generated
app/migrations/1.0.1ができあがりました。
さて、この1.0.1でテーブルをつくったのだから、1.0.0にロールバックした時にはこのテーブルは消えてほしいので
app/migrations/1.0.1/migration_test.phpのmorph()内のソースをupにコピーして、downにdropTableの記述をします
これはrun時にはmorph() -> up()と処理が走り、rollback時には down() -> morph()と処理が走っているので、
morph() に書いたままだと、downで消したテーブルをmorphが作り直してしまうためです
<?php use Phalcon\Db\Column; use Phalcon\Db\Index; use Phalcon\Db\Reference; use Phalcon\Mvc\Model\Migration; /** * Class MigrationTestMigration_101 */ class MigrationTestMigration_101 extends Migration { /** * Define the table structure * * @return void */ public function morph() { } /** * Run the migrations * * @return void */ public function up() { $this->morphTable('migration_test', array( 'columns' => array( new Column( 'id', array( 'type' => Column::TYPE_INTEGER, 'unsigned' => true, 'notNull' => true, 'autoIncrement' => true, 'size' => 11, 'first' => true ) ), new Column( 'name', array( 'type' => Column::TYPE_VARCHAR, 'default' => "", 'notNull' => true, 'size' => 128, 'after' => 'id' ) ) ), 'indexes' => array( new Index('PRIMARY', array('id'), null) ), 'options' => array( 'TABLE_TYPE' => 'BASE TABLE', 'AUTO_INCREMENT' => '1', 'ENGINE' => 'InnoDB', 'TABLE_COLLATION' => 'utf8_general_ci' ), ) ); } /** * Reverse the migrations * * @return void */ public function down() { self::$_connection->dropTable('migration_test'); } }
1.0.0にロールバックします
downは--versionオプションで前バージョンに戻した際に呼び出されます。
$ phalcon migration run --config=./app/config/config_local.php --version=1.0.0 ~ skip Success: Version 1.0.0 was successfully migrated
migration_testテーブルが消えていれば成功です
例えばageカラムを追加した場合のdownはこのように記述します
<?php use Phalcon\Db\Column; use Phalcon\Db\Index; use Phalcon\Db\Reference; use Phalcon\Mvc\Model\Migration; /** * Class TestMigrationMigration_102 */ class TestMigrationMigration_102 extends Migration { /** * Define the table structure * * @return void */ public function morph() { } /** * Run the migrations * * @return void */ public function up() { $this->morphTable('test_migration', array( 'columns' => array( new Column( 'id', array( 'type' => Column::TYPE_INTEGER, 'unsigned' => true, 'notNull' => true, 'autoIncrement' => true, 'size' => 11, 'first' => true ) ), new Column( 'name', array( 'type' => Column::TYPE_VARCHAR, 'notNull' => true, 'size' => 128, 'after' => 'id' ) ), new Column( 'age', array( 'type' => Column::TYPE_INTEGER, 'size' => 11, 'after' => 'name' ) ) ), 'indexes' => array( new Index('PRIMARY', array('id'), null) ), 'options' => array( 'TABLE_TYPE' => 'BASE TABLE', 'AUTO_INCREMENT' => '1', 'ENGINE' => 'InnoDB', 'TABLE_COLLATION' => 'utf8_general_ci' ), ) ); } /** * Reverse the migrations * * @return void */ public function down() { $this->morphTable('test_migration', array( 'columns' => array( new Column( 'id', array( 'type' => Column::TYPE_INTEGER, 'unsigned' => true, 'notNull' => true, 'autoIncrement' => true, 'size' => 11, 'first' => true ) ), new Column( 'name', array( 'type' => Column::TYPE_VARCHAR, 'notNull' => true, 'size' => 128, 'after' => 'id' ) ) ), 'indexes' => array( new Index('PRIMARY', array('id'), null) ), 'options' => array( 'TABLE_TYPE' => 'BASE TABLE', 'AUTO_INCREMENT' => '1', 'ENGINE' => 'InnoDB', 'TABLE_COLLATION' => 'utf8_general_ci' ), ) ); } }
これやり方違うよ!とかご意見マサカリお待ちしています!
AnsibleでFailed to connect to the host via sshと言われた
ansible-playbook -i provisioning/hosts provisioning/site.yml
したら
fatal: [192.168.183.100]: UNREACHABLE! => {"changed": false, "msg": "Failed to connect to the host via ssh.", "unreachable": true}
って怒られた場合
Andibleはhostsに書かれているものを~/.ssh/configから情報引っ張ってきてSSH接続しに行くとのことなので、 まずは疑うべきは.ssh/configとAnsibleのほhostsだと思う。
・対応したホスト名で.ssh/configができているか?
・ssh [hosts]でそもそもログインできるのか?
・ssh/configのポート間違ってない?
・ssh/configの秘密キー間違ってない?
あたりを調べると良さそうだ
Unity z軸に関係なくダメージを最前面に出したい
ダメージをz軸に関係なく最前面に出したいとおもって
SortingLayerを弄っていたのですが、うまく行かず。。
結局カメラを追加する形になりました。
やりたいこと
これをこうしたい。
z軸に関わらず前面に表示されるレイヤーを作成する
FrontObjという名前で作成しました
カメラを作成する
FrontObjCameraという名前で作成します
注意点
Clear FlagsをDepth Onlyにしないといけません。
もともとあったはずのMainCameraをDepth0
新しく追加したFrontObjCameraをDepth1にします
いらないコンポーネントは外しておきましょう
カメラのマスクレイヤーを設定する
Main Camera
FrontObjCamera
カメラを増やして2つとも全部描画させるともちろんポリゴン数も頂点数も2倍になります。
マスク設定がかぶらないようにします。
これで期待通りの描画になりました。
もともSortingLayerがz軸を無視して描画順序を変更してくれるとばかり思っていたのですが、
うまくいきませんでした。
シェーダーをかける人はシェーダーでなんとでもできるかもしれません。
他の方法やご意見マサカリとう歓迎しております!!
Unity ScriptableObjectはバトルデータに向いていると思う
久しぶりのブログです。
アプリ作成は進んでいるのですが、いかんせんブログのネタになる作業をしていない&見せられないよ!
という事であまりブログが書けてません。。むしろブログ書きたいのに。。。
今日の話はScriptableObjectが結構使えそうだったのでその話です。
ScriptableObjectについてはこちらの記事が丁寧に書かれていますのでこちらを参考にしてください。
それでは問題です。
ステージ毎に敵の出現ポイントやタイミングを変更したいのですが、どうしたらいいでしょうか?
よくある事です、
○秒後に○の敵が出現その後○秒後にボスが出現といった
いわゆるバトルデータ(シナリオデータ)をどうやって実現しましょうか?というお話です。
幾つかすぐに出てくる答えとしては、
・csvやjsonでデータを作成してなんやかんやパースすればええやん
・シナリオDBでも作って適当に出せるようにしたらええやん
・最低データのみ保持してランダムで出現させればええやん
全部正解だと思います。 作ろうとしているものの用途に合わせて上記で選ぶのも良いでしょう。 ですが、Unityの場合はScriptableObjectがこれに適合している気がします。
なぜなら、Inspector上で値を変更しながらバトルデータを作成できるから!
です
という事で早速作成します。
まずは構造を決めます。
[Scripts/QuestBattleData]として保存します
using UnityEngine; using System.Collections; using System.Collections.Generic; [System.Serializable] public class QuestBattleData : ScriptableObject { //適当なパラメータ public int param1; public Rect param2; //敵が出現する場所とID public List<EnemyDetail> entryEnemy; } [System.Serializable] public class EnemyDetail{ public int enemy_id; public Vector3 enemy_pos; }
構造を型にアセットを作成できるようにします。
[Editor/CreateBattleData]として保存します
using UnityEngine; using UnityEditor; using System.Collections; public class CreateBattleData : MonoBehaviour { [MenuItem("Assets/Create/CreateBattleData")] public static void CreateAsset() { QuestBattleData item = ScriptableObject.CreateInstance<QuestBattleData>(); //アセットを保存するパス string path = AssetDatabase.GenerateUniqueAssetPath("Assets/Resources/QuestBattleData/" + typeof(QuestBattleData) + ".asset"); AssetDatabase.CreateAsset(item, path); AssetDatabase.SaveAssets(); EditorUtility.FocusProjectWindow(); Selection.activeObject = item; } }
これでAssets/Resources/QuestBattleData/フォルダを作成して、
Project > Createメニューを開くと一番下に出てきます。
これで作成されたアセットはこんなInspectorになってます。
あとはQuestBattleDataとしてスクリプトにアタッチすればOK
public QuestBattleData battleData;
スクリプトからQuestBattleDataを参照する事が可能です。
また、Resouces/に入れておけば動的にアタッチする事も出きますし、
.assetなのでこのままAssetBundle化してLoadしてもよいでしょう。
そして何より、
Inspector上で動的にゲームバランスを整えながらバトルデータを作成できます!
これにより、CSVやJsonのようにパーサーを用意しなくても良いですし、
DBデータでマスタデータを更新して検証DBに反映してとかやらなくても良いし、
値を追加変更する場合も影響範囲がわかりやすいという状態です(スバラっ!)
という事で今回はScriptableObjectのお話でした、
他に良い方法があるよ!とかこんな使い方があるよ!?とか
ご意見マサカリ歓迎しております。
Unity PloyWorldを使ってステージを作成してみる
さて、なんかゲームの大枠ができつつあるのですが、
仮ばっかりでチープさが抜けません!
なのでちょっと一個くらいステージ背景を作ろうとおもいます。
最初Unity付属のTerrainをそのまま使用したところFPSがひどいことになったので、
Terrainをローポリ化してくれる+オブジェクトやらなSkyboxやらも全部丸っとつけて
$50だもってけドロボー的な素晴らしいアセット購入しました。
これの素晴らしいところは他のハイポリモデルも
この世界観にあった感じでローポリ化してくれるらしいのですが、
自分が作ってるVoxleは元々ローポリなので世界観にバッチリです!!
兎にも角にも自分的は$50分働いてもらわないといけないので早速何か作ってみます。
SkyBox変えよう
まずは形から入る私ですが、とりあえずSkyBoxを変えることになりました。
PloyWorldは素晴らしいことに10種類以上のSkyBoxが入っています!(スバラ!!
今回は「Sky_factedにします」
Terrainで適当に地形を作る
これはもうStandard Assets様に頼って適当作りますが、
この時点で頂点数をふやしてもあまり意味がないのでこんな設定にしておきます。
初期値は結構な数字になていますのでスマホ向けに考えているのならここを抑えましょう。
こんな感じになった
まぁよくある山です、これはこのままで使うと
ポリゴン16k、、これはちょっと多いですね、
それに頂点数も10kですか、もっと抑えたいです。
これでも結構使いますね、だいぶ下げたんですけどね。
PolyWorld登場
この状態からWindow > Quantum Theory > PolyWorld Terrainを押すとこんあ画面が出てくるので
作成したTerrainを選択してGenerateボタンを押します。
今回はHalfを選択してみます
するとエラーが出ます(笑)
Terrainって名前はダメだからなんか違う名前にしてよ鬼のおにーちゃんいぇーい、ピースピース
と言われました。
しょうがないので名前も適当にTerrainMountainにして山っぽさを出します。
もう一回ジェネレートすると。
こんなかんじになるのでしたのチェックボックスのUse Associated Meshを押します。
その後Use Custom Shadearsボタンをおして、 Bake Vertex Colorsボタンを押します。
最後に、Hide Terrainにチェックをつけると。。?
いい感じになりました。
ただし
ポリゴン数は設定通り半分になりましたが、頂点数はふえました(笑)
カクついた分頂点数がふえた?Terrainが元々やってくれてたBatchingに負けた?
どちらにせよこの山の時にhalfじゃ期待以上の軽量化を狙えなさそうです。
なので1/4にしてみました。
うん確かに1/4であれましたが、果たしてこの背景にどこまでユーザーはクオリティを求めるでしょうか?(笑
今回は背景をローポリでスマホに耐える形でというのが前提だったので、
1/4を採用しようとおもいます。
今後このPoly Worldをつかっていろいろやってみたいとおもいます。
ご意見マサカリ等募集しておりますm()m
Unity キャラクターの見え方をなんとかしてみる
元はこんな感じ。 まだステージができていないのでなんともチープですが、この段階でもきっとできることがあるはずです。
・影が欲しい
・ちょっとアンチエイリアスががパッキリしすぎてる
影が欲しい
通常であればLightから影を作るのでしょうが、
これだとアクティブなオブジェクトが増えてくると負荷になりそうなので、
今回はただの丸影をつけたいとおもいます。
StandardAssetからEffetsをインポートして、
キャラクターのプレハブにBlobShadowProjectorをアタッチします。
あとはBlobShadowProjectorをキャラクターの頭上に移動すれば丸影ができるのですが、
このままだとキャラクター本体にも影が落ちてしまうので、
Inspectorの
Ignore Layerを使って回避しましょう。
これで丸影ができました
アンチエイリアスがパッキリしすぎている
元々そういうモデルやんみたいな根も葉もないことを言われるとそれまでですが、
もう少し境界をぼやっとさせたいわけです。
これにはカメラにAntialiasingをアタッチして値を調整しましょう。
こうすると
わかるかなー?微妙ですが、雰囲気はバッチリです。
現状できるのはこのくらいでしょうか?
微々たるところがゲームのクオリティに繋がると信じています(笑)
Unity UniRxをつかってピンチやスワイプの処理を書いてみる その2
前回はこんな記事を書いたところ、
Bufferつかった方がらくですよー!とお告げをいただきましたので、つかってみました。
Bufferとは引数分コレクションが溜まったらSubscribeするという便利機能らしいので
前回の機能をかきなおしてみました。
スワイプ
public void MapSwipe() { #if UNITY_EDITOR var drug = Observable.EveryUpdate ().Select (pos => Input.mousePosition); var stop = Observable.EveryUpdate ().Where(_ => Input.GetMouseButtonUp(0)).First(); #elif UNITY_IOS || UNITY_ANDROID var drug = Observable.EveryUpdate ().Select (pos => Input.GetTouch(0).position); var stop = Observable.EveryUpdate ().Where(_ => Input.touchCount != 1).First(); #endif IDisposable onDrug = drug.Buffer (3) .TakeUntil(stop) .Subscribe (colPos => { float delPosx = colPos.Last().x - colPos.First().x; float delPosz = colPos.Last().y - colPos.First().y; float Speed = 0 -(5 * (MainCamera.fieldOfView / 60)); MainCamera.gameObject.GetComponent<Rigidbody>().velocity = new Vector3(delPosx, 0, delPosz) * Speed; }); }
ピンチ
public void MapPinch() { #if UNITY_EDITOR var pinch = Observable.EveryUpdate ().Select (pos_dist => Input.mousePosition.x); var stop = Observable.EveryUpdate ().Where(_ => Input.GetMouseButtonUp(0)).First(); #elif UNITY_IOS || UNITY_ANDROID var pinch = Observable.EveryUpdate () .Select (pos_dist => Vector2.Distance(Input.GetTouch (0).position, Input.GetTouch (1).position)); var stop = Observable.EveryUpdate ().Where (_ => Input.touchCount != 2).First(); #endif IDisposable onPinch = pinch.Buffer(3) .TakeUntil(stop) .Subscribe(distanceParam => { float diff = distanceParam.Last() - distanceParam.First(); #if UNITY_EDITOR MainCamera.fieldOfView -= diff; #elif UNITY_IOS || UNITY_ANDROID MainCamera.fieldOfView -= diff / 10; #endif }); }
ふむふむ、前のソースより可読性が上がっていい感じです。
挙動も問題ないのでこっちで行ってみようと思います!