Unity 1週間ゲームジャムに参加しました

シェアする

  • このエントリーをはてなブックマークに追加

こんばんは。お久しぶりです。

ひなこのーとのアシンメトリー大家先輩が大好きです。

さて、最近はUnityゲーム開発に凝っております。

先日、「Unity 1週間ゲームジャム」なるイベントを知り、勢いで参加しました。

上記のツイートを見て企画の存在を知ったので、開発期間は5日間です。

どうやら2年ぶりの企画だそうで、2年前以前を除けば今回が新シーズン第1回だそうです。

そして投稿した作品がこちら↓

https://unityroom.com/games/jumping

ChromeだとWebGLの動作が重いので、FireFox推奨です。

お題が「跳ねる」とのことで、強制的にジャンプし続ける3Dアクションゲームを作りました。発想が安直ですかね? タイトルは安直です。

アイコンも超雑ですが、良かったら遊んでみて下さい。

以下、「やりたかったけどできなかったこと」と「開発・実装の話」を軽く書いていきます。

やりたかったけどできなかったこと

※以下の事項は"締め切りまでに"できなかったことであり、今後の改良によって追加することも十分あります。

細かいバグ取り

落下するパネルが落下しているときにプレイヤーが当たると回転してしまい、復活時に傾いた状態で現れてしまいます。これは直さないといけませんね。

他にも、ゴール枠を通過せずに枠下まで行ったらゴール判定が出てしまうとか。

ユニティちゃんが落下してから復活するときに向きを奥向きに直すのもやりたい。

改良するときはまずここを直します。

ツイート機能

やっぱりこれは必須ですね。特に投稿サイトにイベントで投稿する場合は。ツイッターで盛り上がるので。

オリジナルのユニークな(被りの無い)ハッシュタグも用意したいですね。ランキング機能が無くても勝手に競うことができるようになります。

次回は必ず公開時に付けておきます。

複数ステージ

期間が短かったのでできませんでしたが、ステージ設計の構想はあるので追加したいです。

追加するとなるとステージ選択画面に各ステージの最速記録などを表示して、解禁されているステージとそうでないステージを区別して……という面倒な話になりますが、より本格的になりますね。

大きいステージになると、中間地点も実装してみたいですね。そこまで到達したら、落下しても中間地点からの再開になるという。

ランキング機能

これを付けると張り合いが出ますよね。でも実現するにはデータベースとの連携が必要になります。

このブログを管理しているレンタルサーバー上にMySQLデータベースがあるので、それとの連携をさせてみたいです。

その際にはユーザー名の入力も必要ですね。結果表示画面を新たに用意するとかも……

斜めのパネル

パネルが傾いていて、そこで跳ねるときは右斜め上に飛ばされるようなパネルを用意したかったですが、操作法との相性が悪く断念。

実現するための構想はあるので、ステージ追加のときに実装したいです。

デザイン面にこだわる

私はほとんど開発しかやってこなかったので、画面デザインやUIなどは苦手です。

今回もデザイン面が手抜きになってしまいました。

デザインは完成度を左右しますし、アイコンがプレイ回数に影響を及ぼすので、大事ですよね……

精進します。デザイン関係の本(配色とか)でも買おうかなあ。

タイトル画面の動き

デザインと被りますが、タイトル画面に動きを付けたかったです。起動したら枠外からタイトルパネルがスライドしてくるような。あるいはユニティちゃんが跳ねてるような。

期間が短かったので無しになりました。

DOTweenを使えばできそうですが、その場合はタイトルパネルをuGUIではなく画像にすべきかもしれません。そうなると手間ですが……

ユニティちゃんのアニメーション

ジャンプして落下するときとかに、髪や服や手が動くアニメーションを付けたかったですが、期間的に厳しかったです。

これがあると見た目がリッチになりそうです。

音をつける

今回はブラウザゲームということで、音が出るのを嫌う人も多いと思うのであまり不満はないかもしれませんが、やっぱりゲームに音は欲しいですよね。

ブラウザの場合でも初期設定を消音にすればいいだけですし。

音は素材を探すのが面倒なので、1週間ゲームジャムでは後回しになってしまいますね。

余裕があればやってみたいです。

リファクタリング

動作に影響を与えずにコードを改良することをリファクタリングといいます。

今回、ユニティちゃんにアタッチしたPlayerMover.csのUpdate()でプレイヤーの位置操作だけでなくカメラの位置操作も行っています。ダサいです。

カメラの移動はカメラにアタッチしたスクリプトで行うようにしたいです。

その他、直接GameManagerのメンバ変数を触っている部分をプロパティやメソッドでアクセスするようにするなどの改善の余地があります。

とりあえず思いつくのはこんな感じです。

では、実装の話に入ります。

実装の話

作成したスクリプトは13あります。数が多いからどうとかは無いですが、これくらいの規模・機能で13程度というのが目安になれば。

作成したプレハブは「ノーマルパネル」「落下パネル」「ゴール(枠と下のパネル)」「移動パネル」の4通りです。最初の障害物ブロックもプレハブにするべきだと思いますが。

インポートしたストアアセットは以下の4つです。

  • ユニティちゃんSDモデル(プレイヤー)
  • Fantasy Skybox FREE(水面に反射する空)
  • DOTween(GOALテキストの落下モーション)
  • TextMesh Pro(タイトルとタイム、落下回数、GOALの文字)

