These search terms have been highlighted:[c++]
2009-04-02
眠かったから,ncurses ライブラリを使ってシューティングゲームを作ってみた.10年以上昔,N88-BASICで同じようなプログラムを作ってたのを思いだした.
作ったのは,
こんなん.十字キーで動かしてスペースでミサイルを発射(3発まで).敵を全部撃ち落とすか,自分が敵にやられると終了.嫌になったらqキーで終了.
コード(雑) †
//-------------------------------------------------------------------------------------------
/*! \file sample.cpp
\brief shooting game on console
\author aki-yam
\version 0.1
\date Apr.02, 2009
\version 0.2
\date Apr.28, 2009 スレッド関連のバグ修正
*/
//-------------------------------------------------------------------------------------------
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <curses.h>
//-------------------------------------------------------------------------------------------
namespace loco_rabbits
{
class TWorld
{
private:
int sizex, sizey;
int oldx, oldy;
WINDOW *win;
bool locked;
struct TCell
{
char s;
int col;
TCell(void) : s(' '), col(0) {};
TCell(char _s) : s(_s), col(0) {};
TCell(char _s,int _c) : s(_s), col(_c) {};
};
std::vector<TCell> display;
TCell& getCell (int x, int y) {return display[(sizex+2)*y+x];};
const TCell& getCell (int x, int y) const {return display[(sizex+2)*y+x];};
public:
TWorld(void) : sizex(0), sizey(0), oldx(-1), oldy(-1), win(NULL), locked(false) {};
~TWorld(void)
{
endwin();
};
void init(int _sizex, int _sizey, bool hidecursor=true)
{
sizex=_sizex; sizey=_sizey;
if(win!=NULL) {endwin(); win=NULL;}
display.resize ((sizex+2)*(sizey+2));
win=initscr();
timeout(1);
noecho();
cbreak();
leaveok(stdscr, TRUE);
scrollok(stdscr, FALSE);
if (hidecursor) curs_set(0);
if (has_colors())
{
start_color();
use_default_colors();
init_pair(0, COLOR_BLACK, -1);
init_pair(1, COLOR_RED, -1);
init_pair(2, COLOR_GREEN, -1);
init_pair(3, COLOR_YELLOW, -1);
init_pair(4, COLOR_BLUE, -1);
init_pair(5, COLOR_CYAN, -1);
init_pair(6, COLOR_MAGENTA, -1);
init_pair(7, COLOR_WHITE, -1);
}
clear();
flush();
};
void clear(void)
{
std::fill (display.begin(),display.end(),TCell());
// draw walls
attrset (COLOR_PAIR(2));
int x,y;
for(x=1,y=0;x<=sizex;++x) getCell(x,y)=TCell('-',2);
for(x=0,y=1;y<=sizey;++y) getCell(x,y)=TCell('|',2);
for(x=1,y=sizey+1;x<=sizex;++x) getCell(x,y)=TCell('-',2);
for(x=sizex+1,y=1;y<=sizey;++y) getCell(x,y)=TCell('|',2);
getCell(0,0)=TCell('+',2);
getCell(0,sizey+1)=TCell('+',2);
getCell(sizex+1,0)=TCell('+',2);
getCell(sizex+1,sizey+1)=TCell('+',2);
};
void forceRange(int &x, int &y)
{
if(x<0) x=0;
else if(x>=sizex) x=sizex-1;
if(y<0) y=0;
else if(y>=sizey) y=sizey-1;
};
void flush (void)
{
locked= true;
std::vector<TCell>::const_iterator itr(display.begin());
for(int y=0;y<sizey+2;++y)
for(int x=0;x<sizex+2;++x,++itr)
{
attrset (COLOR_PAIR(itr->col));
mvaddch (y,x,itr->s);
}
attrset (COLOR_PAIR(0));
refresh();
locked= false;
};
void putChar (int x, int y, char s, int col=0)
{
forceRange(x,y); ++x; ++y;
getCell(x,y)=TCell(s,col);
};
int getChar (void)
{
while (locked) usleep(10);
return getch();
};
void putString (int x, int y, const char *str, int col=0)
{
locked= true;
attrset (COLOR_PAIR(col));
for (const char *s=str; *s!='\0'; ++s,++x)
mvaddch(sizey+2+y,x,*s);
flush();
locked= false;
};
};
//-------------------------------------------------------------------------------------------
/* these names conflict with STL */
#undef clear
//-------------------------------------------------------------------------------------------
}
//-------------------------------------------------------------------------------------------
#include <sstream>
#include <iomanip>
#include <pthread.h>
#include <cstdlib>
#include <cmath>
//-------------------------------------------------------------------------------------------
inline double u_rand (const double &max)
{
return (max)*static_cast<double>(rand()) / static_cast<double>(RAND_MAX);
}
inline double u_rand (const double &min, const double &max)
{
return u_rand(max - min) + min;
}
inline int u_rand (int min, int max)
// return [min,max]
{
return floor(u_rand(static_cast<double>(min),static_cast<double>(max+1)));
}
//-------------------------------------------------------------------------------------------
using namespace std;
using namespace loco_rabbits;
//-------------------------------------------------------------------------------------------
struct TMissile
{
bool active;
float ry;
int x,y;
TMissile(void) : active(false) {}
void fire (int _x, int _y)
{
active= true;
x= _x;
y= _y;
ry= y;
}
void step(void)
{
ry-=0.5;
y=static_cast<int>(ry);
}
};
struct TEnemy
{
bool active, fired;
int x,y;
TEnemy(void) : active(true), fired(false) {}
void step(void)
{
x+= u_rand(-1,1);
y+= 1;
}
};
enum TKeyFlag {kfNone=0,kfEnd,kfFire};
class TSimulator
{
private:
TWorld world;
int sizex, sizey;
int x, y;
int velx, vely;
bool fired;
vector<TMissile> missile;
vector<TEnemy> enemy;
TKeyFlag keyflag;
pthread_t thread_kbhit;
void step_simulate (void);
friend void* kbhit (void *sim); // この関数は private メンバにアクセスできる
public:
TSimulator (int num_of_enemy=3, int num_of_missile=3)
: missile(num_of_missile),
enemy(num_of_enemy)
{}
void start (int _sizex, int _sizey);
};
//-------------------------------------------------------------------------------------------
void* kbhit (void *arg)
{
TSimulator *sim(reinterpret_cast<TSimulator*>(arg));
while(!sim->fired && sim->keyflag!=kfEnd)
{
int key= sim->world.getChar();
if (key=='q')
{
sim->keyflag= kfEnd;
break;
}
if (key==0x41) sim->vely-=1;
else if (key==0x42) sim->vely+=1;
else if (key==0x43) sim->velx+=1;
else if (key==0x44) sim->velx-=1;
else if (key==' ') sim->keyflag= kfFire;
}
return NULL;
}
//-------------------------------------------------------------------------------------------
void TSimulator::start (int _sizex, int _sizey)
{
sizex= _sizex;
sizey= _sizey;
world.init (sizex,sizey);
world.putString(0,0,"usage: arrow key: move, space: missile, `q': exit",2);
// setup myself
x= sizex/2;
y= sizey-1;
velx=vely= 0;
fired=false;
// setup enemy
for (size_t i(0); i<enemy.size(); ++i)
{
enemy[i].x= u_rand(0,sizex-1);
enemy[i].y= u_rand(0,4);
}
// setup missile
keyflag= kfNone;
if (pthread_create(&thread_kbhit, NULL, &kbhit, this)!=0)
{
cerr<<"error in pthread_create"<<endl;
exit(1);
}
while (keyflag!=kfEnd && !fired)
{
world.clear();
// simulate
step_simulate();
// plot
for (size_t i(0); i<enemy.size(); ++i)
{
if (enemy[i].active)
{
if (enemy[i].fired) world.putChar (enemy[i].x,enemy[i].y,'*',1);
else world.putChar (enemy[i].x,enemy[i].y,'V',6);
}
}
for (size_t i(0); i<missile.size(); ++i)
if (missile[i].active) world.putChar (missile[i].x,missile[i].y,'^',3);
if (fired)
{
world.putChar (x,y,'*',1);
world.putString(0,1,"crash! (hit any key)",1);
}
else
world.putChar (x,y,'A',4);
world.flush();
usleep(70000);
}
usleep(1000000);
void *ret(NULL);
pthread_join(thread_kbhit,&ret);
}
//-------------------------------------------------------------------------------------------
void TSimulator::step_simulate (void)
{
if (keyflag==kfFire)
{
keyflag= kfNone;
for (size_t i(0); i<missile.size(); ++i)
if (!missile[i].active) // 使用していないミサイル
{missile[i].fire(x,y); break;}
}
x+= velx;
y+= vely;
velx= 0;
vely= 0;
world.forceRange(x,y);
bool no_enemy(true);
for (size_t i(0); i<enemy.size(); ++i)
{
if (enemy[i].fired) enemy[i].active= false;
if (enemy[i].active)
{
no_enemy= false;
enemy[i].step();
if (enemy[i].y>=sizey) enemy[i].y=0;
world.forceRange(enemy[i].x,enemy[i].y);
if (enemy[i].x==x && enemy[i].y==y) fired= true;
}
}
if (no_enemy)
{
keyflag= kfEnd;
world.putString(0,1,"you win! (hit any key)",4);
}
for (size_t i(0); i<missile.size(); ++i)
{
if (missile[i].active)
{
missile[i].step();
if (missile[i].y<0) missile[i].active= false;
for (size_t i(0); i<enemy.size(); ++i)
{
if (enemy[i].x==missile[i].x && enemy[i].y==missile[i].y)
{
enemy[i].fired= true;
missile[i].active= false;
}
}
}
}
}
//-------------------------------------------------------------------------------------------
int main(int argc, char**argv)
{
srand((unsigned)time(NULL));
TSimulator sim;
sim.start(40,20);
return 0;
}
//-------------------------------------------------------------------------------------------
コンパイル †
g++ -g -Wall -O2 ファイル名.cpp -lm -lncurses -lpthread
遊びかた †
コードをコピーしてコンパイルして ./a.out で動くと思います.
- 十字キー: 移動
- スペース: ミサイルを発射(3発まで)
- qキー: 終了
- 敵を全部撃ち落とすか,自分が敵にやられるか,qキーで終了
その後...(2014年) †
今でもたまに遊びます.