Unityで3D酔いしないように工夫する

    

   

こういう風に壁沿いを歩いているとき
キャラが壁で隠れると壁が近すぎて酔いそうになる。
これはカメラが壁に近すぎるのが原因だと思う。
そこで壁を厚くしてカメラを壁の中にめり込ませる。

カメラには壁の中に張ったテクスチャを映させる。

プレイヤーが壁の中に入れないように外側に膜を作る。

という作戦を考えたですよ。
これがダンジョンの壁なのです。

ブレンダーで作ってます。
見やすいようにソリッドで表示。

左が正面から見た壁。

右が壁の裏がわ。

青い線が法線の向きです。
裏の面を選択して法線を方向転換させます。

めり込んだカメラに映してもらうためです。
裏の面を選択してEキーでその場に押し出してから内側に移動させます。
四隅を選択して面を作成。

これがプレイヤー侵入防止の膜になります。
これでイメージ通りの壁ができました。

Xの距離は実際にプレイして調整します。
壁沿いを歩くと
うんうん。

まあ最初よりはだいぶ良くなったような気がしないでもないな。

3D酔い対策はなかなか難しいですな・・・

——————— 4 / 7 追記 ——————————

もっといい方法を思いついたのであった。

左の壁が今までの壁。

今回は下のほうにナイフで切り込みを入れて
画像で選択してる面だけを内側に押し出します。
その押し出した面の法線は壁の内側に向けておきます。

これをカメラに映してもらいます。

他の面は外側に向けて法線を出しておきます。
最後にプレイヤーが壁に入れないように膜の面を作成。

この面は外側に向けて法線を出す。

さあ壁沿いを歩いてみよう!
プレイヤーと壁が重なると
内側に法線を向けた面だけが描写されます。

うんうん。これは酔わない。

酔いやすい私の脳がそう言っているので間違いない。

やった! ^0^

    

Unityの動作がWindowsとMacで違う問題について

Translateの速度が違う?

—————————————————— ——————————————————

Windowsのunityで開発してMacのxcodeでビルド→iPhone転送→動作確認

という手順でやっていたのですが、どうもwindowsでの動作とmacやiPhoneでの動作に違いがあるように思えるのですよ。

この青いゲートをくぐるとスピードが上がります。
もしゲートをくぐらずに遅いスピードのまま進むと扉が閉まって先に進めない、というステージ。

windowsでは上の画像のようになるのですが、MacやiPhoneでプレイすると青いゲートをくぐらなくても先に進めてしまうのです。

なんか扉が落ちてくるスピードがwindowsよりも遅いので先に進めてしまう。

前に「Unityで自分に向かってくる敵の弾を作る」という記事でプレイヤーを狙い撃つ弾道を作ったのですが、

windowsではこのようにちゃんと命中します。
ところがMac&iPhoneでは弾が遅く、どこ狙ってんの状態。

使ってるwindowsパソコンのほうが性能が高いのでスペックの違いで速度が違うのかな?とも思いましたが、MacとiPhoneは同じような動きなのですよ。

この扉や狙い撃つ弾はTranslateで動かしていました。

↓こんな風に
this.transform.Translate(0, -0.3f, 0);

そこでこの扉をTranslateではなく、rigidbodyで重力落下させてみました。

:ゲームプレイ中断画面:

するとこんどは両方とも手前でちゃんと扉が落下しました。扉が落ちる速度に違いは出ませんでした。

となると怪しいのはTranslateですよ。

windowsとmac&iPhoneではTranslateの速度が違うのでは?

検索しても同じような症状の人がいないのでまだよくわからないのが現状です・・・

      ---

Unityで自分に向かってくる敵の弾を作る

自分を狙って飛んでくる敵の弾を作りたいのです。

ある地点に向かってオブジェクトを動かすにはMoveTowardsを使うと出来るそうなので、プレイヤーの位置情報を取得してそこに動かせばいいのだろうと

