かさやぬすの日記

後で思い出したいことを書く

0から一人で作った web サービスが滑った話

去年6月、初めて一人で作ったwebサービス『部屋立てさん』をリリースした。

『部屋立てさん』は、「ゲーム配信者がリスナーと交流する時に、順番待ちを支援する」webサービスである。

www.heyatatesan.com

使用例としては、例えば、ぷよぷよなどの大戦ゲームをファンと配信者がやりたい時に、順番待ちを管理したいときなどに利用することを目的としている。

しかし、『部屋立てさん』は twitter 広告などを使っても全くユーザーを得ることはできなかった。

それは、需要を見極めることができなかったということに尽きると思う。しかし、一方で、web サービスを0から自分一人で作ったことで学んだことも多かった。

今回は、失敗してしまった理由と 0 からwebサービスをリリースして学べたことについて記す。

 

0. 『部屋立てさん』の技術スタック

まず『部屋立てさん』の技術スタックを紹介する。

当時、isucon の予選問題を解いていた影響で Go 言語に馴染みがあったため、Go 言語を使用している。

  1. サーバー言語 :  Go 1.13.10
  2. フロントエンド : jQuery + Bootstrap
  3. webフレームワーク : echo
  4. webサーバー : nginx
  5. データベース : MySQL 5.7
  6. OS : Ubuntu 18.04
  7. サーバー筐体 : AWS EC2

1. 戦略的に失敗した点

まず、失敗した理由についてだが、簡単に言えば、

ゲーム配信における需要の分析が甘かった

『部屋立てさん』でゲーム部屋を立てることで、確かに順番待ちできるので秩序を保てる。しかし、それは、配信者が秩序を欲しがっていればの話だった。

現在、youtube などで対戦型ゲームの配信をしている配信者を観察していると、挑んでくるリスナーの順番は管理していない。早い者勝ちで順番を決めている人がほとんどである。

そう、配信者からすれば、リスナーが挑んでくる順番はそれほど重要ではなかったのだ。

むしろ、順番待ちを管理することでデメリットすら生まれるということに気づいた。具体的には下記の3点である。

  1. そもそも外部サービスを使って管理するのはめんどくさい
  2. 順番にまだ余裕のあるリスナーが配信から一時的に離れてしまう可能性がある
  3. 配信者が飽きた時、気軽にやめられなくなる

つまり、順番待ちとは、リスナー側にとってのみありがたいシステムであって、配信者が望んでいるわけではなかったということだ。

開発当初、この視点を持つことができなかったため、需要のないサービスを作ることとなってしまった。

 

2. 開発を通して学んだこと

一方で、0からwebサービスをリリースしたことで、インフラ、サーバーサイド、フロントエンドまで多くのことを学べた。

 

1. インフラ

2. サーバーサイド

3. フロントエンド

4. その他( 広告、解析など )

3. 企業で運営するwebサービスとの相違点

 『部屋立てさん』は、企業のプロジェクトに参画する前に作成したサービスだった。その後、企業で実際に運用保守されているサービスに生で触れたことで、『部屋立てさん』のシステムとの大きな違いに気づいた。

最も大きいのは、予算の違いに伴うサーバー台数の違い( 開発環境が用意されている、レプリケーションが行われている、バッチ専用サーバー、Jenkins専用サーバーがある)であったが、本質的な違いを感じたのは下記の3点であった。

3-1.  CI/CDツールを使っている

多くの企業サービスでは、本番デプロイなどの自動化のために、Jenkins をはじめとする CI/CD ツールを利用している。『部屋立てさん』ではこれを使っていなかった。常に手作業で git clone し、 go build して、 systemctl restart を行なっていた。

( ※ 最近になって、3ヶ月ごとのサーバー証明書の更新作業を Jenkins ジョブで行うようにした )

 

3-2. ログを解析するためのシステムがある

ユーザーID と紐づいたリクエストレスポンスの行動ログを残すシステムが企業のwebサービスには備わっている。これにより、問い合わせに対して効率的にログを分析することができる。

『部屋立てさん』にはユーザーごとにログを解析するための仕組みが備わっていなかった。nginx から出力されるログを解析するためのツールが備わっていなかった。

 

