【宇宙よりも遠い場所】なぜめぐっちゃんは赦され、日向のチームメイトは赦されないのか

宇宙よりも遠い場所』を全話見て、練られたプロットとキャラクター、技巧的な演出に感銘を受けました。 ここ数年で見たアニメ作品の中でも、トップに入ると思います。 見ていない方はプライムビデオとかで見られるので全話見てください。

www.amazon.co.jp

ただ、どうしても引っかかった部分が一つだけあって、それが表題の件なのですが、言及しているレビューやブログも多かったので、少し自分なりの考えを整理してみました。

めぐっちゃんと日向のチームメイトは何が違うのか

めぐっちゃんのエピソードは5話で、ずっと自分が世話を焼いていると思っていたキマリが、いつの間にか目標を見つけ、自分の道を進んでいることにショックを受け、陰口を裏で流してしまうというものです。

日向のチームメイトのエピソードは11話。 陸上部だった日向は、上級生とのレギュラー争いで手を抜こうとするが、同じ部活のチームメイトの励ましで、手を抜かずに走り、レギュラーを勝ち取ります。 しかし、それが上級生の逆鱗に触れ、上級生に問い詰められたチームメイトは「自分は止めた」と蝙蝠のような対応をしてしまいます。 それを陰で聞いていた日向は、虚しさから部活を辞め、さらにいじめは続き、高校を辞めることになります。

これらの行いは、いじめに加担した、陰で嘘をついたという点では大きく変わらないものです。 しかし、めぐっちゃんはキマリから絶交を断られ、友人として関係を続けていくことになりますが、一方で、日向のチームメイトは報瀬から二度と関わるなと拒絶されることになります。

実際にこの2つのエピソードは対立的で、3話で日向は創作名言で「人には悪意があるんだ。悪意に悪意で向き合うな。胸を張れ。」と言い放ち、実際にキマリは悪意には悪意で返さずに、めぐっちゃんと和解します。 11話でも日向はこの言葉の通り、チームメイトやいじめの悪意には向き合わず、無視しようとしますが、最終的には報瀬の反論、ともすれば悪意と呼んでもいいほどの暴言でチームメイトらと絶交することになります。

めぐっちゃんは誠実だから赦されたのか

さて、文章にすると如何にもフェアでない審判に思えますが、実際に鑑賞すると、受ける印象はかなり違います。 なぜなら、めぐっちゃんは、涙ながらに自分の行いを告白し、キマリに絶交しようと迫りますが、11話で謝りに来た日向のチームメイトは、髪をいじり談笑しながら出番を待ったりしていて、いかにもいじめられていた日向とは関係なく明るい学生生活を送っていたように見えるからです。 なので、話の流れとして、めぐっちゃんは赦され、日向のチームメイトは赦されないというのは、それほど不自然ではありません。

しかし、これだけでは、シナリオが変わる理由としては少し弱いと思います。 なぜなら、実際のところ、日向のチームメイトは喋る前にカメラを切られてしまったり、一方的にまくし立てられたりしてしまって、ほとんど何も喋れていないからです。 もし、態度の問題であるなら、明確に行動やセリフで違いを表現するはずです(よりもいはそういうところをキッチリするアニメだと信じています)。

キマリは傷つかず、日向は傷ついたからなのか

また、もう一つの見方として、キマリはあまり傷ついていないが、日向は退学するほどに傷ついたからだというものがあると思います。 しかし、これもやはり決定的でないように思えます。

そもそも、キマリはめぐっちゃんの告白を受けて、泣くほど動揺してるわけですし、度合いの話なら「じゃあどれぐらいならいいの?」という漠然とした疑問が残ります。

もちろん、上にも書いたように、話の流れとしては、日向はほとんどトラウマになっているぐらいですから、別にそのような解釈でも違和感はないのです。 ただ、一つの作品として見たときに、かなり似たエピソードをやりながら、シナリオの流れが大きく違うからには、明確な理由が存在しないといけないように思えます。

『よりもい』は友情のアニメで、倫理のアニメではない

「日向のチームメイトが赦されるプロットでも良くない?」と、11話初見時は強く感じたものでした。 友情をテーマにした『よりもい』というアニメで、より戻そうとした友情を無碍にするエピソードなのです。 倫理的な話なら、罪を赦すほうが正しいように思えます。 それは、日向自身も最後まで決めかねたことですし、前述の通り、演出として正解は示されていません。

でも、報瀬は倫理に従って行動しているのか?という点で見ると、そもそも、報瀬は倫理や常識など、まったく気にしていません。 報瀬は、友達の日向が傷つけられたことに激昂しただけであって、日向のチームメイトを裁いたわけではないのです。

これこそが、5話と11話の違いを決める答えです。 5話は、少しの歪みでは揺るがない厚い友情の物語で、11話は、倫理や常識を意に介さない強い友情の物語なのです。

見直してみると、報瀬がキレたすぐ後に、結月がまるっとそのままの答えを言っているのでした。

『いいじゃないですか!友情じゃないですか!』

XXXManager という名前は本当に良くない

XXXManager という名前は良くない。ということは、僕が言うまでもなく過去にたくさんの人が指摘しています。

『名前が漠然としているから』と言うのが大きな理由なのですが、実際に開発をしていると、この手の役割をクラスを作りたくなることは多く、それにどう名前をつけるか毎度考えあぐねるのですが、簡単に考えをまとめておきます。