this.transform.position = Vector3.MoveTowards(this.transform.position, new Vector3(playerpos.x, playerpos.y, playerpos.z), 0.5f);

このように書いてみたところ

自分のほうに飛んでは来たものの、ずっと張り付いて離れなくなってしまった。ずっとプレイヤーの座標を参照してるからずっとその座標を目指してしまう。

位置を参照するのは1回で良さそう。

プレイヤーのボールをP1、敵弾を赤丸とすると

プレイヤーは前に進むわけだからP1の座標を目指して敵弾を飛ばしても命中しないし狙ってる感じがしない。

プレイヤーの移動先のP2の地点の座標をゲットしてそこに飛ばそう。

public class EnemyMove : MonoBehaviour {

   Vector3 tmpE;                        //myballの位置情報をここに入れる 
    public Transform hogeEne;
    GameObject BPninja;     //
    int Ecnt = 0;       //フラグ立てに使う
    float calcZ = 0;    //敵とプレイヤーの距離を測るのに使う

    void Start () {

        this.BPninja= GameObject.Find("MyBall");            //playerposにボール位置情報を格納
        Vector3 playerpos =this.BPninja.transform.position; //
    }

	// Update is called once per frame
	void Update () {

         Vector3 playerpos = this.BPninja.transform.position;    //ball位置情報取得する
            this.calcZ = this.transform.position.z - playerpos.z;   //敵のZ座標からボールのZ座標を引く。これで距離が出る

            if (this.calcZ < 50)    //プレイヤーが弾に近づいて来たら

            {
                if (this.Ecnt == 0)     //ここで一回だけ現在のプレイヤー位置情報を調べる
                {
                    hogeEne = GameObject.Find("MyBall").GetComponent<Transform>();  //ボールを見つける
                    tmpE = hogeEne.transform.position;                              //ボールの位置情報をvector3でtmpに格納
                    this.Ecnt = 2;      //Ecntを2にして再度位置情報をゲットしないようにする
                }

                if (this.Ecnt == 2)     //Ecnt=2ならゲットした位置情報の座標にMoveTowardsで弾を飛ばす
                {
                    this.transform.position = Vector3.MoveTowards(this.transform.position, new Vector3(tmpE.x, tmpE.y, tmpE.z + 10), 0.6f);
                }
            }
        }

上のコードではプレイヤーのボールが敵弾に近づくと弾が発射されるようにしています。狙う位置はプレイヤーの移動先なのでtmpE.z + 10 としてプレイヤーより奥の座標を指定しています。

このコードを敵弾のオブジェクトにD&Dでアタッチして実行してみると

うむ!

プレイヤーが近づくと移動先に向かって敵弾が発射されました!

四角い岩も飛んできたぜ! やった^^v

buttonのPrefabを表示するのに苦労した話

//////////////////////////////////////// ー広告ー ////////////////////////////////////////

こんな感じでSTARTボタンを押したらゲームがスタートするようにしたかったんです。そのボタンをPrefabで作ろうとしたらいろいろエラーが出て苦しみました・・・

まずボタンが表示されませんでした。

なんでかというとボタンとかテキストはCanvasの子オブジェクトにしてやらないと表示されないようです。ボタンのPrefabのBTstartはCanvasの外にできちゃってます。これはアカン。

いろいろ調べてCanvasの子オブジェクトとしてPrefabを作る方法を発見。

public class test : MonoBehaviour { 

public GameObject canvas;//キャンバス
public GameObject PutStart; //スタートボタンのプレファブを入れる箱 

void Start () { 

GameObject prefab = (GameObject)Instantiate (PutStart); 

prefab.transform.SetParent (canvas.transform, false); 
} 

void Update () { 
} 

}

これでいけるはずなんですが・・・

こんなエラーを吐かれてしまう。

UnassignedReferenceException: The variable canvas of MainJ has not been assigned.
You probably need to assign the canvas variable of the MainJ script in the inspector.

はぁ~ん? 何を言っとるのかわからんのう~

public class test : MonoBehaviour { 
public GameObject PutStart; //スタートボタンのプレファブを入れる箱 
public RectTransform canvas;//キャンバス 

void Start () { 
Instantiate(PutStart, new Vector3(4, 12, -299), 
Quaternion.identity).name = "PreStrat"; canvas=GameObject.Find("Canvas").GetComponent<RectTransform>(); 
PutStart.transform.SetParent(canvas.transform, false); } 

void Update () { } 

}

Canvasが見つからないのかと思ってFindで見つけようとか訳のわからんことをしてみる。

すると今度はこんなエラーを吐きだしたのでった。

Setting the parent of a transform which resides in a prefab is disabled to prevent data corruption.

どうもこれはPrefab自身を親オブジェクトに設定しようとすると出るエラーらしい。

なんかよくわからんけど私のPrefabの作り方がおかしかったぽい。

public class test : MonoBehaviour { 

public GameObject canvas; //キャンバス 
public GameObject PutStart; //スタートボタンのプレファブを入れる箱 

GameObject ObjK; //ちゃんとInstantiateするための箱 

void Start () { 
ObjK = Instantiate(PutStart, new Vector3(3.9f, 18.6f, -298.6f), Quaternion.identity); 

canvas = GameObject.Find("Canvas"); 

ObjK.transform.SetParent(canvas.transform, false); } 

void Update () { 
} 

}

このように書くと

ちゃんとCanvas内にクローンが作れて

やっとボタンを表示することができました^0^

すごい基本的なことなのかな~?

やっぱ独学はアカンね・・・^^;

↓こういうところで本格的に勉強したいですな!




Collider Materialのbouncinessをスクリプトから変更する




こういったスロープ状のオブジェクトの上にボールを転がす時

Bouncinessを0.9くらいに設定しているとスロープ内で反射しまくります。

スロープの上にいるときはBouncinessを0にして反射させずにスルスルとボールを転がしたいのですよ。

反射させないオブジェクト用にNotReflecというタグを新しく作ります。

スロープオブジェクトのPlane.002にNotReflecタグを設定。

    
void OnCollisionEnter(Collision col)
    {
        //反射させない処理
        if (col.gameObject.tag == "NotReflec")
        {
            this.GetComponent<SphereCollider>().material.bounciness = 0;
            return;
        }
        //反射させない処理
    }

スクリプトの衝突判定処理内に上記を追加。

実行してみるとスロープの壁に反射せずにスルスルとボールが上がっていきます。やった^^

注意点としてはこの方法では反射させない障害物にぶつかっても
最初の1回は反映されずに反射してしまうようです。
2回目の衝突からは反映されて反射しなくなるみたい。

それと一度bouncinessを0にするとその後もずっと0のままなのでスロープを抜けるとまた0.9にしたい。

