2014年03月

ゲーム製作日誌 プログラム編 No.5 「マウスの入力検知」

こんにちは。
今回は、プログラムでプレイヤーが操作するマウスの状態を検知する方法を解説していきます。

1,マウスの状態

 マウスは、キーボードとは異なり様々な種類の状態を持っています。

 1つ目にマウスのボタンです。一般のマウスには左・右・中央の3つのボタンがあります。左のボタンは何かを選択したり決定したりするときに使用しますね。右のボタンはキャンセルしたりメニューを開くときにしばしば使います。中央のボタンはあまり使う機会が無いですね。

 2つ目にはマウス自体の移動があり、画面上のカーソル位置とも言い換えられます。マウスを移動させることで画面上の矢印を移動させて目的の位置に持ってきたり、ゲームでは視点をずらすために動かすこともありますね。

 今回は、この2つの種類の状態を、プログラム側で把握できるようにしましょう。


2,ボタン状態と”マウス移動”の取得

 マウスボタンが押されているかどうか、またはマウスが動かされたかの確認は、DirectInputを通じて行うことができます。以下のソースを御覧ください。
 
using SharpDX.DirectInput;


DirectInput directInput = new DirectInput();

//接続されているマウスを探す
Guid mouseGuid = Guid.Empty;
foreach (var g in directInput.GetDevices(DeviceType.Mouse, DeviceEnumerationFlags.AllDevices))
{
mouseGuid = g.InstanceGuid;
}
if (mouseGuid == Guid.Empty)
{
throw new Exception("マウスが見つかりません");
}

//デバイスを初期化
Mouse device = new Mouse(directInput);

//マウス状態の取得開始
device.Acquire();

//マウスの状態を取得する
MouseState state = device.GetCurrentState()

 この一連のコードでマウスの状態を取得することができます。
 気づいた方がいるかもわかりませんが、このコードは前々回の記事のキーボード状態の取得のコードと非常によく似ています。具体的には、キーボードの文字がマウスに変わっただけです。マウスもキーボードもやるべきことは大体同じなのです。

 コードの一番最後の行では念願の”マウスの状態”らしきMouseStateクラスを取得できています。このMouseStateクラスには、マウスのボタン状態とマウスの移動量が含まれています。再び下のコードを御覧ください。
//左ボタンがおされているか
bool buttonL = state.Buttons[0];

//右ボタンが押されているか
bool buttonR = state.Buttons[1];

//中央のボタンが押されているか
bool buttonCenter = state.Buttons[2];

//マウスの移動量(x:横 y:縦)
int mouseMoveX = state.X;
int mouseMoveY = state.Y;
 MouseStateクラスのButtonsプロパティは、bool配列になっており左ボタンの場合は1番めに、右ボタンは2番めに押されているかどうかの真偽値が格納されています。
 また、MouseStateクラスのX,Yプロパティにはそれぞれマウス自体の移動量が格納されているわけです。
 一度Mouseクラスを作ってしまえばいつでもGetCurrentState()メソッドによってマウスの情報を得ることができます。



3,”カーソル移動”の取得


 さて、ここまででマウスボタンと移動量を取得することが出来ました。これにより、画面上のボタンをクリックするインターフェースや、マウスの移動によって視点の向きを変える操作が可能になりました。しかし、そのようなインターフェースを扱う上で重要な画面上のカーソル位置がまだ判明していません。
 DirectInputとは、そもそもマウスやキーボードといった装置自体の状態を把握するための機能です。そのため、OS上で管理しているようなカーソルの位置は専門外であるため取得することができません。ですから今回は、DirectInputではなくOSに直接カーソルの位置を尋ねなければいけません。
 下のコードを御覧ください。

using System.Runtime.InteropServices;
//クラス内に記述
[DllImport("user32.dll")]
private static extern void GetCursorPos(ref POINT lpPoint);

private struct POINT
{
public UInt32 x;
public UInt32 y;
}

public void GetCursor(out int x, out int y)
{
POINT p = new POINT();
GetCursorPos(ref p);

x = p.x;
y = p.y;
}
 カーソル位置取得の場合は特に準備は必要なくて、単にAPIと関連付けた関数(上の例ではapiGetCursorPos)を呼び出せば大丈夫です。その点では、他の取得処理よりも楽ですね。



4,まとめ


 今回の内容をまとめると大きく2つです。
  • マウスのボタンや移動量をチェックするには、予め作成しておいたMouseクラスのGetCurrentState()メソッドを呼び出す。その返り値の、Buttonsメンバには[0]:左ボタン,[1]:右ボタン,[2]:中央ボタンの順で状態が格納されている。マウスの移動量は、XとYのメンバから取得する。

  • 画面上のカーソルの位置を取得するには、user32.dllのGetCursorPos()関数を呼ぶ。

 次回は、マウス状態をゲームライブラリに組み込む方法について模索したいと思います。おそらくキーボードの時と似たようなものになるでしょうが。

