とあるプログラマの備忘録

都内某所に住むプログラマが自分用に備忘録を残すという趣旨のブログです。はてなダイアリーから移動しました!

Unity z軸に関係なくダメージを最前面に出したい

ダメージをz軸に関係なく最前面に出したいとおもって
SortingLayerを弄っていたのですが、うまく行かず。。

結局カメラを追加する形になりました。

やりたいこと

f:id:raharu0425:20150827105747p:plain

これをこうしたい。

f:id:raharu0425:20150827105840p:plain

z軸に関わらず前面に表示されるレイヤーを作成する

FrontObjという名前で作成しました

f:id:raharu0425:20150827110002p:plain

カメラを作成する

FrontObjCameraという名前で作成します

f:id:raharu0425:20150827110222p:plain

注意点

Clear FlagsをDepth Onlyにしないといけません。

もともとあったはずのMainCameraをDepth0
新しく追加したFrontObjCameraをDepth1にします

いらないコンポーネントは外しておきましょう

カメラのマスクレイヤーを設定する

Main Camera

f:id:raharu0425:20150827110828p:plain

FrontObjCamera

f:id:raharu0425:20150827110727p:plain

カメラを増やして2つとも全部描画させるともちろんポリゴン数も頂点数も2倍になります。
マスク設定がかぶらないようにします。

これで期待通りの描画になりました。

f:id:raharu0425:20150827111341p:plain

もともSortingLayerがz軸を無視して描画順序を変更してくれるとばかり思っていたのですが、
うまくいきませんでした。
シェーダーをかける人はシェーダーでなんとでもできるかもしれません。

他の方法やご意見マサカリとう歓迎しております!!

Unity ScriptableObjectはバトルデータに向いていると思う

久しぶりのブログです。
アプリ作成は進んでいるのですが、いかんせんブログのネタになる作業をしていない&見せられないよ!
という事であまりブログが書けてません。。むしろブログ書きたいのに。。。

今日の話はScriptableObjectが結構使えそうだったのでその話です。

ScriptableObjectについてはこちらの記事が丁寧に書かれていますのでこちらを参考にしてください。

tsubakit1.hateblo.jp

それでは問題です。

ステージ毎に敵の出現ポイントやタイミングを変更したいのですが、どうしたらいいでしょうか?

よくある事です、 ○秒後に○の敵が出現その後○秒後にボスが出現といった
いわゆるバトルデータ(シナリオデータ)をどうやって実現しましょうか?というお話です。

幾つかすぐに出てくる答えとしては、

csvjsonでデータを作成してなんやかんやパースすればええやん
・シナリオ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メニューを開くと一番下に出てきます。

f:id:raharu0425:20150821184403p:plain

これで作成されたアセットはこんなInspectorになってます。

f:id:raharu0425:20150821184517p:plain

あとはQuestBattleDataとしてスクリプトにアタッチすればOK

public QuestBattleData battleData;

f:id:raharu0425:20150821185329p:plain

スクリプトからQuestBattleDataを参照する事が可能です。
また、Resouces/に入れておけば動的にアタッチする事も出きますし、
.assetなのでこのままAssetBundle化してLoadしてもよいでしょう。

そして何より、

Inspector上で動的にゲームバランスを整えながらバトルデータを作成できます!

これにより、CSVJsonのようにパーサーを用意しなくても良いですし、
DBデータでマスタデータを更新して検証DBに反映してとかやらなくても良いし、
値を追加変更する場合も影響範囲がわかりやすいという状態です(スバラっ!)

という事で今回はScriptableObjectのお話でした、
他に良い方法があるよ!とかこんな使い方があるよ!?とか
ご意見マサカリ歓迎しております。

Unity PloyWorldを使ってステージを作成してみる

さて、なんかゲームの大枠ができつつあるのですが、
仮ばっかりでチープさが抜けません!

なのでちょっと一個くらいステージ背景を作ろうとおもいます。

最初Unity付属のTerrainをそのまま使用したところFPSがひどいことになったので、
Terrainをローポリ化してくれる+オブジェクトやらなSkyboxやらも全部丸っとつけて
$50だもってけドロボー的な素晴らしいアセット購入しました。

Asset Store