    void OnCollisionEnter(Collision col)
    {
        this.GetComponent<SphereCollider>().material.bounciness = 0.9f;

        //反射させない処理
        if (col.gameObject.tag == "NotReflec")
        {
            this.GetComponent<SphereCollider>().material.bounciness = 0;
            return;
        }
        //反射させない処理
    }

最初の行で0.9fに設定しておいてNotReflecタグの時だけ0にするようにしました。

するとスロープを抜けたら壁に反射するようになりました!

最短4週間!オリジナルのゲームアプリを開発しよう!




unityでオブジェクトが壁をすり抜けてしまう問題について





ブレンダーでこういうスロープ状のデッキを作って

その上にボールを転がそうとしたら

左のカーブ部分で

壁をすり抜けしまう問題発生 ^^;

ボールオブジェクトのRigidbody のCollision Detectionを

DiscreteからContinuous

に変更すると

すり抜けなくなりました! やった ^0^




Shader for this material does not support skybox renderingのエラーが出た

こういう画像をゲームの背景に使おうとしたらエラーが出ました。

あ、この写真はエスカヒル鳴門っていうきれいな場所です。^^

新しくマテリアルを作ってShaderに

Legacy Shader→Diffuse を選択。

Window  Rendering  Lighting Settings で

Shader for this material does not support skybox renderingのエラー。

マテリアルのShader → SkyBox → Panoramic

を選択すると解決しました。^-^

パノラマですね~ ^0^

////////////////////////////////////////
ー広告ー



Blenderで作ったオブジェクトをUnityで動かす2

前回まででブレンダーで作ったキャラの左腕をあげることができました。

今回はボタンを押したら腕をあげるようにしたいと思います。

create → UI → Button

でボタンを2つ作成。名前をLeftButton、RightButtonにしました。LとRのボタンを上の図のように配置します。

Lボタンを押すと左腕をあげさせます。

今はシーンを再生すると自動的に左腕をあげるアクションをするようになっています。ボタンを押したときに動かすにはトリガーを設定すると良いそうです。

プロジェクトビューのアニメクリップをダブルクリックしてアニメータービューにしてParameters タブの+をクリック。Triggerを選択します。名前をLeftTriggerにしました。

StandからLeftUpを繋ぐ矢印を選択してインスペクターのHas Exit Timeのチェックを外します。Transition DurationとTransition Offsetの値を0にします。

Conditionsの+をクリックしてLeftTriggerを選択します。

トリガーを設定したので自動的に左腕をあげなくなりました。^-^

次にボタンを押したときにトリガーを引くようにスクリプトを書きます。Create→C#Script

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI; //UI使うのに必要

public class MainV : MonoBehaviour {