3-3. バッチを利用している

現在の機能では、バッチを使う必要はなかったが、例えば『人気のルーム』のランキングをトップページに表示するなどの機能があった場合、cron で定期実行するバッチが必要となっただろう。

 

4. まとめ

今回 0 からサービスを作ったことで、新しいサービスを作るときの心理的な壁は劇的に低くなった。(『部屋立てさん』を参考にして、適宜書き換えればいいので)

今度はもっと需要を研究して、新しいサービスを立ち上げてみたいと思う。

 

CODE VEINをクリアしたので感想。

CODEVEINはダークソウルのような高難易度の探索アクションゲームに、さらにJRPG的なストーリー要素を加えたゲームである。ゴッドイーターで有名なSHIFT社の開発である(パブリッシャーはバンダイナムコ)。SHIFT社のゲームはCODEVEINとゴッドイーターしか知らないが、ゴッドイーターはよくモンスターハンターにストーリー要素を加えたゲームだと言われることも多いが、このゲームもその例に漏れない。この会社は既存のゲームにストーリー要素を加えるのが好きなのかもしれない。

以下ダークソウルと比較してこのゲームの感想を述べる。

 

f:id:kasayanus:20191014164411j:plain

1. よかった点

1-1. 音楽、キャラクター

    ストーリーがあるゲームの方が好きなのもあるが、キャラクターがちゃんと喋ってくれるのはダークソウルに比べて嬉しいことだった。さらに音楽もゴッドイーターの時のような豪華なオーケストラによる演奏があり、重要な場面の雰囲気をしっかりと演出できていた。

 

1-2. 血英回収した時の回想の演出

   このゲームでは血英というアイテムを入手すると、キャラクター太刀の過去をに回想できるようになるシステムがある。この回想の中でキャラを動かすことができ、まるで彫刻が展示されている美術館のように、ライトアップされてキャラクターを模した彫刻がその時の過去をセリフとともに再現する。(説明が難しい)

この演出がとてもよかった。音楽も相まって、キャラクターの過去の回想がとてもセンセーショナルに演出されており、物語に引き込まれた。

 

1-3. 探索で通った足跡が残る

  このゲームではダークソウルと同じように、強い敵がたくさん出てくるダンジョンを探索していき、中間地点を開放していきながら、先に進んでいく。ダークソウルでは、マップがなく、道に迷うことも多いが、CODEVEINではマップは中間地点を開放していくと、見れるようになっていくシステムである。さらにマップがなくても、探索で通った道には足跡が残るので、どこが初見の道なのかがすぐわかるようになっている。これにより同じところをグルグル回ることもなく、快適に探索をすることができた。

 

1-4. バディと探索できる

 このゲームのダークソウルとの違いはバディがいつもついてきてくれるところだ。探索時にも頼りになるし、セリフも喋ってくれるのでダクソの探索の時のような心細さがない。

 

2. 悪かった点

2-1. チュートリアルが長い

  ゲームを始めるとすぐに戦闘チュートリアルが始まるが、一度に全ての機能を教えてくるので、覚えられない。また、ゲーム内の専門用語が全て"血"から始まる似たような熟語であり、覚えづらい。

 

2-2. 同行キャラはその場で変更できた方がいい

 探索に同行するバディは拠点で同行させたいキャラに話しかけないと、変更することができない。探索エリアを移動すると長いロード時間を挟むのでこれがストレスになった。

 

2-3. 拠点で持ち物が使えない

  拠点で装備の強化やアイテムを買うことができるが、そのためにヘイズと呼ばれるてきを倒した時のもらえるゲーム内通貨が必要になる。これを増やすアイテムがあるのだが、拠点では使えないため、所持ヘイズが0の場合、いちいちダンジョンに戻って、ヘイズを増やしてから拠点に戻らなければいけない。長いロード時間を挟んでしまう。

 

2-4. 主人公喋った方がいいのでは

  ゴッドイーターの時も同じだが、キャラクリシステムがあるため、主人公は基本的に喋らないドラクエと同じシステムである。しかし、ストーリーでも主人公は重要な役割を持っているので、個人的には喋って欲しいなと思った。

 