なぜ管理クラスを作るのか

そもそも、XXXが何か処理を行うときは自身がその処理を行えばいいのです。

xxx.doSomething();

しかし、時としてそれを直接呼びたくないケースがあります。その多くは、メソッドを呼ぶときに、他のオブジェクトとの相互作用が発生するようなケースです。

実際のところ、管理クラスは何であるのか

XXXManager が複数の XXX を管理する場合、管理する XXX をメンバとして所有するのですから、単にそれを表すクラスにすればいいのです。

XXXCluster
XXXs
XXXQueue

XXXManager はこの形の改名で解決することが多い気がします。もっと別の理由で管理クラスを挟みたいケースでは、用途に応じた命名をすればいいと思います。もし、そのどちらにも当てはまらないなら、そもそもそのクラスが本当に必要なのか検討するべきです。

Electron でファイルを保存するパターン

意外と簡単に書けるので、面倒くさがらずに書くとすぐです。

レンダラ側

document.getElementById('saveButton').addEventListener('click', () => {
  ipcRenderer.send('save-state', JSON.stringify(state));
});

メイン側

ipcMain.on('save-state', (event, stateString) => {
  const defaultPath = 'my-data.json';
  dialog.showSaveDialog(
    {
      defaultPath,
      properties: {
        multiSelections: false,
      },
    },
    filePath => {
      if (filePath == null) {
        return;
      }
      fs.writeFileSync(filePath, stateString);
    },
  );
});

開くときもだいたい同じ

最小の Unity プロジェクト

Unity はプロジェクトを構成する要素が意外と少ないです。そもそも中核となるプロジェクトファイルのようなものがない。

Unity 2017 では、新規プロジェクトを作成すると次のようなディレクトリが作成されます。

├Assets/
├Library/
│└いろんなふぁいる
├ProjectSettings/
│└いろんなふぁいる
├UnityPackageManager/
└ProjectName.sln

Assets/ にはモデルやテクスチャ、音源やスクリプト、具体的なシーンデータといった、実際にアプリケーション内で利用するファイルが入ります。Library/ はキャッシュ的な何かが入るらしいです。これはコミットしない。ProjectSettings/ はビルドやグラフィック設定、Unityのバージョンなど、ゲーム全体の設定に関するファイルが入ります。UnityPackageManager/ はよく知らないけど、パッケージ用のなにかでしょ(適当)ProjectName.sln は VisualStudio 用のファイルですね。

ここから Unity Editor がプロジェクトであることを認識する最小構成までファイルを減らすとこうなります。

├Assets(空ディレクトリ)/
└ProjectSettings/
 └ProjectSettings.asset

なんとたったこれだけで Unity プロジェクトであると認識してくれます。

Unity の MonoBehaviour と Coroutine

やってみて当たり前だと気づいたのですが、StartCoroutine を使うには、MonoBehaviour を継承している必要があります。

つまり、これはダメ

public class MyAwesomeClass {
  void BestMethodEver() {
    StartCoroutine(SomeCoroutine());
  }
}

どうしても使いたかったら、ちゃんと MonoBehaviour を継承するか、

public class MyAwesomeClass : MonoBehaviour {
  void BestMethodEver() {
    StartCoroutine(SomeCoroutine());
  }
}

StartCoroutine を呼ぶ依代を探します。

public class MyAwesomeClass {
  void BestMethodEver() {
    Yorishiro yorishiro = GameObject.Find("Yorishiro").GetComponent<Yorishiro>();
    yorishiro.StartCoroutine(SomeCoroutine());
  }
}

Unity は、シングルスレッドでの動作が基本で、Coroutine もシングルスレッドで動作します(ちょうど、Node のような感じ)。StartCoroutine で呼び出せば、ちゃんとゲームループと同じメインスレッドで動いてくれるので、自分で Coroutine を実装するよりは、↑のような方法で解決するほうが良さそうです。

Unity のスクリプトの実行順を設定する。

Unity では、MonoBehaviour に設定した Update や LateUpdate は順不同で呼び出される。

これらの順番は、メニューの [Edit] → [Project Settings] → [Script Execution Order] を開き、各スクリプトの優先順位を数値で指定することで、制御することが出来る。 ただし、同じクラスのインスタンス同士の実行順は制御できないので、その場合は管理するためのクラスなどを用意する必要がある。

Script Execution Order は、各スクリプトの meta ファイルに保存されるため、パッケージの Export などをしても引き継ぐことが出来る。

Google Apps Script とロック

恐ろしいことに Google Apps Script(以降 GAS)は並列で実行される。普段 JS を書いていると意識しないが、並列実行ほどプログラムにおいて恐ろしいものはない。

ロックは、以下のような関数で取得できる。

var documentLock = LockService.getDocumentLock();

最大で10秒間待って、ロックを取得するには以下のようなコードを書く。

if (documentLock.tryLock(10000)) {
  // ロックを取得したときの処理
}

取得したロックを解除するには次の関数を呼ぶ。

documentLock.releaseLock();

シートを編集するような処理を書く場合、そもそもロックしなくて良いケースなどレアなので、次のようなコードを脳死で書いておけばいいと思いました。

var documentLock = LockService.getDocumentLock();

if (documentLock.tryLock(10000)) {
  try {
    main();
  } finally {
    documentLock.releaseLock();
  }
} else {
  console.log("failed to aquire lock.");
}