- エイリアンを倒せるようになりました
- スコアがつきました
- 表示をダブルバッファリングに替えたのでいくぶん綺麗になった気分
- 行数かかる書き方とはいえ、500行を越えてしまった
- まだグローバル変数がおおいなぁ、マジックナンバーも
以下、コード。
#include <cstdlib> #include <functional> #include <algorithm> #include <vector> #include <sstream> #include <iomanip> #include <iostream> #include <GLUT/glut.h> static const int Interval = 100; static const int Width = 320; static const int Height = 240; static int width = Width; static int height = Height; class RGBColor { public: RGBColor() { set(0, 0, 0); } RGBColor(GLdouble r,GLdouble g,GLdouble b) { set(r, g, b); } void set(GLdouble r,GLdouble g,GLdouble b) { rgb[0] = r; rgb[1] = g; rgb[2] = b; } const GLdouble* get() const { return rgb; } void setRed(GLdouble value) { rgb[0] = value; } void setGreen(GLdouble value) { rgb[1] = value; } void setBlue(GLdouble value) { rgb[2] = value; } GLdouble getRed() const { return rgb[0]; } GLdouble getGreen() const { return rgb[1]; } GLdouble getBlue() const { return rgb[2]; } private: GLdouble rgb[3]; }; struct Point { int x; int y; Point() : x(0), y(0) {} Point(int x, int y) : x(x), y(y) {} void operator += (const Point& point) { x += point.x; y += point.y; } void operator -= (const Point& point) { x -= point.x; y -= point.y; } }; Point operator + (Point lhs, const Point& rhs) { lhs += rhs; return lhs; } Point operator - (Point lhs, const Point& rhs) { lhs -= rhs; return lhs; } std::ostream& operator << (std::ostream& out, const Point& point) { return out << "(" << point.x << ", " << point.y << ")"; } typedef std::vector<Point> Points; struct Bounds { Point begin; Point end; Bounds() : begin(0, 0), end(0, 0) {} void rearranges() { if(end.x < begin.x) { std::swap(begin.x, end.x); } if(end.y < begin.y) { std::swap(begin.y, end.y); } } Bounds(int x0, int y0, int x1, int y1) : begin(x0, y0), end(x1, y1) { rearranges(); } Bounds(const Point& begin, const Point& end) : begin(begin), end(end) { rearranges(); } const Point& getLeftTop() const { return begin; } const Point getRightTop() const { return Point(begin.x, end.y); } const Point getLeftBottom() const { return Point(end.x, begin.y); } const Point& getRightBottom() const { return end; } bool includes(const Point& point) const { return (begin.x <= point.x) && (point.x <= end.x) && (begin.y <= point.y) && (point.y <= end.y); } bool isOverlapWith(const Bounds& bounds) const { return includes(bounds.getLeftTop()) || includes(bounds.getRightTop()) || includes(bounds.getLeftBottom()) || includes(bounds.getRightBottom()) || bounds.includes(getLeftTop()) || bounds.includes(getRightTop()) || bounds.includes(getLeftBottom()) || bounds.includes(getRightBottom()); } }; std::ostream& operator << (std::ostream& out, const Bounds& bounds) { return out << bounds.begin << "-" << bounds.end; } struct Vertex : public std::unary_function<const Point&, void> { explicit Vertex(const Point& origin) : origin_(origin) {} void operator() (const Point& point) { glVertex2i(origin_.x + point.x, origin_.y + point.y); } const Point origin_; }; class Shape { public: void addPoint(const Point& point) { points_.push_back(point); } void draw(const Point& position, const RGBColor& color, GLenum mode) { glColor3dv(color.get()); glBegin(mode); std::for_each(points_.begin(), points_.end(), Vertex(position)); glEnd(); } private: Points points_; }; class Object { public: explicit Object(const Point& position) : position_(position) { } virtual ~Object() { } const Point& getPosition() const { return position_; } void moveTo(const Point& position) { position_ = position; } void move(const Point& delta) { position_.x += delta.x; position_.y += delta.y; } virtual Bounds getBounds() const = 0; virtual void draw() = 0; void drawWithColor(double r, double g, double b) { shape_.draw(position_, RGBColor(r, g, b), GL_POLYGON); } protected: void addPoint(const Point& point) { shape_.addPoint(point); } private: Shape shape_; Point position_; }; class Bullet : public Object { public: Bullet() : Object(Point(0, 0)), velocityY_(0), visible_(false) { } explicit Bullet(const Point& point) : Object(point), velocityY_(-10), visible_(true) { addPoint(Point(-3, -5)); addPoint(Point( 0, -10)); addPoint(Point( 3, -5)); addPoint(Point( 0, 0)); } ~Bullet() { } void move() { if(visible_) { Object::move(Point(0, velocityY_)); visible_ = (0 <= getPosition().x) && (getPosition().x <= width) && (0 <= getPosition().y) && (getPosition().y <= height); } } void setVisible(bool value) { visible_ = value; } bool isVisible() const { return visible_; } Bounds getBounds() const { const Point p = getPosition(); return Bounds(p + Point(-3, -10), p + Point(3, 0)); } void draw() { if(visible_) { drawWithColor(0, 0, 1); } } private: int velocityY_; bool visible_; }; class Ship : public Object { public: Ship() : Object(Point(Width / 2, Height / 2)) { addPoint(Point(-10, 0)); addPoint(Point( 0, -10)); addPoint(Point( 10, 0)); addPoint(Point( 10, 10)); addPoint(Point(-10, 10)); } static const int Delta = 10; void moveLeft() { move(Point(-Delta, 0)); } void moveRight() { move(Point(+Delta, 0)); } void moveUp() { move(Point(0, -Delta)); } void moveDown() { move(Point(0, +Delta)); } Bullet fireBullet() { return Bullet(getPosition()); } Bounds getBounds() const { const Point p = getPosition(); return Bounds(p + Point(-10, -10), p + Point(10, 10)); } void draw() { drawWithColor(0, 0, 1); } }; class Alien : public Object { public: Alien() : Object(Point(20, 20)), velocityX_(5) { addPoint(Point( -5, -5)); addPoint(Point( 5, -5)); addPoint(Point( 10, 0)); addPoint(Point( 5, 5)); addPoint(Point( 2, 2)); addPoint(Point( -2, 2)); addPoint(Point( -5, 5)); addPoint(Point(-10, 0)); } ~Alien() { } Bounds getBounds() const { const Point p = getPosition(); return Bounds(p + Point(-10, -5), p + Point(10, 5)); } void turn() { velocityX_ = -velocityX_; } void move() { Object::move(Point(velocityX_, 0)); if((getPosition().x < 0) || (width < getPosition().x)) { turn(); Object::move(Point(0, 10)); } } void draw() { drawWithColor(1, 0.5, 0); } private: int velocityX_; }; class Aliens { public: void add() { aliens_.push_back(Alien()); } bool isShotBy(const Object& object) { for(std::vector<Alien>::iterator i = aliens_.begin(); i != aliens_.end(); ++i) { if(i->getBounds().isOverlapWith(object.getBounds())) { aliens_.erase(i); return true; } } return false; } void draw() { std::for_each(aliens_.begin(), aliens_.end(), std::mem_fun_ref(&Alien::draw)); } void move() { std::for_each(aliens_.begin(), aliens_.end(), std::mem_fun_ref(&Alien::move)); } private: std::vector<Alien> aliens_; }; //-- こっから先がゲームのコード static Ship myShip; static Bullet myBullet; static Aliens aliens; static int score = 0; void drawString(int x, int y, const char* text) { glColor3dv(RGBColor(1, 0, 0).get()); glPushMatrix(); glTranslatef(x, y, 0); glScalef(0.10, -0.10, 0); for(const char* p = text; *p != '?0'; ++p) { glutStrokeCharacter(GLUT_STROKE_ROMAN, *p); } glPopMatrix(); } void display() { glClear(GL_COLOR_BUFFER_BIT); myShip.draw(); myBullet.draw(); aliens.draw(); glutSwapBuffers(); } void resize(int w, int h) { glViewport(0, 0, w, h); width = w; height = h; glLoadIdentity(); glOrtho(-0.5, w - 0.5, h - 0.5, -0.5, -1.0, 1.0); } void keyboard(unsigned char key, int, int) { switch(key) { case ' ': if( ! myBullet.isVisible()) { myBullet = myShip.fireBullet(); } break; case 'q': case 'Q': case '?033': std::exit(0); break; default: break; } } void specialKey(int key, int x, int y) { switch(key) { case GLUT_KEY_LEFT: myShip.moveLeft(); break; case GLUT_KEY_UP: myShip.moveUp(); break; case GLUT_KEY_RIGHT: myShip.moveRight(); break; case GLUT_KEY_DOWN: myShip.moveDown(); break; default: break; } } void timer(int) { glClear(GL_COLOR_BUFFER_BIT); if(myBullet.isVisible()) { myBullet.move(); if(aliens.isShotBy(myBullet)) { score += 10; myBullet.setVisible(false); } } if((std::rand() % 20) == 0) { aliens.add(); } aliens.move(); std::stringstream oss; oss.fill('0'); oss << "SCORE : " << std::setw(10) << score; drawString(10, 12, oss.str().c_str()); myShip.draw(); myBullet.draw(); aliens.draw(); glutSwapBuffers(); glutTimerFunc(Interval, timer, 0); } int main(int argc, char* argv[]) { glutInitWindowPosition(100, 100); glutInitWindowSize(width, height); glutInit(&argc, argv); glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE); glutCreateWindow("to write more comprehensible code"); glutDisplayFunc(display); glutReshapeFunc(resize); glutKeyboardFunc(keyboard); glutSpecialFunc(specialKey); glutTimerFunc(Interval, timer, 0); glClearColor(1.0, 1.0, 1.0, 1.0); glutMainLoop(); return 0; }