2-5. ストーリーが途中から追えなくなった

   ストーリーの中盤まで(血涙の源泉を探す)は話の流れは追えたのだが、中盤以降の継承者を開放(?)していくあたりから、ストーリーが飲み込めなくなっていった。ダクソとの差別化をするなら、ストーリーも分かりやすいように案内して欲しかった。

Mediatorパターンの使い方について考えたこと

GoFのmediatorパターンを採用すれば、シーン内のオブジェクトの通信を一つのクラス(mediatorクラス)に集約することができる。これにより次のような利点があると考えられる。
www.techscore.com

  1. シーン全体の通信のやりとりの様子がわかりやすくなり、デバッグしやすくなる。
  2. 各オブジェクトのメンバ変数は、自分自身の状態変数以外にはmediatorクラスのインスタンスしか必要なくなる。(外部とのやりとりは全てmediatorクラスに任せることが出来る。)
  3. シーン内の新しい機能追加に対してその通信に関するやりとりの変更はmediatorクラスにのみ影響するようになる(プログラムの変更の影響範囲が狭められる)

しかし、mediatorパターンを誤った使い方で用いてしまうと、このクラスの実装者はシーン内のオブジェクトの仕様ややりとりの様子を全て把握しなければいけなくなり、極端に実装の負担が増えてしまう。(Mediatorクラスのゴッドクラス化問題)

以前少しだけ、Reactフレームワークを勉強した時にReduxと呼ばれる Fluxアーキテクチャーについて学んだ。このFluxアーキテクチャにはMediatorパターンをより使いやすくするためのヒントがあるのではないかと自分は考える。
qiita.com
以上のことを踏まえて現時点で自分が考えるMediatorクラスの良い使い方を述べたい。
簡単にその実装のポイントを先に述べると次のようになる。

  1. ReduxにおけるActionの役割を持つMessageクラスまた列挙型Messageを定義する。
  2. MediatorクラスはPublicメソッドMediate(Message message)を持つ。(ReduxにおけるReducerの役割)
  3. CollegueクラスはMediatorに通信を発行するSealedメソッドSendMessage(Message message)と、Mediatorクラスからの通信を受け取るメソッドAcceptMessage(Message message)をもつ。
  4. Collegueクラスを継承したGameObjectではAcceptMessageのオーバーライドを行う。(Messageによる条件分岐が必要なので、実装方法はUnityのOnCollisionEnterメソッドのオーバーライドに似た実装を想定。)
//Colleagueクラス
public class Colleague : MonoBehaviour{
  [SerializeField]
  private Mediator mediator

  public sealed void SendMessage(Message message){
       mediator.Mediate(message);
 } 

  virtual protected void AcceptMessage(Message message){
       //継承先で実装する
  }
}
//Mediatorクラス(シーン間共通情報(所持金など)を持たせるのもありかも)
public class Mediator : MonoBehaviour{
   [SerializeField]
   private Colleague[]  colleagues;

   public void Mediate(Message message){
         foreach(var colleague in colleagues)
                 colleague.AcceptMessage(message);
  }
}

※Messageクラスまたはenum型Messageは必要に応じて状態を増やす。

これにより、Mediatorクラスは具体的な通信の内容まで知る必要がなくなるので、クラスの実装者の負担は最小限に抑えられる。
また、シーン内の要素追加などによる仕様変更についてもMediateメソッドの変更によって吸収できる。

課題

  1. MediateメソッドがCollegueクラスのSendMessage以外からも呼ばれてしまう。
  2. SendMessageがMediatorクラス以外からも呼ばれてしまう。
  3. Mediateメソッドは登録されてるCollegue全てのAcceptMessageメソッドを呼び出すので多くのクラスにとっては空メソッドを呼び出すことになってしまう。また、CollegueのAcceptMessageの条件分岐が間違えると誤った通信を行ってしまう危険性がある。

『ラブライブ!サンシャイン!!』を見たので感想。

ラブライブ!サンシャイン!!』をTSUTAYAで借りて見た。この作品のファンもたくさんいるのであまり悪いことは言いたくないが、ここは触れた作品や本の感想を書くためのブログなのでここでは思ったままのことを記そうと思う。まず先に断っておきたいが、これだけたくさんのファンがいるラブライブという作品に素直に敬意を感じている。ここまで多くの人を魅了できる作品はそう容易く作れるものではない。もしこれから述べる考えに申したいことがあるときは自由に反論していただいて構わない。

