unity1week「ふる」でTeamUpに参加しました!
最後にゲーム制作のアレコレを記事にしたの、2021年1月6日らしい。
unity1weekのイベントではこの記事以降も皆勤でゲームを出してはいるのだが、それに関する振り返り記事は一切出してない。もっと過去を振り返れ!
一応1回だけunity1week共有会とかに参加してたりはしますが……。
で、さぼりまくりな状況の中、今回記事を出すきっかけになったのは、unity1weekTeamUp!!という、共同制作を目的としたプロジェクトに参加したからです。
ちなみに、unity1weekTeamUp!!って何?って方はこちらの記事を読むといいと思います。(今この記事読んでる人の大半は知ってそうではあるが)
簡単にまとめると、エンジニア界隈の人がそれ以外の界隈の人とチームを組むことができたら強くね?っていう感じで立ち上がった企画です。
今回、その企画の第一回に参加し、サウンドの方である tMaro さんと組ませていただきました。
そして作った作品がこちらとなります!
公開ツイート
今回の記事では、これがどういう経緯で作られたのかなどの部分を書いていこうと思います。
ゲーム概要
どんなゲームかというのも軽く紹介しておきます。
今回のu1w(unity1weekの略称)のお題は「ふる」!
ということで、雨とブロックが降る世界を舞台としたミニゲームとなっております。
ブロックがテトリスの要領で色々な場所に降ってくるので、そのブロックをプレイヤーが押し出して整地することで列消去をしつつ、雨が止むその時まで生存を目指す。そんな感じのゲームです。
ゲーム進行や、スコア増加に伴ってBGMが変化していく、所謂インタラクティブミュージックというものを導入しております。
ゲームの評価とか
製作過程云々の件が長くなってしまったので、結論部分だけ先に書きます。
評価は以下の通りでした。
楽しさ :3.786
絵作り :3.381
サウンド:4.095(29位)
操作性 :3.071
雰囲気 :3.667
斬新さ :3.952(43位)
総合入りは果たせませんでしたが、共同制作部分であるサウンドが無事ランキング入りできたので目的は達成できたと言えるでしょう。
それ以外の部分は、ゲーム内のスコアとか他者のプレイ動画とか見ててまあそんな気はしてた。
難しい操作性によってスコアが伸びず、それによって楽しさ等諸々の評価が下がってしまったものだと予想。うん、いつものことだな!!
どういう調整をしたかみたいなのは製作過程の件で書いているけど、
要はスコアランキング上位を目指す者たち(所謂ランカーズと呼ばれる者たち)をターゲットにしたバランス調整しか出来ていなかったというのが要因です。
本当はもっとターゲット層を広げたかったけど、1週間の遅刻をしていたというのもあり、そこに調整を割く時間は取れなかったというのが現状です。
無限コンティニュー機能こそ付けはしたけど、カジュアル層向けへの救済としては多分機能してなさそうな感じはある。
ということで、どういう感じのプレイが想定されていたかというのを体感してもらうべく、自身のスコアアタック動画を載せておきます。
また、実際に遊んだ方ならわかると思うのですが、
ゲームで要求されている操作が忙しすぎてインタラクティブミュージック等のゲーム内の変化に浸る余裕が無い
というのもあるように感じたので、ゆっくり視聴できる動画でゲーム内の変化を感じ取ってくれればといったところです。
……っといった具合に、課題こそ沢山あるけれど、
他の項目に対してサウンドの評価を高くすることができたこと、
本難易度でも極一部のコアゲーマーにはぶっ刺さったのを確認できたという点ではよかったのではないのでしょうか。
何より自分にも刺さる作品にできたので(これが一番大事!)
という感じで先にまとめの文章を綴ったので、自身の今後の行動についても書いていきますか。
今後の予定とか
u1wも終わり、振り返り記事も書いた。これで一区切り!
……っと言いたいところですが、unity1weekTeamUp!!では既に別の新しい企画が立ち上がろうとしてまして。
それがこれ。
TeamUp勢の作品のクオリティが軒並み高くなりすぎて、諸々のハードルが上がってしまっているから、ハードルを下げるべく6時間でゲームを作ろうという企画。正気か?
とはいえ、6時間でゲームが完成するかどうかはともかくとして、お手軽にチーム製作の経験が積めるという意味ではカジュアルさがあっていいんじゃないですかね。未完成でも全然OKというくらいには緩いので。チームの相性が悪いってことがわかっても6時間で解散できるからね。
自分はまだ↑の企画に向けた準備等できてない状態ですが、
既にいくつかのチームが結成されてるようで、活発さはありそうです。
u1wでチーム結成を本当はしたかった、という方はこれを機にTeamUpサーバーに入ってみてはいかがでしょうか。
TeamUpの詳細は冒頭で貼ったリンクに書かれているので、
気になった方はそれを見てもろて。
TeamUp以外にも、例年通りだとしたら8月には東方ゲームジャムというのも開催されるらしいです。これには昨年初参加してみました。
こちらも期限は製作期間は1週間です。
そして次回のu1wは9月開催という噂も。あれ、なんかずっとゲームジャム開催されてない??
てな感じで、なんだかんだで忙しい日は続きそうです。
個人開発ススンデナイヨ。
今後の予定部分も書いたので、
残りの部分は、TeamUpで作成したゲームの製作過程とかをダラダラ綴ります。
といってもこの時点で記事の半分くらいまできてるが??
TeamUp参加の経緯
最初はTeamUpそのものへの参加も様子見してました。
というのも、デザイナーがいたところで、自分がそれを活かせる自信がなかったというのがあります。。
そう、今までに「絵作り」にフォーカスを当てた作品をまともに作ってこなかったために、それ周りの知見が無いのである。
ということで様子見を続けていたわけですが、TeamUpの広報力が(主催の想定以上に)高かったようで、デザイナー枠の人が結構参加するようになってきたという情報を聞く。
デザイナーが参加希望しているのにエンジニア側の人が不足しているがためにデザイナーがチーム結成できない、という状況もどうだろうというのがあり、ひとまず見学という体でTeamUpに参加することに。
見学という体なのは、先述した通り、自分がデザイン力を活かせる自信がなかったから。
TeamUpのサーバーに参加後は、過去の書き込みとか漁りながら、周囲の状況を伺ってました。結構色々なところで書き込んでた気もするが。
チーム結成までの経緯
様子見でサーバー内の状況を確認してるうちに、サウンドを専門とする人達がサーバー内に入ってくるようになった。
サーバーに入ってきた人の楽曲とかを聞いてる中で、自分の好みの曲、かつ自分のゲーム内で表現できそうな方を見つけました。
それがtMaroさんでした。
その方は募集を募っていたということもあり、自分から声をかけることに。
ありがたいことに相手側からも快諾していただけて、そのままチーム結成へ。
結成は5/23で、u1w開催の4週間弱前辺りです。
サウンドの方と組んだ以上はインタラクティブミュージックというものを導入してみたい、というのがあり、それに関する事前準備等を行うことになりました。
インタラクティブミュージック(事前準備編)
そもそもインタラクティブミュージックって何?って方は下記記事に目を通してくれれば。(他力本願寺スタイル)
実装方法にも軽く触れているので、イメージも何となく想像はつくのではないでしょうか。
既にインタラクティブミュージックに対応したライブラリも存在するようですが、とりあえず自前で用意してみることに。
ありがたいことに、チームの相方であるtMaroさんからインタラクティブミュージックに対応したサンプル音源を頂いたので、一番の壁である「素材の入手」は突破できたわけです。
実装は多少躓いた部分もありましたが、意外と何とかなりました。保守性はゼロだが。WebGL上でも動くことを確認できたので、u1w本番でもインタラクティブミュージックを使う方向で確定しました。
インタラクティブミュージック(本番編)
今回出した作品では、20の音源を同時に再生していい感じに切り替えるようになっています。
どのタイミングでどの方式で切り替えるか、というのは表でまとめたものを
コードに直書きしてます。
その表が下記です。(一部本記事用に調整してます)
音源自体は最初から作っていただいていたのですが、どう組み合わせるかの話になった際、スコアに対応した変化とレベル(時間変化)での変化と2つの軸があったらいいのではということになり、こういう形になりました。
また、フェードイン/アウト形式で切り替えるか即時で切り替えるかの設定もできるようにしておいたので、その辺りをどうするかも含めてまとめていただきました。
(自分のゲーム部分の実装で遅延が起こっていたためにこの辺りの作業を丸投げしてしまったのは申し訳ないです。。)
それ以外の事前準備とか
共有手段をどうするかを相談し、
アイディアの書き出しとか諸々はNotionで管理
音声データなど、容量の大きいデータはGoogleDriveで別途管理
というスタイルをとることにしました。
先ほど挙げたインタラクティブミュージックの表もNotionで管理してます。
とはいえ、進捗状況はTeamUpサーバー上でもわかるようにする必要があるため、作業内容等の要点を報告という形で共有するよう意識はしました。
あとは事務連絡や、会議を開きたいときなどの一次窓口として、TeamUpサーバーへの書き込みは普通にしてました。
……っという感じで諸々の準備をしつつお題の発表を待ちます。
1日目(お題発表時)
Notionで案だしのお時間。
お題発表から寝るまでの間の1時間くらいを使って、二人でアレコレ羅列してました。
他のチームやTwitterなどでつぶやかれるアイディアとかも確認しつつ、被らなさそうなタイトル、かつゲームの雰囲気が合うかどうかというのを議論してました。
この時点では候補はいくつかあったものの、決定打になりそうなものは出てこなかったので、翌日に持ち込む形で一旦解散。
1日目(昼)
案が急に降ってきたのでNotionにまとめました。
この時点で「雨が降る」「テトリスブロックが降ってくる」という二つの要素を持った世界が構成されました。
テトリスについて
「テトリス」及びそれに関連した専門ワードを作中でそのまま使うのは法的によろしくないそうで……。なので作中では「テトリス」に関連したワードは一切使わないようにしています。
……というのを公言するのもどうなのといったところではあるけれど。振り返り記事ならええやろ多分……。
一応参考文献
構想案の裏話
構想案の段階では文字送りやイベントシーンを含んだストーリーが多く存在していて、
かつゲームロジックもかなり複雑なパズルゲームになっていました。
ゲームの和名は『雨、時々ミノ』
本当はこの名称をそのまま使いたかったけど、先述した専門ワードに触れそうだったので、タイトルを英名に変えつつ、
「Mino」という単語から「Miny」という天気っぽい造語を作り出し、『Rainy With Miny…』というタイトルができました。
「テトリミノ」って単語を使わなければ問題なさそうな感じもするけれど、念のために、ね。
ゲームの流れ等を説明したところ、「よさそう」ということになり、決定になりました。
とはいえ、がっつり作りこむとまず間に合わない点や、テトリス用語を使わずにテトリス世界を説明する必要がある点、そもそもデザイナー必須案件では?などいくつか懸念点があったため、要素を削りつつストーリーを練っていく。
ここまで書いて明記してなかった。
自分のチーム、エンジニアとサウンドの二人構成です。
30チームくらい?ある中で唯一デザイナー不在のチームでした。
ここで色々要素を削ったはいいが、それでもu1wに間に合うかは微妙なラインでした。
(グラフィック周りの手抜き手法を考えたくらいで、イベントシーンなどはまだ作る想定だった)
とはいえ、制作も進めていかないとというのもあり、暫定的に決定。
そして翌日、実装しててやっぱり間に合わないのでは??となり、緊急会議を開くことに。
判断が遅い!
更に工数を削り、
ストーリーを全て排除し、操作対象をミノだったものをプレイヤーに変更
そのプレイヤーは前作から使いまわし
ゲームもミニゲームな感じに仕上げる
という大幅工数削減が行われました。それでも結局大遅刻してるんですが。
tMaroさんにはこの仕様変更だったり、提出直前の状態でSEの再調整をしたりと振り回しっぱなしでしたが、すぐに素材を提供してくれるその速さに驚きました。本当に感謝しかないです。
実装で時間がかかった部分
今回の遅刻は自身の実装で時間がかかってたことが要因なので、備忘も兼ねて、どういう実装にどの程度時間を費やしたのかみたいなのを書いていきます。
テトリスロジック
何だかんだでここが一番時間を取られてる気がする。
まずはテトリスの盤面管理
盤面はXYの2次元マスで構成されてるから、これをどうプログラム上で定義するかってところから始まります。
データ上は1次元で扱って、そのインデックスをいい感じに制御して2次元配列っぽく扱う、という手法があります。
最初はその方法でやってましたが、計算周り面倒!ってなって2次元配列で管理する方法に切り替えました。
盤面も動的には変わらないから固定配列でええやろってことで、マス数も決め打ちしてます。
という感じで、2次元配列を定義してインデックスで特定のマスを参照してコネコネする実装になっているわけです。
そうするとアレがやってくるわけです。
IndexOutOfRangeException
インデックスで指定する際、配列の領域でない場所を指定してしまうと、このエラーが出ます。
この例外が出てしまうとゲームが強制終了してしまうので、これが出ないようにインデックスの制御を上手く行う必要があるわけです。
foreachを始めとした全領域探索処理がいかに楽な存在かというものを改めて思い知らされましたね。
ミノの配置
テトリスロジックを作っていくと同時に、ミノを降らせる処理も作っていきます。
やっていることをまとめると、
ミノの形状、向きを決定
盤面のサイズに応じて落下可能な範囲を定め、落下場所を決定
落下予測位置を表示させ、一定時間後に実体化させる
という流れになるのですが、この辺で別の問題が出てきます。
ここまでの処理はUnityに非依存するような書き方をしていました。
どういうことかというと、MonoBehaviourを継承しないロジックになっていました。
要はこのままだとUnityではまだ使えないため、Unityでも使えるようにしてあげる必要があります。
何をしたかというと、上記のMonoBehaviourを使ってないモデル部分を、
MonoBehaviourを使ったビューに反映させる、という実装をしました。
えーこれは、知る人ぞ知る「MVPパターン」と呼ばれる設計方法です。合ってるよね…?
気づいたらそういう設計になってたわけですが、u1wの規模でガチ設計をするな!
個人開発でMVPパターンを意識して設計したことがそもそも無いというのに!
で、このMonoBehaviourを使ったビューを作るにも色々試行錯誤することになりました。
下記は一部コードを抜粋してます。
public class TetrisFieldObjController : MonoBehaviour
{
[SerializeField] TetrisObjController cellPrefab;
List<TetrisObjController> tetrisCells;
/// <summary>
/// TetrisFieldのデータを基にGameObject作成
/// </summary>
/// <param name="field"></param>
public void Init(TetrisField field)
{
var cells = field.TetrisCells;
tetrisCells = new List<TetrisObjController>();
for (int y = 0; y < cells.GetLength(1); ++y)
{
for (int x = 0; x < cells.GetLength(0); ++x)
{
var obj = Instantiate(cellPrefab);
tetrisCells.Add(obj);
obj.transform.SetParent(transform);
}
}
}
{中略}
}
はいここマサカリ部分です。
モデル部分では二次元配列として定義してた場所が、GameObjectとして定義するときには1次元可変長配列の構造になってます。
固定長配列でGameObjectの一覧を持つ方法、あるのだとは思うけど面倒そうだったので、いつも実装してるやり方に持っていった結果こうなりました。
ここにモデルの状態を反映させていくわけだけど、インデックスの変換作業が必要になるよ。面倒だね。
で、状態の遷移がモデル→ビューだけならまだよかったのでしょうけど、
今回はビュー→モデルへの変更を伝える必要がありました。
プレイヤーのヒップドロップです。
テトリス周りのロジックが自身のルールに則って盤面を制御する一方で、
プレイヤーは任意のタイミングで盤面上のブロックを押し出しできるわけです。
バグの温床要素ですはい。
この辺を上手くまとめるのにも時間がかかってます。そりゃそうだ。
ここで色々なバグ潰しフェーズが入るのですが、長くなるので割愛。
これ周りのバグは大分潰しましたが、実はまだ一つバグが残ってて、列が消えたと同じフレームで同じ場所で列が揃うとバグります。ナオシテナイケドサイゲンリツヒクイカラエエヤロ
ある程度ゲームがまとまってくると、別の課題が見えてきます。
そのゲーム、本当に面白い?
ゲーム作る上で必ず立ちはだかるであろう壁。遭遇したことがない人はいないでしょう。
傍から見たときに面白いと感じられるかどうかは別途考える必要があるとして、まずは自分が面白いと感じるかどうかが大事です。
実装もままならない状態の中、出来ていくものは自分にとっても面白みを感じ取れないものでした。
ゲームを面白くするための、バランス調整のフェーズが始まるわけです。
なおこの時点で日付は6/25(日)。
遅刻が確定した状態で、バランス調整諸々を行っていきます。
ちなみにゲームループもこの時点ではまだの状態。
(仮実装はしてたけど演出面とかは全然未完成状態)
落下場所の調整
プレイヤーを操作させる代わりに、ミノの落下そのものは操作できないようにしています。それにより、落下場所の偏りが発生し、歪な塔が出来上がってしまう問題がありました。
ということで、そこそこの確率で、一番埋まってないX座標に向けてミノが落下するように調整されています。
これによって落下位置の偏りは多少解消されましたが、実はこれには罠があります。
ひとまずミノ落下ロジックを再掲します。
この処理では2.の落下場所が決まるだけで、隙間が埋まるように落下するわけではないため、空いた穴に蓋をする形で塞がれるなんてことも。
この調整、罠ではあるけれど、実は割と気に入っていて、
蓋状態のブロックを押し出しすると、
再度そこに蓋をするようにミノが落下しやすくなるんですよね。
つまりリスクとリターンの駆け引きが生じるわけです。
リスクとリターンの駆け引きいいよ。
落下位置を調整した。これでOKか?
まだです。これだけでは偏り問題は解消していないので、詰みな積み方が起こることがあります。
真っ当なプレイをしているのに詰む。という事態は避けたかったので、その対策もしました。
やってることは大体画像の通り。埋まってる場所が落下場所に選ばれてしまったら、空いている場所に向けて落下するように再抽選処理をしています。
厳密には、一番積まれてない場所に縦のIミノが確定で落ちます。所謂救済Iミノ。
ver1.0.0時点だとこれで確定だったけど、救済Iミノの発生条件が厳しく、
なかなか起こらず待ちが発生するという状態だったので、
低確率だけど無条件で救済Iミノが発生することがあるように修正しました。
……実装が絡むバランス調整は大体こんな感じです。
あとはレベルの上昇具合とか、スコア計算式の調整とかをしてたりとかを、実際に遊びながら調整してました。
この時点で日付は6月末くらいです。ヤバいですね。
この辺りで見た目や演出等の調整を改めて行っていました。
あとインタラクティブミュージックの適用をこのタイミングでしてた。絶対実装順序間違えてる。
そんなかんやで、最終的に一般公開したのは7/2(日)の18時頃。
ほぼほぼunity2weekでの提出となりました。
進捗周りのまとめ
遅刻の原因としては、慣れない実装をしてたというのが一番大きいんじゃないですかね。
慣れてる人なら多分間に合ってる知らんけど。
遅刻自体はu1wのコンセプトとしても問題ない(くらいに緩い設定)にはなっている一方で、遅刻によるデメリットもあるわけです。
(自分の作品を評価される期間が短くなる即ち遊ばれにくくなる、自分が他の人のゲームを遊ぶ時間が無くなる、など)
個人で作る場合には、それを承知の上での遅刻でしょうから特に言及するつもりはないのですが、チーム製作の場合には、相方への負担が大きくなることや、TeamUpの肩書にも影響を与えかねないので、そういう意味では今回の大きな反省点ではありますね。
TeamUp自体は今後も続けていきたいとは考えておりますが、
その規模のゲームが間に合わせられるかどうかというのをディレクションできる能力というのも鍛えていきたいところです。
あとはデザイナー様と共同制作できる力も蓄えなければ!!
---------------------------------------------------------
ということで振り返り終わり!!
ここまでで約1万文字!過去一長い振り返り記事になりましたとさ。
終わり。
この記事が気に入ったらサポートをしてみませんか?