Upload
atsushi-tadokoro
View
12.115
Download
2
Embed Size (px)
DESCRIPTION
Citation preview
グラフィックスプログラミング演習B第4回:openFrameworks基礎たくさんの図形を動かす静的配列と動的配列
2011年10月27日東京藝術大学 芸術情報センター担当:田所淳
今日の内容‣ 先週やったこと:動きを生みだすしくみ‣ 単体の図形をアニメーション
‣ 今週は、複数の物体を同時に動かす方法について‣ 動きのパラメータ(位置、速度)を保存する方法に工夫が必要‣ → 配列
‣ 配列について‣ 静的配列 (Array) と動的配列 (Vector)
先週の復習:単体の物体を動かす
先週の復習:単体の物体を動かす‣ プログラムによるアニメーションの基本原理‣ 値をすこしずつ更新しながら、形を描く‣ 位置、大きさ、角度、色、透明度... etc.
先週の復習:単体の物体を動かす‣ 直線的に移動するパーティクル(粒)を作るには?‣ パーティクルの現在位置(position)と速度(velocity)がパラメータとして必要
‣ それぞれ、x軸方向とy軸方向の値を持つ
現在位置(x, y)
次のフレームの位置(x, y)
速度(x, y)
先週の復習:単体の物体を動かす‣ コーディングしてみる (testApp.h)#pragma once
#include "ofMain.h"
class testApp : public ofBaseApp{ public: void setup(); void update(); void draw(); ofVec2f pos; //位置を記録するための変数(正確にはオブジェクト)};
先週の復習:単体の物体を動かす‣ コーディングしてみる (testApp.cpp)#include "testApp.h"
void testApp::setup(){ ofSetFrameRate(60); //更新を秒間60コマに(fps) ofBackgroundHex(0x000000); //背景色を黒に pos.x = ofGetWidth()/2; //初期位置を画面の中心に:x pos.y = ofGetHeight()/2; //初期位置を画面の中心に:y}
void testApp::update(){ pos.x += 4.0; //位置を更新:x pos.y += 3.0; //位置を更新:y //画面の端まできたら、反対方向から出現 if (pos.x > ofGetWidth()) {pos.x = 0;} if (pos.x < 0) {pos.x = ofGetWidth();} if (pos.y > ofGetHeight()) {pos.y = 0;} if (pos.y < 0) {pos.y = ofGetHeight();}}
void testApp::draw(){ ofSetHexColor(0x3399ff); //描画する色を指定 ofCircle(pos.x, pos.y, 20); //更新された値を利用して円を描く}
先週の復習:単体の物体を動かす‣ 実行結果:単体の物体が直線運動
速度をランダムに決定する
速度をランダムに決定する‣ プログラムを少し改良‣ 初期速度がランダムに変化するようにしてみる‣ testApp::setup() で速度を決定する‣ 速度のための変数を新たに設ける‣ 位置(pos)と速度(vel)の2つの箱ができる
位置:posofVec2f(x, y)
速度:velofVec2f(x, y)
速度をランダムに決定する‣ コーディングしてみる (testApp.h)#pragma once
#include "ofMain.h"
class testApp : public ofBaseApp{ public: void setup(); void update(); void draw(); ofVec2f pos; //位置を記録するための変数(正確にはオブジェクト) ofVec2f vel; //パーティクルの速度(Velocity)};
速度をランダムに決定する‣ コーディングしてみる (testApp.h)#include "testApp.h"
void testApp::setup(){ ofSetFrameRate(60); //更新を秒間60コマに(fps) ofBackgroundHex(0x000000); //背景色を黒に
//初期位置を画面の中心に pos.x = ofGetWidth()/2; pos.y = ofGetHeight()/2;
//速度をランダムに決定 vel.x = ofRandom(-10, 10); vel.y = ofRandom(-10, 10);}
void testApp::update(){ //位置を更新する pos += vel;
//画面の端に来たら反対側へ if (pos.x > ofGetWidth()) pos.x = 0; //右 if (pos.x < 0) pos.x = ofGetWidth(); //左 if (pos.y > ofGetHeight()) pos.y = 0; //下 if (pos.y < 0) pos.y = ofGetHeight(); //上}
速度をランダムに決定する‣ コーディングしてみる (testApp.h)
void testApp::draw(){ ofSetHexColor(0x3399ff); //描画する色を指定 ofCircle(pos.x, pos.y, 4); //更新された値を利用して円を描く}
速度をランダムに決定する‣ 実行結果:起動するたびに別の方向(速度)に
たくさんの図形を動かす:配列
たくさんの図形を動かす:配列‣ 複数のパーティクルを同時に動かすようにする‣ パーティクルのオブジェクトを格納するやりかたに工夫が必要‣ 複数のオブジェクトを格納するには、配列を利用‣ 配列 = 値を格納するロッカーのようなもの
pos[0]
pos[1]
pos[2]...
配列(位置)
NUM個
たくさんの図形を動かす:配列‣ 位置(pos)の配列と、速度(vel)の配列を用意する
pos[0]
pos[1]
pos[2]...
位置(pos)
vel[0]
vel[1]
vel[2]...
速度(vel)
たくさんの図形を動かす:配列‣ 配列の宣言:変数名に [ 要素の数 ] を付加して宣言する‣ 例:100個分の位置と速度を記録する配列を宣言
‣ 配列の要素にアクセス‣ 配列の番号を [番号] で指定 (0から始まることに注意)
ofVec2f pos[100]; //位置の配列ofVec2f vel[100]; //パーティクルの速度(Velocity)
pos[0], pos[1], pos[2], pos[3] ...
たくさんの図形を動かす:配列‣ 大量の配列の要素をまとめて処理するには?‣ くりかえしのための構文「for文」を使用すると便利
‣ 例えば、カウンタ変数を i にして100回くりかえす場合
for (【初期化】;【ループの継続条件】;【カウンタ変数の更新】;) {
【くりかえし実行する処理】
}
for (int i = 0; i < 100; i++) {
【くりかえし実行する処理】
}
たくさんの図形を動かす:配列‣ for文と配列のくみあわせの例‣ 例:velの配列(ofVec2f)全ての要素に、ランダムに数値を指定する
‣ これと同じ要領で、単体の物体のときに位置と速度に対しておこなった処理をすべて、for文と配列で書きなおす
for (int i = 0; i < 100; i++) { //100回くりかえし
//速度をランダムに決定 vel[i].x = ofRandom(-10, 10); vel[i].y = ofRandom(-10, 10);
}
たくさんの図形を動かす:配列‣ コーディングしてみる (testApp.h)#pragma once#include "ofMain.h"#define NUM 100
class testApp : public ofBaseApp{ public: void setup(); void update(); void draw(); ofVec2f pos[NUM]; //位置の配列 ofVec2f vel[NUM]; //パーティクルの速度(Velocity)};
たくさんの図形を動かす:配列‣ コーディングしてみる (testApp.cpp)#include "testApp.h"
void testApp::setup(){ ofSetFrameRate(60); //更新を秒間60コマに(fps) ofBackgroundHex(0x000000); //背景色を黒に for (int i = 0; i < NUM; i++) { //NUM回くりかえし //初期位置を画面の中心に pos[i].x = ofGetWidth()/2; pos[i].y = ofGetHeight()/2; //速度をランダムに決定 vel[i].x = ofRandom(-10, 10); vel[i].y = ofRandom(-10, 10); }}
void testApp::update(){ for (int i = 0; i < NUM; i++) { //NUM回くりかえし //位置を更新する pos[i] += vel[i]; //画面の端に来たら反対側へ if (pos[i].x > ofGetWidth()) pos[i].x = 0; //右 if (pos[i].x < 0) pos[i].x = ofGetWidth(); //左 if (pos[i].y > ofGetHeight()) pos[i].y = 0; //下 if (pos[i].y < 0) pos[i].y = ofGetHeight(); //上 }}
たくさんの図形を動かす:配列‣ コーディングしてみる (testApp.cpp)
void testApp::draw(){ ofSetHexColor(0x3399ff); //描画する色を指定 for (int i = 0; i < NUM; i++) { //NUM回くりかえし ofCircle(pos[i].x, pos[i].y, 4); //更新された値を利用して円を描く }}
たくさんの図形を動かす:配列‣ 実行結果:複数の図形が同時に動く
静的(固定長)配列と、動的(可変長)配列
静的(固定長)配列と、動的(可変長)配列‣ このプログラムを改良して、マウスを画面上でドラッグすると次々とパーティクルが増殖していくようにしたい
‣ 問題点:パーティクルの最大数がわからない!‣ 配列にあらかじめ大量の領域を確保しておく(99999個など)‣ あまり良い方法ではない…
‣ 配列の最大数がわからない場合には、動的配列(可変長配列)を使用すると便利
静的(固定長)配列と、動的(可変長)配列‣ 要素の数が固定された配列と、必要に応じて自由に長さをかえることのできる配列がある
‣ 静的配列:Array - 配列の数をあらかじめ指定する必要‣ 例:Particleクラスのオブジェクトpを100個格納
‣ 動的配列列:Vector - 配列の数を指定する必要はない‣ 例:Particleクラスのオブジェクトpを動的配列に
Particle p[100];
vector <Particle> p;
静的(固定長)配列と、動的(可変長)配列‣ Vectorの配列要素の操作
‣ 配列の末尾に要素を追加 → push_back()‣ 例:Particleのオブジェクトpを、particlesに追加
‣ 配列の末尾に要素を削除 → pop_back()
‣ 配列の全ての要素をクリア → clear()
particles.push_back(p);
particles.clear();
particles.pop_back();
静的(固定長)配列と、動的(可変長)配列‣ 先程、静的配列 (Array) で作成したプログラムを、動的配列(Vector)で書き直していく
‣ マウスをドラッグしたら、配列の末尾にパーティクルをひとつ追加する
静的(固定長)配列と、動的(可変長)配列‣ コーディングしてみる (testApp.h)#pragma once#include "ofMain.h"
class testApp : public ofBaseApp{ public: void setup(); void update(); void draw(); void mouseDragged(int x, int y, int button); void keyReleased(int key); vector<ofVec2f> pos; //位置の可変配列 vector<ofVec2f> vel; //パーティクルの速度の可変配列};
静的(固定長)配列と、動的(可変長)配列‣ コーディングしてみる (testApp.cpp)#include "testApp.h"
void testApp::setup(){ ofSetFrameRate(60); //更新を秒間60コマに(fps) ofEnableBlendMode(OF_BLENDMODE_ADD); //色を加算合成に ofBackgroundHex(0x000000); //背景色を黒に}
void testApp::update(){ for (int i = 0; i < pos.size(); i++) { //NUM回くりかえし //位置を更新する pos[i] += vel[i]; //画面の端に来たら反対側へ if (pos[i].x > ofGetWidth()) pos[i].x = 0; //右 if (pos[i].x < 0) pos[i].x = ofGetWidth(); //左 if (pos[i].y > ofGetHeight()) pos[i].y = 0; //下 if (pos[i].y < 0) pos[i].y = ofGetHeight(); //上 }}
静的(固定長)配列と、動的(可変長)配列‣ コーディングしてみる (testApp.cpp)
void testApp::draw(){ ofSetHexColor(0x3399ff); //描画する色を指定 for (int i = 0; i < pos.size(); i++) { //NUM回くりかえし ofCircle(pos[i].x, pos[i].y, 4); //更新された値を利用して円を描く } //現在の数とフレームレートを表示 string log; log = "particle num = " + ofToString(pos.size(), 0) + "\n"; log += "framerate = " + ofToString(ofGetFrameRate(), 4); ofSetHexColor(0xffffff); ofDrawBitmapString(log, 20, 20);}
void testApp::mouseDragged(int x, int y, int button) { ofVec2f p; //位置の変数を一時的に生成 p.set(x, y); //位置をマウスをクリックした場所に設定 pos.push_back(p); //可変配列の末尾に位置を追加 ofVec2f v; //速度の変数を一時的に生成 v.set(ofRandom(-1, 1), ofRandom(-1, 1)); //速度を設定 vel.push_back(v); //可変配列の末尾に速度を追加}
静的(固定長)配列と、動的(可変長)配列‣ コーディングしてみる (testApp.cpp)
void testApp::keyReleased(int key) { if (key == 'c') { //「c」キーを押したら pos.clear(); //位置の配列を初期化 vel.clear(); //速度の配列を初期化 } if (key == 'f') { //「f」キーを押したら ofToggleFullscreen(); //フルスクリーンon/off }}
静的(固定長)配列と、動的(可変長)配列‣ 実行結果:マウスドラッグでパーティクルが次々と増加
色をランダムに塗りわける
色をランダムに塗りわける‣ さらに応用‣ パーティクルの色にバリエーションをつける
‣ 色を管理するのに便利なクラス:ofColor
‣ 例:ofColor col; //色のオブジェクトをインスタンス化col.r = 31; //redを設定col.g = 127; //greenを設定col.b = 255; //blueを設定
色をランダムに塗りわける‣ コーディングしてみる (testApp.h)#pragma once#include "ofMain.h"
class testApp : public ofBaseApp{ public: void setup(); void update(); void draw(); void mouseDragged(int x, int y, int button); void keyReleased(int key); vector<ofVec2f> pos; //位置の可変配列 vector<ofVec2f> vel; //パーティクルの速度の可変配列 vector<ofColor> col; //色の可変配列};
色をランダムに塗りわける‣ コーディングしてみる (testApp.cpp)#include "testApp.h"
void testApp::setup(){ ofSetFrameRate(60); //更新を秒間60コマに(fps) ofEnableBlendMode(OF_BLENDMODE_ADD); //色を加算合成に ofBackgroundHex(0x000000); //背景色を黒に}
void testApp::update(){ for (int i = 0; i < pos.size(); i++) { //NUM回くりかえし //位置を更新する pos[i] += vel[i]; //画面の端に来たら反対側へ if (pos[i].x > ofGetWidth()) pos[i].x = 0; //右 if (pos[i].x < 0) pos[i].x = ofGetWidth(); //左 if (pos[i].y > ofGetHeight()) pos[i].y = 0; //下 if (pos[i].y < 0) pos[i].y = ofGetHeight(); //上 }}
色をランダムに塗りわける‣ コーディングしてみる (testApp.cpp)void testApp::draw(){ for (int i = 0; i < pos.size(); i++) { //NUM回くりかえし ofSetColor(col[i].r, col[i].g, col[i].b);//ランダムな色で描画 ofCircle(pos[i].x, pos[i].y, 4); //更新された値を利用して円を描く }
//現在の数とフレームレートを表示 string log; log = "particle num = " + ofToString(pos.size(), 0) + "\n"; log += "framerate = " + ofToString(ofGetFrameRate(), 4); ofSetHexColor(0xffffff); ofDrawBitmapString(log, 20, 20);}
色をランダムに塗りわける‣ コーディングしてみる (testApp.cpp)void testApp::mouseDragged(int x, int y, int button) { ofVec2f p; //位置の変数を一時的に生成 p.set(x, y); //位置をマウスをクリックした場所に設定 pos.push_back(p); //可変配列の末尾に位置を追加 ofVec2f v; //速度の変数を一時的に生成 v.set(ofRandom(-1, 1), ofRandom(-1, 1)); //速度を設定 vel.push_back(v); //可変配列の末尾に速度を追加 ofColor c; //色を一時的に生成 c.r = ofRandom(255); //red成分をランダムに c.g = ofRandom(255); //green成分をランダムに c.b = ofRandom(255); //blue成分をランダムに col.push_back(c);}
void testApp::keyReleased(int key) { if (key == 'c') { //「c」キーを押したら pos.clear(); //位置の配列を初期化 vel.clear(); //速度の配列を初期化 } if (key == 'f') { //「f」キーを押したら ofToggleFullscreen(); //フルスクリーンon/off }}
静的(固定長)配列と、動的(可変長)配列‣ 実行結果:色がランダムに変化
3D空間へ!!
3D空間へ!!‣ 3次元空間へパーティクルの運動を拡張!!
‣ 2次元座標を扱うクラス → ofVec2f‣ 3次元座標を扱うクラス → ofVec3f
‣ 位置と速度の座標を、それぞれ、ofVec3fで管理する
3D空間へ!!‣ 3Dの空間で物体の位置と速度を表現するには‣ x, y に加えて、もう一つの軸 z を用いる
x
y
z
3D空間へ!!‣ コーディングしてみる (testApp.h)#pragma once#include "ofMain.h"
class testApp : public ofBaseApp{ public: void setup(); void update(); void draw(); void mouseDragged(int x, int y, int button); void keyReleased(int key); vector<ofVec3f> pos; //位置の可変配列(3D) vector<ofVec3f> vel; //パーティクルの速度の可変配列(3D)};
3D空間へ!!‣ コーディングしてみる (testApp.cpp)#include "testApp.h"
void testApp::setup(){ ofSetFrameRate(60); //更新を秒間60コマに(fps) ofEnableBlendMode(OF_BLENDMODE_ADD); //色を加算合成に ofBackgroundHex(0x000000); //背景色を黒に}
void testApp::update(){ for (int i = 0; i < pos.size(); i++) { //NUM回くりかえし //位置を更新する pos[i] += vel[i]; }}
void testApp::draw(){ glEnable(GL_DEPTH_TEST); //奥行の判定をする ofSetHexColor(0x3399ff); for (int i = 0; i < pos.size(); i++) { //NUM回くりかえし ofPushMatrix(); ofTranslate(pos[i]); //位置を設定 ofCircle(0, 0, 4); //更新された値を利用して円を描く ofPopMatrix(); } glDisable(GL_DEPTH_TEST);
3D空間へ!!‣ コーディングしてみる (testApp.cpp) //現在の数とフレームレートを表示 string log; log = "particle num = " + ofToString(pos.size(), 0) + "\n"; log += "framerate = " + ofToString(ofGetFrameRate(), 4); ofSetHexColor(0xffffff); ofDrawBitmapString(log, 20, 20);}
void testApp::mouseDragged(int x, int y, int button) { ofVec3f p; //位置の変数を一時的に生成 p.set(x, y); //位置をマウスをクリックした場所に設定 pos.push_back(p); //可変配列の末尾に位置を追加
ofVec3f v; //速度の変数を一時的に生成 v.set(ofRandom(-1, 1), ofRandom(-1, 1), ofRandom(-1, 1));//速度を設定 vel.push_back(v); //可変配列の末尾に速度を追加}
3D空間へ!!‣ コーディングしてみる (testApp.cpp)void testApp::keyReleased(int key) { if (key == 'c') { //「c」キーを押したら pos.clear(); //位置の配列を初期化 vel.clear(); //速度の配列を初期化 }
if (key == 'f') { //「f」キーを押したら ofToggleFullscreen(); //フルスクリーンon/off }}
3D空間へ!!‣ 実行結果:3D空間を飛び散るパーティクル!!
画像を貼りつける
画像を貼りつける‣ それぞれのパーティクルの場所に、ビットマップ画像を貼りつけてみる
‣ よりリアルな表現へ
‣ 画像ファイルの扱い方については、また別途詳しく解説します
画像を貼りつける