glutとC言語でシューティングゲーム3日目


これまでのあらすじ

  • シーン管理しやすいゲームの骨格ができた

2日目はここです。

3日目

弾幕シューティング開発3日目です。前回まででゲームの骨格が大体できましたが、ゲームに必要なものはもう少しあります。あまり目に見えない部分の開発がもう少し続きますが、がんばります。

今日はキーボードからの入力を受け付ける仕組みをつくります。さすがにこっちから何もできないプログラムを作り続けるのはさみしい笑。

あとは、ファイル追加に伴って見づらくなっていくMakefileもこのタイミングで書き換えておきます。

今日の目標

  • キーボード入力
  • Makefileの改良

キー入力

glutを使っているので、キーボード入力があったときに呼ばれるコールバック関数を登録することでキー入力受け付けを実現します。

任意のキーが押された瞬間に呼ばれるコールバック関数と任意の押されたキーが離される瞬間に呼ばれるコールバック関数の2つを用意して登録します。その2つの関数を組み合わせて、任意のキーの状態を格納しておく仕組みを実装し、任意のキーの状態を得る関数も用意します。

キー入力処理の実装

key.h
#ifndef ___HEADER_KEY

#define ___HEADER_KEY


void keyInit();
int keyGetState(unsigned char key);


#endif
key.c
#include "key.h"

#include <GL/glut.h>
#include <stdio.h>

static unsigned char keys[256];

void keyboard(unsigned char key, int x, int y);
void keyboardUp(unsigned char key, int x, int y);

void keyInit(){
  glutKeyboardFunc(keyboard);
  glutKeyboardUpFunc(keyboardUp);
  glutIgnoreKeyRepeat(GL_TRUE);
}

int keyGetState(unsigned char key){
  return keys[key];
}

void keyboard(unsigned char key, int x, int y){
  printf("keyboard: \"%c\"(%#x)\n", key, key);
  keys[key] = 1;
}

void keyboardUp(unsigned char key, int x, int y){
  printf("keyboardUp: \"%c\"(%#x)\n", key, key);
  keys[key] = 0;
}

やっていることは簡単です。keyInit関数でキーが押されたときに呼ばれる関数とキーが離れた時に呼ばれる関数を登録します。また、キーが押しっぱなしの際に繰り返しコールバック関数が呼ばれることを防ぐ設定をするために、glutIgnoreKeyRepeat関数を使用しています。あとは、全てのキーの状態を保持するためにkeysという配列を使っていることと、任意のキーの状態を返すkeyGetState関数を実装しました。デバッグ用に、標準出力にキーが押されたときと離された時がわかるようにしていますが、必要なければコメントアウトして使います。

後はゲーム全体を初期化するgame.cのgameInit関数内でkeyInit関数を呼ぶようにgame.cに追記します。

game.c
//以下を追記
#include "key.h"

void gameInit(){
  glInit();
  keyInit(); //追記
  //以下略
}

これでキーボード入力を常時受け付けるプログラムがとりあえずは完成しました。必要であれば、さらにキー入力処理を改良するかもしれませんが、とりあえずはこんなもんで笑。

メニューシーンでキー入力

メニューシーンでキー入力があったときの処理を追加して、キー入力ができるのか確かめます。

sceneMenu.c
#include "sceneMenu.h"

#include <GL/glut.h>
#include "font.h"
#include "key.h"

static int count = 0;
static unsigned char p[SCENE_PARAMETER_MAX] = {1,2,3};
static int testKeyFlag = 0;

void sceneMenuUpdate(void (*changeSceneFunc)(enum eScene, unsigned char *, int)){
  //update
  count++;

  if (keyGetState('a') && (!testKeyFlag))
    testKeyFlag = 1;
  if ((!keyGetState('a')) && testKeyFlag)
    testKeyFlag = 0;

  glutPostRedisplay();
}

void sceneMenuDispaly(){
  glClear(GL_COLOR_BUFFER_BIT);

  float test1 = 1.0f;
  int test2 = 23;

  //font test
  fontBegin();
  fontSetPosition(0.0, 100.0);
  fontSetSize(FONT_DEFAULT_SIZE * 0.25);
  fontSetWeight(1.0);
  fontSetColor(0, 0, 255);
  fontDraw("meun");
  if (testKeyFlag) {
    fontSetPosition(0.0, 200);
    fontSetSize(FONT_DEFAULT_SIZE * 0.25);
    fontSetWeight(1.0);
    fontSetColor(0, 255, 0);
    fontDraw("key 'a' is pushed");
  }
  fontEnd();

  glutSwapBuffers();

}

aというキーボードが押されているときだけ、画面に緑色でkey 'a' is pushedという表示がされるシーンにしました。

Makefileの改良

キー入力の実装が終わったのでいつものようにMakefileに追記して...。毎回同じことをたくさん書くのはしんどいのでMakefileを改良します。といっても特別なことはしてません。ようやく一般的なMakefileになったなくらいです。

Makefile
PROGRAM = main
OBJS = \
game.o \
font.o \
key.o \
scene.o \
sceneMenu.o \
sceneGame.o

CC = gcc
CFLAGS = -lglut -lGLU -lGL

.SUFFIXES: .c .o

$(PROGRAM): $(OBJS)
    $(CC) $(PROGRAM).c -o $(PROGRAM) $^ $(CFLAGS)

.c.o:
    $(CC) -c $< $(CFLAGS)

.PHONY: clean
clean:
    $(RM) $(PROGRAM) $(OBJS)


game.o: game.h 
key.o: key.h
font.o: font.h
scene.o: scene.h
sceneMenu.o: sceneMenu.h
sceneGame.o: sceneMenu.h

それではmakeして実行してみましょう。

うまくいきました。

3日目まとめ

  • キー入力ができるようになった
  • Makefileがまともになった

これでゲームに必要な処理がそろってきました。次の回で実装予定のFPS制御ができたらようやく目に見える部分の実装に移ります。つまらない回がもう少し続きますが、頑張っていきます。