「最果てロマノーヴァ」体験版配布

体験版
同人サークルウマモサクが製作中のRPG、「最果てロマノーヴァ」の体験版が完成いたしましたので配布を行います。このバージョンでは最序盤のシナリオ進行、ダンジョン探索、戦闘を行うことができます。ただし実装予定のうろつきシステム、アビリティシステムなどは未実装となっています。シナリオ的には主人公たちが自由に動けるようになるまでがこのバージョンでの実装部分です。追記にシステムの詳細を書きます。
続きを読む

ゲーム製作日誌 プログラム編 No.4-2 「キーボードの入力検知部分の実装」 サンプルソース

こんにちは。
 この記事では、ゲーム製作日誌 プログラム編 No.4「キーボードの入力検知部分の実装」で考えたキーボード判定処理のゲームライブラリへの組み込み方法を実現したソースコード例を紹介します。No.3で扱った、SharpDXの導入が前提になっています。

/////////////////////////////////////////////////
//
// キーボード入力判定機能クラス
//
// Author. ウマモサク
/////////////////////////////////////////////////

using SharpDX.DirectInput;

class KeyboardInterface{
//シングルトン
private static KeyboardInterface instance;
public static KeyboardInterface Instance
{
get
{
return instance;
}
}
private static bool initialized=false;
public static void Initialize()
{
if (initialized)
return;

initialized = true;
instance = new KeyboardInterface();
}
public static void Dispose()
{
if (initialized)
{
instance.DisposeDevice();
instance = null;
initialized = false;
}
}

protected KeyboardInterface()
{
this.InitializeDevice();
}

private DirectInput directInput;
private Keyboard device;

private KeyboardState keystate;

public bool this[Key key]
{
get
{
return keystate.IsPressed(key);
}
}

public void InitializeDevice()
{
directInput = new DirectInput();

//キーボードが存在しない場合への対処
Guid keyboardGuid = Guid.Empty;
foreach (var g in directInput.GetDevices(DeviceType.Keyboard, DeviceEnumerationFlags.AllDevices))
{
keyboardGuid = g.InstanceGuid;
}

if (keyboardGuid == Guid.Empty)
{
throw new Exception("キーボードが見つかりません");
}

//デバイスを生成してキーの取得を開始
device = new Keyboard(directInput);
device.Acquire();

keystate = device.GetCurrentState();
}

public void DisposeDevice()
{
device.Dispose();
directInput.Dispose();
}

public void Update()
{
keystate = device.GetCurrentState();
}
}


 以上が、ライブラリ部分です。このライブラリは、ゲーム起動時に以下のコードで初期化します。KeyboardInterfaceクラスの実体を作成するのです。

KeyboardInterface.Initialize();


 その後、一定周期で以下のコードを呼び出します。ゲームでは通常、メインループという一定周期で処理をする仕組みを持っているため、その中に記述すればOKです。

KeyboardInterface.Update();


 実際にキー押下状況を取得するときは、以下の関数呼び出しの返り値から判断します。Enterキーの判定を例に挙げます。

KeyboardInterface.IsPressed(Key.Return)

 ゲーム終了時には、作成した実体を消去してデバイスを開放する必要があります。

KeyboardInterface.Dispose();


 

ゲーム製作日誌 プログラム編 No.4 「キーボードの入力検知部分の実装」

 今回は、前ナンバーの延長でキーボードの押下判定処理を扱います。前回は実際の押下判定の仕方を解説しましたが、今回はそれをどのようにゲームライブラリに組み込む方法の一例を解説します。

