BNN CAMP vol.3  インタラクションデザインの現在―プログラミング初心者のためのopenFrameworks入門 2

4,616 views

Published on

0 Comments
11 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
4,616
On SlideShare
0
From Embeds
0
Number of Embeds
1,559
Actions
Shares
0
Downloads
35
Comments
0
Likes
11
Embeds 0
No embeds

No notes for slide

BNN CAMP vol.3  インタラクションデザインの現在―プログラミング初心者のためのopenFrameworks入門 2

  1. 1. BNN CAMP vol.3  インタラクションデザインの現在 プログラミング初心者のための openFrameworks入門 - 2: 構造をつくる 2013年8月3日 田所 淳
  2. 2. このセクションの内容 ‣ 構造をもったより複雑なプログラムへ! ‣ 関数 ‣ クラス - オブジェクト指向プログラミング ‣ くりかえしと配列 ‣ パーティクルシステムをつくる ‣ ベクターフィールド
  3. 3. 処理をまとめる サブルーチン ( = 関数)
  4. 4. サブルーチン (= 関数) とは ‣ プログラム中で意味や内容がまとまっている作業をひとつの手 続きとしたもの ‣ openFrameworks (つまり C++) では、サブルーチンのことを 「関数 (function)」と呼ぶのが一般的 ‣ 関数を使用する利点 ‣ 繰り返し現れる作業をまとめることができる ‣ プログラムの可読性の向上 ‣ 保守性を高く保つ
  5. 5. 関数:引数と返り値 ‣ 関数への入出力 ‣ 引数 (ひきすう, argument) - 関数に渡す値 ‣ 返り値 (return value) - 関数が返す値 関数 引数1 引数2 引数3 戻り値
  6. 6. C++ での関数の書きかた ‣ C++での関数の書き方 ‣ 例えば、int型の数の二乗を計算する関数 ‣ もし戻り値がない関数の場合、戻り値の型は「void」にする 戻り値の型 名前空間::関数名(引数1, 引数2, 引数3...){ 関数の処理の内容 } int testApp::poweroftwo(int a){ ! return a * a; }
  7. 7. 関数によるアニメーション:補完 ‣ 2つの地点を直線移動する座標を計算する関数をつくってみる ‣ 現在の位置は、終了点を1とした割合(0.0∼1.0)で表現 startPos (0.0) currentPos (0.0∼1.0の間) endPos (1.0)
  8. 8. 関数によるアニメーション:補完 ‣ 開始点(startPos)と終了点(endPos)をまず決める startPos (0.0) endPos (1.0)
  9. 9. 関数によるアニメーション:補完 ‣ 経過した地点の割合 pct (0.0∼1.0)を与えると、現在の地点の 座標を計算して返す startPos (0.0) endPos (1.0)0.2
  10. 10. 関数によるアニメーション:補完 ‣ 経過した地点の割合 pct (0.0∼1.0)を与えると、現在の地点の 座標を計算して返す startPos (0.0) endPos (1.0) 0.5
  11. 11. 関数によるアニメーション:補完 ‣ 経過した地点の割合 pct (0.0∼1.0)を与えると、現在の地点の 座標を計算して返す startPos (0.0) endPos (1.0) 0.8
  12. 12. 補足:ofPointで座標を指定する ‣ これ以降、座標の点を表現する際には、X座標、Y座標ではな くそれをまとめた、ofPoint() を使用する ‣ X, Y座標をまとめて1つの変数で扱うことのできるもの(クラス) posx y
  13. 13. 補足:ofPointで座標を指定する ‣ ofPointは、[変数名].x [変数名].y と指定しすることで複数の座 標を一括して管理できる ‣ 例:(10.0, 20.0)の座標を表現する場合 float x; float y; x = 10.0; y = 20.0 複数のfloat型 ofPoint pos; pos.x = 10.0; pos.y = 20.0; ofPoint型
  14. 14. 関数によるアニメーション:補完 ‣ 関数をつかって、定義してみる ‣ 関数名:interpolateByPct ‣ 引数 : ‣ float pct → 現在の二点間を補完する際の割合(0.0 ∼ 1.0) ‣ 返り値 : ‣ ofPoint pos → 割合から算出された座標 ofPoint interpolateByPct(float pct);
  15. 15. 関数によるアニメーション:補完 ‣ 関数をtestAppに追加する場合 ‣ まずヘッダファイル(レシピ!)に、関数の概要を追加する ‣ 料理の手順を工程表に追加するイメージ ‣ 同時に必要となる変数 (材料) も全て追加しておく ヘッダファイル = レシピ interpolateByPct() ofPoint startPos; ofPoint endPos; ofPoint currentPos; float pct;
  16. 16. 関数によるアニメーション:補完 ‣ testApp.h に関数(手順)と変数(材料)を追加 #pragma once #include "ofMain.h" class testApp : public ofBaseApp{ ! public: ! void setup(); ! void update(); ! void draw(); ...《中略》... ! ! ofPoint interpolateByPct(float pct); ! ! ofPoint startPos; ! ofPoint endPos; ! ofPoint currentPos; ! float pct; ! }; 追加
  17. 17. 関数によるアニメーション:補完 ‣ 実装ファイルに手順を記述 ‣ testApp.cppに、testApp::interpolateByPct() { ... } というブ ロックを用意して処理内容を全て書いていく interpolateByPct( ) 位置を割合で指定すると 現在の位置の座標を返す
  18. 18. 関数によるアニメーション:補完 ‣ testApp.cpp に関数(手順)の内容を記述 #include "testApp.h" //-------------------------------------------------------------- void testApp::setup(){ ! ofBackground(0, 0, 0); ! ofSetFrameRate(60); ! ofSetVerticalSync(true); } 《中略》... //-------------------------------------------------------------- ofPoint testApp::interpolateByPct(float _pct){ ! ! ofPoint pos; ! pos.x = (1.0 - _pct) * startPos.x + (_pct) * endPos.x; ! pos.y = (1.0 - _pct) * startPos.y + (_pct) * endPos.y; ! ! return pos; ! } 追加
  19. 19. 関数によるアニメーション:補完 ‣ 実際に座標を計算してみる ‣ 2段階の手順が必要 ‣ setup() でまず開始位置と終了位置を決定 ‣ update() で割合(pct)を継続して入力すると、現在の位置を計 算して返す startPos endPos interpolateByPct( )pct currentPos interpolateByPct( )
  20. 20. 関数によるアニメーション:補完 ‣ testApp.cpp に処理を追加 #include "testApp.h" //-------------------------------------------------------------- void testApp::setup(){ ! ofBackground(0, 0, 0); ! ofSetFrameRate(60); ! ofSetVerticalSync(true); ! startPos = ofPoint(10, 100); ! endPos = ofPoint(1000, 600); ! pct = 0; } //-------------------------------------------------------------- void testApp::update(){ ! pct = pct + 0.01; ! currentPos = interpolateByPct(pct); } 追加 追加
  21. 21. 関数によるアニメーション:補完 ‣ testApp.cpp に処理を追加 //-------------------------------------------------------------- void testApp::draw(){ ! ofSetColor(31, 127, 255); ! ofCircle(currentPos.x, currentPos.y, 20); } //-------------------------------------------------------------- ofPoint testApp::interpolateByPct(float _pct){ ! ! ofPoint pos; ! pos.x = (1.0 - _pct) * startPos.x + (_pct) * endPos.x; ! pos.y = (1.0 - _pct) * startPos.y + (_pct) * endPos.y; ! ! return pos; ! } 追加
  22. 22. 関数によるアニメーション:補完 ‣ 実行結果:設定した座標間を直線運動する
  23. 23. 関数によるアニメーション:補完 ‣ もう少し工夫 ‣ 0.0から1.0に到達したら、また0.0に戻る(リセット)するように ‣ 条件をつかえば簡単に実現可能 ‣ 「もし、pct の値が1.0を越えたら、0.0に戻れ」 ‣ 「もし○○なら、××せよ」→ if分を用いる
  24. 24. 関数によるアニメーション:補完 ‣ 選択 - 何らかの条件が成立したとき文の並びを実行する ‣ 「もし○○なら××せよ、そうでなければ△△せよ」 処理 A 処理 B 条件? Yes No
  25. 25. 関数によるアニメーション:補完 ‣ if∼else文を使う if (【条件式】) { 【条件式が正しい時の処理 (真文)】 } else { 【条件式が正しくない時の処理 (偽文)】 }
  26. 26. 関数によるアニメーション:補完 ‣ testApp.cpp - update() に処理を追加 //-------------------------------------------------------------- void testApp::update(){ ! pct = pct + 0.01; ! ! if (pct > 1.0) { ! ! pct = 0.0; ! } ! ! currentPos = interpolateByPct(pct); } 追加
  27. 27. 関数によるアニメーション:補完 ‣ 実行結果:動きがループするようになったはず!
  28. 28. 高度な補完:加速と減速 ‣ 補完の動きに、表情をつける ‣ %による補完を、単純な加算ではなく、指数で指定してみる ‣ 指数の値によって、時間と距離の曲線はどうなるか? ‣ OSX付属のGrapher.appをつかって、確かめてみる
  29. 29. 高度な補完:加速と減速 y = x
  30. 30. 高度な補完:加速と減速 y = x^2
  31. 31. 高度な補完:加速と減速 y = x^0.5
  32. 32. 高度な補完:加速と減速 ‣ C++で指数計算をするには、powerf() 関数を使用する ‣ 例:10の2乗 ‣ interpolateByPct 関数に、引数 shaper を追加 ‣ pctを、shaperで指定した指数で乗算していく powf(10.0, 2.0); ofPoint testApp::interpolateByPct(float _pct, float _shaper){ ! ofPoint pos; ! float shapedPct = powf(_pct, _shaper); ! pos.x = (1.0 - shapedPct) * startPos.x + shapedPct * endPos.x; ! pos.y = (1.0 - shapedPct) * startPos.y + shapedPct * endPos.y; ! return pos; }
  33. 33. 高度な補完:加速と減速 ‣ testApp.h :追加と修正 #pragma once #include "ofMain.h" class testApp : public ofBaseApp{ ! public: ! void setup(); ! void update(); ! void draw(); ...《中略》... ! ofPoint interpolateByPct(float pct, float shaper); ! ! ofPoint startPos; ! ofPoint endPos; ! ofPoint currentPos; ! float pct; ! float shaper; ! }; 修正 追加
  34. 34. 高度な補完:加速と減速 ‣ testApp.cpp:追加と修正 #include "testApp.h" //-------------------------------------------------------------- void testApp::setup(){ ! ofBackground(0, 0, 0); ! ofSetFrameRate(60); ! ofSetVerticalSync(true); ! startPos = ofPoint(10, 100); ! endPos = ofPoint(1000, 600); ! pct = 0; ! shaper = 2.0; } //-------------------------------------------------------------- void testApp::update(){ ! pct = pct + 0.01; ! if (pct > 1.0) { ! ! pct = 0.0; ! } ! currentPos = interpolateByPct(pct, shaper); } 修正 追加
  35. 35. 高度な補完:加速と減速 ‣ testApp.cpp:追加と修正 //-------------------------------------------------------------- ofPoint testApp::interpolateByPct(float _pct, float _shaper){ ! ! ofPoint pos; ! float shapedPct = powf(_pct, _shaper); ! ! pos.x = (1.0 - shapedPct) * startPos.x + shapedPct * endPos.x; ! pos.y = (1.0 - shapedPct) * startPos.y + shapedPct * endPos.y; ! ! return pos; ! } 修正
  36. 36. 高度な補完:加速と減速 ‣ 実行結果
  37. 37. 高度な補完:加速と減速 ‣ shaperの値をいろいろ変化させて、動きを観察してみる shaper = 4.0 shaper = 0.5
  38. 38. オブジェクト指向プログラミング
  39. 39. ‣ オブジェクト指向プログラミング ‣ Object Oriented Programming (OOP) ‣ オブジェクト指向でProcessingのプログラムを作る ‣ そもそもオブジェクト指向とは? ‣ 簡単なプログラムを、オブジェクト指向で書いてみる ‣ クラスの定義 ‣ クラスの呼びだし オブジェクト指向プログラミングとは?
  40. 40. ‣ オブジェクト指向プログラミング言語のイメージ プログラミング・パラダイムの変遷 オブジェクト オブジェクト オブジェクト オブジェクト
  41. 41. ‣ OOPの特徴 ‣ 相互にメッセージを送り合う「オブジェクト」の集まりとして プログラムを構成 ‣ オブジェクトは、プロパティとメソッドから構成される ‣ カプセル化 - 必要のない情報は隠す ‣ インヘリタンス(継承) - あるオブジェクトが他のオブジェク トの特性を引き継ぐ ‣ ポリモーフィズム(多態性・多様性) - プログラミング言語の 各要素が複数の型に属することを許す オブジェクト指向プログラミングの概念
  42. 42. ‣ オブジェクト ‣ プロパティとメソッド ‣ カプセル化 ‣ 継承 (インヘリタンス) ‣ 多態性、多相性 (ポリモーフィズム) OOP、5つのポイント
  43. 43. ‣ オブジェクト指向プログラムのポイント:その1 ‣ オブジェクトの集まりとしてプログラムを構成 ‣ オブジェクト同士がメッセージを送りあう OOP:ポイントその1
  44. 44. ‣ オブジェクト指向プログラムのポイント:その2 ‣ オブジェクトは、プロパティ(性質、状態)と、メソッド(動作、 ふるまい) から構成される 状態1 状態2 状態3 オブジェクト メ ソ ッ ド 1 メ ソ ッ ド 2 メ ソ ッ ド 3 メ ソ ッ ド 4 OOP:ポイントその2
  45. 45. ‣ 例:「りんご」をオブジェクトとして考える 赤 5.0 甘い ふじ 実 が な る 成 長 す る 落 ち る 腐 る 青 4.0 すっぱい 青リンゴ 実 が な る 成 長 す る 落 ち る 腐 る OOP:ポイントその2
  46. 46. ‣ オブジェクト指向プログラムのポイント:その3 ‣ 必要のない情報は隠す (カプセル化) ‣ プログラムの実装全てを知る必要はない ‣ 必要なインターフェイス(接点)だけ見せて、あとは隠す To invent programs, you need to be able to capture abstractions and ex design. It’s the job of a programming language to help you do this. The process of invention and design by letting you encode abstractions tha It should let you make your ideas concrete in the code you write. Surf the architecture of your program. All programming languages provide devices that help express abstrac are ways of grouping implementation details, hiding them, and giving a common interface—much as a mechanical object separates its interfa illustrated in “Interface and Implementation” . Figure 2-1 Interface and Implementation 9 10 11 8 7 6 implementationinterface インターフェイス 実装 OOP:ポイントその3
  47. 47. ‣ オブジェクト指向プログラムのポイント:その4 ‣ インヘリタンス(継承) ‣ オブジェクトから新たなオブジェクトを派生させる 植物 生物 動物 果物 穀物 りんご ふじ 紅玉 デリシャス バナナ マンゴー OOP:ポイントその4
  48. 48. ‣ オブジェクト指向プログラムのポイント:その5 ‣ ポリモーイズム(多態性、多様性) ‣ オブジェクトはメッセージを受け取りそれに応じた処理を行う ‣ メッセージの処理方法は、オブジェクト自身が知っていて、そ の処理はオブジェクトによって異なる getName() オブジェクトA:人間 「田所 淳」 getName() オブジェクトB:車 「トヨタカローラ」 OOP:ポイントその5
  49. 49. ‣ クラス ‣ クラスとは:オブジェクトの「型紙」 ‣ クラスをインスタンス化 (実体化) することでインスタンス (オブジェクト)となる 色 重さ(g) 味 リンゴ (クラス) 実 が な る 成 長 す る 落 ち る 腐 る 赤 5.0 甘い ふじ (インスタンスオブジェクト) 実 が な る 成 長 す る 落 ち る 腐 る 青 4.0 すっぱい 青リンゴ (インスタンスオブジェクト) 実 が な る 成 長 す る 落 ち る 腐 る インスタンス化 クラス
  50. 50. ‣ いままで扱ってきた、testApp も一つのクラス ‣ メソッド - setup(), update(), draw() ...etc. ‣ プロパティ - testApp全体で使用する変数 testAppもクラス クラス変数 setup() update() draw () exit() testApp
  51. 51. ‣ これまでのようにtestAppにどんどん機能を追加すると、様々 な弊害が ‣ 可読性の低下、機能ごとに再利用できない、拡張が困難 ..etc. testApp単体の限界 testApp testApp 肥大化
  52. 52. ‣ 機能ごとにオブジェクトを分けてプロジェクトを構成する ‣ オブジェクトが相互に連携 testApp単体の限界 testApp Rectangle Particle Control Panel
  53. 53. OOP実践編 クラスを作る
  54. 54. クラスの実装 ‣ 前半にやった、アニメーションする円をクラス化してみる ‣ まずは円を表示するところまで ‣ クラス名:MoveCircle ‣ プロパティ (状態、変数): ‣ ofPoint startPos : 円の初期位置 ‣ メソッド (ふるまい、関数): ‣ draw( ) : 円を描く
  55. 55. クラスの実装 ‣ こんな図にかく場合も ‣ UMLクラス図 + draw():void + currentPos:ofPoint MoveCircle
  56. 56. クラスの実装 ‣ こんな図にかく場合も ‣ UMLクラス図 + draw():void + currentPos:ofPoint MoveCircle プロパティ (状態) メソッド(ふるまい)
  57. 57. XCodeプロジェクトにクラスを追加する ‣ ファイルのリストの「src」フォルダを右クリック ‣ リストから「New File (新規ファイル)」を選択
  58. 58. XCodeプロジェクトにクラスを追加する ‣ Mac OS X > C and C++ > C++ File を選択
  59. 59. XCodeプロジェクトにクラスを追加する ‣ 名前をつけて、「src」フォルダに保存
  60. 60. XCodeプロジェクトにクラスを追加する ‣ 再度、リストから「New File (新規ファイル)」を選択
  61. 61. XCodeプロジェクトにクラスを追加する ‣ Mac OS X > C and C++ > Header File を選択
  62. 62. XCodeプロジェクトにクラスを追加する ‣ 名前をつけて、「src」フォルダに保存
  63. 63. XCodeプロジェクトにクラスを追加する ‣ ファイルリストは以下のようになるはず ‣ あとは、それぞれのファイルにコーディングしていく
  64. 64. クラスの記述 ‣ まずはヘッダーファイル (MoveCircle.h) から ‣ レシピの材料と手順の一覧! ‣ 材料 → 状態、性質 → つまり、プロパティ(変数) ‣ 手順 → ふるまい、動作 → つまり、メソッド(関数) ‣ そのクラスのプロパティとメソッドを記述 ‣ 外部から参照するものは、public: 以下に書く
  65. 65. クラスの実装 ‣ MoveCircle.h - 円を描く際のレシピ! #pragma once #include "ofMain.h" class MoveCircle { ! public: ! ! void draw(); ! ofPoint currentPos; ! };
  66. 66. クラスの実装 ‣ MoveCircle.h - 円を描く際のレシピ! #pragma once #include "ofMain.h" class MoveCircle { ! public: ! ! void draw(); ! ofPoint currentPos; ! }; インクルードガード Buildの際に複数回読みこまれないためのしくみ
  67. 67. クラスの実装 ‣ MoveCircle.h - 円を描く際のレシピ! #pragma once #include "ofMain.h" class MoveCircle { ! public: ! ! void draw(); ! ! ofPoint currentPos; ! }; oFの機能を使うためのライブラリを 必ず読み込む
  68. 68. クラスの実装 ‣ MoveCircle.h - 円を描く際のレシピ! #pragma once #include "ofMain.h" class MoveCircle { ! public: ! ! void draw(); ! ! ofPoint currentPos; ! }; クラス名
  69. 69. クラスの実装 ‣ MoveCircle.h - 円を描く際のレシピ! #pragma once #include "ofMain.h" class MoveCircle { ! public: ! ! void draw(); ! ! ofPoint currentPos; ! }; public: 以下は外部に公開される
  70. 70. クラスの実装 ‣ MoveCircle.h - 円を描く際のレシピ! #pragma once #include "ofMain.h" class MoveCircle { ! public: ! ! void draw(); ! ! ofPoint currentPos; ! }; メソッド:draw() - 円を描く
  71. 71. クラスの実装 ‣ MoveCircle.h - 円を描く際のレシピ! #pragma once #include "ofMain.h" class MoveCircle { ! public: ! ! void draw(); ! ! ofPoint currentPos; ! }; プロパティ:currentPos - 初期位置
  72. 72. クラスの実装 ‣ MoveCircle.h - 円を描く際のレシピ! #pragma once #include "ofMain.h" class MoveCircle { ! public: ! ! void draw(); ! ! ofPoint currentPos; ! }; 最後に必ずセミコロンをつける
  73. 73. クラスの記述 ‣ つぎに実装ファイル(MoveCircle.cpp)を書く ‣ 円を描くための全ての手続きを記述していく ‣ 現在は、draw() 関数のみ
  74. 74. クラスの記述 ‣ MoveCircle.cpp - 円を実際に描く手順 #include "MoveCircle.h" void MoveCircle::draw() { ! ! ofFill(); ! ofSetColor(31,127,255); ! ofCircle(currentPos.x, currentPos.y, 20,20); ! } 必ずヘッダーファイルを読み込む
  75. 75. クラスの記述 ‣ MoveCircle.cpp - 円を実際に描く手順 #include "MoveCircle.h" void MoveCircle::draw() { ! ! ofFill(); ! ofSetColor(31,127,255); ! ofCircle(currentPos.x, currentPos.y, 20,20); ! } 戻り値 クラス名::関数名(引数) クラス名を名前空間として使用している 他のクラスのdraw()関数との混同を避けている
  76. 76. クラスの記述 ‣ MoveCircle.cpp - 円を実際に描く手順 #include "MoveCircle.h" void MoveCircle::draw() { ! ! ofFill(); ! ofSetColor(31,127,255); ! ofCircle(currentPos.x, currentPos.y, 20,20); ! } 円を描画
  77. 77. クラスの記述 ‣ 最後に作成したクラスを、oFのメインクラスであるtestAppか ら呼び出します ‣ ヘッダーファイル testApp.h で MoveCircle を宣言 ‣ これだけで、クラスが実体化(インスタンス化)される ‣ クラス名:MoveCircle ‣ インスタンス:myCircle MoveCircle myCircle;
  78. 78. クラスの実装 ‣ testApp.h - MoveCircleをインスタンス化 #pragma once #include "ofMain.h" #include "MoveCircle.h" class testApp : public ofBaseApp{ ! public: ! void setup(); ! void update(); ! void draw(); ! ...《中略》... ! ! MoveCircle myCircle; };
  79. 79. クラスの実装 ‣ testApp.h - MoveCircleをインスタンス化 #pragma once #include "ofMain.h" #include "MoveCircle.h" class testApp : public ofBaseApp{ ! public: ! void setup(); ! void update(); ! void draw(); ! ...《中略》... ! ! MoveCircle myCircle; }; MovieCircleのヘッダを読み込む
  80. 80. クラスの実装 ‣ testApp.h - MoveCircleをインスタンス化 #pragma once #include "ofMain.h" #include "MoveCircle.h" class testApp : public ofBaseApp{ ! public: ! void setup(); ! void update(); ! void draw(); ! ...《中略》... ! ! MoveCircle myCircle; }; MovieCircleをインスタンス化
  81. 81. クラスの記述 ‣ メインの実装ファイル、testApp.cpp で作成したインスタンス を使用して円を描かせる ‣ testApp::setup( ) で円の初期位置を指定 ‣ testApp::draw( ) でMyCircleのdraw( )メソッドを呼びだし
  82. 82. クラスの実装 ‣ testApp.cpp - MoveCircleで円を描く #include "testApp.h" //-------------------------------------------------------------- void testApp::setup(){ ! ofSetFrameRate(60); ! ofSetVerticalSync(true); ! ofEnableAlphaBlending(); ! ofBackground(0, 0, 0); ! ! myCircle.currentPos = ofPoint(400, 300); } //-------------------------------------------------------------- void testApp::update(){ } //-------------------------------------------------------------- void testApp::draw(){ ! ! myCircle.draw(); ! }
  83. 83. クラスの実装 ‣ testApp.cpp - MoveCircleで円を描く #include "testApp.h" //-------------------------------------------------------------- void testApp::setup(){ ! ofSetFrameRate(60); ! ofSetVerticalSync(true); ! ofEnableAlphaBlending(); ! ofBackground(0, 0, 0); ! ! myCircle.currentPos = ofPoint(400, 300); } //-------------------------------------------------------------- void testApp::update(){ } //-------------------------------------------------------------- void testApp::draw(){ ! ! myCircle.draw(); ! } 初期位置の指定
  84. 84. クラスの実装 ‣ testApp.cpp - MoveCircleで円を描く #include "testApp.h" //-------------------------------------------------------------- void testApp::setup(){ ! ofSetFrameRate(60); ! ofSetVerticalSync(true); ! ofEnableAlphaBlending(); ! ofBackground(0, 0, 0); ! ! myCircle.currentPos = ofPoint(400, 300); } //-------------------------------------------------------------- void testApp::update(){ } //-------------------------------------------------------------- void testApp::draw(){ ! ! myCircle.draw(); ! } myCircleを使用して、円を描く
  85. 85. クラスの実装 ‣ ようやく円が描けた!!
  86. 86. クラスの実装 ‣ 次にMoveCircleクラスに、動きをつけてみる! ‣ 関数のところで学んだ、補完する動きをクラスに組み込む
  87. 87. クラスの実装 ‣ クラス名:MoveCircle ‣ プロパティ: ‣ ofPoint startPos - 開始位置 ‣ ofPoint endPos - 終了位置 ‣ ofPoint currentPos - 現在位置 ‣ float pct - 現在の位置の割合(%) ‣ float shaper - 加速・減速の加減 ‣ メソッド: ‣ void draw(); - 四角形の描画 ‣ void update(); - 状態の更新 ‣ float interpolateByPct(float myPct); - 位置の計算
  88. 88. クラスの実装 ‣ クラス名:MoveCircle ‣ UMLクラス図で表現すると、こんな感じ ‣ この設計図の通りに、ヘッダーファイル(MoveCircle.h)と実装 ファイル(MoveCircle.cpp)を記述していく + draw():void + update():void + interpolateByPct():ofPoint + startPos:ofPoint + endPos:ofPoint + currentPos:ofPoint + pct:float + shaper:float MoveCircle
  89. 89. クラスの実装 ‣ MoveCircle.h - 円を描く際のレシピ! #pragma once #include "ofMain.h" class MoveCircle { ! public: ! ! void draw(); ! void update(); ! ofPoint interpolateByPct(float pct); ! ! ofPoint currentPos; ! ofPoint startPos; ! ofPoint endPos; ! float pct; ! float shaper; };
  90. 90. クラスの実装 ‣ MoveCircle.h - 円を描く際のレシピ! #pragma once #include "ofMain.h" class MoveCircle { ! public: ! ! void draw(); ! void update(); ! ofPoint interpolateByPct(float pct); ! ! ofPoint currentPos; ! ofPoint startPos; ! ofPoint endPos; ! float pct; ! float shaper; }; メソッド
  91. 91. クラスの実装 ‣ MoveCircle.h - 円を描く際のレシピ! #pragma once #include "ofMain.h" class MoveCircle { ! public: ! ! void draw(); ! void update(); ! ofPoint interpolateByPct(float pct); ! ! ofPoint currentPos; ! ofPoint startPos; ! ofPoint endPos; ! float pct; ! float shaper; }; プロパティ
  92. 92. クラスの実装 ‣ 次に実装ファイル MoveCircle.cpp を改変
  93. 93. クラスの実装 ‣ MoveCircle.cpp - 円を動かす #include "MoveCircle.h" void MoveCircle::draw() { ! ofFill(); ! ofSetColor(31,127,255); ofCircle(currentPos.x, currentPos.y, 20, 20); ! } void MoveCircle::update() { ! pct += 0.01f; ! if (pct > 1) { ! ! pct = 0; ! } ! ! currentPos = interpolateByPct(pct); ! }
  94. 94. クラスの実装 ‣ MoveCircle.cpp - 円を動かす ofPoint MoveCircle::interpolateByPct(float _pct){ ! ! float shapedPct = powf(_pct, shaper); ! ! ofPoint pos; ! pos.x = (1 - shapedPct) * startPos.x + shapedPct * endPos.x; ! pos.y = (1 - shapedPct) * startPos.y + shapedPct * endPos.y; ! ! return pos; ! }
  95. 95. クラスの実装 ‣ メインクラスの実装ファイル testApp.cpp も変更が必要
  96. 96. クラスの実装 ‣ testApp.cpp - MoveCircleで円を動かす #include "testApp.h" //-------------------------------------------------------------- void testApp::setup(){ ! ofSetFrameRate(60); ! ofBackground(0, 0, 0); ! myCircle.startPos = ofPoint(10, 400); ! myCircle.endPos = ofPoint(1000, 200); ! myCircle.shaper = 2.0; } //-------------------------------------------------------------- void testApp::update(){ ! myCircle.update(); } //-------------------------------------------------------------- void testApp::draw(){ ! myCircle.draw(); } ...《後略》...
  97. 97. クラスの実装 ‣ 完成!!
  98. 98. OOP実践編 パーティクルを動かす
  99. 99. OOP実践編:パーティクルを動かす ‣ より実践的なOOPを、例を参照しながら理解する ‣ パーティクル (Particles = 粒子) が空間内を飛びまわるプログ ラムを作成してみる ‣ 最初は1つの粒 ‣ 大量の粒を個別に動かすには? ‣ オブジェクトの配列を利用すると便利 ‣ 2種類の配列 ‣ 静的配列 (Array):固定長の配列 ‣ 動的配列 (Vector):可変長の配列
  100. 100. パーティクルのイメージ ‣ 一定方向に向けて移動する力を持つ ‣ ただし、常に反対方向の抵抗(摩擦など)を受けている 物体にかかる力 (force) F = ma (力 = 質量 * 加速度) ニュートンの運動方程式 抵抗力 (damping)
  101. 101. パーティクルクラスを設計する ‣ パーティクルクラスに必要な要素 ‣ クラス名:Particle ‣ プロパティ(状態) ‣ 現在位置:pos (ofVec2f) ‣ 速度:vel (ofVec2f) ‣ 力:frc (ofVec2f)
  102. 102. パーティクルクラスを設計する ‣ パーティクルクラスに必要な要素 (つづき) ‣ メソッド (動作) ‣ コンストラクタ(後述):Particle() ‣ デストラクタ(後述): Particle() ‣ 力のリセット:recetForce() ‣ 力を加える:addForce() ‣ 抵抗力(摩擦)を加える:addDampingForce() ‣ 状態の初期化:setInitialCondition() ‣ 位置更新:update() ‣ 描画:draw()
  103. 103. パーティクルクラスを設計する ‣ ofVec2f とは? ‣ ofPoint (x座標, y座標)に加えて、距離の算出や2点間の角度の 計算など、ベクトル計算に特化したクラス ‣ oF v007からは、ofVectorMathとして標準で使用可能
  104. 104. パーティクルクラスを設計する ‣ ParticleクラスのUMLクラス図 + Particle() + ~Particle() + resetForce():void + addForce(float x, float y):void + addDumpingForce():void + setInitialCondition(float px, float py, float vx, float vy):void + update():void + draw():void + pos:ofVec2f + vel:ofVec2f + frc:ofVec2f + dumping:float Particle
  105. 105. コンストラクタとデストラクタ ‣ コンストラクタ、デストラクタ:特殊なメソッド ‣ コンストラクタ ‣ クラスがインスタンス化された際に実行される ‣ つまり初期化の関数 ‣ 関数名はかならずクラス名と同じにする ‣ デストラクタ ‣ クラスが廃棄された際に実行される ‣ 終了処理 ‣ 関数名は「 クラス名」にする
  106. 106. コンストラクタとデストラクタ ‣ Particleクラスは完成したものをあらかじめ用意してきました ‣ これを活用していきます!!
  107. 107. Particleクラスの実装 ‣ ヘッダーファイル:Particle.h #pragma once #include "ofMain.h" class Particle { public: ! ! ofVec2f pos; ! ofVec2f vel; ! ofVec2f frc; ! float damping; ! ! Particle(); ! ~Particle(); ! void resetForce(); ! void addForce(float x, float y); ! void addDampingForce(); ! void setInitialCondition(float px, float py, float vx, float vy); ! void update(); ! void draw(); ! };
  108. 108. Particleクラスの実装 ‣ 実装ファイル:Particle.cpp #include "Particle.h" //コンストラクタ(初期化) Particle::Particle(){ ! setInitialCondition(0,0,0,0); ! damping = 0.01f; } //デストラクタ(終了処理) Particle::~Particle(){} //力(加速度)をリセット void Particle::resetForce(){ frc.set(0,0); } //力を加える void Particle::addForce(float x, float y){ frc.x = frc.x + x; frc.y = frc.y + y; }
  109. 109. Particleクラスの実装 ‣ 実装ファイル:Particle.cpp //抵抗力の計算 void Particle::addDampingForce(){ frc.x = frc.x - vel.x * damping; frc.y = frc.y - vel.y * damping; } //初期状態を設定 void Particle::setInitialCondition(float px, float py, float vx, float vy){ pos.set(px,py); ! vel.set(vx,vy); } //更新 void Particle::update(){! ! vel = vel + frc; ! pos = pos + vel; } //描画 void Particle::draw(){ ofCircle(pos.x, pos.y, 3); }
  110. 110. testAppを設計 ‣ testAppから、Particleを呼びだしてみる ‣ Particleクラスをインスタンス化、名前を「p」に ‣ testAppのsetup()内 ‣ Particleを初期設定:p.setInitialCondition() ‣ testAppのupdate()では… ‣ Particleの力をリセット:p.restForce() ‣ Particleの抵抗力を計算:p.addDampingForce() ‣ Particleの位置を更新:p.update() ‣ testAppのdraw()で ‣ Particleを描画:p.draw()
  111. 111. testAppクラスの実装 ‣ ヘッダーファイル:testApp.h #pragma once #include "ofMain.h" #include "Particle.h" class testApp : public ofSimpleApp{ ! public: void setup(); void update(); void draw(); void keyPressed (int key); void keyReleased (int key); void mouseMoved(int x, int y ); void mouseDragged(int x, int y, int button); void mousePressed(int x, int y, int button); void mouseReleased(); //クラスParticleをインスタンス化 Particle p; };
  112. 112. testAppクラスの実装 ‣ 実装ファイル:testApp.cpp #include "testApp.h" void testApp::setup(){! ! ofSetVerticalSync(true); ! ofSetFrameRate(60); ! ofBackground(0, 0, 0); ! p.setInitialCondition(ofGetWidth()/2, ofGetHeight()/2, ofRandom(-10,10), ofRandom(-10,10)); } void testApp::update(){ ! p.resetForce(); ! p.addDampingForce(); ! p.update(); } void testApp::draw(){ ! ofSetColor(255, 255, 255); ! p.draw(); } ...《後略》...
  113. 113. testAppクラスの実装 ‣ 完成
  114. 114. インタラクションを追加 ‣ マウスをクリックすると、クリックした位置からパーティクル を放出するように改造 ‣ 参考:testAppのマウスに関するインタラクション ‣ void mouseMoved(int x, int y ); ‣ マウスを移動 ‣ void mouseDragged(int x, int y, int button); ‣ マウスをドラッグ ‣ void mousePressed(int x, int y, int button); ‣ マウスのボタンを押した瞬間 ‣ void mouseReleased(); ‣ マウスの押していたボタンを離した瞬間
  115. 115. testAppクラスの実装 ‣ testApp.cppに、下記の処理を追加する void testApp::mousePressed(int x, int y, int button){ ! p.setInitialCondition(x,y,ofRandom(-10,10), ofRandom(-10,10)); }
  116. 116. testAppクラスの実装 ‣ 完成:マウスを画面上でクリックしてみる
  117. 117. クイズ:重力を追加する ‣ testAppを少しだけ変更して、パーティクルに重力を適用して みる ‣ 重量とは、常にパーティクルに一定に係りつづける力 ‣ Particleには既に力を加える関数addForce()を実装済み 重力 (Velocity)
  118. 118. 複数のパーティクルを同時に動かす 静的配列(Array)と、動的配列(Vector)
  119. 119. 複数のパーティクルを同時に動かす ‣ 複数のパーティクルを同時に動かすようにする ‣ パーティクルのオブジェクトを格納するやりかたに工夫が必要 ‣ 複数のオブジェクトを格納するには、配列を利用 ‣ 配列 = 値を格納するロッカーのようなもの p[0] p[1] p[2] . . . 配列 NUM個
  120. 120. 静的(固定長)配列と、動的(可変長)配列 ‣ 要素の数が固定された配列と、必要に応じて自由に長さをかえ ることのできる配列がある ‣ 静的配列:Array - 配列の数をあらかじめ指定する必要 ‣ 例:Particleクラスのオブジェクトpを100個格納 ‣ 動的配列列:Vector - 配列の数を指定する必要はない ‣ 例:Particleクラスのオブジェクトpを動的配列に Particle p[100]; vector <Particle> p;
  121. 121. 複数のパーティクルを同時に動かす(Array版) ‣ まずは静的配列(Array)で実装してみる ‣ Particleクラス(Particle.hとParticle.cpp)はそのまま変更なしに 使用可能 p[0] p[1] p[2] . . . Particle p[NUM] NUM個 p[NUM]
  122. 122. 複数のパーティクルを同時に動かす(Array版) ‣ 配列(Array)に格納した、大量のパラメータを使用して、同時 にパーティクルを動かすには? ‣ くりかえしの構文を使用する ‣ C++ では for文を使うと便利!!
  123. 123. ‣ for文の書きかた ‣ 初期化式:初期化の際の条件式 ‣ 継続条件式:繰り返しを継続する条件式 ‣ 再初期化式:繰り返されるたびに実行される式 繰り返し for (初期化式; 継続条件式; 再初期化式) { 文; }
  124. 124. /* 100回「+」の文字を出力する */ for (int i = 0; i < 100; i++) { ! cout << "+"; } ‣ for分を用いた、繰り返しの例 1 繰り返し
  125. 125. 複数のパーティクルを同時に動かす(Array版) ‣ パーティクルの位置の更新 (update) と 描画 (draw) を複数同時 に動くように書き換える ‣ 配列と for文がポイント
  126. 126. void testApp::update(){ ! p.resetForce(); ! p.addForce(0, 0.1); ! p.addDampingForce(); ! p.update(); } 複数のパーティクルを同時に動かす(Array版) ‣ update() の部分は、配列とfor文でこうなる void testApp::update(){ ! for (int i = 0; i < NUM; i++) { ! ! p[i].resetForce(); ! ! p[i].addForce(0, 0.1); ! ! p[i].addDampingForce(); ! ! p[i].update(); ! } }
  127. 127. void testApp::draw(){ ! ofSetColor(255, 255, 255); ! p.draw(); } 複数のパーティクルを同時に動かす(Array版) ‣ draw() も同様に void testApp::draw(){ ! ofSetColor(255, 255, 255); ! for (int i = 0; i < NUM; i++) { ! ! p[i].draw(); ! } }
  128. 128. 複数のパーティクルを同時に動かす(Array版) ‣ ヘッダーファイル:testApp.h #pragma once #include "ofMain.h" #include "Particle.h" #define NUM 100 class testApp : public ofSimpleApp{ ! public: void setup(); void update(); void draw(); void keyPressed (int key); void keyReleased (int key); void mouseMoved(int x, int y ); void mouseDragged(int x, int y, int button); void mousePressed(int x, int y, int button); void mouseReleased(); //クラスParticleの配列 (NUM個) Particle p[NUM]; };
  129. 129. 複数のパーティクルを同時に動かす(Array版) ‣ 実装ファイル:testApp.cpp #include "testApp.h" void testApp::setup(){! ! ofSetVerticalSync(true); ! ofSetFrameRate(60); ! ofBackground(0, 0, 0); } void testApp::update(){ for (int i = 0; i < NUM; i++) { p[i].resetForce(); p[i].addForce(0, 0.1); p[i].addDampingForce(); p[i].update(); } } void testApp::draw(){ ! ofSetColor(255, 255, 255); for (int i = 0; i < NUM; i++) { p[i].draw(); } }
  130. 130. 複数のパーティクルを同時に動かす(Array版) ‣ 実装ファイル:testApp.cpp ...《中略》... void testApp::mousePressed(int x, int y, int button){ for (int i = 0; i < NUM; i++) { p[i].setInitialCondition(x, y, ofRandom(-10,10), ofRandom(-10,10)); } }
  131. 131. 複数のパーティクルを同時に動かす(Array版) ‣ 完成:画面上でマウスクリック!
  132. 132. 複数のパーティクルを動かす(vector版) ‣ 同じ動きを、動的配列(Vector)で置き換える particles[0] particles[1] particles[2] . . . Vector <Particle> particles 数は可変
  133. 133. 複数のパーティクルを動かす(vector版) ‣ Vectorの配列要素の操作 ‣ 配列の末尾に要素を追加 → push_back() ‣ 例:Particleのオブジェクトpを、particlesに追加 ‣ 配列の末尾に要素を削除 → pop_back() ‣ 配列の全ての要素をクリア → clear() particles.push_back(p); particles.clear(); particles.pop_back();
  134. 134. 複数のパーティクルを動かす(vector版) ‣ ヘッダーファイル:testApp.h #pragma once #include "ofMain.h" #include "Particle.h" #define NUM 100 class testApp : public ofSimpleApp{ ! public: void setup(); void update(); void draw(); void keyPressed (int key); void keyReleased (int key); void mouseMoved(int x, int y ); void mouseDragged(int x, int y, int button); void mousePressed(int x, int y, int button); void mouseReleased(); //クラスParticleの動的配列particles vector <Particle> particles; };
  135. 135. 複数のパーティクルを動かす(vector版) ‣ 実装ファイル:testApp.cpp #include "testApp.h" void testApp::setup(){! ! ofSetVerticalSync(true); ! ofSetFrameRate(60); ! ofBackground(0, 0, 0); } void testApp::update(){ for (int i = 0; i < particles.size(); i++) { particles[i].resetForce(); particles[i].addForce(0, 0.1); particles[i].addDampingForce(); particles[i].update(); } } void testApp::draw(){ ! ofSetColor(255, 255, 255); for (int i = 0; i < particles.size(); i++) { particles[i].draw(); } }
  136. 136. 複数のパーティクルを動かす(vector版) ‣ 実装ファイル:testApp.cpp ...《中略》... void testApp::mousePressed(int x, int y, int button){ particles.clear(); for (int i = 0; i < NUM; i++) { //Particleをインスタンス化 → myParticle Particle myParticle; //初期化 float vx = ofRandom(-10, 10); float vy = ofRandom(-10, 10); myParticle.setInitialCondition(x, y, vx, vy); //作成したオブジェクトを配列の末尾に追加 particles.push_back(myParticle); } }
  137. 137. 複数のパーティクルを動かす(vector版) ‣ 完成:画面上でマウスクリック!
  138. 138. 応用編:パーティクルをどんどん追加 ‣ 動的配列の利点を活用 ‣ 最大数の制限なく、どんどんパーティクルを追加していく ‣ ユーザからのインタラクション: ‣ マウスをドラッグすると、パーティクルを1つ追加 ‣ ドラッグしつづけると、どんどん増えていく ‣ キーボードで「c」キーを入力すると、全部消去 ‣ 今回もParticleクラスは一切変更の必要なし
  139. 139. 応用編:パーティクルをどんどん追加 ‣ ヘッダーファイル:testApp.h #pragma once #include "ofMain.h" #include "Particle.h" class testApp : public ofSimpleApp{ ! public: void setup(); void update(); void draw(); void keyPressed (int key); void keyReleased (int key); void mouseMoved(int x, int y ); void mouseDragged(int x, int y, int button); void mousePressed(int x, int y, int button); void mouseReleased(); //クラスParticleの動的配列particles vector <Particle> particles; };
  140. 140. 応用編:パーティクルをどんどん追加 ‣ 実装ファイル:testApp.cpp #include "testApp.h" void testApp::setup(){! ! ofSetVerticalSync(true); ! ofSetFrameRate(60); ! ofBackground(0, 0, 0); } void testApp::update(){ for (int i = 0; i < particles.size(); i++) { particles[i].resetForce(); particles[i].addDampingForce(); particles[i].update(); } } void testApp::draw(){ ! ofSetColor(255, 255, 255); ! //画面左上にメッセージを表示 ! string message = "current particle num = " + ofToString(particles.size(),0); ! ofDrawBitmapString(message, 20, 20); for (int i = 0; i < particles.size(); i++) { particles[i].draw(); } }
  141. 141. 応用編:パーティクルをどんどん追加 ‣ 実装ファイル:testApp.cpp (つづき) void testApp::keyPressed (int key){ ! //'c'キーでパーティクルを全部消去 ! if (key == 'c') { ! ! particles.clear(); ! } ! //'f'キーでフルスクリーン表示 ! if (key == 'f') { ! ! ofToggleFullscreen(); ! } } ...《中略》... void testApp::mouseDragged(int x, int y, int button){ ! //マウスをドラッグするとパーティクルが追加される ! Particle myParticle; ! float vx = ofRandom(-3, 3); ! float vy = ofRandom(-3, 3); ! myParticle.setInitialCondition(x, y, vx, vy); ! particles.push_back(myParticle); } ...《後略》...
  142. 142. 応用編:パーティクルをどんどん追加 ‣ 完成:画面上をドラッグしてパーティクルを追加
  143. 143. 実習:パーティクルをつかって表現 ‣ Particleクラスと動的配列をつかって、表現してみる ‣ Patricleクラス自体を自由にカスタマイズしてもOK ‣ Particleの位置情報を活用 ‣ 入力の方法を工夫する ‣ Particleの動きを変更 ‣ 色や形を工夫 ‣ ...etc
  144. 144. 実習:パーティクルをつかって表現 ‣ 応用例その1: ‣ Particleの位置に点を描くのではなく、それぞれの点を結ぶ カーブを描く ‣ ofCarbeVertex() をつかう ‣ testApp.cpp の draw()を書き換える
  145. 145. 実習:パーティクルをつかって表現 ‣ 実装ファイル:testApp.cpp (drawのみ書き換え) ...《中略》... void testApp::draw(){ ! ofSetColor(255, 255, 255); ! //画面左上にメッセージを表示 ! string message = "current particle num = " ! + ofToString(particles.size(),0); ! ofDrawBitmapString(message, 20, 20); ! ! ofNoFill(); ! ofBeginShape(); ! for (int i = 0; i < particles.size(); i++){ ! ! ofCurveVertex(particles[i].pos.x, particles[i].pos.y); ! } ! ofEndShape(); } ...《後略》...
  146. 146. 実習:パーティクルをつかって表現 ‣ 完成:画面上をドラッグして線を描く
  147. 147. 実習:パーティクルをつかって表現 ‣ 応用例その2: ‣ ビットマップ画像でパーティクルを描く ‣ ofImageを使うと、外部画像ファイルを読みこんで表示可能 ‣ ofImageの詳細の解説は、また別途行う予定 ‣ パーテイクルの位置に、ビットマップ画像を配置していく ‣ X座標 → particles[i].pos.x; ‣ Y座標 → particles[i].pos.y;
  148. 148. 実習:パーティクルをつかって表現 ‣ ヘッダーファイル:testApp.h #pragma once #include "ofMain.h" #include "Particle.h" class testApp : public ofSimpleApp{ ! public: void setup(); void update(); void draw(); void keyPressed (int key); void keyReleased (int key); void mouseMoved(int x, int y ); void mouseDragged(int x, int y, int button); void mousePressed(int x, int y, int button); void mouseReleased(); //クラスParticleの動的配列particles vector <Particle> particles; ! //ビットマップ画像 ! ofImage img; };
  149. 149. 実習:パーティクルをつかって表現 ‣ 実装ファイル:testApp.cpp (1 / 3) #include "testApp.h" void testApp::setup(){! ! ofSetVerticalSync(true); ! ofSetFrameRate(60); ! ofBackground(0, 0, 0); ! ofEnableBlendMode(OF_BLENDMODE_ADD); ! //イメージファイルを読込み ! img.loadImage("particle32.png"); } void testApp::update(){ for (int i = 0; i < particles.size(); i++) { particles[i].resetForce(); particles[i].addDampingForce(); particles[i].update(); } }
  150. 150. 実習:パーティクルをつかって表現 ‣ 実装ファイル:testApp.cpp (2 / 3) void testApp::draw(){ ! //画面左上にメッセージを表示 ! ofSetColor(255, 255, 255); ! string message = "current particle num = " + ofToString(particles.size(),0); ! ofDrawBitmapString(message, 20, 20); ! //パーティクルの位置に画像を表示 ! for (int i = 0; i < particles.size(); i++){ ! ! float posx = particles[i].pos.x - 16; ! ! float posy = particles[i].pos.y - 16; ! ! img.draw(posx, posy); ! } } void testApp::keyPressed (int key){ ! //'c'キーでパーティクルを全部消去 ! if (key == 'c') { ! ! particles.clear(); ! } ! //'f'キーでフルスクリーン表示 ! if (key == 'f') { ! ! ofToggleFullscreen(); ! } }
  151. 151. 実習:パーティクルをつかって表現 ‣ 実装ファイル:testApp.cpp (3 / 3) 【中略】 void testApp::mouseDragged(int x, int y, int button){ ! //マウスをドラッグするとパーティクルが追加される ! Particle myParticle; ! float vx = ofRandom(-1, 1); ! float vy = ofRandom(-1, 1); ! myParticle.setInitialCondition(x, y, vx, vy); ! particles.push_back(myParticle); } 【後略】
  152. 152. 実習:パーティクルをつかって表現 ‣ 完成:画面上をドラッグすると画像が表示される
  153. 153. 休憩

×