はじめに
前回は、キーボードの操作をサポートする方法を解説しました。
今回は、マウスの操作をサポートしましょう。キーボード操作とマウス操作がサポートできれば、大概のゲームは作れるようになります。
1. Inputクラスを修正する
Inputクラスに、マウス操作の関数を追加しましょう。まずInput.hppファイルに、必要な変数と関数の宣言を追加します。次のコードの内、名前に「Mouse」と付いている変数8個と関数8個が、追加するコードです。また、インクルード文も1つ追加していますので、忘れずに書き加えてください。
...
#include <cstdlib>
#include <GLKit/GLKMath.h>
...
class Input
{
static uint64_t keyState;
static uint64_t keyStateOld;
static uint64_t keyDownStateTriggered;
static uint64_t keyUpStateTriggered;
static bool isMouseDown;
static bool isMouseDownOld;
static bool isMouseDownTriggered;
static bool isMouseUpTriggered;
static bool isMouseDownRight;
static bool isMouseDownOldRight;
static bool isMouseDownTriggeredRight;
static bool isMouseUpTriggeredRight;
public:
static bool GetKey(uint64_t keyCode);
static bool GetKeyDown(uint64_t keyCode);
static bool GetKeyUp(uint64_t keyCode);
static bool GetMouseButton(int button);
static bool GetMouseButtonDown(int button);
static bool GetMouseButtonUp(int button);
static GLKVector2 GetMousePosition();
public:
static void ProcessKeyDown(uint64_t keyCode);
static void ProcessKeyUp(uint64_t keyCode);
static void ProcessMouseDown();
static void ProcessMouseUp();
static void ProcessMouseDownRight();
static void ProcessMouseUpRight();
static void Update();
};
...
キーボードの各キーの情報は64ビット符号なし整数のビットマスクで管理していましたが、マウスは左ボタンと右ボタンそれぞれに、bool変数でON/OFFを表すための変数を用意して、それを更新したり状態を取得するための関数を用意しています。
マウスのカーソル位置を取得するためのGetMousePosition()
関数では、macOS標準のGLKitフレームワークで用意されているGLKVector2構造体を使用しますので、GLKit/GLKMath.hファイルをインクルードしています(GLKit/GLKit.hファイルにはObjective-Cのコードが含まれていますので、インクルードするのはGLKit/GLKMathTypes.hファイルであることに注意してください)。GLKitフレームワーク自体は、「1-1. プロジェクトの作成〜OpenGLの基本設定」でプロジェクトの作成と同時に追加しています。
次に、Inputクラスに追加した変数の初期化コードと、関数の実装コードを追加します。その前に、マウスのカーソル位置の取得にはObjective-Cのコードを書く必要がありますので、Input.cppファイルの拡張子を.mmに変更して、InputクラスがObjective-C++のファイルとしてコンパイルされるようにしておきましょう。
Input.mmファイルの先頭に、必要なファイルの読み込みのコードと、マウス関係のstatic変数の実体と、それを初期化するためのコードを追加しましょう。
#import "MyGLView.h"
...
bool Input::isMouseDown = false;
bool Input::isMouseDownOld = false;
bool Input::isMouseDownTriggered = false;
bool Input::isMouseUpTriggered = false;
bool Input::isMouseDownRight = false;
bool Input::isMouseDownOldRight = false;
bool Input::isMouseDownTriggeredRight = false;
bool Input::isMouseUpTriggeredRight = false;
次に、Inputクラスの関数の実装コードを追加します。まずはマウスの状態を取得するための関数の実装です。
bool Input::GetMouseButton(int button)
{
if (button == 0) {
return isMouseDown;
} else {
return isMouseDownRight;
}
}
bool Input::GetMouseButtonDown(int button)
{
if (button == 0) {
return isMouseDownTriggered;
} else {
return isMouseDownTriggeredRight;
}
}
bool Input::GetMouseButtonUp(int button)
{
if (button == 0) {
return isMouseUpTriggered;
} else {
return isMouseUpTriggeredRight;
}
}
GLKVector2 Input::GetMousePosition()
{
return [[MyGLView sharedInstance] mousePosition];
}
GetMousePosition()
関数では、MyGLViewクラスのインスタンスを取得して、マウスのカーソル位置を取得するためのメソッドをさらに呼び出します。
次に、マウスの状態を設定するためのProcess〜()
系の関数の実装です。
void Input::ProcessMouseDown()
{
isMouseDown = true;
}
void Input::ProcessMouseUp()
{
isMouseDown = false;
}
void Input::ProcessMouseDownRight()
{
isMouseDownRight = true;
}
void Input::ProcessMouseUpRight()
{
isMouseDownRight = false;
}
そしてInput::Update()
関数の実装に、マウス関係の変数を更新するためのコードを追加します。キーボード関係の変数を更新するためのコードと見比べると、まったく同じことを、マウスの左ボタンと、マウスの右ボタンに対して計算していることが分かります。
void Input::Update()
{
keyDownStateTriggered = keyState & ~keyStateOld;
keyUpStateTriggered = ~keyState & keyStateOld;
keyStateOld = keyState;
isMouseDownTriggered = isMouseDown & ~isMouseDownOld;
isMouseUpTriggered = ~isMouseDown & isMouseDownOld;
isMouseDownOld = isMouseDown;
isMouseDownTriggeredRight = isMouseDownRight & ~isMouseDownOldRight;
isMouseUpTriggeredRight = ~isMouseDownRight & isMouseDownOldRight;
isMouseDownOldRight = isMouseDownRight;
}
2. MyGLViewクラスを修正する
MyGLView.hファイルに、次のように、GLKitフレームワークのインポート文と、マウスのカーソル位置を取得するためのメソッドの宣言を追加します。
...
#import <GLKit/GLKit.h>
@interface MyGLView : NSOpenGLView
...
- (GLKVector2)mousePosition;
@end
MyGLView.mmファイルの先頭に、次のように、MyGLViewクラスのインスタンス変数を2つ追加します。
@implementation MyGLView {
...
NSWindow *window;
NSSize size;
}
ビューの親であるウィンドウを取得するコードと、ビューのサイズを取得するためのコードは、どちらもメインスレッド上でしか実行できないため、ビューの作成時に呼ばれるprepareOpenGL
メソッドで実行して、この2つの変数に保持しておきます。
- (void)prepareOpenGL
{
[super prepareOpenGL];
window = self.window;
size = self.bounds.size;
...
}
そしてmousePosition
メソッドの実装を、次のように、保持しておいた2つの変数を使って実装します。
- (GLKVector2)mousePosition
{
NSPoint location = [NSEvent mouseLocation];
NSRect rect = NSMakeRect(location.x, location.y, 0.0f, 0.0f);
rect = [window convertRectFromScreen:rect];
location = rect.origin;
location.x = location.x * 2 / size.width - 1.0f;
location.y = location.y * 2 / size.height - 1.0f;
return GLKVector2Make(location.x, location.y);
}
マウス位置はNSEventクラスのmouseLocation
メソッドを使って取得できますが、この値はMacのスクリーン全体での座標における値なので、ウィンドウの座標系に変換します。そして変換した値を2倍した値をビューの大きさで割り、1を引くことで、X座標・Y座標ともに-1.0〜1.0の範囲の値が取得できます。
マウスカーソルの座標はピクセル単位で取得しても良いのですが、いずれカメラを移動したりモデルを回転・移動・スケーリングしたりするなどした場合には変換が必要になりますので、ここではあらかじめ正規化した値を取得するようにしました。
3. ViewControllerクラスを修正する
ViewController.mmファイルの最後の「@end
」の行の直前に、マウスが押されたり離されたりした時に呼ばれる4つのメソッドを次のように追加して、先程用意したInputクラスの関数が呼ばれるようにしましょう。
- (void)mouseDown:(NSEvent*)theEvent
{
Input::ProcessMouseDown();
}
- (void)mouseUp:(NSEvent*)theEvent
{
Input::ProcessMouseUp();
}
- (void)rightMouseDown:(NSEvent*)theEvent
{
Input::ProcessMouseDownRight();
}
- (void)rightMouseUp:(NSEvent*)theEvent
{
Input::ProcessMouseUpRight();
}
4. Gameクラスの実装を修正する
Game.hppを修正して、value
変数を削除して、value1
とvalue2
の2つの変数を追加しましょう。
class Game
{
private:
float value1;
float value2;
...
};
Game.cppを修正して、Gameクラスのコンストラクタで、追加した2つの変数を初期化します。
Game::Game()
{
glClearColor(1.0f, 0.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
value1 = 0.0f;
value2 = 0.0f;
}
Game::Render()
関数の実装を、次のように書き換えましょう。マウスの左ボタンが押されている時に、マウスの現在のカーソル位置を取得して、X座標の-1.0〜1.0の値を0.0〜1.0の値に変換してvalue1
変数に、Y座標の-1.0〜1.0の値を同様に変換してvalue2
変数に格納します。そしてこれらの値を元にして、画面をクリアする色を変更します。
void Game::Render()
{
if (Input::GetMouseButton(0)) {
GLKVector2 pos = Input::GetMousePosition();
value1 = (pos.x + 1.0f) / 2.0f;
value2 = (pos.y + 1.0f) / 2.0f;
}
glClearColor(value1, value2, 1.0f - value1 * value2, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
}
実行して、マウスの左ボタンを押しながら画面上をドラッグすると、マウス位置に応じた色が表示されるようになっていることが確認できます。
ここまでのプロジェクト:MyGLGame_step1-9.zip
5. まとめ
今回はマウスの操作をサポートしました。
次回は、OpenGLで描画可能な頂点の数など、様々なOpenGLの環境を確認して、シェーダで描画を行うための準備をしましょう。