Unityのメッシュ生成でへにょりレーザーを作ろう – 後編

ども、ゴコーです。

前回はマウスでレーザーを描くところまでやりました。

今回はマウスではなくオブジェクトの移動でレーザーを描けるようにしてみましょう。

スポンサーリンク

ポイントの追加をインターフェイスに委譲する

現在は CurveLaserMaker がマウスの移動量を元に自分でポイントを追加していますが、へにょりレーザーを描くにはオブジェクトの移動を元にしなければいけませんね。では、マウスの入力部分をオブジェクトの移動読み取りに変更すればいいでしょうか?

しかし、今後もマウスやオブジェクトの移動以外に元にしたいパラメータが増える可能性もありますよね。そのような可能性を考慮して、今回はインターフェイスを使おうと思います。

まずはインターフェイスの定義です。ポイントの値を取得するための関数を一つだけ定義しました。

次にこのインターフェイスを実現するマウス用のコンポーネントを定義しましょう。

やっていることはほぼ前回のマウス位置取得と同じです。次にCurveLaserMakerを修正します。

IPointGetter 型の変数を定義し、 Start でキャッシュして setPoints 時に子のインターフェイスから値を取得します。このキャッシュはプロパティでやるべきかもしれません。

子オブジェクトを準備します。まず、空のゲームオブジェクトを作ります。 ドロップダウンから選択してもいいですし、 Ctrl + Shift + N キーでも作成することができます。

2016-09-27_17h57_30

作成したオブジェクトに MousePointGetter をアタッチしてください。

2016-09-27_18h09_04

名前をMousePointGetterに変更し、オブジェクトをCurveLaserMakerオブジェクトの子にしてください。これで準備完了です。

さて、実行してみます。

henyori3-1

最初のクリック時にいきなりレーザーが描かれ、クリックを止めると原点へと線が伸びてしまいます。これは MousePointGetter.GetPoint() がクリックしていない間は原点を返しているからです。

例えば、インデックスを取得する場合は最低値は 0 でマイナスの値は無効であるため、 -1 などを返して -1 の場合は処理をしないという風にすればこのような現象を回避できます。しかし今回の場合扱うのは座標でマイナスの値も有効なのでこの手は使えません。
そもそも無効な値を返したい場合は null を返せばいいのですが、 int や Vector3 は値型なのでこの手も使えません。という訳で、今回は  null 許容型を使います。

この2つの変更はほぼ返り値に ? を付けるだけです。これだけで値型でも null を返せるようになります。あとは無効な値の時に null を返すだけです。

次は CurveLaserMaker側の変更です。

null 許容型は HasValue で null かどうかを調べることができ、 Value で実際の値を取得することができます。 null の場合は以降の処理をする必要がないので処理を抜けています。

henyori3-2

修正した様子がこちら。クリックを止めても原点に飛ぶことはなくなりました。へにょりレーザーを作るのにはこの仕様で十分ですが、マウスで描きたい場合はクリックしたときに前回の場所から離れているとこのGIFのように直線的な線が描かれるだけになって違和感があるため、別途対策を考えなくてはいけません。そちらは本編終了後に取り扱う…かもしれません。

オブジェクトの移動によってポイントを追加する

先ほどのインターフェイスの仕組みを使ってオブジェクトの移動をもとにポイントを追加します。まずは定義と IPointGetter の実装を行います。コンポーネント名は HenyoriMove です()

今回はオブジェクト自身の位置を用いるため、 GetPoint でオブジェクトの場所を返しています。また、この後何度も transform にアクセスすることになるのでキャッシュしています。

次はへにょらせるための関数を追加します。

必要なものは次の通り3つあります。

  1. 移動
  2. 速度変更
  3. 角度変更

まずは移動用の関数を追加します。

オブジェクト基準で上に向かって moveSpeed で移動します。つまり、オブジェクトの角度を変えれば進行方向が変わるようにしました。

次は移動速度を変更できるようにします。 Update の下に以下のコードを追加してください。

現在の速度と相対的に速度を決めたい場合は relative を true にします。

term(何秒かけて移動速度を変えるか) が 0 以下の場合は値を適用してすぐに処理を抜けます。

後は移動速度を目標速度まで線形補間しています。

最後に角度変換をできるようにします。changeSpeedCor の後に以下のコードを追加してください。

ほぼ先ほどの速度変更と変わりませんが、角度変更の補助関数 setAngleZ も追加しています。

次はこれらの関数を使って実際にへにょらせてみます。

まず、Update の上にStartを追加します。

Startは返り値をIEnumeratorとすることでコルーチンとして使うこともできます。

