こんにちは。開発担当のマットです。
最近、暇の時に、Unity というゲーム・エンジンで色々遊んでいます。
Unity で自由自在にゲームを作ることができ、武器を振り回して街を爆発させるゲームやゾンビー・ゲームやストラテジー・ゲームなど、色々作ったことはあります。
ところで、ゲームは割と複雑なものです。ビジュアルも、アニメーションも、効果音も色々ありまして、プログラムを正しく設計しないと、バグに繋がってしまいます。
従来の書き方
とても簡単な例でゲーム・プログラムの従来の書き方を紹介します。
例のゲームを、「マイ・ペット」と呼びましょう。このゲームにペットを育てることができます。
このゲームを作るには、データを持つクラスと、画面にペットを書き出すクラスは必要でしょう。
1. PetData クラス。
このスクリプトの中に、ペットの色・サイズ・特徴など、全てのデータを記録しています。
2. DrawPet クラス
このスクリプトは PetData のデータを使って、ペットを画面に描き出す(描画する)。
普通だったら、ゲームが開始されたら、DrawPet が PetData の中身を見て、ペットを画面に書き出します。
ところで、ゲームの中で「ピンク薬」という魔法なアイテムを使って、ペットの色が「ピンク」になった場合はどうしたらいいでしょう?DrawPet はペットを描き直さないといけませんが、データが変わったことにどうやって気づくでしょうか?
従来な方法は2つあります。
1. PetData の中のデータが変わる時、PetData は DrawPet のファンクションを呼び出す。(Function Call)
2. DrawPet は定期的に、PetData の中身が変わったかを確認する。(Polling)
ところで、どちらにも問題はあります。
Function Call の問題
データが変わった時に、DrawPet のファンクションを呼ぶことは大して悪くはないですが…
気をつけないと膨大してしまうプログラムの書き方です。
例えば、ゲームの中に「ペット詳細」の画面を追加しようと思った時、その「ペット詳細」画面を更新するファンクション・コールも必要になるでしょう。
それとも、色が変わった時、効果音を再生したい場合は?
アニメーションを実行したい場合は?
色のデータを変更するタイミングですべてのシステムにファンクション・コールをしないといけません。
そして、一つでも抜けたらバグ!
直感的ではありますが、問題ですね。
Polling の問題
確かに、DrawPet は PetData を確認したら、上記の問題は解決となります。
ただし、問題はタイミングとなります。DrawPet はいつ PetData を確認すればいいでしょうか?
一瞬で反応する仕組みを作りたければ、毎秒毎秒、何十回、何百回も繰り返しで PetData の中身を確認する必要が生まれてしまいます。
一つのペットなら問題ないかもしれませんが、ペットを増やそうと思ったら、その分の処理も増えます。
そして、DrawPetだけではなく、UI、効果音、アニメーションなど、それぞれのシステムは各々の PetData を連続的に確認する必要になりますね。
しかも、データはそれほど頻繁にかわらないので、無駄な処理ばっかり走っていますね。
ゲームがガタガタと遅くなってしまう恐れはあります。良くないですね。
Callback のチカラ
ここで紹介したいのは Callback の力です。
PetData は DrawPet などのファンクションを呼ぶことも良くないし、
DrawPet は PetData に毎回尋ねるのも、無駄な処理になりますので、
使えばいいのは Callback です。
PetData の方でファンクション参照先を登録できるようにすることです。
これを使って、DrawPet はPetData に “RedrawPet” というファンクションを登録する。
DrawPet は “RedrawPet” のファンクションを実行するではなく、そのファンクションの参照先を PetData に登録するのだけです。
そして、PetData の方でデータが変わる時、PetData は登録された “RedrawPet” ファンクションを呼ぶことはできます。
このプログラミング方法を使うと、プログラマーはデータのクラスに、描画やUIや効果音やアニメーションのことを一切書くなくて大丈夫です。
それぞれのシステムはデータクラスにファンクションの参照先を登録をして、呼ばれるのを待つだけです。
「呼び返す」の感じですね。なので、Callback と言います。
ぜひ、次のプロジェクトで、Callbackプログラミングをお試しください。