これの素晴らしいところは他のハイポリモデルも
この世界観にあった感じでローポリ化してくれるらしいのですが、
自分が作ってるVoxleは元々ローポリなので世界観にバッチリです!!

兎にも角にも自分的は$50分働いてもらわないといけないので早速何か作ってみます。

SkyBox変えよう

まずは形から入る私ですが、とりあえずSkyBoxを変えることになりました。
PloyWorldは素晴らしいことに10種類以上のSkyBoxが入っています!(スバラ!!

今回は「Sky_factedにします」

f:id:raharu0425:20150730203114p:plain

Terrainで適当に地形を作る

これはもうStandard Assets様に頼って適当作りますが、
この時点で頂点数をふやしてもあまり意味がないのでこんな設定にしておきます。

f:id:raharu0425:20150730203244p:plain

初期値は結構な数字になていますのでスマホ向けに考えているのならここを抑えましょう。

こんな感じになった

f:id:raharu0425:20150730203417p:plain

まぁよくある山です、これはこのままで使うと

f:id:raharu0425:20150730204440p:plain

ポリゴン16k、、これはちょっと多いですね、
それに頂点数も10kですか、もっと抑えたいです。

これでも結構使いますね、だいぶ下げたんですけどね。

PolyWorld登場

この状態からWindow > Quantum Theory > PolyWorld Terrainを押すとこんあ画面が出てくるので

f:id:raharu0425:20150730204639p:plain

作成したTerrainを選択してGenerateボタンを押します。
今回はHalfを選択してみます

するとエラーが出ます(笑)

f:id:raharu0425:20150730204757p:plain

Terrainって名前はダメだからなんか違う名前にしてよ鬼のおにーちゃんいぇーい、ピースピース
と言われました。

しょうがないので名前も適当にTerrainMountainにして山っぽさを出します。

もう一回ジェネレートすると。

f:id:raharu0425:20150730205147p:plain

こんなかんじになるのでしたのチェックボックスのUse Associated Meshを押します。

その後Use Custom Shadearsボタンをおして、 Bake Vertex Colorsボタンを押します。

f:id:raharu0425:20150730205313p:plain

最後に、Hide Terrainにチェックをつけると。。?

f:id:raharu0425:20150730205421p:plain

いい感じになりました。
ただし
ポリゴン数は設定通り半分になりましたが、頂点数はふえました(笑)
カクついた分頂点数がふえた?Terrainが元々やってくれてたBatchingに負けた?

f:id:raharu0425:20150730205449p:plain

どちらにせよこの山の時にhalfじゃ期待以上の軽量化を狙えなさそうです。

なので1/4にしてみました。

f:id:raharu0425:20150730210121p:plain f:id:raharu0425:20150730205846p:plain

うん確かに1/4であれましたが、果たしてこの背景にどこまでユーザーはクオリティを求めるでしょうか?(笑
今回は背景をローポリでスマホに耐える形でというのが前提だったので、
1/4を採用しようとおもいます。

今後このPoly Worldをつかっていろいろやってみたいとおもいます。

ご意見マサカリ等募集しておりますm()m

Unity キャラクターの見え方をなんとかしてみる

元はこんな感じ。 まだステージができていないのでなんともチープですが、この段階でもきっとできることがあるはずです。

f:id:raharu0425:20150730183724p:plain

・影が欲しい
・ちょっとアンチエイリアスががパッキリしすぎてる

影が欲しい

通常であればLightから影を作るのでしょうが、
これだとアクティブなオブジェクトが増えてくると負荷になりそうなので、
今回はただの丸影をつけたいとおもいます。

StandardAssetからEffetsをインポートして、
キャラクターのプレハブにBlobShadowProjectorをアタッチします。

f:id:raharu0425:20150730184428p:plain

f:id:raharu0425:20150730184437p:plain

あとはBlobShadowProjectorをキャラクターの頭上に移動すれば丸影ができるのですが、
このままだとキャラクター本体にも影が落ちてしまうので、

Inspectorのf:id:raharu0425:20150730184528p:plain

Ignore Layerを使って回避しましょう。

これで丸影ができました

f:id:raharu0425:20150730184631p:plain

アンチエイリアスがパッキリしすぎている

元々そういうモデルやんみたいな根も葉もないことを言われるとそれまでですが、
もう少し境界をぼやっとさせたいわけです。

これにはカメラにAntialiasingをアタッチして値を調整しましょう。

f:id:raharu0425:20150730184858p:plain

こうすると

f:id:raharu0425:20150730184913p:plain

わかるかなー?微妙ですが、雰囲気はバッチリです。

現状できるのはこのくらいでしょうか?
微々たるところがゲームのクオリティに繋がると信じています(笑)

Unity UniRxをつかってピンチやスワイプの処理を書いてみる その2

前回はこんな記事を書いたところ、

raharu0425.hatenablog.com

raharu0425.hatenablog.com

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
            });

    }

