【Unity】第四回 unity1week 「夏」の開発について【レールの作り方】
第四回 Unity一週間ゲームジャムに参加
Unity 1週間ゲームジャム | 無料ゲーム投稿サイト unityroom - Unityのゲームをアップロードして公開しよう
一回目から参加し続けてもう四回目にもなってしまった。
参加者もちょっと少なかったのかもしれない。
イカとドラクエのせいだ。
みんな早い!自分も遅れずに投稿しました。お疲れ様でした😀 サマーサーフィン | ゲーム投稿サイト unityroom #unity1week https://t.co/xZYTPpFWXa pic.twitter.com/JhQwPujqr0
— ぐーるさん (@uuha_goul) 2017年7月30日
結論から言えば、今回も結果は振るわなかったのだけど、
まだまだUnity学びたいし頑張ろうと思っている。
アイデアについて
とりあえず
- スイカ
- 花火
- 海
- お化け
この辺りが夏の記号になりそうだなと漠然と考えた。
で、とりあえずスイカ割らせてみたり。
今回のテーマは「夏」。
— ぐーるさん (@uuha_goul) 2017年7月24日
アイデアが出ずに遊んでいる。
何やってんだ…俺は。
うああああああああああああ!
どうしよおおおおおお!#unity1week pic.twitter.com/0TVkCgNvOK
切り方はMeshCutterで数回ランダムに切っているだけ。
毎回結果が変わるのが面白い所。
だが、スイカ割りは特に人気で被りまくりそう。
花火も上に同じ。
最近気になっていたレール式ランゲームをちょっとやってみたかったので、
サーフィンを題材に今回はそれに。
レール式ランゲーム
決められたレールの上を走るランゲーム。
ジェットコースターやトロッコとかイメージすればOK。
例えば右方向へ向かって走るランゲームと比べて、
3Dを活かしたダイナミックな動きを出すことが容易に出来る。
また制御しやすい。(一回転を作ったり、この曲がり角で敵を出す!とか)
unityに落とし込む
- ランダムにルートを生成する
- 無限に走ることが出来ないとならない
- 敵の配置
- ゲームとしての体裁を整える
敵の配置やゲームとしての体裁を整える辺りは長くなりすぎるので割愛。
今回はルート生成についてコードレベルで紹介。
ルート生成のアルゴリズム
一言で言えばルートはpointの集合体です。
「次に向かうポイント」
をどんどん点で打っていけばOKです。
流れとしては
- ポイントをざっくり置く
- 滑らかに整える
- デコレーションする
- 描画する
という流れになります。
ポイントをざっくり置く
まずはじめにポイントをざっくりと置いていきます。
for (var i = 1; i < LINE_CNT; i++) { var distance = Random.Range(1, 5); // 距離の力 range = Random.Range(-3, 3) * 30; // ランダムな方向を向ける lastAngle = Mathf.Clamp(lastAngle + range, 0, 180); // 前回の角度から0度~180度の間でランダムな方向を出す float angle = lastAngle * Mathf.PI / 180; // 極座標の公式で計算する path.LinePoints[i] = path.LinePoints[i - 1] + new Vector3(Mathf.Cos(angle), 0, Mathf.Sin(angle)) * distance; }
pointはランダムな方向と、角度から位置を計算します。
極座標の公式
線の長さ(r)と角度(θ)で到達点座標(x,y)が求まる (x,y) = (r cosθ , r sinθ)
イメージとしてはこんな感じでまだガタガタな点が作られます。
滑らかに整える
点を細かくして補完していきます。
この計算をどのようにするかによって補完の仕方が変わります。
http://d.hatena.ne.jp/nakamura001/20111117/1321539246
イージングのアルゴリズムを応用して、緩急を作り出したりしても面白い!
/// <summary> /// 線を滑らかに補完する /// </summary> /// <param name="linePoints"></param> /// <param name="smoothness"></param> /// <returns></returns> private Vector3[] SmoothCurve(Vector3[] linePoints , float smoothness) { if (smoothness < 1.0f) smoothness = 1.0f; var pointsLength = linePoints.Length; var curvedLength = (pointsLength * Mathf.RoundToInt(smoothness)) - 1; var curvedPoints = new List<Vector3>(curvedLength); float t = 0.0f; List<Vector3> points; for (int i = 0; i < curvedLength + 1; i++) { t = Mathf.InverseLerp(0, curvedLength, i); points = new List<Vector3>(linePoints); for (int j = pointsLength - 1; j > 0; j--) { for (int k = 0; k < j; k++) { points[k] = (1 - t) * points[k] + t * points[k + 1]; } } curvedPoints.Add(points[0]); } return (curvedPoints.ToArray()); }
青い線が補完後の線になります。
ちょっと滑らかさが強いかもしれませんね…。
デコレーションする
一旦パスを作った後、ゲーム的な要素を追加します。
敵を置いたり、ループ作ったり。
基本的に完成したパスを再度舐めつつ、
ランダムに処理をしていけばOK
var EFFECT_IDX = 20; // 20個目までの点には効果を出さないように~とか。 for (var i = EFFECT_IDX ; i < path.LinePoints.Length ; i++) { var pos = path.LinePoints[i]; // 効果がランダムで発動 switch (Random.Range(1, 5)) { // 障害物を出す、とか //case XXX: // ~~~ // break; } }
描画する
無くてもいいのですが、
見せてもいいのです。
LineRendererにまとめて入れるだけ
_lineRenderer.positionCount = _linePoints.Length; _lineRenderer.SetPositions(_linePoints);
あれ、SetVertexCount だったような…とちょっと気になるヤーツ。
https://docs.unity3d.com/ja/540/ScriptReference/LineRenderer.SetVertexCount.html
変わったようです。
処理速度について
もうここまででもいいんじゃね?って話ですが
ゲームにした場合、気になるのは処理速度です。
どうしても計算はちょっと長いのでストレートに処理していたらプチフリーズしてしまうでしょう。
そして、パスの終点まで到達した時に処理をしていたのでは、遅れてしまうでしょう。
- 並列に処理する
- 予め次を計算しておく
この2つを満たせばOKです。
並列に処理するのは何でもいいです。
Coroutineでも、Taskでも、Threadでも…。
慣れ親しんだCoroutine辺りでとりあえずいいのではないでしょうか。
次を用意しておく処理はこんな感じで
// indexが半分を過ぎたら。ここはもっと早くてもいいかも。ゲームスピードとの兼ね合い var len = currentPath.LinePoints.Length; if (index > 0.5f * len && isCreateNext) { var lastPoint = currentPath.LastPoint; // 最後のposition var lastAngle = currentPath.LastAngle; // 最後の角度 // 続きのサブを作成しておく StartCoroutine(CreateRailPath(lastPoint, lastAngle , subPath)); isCreateNext = false; // 何度も作らせないために } // 最後まで来たらswapする if (index > len - 1) { // 予め作っておいたサブとカレントを切り替える var tmpPath = currentPath; currentPath = subPath; subPath = tmpPath; isCreateNext = true; // 次を作らせるフラグをON index = 0; }
currentとsubを用意しておいて、
終わり際にswapする!というだけ。
後は進捗をチェックしつつ、次を用意させるだけです。
進んでいくと
交互に次が生成されるのがわかると思います。
この辺りだけgithubにまとめておきました。
よく分からない所はこちらを参考に。
今回の感想
というわけで第四回も終わりました。
今回も大変だった…。手が遅くて後手後手に…。
アイデアは相変わらずヒドイもんで、
センスが腐ってんなと思います。
次こそはもうちょっと良いものを作りたい。