エンジニアのソフトウェア的愛情

または私は如何にして心配するのを止めてプログラムを・愛する・ようになったか

わかりやすいコード・その3

エイリアン登場。課題もどんどん登場。

  • グローバル変数が増えている
  • マジックナンバーもいっぱい
  • エイリアンの登場パターンがただのランダム
  • エイリアンを倒せない
  • エイリアンが減らないので画面上に溢れる
  • 他、スコアがでないとか、エイリアンが弾を撃ってこないとか、いっぱい
  • コードが400行近くなってきて、このままブログで紹介は厳しいか

以下、コード。

#include <cstdlib>
#include <functional>
#include <algorithm>
#include <vector>

#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) {}
};

typedef std::vector<Point> Points;

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();
        glFlush();
    }

private:
    Points points_;
};

class Object
{
public:
    explicit Object(const Point& position) : position_(position)
    {
    }

    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;
    }

    void erase()
    {
        drawWithColor(1, 1, 1);
    }

    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:
    explicit Bullet(const Point& point) : Object(point), velocityY_(-10)
    {
        addPoint(Point(-3,  -5));
        addPoint(Point( 0, -10));
        addPoint(Point( 3,  -5));
        addPoint(Point( 0,   0));
    }

    ~Bullet()
    {
        erase();
    }

    void move()
    {
        erase();
        Object::move(Point(0, velocityY_));
        draw();
    }

    bool isVisible() const
    {
        return (0 <= getPosition().x) && (getPosition().x <= width) &&
               (0 <= getPosition().y) && (getPosition().y <= height);
    }

    void draw()
    {
        drawWithColor(0, 0, 1);
    }

private:
    int   velocityY_;
};

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 left()  { erase(); move(Point(-Delta, 0)); draw(); }
    void right() { erase(); move(Point(+Delta, 0)); draw(); }
    void up()    { erase(); move(Point(0, -Delta)); draw(); }
    void down()  { erase(); move(Point(0, +Delta)); draw(); }

    Bullet* fireBullet()
    {
        return new Bullet(getPosition());
    }

    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));
    }

    void turn()
    {
        velocityX_ = -velocityX_;
    }

    void move()
    {
        erase();

        Object::move(Point(velocityX_, 0));
        if((getPosition().x < 0) || (width < getPosition().x))
        {
            turn();
            Object::move(Point(0, 10));
        }

        draw();
    }

    void draw()
    {
        drawWithColor(1, 0.5, 0);
    }

private:
    int velocityX_;
};

class Aliens
{
public:
    void add()
    {
        aliens_.push_back(Alien());
    }

    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 = 0;
static Aliens  aliens;

void display()
{
    glClear(GL_COLOR_BUFFER_BIT);

    myShip.draw();
    aliens.draw();
}

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 == 0)
        {
            myBullet = myShip.fireBullet();
            myBullet->draw();
        }
        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.left();  break;
    case GLUT_KEY_UP:    myShip.up();    break;
    case GLUT_KEY_RIGHT: myShip.right(); break;
    case GLUT_KEY_DOWN:  myShip.down();  break;
    default:                             break;
    }
}

void timer(int)
{
    if(myBullet != 0)
    {
        myBullet->move();

        if( ! myBullet->isVisible())
        {
            delete myBullet;
            myBullet = 0;
        }
    }

    if((std::rand() % 20) == 0)
    {
        aliens.add();
    }

    aliens.move();

    glutTimerFunc(Interval, timer, 0);
}

int main(int argc, char* argv[])
{
    glutInitWindowPosition(100, 100);
    glutInitWindowSize(width, height);
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_RGBA);

    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;
}