- エイリアンが攻撃してくるようになりました
- エイリアンの弾にあったたら即終了
プログラムを実現する、という目的の他に、タイトルにもしている「わかりやすいコード」が実現できているか、考えながら進行中。個人的にはBounds(0, 0, width, height).includes(position_)
というのが、意味通りにコードを書けた気がしているところ(BoundsよりBoundaryの方がいいかもしれませんが)。
以下、コード。
#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) {} Point& operator += (const Point& point) { x += point.x; y += point.y; return *this; } Point& operator -= (const Point& point) { x -= point.x; y -= point.y; return *this; } }; 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; class Bounds { public: Bounds() : leftTop_(0, 0), rightBottom_(0, 0) {} Bounds(const Point& leftTop, const Point& rightBottom) : leftTop_(leftTop), rightBottom_(rightBottom) { rearranges(); } Bounds(int x0, int y0, int x1, int y1) : leftTop_(x0, y0), rightBottom_(x1, y1) { rearranges(); } const Point& getLeftTop() const { return leftTop_; } const Point getRightTop() const { return Point(leftTop_.x, rightBottom_.y); } const Point getLeftBottom() const { return Point(rightBottom_.x, leftTop_.y); } const Point& getRightBottom() const { return rightBottom_; } bool includes(const Point& point) const { return (leftTop_.x <= point.x) && (point.x <= rightBottom_.x) && (leftTop_.y <= point.y) && (point.y <= rightBottom_.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()); } private: void rearranges() { if(rightBottom_.x < leftTop_.x) { std::swap(leftTop_.x, rightBottom_.x); } if(rightBottom_.y < leftTop_.y) { std::swap(leftTop_.y, rightBottom_.y); } } Point leftTop_; Point rightBottom_; }; std::ostream& operator << (std::ostream& out, const Bounds& bounds) { return out << bounds.getLeftTop() << "-" << bounds.getRightBottom(); } 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 setPosition(const Point& position) { position_ = position; } virtual Bounds getBounds() const = 0; virtual bool isVisible() const { return Bounds(0, 0, width, height).includes(position_); } void move(const Point& delta) { position_.x += delta.x; position_.y += delta.y; } void drawWithColor(const RGBColor& color) { shape_.draw(position_, color, GL_POLYGON); } virtual void draw() = 0; protected: void addPoint(const Point& point) { shape_.addPoint(point); } private: Shape shape_; Point position_; }; class Bullet : public Object { public: Bullet() : Object(Point(0, 0)), deltaY_(0), color_(), visible_(false) { } Bullet(const Point& point, int delta, const RGBColor& color) : Object(point), deltaY_(delta), color_(color), 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, deltaY_)); visible_ = Object::isVisible(); } } void setVisible(bool value) { visible_ = value; } bool isVisible() const { return visible_; } Bounds getBounds() const { return Bounds(getPosition() + Point(-3, -10), getPosition() + Point(3, 0)); } void draw() { if(visible_) { drawWithColor(color_); } } private: int deltaY_; RGBColor color_; 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 fire() { return Bullet(getPosition(), -10, RGBColor(0, 0, 1)); } Bounds getBounds() const { return Bounds(getPosition() + Point(-10, -10), getPosition() + Point(10, 10)); } void draw() { drawWithColor(RGBColor(0, 0, 1)); } }; class Alien : public Object { public: Alien() : Object(Point(20, 20)), deltaX_(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 { return Bounds(getPosition() + Point(-10, -5), getPosition() + Point(10, 5)); } Bullet fire() { return Bullet(getPosition(), 10, RGBColor(1, 0.5, 0)); } void turn() { deltaX_ = -deltaX_; } void move() { Object::move(Point(deltaX_, 0)); if((getPosition().x < 0) || (width < getPosition().x)) { turn(); Object::move(Point(0, 10)); } } void draw() { drawWithColor(RGBColor(1, 0.5, 0)); } private: int deltaX_; }; struct Overlap : public std::unary_function<const Object&, bool> { const Bounds& bounds; explicit Overlap(const Bounds& bounds) : bounds(bounds) {} bool operator() (const Object& object) { return object.getBounds().isOverlapWith(bounds); } }; class Aliens { public: void add() { aliens_.push_back(Alien()); } bool isShotBy(const Object& object) { if( ! object.isVisible()) { return false; } std::vector<Alien>::iterator i = std::find_if(aliens_.begin(), aliens_.end(), Overlap(object.getBounds())); if(i != aliens_.end()) { aliens_.erase(i); return true; } return false; } bool hasShot(const Object& object) { return std::find_if(bullets_.begin(), bullets_.end(), Overlap(object.getBounds())) != bullets_.end(); } void draw() { std::for_each(aliens_.begin(), aliens_.end(), std::mem_fun_ref(&Alien::draw)); std::for_each(bullets_.begin(), bullets_.end(), std::mem_fun_ref(&Bullet::draw)); } void move() { std::for_each(aliens_.begin(), aliens_.end(), std::mem_fun_ref(&Alien::move)); std::for_each(bullets_.begin(), bullets_.end(), std::mem_fun_ref(&Bullet::move)); bullets_.erase(remove_if(bullets_.begin(), bullets_.end(), std::not1(std::mem_fun_ref(&Bullet::isVisible))), bullets_.end()); for(std::vector<Alien>::iterator i = aliens_.begin(); i != aliens_.end(); ++i) { if((std::rand() % 20) == 0) // エイリアン1匹当たりの発射頻度 { bullets_.push_back(i->fire()); } } } private: std::vector<Alien> aliens_; std::vector<Bullet> bullets_; }; //-- こっから先がゲームのコード 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.fire(); } 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(aliens.isShotBy(myBullet)) { score += 10; myBullet.setVisible(false); } if(aliens.hasShot(myShip)) { exit(0); } if((std::rand() % 20) == 0) // エイリアン発生頻度 { aliens.add(); } myBullet.move(); aliens.move(); myShip.draw(); myBullet.draw(); aliens.draw(); std::stringstream oss; oss.fill('0'); oss << "SCORE : " << std::setw(10) << score; drawString(10, 12, oss.str().c_str()); 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; }