f:id:kasayanus:20190923220451j:plain

自分がアニメやゲームをやっていて萎えてしまう瞬間というのが一つある。それはゲームやアニメの制作サイドの意思や人工的なものを作品の中に感じてしまった時である。"人工的なもの"というのは説明するのが難しいが、例えば、作品のストーリーにあからさまな"起承転結"が垣間見えると冷めてしまう。なぜなら起承転結というのは「人がフィクションの物語はこうあるべき」と考えた極めて人工的なガイドラインだからだ。これはあくまでただの例だが、とにかく作品の中にに人間を感じると冷めてしまうのである。そして『ラブライブ!サンシャイン!!』も作品の中に人工的なものを感じてしまった。以下ではどこに人工的なものを感じたのか、述べる。

ラブライブは主人公が高校生活の中で一緒にスクールアイドルを目指してくれる仲間を探すところから始まる。最初のうちは部員がなかなか集まらなくて、勧誘されても「スクールアイドルにはならない」と断られるシーンもある。それでも必死に説得したり、真正面から気持ちをぶつけることでスクールアイドルの魅力に気づいてもらうことで仲間を増やしていき、ようやく9人のメンバーが揃うのである。

しかし、ここで問題なのは、視聴者は一話の時点ですでに誰がのちにメンバーになるのかをわかっていることである。OPやEDでは、まだメンバーが集まっていない一話の時点でaquorsの全メンバーがステージで踊っているシーンがある。これはネタバレということではなく、制作サイドももちろん意識してそういう風に作っているはずだ。おそらく制作サイドは「これからaquorsのメンバーがどうやって繋がっていくのか、その過程をアニメで見て楽しんで欲しい」という風に考えているのだと思う。しかし、これがどうしても自分には合わなかった。例えば、学校の生徒会長でのちにメンバーになる黒澤ダイヤは、初めスクールアイドルが嫌いと宣言した上で主人公の持ってきたアイドル部活新設のための書類を却下する。しかし自分的にこのシーンで思ったことはこうである。「でも後で仲間になるんでしょ?」。誰がメンバーになるかすでにわかってる時点で、こういう熱いやりとりが全て茶番に見えてしまうのである。「いろいろアイドルについて反対してるけど結局仲間になるじゃん」。こう一度感じてしまうと、aquorsのメンバーが仲間になるまでの全ての熱いやりとりは、アニメの製作陣の人たちが脳で考えたただの虚構に見えてしまうのである。

ここまで書くと、「他の作品ならどうなのか」ということを説明のため書きたくなるが、それは他作品と比較してラブライブを貶めているように見えてしまう可能性があるので省略する。ラブライブ単体について思ったことは以上である。

 

 

 

『ゲームプログラミングパターンズ』を読んだので感想

 先日CEDECに行った時に10%引きセールをやってたので買った。効率のいいクラス設計や応用の効くクラス設計を調べるのは今後役に立ちそうなのと、純粋にそういうのを調べるのが好きなのでとても面白かった。サンプルコードはC++で書かれているが、デザインパターンが主題なので言語はあまり関係ない。有名なGoFデザインパターンについても触れられているが、特にゲームプログラミングの分野においてはどのように応用され得るのかについてじっくり詳しく書かれている。また、GoFのパターン以外でもゲームの分野で使う機会が多いパターンについても詳細に書かれており、以下では目から鱗だったパターンについて備忘録もかねて記しておこうと思う。

f:id:kasayanus:20190921121749j:plain

〜目次〜
1. コマンドパターン
2. ステートパターン
3. 型オブジェクト
4. イベントキュー
5. サービスロケーター

1. コマンドパターン

GoFのパターンのひとつ。ゲームプログラミングではコントローラー入力の実装で使うことができる。