コルーチンのいいところは色々ありますが、今回は

  • タイマー用の変数がなくても一定時間待つことができるので見通しがよくなる
  • コルーチン内で他のコルーチンを開始する場合、終了を待つことも待たないこともできる

という特徴を使います。具体的には

  • 速度を変更する場合は changeSpeedCor を呼び、 角度を変更する場合は changeDirectionCor を呼ぶ
  • 値を変更するコルーチンの終了を待つ場合は yield return を付け、そうじゃない場合(=他のコルーチンと同時に処理したい)は普通に呼ぶ
  • 曲げたり移動速度を変えない部分では WaitForSeconds で一定時間待つ

という感じです。そういう方針で作ったへにょらせるコードがこちら。

どのように変化させるかは完全に感覚と実行結果を元に決めています。あくまで例なので、適当に値を弄ってみるといいと思います。

さて、これでコード側はOKです。 MousePointGetter オブジェクトを削除し、空のゲームオブジェクトをHenyoriMove オブジェクトと名付けて代わりに CurveLaserMaker の子にしてください。

これもあくまで例ですが、 CurveLaserMakerの値は以下のようにしました。

2016-09-29_21h27_12

この状態で実行してみましょう。

henyori3-3

最初だけ変ですね。これはエディタのプレイ開始直後によくある重くなる現象のせいです。スクリプトのコンパイルに時間がかかっているのでしょうか…。回避方法を知っている方がいたら逆に教えてほしいのですが、今回は一定時間待ってから処理を開始することでとりあえず回避します。

henyori3-4

レーザーを複製する

先ほどの節でレーザーの動きはできたので後はバリエーションを増やします。今回の場合、8個のレーザーを一定角度おきに並べます。

子を増やす手順は、CurveLaserMaker をクリックして Ctrl + D で複製。その後、子である HenyoriMove の Rotation Z を複製した1つ目は45度、2つ目は90度というように45(= 360 / 8)度ずつ回転させるだけです。最後に複製した CurveLaserMaker (7) の子の角度は以下のように315になります。

2016-09-29_21h38_07

さてこの状態で再生してみます。

henyori3-52

悪くないですね。しかし、他のメッシュと重なっている間、メッシュの前後が激しく入れ替わっているのが分かると思います。これは明確な描画順を指定していないため、レーザーの描画順がUpdate毎に変わってしまっているためです。

今回のケースでは描画順はあまり重要な意味を持っておらず、他のレーザーと順番が頻繁に入れ替わるのは勘弁してほしい、くらいのこだわりしかないためインスタンスIDで適当に描画順を決めることにします。

コラム:インスタンスID

CurveLaserMakerのStartを以下のように書き換えてください。

henyori3-6

これで重なっている部分も描画順が前後しなくなりました。これで見た目部分は完成です!

当たり判定を追加する

最後にあたり判定を追加します。一番最初に仕込んでおきながら今まで使ってこなかったMeshColliderを今から使います。

CurveLaserMaker.createMeshを以下のように修正してください。

やっていることはとても単純で、 MeshCollider の sharedMesh(どのメッシュを元にあたり判定を作るか)というプロパティに更新したメッシュを渡しているだけです。

henyori3-7

バッチリあたっていますね。

完成!!

これにてへにょりレーザーの完成です。お疲れ様でした。

…まあ「なんちゃって」レベルのものですけどねw

東方だとプレイヤーが当たるとその部分が消え、消えたところを端としてレーザーを2本に作り直している感じです。しかし、今の仕様だとそれはできません。レーザーを丸ごと消すかプレイヤーに当たっても消さないかになります。

あと、見た目も使っているテクスチャの色付き部分の幅が違うか、UV座標の割り当て方が違うような印象です。

さらにさらに、ダライアスのように画面内でステージにぶつかった部分からどんどん消す、みたいなこともできませんよね。

今回のものはあくまでチュートリアル仕様なので、ご自身で拡張にチャレンジしてもらえればいいかと思います(と書いてお茶を濁しておく)。自分のSTGで使うにもこのままではちょっとアレなので、拡張はするつもりです。

さて、中編でも予告していた次回ですが現在の仕様で気になる部分の最適化を行います。本編は終了ですが、個人的には次回が本番かなーという気もしてます。

ここ何してんの?みたいな疑問がありましたら、コメントやTwitterの方で教えてもらえればコラムとして書き足すのでお気軽にどうぞー。

では、また。

Comments

comments

スポンサーリンク
336*280px

コメント

  1. @caitsithware より:

    RT @backlight1144: 新しい記事です: Unityのメッシュ生成でへにょりレーザーを作ろう – 後編 https://t.co/Q1jWxxoynS by Unity道しるべ