水面は Assets > Import Package > Environment からインポートできるWaterProDayTimeです。

GameManagerという名の空のゲームオブジェクトを用意し、スクリプトで時間(time)や状態(isRunningとisFinished)を管理しています。

ジャンプは、各パネルにアタッチしたスクリプトのOnCollisionEnter()からプレイヤーにAddForce()することで実現しています。ジャンプの高さにばらつきが出るのも面白いですね(決して統一させるのが面倒っだったわけでは……)。

プレイヤーの操作はユニティちゃんにアタッチしたスクリプトのUpdate()からGetAxisで操作方向を受け取り、Rigidbodyのvelocityを操作しています(GameManagerのisRunningがfalseのときはUpdate()で何もしません)。

Rigidbodyを動かすときはAddForce()するべきだと聞いたことがありますが、横着してしまいました。減速度を定義するとかできるんですかね?

コース全体の下に落下判定用のPlaneを配置しています。Mesh Colliderがアタッチされています(Is TriggerとConvexにチェックを入れています)。

プレイヤーが判定板に衝突すると、PlaneのOnTriggerEnter()で、GameManagerのisRunningをfalseにすることで操作を無効にし、0.5秒後にスタート位置に復活させています。

各パネルは、厚さ0.01のCubeです。QuadだとRigidbodyがアタッチできないので。

スタート地点近くの障害物ブロックと終わりの方にある移動パネルは、Animationで動かしています。

落下する床は、当たってから0.1秒後に落下させるコルーチンを利用しています。落下はRigidbodyのconstraintsをnoneにしてuseGravityを有効(true)にするだけです。constraintsはpositionのYだけOFFにするべきでしたね。

また、落下する床が判定板に衝突したら、5秒後に復活するようにしています。

このとき、ちゃんと復活させるときにuseGravityとconstraintsを設定しなおしています。

また、プレイヤーが落下したらGameManagerのDeath()を呼んで落下回数を1増やします。

Goalは枠を表す4つの薄いCubeと下のパネル(Cube)に加えて、透明な通過判定用Cubeを含みます。

通過判定CubeはMesh Rendererのチェックを外すことで見えなくして、Box ColliderのIs Triggerのチェックをはずしてすり抜けるようにしています。

最速記録は、SaveData.csのSaveDataクラスに用意したstaticメソッド内でPlayerprefsを用いて実現しています。

タイトル画面では、最速記録をロードして、存在していればタイムを表示し、無ければ--:--.--を表示するようにしています。

ゴールしたときに最速記録をロードし、現在の記録より短くなっていればセーブします。

タイトル画面の記録表示とステージ画面のタイム表示では、float型の秒を文字列に変換する関数を作成して行っています。

Math.Truncate(float)で整数にして60で割った商を求めることで分を得て、floatのtimeから分*60を引くことで秒部分を得て、ToString("00.00")で整数2桁小数2桁0埋めの文字列に変換して、分と秒の文字列を合わせます。

タイトル画面のパネルはuGUIのImageを使用しています。画像を使用しているわけではないのですが、これは正しい使い方なのでしょうか……?

スタートボタンを表すパネル(Image)とテキスト(TextMesh Pro)にアタッチしたスクリプトのOnClick()を、各オブジェクトにアタッチしたEvent TriggerからPointer Clickに設定して利用しています。

ゴールしたら画面外にあるGOALテキストを有効化し、落下モーションを開始します。

落下モーションはDOTweenのSetEase(Ease.OutBounce)で実現しています。DOLocalMoveY()で画面中央に落下するようにしています。

そして、ゴールの3秒後にスタート画面(Startシーン)をロードするようにコルーチンを開始します。

強制的にシーン遷移させられます。モンハンみたいですね。

プレイ中のタイムはGameManagerのUpdate()で毎回time = startTime - Time.timeを行って取得しています。

GameManagerのStart()でstartTime = Time.timeを行っています。

他のスクリプトのStart()で行っている参照の取得などはAwake()で行うべきかもしれませんね。GameManagerの開始時刻取得よりも後に実行されるとタイムに影響が出るかもしれないので。

こんな感じですかね。

ストアアセットを除外した簡易版のプロジェクトやソースコードをGitHubとかで公開するかもしれません。

感想

ツイッターを眺めていたら見つけたのがきっかけで参加したイベントでしたが、とても面白かったです。

最近ゲーム開発チーム的なものを立ち上げたり個人でもいろいろ挑戦したりしてUnityゲーム開発にハマっていたのですが、公開しても多くの人の目に触れることは無かったので、この企画はすごく良い場でした。

期間が短いことを低クオリティ・低ボリューム・未完成の免罪符にできる一方で、やはり「成果物として公開する」というゴールと"""締切"""が明確に存在することで、なんとなく中途半端に作って放置してしまうようなことも無く、面倒だからとタイトル画面等を作らないということも無く、完成を意識して開発することができたので、色々なことが経験できて勉強にもなりました。

1週間(というか5日間)でも案外頑張れるんだとわかりました。

1週間ゲームジャムは毎週行われる企画ではなく、隔週~月一回あたりを考えていらっしゃるそうなので、次回があれば、時間的に余裕があれば参加したいです。

では。

スポンサーリンク
レクタングル(大)
レクタングル(大)

シェアする

  • このエントリーをはてなブックマークに追加

フォローする