    public Animator animator;

    // Use this for initialization
    void Start () {
		
	}
	
	// Update is called once per frame
	void Update () {
		
	}

    //Lボタン押した
    public void PushL()
    {
        animator = GameObject.Find("blood-Ugoku2-Action1-5").GetComponent<Animator>();
        animator.SetTrigger("LeftTrigger");
    }
}

create Empty で空のオブジェクトを作ってゲーム監督にします。名前をKantokuにしました。書けたスクリプトをこれにD&D。次にLeftButtonのOnClickの+押してobjectの欄にKantokuをD&D。関数の欄にさっき書いたスクリプトのPushLを選択します。

実行してみるとLボタンを押すとちゃんと左腕をあげました!やった^^v

ただこのままだとずっと左腕をあげたままになります。腕をあげたらまた元の直立Stand状態に戻したいと思います。

LeftUpのアクションを選択してMake Transitionで矢印をStandに接続。矢印を選択してHas Exit Timeにチェックを入れる。Exit Timeを0、Transition Durationを0.5、Transition Offsetを0に設定。

ボタンを押すと1回左腕をあげてその後はゆっくりと元のStand状態になりました! ^0^

なんかどれもすんなり成功してびっくり。

やる前にいろいろ読んでたらなんか難しそうだなーって思いましたが実際やってみると意外と簡単に動かせました!(^∇^)

 

////////////////////////////////////////
ー広告ー



Blenderで作ったオブジェクトをUnityで動かす1

Blenderで作ったオブジェクトをUnityで動かそうと思います。

やりたいこととしてはAというボタンを押せば左腕を上にあげるアクションをさせる。Bというボタンを押せば右腕を上にあげるアクションをさせる。というようなことをやりたいのです。

そのためにまず

ただ立っているだけのアクション1

左腕を上にあげるアクション2

右腕を上にあげるアクション3

の3つのアクションをブレンダーでまず作ります。

上のDefaultって所をAnimationに切り替える。

ドープシートをアクションに切り替えてアニメーション終了時間を20にする。

ただ立っているだけのアニメーションを作ります。

腕を下げて全部のボーンを選択した状態で iキーでメニューを出して 位置/回転を選択。

キーフレームが作成されました。

Fボタンを押すと横に2って数字が出るのでそれを押すとArmatureAction.001とかいうのが出来るのでそれをStandという名前にリネーム。

次に左腕を上げるアクションを作ります。

F ArmatureActionを選択して数字の2を押すとまたArmatureAction.001とかいうのが出来るのでそれをLeftUpという名前にリネーム。

グラフエリアで10フレーム目にカーソルを移動して左腕を上げている状態で iキー → 位置/回転 でキーフレーム作成。左腕を上げるアクションができました。

同じように右腕を上げるアクションをRightUpという名前で作りました。

これでStand、LeftUp、RightUpの3つのアクションができました。

FBX形式でエクスポートします。

今度はUnityでこれを読み込んで動かしたいと思います。

fbxファイルをプロジェクトビューにD&D。

インスペクターのRigでHumanoidを選択してApplyをクリック。

読み込んだキャラをシーンビューにD&D。

無事読み込めて一安心。^-^

おいおい

ハゲとるやないけww あっはっは (^▽^)

まあハゲはあとで修正すればよろし。

ヒエラルキーでオブジェクトを選択してからWindow → Animationをクリック。

こんな画面が出てくるのでCreateで適当な名前のアニメーションクリップを作成保存。

さっき作ったクリップをダブルクリックするとこんなアニメーション同士の繋がりを設定できる画面が出てきました。

プロジェクトビューからStandのアクションをD&Dします。

最初からあるNewStateとかいうのは右クリックでDelete。

すると自動的に矢印がStandアクションに繋がりました。

シーン再生してみるとちゃんとただ立っているだけのアクションをしているな。ヨシヨシ^-^

今度はLeftUpのアクションをD&D。

Standを右クリックしてMake Transitionを選択すると矢印が出てくるのでLeftUpアクションと連結させる。

おおー 左腕を上げた!思い通りに動くとうれしいぜ!(・∀・)

次回はボタンを押すと腕が動くようにしたい!

 

////////////////////////////////////////
ー広告ー



UnityでCSVファイルを読み込んでマップ作製2

前回まででCSVファイルのマップデータをUnityで
読み込むことができました。
今回は読み込んだデータからオブジェクトを
配置していきたいと思います。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;

public class MapMaker : MonoBehaviour {