ふむふむ、前のソースより可読性が上がっていい感じです。
挙動も問題ないのでこっちで行ってみようと思います!

Unity UniRxをつかって似非ピンチ処理を作ってみる

ピンチインアウトって本来2点座標の中間地点を拡大縮小する機能な気がするんですけど、
今回似非なのでカメラの中心点を拡大する形になります。

またエディター上ではマルチタッチが再現できないので、
スペースキー + スワイプで代用します。

前回のソースに追記するので流れがわからない人はこちら

raharu0425.hatenablog.com

これに、ピンチ処理を作ってみます。

using UnityEngine;
using System.Collections;
using System;
using UniRx;

public class CameraMove : MonoBehaviour {
    
    //メインカメラ
    public Camera MainCamera;

    // Update
    void Update () {
        
       #if UNITY_EDITOR
        // マウスが押された場合
        if (Input.GetMouseButtonUp(0)) {
        }else if(Input.GetKey(KeyCode.Space) && Input.GetMouseButton(0)){
            //エディターではマルチタッチが再現できないので適当にスペースキー+スワイプでピンチを再現します。
            MapPinch();
        } else if (Input.GetMouseButton(0)) {
            MapSwipe();
        }
       #elif UNITY_IOS || UNITY_ANDROID
        
        if (Input.touchCount == 0) {
        } else if (Input.touchCount == 1) {
            MapSwipe();
        } else if (Input.touchCount >= 2) {
            MapPinch();
        }
       #endif
    }

    //スワイプの処理は省略
        ~Skip
    
    //ピンチの処理
    public void MapPinch()
    {
       #if UNITY_EDITOR
        //エディターではマウスポジションで判定する(検証用)
        var pinch = Observable.EveryUpdate ().Select (pos_dist => Input.mousePosition.x);
        var stop = Observable.EveryUpdate ().Where(_ => Input.GetMouseButtonUp(0));
       #elif UNITY_IOS || UNITY_ANDROID
        //2点の距離を取得
        var pinch = Observable.EveryUpdate ()
            .Select (pos_dist => Vector2.Distance(Input.GetTouch (0).position, Input.GetTouch (1).position));
        //タッチカウントが2では無くなったら購読停止
        var stop = Observable.EveryUpdate ().Where (_ => Input.touchCount != 2);
       #endif
        
        //次フレームのポジションを取得して2点座標の差分を取得
        IDisposable onPinch = pinch.Zip(pinch.Skip(1), (dist1, dist2) => new { diff = dist2 - dist1})
            .TakeUntil(stop)
            .Subscribe(distanceParam => {
                // カメラのfieldOfViewを変更することでピンチを再現する
                MainCamera.fieldOfView -= distanceParam.diff / 30;

                //0以下になると反転してしまうので限界値を設ける
                if(MainCamera.fieldOfView < 4){
                    MainCamera.fieldOfView = 4;
                }
                    
                if(MainCamera.fieldOfView > 70){
                    MainCamera.fieldOfView = 70;
                }
            });
    }


}

若干動きがあやしいです、
本来はスワイプ > ピンチの状態管理はもっと適切に行う必要があります。


ピンチ - YouTube

ご意見マサカリ歓迎しています!

Unity UniRxをつかってスワイプでカメラ移動をしてみた

UniRxをAssetsストアから落としてきます。

アセット検索すれば出てきます。

パネルを用意する

f:id:raharu0425:20150716182503p:plain

今回わかりやすいように色をかえてみましたが
特に意味はありません。

カメラの位置を決定する

f:id:raharu0425:20150716182327p:plain

斜め45℃下を向くカメラを置き、パネル全体が写るように調整します。