/*コマンドインターフェイス*/
class Command{
public:
    virtual void execute(GameActor& actor) = 0
}
/*ジャンプコマンド実装*/
class JumpCommand : public Command{
public:
     virtual void execute(GameActor& actor){
         actor.jump();
     }
}
/*キーインプットクラス的なところでハンドラー関数を定義する*/
Command* InputHandler::handleInput(){
   if(isPressed(BUTTON_X) return buttonX_;
   if(isPressed(BUTTON_Y) return buttonY_;
   return NULL;
}
/*コマンドを受け付けて実行する*/
Command* command = inputHandler.handleInput();
if(command)
{
   command->execute(actor);
}

このパターンを使えば、キー設定の変更も簡単に実装できる。また、AIの行動もキーインプットと切り離してCommandの操作だけで実装できる。

2. ステートパターン

GoFデザインパターンのひとつ。このパターンは明らかにゲームに向いてるパターンだと思し、以前個人でゲームを作っていた時に触れたことがあるので、詳しい設計は省略する。しかし、今回新しくこのパターンの応用で気付いたのはスタックを用いた状態管理である。例えば、しゃがんでいる状態から銃を撃って、打ち終わったら、元のしゃがんでいる状態に戻って欲しい、また、立っている状態から銃を撃っても元の立っている状態に戻って欲しい、などあるアクションをした後にその前の状態に戻れるようにするには、スタック型の状態管理システムが便利だという気づきがあった。また、あるステートに入った時に一度だけ実行して欲しい処理を今までステートクラスのコンストラクターで記述していたが、enter関数を作ってそこで初めの処理を行う、という設計パターンもあるようだ。これについてはコンストラクター内に各記述とは何が違うのかはわからない。

3. サブクラスサンドボックスパターン

ある基底クラスSuperPowerから継承して様々な能力を実装したい場合、派生クラスとベースコードの結合を少なくするパターン。これによって、ベースコードの変更による影響が基底クラスに集約されるためメンテナンスが楽になる。GoFのテンプレートメソッドパターンと似ているが、ユーティリティメソッドを実装するクラスが基底クラス(サブクラスサンドボックス)なのか派生クラス(テンプレートメソッド)なのかの違いがある。

class Superpower{
public:
protected:
   virtual void activate() = 0;

    /*
          以下は派生クラスで使うユーティリティメソッド
   */
   void move(double x,double y,double z){
           //実際のコードが入る。
   }
  
   void playSound(SoundId sound){
          //実際のコードが入る。
   }

   void spawnParticles(ParticleType type, int count){
         //実際のコードが入る。
   }
}
class SkyLaunch : public Superpower{
protected:
   virtual void activate(){
          move(0,0,20);
          playSound(Sound_SPROING);
          spawnParticles(PARTICLE_DUST,10);
   }
}

4. 型オブジェクトパターン

モンスターの種類をたくさん増やそうとする時に、基底クラスを作って、それからモンスターの種類ごとに派生クラスを作ると、似たようなコードの派生クラスがたくさん現れることになる(体力や攻撃力などのメンバ変数を変えただけなど)。それを防ぐためにモンスターの属性を表すクラスBleedに情報を格納し、それによってモンスターの挙動を変えるようにする。これによって、デザイナーやプランナーも変数の調整に参加しやすくなる、などのメリットがある。

5. イベントキューパターン

ユーザーによるキーやタッチなどによる入力とそれに応じた出力コールバックを時間的に分離するパターン。リングバッファ(ダブルバッファの多要素版みたいなもの)で有限要素の配列のキューを効率的に使う。

//音声情報をキューに登録
void Audio::playSound(SoundId id,int volume){
   pending_[tail_].id = id;
   pending_[tail_].volume = volume;
   tail_=(tail_+1)%MAX_PENDING; //リングキューの先頭を更新
}
//キューのheadから音声を読み取って、成功したらstartSoundする
void Audio::update(){
   if(head_== tail_) return;
  
   ResourceId  resource = loadSound(pending_[head_].id);
   int channel = findOpenChannel();
   if(channel == -1) return;
   startSound(resource, channel, pending_[head_].volume);

   head_ = (head_+1)%MAX_PENDING;
}

自分がよくやっているシャドウバースでも、ユーザーがカードを出す手順をキューに蓄えておき、適切なタイミングで実際に場にカードが出るようにキューから取り出していると思うので、このパターンを使っていると思った。オブザーバーパターンの非同期版とも言えるパターンである。

6. サービスロケータパターン

GoFのシングルトンパターンに似ている。

//サービスロケータークラス。これを使ってユーザーは音声サービスを呼び出す。
class Locator{
public:
   static Audio* getAudio(){return service_;}
   static void provide(Audio* service){
         service_ = service;
   }
private:
   static Audio* service_;
}
Audio *audio = Locator::getAudio();
audio->playSound(VERY_LOUD_BANG);

このパターンではAudioクラスを仮にシングルトンにしていなくても後から、サービスロケーターを適用できる。 このパターンではあらかじめLocatorにAudio(プロバイダ)を登録する必要がある。

ConsoleAudio *audio = new ConsoleAudio();
Locator::provide(audio);

GoFのデコレーターパターンと組み合わせてログ出力できるようにすることも可能。分からなかったのは、最後の補足に, 「Unityは、サービスロケーターをコンポーネントパターンを組み合わせて、GetComponent()の中で使っている。」の部分。

ニーアオートマタをクリアしたので感想。

PlayStation Storeの夏のセールということで、以前から神ゲーとして有名だったこのゲームを買ってプレイすることにした。ちなみに、このゲームの前作であるニーアレプリカントはプレイしていない。以下ネタバレも含むが、この記事の目的はストーリーの考察ではなく(正直細部までストーリーは追っていない)、あくまでプレイした感想なので注意したい。

f:id:kasayanus:20190829010052j:plain

 

1. ストーリーや世界観についての感想

簡単に説明するとこのゲームの世界観は、地球がエイリアンに侵略され、人類が月に逃れた後の世界、西暦一万ごろの世界であり、地球奪還のため人類はアンドロイド(人型の機械)を使役し、それに対抗してエイリアンは機械生命体を使役して、アンドロイドと機械生命体が代理戦争をするストーリーである。主人公はアンドロイドの2Bである。

 

BGMの効果もあるが、全体的に悲壮感の漂うゲームであると感じた。また、敵である機械生命体は、見た目が人型ではなく、2等身くらいであり、人によっては可愛らしさを感じる人もいるらしいが、個人的には気味悪さのほうが強かった。機械生命体は見た目も全員同じで顔に感情を表さない無機物という感じだが、話す言葉は片言の人語であり、感情的な言葉も発するし(「やめて、コロさないで」など)、凶悪な武器を振り回す姿には不思議な不安を感じた。

 

このゲームのストーリーは大きく前半後半に分けることができると思う。ドラクエ11ドラクエ8のようにストーリーを二つに分けることができるゲームは多いと思う。その中でも、このゲームはドラクエ11のような特徴的な分け方をしている。

このゲームの前半では、主人公2Bがアンドロイドとして司令部から与えられた任務をこなしていき、地球奪還のために立ちふさがる強敵たちと戦っていくアクションゲームであり、主人公たちの目的は地球の奪還である。

一方、後半になると主人公たちはある出来事に絶望することになる。それから彼らの目的は全く別のものに変わる

このように前半と後半で、ある"絶望的な出来事"から主人公の行動原理が大きく変わるようなストーリーであり、やったことがある人は分かると思うが、そういう意味でドラクエ11的な二部構成になっている。

そういう構成のストーリーはプレイヤーの気持ちを掻き立てる。前半では順調に物事が進んでいた展開から、行動の目的すら失うようなどんでん返し。主人公とプレイヤーはそこでこれからどうすればいいんだろうという気持ちが共鳴する。こういったストーリーは話が間延びせず、目的も変わってしまうので、一つのゲームで二度楽しめるような壮大なボリュームを感じるし、とても魅力的だ。

 

2. ゲームシステムについての感想

このゲームの戦闘は2Dアクション,3Dアクション、2Dシューティング、3Dシューティングなど多様な戦闘ジャンルが混ぜられている。一般的にPS4のゲームの戦闘は3Dアクションが主流だが、このゲームではマリオやロックマンを彷彿とする2Dアクションも存在する。なぜ2Dアクションを取り入れたのかについて考える。個人的には二つ理由が考えられた。一つは、複雑なダンジョン攻略を避けるため。もう一つは、背景をプレイヤーによく見てもらいたいから。この二点ではないかと想像する。2Dアクション戦闘に切り替わる場面には共通点がある。それは複雑なダンジョン(廃工場内、森の城内)であり、これらのダンジョンを仮に3Dアクションにしてしまうと、マップの中で迷ってしまうプレイヤーも出てきてしまうと思ったからではないだろうか。ダンジョンで迷うことに面白さを感じるプレイヤーなど誰もいない。これはストーリーを楽しむ上で大きな障害になってしまう。廃工場や森の城は広大なダンジョンであるため、あえてスムーズにダンジョン攻略ができるように一本道の2Dアクションにしたのではないかと推測する。また、廃工場の広さをプレイヤーに魅せるために背景がしっかり映る2D画面を採用したのではないかと推測する。

 

 

3. まとめ

このゲームのことが大好きな人も多いし、実際自分はプレイしたあとにyoutubeで実況動画や配信のアーカイブを見て、多くのプレイした人たちが感動していたこともわかった。が、正直自分は彼らほど感動はしてなかったと思う。それはおそらくこのゲームのせいではない。自分との相性や自分の感受性に問題があるのだと思う。元々悲壮感のあるゲームはあまり好きではないのである。またソシャゲのやりすぎなのかもしれないが、壮大なマップの移動や強敵との戦闘が非常にけだるく感じてしまった(しかもオートセーブがないので負けたらかなり戻されたこともあった)。数年前までは、それも含めてRPGが大好きだったのだが。。。こういうふうに変わってしまった理由についてはわからないけど、これからも考えていきたい。それでも、3Dモデルはとても精巧できれいだったし、やはり前半と後半を分かつ部分の演出には鳥肌はたったし「あ~、これが神ゲーといわれる所以か」と思った。とても作り込まれた良ゲーである。

 

 

 

『30日でできる! OS自作入門』を読んだので感想。

おそらく将来仕事で関わることはほとんどないと思うけど、OSはPCを使ってる限り、必ずお世話になっているものだし、今のうちしかOSについて勉強する機会はないだろうということで、OS本の中では結構有名なこの本を買って読むことにした。

f:id:kasayanus:20190825235605j:plain

本全体の文調が技術書にしてはかなり砕けていて、一人ツッコミしたり、「あ~、今日は眠いのでここまで!」みたいな友達に話すような感じで、いい意味で人間味のある本だなぁと思った。著者いわく「中学生にも読めるようにしたい」とのことで、読んでいて内容以外の部分も面白かったし、クスッとなる箇所もあった。

f:id:kasayanus:20190825235627j:plain

著者 川合秀実

本の内容はOSをアセンブリC言語を使って0から30日でどれくらいの機能をもったOSを作れるか、ということを本の中で実践しながら学んでいくものである。

OSに関しては、以前まで「OSは機械と人間をつなぐソフトウェア」とか「OSの種類は~がある」とかくらいの表面的な知識しか持ってなかった自分にとっては、目からウロコな内容が多くてとても勉強になった(それでもまだまだ知識は浅いけど)。全体を通して一番感じたのはマジックナンバー多すぎ!!ということである。OSは当然、CPUの上に乗っかっているものなので仕様に従って、レジスターの値をいじったりするわけだけど、「"これ"をしたいときは"このレジスター"の"何ビット目"に"この値"を入れなければいけない」とかがいちいち決まっていて、CPUの仕様を調べて慎重にやらないといけなくて大変だなと感じた。もしこれらのマジックナンバーが学校の試験とかで出されたらお手上げである(歴史の年号を覚えるようなもの)。しかし、それでもOSに作り方で「こうやって作られてるんだ」という驚きと発見が多かったので、本の中で取り上げている個人的に面白かった項目をとりあえず列挙しておく。

 

目からウロコだったトピック。

1. ブートローダーからプログラムがはじまる。

PCを起動したら、最初に動くのは第一セクター(ブートセクター)に埋まっているプログラム、これをブートローダー、イニシャルプログラムローダー(IPL)と読んでいて、このプログラムにはOS本体を呼び出す命令が書かれている。この本では、このIPLもしっかり初めから実装していく。


2. 文字の出力の仕方(画素単位で色指定)。

OSの文字出力の実装では画素単位で色を指定して発色させるので、文字は配列で図形のように定義して使う。


3. C言語で使えない関数(printf)はアセンブリも合わせて実装する

OSごとに文字出力の実装が違うので当然C言語の基本的な関数(printf)などはOSの実装では使えない。その他にも特定のレジスターを直接いじる関数はC言語には存在しないので、OSの機能を実装する上でレジスターをいじる必要がある場合はアセンブリで関数を実装する。その後にC言語のファイルとアセンブリのファイルをリンクしてOS本体を作っていく。


4. マルチタスク処理

二つ以上のタスクを同時で行うことができるのは、CPUがそのタスクを非常に短い間隔で切り替えながら行っている。マルチタスクはそのみかけの動きである。マルチタスク処理の実装も本の中で行う。


5. 重ね合わせ処理

OSには便利なライブラリやフレームワークはないので当然ウィンドウも自分で実装しないといけない。さらに複数のウィンドウが重なったときに見え方についても自分で処理を実装しなければいけない。これを重ね合わせ処理という。この実装も非常に原始的で、普段アプリケーションを作っているだけでは全く意識しなかったことであり、OSづくりには手間がかかるんだなぁということを実感した。


6. マウス、キーボードの割り込み処理

OSはマウスやキーボードが押されたときに、CPUは一旦それまでのタスクを中断して一瞬だけ割り込み処理を行うようにできている。これを本では1から実装する。このトピックはとくにマジックナンバーが多かった。CPUに割り込みを知らせる特殊な回線が20個弱ついていて、それぞれがキーボードなど割り込みする機器とつながっている(何番目の回線がキーボードにつながっているとかはすでにCPUの仕様で決まっている)。割り込みしたときに実行する関数(ハンドラー)のアドレスを書く番地も決めて、実際に割り込みがおきたときは、CPUのこの番地の命令を呼び出すように決まっている。


7. メモリ管理

ある用途のために使いたいメモリ領域をあらかじめOSに登録しておいて後から他の用途でこのメモリ領域が侵食されるのを防いだり、アプリケーションがシステム用に使用されているメモリを侵食しないようにするのがメモリ管理。これも本ではC言語で実装する。本では誰でもまずは思いつくような方法でこれを実装している。このメモリ管理の実装は非常に多くの実装が考えられて、良くない実装だとメモリの仕様効率が著しく下がっていしまうこともある。メモリ管理の実装はOS開発でもかなり工夫が必要な箇所である、OS開発者の腕の見せ所の一つなのではないかと感じた。


8. コマンド(dir,cls)の実装

普段OSを使っていると特に意識することなく実行してしまうOSコマンド。これも本では実装する。これはキーボードの割り込み処理から特定のメモリ番地に書き込まれたキーボード情報から文字列を読み取って、それがdirとかclsと一致するなら、ファイル一覧を表示したり、コンソール画面をクリアしたりなどのコマンド処理に分岐するというふうなとても原始的な実装だった。


9. 解像度変更

 解像度の変更もマジックナンバーでどのレジスターのどの値を入れればHD解像度に変更できるとかがビデオカードの仕様から決まっている。

 

 

まとめ

このOS本ではOSの最低限の機能を実装するにとどめており、この後OSの機能を追加したり向上させたりすることは読者に任せるとしている。なので、例えば、ネットワーク機能、ファイル管理システム、ハードディスクの情報の読み書きなどについては触れていない。この本ではフロッピーディスクにOS本体やアプリケーションファイルなどを全てあらかじめ詰め込んでおき、それをメモリに流し込んで全てメモリ上で作業する事になっているので、ハードディスクとのやり取りについては書かれていないが、やはりOSはハードディスクとのやり取りも重要だと思うのでその部分はぜひ知りたいところであったが、筆者いわく「その部分まで書くと、3章分はさらに必要になる」との事だったので泣く泣く断念したのであろう。

ともあれ、この本を読んだおかげで、普段PCを使っているときにも「そういえばこのOSの機能ってどうやって実装しているんだろう」「この機能を実装するためにはこんな情報が必要になりそうだ」とかを自分で考えることができるようになったし、普段空気のように使っているOSについて疑問を持てるようになった事自体が大きな収穫なのかなと思った。