概要
OpenGL/GLUTにて作成したインベーダーゲームです。
敵の数は8匹、ランダムに発射してくる弾を避けつつ、敵を倒します。
環境
Mac OS X 10.10.1 GNU gcc使用
コンパイルコマンド
g++ -o InvaderGame.o main.cpp invaderClass.cpp unitClass.cpp -mmacosx-version-min=10.8 -framework GLUT -framework OpenGL
プログラムファイル
invaderClass.h
# ifndef _INVADER_CLASS
# define _INVADER_CLASS
# include <GLUT/glut.h>
# include <cmath>
using namespace std;
# define DRAW_MAG 4 //描画時倍率
# define INVADER_SPEED 0.1 //インベーダーの移動速度
# define MOVE_COUNT 400 //インベーダーの折り返しカウント
# define PI 3.141592//π
# define HEIGHT 480 //画面縦幅
# define SHOT_X 20 //弾発射位置を真ん中に調整
class INVADER{
public:
INVADER(int x, int y); //constructor
void InvaderPoint(int x, int y); //描画
void Coordinate(); //座標
void Magnification(); //倍率変更
void Initialization(); //初期化
void Move(); //動き
bool Shot(int y); //攻撃
void Dead(); //死亡
int DrawyReturn(); //弾発射時用drawy渡し
int ShotxReturn(); //当たり判定時shotx渡し
bool Hit(int uShotx, int uShoty); //当たり判定
private:
bool life; //生死判定
double invx,invy; //bool invaderの座標取得ループの値保存用
double drawx, drawy;//描画位置
int shotx; //弾発射時のx座標保存
int mcount; //移動距離記憶
bool move; //移動方向記憶
const bool invader[10][10] = {
// y 0 1 2 3 4 5 6 7 8 9 x
{0,0,0,0,0,1,1,1,0,0,},//0
{0,0,0,0,1,1,0,0,0,0,},//1
{0,1,0,1,1,1,1,1,0,0,},//2
{0,0,1,1,0,1,1,0,1,0,},//3
{0,0,0,1,1,1,1,0,0,0,},//4
{0,0,0,1,1,1,1,0,0,0,},//5
{0,0,1,1,0,1,1,0,1,0,},//6
{0,1,0,1,1,1,1,1,0,0,},//7
{0,0,0,0,1,1,0,0,0,0,},//8
{0,0,0,0,0,1,1,1,0,0,},//9
};
};
# endif
invaderClass.cpp
# include "invaderClass.h"
# include <GLUT/glut.h>
# include <cmath>
# include <iostream>
using namespace std;
INVADER::INVADER(int x, int y){
life = true;
drawx = x; drawy = y;//初期位置設定
move = true;
Initialization();
}
//DRAW_MAG pix ずつ QUADS 作成し描画
void INVADER::InvaderPoint(int x, int y){
glColor3d(1.0,0.0,0.0); //赤色
glBegin(GL_QUADS);
glVertex2i(x ,y);
glVertex2i(x ,y+DRAW_MAG);
glVertex2i(x+DRAW_MAG,y+DRAW_MAG);
glVertex2i(x+DRAW_MAG,y);
glEnd();
}
//描画座標の決定
void INVADER::Coordinate(){
int x = invx + drawx,
y = invy + drawy;
InvaderPoint(x, y);
}
//倍率変更
void INVADER::Magnification(){
invx = invx * DRAW_MAG;
invy = invy * DRAW_MAG;
Coordinate();
}
//初期化。配列情報取得ループ
void INVADER::Initialization(){
for(int x = 0; x < 10; x++){
for(int y = 0; y < 10; y++){
if(invader[x][y] == true){
invx = x; invy = y;
Magnification();
}
}
}
}
//移動
void INVADER::Move(){
if(life){
if(move == true){
drawx -= INVADER_SPEED;//Left
}else if(move == false){
drawx += INVADER_SPEED;//Right
}
//壁に当たったら一段下がって折り返しの動きをさせる
mcount++;
if(mcount > MOVE_COUNT){
drawy += 10;//Dawn
mcount = 0;
move = move ? false : true;
}
Initialization();
}
}
//弾発射
bool INVADER::Shot(int y){
if(life){
//x座標の初期設定
if(y == drawy){
shotx = drawx;
}
glColor3d(1.0,1.0,1.0);
glPointSize(5);
glBegin(GL_POINTS);
glVertex2d(shotx+SHOT_X,y + 40);
glEnd();
//弾描画終了判定
if(y > HEIGHT){
return false;
}else return true;
}
}
//死亡
void INVADER::Dead(){
life = false;
}
//drawyを渡すだけ
int INVADER::DrawyReturn(){
return drawy;
}
//shotxを渡すだけ
int INVADER::ShotxReturn(){
return shotx;
}
//当たり判定部
bool INVADER::Hit(int uShotx, int uShoty){
if(life){
double x , y,
maxx = drawx, minx = drawx,
maxy = drawy, miny = drawy;
//円描画
for(double i = -20.0; i < 20.0; i++){
y = 20 * sin((i + 180) / PI);
x = 20 * cos((i + 180) / PI);
//Invaderの座標を適用
y += drawy + 20.0;
x += drawx + 20.0;
//x,yの最大、最小値を記憶
if(drawx + 20.0 < x && maxx < x)maxx = x;
if(drawx + 20.0 > x && minx > x)minx = x;
if(drawy + 20.0 < y && maxy < y)maxy = y;
if(drawy + 20.0 > y && miny > y)miny = y;
//x,yの範囲内に弾の座標が入れば死亡
//当たり判定は矩形になる
if(uShotx > minx && uShotx < maxx && uShoty > miny && uShoty < maxy){
Dead();
return true;
}
return false;
}
}
}
unitClass.h
# ifndef _UNIT_CLASS
# define _UNIT_CLASS
# include <GLUT/glut.h>
# include <cmath>
# define UNIT_X 300 //ユニット初期位置x軸
# define UNIT_Y 400 //ユニット初期位置Y軸
# define DRAW_MAG 4 //描画時倍率
# define WIDTH 640 //画面横幅
# define PI 3.141592//π
# define SHOT_X 20 //弾発射位置を真ん中に調整
class UNIT{
public:
UNIT();
void Drawing(int x, int y); //描画
void Coordinate(); //座標決定
void Magnification(); //倍率変更
void Initialization(); //初期化
int Shot(int y); //弾発射
void LeftMove(); //左移動
void RightMove(); //右移動
void Dead(); //死亡
void Hit(int iShotx, int iShoty); //当たり判定
private:
int unitx,unity;//bool arrayの座標取得ループの値保存用
int drawx,drawy;//描画位置
bool life; //生死判定
//[x][y]
bool unit[10][10] = {
// y 0 1 2 3 4 5 6 7 8 9 x
{0,0,0,0,0,1,1,1,1,0,},//0
{0,0,0,0,0,1,1,1,1,0,},//1
{0,0,0,0,0,1,1,1,1,0,},//2
{0,0,1,1,1,1,1,1,1,0,},//3
{0,0,1,1,1,1,1,1,1,0,},//4
{0,0,1,1,1,1,1,1,1,0,},//5
{0,0,1,1,1,1,1,1,1,0,},//6
{0,0,0,0,0,1,1,1,1,0,},//7
{0,0,0,0,0,1,1,1,1,0,},//8
{0,0,0,0,0,1,1,1,1,0,},//9
};
};
# endif
unitClass.cpp
# include "unitClass.h"
# include <GLUT/glut.h>
# include <cmath>
UNIT::UNIT(){
drawx = UNIT_X;
drawy = UNIT_Y;
life = true;
}
//DRAW_MAG pix ずつ QUADS 作成し描画
void UNIT::Drawing(int x, int y){
glColor3d(1.0,1.0,1.0);
glBegin(GL_QUADS);
glVertex2i(x ,y);
glVertex2i(x ,y+DRAW_MAG);
glVertex2i(x+DRAW_MAG,y+DRAW_MAG);
glVertex2i(x+DRAW_MAG,y);
glEnd();
}
//描画座標の決定
void UNIT::Coordinate(){
int x = unitx + drawx,
y = unity + drawy;
Drawing(x, y);
}
//倍率変更
void UNIT::Magnification(){
unitx = unitx * DRAW_MAG;
unity = unity * DRAW_MAG;
Coordinate();
}
//初期化。配列情報取得ループ
void UNIT::Initialization(){
if(life){
for(int x = 0; x < 10; x++){
for(int y = 0; y < 10; y++){
if(unit[x][y] == true){
unitx = x; unity = y;
Magnification();
}
}
}
}
}
//左移動
void UNIT::LeftMove(){
drawx -= 4;
if(drawx < 0)drawx = 0;
Initialization();
}
//右移動
void UNIT::RightMove(){
drawx += 4;
if(drawx > WIDTH - DRAW_MAG * 10)drawx = WIDTH - DRAW_MAG * 10;
Initialization();
}
//死亡
void UNIT::Dead(){
life = false;
}
//弾発射
int UNIT::Shot(int y){
if(life){
static int x = 0;
//x座標の初期設定
if(y == UNIT_Y){
x = drawx;
}
glColor3d(1.0,1.0,1.0);
glPointSize(4);
glBegin(GL_POINTS);
glVertex2d(x+SHOT_X,y);
glEnd();
//戻り値をUnit描画の中心に調整
return x + SHOT_X;
}
}
//当たり判定
void UNIT::Hit(int iShotx, int iShoty){
double x , y,
maxx = drawx, minx = drawx,
maxy = drawy, miny = drawy;
//円描画
for(double i = -20.0; i < 20.0; i++){
y = 20 * sin((i + 180) / PI);
x = 20 * cos((i + 180) / PI);
//Unitの座標を適用
y += drawy + 20.0;
x += drawx + 20.0;
//x,yの最大、最小値を記憶
if(drawx + 20.0 < x && maxx < x)maxx = x;
if(drawx + 20.0 > x && minx > x)minx = x;
if(drawy + 20.0 < y && maxy < y)maxy = y;
if(drawy + 20.0 > y && miny > y)miny = y;
//x,yの範囲内に弾の座標が入れば死亡
if(iShotx > minx && iShotx < maxx && iShoty + 40 > miny && iShoty + 40 < maxy){
Dead();
}
}
}
main.cpp
# include "invaderClass.h"
# include "unitClass.h"
# include <GLUT/glut.h>
# include <cstdlib>
# include <ctime>
# define WIDTH 640 //画面横幅
# define HEIGHT 480 //画面縦幅
# define INVADER_Y 40 //インベーダー初期位置Y軸
//UNIT弾発射判定
GLboolean unitBulletParameter = GL_FALSE;
UNIT unit;
int unitMove = 0;
void display(){
glClear(GL_COLOR_BUFFER_BIT);
static INVADER inv0( 40, INVADER_Y), inv1(120, INVADER_Y), inv2(200, INVADER_Y), inv3(280, INVADER_Y),
inv4(360, INVADER_Y), inv5(440, INVADER_Y), inv6(520, INVADER_Y), inv7(600, INVADER_Y);
//弾発射判定用
static bool inv0b = false, inv1b = false, inv2b = false, inv3b = false,
inv4b = false, inv5b = false, inv6b = false, inv7b = false;
//Invader描画
inv0.Move();inv1.Move();inv2.Move();inv3.Move();
inv4.Move();inv5.Move();inv6.Move();inv7.Move();
//Unit描画
unit.Initialization();
//unit射撃のための条件式
if(unitBulletParameter == GL_TRUE){
static int i = UNIT_Y; //弾のy座標
int x; //弾のx座標
x = unit.Shot(i);
i -= 8;
if(i < 0){
i = UNIT_Y;
unitBulletParameter = GL_FALSE;
}
//それぞれの当たり判定に重なるかチェック。重なれば弾は消える。
if(inv0.Hit(x,i)){unitBulletParameter = GL_FALSE; i = UNIT_Y;}
if(inv1.Hit(x,i)){unitBulletParameter = GL_FALSE; i = UNIT_Y;}
if(inv2.Hit(x,i)){unitBulletParameter = GL_FALSE; i = UNIT_Y;}
if(inv3.Hit(x,i)){unitBulletParameter = GL_FALSE; i = UNIT_Y;}
if(inv4.Hit(x,i)){unitBulletParameter = GL_FALSE; i = UNIT_Y;}
if(inv5.Hit(x,i)){unitBulletParameter = GL_FALSE; i = UNIT_Y;}
if(inv6.Hit(x,i)){unitBulletParameter = GL_FALSE; i = UNIT_Y;}
if(inv7.Hit(x,i)){unitBulletParameter = GL_FALSE; i = UNIT_Y;}
}
//敵弾発射ランダム
switch(rand() % 400){
case 0:
inv0b = true;
break;
case 1:
inv1b = true;
break;
case 2:
inv2b = true;
break;
case 3:
inv3b = true;
break;
case 4:
inv4b = true;
break;
case 5:
inv5b = true;
break;
case 6:
inv6b = true;
break;
case 7:
inv7b = true;
break;
}
//敵の弾発射部分
if(inv0b){
bool foo = false;
int x;
static int i = 0;
if(i >= HEIGHT){
i = inv0.DrawyReturn();
}
inv0b = inv0.Shot(i);
i += 4;
x = inv0.ShotxReturn();
unit.Hit(x+SHOT_X, i);
}
if(inv1b){
int x;
static int i = 0;
if(i >= HEIGHT){
i = inv1.DrawyReturn();
}
inv1b = inv1.Shot(i);
i += 4;
x = inv1.ShotxReturn();
unit.Hit(x+SHOT_X, i);
}
if(inv2b){
int x;
static int i = 0;
if(i >= HEIGHT){
i = inv2.DrawyReturn();
}
inv2b = inv2.Shot(i);
i += 4;
x = inv2.ShotxReturn();
unit.Hit(x+SHOT_X, i);
}
if(inv3b){
int x;
static int i = 0;
if(i >= HEIGHT){
i = inv3.DrawyReturn();
}
inv3b = inv3.Shot(i);
i += 4;
x = inv3.ShotxReturn();
unit.Hit(x+SHOT_X, i);
}
if(inv4b){
int x;
static int i = 0;
if(i >= HEIGHT){
i = inv4.DrawyReturn();
}
inv4b = inv4.Shot(i);
i += 4;
x = inv4.ShotxReturn();
unit.Hit(x+SHOT_X, i);
}
if(inv5b){
int x;
static int i = 0;
if(i >= HEIGHT){
i = inv5.DrawyReturn();
}
inv5b = inv5.Shot(i);
i += 4;
x = inv5.ShotxReturn();
unit.Hit(x+SHOT_X, i);
}
if(inv6b){
int x;
static int i = 0;
if(i >= HEIGHT){
i = inv6.DrawyReturn();
}
inv6b = inv6.Shot(i);
i += 4;
x = inv6.ShotxReturn();
unit.Hit(x+SHOT_X, i);
}
if(inv7b){
int x;
static int i = 0;
if(i >= HEIGHT){
i = inv7.DrawyReturn();
}
inv7b = inv7.Shot(i);
i += 4;
x = inv7.ShotxReturn();
unit.Hit(x+SHOT_X, i);
}
glutSwapBuffers();
}
void UnitMove(){
if(unitMove == 1)unit.LeftMove();
else if(unitMove == 2)unit.RightMove();
glutPostRedisplay();
}
void keyboard(unsigned char key, int x, int y){
switch(key){
case ' ':
unitBulletParameter = GL_TRUE;
glutPostRedisplay();
break;
case 'a':
case 'A':
unitMove = 1;
break;
case 'd':
case 'D':
unitMove = 2;
break;
case 'q':
case 'Q':
case '\033':
exit(0);
}
}
void keyboardup(unsigned char key, int x, int y){
switch(key){
case 'a':
case 'A':
unitMove = 0;
break;
case 'd':
case 'D':
unitMove = 0;
break;
}
}
void specialKey(int key, int x, int y){
switch(key){
case GLUT_KEY_LEFT:
unitMove = 1;
break;
case GLUT_KEY_RIGHT:
unitMove = 2;
break;
}
}
void specialUpKey(int key, int x, int y){
switch(key){
case GLUT_KEY_LEFT:
unitMove = 0;
break;
case GLUT_KEY_RIGHT:
unitMove = 0;
break;
}
}
void timer(int value){
glutIdleFunc(UnitMove);
glutTimerFunc(50,timer,0);
}
void Init(){
glClearColor(0.0,0.0,0.0,0.0);
glOrtho(0,WIDTH,HEIGHT,0,-1,1);
}
void resize(int w, int h){
glViewport(0,0,w,h);
glLoadIdentity();
glOrtho(0,WIDTH,HEIGHT,0,-1,1);
}
int main(int argc, char *argv[]){
srand((unsigned int)time(NULL));
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
glutInitWindowSize(WIDTH,HEIGHT);
glutCreateWindow("Invader");
glutDisplayFunc(display);
glutKeyboardFunc(keyboard);
glutKeyboardUpFunc(keyboardup);
glutSpecialFunc(specialKey);
glutSpecialUpFunc(specialUpKey);
glutIgnoreKeyRepeat(GL_TRUE); //キーの繰り返し入力を無視
glutReshapeFunc(resize);
glutTimerFunc(100,timer,0);
Init();
glutMainLoop();
return 0;
}
今後修正したい部分として
1.クラスの配列化
2.if文の統一
等をし、コードを簡略化しつつ、
1.スタート画面、終了画面の実装
2.BGM、SEの実装
等を行いたいと考えてます。