    public GameObject MapObject;    //石のPrefabを入れる箱をGameObject型変数で宣言
    public GameObject TreeObject;   //木のPrefabを入れる箱をGameObject型変数で宣言

    public GameObject MapPut;     //石オブジェクトを配置するのに使う
    public GameObject TreePut;    //木オブジェクトを配置するのに使う

    public TextAsset csvFile; // CSVファイル読み込むのに使う

    string str = "";        //CSVの全文字列を保存する
    string strget = "";     //取り出した文字列を保存する

    int gyou = 10;  //CSVデータの行数
    int retu = 10;  //CSVデータの列数

    int[,] map = new int[20, 20];   //マップ番号を格納するマップ用変数
    int[] iDat = new int[15];       //文字検索用

    int a = 0;    //濫用 数値型変数
    int b = 0;    //濫用 数値型変数
    int c = 0;    //濫用 数値型変数

    int ix = 0;     //マップ置くX座標 初期位置
    int iy = 0;     //マップ置くY座標 初期位置
    int iz = 0;     //マップ置くZ座標 初期位置

    // Use this for initialization
    void Start () {

  //----------↓ ここでCSVデータをstrに保存 ↓
        csvFile = Resources.Load("testmap2") as TextAsset;
        StringReader reader = new StringReader(csvFile.text);

        while (reader.Peek() > -1)
        {
            string line = reader.ReadLine();
            str = str +","+ line;
        }

        str = str + ",";        //最後に検索文字列の","を追記。これがないと最後の文字を取りこぼす。
  //----------↑ ここでCSVデータをstrに保存 ↑

  //----------↓ ここでCSVデータをマップ配列変数mapに保存 ↓
        for (int c = 0; c < gyou; c++)
        {
            for (int i = 0; i < retu; i++)
            {
                try
                {
                    iDat[0] = str.IndexOf(",", iDat[0]);   //","を検索
                }
                catch { break; }

                try
                {
                    iDat[1] = str.IndexOf(",", iDat[0] + 1);   //次の","を検索
                }
                catch { break; }

                iDat[2] = iDat[1] - iDat[0] - 1;                //何文字取り出すか決定

                try
                {
                    strget = str.Substring(iDat[0] + 1, iDat[2]);   //iDat[2]文字ぶんだけ取り出す
                }
                catch { break; }

                try
                {
                    iDat[3] = int.Parse(strget);        //取り出した文字列を数値型に変換
                }
                catch { break; }

                map[a, b] = iDat[3];                    //マップ用変数に保存。1とか6とか数字が入るよ
                b++;                            //一つ右のマップ用変数へ
                iDat[0]++;                      //次のインデックスへ
            }

            a++;                                //一つ下のマップチップへ
            b = 0;                              //マップチップ格納を一番左に戻す。
        }

  //----------↑ ここでCSVデータをマップ配列変数mapに保存 ↑

  //----------↓ ここでマップ配列変数mapの内容を確認できます ↓
        //a = 0;
        //for (int c = 0; c < gyou; c++)
        //{
        //    for (int i = 0; i < retu; i++)
        //    {
        //        Debug.Log("map." +a+"."+ i + ": " + map[a, i]);
        //    }
        //    a++;
        //}
  //----------↑ ここでマップ配列変数mapの内容を確認できます ↑

  //----------↓ ここでマップ配列変数mapからオブジェクトを配置 ↓

        ix = -10;   //マップ置くX座標 初期位置
        iy = 0;     //マップ置くY座標 初期位置
        iz = 100;   //マップ置くZ座標 初期位置

        a = 0;
        b = 0;
        c = 0;

        for (int c = 0; c < gyou; c++)
        {

            for (int i = 0; i < retu; i++)
            {
                if (map[a, b] == 1)     //マップ番号が1なら木を配置
                {
                    TreePut = Instantiate(TreeObject) as GameObject;   //Instantiate関数でTreePreのコピーを作る。
                    TreePut.transform.position = new Vector3(ix, iy, iz);
                }

                if (map[a, b] == 2)     //2なら石を地面に配置
                {
                    MapPut = Instantiate(MapObject) as GameObject;   //Instantiate関数でCubePreのコピーを作る。
                    MapPut.transform.position = new Vector3(ix, iy+3, iz);
                }

                if (map[a, b] == 3)     //3なら石を高めに配置してダンジョンの屋根にする
                {
                    MapPut = Instantiate(MapObject) as GameObject;   //Instantiate関数でCubePreのコピーを作る。
                    MapPut.transform.position = new Vector3(ix, iy + 8, iz);
                }

                b++;            //次の右のマップ番号を読み込む
                ix = ix + 5;    //配置位置を右に移動
            }

            a++;            //一行終了。下の段のマップ番号を読み込んでいく
            b = 0;          //行の始めに戻るからb=0
            iz = iz - 5;    //下の段に配置していくからZ座標をー5する
            ix = -10;       //一段下の左端からまた配置していくからX座標は初期位置になる
        }

    }
	
	// Update is called once per frame
	void Update () {
		
	}
}

長々とした美しくないコードができてしまったよ。
もっとスマートなやり方があると思います。

実行結果↓

まあまあ思ってた感じに配置できているような
気がしますな。
石の高さが低いので遺跡の中には入れないけど
高さを調整したら入れそう。

なんか例外エラーが出てたので
try~catchいうのを初めて使いました。
何事も挑戦だ! 失敗したらcatchでbreakだ!

////////////////////////////////////////
ー広告ー