プログラム初心者が2年でゲーム会社に就職するまで
アドべントカレンダー参加について
この記事はdiscordサーバー"まんてらスタジオ"の企画
【まんてらスタジオ】みんなの闇鍋 Advent Calendar 2022
に向けた記事です。プログラム初学者の二年の軌跡(記事作成時一年と7ヶ月くらい)を垂れ流したものですが、どこかで役に立つと良いと考えて書きます。
はじまり
ゲーム業界で働き、ゲームを作りたかったがプログラムが一切分からなかった。加えて、どう勉強すればよいかも分からず独学で挑むも挫折を5回ほど繰り返していた。情報系とは全く関わりのない学科だったため触れる機会自体がそもそもなかったことも燻りに拍車をかけた。
という流れで大学の理系学科を卒業したあと、親の反対を押しのけて某専門学校に入学した。コースは2年制。年齢的にも金銭的にも3年や4年かけられるほど余裕がなかった。
当然同じコースのメンツも同じような境遇だろう。切磋琢磨して行けるのだろうと思いながら一年目がスタートした。
スタートダッシュ
4月の中盤に授業がスタートした。と、思いきやいきなりGWが迫り、授業はたったの一回で2週間の休みに突入。
当然プログラムを勉強したくてたまらず、時間の焦りも感じた私は買うべき本を教師に訪ね、"ロベールのC++入門講座"という本を購入した。
(余談ですがこのコースではC++とUnity/C#を同時並行で学んでいた。C++の方が勉強するのは難しいと考えたためそちらに注力することに・・・)
お値段五千円前後。手痛い出費だが背に腹は代えられない。
一週間で読み切ることを決意し、実行。何が書いてあるのかわからなかったり、言葉の意味もよくわからない状態だったが、逐一気にせずにとにかく全体を通して何をしているのかのイメージだけ叩き込んだ。
その後環境構築だけ済ませただけの状態であるVisualStudioと向き合い、ひたすらに模写を繰り返した。
模写に飽きた私は所々改変して遊んで見ることにした。が、出るわ出るわ赤いエラーの嵐。しかもVisualStudioは日本語が不自由なのか何を言っているのかよくわからない。
この時点でfor文だのif文だのint型だのという基本文法は理解していた私だったが、エラー一つ治すのに途方もない時間を使うことが多かった。
特に苦労したのは配列の踏み外しだ。
//当時やらかしたやつ
int num[10]={0,1,2,3,4,5,6,7,8,9};
int main(){
for(int i=0;i<11;++i){ //配列の外を参照している
...//足したり引いたり
}
}
宣言した大きさ以上のメモリ位置に不用意に書き込んだ内容を何処か別のところで読み込んだり、書き込んだりしようとしたときにプログラムが落ちる。
しかし出てくるエラーや止まった場所は原因とは全く別の場所である。当然訳が分からない。
こんなエラーを繰り返し見てエラー文と理由がなんとなくつながってくる過程でメモリとはなにかを掴み始めた。
学び:エラーは起こしてからエラー文の意味を理解しながら原因を学ぶのが大事。ただしそのエラー文が本当かどうかは分からない。配列には気をつけよう
戸惑い
GWが明けた。同級生と勉強の成果を話そうとウキウキで向かった私を待っていたのは、GW前から何も進捗がなく、遊び呆けたりバイト三昧で過ごしていた同級生たちだった。
専門学校。それも二年制。二年目は3月から就活だから勉強できるのは実質一年間。それなのに私の覚悟と焦りとは裏腹に誰も勉強していなかった。
正直戸惑った。誰かに相談したいこともあったし、自慢したいこともあったが、全部できなかった。
もう一つの覚悟を決めた。それは、周りがどれだけ遊ぼうとも自分は一年間勉強をやりとげること。ゲームづくりを続けること。
プログラムが上手く動かない以上に現実の環境構築が怖かった。
学び:学校にいるとはいえ全員が不退転の覚悟で来ているとは限らない。他人に引きずられない事が大事
初めてのゲーム制作
四半期に一つ、ゲームを作成して講師の前で発表する機会があるらしい。
私は昔から作ってみたかった2DRPGを制作することにした。
マップチップを並べてマップを作り、ランダムエンカウントで戦闘シーンに移行するようなあれだ。
環境ではDxLibを入れて制作することになり、よく分からないまま導入。意気揚々と仕様を考えて端から書き始めた。
が、作り始めたもののループしているmain関数の中が300行以上続く上にif文の分岐が多発して頭が爆発しそうになるコードになってしまった。
処理の流れも殆どわからずデバッグすらできない。当然バグの原因もわからない。
ここで初めてファイル分けを学んだ。
Unity/C#と違い、C++だけで作る場合は.cppファイルとは別に.hファイルも使用する。このヘッダーファイルというものを、随所でincludeすることによって別のファイルの中身が使用可能になるのだ。(簡潔に省略)
すでにclassを作成し、メンバ変数なども実装していた私は別のファイルにclassごと移動させて、includeでつなぎ、いざ使おうとしたが・・・
コンパイルエラーの壁に阻まれた。
#include"Player.h"
Player player;
int main(){
player::Move();//何故か呼べない
}
ここでポインタらしきものが必要だと思い出した。ロベールでやったやつだ!
で、書いた。
#include"Player.h"
Player* player;
int main(){
player->Move();//エラー
}
???????????
いやポインタ変数にしたのになんかエラー吐いたんだが?
初学者がよく躓くポインタの穴にすっぽりとハマった。
仕方ないので、この際意味は理解しなくてもいいから使えるようになろうと方針を変換した。
ポインタ変数にnewしてクラスのインスタンスを作って入れると変数を介してクラスの中にアクセスできるらしいぞ!
呪文か?
と思ったがいよいよ分からなかった言葉を調べる時期に来たと思い、それぞれ調べた結果がこうなった。
#include"Player.h"
Player* player;
bool init=false;
int main(){
if(init==false){
player=new Player();
init=true;
}
player->Move();//動いた!
}
このコードになるまでも、毎フレームクラスのインスタンスを生成したせいでVisualStudioが固まったり、main関数冒頭でbool変数を宣言したせいで何度もインスタンスが生成されVisualStudioが固まったりと事件を引き起こした。
が、ファイル分けは成功。コードの分割にも成功し、なんとmain関数は最初400行以上あったのが50行弱にまで減少!
気を良くした私はGameManagerとやらを作って全体を管理させようとしたり、SceneManagerとやらを作り、シーン遷移でマップと戦闘を切り替えようとしたりと、講師のアドバイスやネットの記事をみてひたすらに実装を重ねた。
もちろん出会ったエラーの数が膨大であったことや、メモリの開放をしていなかったために起きるメモリリークとやらでPCのメモリの80%をVisualStudio周辺で使うという大事件があったことも書いておく。
とは言えRPGっぽいものは出来上がり、三すくみのルールを組み込んだコマンドバトル、一マップだけではあるがランダムエンカウントや画面スクロール、街への出入りができるマップ、主人公と敵のステータスの実装など最初のゲームにしては要素が多めなプログラムが完成した。
書いている現在で見直すととんでもないスパゲッティコードだが、それでもゲームが動いたことは当時の私にとっては大きな一歩だった。
動きゃええねん動きゃ
学び1:作りたい物は迷わず作り始めろ。最初は動けば良いでなんとかなる。動くのが偉いのだ
学び2:エラーを恐れるな。エラーで止めてくれたVisualStudioに感謝しながら罵倒しよう
2回目のゲーム制作:シークエンスとはなんぞや
いきなり技術的なワードになったが、この当時のプログラムは、処理の流れをすべてif文で実装しているせいでデバッグはやりづらいしあとから読むと訳が解らなくなる現状なのは変わらなかった。
というのも、次に作ろうとしていたのはすごろくと育成ゲームを合体させたファンタジー育成シミュレーションゲームだったのだ。(パワ○ロの栄○ナ○ンを元ネタにしたようなゲーム)
このまま進めれば今よりもっと複雑で分岐にまみれた地獄が広がるだろう。
時期的には9月。もう半分しかない。
ここで今の処理の流れを軽くおさらい。
現在はシーンを分け、その中のUpdate関数を毎フレーム呼ぶことでゲームが回っている。このUpdate関数の中でif文が何十にも折り重なり、分岐している。
それならば、各条件で処理の内容を分けた関数を作り、Update関数の中で呼び、その呼ぶ関数を変える処理を実装することで、条件に応じて実行先が切り替わるようにすれば分かり易いではないか!
というようなのがシークエンス処理のイメージである。
最初は自作したただの返り値voidの関数を切り替えたものを実装した。
プレイヤーの操作を待っている関数。
選んだ日程分だけすごろくを移動する関数。
使ったカードの効果と止まったマスの効果を発動させる関数。
これらをif文で呼び出すものを切り替え、なんとか分かりやすくすることが出来た。
その後更に勉強をすすめ、関数ポインタなるものを使ってみようとなるのだが、そこは省略する。
二回目のゲームづくりから意識したのは、自分が知らない、もしくは使ったことがないC++の機能や処理の考え方、クラス設計などを積極的に行ってみようということだった。
シークエンスの考えもその一部であるし、csvファイルにデータを書き、ゲーム開始時に読み込むことでデータのべた書きを避けたり、publicとprivateに分け、アクセス制限を行ったり、基底クラスを作って継承させてみたり・・・
ただ動けば良いゲームは作れたのなら、今度はより分かりやすくより先進的なコードに少しでも近づくための勉強とチャレンジをしようと考えた。
ゲームは完成したが、オリジナル要素が薄く、元にしたゲームをただファンタジーナイズしただけの物になってしまった。
しかし今はそれでもいいと考えた。一つ前のよりも飛躍的にコードの質が上がり、設計についてもなんとなく理解し、使ったことがないC++の機能や書き方もいくつか使うことが出来た。目的は達成されたと考えた。
学び:知らないことや分からないことには効率化のためのヒントやゲームを面白くするための力が眠っている。どんどん手を出すことが大事
就活作品の制作
時期的には1月初旬。
3月によーいどんでスタートするためにはそろそろ就活作品が欲しかった。
2作品目でも良かったが、せっかくなので温めておいたジャンルにチャレンジすることにした。
それはダンジョンローグライクだ。
簡単にいえばトルネコやポケモン不思議のダンジョンのような2Dでランダム生成されたダンジョンをターン制で攻略するゲームだ。
ローグライクというジャンルは就活作品として評価されるコード上の処理を多く含んでいる。ターン制の実装、描画順序のソート、マップの自動生成、経路探索、オートタイル、膨大なデータの作成と読み込み・・・
実装すべき項目は山ほどあるのにここに少しでもオリジナル要素を加えなければ就活タイトルとしては評価に値しない物となってしまう。
しかもこの作品は最後の講師発表でも使用するつもりであったため、提出期限が2月の上旬だった。開発期間は一ヶ月と少ししかない。
ただ、この頃にはもうある程度C++にも慣れ、以前に作ったコードという資産もあったためスタートはスムーズに動くことが出来た。
という流れでぬるりと始まった一年最後の作品の目標は、一年の集大成として学んできたことを最大限活かしたゲームを作ること、と決めた。
実装で詰まったのは主に2つ。
1つ目はマップの自動生成。2つ目は敵の徘徊経路探索だ。
自動生成では区域分割法という手法を使うことを決めた。画面全体を縦と横に切り分けていき、割ったエリア内に部屋をランダムな位置に作成,分割線を通路にしてつなぐ方法だ。
分割方向をランダムにしたり、一つ前の部屋とつなぐために配列に登録する順序を調整したりと、配列の操作や処理順の確認が忙しなく、難しかったがなんとか実装した。そのままでは一直線のマップになるため、端の部屋からは別の通路を伸ばし、周回出来るように工夫も加えた。
こちらは何をすればよいのか、最後にどうなっていればよいのかの形がはっきりしているため、ノートに図を書いたりしつつ整理と実装を交互に続けることで完成まで持っていけた。
しかし問題は敵のAIである。
プレイヤーの周囲にいないときはマップを無作為に徘徊し、プレイヤーと同じ部屋に入るか3マス以内に入れば追跡する。
特に徘徊モードのときの経路探索を実装するのに苦労した。
A*やダイクストラなどの有名な経路探索アルゴリズムもあるが、毎フレームそれを行うのでは重くなりすぎる。
結局徘徊ロジックは自力で組むことになり、自分が進むことが出来る方向のマスからメルセンヌ・ツイスタを用いたランダム処理によって方向を決定し、偏りが出にくい徘徊ロジックを組み上げた。
なお、プレイヤーと同じ部屋に入った瞬間にプレイヤーを追っかけ回す行動パターンも実装したが、部屋の中に障害物がないことと、部屋が短形であることからA*の実装はここでも行わなかった。
最後にオリジナル要素というか、スパイスだが、アイテムの実装と共に、ランダムパラメータを実装してみることにした。
私自身がトレジャーハント要素を持つゲームが好きで、同じアイテムでも数値が違う物をひたすら集めては比べてより良いものにしていく工程を楽しむことが出来る、そんなゲームを作ってみたかったのだ。
実装目標は一定の幅の中で変動するパラメータを持つアイテムの実装。
ではどうすれば作れるのか。
選んだ方法としては、csvファイルにステータスパラメータの中央値を決めておき、こちらもメルセンヌ・ツイスタを用いたレンジ内のランダム関数を作ることで最終的にプレイヤーが入手するアイテムのステータスを決定するというものとなった。
また、アイテムを入手したあとにしまう場所であるインベントリの実装も配列操作やポインタの所持者を誰にするのかなどの問題にぶち当たったが、なんとか提出期限までに実装を終えることが出来た。
講師発表は終わり、いよいよ3月。
未経験からひたすらに駆け抜けた一年間の成果を企業にぶつけ、熱意と覚悟、今の技術を見てもらう時が迫っていた。
学び:コードを書くことに慣れたならアルゴリズムに挑戦したり、全体の設計を考えてみたりすることが大事。最新のC++要素もチェックするとより良い処理が書けるかもしれない
就活戦線異常あり
3月がスタート。この日のために用意したReadMeには作品の情報や製作にかかった日にちなど様々な項目をできる限り分かりやすくまとめた。共有用にGitも一から勉強し、各作品をまとめたポートフォリオも制作。
この記事では書いていないが、Unityを使った3Dジャンプアクションゲームも作成し、その一つに加えてある。
ローグライクは簡単な動画もとった。
一年間のすべてを集めた現時点で最高の武器を揃えた。
しかし現実は厳しい。特に3月は大手ゲーム会社が多く、そのほとんどにおいて書類選考で沈むことになる。
私の最終的に書類選考にチャレンジした企業の合計数は33社。
3月中に出したのはそのうち15社だった。
そこから一次面接に呼ばれたのはわずか1社。
その1社も二次面接で撃沈することとなる。
ここまで苦戦した理由の一番大きな要因は3Dのゲームを制作していないことだった。C++でゲームを作る以上、どうしてもDirectXやOpenGLなどを使用した3Dのゲームを作る技術を求める企業が多い。最近ではUnityを使う企業が多いが、あくまでも私の主力はC++。2Dゲームを作ることで精一杯だった一年間ではスタートラインにすらたどり着けていないのかも知れないと感じた。
4月、5月と進むに連れて焦りが出てくる。書類選考通過数は増えては来たが、そもそもの募集が減ってきているのだ。
私の場合は常に10社前後を選考待ちの状態にしておきたかったため、ほぼ毎日企業を探しては応募書類の体裁を整えていた。
面接ではゲームづくりの道しか無い事、一年間でやってきた勉強とその知識、意欲、成果があること、会社に入ってからはただのエンジニアだけでなく、ディレクターを目指していくことなどを訴えた。
わからない言葉をひたすらに調べては知識としてインプットしていたことがここで活き、面接官のエンジニアとの話にもついていくことが出来た。
そして二次面接を突破する企業が出てくる。
この時点で受けた会社数は31社。書類選考を通ったのは11社。一次面接で散ったのは7社。
つまり31社|書類選考の壁|->11社|人事面接の壁|->4社|現場面接の壁|->1社というような状況。
よく30社受けて内定が1社出てくる みたいな話を聞いていた。
焦りにまみれながらもここで最大のチャンスが来たことで、その話は本当なのかもしれないと思うようになる。
最後の役員面接の前日。さらに1社最終面接に進む会社が出てくる。
初めて自分が優位に立ったような気分になったが、まだ結果は得ていない。
その役員面接は全く表情が変わらない社長と、にこやかに質問を繰り出す役員との面接だった。
怖くて仕方がなかったし、何を話したのか全く覚えていない。
たった一年しか勉強に費やしていない自分に最大限貼ったメッキが剥がれていくような感覚だけ覚えている。
正直落ちたと思った。ちゃんと受け答えできた感触がまったくなかった。プログラムの話もしっかり答えられていただろうか。社会人としての心構えや覚悟を示せただろうか。
半分諦めながらもう一社の最終面接に向けて手元資料を作り直した。
結果
内定が出た。
最初の役員面接にして最初の内定。
肩の力が抜けた。その時点で1年と2ヶ月しか学ぶ期間が無かった初学プログラマーが、ゲームづくりのプロの道を切り開いた。
期間はその短さだが、費やした時間と密度、覚悟は他の誰よりも大きかったと胸を張って言える。だからこそ嬉しかった。
一社内定が出ると途端に元気になる就活生。失うものが無くなった者は面接にも適度な緊張で望むことが出来るようになった。
二社目の最終面接の直前にもう一社最終面接の案内が来る。
これで合計33社。内定が出たことからこれ以上追加する気はなかった。
二社目の最終面接は失敗した。役員の質問したことと、私の返した言葉が噛み合わない印象を私自身も持っていたから、うまくいかないだろうとはっきり感じていた。
三社目の最終面接。泣いても笑ってもこれが最後の就活。
思い切ってゲーマーとしての私もさらけ出すことにした。どうせ何も失わない。この自分を含めてほしいと思わせることが出来たなら、今後何年もその会社でやっていけるのではないかとも考えていた。
三者目も内定が出た。選ぶ側になってしまった。こんな事が起こるとは3月時点では考えてもいなかった。
その後働く環境や内容、使用言語や給与、会社の展望などを聞き、二週間時間を使って考えた結果、二社目の内定先に承諾を出すことになった。
相当迷ったが、最後の最後で自分を出し尽くしてなお話を聞いてくれたという経験と今後の展望から後者に行くことにした。
この時6月の終盤。就活を初めて3ヶ月と少しだった。
生きた心地がしなかった。あの一年間のプログラミングの全てをぶつけても、プロの壁は遥かに高く厳しかった。これを書いている今までもずっと新しいゲームを作り続け、勉強を続けているが、それでも入社時ではまだひよっこもいいところだろう。
だが。勝ちは勝ちだ。他の人よりも早くはじめ、スタートダッシュし、誰よりもエラーと戦い、周りに引きずられる事なく走り続け、お祈りメールの山を乗り越えた勝ちだ。
入り口には立った。来春からは私もプロの一員となる。人生をかけて面白いゲームを作るために生きる。専門学校に入るときの覚悟は今でも薄れていない。
学び:努力した結果は評価されるとは限らない。ただし、努力した過程に得た教訓やそのときに持った気持ちは絶対に無くならない。例え初学者からでも一年と少しでプロの入り口に立てる。死ぬ気の覚悟さえあれば。
駄文長文をここまで読んでいただきありがとうございました。
次はプロとして業務に携わり、作品を通して私の覚悟と結果を示せればと思います。
いつかあなたの手に私のゲームが届くことを祈って。