読者です 読者をやめる 読者になる 読者になる

グローバル変数は外部参照すべきか?

突然ですが、グローバル変数をやもえず外部参照する場合、
以下のような方法が考えられると思います。
// ヘッダファイルの記述(key.h)
extern ST_KEY Key;

// ソースファイルの記述(key.c)
ST_KEY Key;
これは慣例らしく、色々なところに普及されているっぽいです。

2008-07-31 - GBA homebrew日記

慣習化されているのは確かなのですが、
今回の件で言えばキーの構造体を直に見るのはあまりいただけないかなぁ、とか。
コードの可読性、即ち「Keyがグローバルであると気がつくか?」とはちと外れますが、
Keyの場合の問題点いくつか。

  • キーカスタマイズの問題
    • ゲームだとたまにオプションで「操作タイプA」「操作タイプB」なんてのが選べますが、こういうのに対応できない。(構造体操作時にいれるデータをボタン名称ではなく機能名称単位にすれば良いんだけれど、管理しにくい)
    • ゲームだとよくあることに開発中にキーが変更になることがよくありますが、こういうのに対応しにくい。
  • 可読性の問題
    • ゲームだと「Trigger(今押された)」「Press(押されている)」「Release(離された)」「Repeat(リピート入力)」みたいなキーの状態を扱いますが、そういうのが分かりづらい。構造体のメンバ名で表すこともできますが。
  • 複数のコントローラの問題
    • GBAとかDSだとコントローラは一つですが、複数のパッドがあった場合、結局面倒なことに。Key[PORT1].btnとか。
  • リプレイ機能やデモ再生の問題
    • アクションゲームとかプレイをリプレイでみせたり、デモ再生するさいにキー入力エミュレーションを使うことが多いでしょうが、構造体だとちょっと面倒ですよね。

で、やっぱりグローバル関数で、

// キャンセルボタンが放されたら(ポート指定版?
if (IsPadRelease(PORT1, PAD_CANCEL)) {
    ...
}
// 特殊攻撃ボタンが押されたら
if (IsPadTrigger(PAD_SPECIAL_ATTACK)) {
    ...
}
// 補助ボタンを押しつつ、攻撃ボタンが押されたら
if (IsPadButton(PAD_ASSIST)) {
    if (IsPadTrigger(PAD_ATTACK)) {
        ...
    }
}

とかでしょうかねえ。
PAD_SPECIAL_ATTACKとかにするのは、
同時押しなどを|で表記するより別定義の方が良いので。
何をしようとしているのかが明白になって可読性もあがりますし、
何よりKeyとはなんぞや? なんてことを思う必要はありません。
キーカスタマイズをする場合は、|定義ではなく、別enumとかにしておいて、
関数内で変換をかませばいいです。
PAD_SPECIAL_ATTACK -> PAD_A|PAD_L
のときもあれば、
PAD_SPECIAL_ATTACK -> PAD_B
のこともあるでしょう。
もちろんデバッグで簡易に扱いたいことも多いので

if (GetPadButton() & PAD_A) {
    ...
}

なんてこともありますけれど。
あとは、数フレーム前の入力が欲しいこともあるので、
Record(履歴)化したりとか?
(格闘ゲームなんかのキー入力はそういう必要がありますね、先行入力があるゲームもそうかな。

と、構造体を直接みるのは問題が色々と多いので、
関数化推進派です。
面倒でも煩雑になるよりは関数にしちゃうのがLoveです。
でも、簡単なゲームだと構造体参照で済むこともありますけどね。(^_^;
元々がCの話題のようなので関数にしてますが、
C++だったらクラスにしてポートを隠蔽したりするかも。