1、組み込み方について考える要素

 ライブラリに組み込む前にまずどのように組み込むかを考えることにしましょう。それを決めるためには、今回作ろうとしている機能がどんな特徴をもって動作するか考える必要があります。
 
  • 判定はある周期のタイミングで一気に行い、ライブラリ使用者にはその周期内の判定記録を教えてあげればよい

     前回のナンバーを見ると分かると思うのですが、キーボードの判定はまずKeyboardStateクラスを取得してから行いました。そのクラスには、すべ てのキーボードの判定結果が入っていました。そのような判定方法しかない中、毎回1種類のキーの判定をするためにすべてのキーの判定するのは効率が悪そうです。
     そこでその性質を利用し、ゲームライブラリが一定周期(およそ1/60秒おき)毎に全キーの判定結果であるKeyboardStateクラスを代わりに取 得しておいて、ライブラリ使用者にはその最新のKeyboardStateクラスの中身を教えてあげれることにしましょう。そうすれば、より無駄の少ない効率的な動作が実現できます。


  • 一つのキーに対する操作を反復して行えばよい

     キーボードはいくつかのキーに分かれていて、それぞれの形状・名前・役割はプレイヤーにとっては異なるものです。しかし、ライブラリ使用者(ゲーム製作 者)にとっては、ただ名前が違うだけですべて押されているか押されていないかを伝える存在という意味でしかありません。(多分)
      ですから、どのキーに対しても押下判定に関してライブラリが行うことに代わりはなく、その判定結果をキーの名前に関連づけておけばよい事になります。
      そこで、今回作るライブラリでも、キーの名前をつけた判定結果構造体を用意しておき、それらに対して同じ処理を反復して行うことにより、少ないコードで押下判定を実現しましょう。


  • ゲームライブラリはひとつでよい、ふたつもいらない

     最初の特徴の項目のような一定周期に判定をするような仕組みにするのなら、その判定をする人は二人もいらないです。複数用意したら判定を二重にすることになりますし、その人達の判定した僅かなタイミングの差がキーの状態を尋ねた人によって答えが違ってくる悩まし現象も起きるでしょう。
     これはキーの判定のみの話ではなく、今後取り上げていく”マウス状態検知”,”サウンド再生”,”グラフィック機能”にも同じことが言えます。ゲームライブラリはひとつでよいのです。
     ライブラリ使用者が、誤って2つの判定人を生まないようにライブラリの設計も考える必要がありそうです。そのために今回は、デザインパターンの一つである「シングルトンパターン」を利用することにします。

     「シングルトンパターン」とは、プログラム実行内で最高1つしかクラスの実体(インスタンス)が生成されないことを保証することができるプログラムの技法のことです。キーの押下判定をするクラスを「シングルトンパターン」にはめ込んでしまえば、今回の要件は満たされてしまいそうですね。

     ここで、「シングルトンパターン」についての簡単な説明をしておきましょう。
     クラスを作れるのはいつなのか、それはnewした時でコンストラクタを読んだ時です。「シングルトンパターン」では、このコンストラクタの呼び出しを外部からできないようにしてしまいます。
     そのままでは、1つだけどころか何も実体が作れないクラスになってしまうため、自身のクラス型の静的メンバをprivateで追加します。さらに、そのメンバの中身を返すpublicなプロパティを追加することで実質1つしか存在できないことになります。
     また、publicで静的メンバにインスタンスを代入するメンバ関数Initialize()を追加すれば、1つしか生成できなくなります。
     詳しい説明は、「C# シングルトン」で検索を。
    class Singleton
    {
    ///<summary>
    ///protectedで継承先と内部からしかアクセス出来ないようにしたコンストラクタ
    ///</summary>
    protected Singleton()
    {
    }

    public static void Initialize()
    {
    instance = new Singleton();
    }
    private static Singleton instance;
    public static Singleton Instance
    {
    get
    {
    return instance;
    }
    }
    }


  • クラスと関わるのは、更新タイミングとキーの状態取得時のみ

     最後に、キー判定クラスの隠蔽を考える必要があります。ライブラリを作成することで、利用者の負担を軽減することもこの記事の目的であったので、必要最低限の機能だけ利用者に見えるようにしなくてはいけません。KeyboardStateクラスを取得するといった”面倒な作業”は、ライブラリ利用者から隠ぺいするのです。
     このキー判定クラスが、利用者と関わる必要があるのはたった2つだけで、更新タイミングとキーの状態取得時のみです。具体的にすると
    ///<summary>
    一定周期が経過しキーボード状態を取得するタイミング
    </summary>
    void Update();

    ///<summary>
    キー押下判定をする。(返り値:キーが押されているか)
    </summary>
    bool IsPressed(Key key);
    この2つであります。(生成時と破棄時もあります...)


2、実装しよう

 どのようにしてライブラリにキー判定処理を組み込むのかがまとまりました。あとは、実装するだけです。
 とりあえず、実装例を載せておこうと思ったのですが、ここに載せてしまったらページが長くなってしまいそうなので別ページに載せることにします。


3、まとめ

 とても短いですが、今回のまとめはこのようになります。
  • SharpDXの動き方に合わせて、ライブラリ使用者が楽になりそうで最適そうなクラス設計をする。
 
 「最果てロマノーヴァ」制作の体験版リリースに向けた準備で、ここのところ忙しくブログ記事を更新が滞っていました。「最果てロマノーヴァ」の体験版リリース日は、はっきりとはわかりませんが早くて3月上旬・中旬、遅くて4月頭になるかもしれません。
 リリースの際には、ぜひプレイしてくださいね。

 次回は、今回と似たような処理としてマウス判定の解説をしていく予定です。キーボードと同じく判定の実際と、ライブラリへの組み込み編と分けていくと思います。

twitterはこちら
ニコ生放送します
ギャラリー
  • ウマモサク
  • ウマモサク
  • ウマモサク
  • 3/17 制作記録
  • 3/17 制作記録
  • C91告知
  • C91告知
  • C91告知
  • C91告知
アクセスカウンター
  • 今日:
  • 昨日:
  • 累計:

  • ライブドアブログ