ライフゲーム
提供:kuhalaboWiki
(版間での差分)
(ページの作成:「=== Cellクラスを作る=== ;Cell.h <pre> #pragma once #include "ofMain.h" class Cell { public: int currState; //現在の状態 int nextState; //次回の状態 ...」) |
|||
(1人の利用者による、間の16版が非表示) | |||
1行: | 1行: | ||
+ | == openFrameworksのサンプル == | ||
+ | |||
=== Cellクラスを作る=== | === Cellクラスを作る=== | ||
11行: | 13行: | ||
int nextState; //次回の状態 | int nextState; //次回の状態 | ||
int activeNeighbors; //近傍の生命の数 | int activeNeighbors; //近傍の生命の数 | ||
− | ofColor color; // | + | ofColor color; //セルの色 |
ofColor gcolor; //グリッドの色 | ofColor gcolor; //グリッドの色 | ||
61行: | 63行: | ||
break; | break; | ||
default: | default: | ||
− | nextState = | + | nextState = 0; |
break; | break; | ||
} | } | ||
86行: | 88行: | ||
</pre> | </pre> | ||
+ | === 主プログラム === | ||
+ | ;ofApp.h | ||
<pre> | <pre> | ||
+ | #pragma once | ||
+ | |||
+ | #include "ofMain.h" | ||
+ | #include "Cell.h" | ||
+ | |||
+ | class ofApp : public ofBaseApp { | ||
+ | |||
+ | public: | ||
+ | void setup(); | ||
+ | void update(); | ||
+ | void draw(); | ||
+ | |||
+ | void keyPressed(int key); | ||
+ | void mousePressed(int x, int y, int button); | ||
+ | |||
+ | void keyReleased(int key); | ||
+ | void mouseMoved(int x, int y ); | ||
+ | void mouseDragged(int x, int y, int button); | ||
+ | void mouseReleased(int x, int y, int button); | ||
+ | void windowResized(int w, int h); | ||
+ | void dragEvent(ofDragInfo dragInfo); | ||
+ | void gotMessage(ofMessage msg); | ||
+ | |||
+ | //ライフゲームのセルに関する定義 | ||
+ | Cell **grid; | ||
+ | int rows, cols; //セルの個数。縦と横の2次元配列 | ||
+ | float cellWidth, cellHeight; //グリッドの幅と高さのピクセル数 | ||
+ | bool fullScreen; // フルスクリーンモードの切り替え | ||
+ | bool active; // オートマトンの進行のオン・オフ | ||
+ | |||
+ | void init(int width, int height, int cellsize); //グリッドの初期化 | ||
+ | void pause(); //オートマトンの進行を止める | ||
+ | int getNumActiveNeighbors(int colIndex, int rowIndex); //近傍の生命の数をカウントする | ||
+ | void RandomCell(); //グリッド上にランダムに生命を置く | ||
+ | void ClearCell(); //グリッド上の生命をクリアする。 | ||
+ | void goFullScreen(); //フルスクリーンモード切り替え | ||
+ | }; | ||
</pre> | </pre> | ||
+ | ;ofApp.cpp | ||
+ | <pre> | ||
+ | #include "ofApp.h" | ||
+ | |||
+ | const int WIDTH = 800; | ||
+ | const int HEIGHT = 600; | ||
+ | const int CELLSIZE = 6; | ||
+ | const int FULLSCREEN_CELLSIZE = 8; | ||
+ | const int FRAMERATE = 30; | ||
+ | |||
+ | void ofApp::setup() { | ||
+ | fullScreen = false; | ||
+ | active = false; | ||
+ | |||
+ | ofSetFullscreen(false); | ||
+ | ofSetWindowShape(WIDTH, HEIGHT); | ||
+ | ofBackground(ofColor::white); | ||
+ | ofSetBackgroundAuto(true); | ||
+ | ofSetWindowTitle("Conway's Game of Life"); | ||
+ | ofSetFrameRate(FRAMERATE); | ||
+ | |||
+ | init(WIDTH, HEIGHT, CELLSIZE); | ||
+ | } | ||
+ | |||
+ | void ofApp::init(int width, int height, int cellSize) { | ||
+ | cols = width/cellSize; | ||
+ | rows = height/cellSize; | ||
+ | |||
+ | grid = new Cell *[cols]; | ||
+ | for (int i=0; i<cols; i++) { | ||
+ | grid[i] = new Cell[rows]; | ||
+ | for (int j=0; j<rows; j++) { | ||
+ | Cell *thisCell = &grid[i][j]; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | if (width % cellSize != 0 || (height & cellSize) != 0) { | ||
+ | float ratio = width/height; | ||
+ | cellWidth = cellSize * ratio; | ||
+ | cellHeight = cellSize; | ||
+ | } else { | ||
+ | cellWidth = cellSize; | ||
+ | cellHeight = cellSize; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | void ofApp::update() { | ||
+ | if(active){ | ||
+ | // get active neighbors for each cell | ||
+ | for (int i=0; i<cols; i++) { | ||
+ | for (int j=0; j<rows; j++) { | ||
+ | grid[i][j].activeNeighbors = getNumActiveNeighbors(i, j); | ||
+ | grid[i][j].update(); | ||
+ | } | ||
+ | } | ||
+ | for (int i=0; i<cols; i++) { | ||
+ | for (int j=0; j<rows; j++) { | ||
+ | grid[i][j].currState = grid[i][j].nextState; | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | void ofApp::draw() { | ||
+ | for (int i=0; i<cols; i++) { | ||
+ | for (int j=0; j<rows; j++) { | ||
+ | grid[i][j].draw(i*cellWidth, j*cellHeight, cellWidth, cellHeight); | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | void ofApp::RandomCell() { | ||
+ | for (int i=0; i<cols; i++) { | ||
+ | for (int j=0; j<rows; j++) { | ||
+ | grid[i][j].clear(); | ||
+ | grid[i][j].currState = ofRandom(2); | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | void ofApp::ClearCell() { | ||
+ | for (int i=0; i<cols; i++) { | ||
+ | for (int j=0; j<rows; j++) { | ||
+ | grid[i][j].clear(); | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | int ofApp::getNumActiveNeighbors(int colIndex, int rowIndex) { | ||
+ | int ret = 0; | ||
+ | |||
+ | int prevCol = (colIndex - 1 + cols) % cols; | ||
+ | int nextCol = (colIndex + 1 + cols) % cols; | ||
+ | int prevRow = (rowIndex - 1 + rows) % rows; | ||
+ | int nextRow = (rowIndex + 1 + rows) % rows; | ||
+ | |||
+ | ret += grid[prevCol][prevRow].currState; | ||
+ | ret += grid[prevCol][rowIndex].currState; | ||
+ | ret += grid[prevCol][nextRow].currState; | ||
+ | |||
+ | ret += grid[colIndex][prevRow].currState; | ||
+ | ret += grid[colIndex][nextRow].currState; | ||
+ | |||
+ | ret += grid[nextCol][prevRow].currState; | ||
+ | ret += grid[nextCol][rowIndex].currState; | ||
+ | ret += grid[nextCol][nextRow].currState; | ||
+ | |||
+ | return ret; | ||
+ | } | ||
+ | |||
+ | void ofApp::goFullScreen() { | ||
+ | active = false; | ||
+ | ofToggleFullscreen(); | ||
+ | fullScreen = !fullScreen; | ||
+ | if (fullScreen) { | ||
+ | init(ofGetScreenWidth(), ofGetScreenHeight(), FULLSCREEN_CELLSIZE); | ||
+ | } else { | ||
+ | init(WIDTH, HEIGHT, CELLSIZE); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | void ofApp::pause() { | ||
+ | active = false; | ||
+ | } | ||
+ | |||
+ | void ofApp::mousePressed(int x, int y, int button) { | ||
+ | int xcell = x/cellWidth; | ||
+ | int ycell = y/cellHeight; | ||
+ | grid[xcell][ycell].currState = !grid[xcell][ycell].currState; | ||
+ | } | ||
+ | |||
+ | void ofApp::keyPressed(int key) { | ||
+ | switch (key) { | ||
+ | case ' ': | ||
+ | active = !active; | ||
+ | break; | ||
+ | case 'R': | ||
+ | RandomCell(); | ||
+ | break; | ||
+ | case 'c': | ||
+ | ClearCell(); | ||
+ | break; | ||
+ | case 'f': | ||
+ | goFullScreen(); | ||
+ | break; | ||
+ | case 'g': | ||
+ | // pause(); | ||
+ | patterns::gliderGun(grid, 2, 2); | ||
+ | break; | ||
+ | case 'p': | ||
+ | // pause(); | ||
+ | patterns::pufferTrain(grid, cols/2 - 10, rows - 30); | ||
+ | break; | ||
+ | default: | ||
+ | break; | ||
+ | } | ||
+ | } | ||
+ | </pre> | ||
+ | |||
+ | ここまでで、ライフゲームとして、成立する。 | ||
+ | |||
+ | 以下は追加の機能で典型的なパターンを定義してクラスを作る。 | ||
+ | |||
+ | === 典型的セルパターンのクラス === | ||
+ | *グライダー砲とパッファートレインのパターン定義を作る。 | ||
+ | *ofApp.hの先頭辺りにに以下を追加すること。 | ||
<pre> | <pre> | ||
+ | #include "patterns.h" | ||
</pre> | </pre> | ||
+ | ;patterns.h | ||
+ | <pre> | ||
+ | #pragma once | ||
+ | #include "ofApp.h" | ||
+ | |||
+ | class patterns { | ||
+ | public: | ||
+ | static void gliderGun(Cell **grid, int startPosX, int startPosY); | ||
+ | static void pufferTrain(Cell **grid, int startPosX, int startPosY); | ||
+ | }; | ||
+ | </pre> | ||
+ | |||
+ | patterns.cpp | ||
<pre> | <pre> | ||
+ | |||
+ | #include "patterns.h" | ||
+ | |||
+ | void patterns::gliderGun(Cell **grid, int startPosX, int startPosY) { | ||
+ | // left box | ||
+ | grid[startPosX+1][startPosY+5].currState = 1; | ||
+ | grid[startPosX+1][startPosY+6].currState = 1; | ||
+ | grid[startPosX+2][startPosY+5].currState = 1; | ||
+ | grid[startPosX+2][startPosY+6].currState = 1; | ||
+ | |||
+ | // left middle of gun | ||
+ | grid[startPosX+11][startPosY+5].currState = 1; | ||
+ | grid[startPosX+11][startPosY+6].currState = 1; | ||
+ | grid[startPosX+11][startPosY+7].currState = 1; | ||
+ | grid[startPosX+12][startPosY+4].currState = 1; | ||
+ | grid[startPosX+12][startPosY+8].currState = 1; | ||
+ | grid[startPosX+13][startPosY+3].currState = 1; | ||
+ | grid[startPosX+13][startPosY+9].currState = 1; | ||
+ | grid[startPosX+14][startPosY+3].currState = 1; | ||
+ | grid[startPosX+14][startPosY+9].currState = 1; | ||
+ | grid[startPosX+15][startPosY+6].currState = 1; | ||
+ | grid[startPosX+16][startPosY+4].currState = 1; | ||
+ | grid[startPosX+16][startPosY+8].currState = 1; | ||
+ | grid[startPosX+17][startPosY+5].currState = 1; | ||
+ | grid[startPosX+17][startPosY+6].currState = 1; | ||
+ | grid[startPosX+17][startPosY+7].currState = 1; | ||
+ | grid[startPosX+18][startPosY+6].currState = 1; | ||
+ | |||
+ | // right middle of gun | ||
+ | grid[startPosX+21][startPosY+3].currState = 1; | ||
+ | grid[startPosX+21][startPosY+4].currState = 1; | ||
+ | grid[startPosX+21][startPosY+5].currState = 1; | ||
+ | grid[startPosX+22][startPosY+3].currState = 1; | ||
+ | grid[startPosX+22][startPosY+4].currState = 1; | ||
+ | grid[startPosX+22][startPosY+5].currState = 1; | ||
+ | grid[startPosX+23][startPosY+2].currState = 1; | ||
+ | grid[startPosX+23][startPosY+6].currState = 1; | ||
+ | grid[startPosX+25][startPosY+1].currState = 1; | ||
+ | grid[startPosX+25][startPosY+2].currState = 1; | ||
+ | grid[startPosX+25][startPosY+6].currState = 1; | ||
+ | grid[startPosX+25][startPosY+7].currState = 1; | ||
+ | |||
+ | // right block | ||
+ | grid[startPosX+35][startPosY+3].currState = 1; | ||
+ | grid[startPosX+35][startPosY+4].currState = 1; | ||
+ | grid[startPosX+36][startPosY+3].currState = 1; | ||
+ | grid[startPosX+36][startPosY+4].currState = 1; | ||
+ | } | ||
+ | |||
+ | void patterns::pufferTrain(Cell **grid, int startPosX, int startPosY) { | ||
+ | grid[startPosX+1][startPosY+2].currState = 1; | ||
+ | grid[startPosX+2][startPosY+1].currState = 1; | ||
+ | grid[startPosX+3][startPosY+1].currState = 1; | ||
+ | grid[startPosX+3][startPosY+5].currState = 1; | ||
+ | grid[startPosX+4][startPosY+1].currState = 1; | ||
+ | grid[startPosX+4][startPosY+2].currState = 1; | ||
+ | grid[startPosX+4][startPosY+3].currState = 1; | ||
+ | grid[startPosX+4][startPosY+4].currState = 1; | ||
+ | |||
+ | grid[startPosX+8][startPosY+5].currState = 1; | ||
+ | grid[startPosX+9][startPosY+3].currState = 1; | ||
+ | grid[startPosX+9][startPosY+4].currState = 1; | ||
+ | grid[startPosX+10][startPosY+3].currState = 1; | ||
+ | grid[startPosX+11][startPosY+3].currState = 1; | ||
+ | grid[startPosX+12][startPosY+4].currState = 1; | ||
+ | |||
+ | grid[startPosX+15][startPosY+2].currState = 1; | ||
+ | grid[startPosX+16][startPosY+1].currState = 1; | ||
+ | grid[startPosX+17][startPosY+1].currState = 1; | ||
+ | grid[startPosX+17][startPosY+5].currState = 1; | ||
+ | grid[startPosX+18][startPosY+1].currState = 1; | ||
+ | grid[startPosX+18][startPosY+2].currState = 1; | ||
+ | grid[startPosX+18][startPosY+3].currState = 1; | ||
+ | grid[startPosX+18][startPosY+4].currState = 1; | ||
+ | } | ||
</pre> | </pre> | ||
+ | === クラスの記述を洗練させた === | ||
+ | |||
+ | ;Cell.h | ||
<pre> | <pre> | ||
+ | #pragma once | ||
+ | #include "ofMain.h" | ||
+ | |||
+ | #define NEIGHBORS 8 | ||
+ | |||
+ | class Cell { | ||
+ | public: | ||
+ | int currState; //現在の状態 | ||
+ | int nextState; //次回の状態 | ||
+ | int liveNeighbors; //近傍の生命の数 | ||
+ | ofColor color; //生きているセルの色 | ||
+ | ofColor gcolor; //グリッドの色 | ||
+ | Cell* nCell[NEIGHBORS]; //近傍のセルへのポインタ | ||
+ | |||
+ | Cell(); | ||
+ | void update(); | ||
+ | void draw(float x, float y, float w, float h); | ||
+ | void clear(); | ||
+ | int getNumLiveNeighbors(); //近傍の生きているセルの数を得る | ||
+ | |||
+ | }; | ||
</pre> | </pre> | ||
+ | |||
+ | ;Cell.cpp | ||
+ | <pre> | ||
+ | #include "Cell.h" | ||
+ | |||
+ | //初期状態の設定 | ||
+ | Cell::Cell(){ | ||
+ | currState = 0; | ||
+ | nextState = 0; | ||
+ | color = ofColor::black; // cell color | ||
+ | gcolor = ofColor(150, 150, 150); // grid color | ||
+ | } | ||
+ | |||
+ | //セルの状態変異測 | ||
+ | void Cell::update(){ | ||
+ | liveNeighbors = getNumLiveNeighbors(); //近傍の生きているセルの数を得る | ||
+ | if (currState) { | ||
+ | switch(liveNeighbors){ | ||
+ | case 0: | ||
+ | case 1: | ||
+ | nextState = 0; | ||
+ | break; | ||
+ | case 2: | ||
+ | case 3: | ||
+ | nextState = 1; | ||
+ | color = ofColor::black; // live cell color | ||
+ | break; | ||
+ | case 4: | ||
+ | case 5: | ||
+ | case 6: | ||
+ | case 7: | ||
+ | case 8: | ||
+ | nextState = 0; | ||
+ | break; | ||
+ | } | ||
+ | } | ||
+ | else{ | ||
+ | switch(liveNeighbors){ | ||
+ | case 3: | ||
+ | nextState = 1; | ||
+ | color = ofColor::green; // born cell color | ||
+ | break; | ||
+ | default: | ||
+ | nextState = false; | ||
+ | break; | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | //近傍の生きているセルの数を得る | ||
+ | int Cell::getNumLiveNeighbors() { | ||
+ | int ret = 0; | ||
+ | |||
+ | for(int i=0; i<8;i++){ | ||
+ | ret += nCell[i]->currState; | ||
+ | } | ||
+ | return ret; | ||
+ | } | ||
+ | |||
+ | //セルを描画 | ||
+ | void Cell::draw(float x, float y, float w, float h){ | ||
+ | ofSetColor(gcolor); | ||
+ | ofNoFill(); | ||
+ | ofRect(x, y, w, h); | ||
+ | if (currState) { | ||
+ | ofSetColor(color); | ||
+ | ofFill(); | ||
+ | ofRect(x, y, w, h); | ||
+ | ofNoFill(); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | void Cell::clear() { | ||
+ | currState = 0; | ||
+ | nextState = 0; | ||
+ | color = ofColor::black; | ||
+ | } | ||
+ | </pre> | ||
+ | |||
+ | ;ofApp.h | ||
+ | <pre> | ||
+ | #pragma once | ||
+ | |||
+ | #include "ofMain.h" | ||
+ | #include "Cell.h" | ||
+ | #include "patterns.h" | ||
+ | |||
+ | class ofApp : public ofBaseApp { | ||
+ | |||
+ | public: | ||
+ | void setup(); | ||
+ | void update(); | ||
+ | void draw(); | ||
+ | |||
+ | void keyPressed(int key); | ||
+ | void mousePressed(int x, int y, int button); | ||
+ | |||
+ | void keyReleased(int key); | ||
+ | void mouseMoved(int x, int y ); | ||
+ | void mouseDragged(int x, int y, int button); | ||
+ | void mouseReleased(int x, int y, int button); | ||
+ | void windowResized(int w, int h); | ||
+ | void dragEvent(ofDragInfo dragInfo); | ||
+ | void gotMessage(ofMessage msg); | ||
+ | |||
+ | //ライフゲームのセルに関する宣言 | ||
+ | Cell **grid; | ||
+ | int cols, rows; //セルの個数。縦と横の2次元配列 | ||
+ | float cellWidth, cellHeight; //グリッドの幅と高さのピクセル数 | ||
+ | bool fullScreen; // フルスクリーンモードにするか否か | ||
+ | bool highlight; // 新しく誕生したセルをハイライトするか否か | ||
+ | bool active; // オートマトンの進行のオン・オフ | ||
+ | |||
+ | void init(int width, int height, int cellsize); //グリッドの初期化 | ||
+ | void pause(); //オートマトンの進行を止める | ||
+ | void RandomCell(); //グリッド上にランダムに生命を置く | ||
+ | void ClearCell(); //グリッド上の生命をクリアする。 | ||
+ | void goFullScreen();//フルスクリーンモード切り替え | ||
+ | }; | ||
+ | </pre> | ||
+ | |||
+ | ;ofApp.cpp | ||
+ | <pre> | ||
+ | #include "ofApp.h" | ||
+ | |||
+ | const int WIDTH = 800; | ||
+ | const int HEIGHT = 600; | ||
+ | const int CELLSIZE = 6; | ||
+ | const int FULLSCREEN_CELLSIZE = 8; | ||
+ | const int FRAMERATE = 30; | ||
+ | |||
+ | void ofApp::setup() { | ||
+ | fullScreen = false; | ||
+ | active = false; | ||
+ | |||
+ | ofSetFullscreen(false); | ||
+ | ofSetWindowShape(WIDTH, HEIGHT); | ||
+ | ofBackground(ofColor::white); | ||
+ | ofSetBackgroundAuto(true); | ||
+ | ofSetWindowTitle("Conway's Game of Life"); | ||
+ | ofSetFrameRate(FRAMERATE); | ||
+ | |||
+ | init(WIDTH, HEIGHT, CELLSIZE); //ライフゲームの初期化 | ||
+ | } | ||
+ | |||
+ | void ofApp::init(int width, int height, int cellSize) { | ||
+ | cols = width/cellSize; | ||
+ | rows = height/cellSize; | ||
+ | |||
+ | grid = new Cell *[cols]; | ||
+ | for (int i=0; i<cols; i++) { | ||
+ | grid[i] = new Cell[rows]; | ||
+ | } | ||
+ | // 自分の8近傍のセルのポインタをつける | ||
+ | // 剰余を使って、上下左右の通過を定義 | ||
+ | for (int i=0; i<cols; i++) { | ||
+ | for (int j=0; j<rows; j++) { | ||
+ | grid[i][j].nCell[0] = &(grid[(i-1 + cols)%cols][(j-1 + rows)%rows]); | ||
+ | grid[i][j].nCell[1] = &(grid[(i + cols)%cols][(j-1 + rows)%rows]); | ||
+ | grid[i][j].nCell[2] = &(grid[(i+1 + cols)%cols][(j-1 + rows)%rows]); | ||
+ | grid[i][j].nCell[3] = &(grid[(i-1 + cols)%cols][(j + rows)%rows]); | ||
+ | grid[i][j].nCell[4] = &(grid[(i+1 + cols)%cols][(j + rows)%rows]); | ||
+ | grid[i][j].nCell[5] = &(grid[(i-1 + cols)%cols][(j+1 + rows)%rows]); | ||
+ | grid[i][j].nCell[6] = &(grid[(i + cols)%cols][(j+1 + rows)%rows]); | ||
+ | grid[i][j].nCell[7] = &(grid[(i+1 + cols)%cols][(j+1 + rows)%rows]); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | if (width % cellSize != 0 || (height & cellSize) != 0) { | ||
+ | float ratio = width/height; | ||
+ | cellWidth = cellSize * ratio; | ||
+ | cellHeight = cellSize; | ||
+ | } else { | ||
+ | cellWidth = cellSize; | ||
+ | cellHeight = cellSize; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | void ofApp::update() { | ||
+ | if(active){ | ||
+ | // セルの現在の状態を調べる | ||
+ | for (int i=0; i<cols; i++) { | ||
+ | for (int j=0; j<rows; j++) { | ||
+ | grid[i][j].update(); | ||
+ | } | ||
+ | } | ||
+ | // セルの状態変数の更新 | ||
+ | for (int i=0; i<cols; i++) { | ||
+ | for (int j=0; j<rows; j++) { | ||
+ | grid[i][j].currState = grid[i][j].nextState; | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | // | ||
+ | void ofApp::draw() { | ||
+ | for (int i=0; i<cols; i++) { | ||
+ | for (int j=0; j<rows; j++) { | ||
+ | grid[i][j].draw(i*cellWidth, j*cellHeight, cellWidth, cellHeight); | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | void ofApp::RandomCell() { | ||
+ | for (int i=0; i<cols; i++) { | ||
+ | for (int j=0; j<rows; j++) { | ||
+ | grid[i][j].clear(); | ||
+ | grid[i][j].currState = ofRandom(2); | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | void ofApp::ClearCell() { | ||
+ | for (int i=0; i<cols; i++) { | ||
+ | for (int j=0; j<rows; j++) { | ||
+ | grid[i][j].clear(); | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | void ofApp::goFullScreen() { | ||
+ | active = false; | ||
+ | ofToggleFullscreen(); | ||
+ | fullScreen = !fullScreen; | ||
+ | delete grid; | ||
+ | if (fullScreen) { | ||
+ | init(ofGetScreenWidth(), ofGetScreenHeight(), FULLSCREEN_CELLSIZE); | ||
+ | } else { | ||
+ | init(WIDTH, HEIGHT, CELLSIZE); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | void ofApp::pause() { | ||
+ | active = false; | ||
+ | } | ||
+ | |||
+ | void ofApp::mousePressed(int x, int y, int button) { | ||
+ | int xcell = x/cellWidth; | ||
+ | int ycell = y/cellHeight; | ||
+ | grid[xcell][ycell].currState = !grid[xcell][ycell].currState; | ||
+ | } | ||
+ | |||
+ | void ofApp::keyPressed(int key) { | ||
+ | switch (key) { | ||
+ | case ' ': | ||
+ | active = !active; | ||
+ | break; | ||
+ | case 'R': | ||
+ | RandomCell(); | ||
+ | break; | ||
+ | case 'c': | ||
+ | ClearCell(); | ||
+ | break; | ||
+ | case 'f': | ||
+ | goFullScreen(); | ||
+ | break; | ||
+ | case 'g': | ||
+ | patterns::gliderGun(grid, 2, 2); | ||
+ | break; | ||
+ | case 'p': | ||
+ | patterns::pufferTrain(grid, cols/2 - 10, rows - 30); | ||
+ | break; | ||
+ | default: | ||
+ | break; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | //-------------------------------------------------------------- | ||
+ | void ofApp::keyReleased(int key){ | ||
+ | |||
+ | } | ||
+ | //-------------------------------------------------------------- | ||
+ | void ofApp::mouseMoved(int x, int y ){ | ||
+ | |||
+ | } | ||
+ | //-------------------------------------------------------------- | ||
+ | void ofApp::mouseDragged(int x, int y, int button){ | ||
+ | |||
+ | } | ||
+ | //-------------------------------------------------------------- | ||
+ | void ofApp::mouseReleased(int x, int y, int button){ | ||
+ | |||
+ | } | ||
+ | //-------------------------------------------------------------- | ||
+ | void ofApp::windowResized(int w, int h){ | ||
+ | |||
+ | } | ||
+ | //-------------------------------------------------------------- | ||
+ | void ofApp::gotMessage(ofMessage msg){ | ||
+ | |||
+ | } | ||
+ | //-------------------------------------------------------------- | ||
+ | void ofApp::dragEvent(ofDragInfo dragInfo){ | ||
+ | |||
+ | } | ||
+ | </pre> | ||
+ | |||
+ | == 参考 == | ||
+ | [[ジェネラティブアート論]] | ||
+ | |||
+ | [[Category:授業]] |
2020年10月31日 (土) 00:21時点における最新版
目次 |
[編集] openFrameworksのサンプル
[編集] Cellクラスを作る
- Cell.h
#pragma once #include "ofMain.h" class Cell { public: int currState; //現在の状態 int nextState; //次回の状態 int activeNeighbors; //近傍の生命の数 ofColor color; //セルの色 ofColor gcolor; //グリッドの色 Cell(); void update(); void draw(float x, float y, float w, float h); void clear(); };
- Cell.cpp
#include "Cell.h" //初期状態の設定 Cell::Cell(){ currState = 0; nextState = 0; color = ofColor::black; // cell color gcolor = ofColor(150, 150, 150); // grid color } //セルの状態変異測 void Cell::update(){ if (currState) { switch(activeNeighbors){ case 0: case 1: nextState = 0; break; case 2: case 3: nextState = 1; color = ofColor::black; // live cell color break; case 4: case 5: case 6: case 7: case 8: nextState = 0; break; } } else{ switch(activeNeighbors){ case 3: nextState = 1; color = ofColor::green; // born cell color break; default: nextState = 0; break; } } } //セルを描画 void Cell::draw(float x, float y, float w, float h){ ofSetColor(gcolor); ofNoFill(); ofRect(x, y, w, h); if (currState) { ofSetColor(color); ofFill(); ofRect(x, y, w, h); ofNoFill(); } } void Cell::clear() { currState = 0; nextState = 0; color = ofColor::black; }
[編集] 主プログラム
- ofApp.h
#pragma once #include "ofMain.h" #include "Cell.h" class ofApp : public ofBaseApp { public: void setup(); void update(); void draw(); void keyPressed(int key); void mousePressed(int x, int y, int button); void keyReleased(int key); void mouseMoved(int x, int y ); void mouseDragged(int x, int y, int button); void mouseReleased(int x, int y, int button); void windowResized(int w, int h); void dragEvent(ofDragInfo dragInfo); void gotMessage(ofMessage msg); //ライフゲームのセルに関する定義 Cell **grid; int rows, cols; //セルの個数。縦と横の2次元配列 float cellWidth, cellHeight; //グリッドの幅と高さのピクセル数 bool fullScreen; // フルスクリーンモードの切り替え bool active; // オートマトンの進行のオン・オフ void init(int width, int height, int cellsize); //グリッドの初期化 void pause(); //オートマトンの進行を止める int getNumActiveNeighbors(int colIndex, int rowIndex); //近傍の生命の数をカウントする void RandomCell(); //グリッド上にランダムに生命を置く void ClearCell(); //グリッド上の生命をクリアする。 void goFullScreen(); //フルスクリーンモード切り替え };
- ofApp.cpp
#include "ofApp.h" const int WIDTH = 800; const int HEIGHT = 600; const int CELLSIZE = 6; const int FULLSCREEN_CELLSIZE = 8; const int FRAMERATE = 30; void ofApp::setup() { fullScreen = false; active = false; ofSetFullscreen(false); ofSetWindowShape(WIDTH, HEIGHT); ofBackground(ofColor::white); ofSetBackgroundAuto(true); ofSetWindowTitle("Conway's Game of Life"); ofSetFrameRate(FRAMERATE); init(WIDTH, HEIGHT, CELLSIZE); } void ofApp::init(int width, int height, int cellSize) { cols = width/cellSize; rows = height/cellSize; grid = new Cell *[cols]; for (int i=0; i<cols; i++) { grid[i] = new Cell[rows]; for (int j=0; j<rows; j++) { Cell *thisCell = &grid[i][j]; } } if (width % cellSize != 0 || (height & cellSize) != 0) { float ratio = width/height; cellWidth = cellSize * ratio; cellHeight = cellSize; } else { cellWidth = cellSize; cellHeight = cellSize; } } void ofApp::update() { if(active){ // get active neighbors for each cell for (int i=0; i<cols; i++) { for (int j=0; j<rows; j++) { grid[i][j].activeNeighbors = getNumActiveNeighbors(i, j); grid[i][j].update(); } } for (int i=0; i<cols; i++) { for (int j=0; j<rows; j++) { grid[i][j].currState = grid[i][j].nextState; } } } } void ofApp::draw() { for (int i=0; i<cols; i++) { for (int j=0; j<rows; j++) { grid[i][j].draw(i*cellWidth, j*cellHeight, cellWidth, cellHeight); } } } void ofApp::RandomCell() { for (int i=0; i<cols; i++) { for (int j=0; j<rows; j++) { grid[i][j].clear(); grid[i][j].currState = ofRandom(2); } } } void ofApp::ClearCell() { for (int i=0; i<cols; i++) { for (int j=0; j<rows; j++) { grid[i][j].clear(); } } } int ofApp::getNumActiveNeighbors(int colIndex, int rowIndex) { int ret = 0; int prevCol = (colIndex - 1 + cols) % cols; int nextCol = (colIndex + 1 + cols) % cols; int prevRow = (rowIndex - 1 + rows) % rows; int nextRow = (rowIndex + 1 + rows) % rows; ret += grid[prevCol][prevRow].currState; ret += grid[prevCol][rowIndex].currState; ret += grid[prevCol][nextRow].currState; ret += grid[colIndex][prevRow].currState; ret += grid[colIndex][nextRow].currState; ret += grid[nextCol][prevRow].currState; ret += grid[nextCol][rowIndex].currState; ret += grid[nextCol][nextRow].currState; return ret; } void ofApp::goFullScreen() { active = false; ofToggleFullscreen(); fullScreen = !fullScreen; if (fullScreen) { init(ofGetScreenWidth(), ofGetScreenHeight(), FULLSCREEN_CELLSIZE); } else { init(WIDTH, HEIGHT, CELLSIZE); } } void ofApp::pause() { active = false; } void ofApp::mousePressed(int x, int y, int button) { int xcell = x/cellWidth; int ycell = y/cellHeight; grid[xcell][ycell].currState = !grid[xcell][ycell].currState; } void ofApp::keyPressed(int key) { switch (key) { case ' ': active = !active; break; case 'R': RandomCell(); break; case 'c': ClearCell(); break; case 'f': goFullScreen(); break; case 'g': // pause(); patterns::gliderGun(grid, 2, 2); break; case 'p': // pause(); patterns::pufferTrain(grid, cols/2 - 10, rows - 30); break; default: break; } }
ここまでで、ライフゲームとして、成立する。
以下は追加の機能で典型的なパターンを定義してクラスを作る。
[編集] 典型的セルパターンのクラス
- グライダー砲とパッファートレインのパターン定義を作る。
- ofApp.hの先頭辺りにに以下を追加すること。
#include "patterns.h"
- patterns.h
#pragma once #include "ofApp.h" class patterns { public: static void gliderGun(Cell **grid, int startPosX, int startPosY); static void pufferTrain(Cell **grid, int startPosX, int startPosY); };
patterns.cpp
#include "patterns.h" void patterns::gliderGun(Cell **grid, int startPosX, int startPosY) { // left box grid[startPosX+1][startPosY+5].currState = 1; grid[startPosX+1][startPosY+6].currState = 1; grid[startPosX+2][startPosY+5].currState = 1; grid[startPosX+2][startPosY+6].currState = 1; // left middle of gun grid[startPosX+11][startPosY+5].currState = 1; grid[startPosX+11][startPosY+6].currState = 1; grid[startPosX+11][startPosY+7].currState = 1; grid[startPosX+12][startPosY+4].currState = 1; grid[startPosX+12][startPosY+8].currState = 1; grid[startPosX+13][startPosY+3].currState = 1; grid[startPosX+13][startPosY+9].currState = 1; grid[startPosX+14][startPosY+3].currState = 1; grid[startPosX+14][startPosY+9].currState = 1; grid[startPosX+15][startPosY+6].currState = 1; grid[startPosX+16][startPosY+4].currState = 1; grid[startPosX+16][startPosY+8].currState = 1; grid[startPosX+17][startPosY+5].currState = 1; grid[startPosX+17][startPosY+6].currState = 1; grid[startPosX+17][startPosY+7].currState = 1; grid[startPosX+18][startPosY+6].currState = 1; // right middle of gun grid[startPosX+21][startPosY+3].currState = 1; grid[startPosX+21][startPosY+4].currState = 1; grid[startPosX+21][startPosY+5].currState = 1; grid[startPosX+22][startPosY+3].currState = 1; grid[startPosX+22][startPosY+4].currState = 1; grid[startPosX+22][startPosY+5].currState = 1; grid[startPosX+23][startPosY+2].currState = 1; grid[startPosX+23][startPosY+6].currState = 1; grid[startPosX+25][startPosY+1].currState = 1; grid[startPosX+25][startPosY+2].currState = 1; grid[startPosX+25][startPosY+6].currState = 1; grid[startPosX+25][startPosY+7].currState = 1; // right block grid[startPosX+35][startPosY+3].currState = 1; grid[startPosX+35][startPosY+4].currState = 1; grid[startPosX+36][startPosY+3].currState = 1; grid[startPosX+36][startPosY+4].currState = 1; } void patterns::pufferTrain(Cell **grid, int startPosX, int startPosY) { grid[startPosX+1][startPosY+2].currState = 1; grid[startPosX+2][startPosY+1].currState = 1; grid[startPosX+3][startPosY+1].currState = 1; grid[startPosX+3][startPosY+5].currState = 1; grid[startPosX+4][startPosY+1].currState = 1; grid[startPosX+4][startPosY+2].currState = 1; grid[startPosX+4][startPosY+3].currState = 1; grid[startPosX+4][startPosY+4].currState = 1; grid[startPosX+8][startPosY+5].currState = 1; grid[startPosX+9][startPosY+3].currState = 1; grid[startPosX+9][startPosY+4].currState = 1; grid[startPosX+10][startPosY+3].currState = 1; grid[startPosX+11][startPosY+3].currState = 1; grid[startPosX+12][startPosY+4].currState = 1; grid[startPosX+15][startPosY+2].currState = 1; grid[startPosX+16][startPosY+1].currState = 1; grid[startPosX+17][startPosY+1].currState = 1; grid[startPosX+17][startPosY+5].currState = 1; grid[startPosX+18][startPosY+1].currState = 1; grid[startPosX+18][startPosY+2].currState = 1; grid[startPosX+18][startPosY+3].currState = 1; grid[startPosX+18][startPosY+4].currState = 1; }
[編集] クラスの記述を洗練させた
- Cell.h
#pragma once #include "ofMain.h" #define NEIGHBORS 8 class Cell { public: int currState; //現在の状態 int nextState; //次回の状態 int liveNeighbors; //近傍の生命の数 ofColor color; //生きているセルの色 ofColor gcolor; //グリッドの色 Cell* nCell[NEIGHBORS]; //近傍のセルへのポインタ Cell(); void update(); void draw(float x, float y, float w, float h); void clear(); int getNumLiveNeighbors(); //近傍の生きているセルの数を得る };
- Cell.cpp
#include "Cell.h" //初期状態の設定 Cell::Cell(){ currState = 0; nextState = 0; color = ofColor::black; // cell color gcolor = ofColor(150, 150, 150); // grid color } //セルの状態変異測 void Cell::update(){ liveNeighbors = getNumLiveNeighbors(); //近傍の生きているセルの数を得る if (currState) { switch(liveNeighbors){ case 0: case 1: nextState = 0; break; case 2: case 3: nextState = 1; color = ofColor::black; // live cell color break; case 4: case 5: case 6: case 7: case 8: nextState = 0; break; } } else{ switch(liveNeighbors){ case 3: nextState = 1; color = ofColor::green; // born cell color break; default: nextState = false; break; } } } //近傍の生きているセルの数を得る int Cell::getNumLiveNeighbors() { int ret = 0; for(int i=0; i<8;i++){ ret += nCell[i]->currState; } return ret; } //セルを描画 void Cell::draw(float x, float y, float w, float h){ ofSetColor(gcolor); ofNoFill(); ofRect(x, y, w, h); if (currState) { ofSetColor(color); ofFill(); ofRect(x, y, w, h); ofNoFill(); } } void Cell::clear() { currState = 0; nextState = 0; color = ofColor::black; }
- ofApp.h
#pragma once #include "ofMain.h" #include "Cell.h" #include "patterns.h" class ofApp : public ofBaseApp { public: void setup(); void update(); void draw(); void keyPressed(int key); void mousePressed(int x, int y, int button); void keyReleased(int key); void mouseMoved(int x, int y ); void mouseDragged(int x, int y, int button); void mouseReleased(int x, int y, int button); void windowResized(int w, int h); void dragEvent(ofDragInfo dragInfo); void gotMessage(ofMessage msg); //ライフゲームのセルに関する宣言 Cell **grid; int cols, rows; //セルの個数。縦と横の2次元配列 float cellWidth, cellHeight; //グリッドの幅と高さのピクセル数 bool fullScreen; // フルスクリーンモードにするか否か bool highlight; // 新しく誕生したセルをハイライトするか否か bool active; // オートマトンの進行のオン・オフ void init(int width, int height, int cellsize); //グリッドの初期化 void pause(); //オートマトンの進行を止める void RandomCell(); //グリッド上にランダムに生命を置く void ClearCell(); //グリッド上の生命をクリアする。 void goFullScreen();//フルスクリーンモード切り替え };
- ofApp.cpp
#include "ofApp.h" const int WIDTH = 800; const int HEIGHT = 600; const int CELLSIZE = 6; const int FULLSCREEN_CELLSIZE = 8; const int FRAMERATE = 30; void ofApp::setup() { fullScreen = false; active = false; ofSetFullscreen(false); ofSetWindowShape(WIDTH, HEIGHT); ofBackground(ofColor::white); ofSetBackgroundAuto(true); ofSetWindowTitle("Conway's Game of Life"); ofSetFrameRate(FRAMERATE); init(WIDTH, HEIGHT, CELLSIZE); //ライフゲームの初期化 } void ofApp::init(int width, int height, int cellSize) { cols = width/cellSize; rows = height/cellSize; grid = new Cell *[cols]; for (int i=0; i<cols; i++) { grid[i] = new Cell[rows]; } // 自分の8近傍のセルのポインタをつける // 剰余を使って、上下左右の通過を定義 for (int i=0; i<cols; i++) { for (int j=0; j<rows; j++) { grid[i][j].nCell[0] = &(grid[(i-1 + cols)%cols][(j-1 + rows)%rows]); grid[i][j].nCell[1] = &(grid[(i + cols)%cols][(j-1 + rows)%rows]); grid[i][j].nCell[2] = &(grid[(i+1 + cols)%cols][(j-1 + rows)%rows]); grid[i][j].nCell[3] = &(grid[(i-1 + cols)%cols][(j + rows)%rows]); grid[i][j].nCell[4] = &(grid[(i+1 + cols)%cols][(j + rows)%rows]); grid[i][j].nCell[5] = &(grid[(i-1 + cols)%cols][(j+1 + rows)%rows]); grid[i][j].nCell[6] = &(grid[(i + cols)%cols][(j+1 + rows)%rows]); grid[i][j].nCell[7] = &(grid[(i+1 + cols)%cols][(j+1 + rows)%rows]); } } if (width % cellSize != 0 || (height & cellSize) != 0) { float ratio = width/height; cellWidth = cellSize * ratio; cellHeight = cellSize; } else { cellWidth = cellSize; cellHeight = cellSize; } } void ofApp::update() { if(active){ // セルの現在の状態を調べる for (int i=0; i<cols; i++) { for (int j=0; j<rows; j++) { grid[i][j].update(); } } // セルの状態変数の更新 for (int i=0; i<cols; i++) { for (int j=0; j<rows; j++) { grid[i][j].currState = grid[i][j].nextState; } } } } // void ofApp::draw() { for (int i=0; i<cols; i++) { for (int j=0; j<rows; j++) { grid[i][j].draw(i*cellWidth, j*cellHeight, cellWidth, cellHeight); } } } void ofApp::RandomCell() { for (int i=0; i<cols; i++) { for (int j=0; j<rows; j++) { grid[i][j].clear(); grid[i][j].currState = ofRandom(2); } } } void ofApp::ClearCell() { for (int i=0; i<cols; i++) { for (int j=0; j<rows; j++) { grid[i][j].clear(); } } } void ofApp::goFullScreen() { active = false; ofToggleFullscreen(); fullScreen = !fullScreen; delete grid; if (fullScreen) { init(ofGetScreenWidth(), ofGetScreenHeight(), FULLSCREEN_CELLSIZE); } else { init(WIDTH, HEIGHT, CELLSIZE); } } void ofApp::pause() { active = false; } void ofApp::mousePressed(int x, int y, int button) { int xcell = x/cellWidth; int ycell = y/cellHeight; grid[xcell][ycell].currState = !grid[xcell][ycell].currState; } void ofApp::keyPressed(int key) { switch (key) { case ' ': active = !active; break; case 'R': RandomCell(); break; case 'c': ClearCell(); break; case 'f': goFullScreen(); break; case 'g': patterns::gliderGun(grid, 2, 2); break; case 'p': patterns::pufferTrain(grid, cols/2 - 10, rows - 30); break; default: break; } } //-------------------------------------------------------------- void ofApp::keyReleased(int key){ } //-------------------------------------------------------------- void ofApp::mouseMoved(int x, int y ){ } //-------------------------------------------------------------- void ofApp::mouseDragged(int x, int y, int button){ } //-------------------------------------------------------------- void ofApp::mouseReleased(int x, int y, int button){ } //-------------------------------------------------------------- void ofApp::windowResized(int w, int h){ } //-------------------------------------------------------------- void ofApp::gotMessage(ofMessage msg){ } //-------------------------------------------------------------- void ofApp::dragEvent(ofDragInfo dragInfo){ }