カメラにRigidBodyを追加する。
f:id:raharu0425:20150716183902p:plain

適当なオブジェクトに下記スクリプトをアタッチする

名前は適当にCameraMove.csにしました。
MainCameraにカメラをタッチします。

f:id:raharu0425:20150716184053p:plain

using UnityEngine;
using System.Collections;
using System;
using UniRx;

public class CameraMove : MonoBehaviour {
    
    //メインカメラ
    public Camera MainCamera;

    // FixedUpdate
    void FixedUpdate () {
        // マウスが押された場合
        if (Input.GetMouseButtonUp(0)) {
        } else if (Input.GetMouseButton(0)) {
            MapSwipe();
        }
    }

    
    //スワイプの処理
    public void MapSwipe()
    {
        //mousePositionの取得
        var drug = Observable.EveryUpdate ().Select (pos => Input.mousePosition);
        //クリックが話されたら購読停止
        var stop = Observable.EveryUpdate ().Where(_ => Input.GetMouseButtonUp(0));

        //マウスポジションの4フレーム後のポジションを比較してカメラのRigidbodyに力を加える。
        IDisposable onDrug = drug.Zip (drug.Skip (4), (pos1, pos2) => new {x = pos2.x - pos1.x, z = pos2.y - pos1.y})
            //クリックが話されたら購読停止
            .TakeUntil(stop)
            .Subscribe (deltaPosition => {
                MainCamera.gameObject.GetComponent<Rigidbody>().velocity = new Vector3(deltaPosition.x, 0, deltaPosition.z) *  -5;
            }
        );
    }


}


スワイプ- YouTube

いい感じにうごきました、がブログ用に抜粋して書いたのでちょっといびつですね。
実際はupdate内で状態管理したりすると思います。

稼働領域の限界値はSubscribe内に書くと慣性の力で限界値よりも動いてしまう場合があるので
それはupdate内で監視しておいた方がいいかもしれません。

また、購読完了はTakeUntilでやりましたが、実際に使う場合は
停止は停止でSubscribe呼び出してその中でステート変更などを行うのが良いでしょう。

次は似非ピンチイン、ピンチアウトでも作成しようかと思います。

--- 追記

上記スクリプトはエディター専用で実機の場合は別途対応が必要になります。
面倒くさがらずにあるなら書けやと言われそうなので下記に記載しておきます。
参考までにどうぞmm

using UnityEngine;
using System.Collections;
using System;
using UniRx;

public class CameraMove : MonoBehaviour {
    
    //メインカメラ
    public Camera MainCamera;

    // FixedUpdate
    void FixedUpdate () {
        
       #if UNITY_EDITOR
        // マウスが押された場合
        if (Input.GetMouseButtonUp(0)) {
        } else if (Input.GetMouseButton(0)) {
            MapSwipe();
        }
       #elif UNITY_IOS || UNITY_ANDROID
        
        if (Input.touchCount == 0) {
        } else if (Input.touchCount == 1) {
            MapSwipe();
        }
       #endif
    }

    
    //スワイプの処理
    public void MapSwipe()
    {   
       #if UNITY_EDITOR
        //mousePositionの取得
        var drug = Observable.EveryUpdate ().Select (pos => Input.mousePosition);
        //クリックが話されたら購読停止
        var stop = Observable.EveryUpdate ().Where(_ => Input.GetMouseButtonUp(0));
       #elif UNITY_IOS || UNITY_ANDROID
        //positionの取得
        var drug = Observable.EveryUpdate ().Select (pos => Input.GetTouch(0).position);
        //タッチカウントが変更され場合に購読停止
        var stop = Observable.EveryUpdate ().Where(_ => Input.touchCount != 1);
       #endif

        //マウスポジションの4フレーム後のポジションを比較してカメラのRigidbodyに力を加える。
        IDisposable onDrug = drug.Zip (drug.Skip (4), (pos1, pos2) => new {x = pos2.x - pos1.x, z = pos2.y - pos1.y})
            //クリックが話されたら購読停止
            .TakeUntil(stop)
            .Subscribe (deltaPosition => {
                MainCamera.gameObject.GetComponent<Rigidbody>().velocity = new Vector3(deltaPosition.x, 0, deltaPosition.z) *  -5;
            }
        );
    }


}

ご意見マサカリ等歓迎しております!