Likeボタンの実験
ちょっと実験用にポスト。
ちょっと実験用にポスト。
プログラムで、値を記憶して必要なときに利用するには、変数(variable)を使います。変数を直感的に理解するには、1つの値を格納することのできる箱をイメージすると良いでしょう。この箱を利用するためには、まず箱の名前と箱の中にいれるデータの種類を決めます。これを「変数の宣言」といいます。箱が用意できたら、この箱の中に実際のデータを格納し箱の名前と関連づけます。このデータを箱の中に入れる操作を「代入」といいます。一度箱の中に入れたら、後は箱の名前を指定することで、箱の中身を見ることが可能となります。この箱の名前からデータの中身を見ることを変数の「参照」といいます。
変数を宣言する際には、プログラムの中でどのような名前で変数を用いるのかという名前の宣言と、どのような種類の変数を用いるのかたという情報をあわせて宣言します。この変数の種類のことを「データ型」と呼びます。ActionScript 3.0でよく用いられるデータ型としては以下のようなものがあります。
変数を使用するにはまず変数名と型を指定して宣言する必要があります。ActionScript 3.0で変数を宣言するには、以下のような書式を使用します。
[sourcecode language=”as3″]
var 変数名:型;
[/sourcecode]
例えば、以下のように使用します。
[sourcecode language=”as3″]
var i:int;
var str:String;
var bTest:Boolean;
[/sourcecode]
宣言した変数に値を代入するには、代入演算子「=」を用います。代入演算子は数学でいう等号とは意味が異るので注意が必要です。数学での等号は、イコール「=」を挟んで左辺と右辺は等しいという意味になります。しかし、プログラミングにおいては、イコール「=」を挟んだ場合、右辺の値を左辺の変数に代入する、という意味となります。
[sourcecode language=”as3″]
package {
import flash.display.Sprite;
public class Main extends Sprite {
public function Main() {
// 整数型の変数xを宣言
var x:int;
// 実数型の変数yを宣言
var y:Number;
// 整数の代入:変数xに10を代入
x=10;
// 式の代入:xに0.5加えた値をyに代入、結果として、y = 10.5 となる
y=x+0.5;
// y自身を更新:変数yに2を加える
y=y+2;
// 結果:x = 10, y = 12.5 になるはず
trace("x = " + x + ", y = " + y);
}
}
}
[/sourcecode]
次に変数を利用して図形を描いてみましょう。
まず、先週の授業でやった方法で、FLAファイル(AS3)を作成し、ドキュメントクラスを「Main」に設定します。その上で新規にムービークリップを作成して円を描き、作成したムービークリップにMyCircleというクラスを設定します。
次に、ActionScript 3.0ファイルとして、Main.asをFLAファイルと同じ場所に新規に作成します。以下のようなプログラムを作成してみましょう。
Main.as
[sourcecode language=”as3″]
package {
import flash.display.Sprite;
public class Main extends Sprite {
public function Main() {
//変数を宣言
var locX:Number;
var locY:Number;
var circleAlpha:Number;
//変数の初期値を設定
locX=150;
locY=200;
circleAlpha=1.0;
//円1を描画
var circle1:MyCircle = new MyCircle();
circle1.x=locX;
circle1.y=locY;
circle1.alpha=circleAlpha;
addChild(circle1);
//変数を更新
locX=locX+80;
circleAlpha=circleAlpha-0.2;
//円2を描画
var circle2:MyCircle = new MyCircle();
circle2.x=locX;
circle2.y=locY;
circle2.alpha=circleAlpha;
addChild(circle2);
//変数を更新
locX=locX+80;
circleAlpha=circleAlpha-0.2;
//円3を描画
var circle3:MyCircle = new MyCircle();
circle3.x=locX;
circle3.y=locY;
circle3.alpha=circleAlpha;
addChild(circle3);
//変数を更新
locX=locX+80;
circleAlpha=circleAlpha-0.2;
//円4を描画
var circle4:MyCircle = new MyCircle();
circle4.x=locX;
circle4.y=locY;
circle4.alpha=circleAlpha;
addChild(circle4);
}
}
}
[/sourcecode]
宣言した変数の値(locX:X座標、locY:Y座標、circleAlpha:透明度)を更新しながら円をくりかえし描画することで、すこしずつ変化する円を順番に描いています。
/wp-content/uploads/2010/10/Main1.swf, 550, 400
現状のサンプルのプログラムを注意深く観察すると、似たような記述の繰り返しになっていることに気がつきます。10回程度までの繰り返しであれば、ここまでのやり方のように繰り返しの数だけ似たような命令を記述しても良いのですが、例えばこれが100回の繰り返し、1000回の繰り返しといったように、繰り返しの数を増やしていった場合、現在のやり方では破綻してしまいます。
この似通った繰り返しの部分を、プログラミングの記述を工夫してすっきりとまとめることが可能です。ActionScript 3.0では、「for文」という文を用いて、繰り返しの構造を記述します。
for文の基本的な構文は以下のようになります。
[sourcecode language=”as3″]
for(《初期化》; 《ループの継続条件》; 《カウンタ変数の更新》){
《文》
}
[/sourcecode]
例えば、カウンター変数「i」を用意して、iが0から99まで、合計100回くりかえして同じ処理をする繰り返し文を書く場合は、以下のような構文となります。
[sourcecode language=”as3″]
var i:int;
for(i = 0; i < 100; i = i + 1){
《繰り返す処理の内容》
}
[/sourcecode]
カウンタ変数」とは、for文が繰り返されるたびにその繰り返し回数を記録するカウンタのような役割を果たす変数です。
この時変数iは、for文の中で繰り返し処理が1回行われるごとに、1つ増加していきます。この変数iを効果的に用いることで、繰り返し回数に応じて、パラメータを変化させることも可能となります。
さきほどの変数を用いた円の描画のプログラムを注意深く眺めてみると、同じ操作のくりかえしになっている部分があることに気付くでしょう。
この、くりかえしの部分をfor文を用いて短く書き直してみたいと思います。
Main.cpp
[sourcecode language=”as3″]
package {
import flash.display.Sprite;
public class Main extends Sprite {
public function Main() {
//変数を宣言
var locX:Number;
var locY:Number;
var circleAlpha:Number;
var i:int;
//変数の初期値を設定
locX=50;
locY=200;
circleAlpha=1.0;
//10回くりかえし
for (i = 0; i < 10; i = i + 1) {
//円を描画
var circle:MyCircle = new MyCircle();
circle.x=locX;
circle.y=locY;
circle.alpha=circleAlpha;
addChild(circle);
//変数を更新
locX=locX+50;
circleAlpha=circleAlpha-0.1;
}
}
}
}
[/sourcecode]
/wp-content/uploads/2010/10/Main2.swf, 550, 400
もう少し工夫して、くりかえしを効果的に使用した表現についてトライしてみましょう。
単純な四角形ではなく、下記のような図形を描いて、「MyMc」というクラス名でムービークリップを作成します。
この図形のX座標を少しずつ左から右に変化させながら、少しずつ回転をしていきたいと思います。
Main.as
[sourcecode language=”as3″]
package {
import flash.display.Sprite;
public class Main extends Sprite {
public function Main() {
//カウンタ用の変数iを宣言
var i:int;
//200回くりかえす
for (i = 0; i < 200; i++) {
//MyMcのインスタンスを生成
var mc:MyMc = new MyMc();
//ムービクリップの位置と回転角度を設定
mc.y=200;
mc.alpha=0.7;
mc.x=5*i;
mc.rotation=6*i;
//ステージに表示
addChild(mc);
}
}
}
}
[/sourcecode]
このスクリプトを実行すると、変化させる回転角度に応じて様々なパターンが描きだされます。
X座標の増加:5pixel、角度の増加:6度
/wp-content/uploads/2010/10/Main5.swf, 550, 400
X座標の増加:5pixel、角度の増加:45度
/wp-content/uploads/2010/10/Main3.swf, 550, 400
回転しながら横に移動するサンプルをさらに繰り返して、上から下に描くことで、空間を埋めつくすようにパターンを描くことができます。くりかえしをくりかえすには、for文を2重に入れ子状にすることで実現可能です。
Main.cpp
[sourcecode language=”as3″]
package {
import flash.display.Sprite;
public class Main extends Sprite {
public function Main() {
var i:int;
var j:int;
for (j = 0; j<10; j++) {
for (i = 0; i < 200; i++) {
var rect:MyRect = new MyRect();
rect.alpha=0.7;
rect.x=5*i;
rect.y=80*j;
rect.rotation=93*i;
addChild(rect);
}
}
}
}
}
[/sourcecode]
/wp-content/uploads/2010/10/Main4.swf, 550, 400
今日とりあげたサンプルは全て下記からダウンロード可能です。
前回の授業では、見出し、段落、改行、インライン画像をWebページで使用するための要素を紹介しました。これらの要素を組み合せて、文章を中心にしたWebページを作成することが可能となりました。
今日の授業では、より情報をわかりやすく表現するための手段として、「箇条書き」によって情報を表現する「リスト」と、表を作成する「テーブル」に関係する要素について紹介したいと思います。これらの要素を正しく効果的に用いることで、文章だけで説明するよりも、よりわかり易く情報を表現するためのバリエーションになるでしょう。
まず、リストに関係する要素について解説します。リストとは箇条書きで情報を表現する手段です。リストは短い文字量でも、表現したい論旨を的確にわかり易く表現することが可能となります。また、視覚的にも読み易くなるという効果も期待できます。
HTMLのリストには大きくわけて3種類のタイプが存在ます。項目の順番にはあまり大きな意味を持たず、並列的に列挙する「並列列挙リスト」と、項目の順番が意味を持たせたり、もしくは項目をあとで参照するための番号をつける「順序付きリスト」、そして辞書のように用語とその定義から構成される「定義型リスト」の3種類です。
「並列列挙リスト」には「ul (= Unorderd List)」要素、「順序付きリスト」には「ol (= Orderd List)」要素、「定義型リスト」には「dl (= Definition List)」要素を用います。
リストをマークアップするには、まずその箇条書きする要素全体を「ul要素」もしくは「ol要素」としてマークアップします。その上で、列挙するそれぞれの要素を「li要素 (List Item)」といてマークアップします。
並列列挙リスト (ul要素) の例
<ul>
<li>ドはドーナッツのド</li>
<li>レはレモンのレ</li>
<li>ミはみんなのミ</li>
<li>ファはファイトのファ</li>
<li>空を仰いで</li>
<li>ラララララ</li>
<li>シは幸せよ</li>
</ul>
順序付きリスト (ol要素) の例
<ol>
<li>一本でも人参</li>
<li>二足でもサンダル</li>
<li>三艘でもヨット</li>
<li>四粒でもごま塩</li>
<li>五台でもロケット</li>
<li>六羽でも七面鳥</li>
<li>七匹でも蜂</li>
<li>八頭でもクジラ</li>
</ol>
定義型リストは、その全体を「dl要素 (= Definition List)」で囲んでマークアップします。その中に、用語をマークアップする「dt要素 (= Definition Title)」と、その用語の定義「dd要素 (= Definition Description)」から構成されます。
定義型リストの例
<dl>
<dt>HTML</dt>
<dd>Hyper Text Markup Languageの略。Webページを記述するためのマークアップ言語。</dd>
<dt>HTTP</dt>
<dd>Hyper Text Transfer Protcol。Webサーバとクライアントがデータを送受信するのに使われるプロトコル。</dd>
<dt>URL</dt>
<dd>Uniform Rosource Locator。インターネット上の情報の場所を指定する。</dd>
</dl>
リストはまた、複数のレベルを持つことが可能です。リストのレベルを表現するには、リストの項目の中に、新たなリスト(ul要素、またはol要素)を入れ子状にして、多重構造のリストを記述していくことで表現か可能です。
多重構造のリストの例
<ul>
<li>リスト1
<ul>
<li>リスト1.1</li>
<li>リスト1.2</li>
<li>リスト1.3</li>
</ul>
</li>
<li>リスト2
<ul>
<li>リスト2.1</li>
<li>リスト2.2</li>
<li>リスト2.3</li>
</ul>
</li>
</ul>
リストが多重構造になると、開始タグと終了タグの関係性が複雑に組み合わされます。必ず開始した要素は終了しているように、またその対応の順番が混乱しないように、インデントを使用しながら整理して記述するように心掛けましょう。
HTMLで行と列から成る「表」で情報を表現する際には「table要素」を使用します。HTMLでは、表(= table要素)の中に1つ以上の「行(縦の並び)」があり、その行の中に1つ以上の項目(セル、データ)があると考えます。
table要素の中の行を「tr (= Table Row) 要素」、セルを「td (= Table Data) 要素」でマークアップします。
では、簡単な表を作成してみましょう。
<table border=”3″ cellpadding=”5″>
<tr>
<th>県名</th>
<th>県庁所在地</th>
<th>面積</th>
<th>総人口</th>
</tr>
<tr>
<td>東京都</td>
<td>東京</td>
<td>2,187.65km²</td>
<td>13,044,818人</td>
</tr>
<tr>
<td>千葉県</td>
<td>千葉市</td>
<td>5,156.60km²</td>
<td>6,200,335人</td>
</tr>
<tr>
<td>埼玉県</td>
<td>さいたま市</td>
<td>3,797.25km²</td>
<td>7,189,176人</td>
</tr>
</table>
県名 | 県庁所在地 | 面積 | 総人口 |
---|---|---|---|
東京都 | 東京 | 2,187.65km² | 13,044,818人 |
千葉県 | 千葉市 | 5,156.60km² | 6,200,335人 |
埼玉県 | さいたま市 | 3,797.25km² | 7,189,176人 |
Webページの体裁(デザイン)やレイアウトをする、スタイルシートという仕組みが整っていなかった時代には、table要素を使用してページのレイアウトを行うことが一般的に行われていました。テーブル要素を用いて、ページヘッダーやフッター、サイドメニューなどの各要素をグリッド状に配置していくというアプローチです。
しかしながら、現在はこのやり方は推奨されておらず、レイアウトにtable要素を用いるべきではないという考え方が一般的です。レイアウトの手段としてtable要素を使用するということは、さまざまなデメリットがあります。
こうした様々なデメリットから、table要素をレイアウトの手段として使用することは、現在では賢いやり方とはいえません。この授業でもtable要素はあくまで「表形式」のデータを表現するための手法として使用するに留め、ページ全体のレイアウトや細かな位置の調整は授業の後半で学ぶCSS(カスケーディング・スタイルシート)という仕組みで一括して行うようにします。
今日の授業では、リストと表という形式を用いて、情報を整理して表現する手法について学びました。リストや表をその意味や構造に忠実に、効果的に用いることで、情報を視覚的にわかり易く表現することが可能となります。
オブジェクト指向プログラミング(OOP)とは相互にメッセージを送りあうオブジェクトの集まりとしてプログラムを構成するプログラミングの技法です。現在使用されているプログラミング言語の多くは、OOPのパラダイムをベースに設計されていて、現在主流のプログラミングの手法と言えるでしょう。オブジェクト指向プログラミング言語を列挙してみると、Smalltalk、C++、Objective-C、Python、Ruby、Perl(5.0以降)、Java、JavaScript、C#など数多くの言語があげられます。
openFrameworksはC++をベースにしたフレームワークです。ですので、openFrameworksもオブジェクト指向のプログラミング言語です。OOPの利点を最大限に利用することで、より効率的に強力なプログラムをしていくことが可能となります。
「プロパティ」「メソッド」「インスタンス」「カプセル化」「ポリモーフィズム」など慣れない言葉が頻出するので、最初はとまどうかもしれませんが、その考え方の根本はシンプルなものです。今回はopenFrameworksを用いてOOPをしていくための基本についてじっくりとやっていきたいと思います。
このセクションでは、まずオブジェクト指向プログラミングの根幹を成す「オブジェクト」という概念自身について考えていきます。
オブジェクト指向プログラミングとは、オブジェクトというプログラム機能の部品の集合が、相互にメッセージを送りあいながらプログラムを構成していくというプログラミング技法です。それぞれのオブジェクトは独立していて、自分自身で値や処理手順を保持しています。オブジェクトの一つ一つが、小さなプログラムのモジュールあると考えられます。そうした小さなモジュール同士がが相互にメッセージを送りあうことで大きなプログラムを構成するというのが、オブジェクト指向の基本的な考えかたとなります。
オブジェクト指向の考え方を、より日常的で具体的な例で考えてみましょう。オブジェクトは日本語にすると「物」です。オブジェクト指向プログラミングの構成単位であるこのオブジェクトは、私達の周囲にある物で例えてみることができます。オブジェクト指向プログラミングでは、物を二つの観点から整理します。一つはその物固有の「属性」、もう一つはその物自身に対する「動作」です。属性のことを「プロパティ」、動作のことを「メソッド」と言い換えることもできます。この複数のプロパィとメソッドから構成されるオブジェクトの概念を図示すると、次のようになります。
この考えかたは、実際に身の回りのものに当てはめてみるとより容易に理解可能になるでしょう。
もちろん、現実の犬やテレビやリンゴはこんなに単純ではありません。もっと沢山の属性や動作を持っています。しかし重要なことは、物の全ての性質を写しとるのではなく、プログラムで必要となる属性と動作を抽出するという操作です。プログラムの中のオブジェクトは、あくまでも物そのものではなく、現実世界の中からそのプログラムの機能で必要なものだけをとりだした抽象化された「物 = オブジェクト」なのです。
では、どのようにしたらオブジェクトをプログラム内に生成することができるのでしょうか。オブジェクト指向プログラミングでは、オブジェクトを生成するには、まずその設計図を作成しなければなりません。この設計図のことを「クラス」と呼びます。クラスに属性と動作の詳細を記述することで、オブジェクトのふるまいを決定します。
クラスは設計図なので、そのままではプロブラムのモジュールとして動作することはできません。クラスを実際に使用するには、クラスをオブジェクトとして生成する必要があります。この操作を「インスタンス化」といいます。
クラスからインスタンス化されるオブジェクトは一つとは限りません。一つのクラスから、必要に応じて複数のオブジェクトを生成することも可能です。例えば、車の設計図を一つ作成すれば、それを元にして何台でも車を製造することが可能となるということと同じです。このようにして、共通の属性を持ち同じ動作が可能なオブジェクトを、一度に沢山生成することが可能となります。
カプセル化とは、外部からアクセスする必要の無い場合、オブジェクトのメソッドやプロパティを外部からは見えないように隠す(隠蔽)という概念です。このことにより、プログラムの変更に対する耐久性が上がったり、プログラムの抽象度が上がるという利点があります。
これは時計を例にとるとわかりやすいかもしれません。我々は時計を利用する際には、時刻を調整する機能(メソッド)や、時刻を知るための針の角度の状態(プロパティ)を知ることができます。しかし、時計の内部でどのような機構によってその機能が実現されているのかは、時計を使用する範囲においては知る必要はありません。ですので時計は必要な機能以外には触れることができないようにその機能を隠しています。これがカプセル化の考え方です。
カプセル化した結果、外部からアクセスできなくなった領域をプライベート(Private)、外部に公開してアクセス可能にした領域をパブリック(Public)と呼びます。
継承とは、既存のクラスから、その機能や構造を共有する新たなクラスをサブクラスとして派生することができるという機能です。そのようなクラスは「親クラス(スーパークラス)を継承した」と表現します。
例えば、「車」という既存のクラスを派生して「パトカー」「消防車」「タクシー」といった、その機能を共有し新たな機能を付加したクラスを生成することが可能となります。
ポリモーフィズムとは、オブジェクトの処理の実態は、メッセージの名前からではなく、オブジェクトごとに決定された方法で異った処理を行うことが可能であるという性質です。
用語が難解なので難しく考えがちなのですが、簡単に言えば「メッセージに対する処理の方法はオブジェクト自身が決めてよい」ということです。例えば、「犬」「猫」「ライオン」というオブジェクトがあったとします。ここに「鳴く」という共通のメッセージをそれぞれ送った際に、犬「ワンワン」、猫「ニャー」、ライオン「ガオー」とそれぞれ異った鳴きかたをします。それぞれのオブジェクトによって異なる鳴き方(メソッド)が決められているからです。これがポリモーフィズムです。
オブジェクト指向プログラミングの考えかたを一通り理解した上で、もう一度、いままで使用してきた testApp.h について眺めてみましょう。
testApp.h
[sourcecode language=”cpp”]
#ifndef _TEST_APP
#define _TEST_APP
#include "ofMain.h"
class testApp : public ofBaseApp{
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(int x, int y, int button);
void windowResized(int w, int h);
};
#endif
[/sourcecode]
testApp.h の6行目に「class testApp : public ofBaseApp」という記述があります。この記述のは「testAppクラスは、公開されたofBaseAppというクラスを継承している」ということを意味しています。つまり、いままで編集してきた「testApp」というものは、ひとつのクラス、つまりオブジェクトの設計図なのです。
また、このtestAppは、ofBaseAppというクラスを継承しています。このofBaseAppクラスには、setup()、update()、draw() などのopenFrameworksの基本的な構造に関する動作(メソッド)、また keyPressed()、mouseMoved()、mouseReleased() などのインタラクションに関する動作(メソッド)が定義されています。実は今までtestAppで行ってきたのは、このofBaseAppクラスの機能を共有して、サブクラスとしてさらに新たな機能を付加するという作業であったと捉えることもできます。
オブジェクトの説明のところで、オブジェクトはプロパティ(= 属性、状態)とメソッド(= 動作、ふるまい)から構成されると解説しました。では、実際にクラスをC++で記述する際にはこれらのプロパティとメソッドはどう記述されるのでしょうか。
実際にはとても単純で、C++では、クラスの属性は変数で定義します。変数は一定の値を格納できる機能をもっているからです。また、クラスのメソッドは関数で表現します。関数はプログラムの一連の処理をまとめて表現している機能だからです。つまり、C++のプログラムにおいては、クラス共通の変数はクラスのプロパティ、関数はメソッドをあらわしていると考えます。
C++では、あらかじめ宣言されていない変数や関数を使用することができません。C++では、あらかじめクラスで使用される変数(クラスのプロパティに相当)や関数(クラスのメソッドに相当)を宣言してそれを共有して使用できるようにヘッダーファイルというものを作成して、そこに宣言をまとめることが一般的です。言い換えると、ヘッダーファイルにはそのクラスの機能の概要が書かれていると読みとることも可能です。ヘッダーファイルの拡張子は「.h」になります。例えば、testAppクラスのヘッダーファイルはtestApp.hとなります。
ヘッダファイルに変数(= プロパティ)や関数(= メソッド)を記述する場合、それが公開されているのか(public)、それとも隠蔽されているのか(private)によって分けて記述されます。これがカプセル化の機能に相当します。publicの変数や関数は、「public:」という記述の後に、privateの変数や関数は「private:」という記述の後に宣言します。
ソースファイルは、そのヘッダーファイルで宣言された情報を元に、実際の処理の内容を記述していきます。拡張子は「.cpp」です。testAppクラスであれば、testApp.cppとなります。
XCodeでopenFrameworksのプロジェクトを開き、プロジェクトのアイコンを選択した上で、メニューから「設計」→「クラスモデル」→「クイックモデル」を選択します。すると、自動的に現在の状態を図に整理されます。この生成される図を、「UMLクラス図」と呼びます。それぞれの四角のブロックがひとつのクラスを表しています。クラスの中は3つの領域に分割されていて、上からそのクラスのクラス名、属性(プロパティ)、動作(メソッド)が記述されています。また、それぞれのクラスの継承関係も図の中で表現されています。
では、いよいよopenFrameworksで、オリジナルのカスタムクラスを追加して、それをtestAppから呼びだして操作してみましょう。
まずは簡単な例から、徐々に複雑なサンプルへと変化さえていきたいと思います。
まずはじめに、円を表示するだけのシンプルな機能のクラスを作成してみたいと思います。円を表示するために下記のようなクラスを設計してみます。
クラスを作成するには、まずXCode左コラムの「グループとファイル」のリストの中から「src」フォルダを右クリックします。表示されるメニューから「追加」→「新規ファイル」を選択します。
すると新規ファイルのテンプレートを選択する画面が表示されます。まず、「C and C++」>「C++ File」を選択し、ファイル名を「MyCircle.cpp」にします。その下にある「同時に”MyCircle.h”も作成」のチェックボックスをチェックします。保存場所やその他の設定はそのままで、「完了ボタン」を押します。この操作で、Sourceフォルダの中にMyCircle.hとMyCircle.cppの2つのファイルが追加されます。
まず、クラスのヘッダーファイルを記述します。クラスのヘッダーファイルは、下記のような構造になっています。
クラステンプレート
[sourcecode language=”cpp”]
#ifndef /* ユニークな名前 */ //インクルードガード
#define /* ユニークな名前 */
#include "ofMain.h" //ofMain.hをインクルード
class /* クラス名 */ { //クラスの開始
public:
//公開のプロパティ、メソッドを宣言する
private:
//非公開のプロパティ、メソッドを宣言する
}; //セミコロンを忘れずに!!
#endif //インクルードガードの終了
[/sourcecode]
最初の2行は「インクルードガード」と呼ばれる仕組みです。これはコンパイルする際にヘッダーファイルを二重に読み込んでしまわないようにするための仕組みです。#ifdef と #define の後ろには大文字の英文字とアンダーバー「_」を組み合わせて、そのクラスを定義する一意の名前を命名します。例えばMyCircleクラスの場合は「_MY_CIRCLE」などとするのが良いでしょう
また、openFrameworksの機能を利用したクラスを生成する場合には、必ず include文を使用して「ofMain.h」を読み込むようにします。この「ofMain.h」ファイルはopenFrameworksの様々な機能(アニメーション基本機能、図形の描画、音声、動画、フォントなど)を一括して読みこむためのヘッダーファイルとなっています。
では、このテンプレートにMyCircle.hの情報をあてはめてみましょう。
MyCircle.h
[sourcecode language=”cpp”]
#ifndef _MY_CIRCLE
#define _MY_CIRCLE
#include "ofMain.h"
class MyCircle {
public:
private:
};
#endif
[/sourcecode]
では、ヘッダーファイルに円を描画するメソッドdraw()を追加してみましょう。draw() メソッドは、外部から参照される必要があるので公開(public)の関数として記述します。
ヘッダーファイルの関数は、下記のような記述のルールになっています。
[sourcecode language=”cpp”]
戻り値の型 関数名(引数1, 引数2, 引数3…);
[/sourcecode]
引数がない場合は、空白の括弧 () にします。また関数の戻り値がない場合は、「void」という記述を戻り値の型に記述します。このルールを守って、ヘッダーファイルにdraw() メソッドを記入します。
MyCircle.h
[sourcecode language=”cpp”]
#ifndef _MY_CIRCLE
#define _MY_CIRCLE
#include "ofMain.h"
class MyCircle {
public:
void draw();
private:
};
#endif
[/sourcecode]
次にヘッダーファイルで設計した情報に基いて、実際の処理内容を記述するソースファイルを記述します。ソースファイル内のメソッド(関数)は、「戻り値の型 クラス名::関数名(引数1, 引数2, 引数3 …) { 処理の内容 }」というような記述をする必要になります。例えば、MyCircleクラスのdrawメソッドの場合は下記のようになります。
[sourcecode language=”cpp”]
void MyCircle::draw()
{
/* 処理の内容 */
}
[/sourcecode]
また、ソースファイルの先頭には必ず、include文を使用して自身のヘッダーファイルを読み込むようにします。
メソッド draw() の処理はまずはシンプルに ofSetColorで色を指定して、ofCircleで画面の中心に円を描画するようにしてみましょう。
MyCircle.cpp
[sourcecode language=”cpp”]
#include "MyCircle.h"
void MyCircle::draw()
{
//色を指定
ofSetColor(31, 127, 255);
//画面の中央に円を描く
ofCircle(ofGetWidth()/2, ofGetHeight()/2, 100);
}
[/sourcecode]
XCodeの機能として、現在作成しているプロジェクトのクラス図を自動作成する機能が搭載されています。この機能を利用して現在のクラズ図を書出してみましょう。XCodeのメニューから「設計」→「クラスモデル」→「クイックモデル」を選択します。すると、しばらくレンダリングした後にクラス図が生成されます。
これでMyCircleクラスの実装は完了です。
クラスの実装は完了したのに、現在のプロジェクトをビルドしてみても画面には何も表示されません。冒頭のオブジェクト指向プログラミング解説に書いたようにクラスというのはあくまで設計図に過ぎません。クラスはインスタンス化という処理を行うことにより実体化して始めて、実際に機能するプログラムの機能単位として動きます。作成したクラスをインスタンス化するには、メインのクラスであるtestAppクラスからインスタンス化の処理をする必要があります。
実際のインスタンス化の処理は簡単です。まずメインのクラスtestAppとMyCircleの定義を関連させるために、testApp.hの冒頭で、include文を使用してMyCircleのヘッダーファイルであるMyCirle.hを読み込みます。
その上で、変数を定義するようにクラスを宣言することでインスタンス化は完了します。例えばMyCircleクラスをインスタンス化して、circleというインスタンス(実体)を生成するには下記のように行います。
[sourcecode language=”cpp”]
MyCircle circle;
[/sourcecode]
ではtestApp.hにMyCircleのインスタンス化の処理を加えてみましょう。
testApp.h
[sourcecode language=”cpp”]
#ifndef _TEST_APP
#define _TEST_APP
#include "ofMain.h"
#include "MyCircle.h"
class testApp : public ofBaseApp{
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(int x, int y, int button);
void windowResized(int w, int h);
//MyCircleクラスをインスタンスcircle
MyCircle circle;
};
#endif
[/sourcecode]
クラスのインスタンス化が完了したので、そのインスタンスのメソッドを呼び出すことでクラス内で定義された処理が実行されます。インスタンスのメソッドを呼びだすには、下記のように記述します。
[sourcecode language=”cpp”]
インスタンス名.関数名(引数1, 引数2, 引数3…)
[/sourcecode]
この書式に従って、testAppのdraw()関数より、MyCircleのdraw()メソッドを呼びだして円を描画してみましょう。
testApp.cpp
[sourcecode language=”cpp”]
#include "testApp.h"
//————————————————————–
void testApp::setup(){
ofBackground(0, 0, 0);
ofSetCircleResolution(64);
}
//————————————————————–
void testApp::update(){
}
//————————————————————–
void testApp::draw(){
//MyCircleクラスのインスンタンスcircleのdraw()メソッドを実行
circle.draw();
}
/* 後略 */
[/sourcecode]
また、プロジェクトのUMLクラス図も更新されています。testAppクラスにMyCircleのインスタンスcircleがプロパティとして加わりました。
では、このMyCircleで描画される円の色、位置、半径をクラスの公開されたプロパティとして定義して、testAppから操作できるようにしてみましょう。それぞれのプロパティの名前と型を下記のように定義するものとします。
プロパティを操作するには、公開された(public)プロパティを直接操作する方法と、非公開の(private)プロパティを、メソッドを介して操作するという2種類の方法があります。
シンプルな方法は、操作したいプロパティをPublicとして宣言し、直接操作する方法です。Publicなプロパティを操作する方法はメソッドの実行とよく似ています。
[sourcecode language=”cpp”]
インスタンス名.プロパティ = 設定する値;
[/sourcecode]
この方法で、testAppから、MyCircleのcolor、pos、radiusのそれぞれを操作してみましょう。
MyCircle.h
[sourcecode language=”cpp”]
#ifndef _MY_CIRCLE
#define _MY_CIRCLE
#include "ofMain.h"
class MyCircle {
public:
//円を描く
void draw();
//円の色
ofColor color;
//円の位置
ofPoint pos;
//円の半径
float radius;
private:
};
#endif
[/sourcecode]
MyCircle.cpp
[sourcecode language=”cpp”]
#include "MyCircle.h"
void MyCircle::draw()
{
//colorで指定された色を塗る
ofSetColor(color.r, color.g, color.b);
//指定した場所に、指定した半径で円を描画
ofCircle(pos.x, pos.y, radius);
}
[/sourcecode]
testApp.h はそのまま変化なし。
testApp.cpp
[sourcecode language=”cpp”]
#include "testApp.h"
//————————————————————–
void testApp::setup(){
ofBackground(0, 0, 0);
ofSetCircleResolution(64);
//MyCircleのインスタンスcircleのプロパティを設定
//位置
circle.pos = ofPoint(200, 200);
//色
ofColor col;
col.r = 255;
col.g = 127;
col.b = 31;
circle.color = col;
//半径
circle.radius = 300;
}
//————————————————————–
void testApp::update(){
}
//————————————————————–
void testApp::draw(){
//MyCircleクラスのインスンタンスcircleのdraw()メソッドを実行
circle.draw();
}
/* 後略 */
[/sourcecode]
クラスMyCircleにプロパティが3つ追加されました。その結果として、このプログラムのUMLクラス図は下記のようになります。
プログラムを実行すると、指定した色、位置、半径で円が描画されます。
クラスのプロパティを操作する方法として、もう一つの手法を紹介します。クラスのメソッドを介してPrivateなプロパティにアクセスするという方法です。プロパティを取得したり設定したりするメソッドのことを、アクセサ(Accessor)と呼びます。また、取得するメソッドのことをgetter、設定するメソッドはsetterと呼び、それぞれgetXXX、setXXXという関数名をにするのが一般的です。
アクセサを用いる利点はいくつか挙げられます。
これらの利点を考えると、多少面倒でもアクセサを利用するほうが良いでしょう。先程の3つのプロパティ、color、pos、radiusを、アクセサを介して設定できるようにしましょう。今回は値を設定するだけなので、setterだけを定義してみます。それぞれsetColor()、setPos()、setRadius()というメソッドとなります。
MyCircle.h
[sourcecode language=”cpp”]
#ifndef _MY_CIRCLE
#define _MY_CIRCLE
#include "ofMain.h"
class MyCircle {
public:
//円を描く
void draw();
//アクセサ
void setColor(ofColor color);
void setPos(ofPoint pos);
void setRadius(float radius);
private:
//プロパティはprivateで宣言
//円の色
ofColor color;
//円の位置
ofPoint pos;
//円の半径
float radius;
};
#endif
[/sourcecode]
MyCircle.cpp
[sourcecode language=”cpp”]
#include "MyCircle.h"
void MyCircle::draw()
{
//colorで指定された色を塗る
ofSetColor(color.r, color.g, color.b);
//指定した場所に、指定した半径で円を描画
ofCircle(pos.x, pos.y, radius);
}
void MyCircle::setColor(ofColor _color)
{
color = _color;
}
void MyCircle::setPos(ofPoint _pos)
{
pos = _pos;
}
void MyCircle::setRadius(float _radius)
{
radius = _radius;
}
[/sourcecode]
testApp.h はそのまま変化なし。
testApp.cpp
[sourcecode language=”cpp”]
#include "testApp.h"
//————————————————————–
void testApp::setup(){
ofBackground(0, 0, 0);
ofSetCircleResolution(64);
//アクセサを介してcircleのプロパティを設定
//位置
circle.setPos(ofPoint(400, 600));
//色
ofColor col;
col.r = 31;
col.g = 255;
col.b = 127;
circle.setColor(col);
//半径
circle.setRadius(300);
}
//————————————————————–
void testApp::update(){
}
//————————————————————–
void testApp::draw(){
//MyCircleクラスのインスンタンスcircleのdraw()メソッドを実行
circle.draw();
}
/* 後略 */
[/sourcecode]
MyCircleクラスにアクセサが追加されました。URLクラス図も変更されています。
ひとつのクラスから複数のインスタンスを生成することも可能です。方法は簡単で、インスタンス名を変えて、それぞれに対してインスタンス化をするだけです。例えば、MyCircleクラスから3つのインスタンス、circle1, circle2, circle3を生成してみましょう。
MyCircle.hとMyCircle.cppはそのまま変化なし。
testApp.h
[sourcecode language=”cpp”]
#ifndef _TEST_APP
#define _TEST_APP
#include "ofMain.h"
#include "MyCircle.h"
class testApp : public ofBaseApp{
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(int x, int y, int button);
void windowResized(int w, int h);
//MyCircleクラスのインスタンスを3つ生成
MyCircle circle1, circle2, circle3;
};
#endif
[/sourcecode]
testApp.cpp
[sourcecode language=”cpp”]
#include "testApp.h"
//————————————————————–
void testApp::setup(){
ofBackground(0, 0, 0);
ofSetCircleResolution(64);
ofColor col;
//circle1のプロパティ設定
col.r = 31;
col.g = 255;
col.b = 127;
circle1.setColor(col);
circle1.setPos(ofPoint(600, 400));
circle1.setRadius(300);
//circle2のプロパティ設定
col.r = 255;
col.g = 127;
col.b = 31;
circle2.setColor(col);
circle2.setPos(ofPoint(300, 400));
circle2.setRadius(200);
//circle3のプロパティ設定
col.r = 31;
col.g = 127;
col.b = 255;
circle3.setColor(col);
circle3.setPos(ofPoint(800, 200));
circle3.setRadius(150);
}
//————————————————————–
void testApp::update(){
}
//————————————————————–
void testApp::draw(){
//circle1, circle2, circle3それぞれを描画
circle1.draw();
circle2.draw();
circle3.draw();
}
/* 後略 */
[/sourcecode]
UMLクラス図
クラスから生成するインスタンスの数が増えてくると、それぞれインスタンスとして生成して操作していくことに限界が生じてきます。いままで用いてきた複数の変数を配列にする方法と同じ方法で、クラスのインスタンスの配列を使用することも可能です。MyCircleの配列、circlesを生成して一気に大量のクラスを生成してみましょう。
MyCircle.hとMyCircle.cppはそのまま変化なし。
testApp.h
[sourcecode language=”cpp”]
#ifndef _TEST_APP
#define _TEST_APP
#define CIRCLE_NUM 100
#include "ofMain.h"
#include "MyCircle.h"
class testApp : public ofBaseApp{
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(int x, int y, int button);
void windowResized(int w, int h);
//MyCircleクラスのインスタンスの配列を生成
MyCircle circles[CIRCLE_NUM];
};
#endif
[/sourcecode]
testApp.cpp
[sourcecode language=”cpp”]
#include "testApp.h"
//————————————————————–
void testApp::setup(){
ofBackground(0, 0, 0);
ofSetCircleResolution(64);
ofColor col;
//配列の数だけプロパティを設定
for (int i = 0; i < CIRCLE_NUM; i++) {
//ランダムな色に
col.r = ofRandom(0, 255);
col.g = ofRandom(0, 255);
col.b = ofRandom(0, 255);
circles[i].setColor(col);
//ランダムな場所に
circles[i].setPos(ofPoint(ofRandom(0, ofGetWidth()), ofRandom(0, ofGetHeight())));
//ランダムな半径に
circles[i].setRadius(ofRandom(10, 100));
}
}
//————————————————————–
void testApp::update(){
}
//————————————————————–
void testApp::draw(){
//配列の数だけ描画
for (int i = 0; i < CIRCLE_NUM; i++) {
circles[i].draw();
}
}
/* 後略 */
[/sourcecode]
UMLクラス図
今回の授業では、オブジェクト指向プログラミングの概念について解説しました。また概念を理解した上で、openFrameworksでカスタムのクラスを設計し、そのクラスのインスタンスを生成し、プロパティやメソッドを操作するということを行いました。
しかしながら、今回のプログラム例はあくまでオブジェクト指向プログラミングの理解のためのサンプルという意味合いが強く、あまり現実的なものではありませんでした。円を描くというだけの機能であれば、わざわざクラスに分割する必要性はないでしょう。
次回はオブジェクト指向プログラミングの後編ということで、より実践的なプログラムを通して、オブジェクト指向プログラミングの利便性をより強く実感するような内容を取り扱っていく予定です。またその過程でC++における最も難解な部分である、ポインタという概念や、メモリの管理について併せて学んでいきたいと思います。
今日とりあげたプログラムは全て下記のリンクからダウンロードできます。
先週の授業では、ムービークリップの属性(プロパティ)を操作する際に、ActionScriptをタイムラインのキーフレーム内に記述していました。このフレームにActionScriptを書き込む手法をフレームスクリプトと呼んでいます。
フレームスクリプトは手軽にスクリプトを記述できて便利なのですが、いくつかの欠点もあります。
これらの欠点を解決するために、ActionScriptを外部ファイルとして記述する方法について、今日の授業では取り扱っていきます。この手法は将来的により大規模なFlashプロジェクトを制作しようとする際に、とても重要な手法となります。
まず始めに、Flashファイル(AS3.0) 形式で新規にFLAファイルを作成します。FLAファイルが開いたら、何も選択しない状態で、プロパティパネルを開きます。
プロパティパネルのパブリッシュの欄にあるクラスの入力欄に半角英数文字で名前を指定します(例:Main)。ここでつけた名前がドキュメントクラスの名称となります。
ドキュメントクラスとは、作成するFLAファイルのメインのタイムラインに関連付けられたプログラム(正確にはクラス)です。ドキュメントクラスに設定した命令がSWF実行時に最初に起動されます。ドキュメントクラスは、外部ファイルとして作成します。この際にFLAファイルで指定したクラスの名称と同じ名前のファイルにする必要があります。拡張子は「.as」です。
例えば、FLAのクラス欄に「Main」と設定した場合は、ドキュメントクラスのファイル名は「Main.as」になります。
ドキュメントクラスを使用することで、FLAファイルにはActionScriptを一切書き込むことなく、Flashプロジェクトを作成していくことが可能となります。
では早速ドキュメントクラスを作成してみましょう。FLAでクラスを「Main」という名前で設定します。次に「ファイル」→「新規」を選択して、作成する新規ドキュメントの選択画面を表示します。リストの中から「ActionScript (AS) ファイル」を選択します。
作成したActionScriptファイルは、FLAファイルと同じ場所に「Main.as」というファイル名で保存します。
Main.asに下記のActionScriptを記入して、FLAファイルの「制御」→「ムービープレビュー」を実行してみましょう。
[sourcecode language=”as3″]
package {
import flash.display.Sprite;
public class Main extends Sprite {
public function Main() {
trace("こんにちは、世界!");
}
}
}
[/sourcecode]
すると、出力パネルに「こんにちは、世界!」というメッセージが表示されるはずです。これは、SWFが実行される際に正しくドキュメントクラス「Main.as」が読み込まれ実行されたことを意味しています。
先週の授業では、タイムラインスクリプトから、ライブラリに格納したムービークリップシンボルを呼び出してステージに表示するというサンプルについて紹介しました。
これと同じ操作を、ドキュメントクラスから行うことも可能です。やり方はタイムラインスクリプトから呼び出す際と同様に、まず作成したムービークリップにクラス名を割り当て(例:MyCircle)、ドキュメントクラスからそのクラスを呼び出し実体を生成(インスタンス化)します。あとは、addChild() を使用して円のインスタンスをステージ上に配置すると、画面に表示されます。
[sourcecode language=”as3″]
package {
import flash.display.Sprite;
public class Main extends Sprite {
public function Main() {
var circle:MyCircle = new MyCircle();
circle.x=200;
circle.y=150;
this.addChild(circle);
}
}
}
[/sourcecode]
/wp-content/uploads/2010/10/ClassTest02.swf, 400, 300
ライブラリに格納した複数のムービクリップを、それぞれ配置することも簡単です。例えば、丸、四角形、三角形の形をそれぞれMyCircle、MyRect、MyTriangleとうクラス名でMovieClipとしてシンボル化し、ライブラリに格納します。この例では、それをドキュメントクラスから順番に呼び出して、ステージ上に配置しています。
[sourcecode language=”as3″]
package {
import flash.display.Sprite;
public class Main extends Sprite {
public function Main() {
var circle:MyCircle = new MyCircle();
circle.x=200;
circle.y=150;
this.addChild(circle);
var rect:MyRect = new MyRect();
rect.x=140;
rect.y=100;
this.addChild(rect);
var tri:MyTriangle = new MyTriangle();
tri.x=220;
tri.y=200;
this.addChild(tri);
}
}
}
[/sourcecode]
/wp-content/uploads/2010/10/ClassTest03.swf, 400, 300
今日の授業では、FLAファイルとActionScriptファイルを分離する方法として、ドキュメントクラスの作成について学びました。
ドキュメントクラスを使用することで、ActionScriptとFLAファイルを分離することで、FLAファイルはムービークリップや画像、音声などFlashに必要となるリソースを格納するためだけの機能として使用し、その動きやインタラクション、画面遷移などのロジックは全て外部ファイルのActionScriptとしてプロジェクト全体を構成していくことが可能となります。
Flashのデザインに必要なパーツとロジックを分離することで、複数人での共同作業や、大規模で更新可能なFlashプロジェクトの作成などが可能となります。本格的なFlashコンテンツを制作する際には必須のテクニックと言えるでしょう。
今日の授業で紹介したサンプルは下記からダウンロードしてください。
先週は、HTMLの導入としてHTML(= Hyper Text Markup Language)の重要な概念である「マークアップ」について解説しました。またその上で、HTMLのマークアップの基礎、要素と、開始タグ・終了タグについて学び、基本的な要素をいくつか紹介しました。
まず始めに先週学んだ要素について復習してみましょう。
先週学んだ要素を使用して、下記のようなHTML書類を作成することができました。
[sourcecode language=”html”]
<html>
<head>
<title>はじめてのHTML</title>
</head>
<body>
<h1>はじめに</h1>
<p>あのイーハトーヴォのすきとおった風、夏でも底に冷たさをもつ青いそら、うつくしい森で飾られたモーリオ市、郊外のぎらぎら光る草の波。あのイーハトーヴォのすきとおった風、夏でも底に冷たさをもつ青いそら、うつくしい森で飾られたモーリオ市、郊外のぎらぎら光る草の波。</p>
<p>あのイーハトーヴォのすきとおった風、夏でも底に冷たさをもつ青いそら、うつくしい森で飾られたモーリオ市、郊外のぎらぎら光る草の波。あのイーハトーヴォのすきとおった風、夏でも底に冷たさをもつ青いそら、うつくしい森で飾られたモーリオ市、郊外のぎらぎら光る草の波。</p>
<h2>このページについて</h2>
<p>あのイーハトーヴォのすきとおった風、夏でも底に冷たさをもつ青いそら、うつくしい森で飾られたモーリオ市、郊外のぎらぎら光る草の波。あのイーハトーヴォのすきとおった風、夏でも底に冷たさをもつ青いそら、うつくしい森で飾られたモーリオ市、郊外のぎらぎら光る草の波。</p>
</body>
</html>
[/sourcecode]
今日は、このHTMLをより「正確に」宣言するための方法と、さらにいくつかの基本的な要素について学んでいきます。
初回の授業で解説したように、ひとくちにHTMLといっても様々なバージョンが存在します。それぞれのバージョンで使用できる要素や文法規則に差異があります。
様々な環境で正しく文書を閲覧してもらうには、その文書がどのバージョンのHTMLに準拠して記述されているのかを宣言する必要があります。この、記述されているHTMLがどの定義(= DTD)に基づいて記述されているかを示すことを、文書型宣言と呼びます。文書型宣言は、html要素の手前に記入します。
文書型宣言は、使用したいHTMLのバージョンによって異なります。いくつかの代表的な文書型宣言を紹介します。
HTML4.01 Transitional
[sourcecode language=”html”]
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
[/sourcecode]
HTML4.01 Strict
[sourcecode language=”html”]
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
[/sourcecode]
XHTML1.0 Transitional
[sourcecode language=”html”]
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
[/sourcecode]
XHTML1.0 Strict
[sourcecode language=”html”]
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
[/sourcecode]
XHTML1.1 Strict
[sourcecode language=”html”]
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
[/sourcecode]
HTML 5
[sourcecode language=”html”]
<!DOCTYPE html>
[/sourcecode]
こうして過去のバージョンからの文書型宣言の変遷を眺めてみると、HTML 5 で急激に簡略化された記法になったことがわかると思います。HTML 5 では以前使用されていた、定型句を極力省略しようという方針があります。ですので、この文書型宣言に関しても、とてもシンプルに記述されるようになりました。
この授業では、HTML 5 に準拠してHTMLを記述していきますので、HTML 5 の文書型宣言を使用するようにします。先週作成したHTMLの冒頭ににHTML 5 の文書型宣言を記入しましょう。
[sourcecode language=”html”]
<!DOCTYPE html>
<html>
<head>
<title>はじめてのHTML</title>
</head>
<body>
<h1>はじめに</h1>
<p>あのイーハトーヴォのすきとおった風、夏でも底に冷たさをもつ青いそら、うつくしい森で飾られたモーリオ市、郊外のぎらぎら光る草の波。あのイーハトーヴォのすきとおった風、夏でも底に冷たさをもつ青いそら、うつくしい森で飾られたモーリオ市、郊外のぎらぎら光る草の波。</p>
<p>あのイーハトーヴォのすきとおった風、夏でも底に冷たさをもつ青いそら、うつくしい森で飾られたモーリオ市、郊外のぎらぎら光る草の波。あのイーハトーヴォのすきとおった風、夏でも底に冷たさをもつ青いそら、うつくしい森で飾られたモーリオ市、郊外のぎらぎら光る草の波。</p>
<h2>このページについて</h2>
<p>あのイーハトーヴォのすきとおった風、夏でも底に冷たさをもつ青いそら、うつくしい森で飾られたモーリオ市、郊外のぎらぎら光る草の波。あのイーハトーヴォのすきとおった風、夏でも底に冷たさをもつ青いそら、うつくしい森で飾られたモーリオ市、郊外のぎらぎら光る草の波。</p>
</body>
</html>
[/sourcecode]
日本語の文書をインターネット上で流通させる際に、ひとつ面倒な問題が存在します。それは文字コードに関する問題です。コンピュータで文字を扱う際には、その各文字に対してコードを割り振っていく必要があります。日本語に関しても全ての文字と対応した文字コードの基準が存在していて、それを元にコンピュータ上でテキストを扱っています。
ところが、日本語を表現するための文字コードは、その歴史的な経緯から複数の文字コードが混在してしまっています。良く使用される文字コードとしては、下記のものがあげられます。
ここに取り上げた以外にも数多くの日本語用の文字コードが存在します。このように、日本語を表記するだけでも、様々な文字コードが存在しており、きちんと指定しないといわゆる「文字化け」という現象が起きてしまします。どの文字コードを使用するのが正解というものはないのですが、この授業では、XHTMLで標準的に用いられている、UTF-8を標準の文字コードとして使用するものとします。
HTML 5 の規格にのっとって文字コードを指定してみましょう。文字コードを指定するには、head要素の中にmeta要素を使用して指定します。UTF-8を指定する場合には、meta要素は、下記のような記述になります。
[sourcecode language=”html”]
<meta charset="utf-8" />
[/sourcecode]
この文字コードの記法も、HTML 5 から大幅に簡略化されました。では、作成したHTMLに、文字コードを指定しましょう。
[sourcecode language=”html”]
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>はじめてのHTML</title>
</head>
<body>
<h1>はじめに</h1>
<p>あのイーハトーヴォのすきとおった風、夏でも底に冷たさをもつ青いそら、うつくしい森で飾られたモーリオ市、郊外のぎらぎら光る草の波。あのイーハトーヴォのすきとおった風、夏でも底に冷たさをもつ青いそら、うつくしい森で飾られたモーリオ市、郊外のぎらぎら光る草の波。</p>
<p>あのイーハトーヴォのすきとおった風、夏でも底に冷たさをもつ青いそら、うつくしい森で飾られたモーリオ市、郊外のぎらぎら光る草の波。あのイーハトーヴォのすきとおった風、夏でも底に冷たさをもつ青いそら、うつくしい森で飾られたモーリオ市、郊外のぎらぎら光る草の波。</p>
<h2>このページについて</h2>
<p>あのイーハトーヴォのすきとおった風、夏でも底に冷たさをもつ青いそら、うつくしい森で飾られたモーリオ市、郊外のぎらぎら光る草の波。あのイーハトーヴォのすきとおった風、夏でも底に冷たさをもつ青いそら、うつくしい森で飾られたモーリオ市、郊外のぎらぎら光る草の波。</p>
</body>
</html>
[/sourcecode]
これにより、ほとんどのブラウザで「文字化け」を防ぐことができるでしょう。
WWWは、ハイパーテキストだけでなく、画像や動画、音声なども文書に貼りつけることで表現できることに大きな特徴があります。画像や動画など様々なメディアを効果的に用いることでページを効果的に見せることが可能です。
メディアの利用の第一歩として、画像をページに貼りつけてみましょう。
画像をWebページに貼りつけるには、img要素を使用します。具体的には下記のように記述します。img要素もbr要素と同様に、終了タグのない内容をもたない空要素ですので、末尾は「/>」で閉じる必要があります。
[sourcecode language=”html”]
<img src="画像ファイルのURL" alt="画像の説明" />
[/sourcecode]
alt属性は、代替文字を用意して、画像を表示しない利用者のブラウザで代わりにその文字が表示するためのものです。alt属性は様々な状況で正しく情報を提供するためにとても重要なもので、HTML4以降では必須となっています。必ず画像の内容を要約する内容を記述するようにします。
src属性には画像ファイルの場所(URL)を示します。このURLは、Webブラウザのアドレス欄に入力するURLと同じものです。例えば、千葉商科大学のトップページの大学のロゴの画像の場所は「http://www.cuc.ac.jp/img/logo_top.gif」にあります。この画像を自分の作成するWebページに貼りつける場合は、下記のようになります。
[sourcecode language=”html”]
<img src="http://www.cuc.ac.jp/img/logo_top.gif" alt="千葉商科大学" />
[/sourcecode]
画像ファイルはこの千葉商科大のロゴの画像のように、Webサーバーのマシン名から始めた完全なURLで記述することも可能ですが、もし画像ファイルが同じWebサーバ内に格納されている場合は、画像を貼りつけるHTMLファイルから見た相対的な場所で指定することも可能です。このような貼り付けるファイルからみた場所の指定のやりかたを「相対URL」と呼びます。それに対して先程の千葉商科大のロゴのように、先頭からの完全なアドレスで指定する方法を「絶対URL」と呼びます。
相対URLの例をいくつかみてみましょう。
画像ファイルとそれを貼りつけるHTMLファイルが同じディレクトリ(フォルダ)にある場合には、相対URLはファイル名を指定するだけです。例えば、画像ファイル名が「photo.jpg」だったとすると、URLを入れたimg要素は下記のようになります。
[sourcecode language=”html”]
<img src="photo.jpg" alt="写真" />
[/sourcecode]
HTMLファイルと同じ場所にひとつ新たにディレクトリを作成して、その中に画像ファイルを入れた場合は、階層構造の区切りをスラッシュ「/」で表現します。HTMLファイルと同じ場所にあるディレクトリの名前を「image」その中に配置した画像ファイルが「photo.jpg」とした場合、相対URLは「img/photo.jpg」となります。よって、img要素は下記のように記述されます。
[sourcecode language=”html”]
<img src="image/photo.jpg" alt="写真" />
[/sourcecode]
より複雑な相対URLについて考えてみます。
HTMLファイルのひとつ上の階層に、画像を保存するディレクトリ「image」があり、その中に「photo.jpg」があったとします。その際には、相対URLは「自分自身から一つ上の階層にあるimageディレクトリの下のphoto.jpg」というように考えます。一つ上の階層を表現するには「../」という記号を使用します。よって、一つ上の階層のimageフォルダの下のphoto.jpgファイルを相対URLで表現すると「../image/photo.jpg」となります。
[sourcecode language=”html”]
<img src="../image/photo.jpg" alt="写真" />
[/sourcecode]
この、ひとつ上のを表現する「../」という記号を重ねてどんどん上の階層へ辿ることも可能です。例えば2つ上の階層は「../../」3つ上の階層は「../../../」というように表現します。
ここまで学んだ要素を駆使して、簡単な自己紹介のページを作成してみましょう。下記の要素を盛り込んでページを作成するものとします。
openFrameworkに、音声や、画像、動画ファイルを読み込んでプログラムの中で使用することが可能です。様々なメディアを読み込んで活用する方法について解説していきます。
まず始めにサウンドを扱ってみましょう。既存のサウンドファイルのデータを読み込んで再生します。openFrameworksでは、サウンドファイルを読みこんで再生するためのofSoundPlayerというクラスが用意されています。ofSoundPlyaerは、Waveファイル(.wav)、Aiffファイル(.aif)、MP3ファイル(.mp3)、RAWファイル(.raw)など様々なファイル形式のサウンドファイルを読み込んで再生することが可能です。
まずはじめに、サウンドファイルを再生する簡単なサンプルを作成してみましょう。
testApp.h
[sourcecode language=”cpp”]
#ifndef _TEST_APP
#define _TEST_APP
#include "ofMain.h"
class testApp : public ofBaseApp {
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(int x, int y, int button);
void windowResized(int w, int h);
ofSoundPlayer mySound; //ofSoundクラスをインスタンス化
};
#endif
[/sourcecode]
testApp.cpp
[sourcecode language=”cpp”]
#include "testApp.h"
void testApp::setup(){
mySound.loadSound("glitch_loop.wav"); //サウンドファイルの読込み
mySound.setLoop(true); //ループ再生をONに
mySound.play(); //サウンド再生開始
}
/* 後略 */
[/sourcecode]
ofSoundPlayerはクラスなので、使用するためにはまずインスンタンス化する必要があります。このサンプルでは、インスタンス化はtestApp.hの中で行っています。次に、ofSoundPlayerのインスタンスのメソッドを利用して、サウンドファイルの読み込みをします。再生するサウンドファイルは、アプリケーションの実行ファイルと同じ場所にある「data」フォルダの中に配置します。
openFrameworksのプロジェクト内では、サウンドファイルの配置場所は
ofSoundPlayerを利用すると、サウンドファイルの読み込み、再生の他にも様々な指定が可能です。先程のサンプルでは、再生の際にループするように設定していました。
もう少し、サウンドファイルを操作できるように改良してみましょう。画面上でマウスボタンを押すと再生開始、マウスボタンを離すと再生終了するにしてみます。また、マウスのy軸上の位置で再生スピードを変更、マウスのx軸上の位置で、左右の音量バランスを変更できるようにもしてみましょう。
testApp.h
[sourcecode language=”cpp”]
#ifndef _TEST_APP
#define _TEST_APP
#include "ofMain.h"
class testApp : public ofBaseApp {
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(int x, int y, int button);
void windowResized(int w, int h);
ofSoundPlayer mySound; //ofSoundクラスをインスタンス化
};
#endif
[/sourcecode]
testApp.cpp
[sourcecode language=”cpp”]
#include "testApp.h"
void testApp::setup(){
ofBackground(0, 0, 0);
mySound.loadSound("drum_loop.aif"); //サウンドファイルの読込み
mySound.setLoop(true); //ループ再生をONに
}
/* 中略 */
void testApp::mouseDragged(int x, int y, int button){
//パンの設定
mySound.setPan(x / (float)ofGetWidth() * 2 – 1.0f);
//再生スピード変更
mySound.setSpeed( 0.5f + ((float)(ofGetHeight() – y) / (float)ofGetHeight())*1.0f);
}
void testApp::mousePressed(int x, int y, int button){
//パンの設定
mySound.setPan(x / (float)ofGetWidth() * 2 – 1.0f);
//再生スピード設定
mySound.setSpeed( 0.5f + ((float)(ofGetHeight() – y) / (float)ofGetHeight())*1.0f);
//サウンド再生開始
mySound.play();
}
void testApp::mouseReleased(int x, int y, int button){
mySound.stop(); //サウンド再生終了
}
/* 後略 */
[/sourcecode]
さらに工夫を加えてみます。現在再生しているサウンドの音量を取得して、その音量にあわせて円の半径を変化させてみましょう。音を再生する様子を視覚的に捉えることができるようになります。
testApp.h
[sourcecode language=”cpp”]
#ifndef _TEST_APP
#define _TEST_APP
#include "ofMain.h"
class testApp : public ofBaseApp {
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(int x, int y, int button);
void windowResized(int w, int h);
ofSoundPlayer mySound; //ofSoundクラスをインスタンス化
float radius; //円の半径
};
#endif
[/sourcecode]
testApp.cpp
[sourcecode language=”cpp”]
#include "testApp.h"
void testApp::setup(){
ofBackground(0, 0, 0);
ofSetVerticalSync(true);
ofSetCircleResolution(64);
ofEnableAlphaBlending();
radius = 0; //円の半径
mySound.loadSound("drum_loop.aif"); //サウンドファイルの読込み
mySound.setLoop(true); //ループ再生をONに
}
void testApp::update(){
float * val = ofSoundGetSpectrum(1); //再生中のサウンドの音量を取得
radius = val[0] * 800.0; //円の半径に適用
}
void testApp::draw(){
ofSetColor(0, 63, 255, 180);
ofCircle(mouseX, mouseY, radius);
}
/* 中略 */
void testApp::mouseDragged(int x, int y, int button){
//パンの設定
mySound.setPan(x / (float)ofGetWidth() * 2 – 1.0f);
//再生スピード変更
mySound.setSpeed( 0.5f + ((float)(ofGetHeight() – y) / (float)ofGetHeight())*1.0f);
}
void testApp::mousePressed(int x, int y, int button){
//パンの設定
mySound.setPan(x / (float)ofGetWidth() * 2 – 1.0f);
//再生スピード設定
mySound.setSpeed( 0.5f + ((float)(ofGetHeight() – y) / (float)ofGetHeight())*1.0f);
//サウンド再生開始
mySound.play();
}
void testApp::mouseReleased(int x, int y, int button){
mySound.stop(); //サウンド再生終了
}
/* 後略 */
[/sourcecode]
音に続いて、画像データを扱ってみましょう。静止したデジタル画像データは、コンピュータ画面上に図形を描画する際と同様に、X軸方向とY軸方向にグリッド状に整列したピクセルの集合体です。それぞれのピクセルの色の濃度を数値として記録しています。現在のほとんどのコンピュータは、32bitのカラーを表示できる性能があります。32bitカラーの場合、RGBA(Red、Green、Blue、Alpha)それぞれの色の値で8bitずつ、つまり256段階で色の階調を記録しています。
画像データをハードディスクなどの記憶装置などに保存する際には、ピクセルの数値データをそのまま保存するとファイルの容量が巨大になってしまうため、計算処理によりデータを圧縮して容量を削減して保存する場合がほとんどです。情報の圧縮の方法、保存の形式などによって、様々な画像ファイルの形式が存在します。そのため、画像ファイルを読み書きするためには、こうした画像フォーマットの規格に沿って、データを取り扱う必要があります。
openFrameworksでは、画像ファイルの読み込みと書き出しに「freeImage」という既存のライブラリをopenFrameworksから利用できるようにした、ofImageクラスを利用します。ofImageクラスの機能を介して、PNG、BMP、Jpeg、TIFFなどの主要な形式の画像をデータとして読み込んだり、生成した画像データをファイルとして書き出すことが可能となります。
では、実際にofImageを利用して画像ファイルを読み込んで画像データとして利用したり、画面をキャプチャーしたデータを画像ファイルとして保存してみましょう。
新規プロジェクト「ShowImage」を作成します。
testApp.h
[sourcecode language=”cpp”]
#ifndef _TEST_APP
#define _TEST_APP
#include "ofMain.h"
class testApp : public ofBaseApp {
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(int x, int y, int button);
void windowResized(int w, int h);
ofImage myImage; //画像ファイルより読みこまれたイメージデータ
ofImage grabbedImage; //画面をキャプチャーしたイメージデータ
};
#endif
[/sourcecode]
testApp.cpp
[sourcecode language=”cpp”]
#include "testApp.h"
void testApp::setup(){
//画面の基本設定
ofBackground(0,0,0);
ofEnableSmoothing();
//画面の混色の設定を加算合成にする
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
//画像データの読込み
myImage.loadImage("MonaLisa.jpg");
}
void testApp::update(){
}
void testApp::draw(){
//色の設定
ofSetColor(255, 255, 255);
//読み込んだ画像データを画面に描画
myImage.draw(20,20);
//画像データのビットマップ情報を配列に格納
unsigned char * pixels = myImage.getPixels();
//画像の幅と高さを所得
int w = myImage.width;
int h = myImage.height;
//画像を8ピクセル間隔でスキャン
for (int i = 0; i < w; i+=8){
for (int j = 0; j < h; j+=8){
//ピクセルのRGBの値を取得
int valueR = pixels[j*3 * w + i*3];
int valueG = pixels[j*3 * w + i*3+1];
int valueB = pixels[j*3 * w + i*3+2];
//取得したRGB値をもとに、円を描画
//取得したピクセルの明るさを、円の半径に対応させている
ofSetColor(255, 0, 0, 63);
ofCircle(440+i, 20+j, 10*valueR/255.0);
ofSetColor(0, 255, 0, 63);
ofCircle(440+i, 20+j, 10*valueG/255.0);
ofSetColor(0, 0, 255, 63);
ofCircle(440+i, 20+j, 10*valueB/255.0);
}
}
}
void testApp::keyPressed(int key){
//「x」キーを押すと、画面をキャプチャーする
if(key == ‘x’){
//位置とサイズを指定して、画面をキャプチャー
grabbedImage.grabScreen(430,10,420,642);
//キャプチャーした画像データを「grabbedImage.png」で保存
grabbedImage.saveImage("grabbedImage.png");
}
}
/* 後略 */
[/sourcecode]
まず、testApp.hで、ofImageのインスタンスを2つ生成しています。myImageは、既存の画像ファイルを読み込んで、データとして利用するためのものです。また、grabbedImageはopenFrameworksで生成した画面の領域を指定して、その枠内の画像データをキャプチャーして画像データとして保存するため使用します。
testApp.cppでは、ofImageを利用した画像ファイルの読み込み、画像ファイルの解析、画像ファイルの書き出しという一連の処理を行っています。
ofImageのインスタンスに対して、「loadImage(“ファイル名”)」というメソッドを実行して、指定した場所にある画像ファイルを画像データとして読み込んでいます。ここで読みこむ画像ファイルは、音声データと同様に、openFrameworksのプロジェクトフォルダ内にある「bin」→「data」フォルダ内に配置します。読込むファイルの画像形式は、そのファイルの拡張子から類推して自動的に判別しています。
ofImageにloadImageを使用して読みこんだ画像ファイルのデータは、ピクセル単位でその値を取り出して解析することが可能です。ofImageのインスタンスに「getPixel()」というメソッドを実行すると、その画像データのピクセル毎の色の階調の情報を配列として取り出すことができます。画像ファイルのカラーモードが透明度の情報を持ったRGBAカラーモデルの場合には、画像の1ピクセルに対して4つの値(RGBA)、透明度の情報のないRGBカラーモデルの画像の場合にはピクセルあたり3つの値(RGB)の値が、順番に並んで配列に格納されています。
このサンプルでは、RGBカラーモードの画像を読み込んで、RGBそれぞれの色の濃度の情報を8ピクセル間隔でスキャンして、それぞれ別々の配列に格納しています。解析したピクセル情報をもとに、それぞれの色ごとに濃度に応じた半透明の円を描画して、読み込んだ画像を点描のような効果で再現しています。
こうして生成した画像を今度は画像ファイルとして書き出してみましょう。openFrameworksで生成した画面を、画像ファイルとして書き出すには、実行している画面の中から画像ファイルとして書き出す領域を指定します。ofImageのインスタンスに対して、「grabScreen(左端のX座標, 上端のY座標, 幅, 高さ)」というメソッドを実行することで、領域の指定枠内をキャプチャーして、メソッドを実行したofImageのインスタンスにその画像データを格納します。
画像データをファイルとして保存するには、ofImageのインスタンスに、「savaImage(“ファイル名”)」というメソッドを実行します。そうすると、指定したファイル名で、画像ファイルを読み込んだ場合と同様に、openFrameworksのプロジェクトフォルダ内にある「bin」→「data」フォルダ内に画像ファイルを書き出します。画像ファイルのフォーマットは、指定した拡張しから類推して自動的に決定されます。このサンプルでは、点描風に画像を再現した部分のみ抽出して、PNG形式で保存しています。
次に、動画ファイルを読み込んで再生してみましょう。openFrameworksでは、QuickTimeのライブラリを利用して動画の読み込み、再生を行っています。OpenFrameworksからQuickTimeを利用するためには、ofVidePlayerクラスを使用します。ofMoviePlayerクラスを介して、QuickTime形式のファイルを読み込み、画面上で再生したり解析することが可能です。openFramwroksで扱う動画は、パラパラマンガのように、変化する画像データを指定した間隔で次々に表示していると考えると良いでしょう。
testApp.h
[sourcecode language=”cpp”]
#ifndef _TEST_APP
#define _TEST_APP
#include "ofMain.h"
class testApp : public ofBaseApp{
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(int x, int y, int button);
void windowResized(int w, int h);
ofVideoPlayer fingersMovie;
};
#endif
[/sourcecode]
testApp.cpp
[sourcecode language=”cpp”]
#include "testApp.h"
#include "stdio.h"
void testApp::setup(){
//画面の基本設定
ofBackground(0,0,0);
ofEnableSmoothing();
//画面の混色の設定を加算合成にする
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
//ムービーデータを読込む
fingersMovie.loadMovie("fingers.mov");
//ムービーの再生開始
fingersMovie.play();
}
void testApp::update(){
//ムービー再生を待機状態に
fingersMovie.idleMovie();
}
void testApp::draw(){
//色の設定
ofSetColor(0xFFFFFF);
//ムービーデータを画面に表示
fingersMovie.draw(20,20);
//ムービーのビットマップデータを解析し、配列に格納
unsigned char * pixels = fingersMovie.getPixels();
//画像を8ピクセルごとにスキャン
for (int i = 0; i < fingersMovie.width; i+=8){
for (int j = 0; j < fingersMovie.height; j+=8){
//RGBそれぞれのピクセルの明度を取得
unsigned char r = pixels[(j * 320 + i)*3];
unsigned char g = pixels[(j * 320 + i)*3+1];
unsigned char b = pixels[(j * 320 + i)*3+2];
//取得したRGB値をもとに、円を描画
//取得したピクセルの明るさを、円の半径に対応させている
ofSetColor(255, 0, 0, 100);
ofCircle(360 + i,20+j,10.0*(float)r/255.0);
ofSetColor(0, 255, 0, 100);
ofCircle(360 + i,20+j,10.0*(float)g/255.0);
ofSetColor(0, 0, 255, 100);
ofCircle(360 + i,20+j,10.0*(float)b/255.0);
}
}
}
void testApp::keyPressed (int key){
switch(key){
case ‘0’:
//「0」キーを押すと、ムービーを最初のフレームに巻き戻し
fingersMovie.firstFrame();
break;
}
}
void testApp::keyReleased(int key){
}
void testApp::mouseMoved(int x, int y ){
}
void testApp::mouseDragged(int x, int y, int button){
//マウスをドラッグすると、ムービーのタイムラインを操作できる
fingersMovie.setPosition((float)x / (float)ofGetWidth());
}
void testApp::mousePressed(int x, int y, int button){
//マウスのプレスで、ムービーを一時停止
fingersMovie.setPaused(true);
}
void testApp::mouseReleased(int x, int y, int button){
//マウスのプレスで、ムービーの再生を再開
fingersMovie.setPaused(false);
}
void testApp::windowResized(int w, int h){
}
[/sourcecode]
testApp.hでは動画を再生するためのofVideoPlayerクラスのインスタンスfingersMovieを生成しています。
testApp.cppで、実際に動画ファイルを読み込んで再生します。ムービーファイルを読み込むには、ofMoviePlayerクラスのインスタンスに対して「loadMovie(“ムービーファイル名”)」というメソッドを実行します。ムービーファイルのデータは画像やフォントと同様に、「bin」→「data」フォルダに格納します。
読み込んだムービーを再生するには、いくつかの手続きが必要です。まず、読み込んだムービーを再生状態にします。ムービーの再生には「play()」メソッドを使用します。この命令によって読み込んだムービーの再生を開始します。再生状態になったムービーに対して、testAppのupdate()メソッド内では「idleMovie()」メソッドを使用して待機状態にしておく必要があります。画面上に再生しているムービーを表示するために、draw()関数の中でofVideoPlayerのインスタンスに対して「draw(表示位置のX座標, 表示位置のY座標)」というメソッドを使用します。この命令によって、ムービファイルの内容を画面に表示されます。
動画データが画像やフォントと大きく異なる点は、時間軸をもったデータだというところでしょう。ofVideoPlayerクラスには、ムービーの時間軸を操作するためのメソッドがいくつか用意されています。
このサンプルでは、キーボードから「0」キーを押すと、最初のフレームに戻り、マウスをドラッグするマウスポインタのX軸上の位置に応じてムービーの再生位置を変化させることで、動画をタイムラインで捜査できるようにしています。
ofVideoPlayerで取得したムービーデータは、画像ファイルと同様に、ピクセル単位で解析することが可能です。そのためには、ofVideoPlayerのインスタンスに対して「getPixels()」というメソッドを実行することで、RGBに分解された状態で、それぞれのピクセルの濃度を取得することができます。サンプルではその値を利用して、映像を点描風にピクセレイトしています。
次にコンピュータに接続したカメラから動画をリアルタイムに読み込んで利用してみましょう。動画をカメラから読み込むには、ofVideoGrabberクラスを利用します。コンピュータにUSBやFireWire(IEEE 1394)で接続したライブカメラや、コンピュータに内蔵されたカメラを利用することが可能です。
testApp.h
[sourcecode language=”cpp”]
#ifndef _TEST_APP
#define _TEST_APP
#include "ofMain.h"
class testApp : public ofBaseApp{
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(int x, int y, int button);
void windowResized(int w, int h);
ofVideoGrabber vidGrabber; //ofVideoGrabberのインスタンス
int camWidth; //カメラから取り込む画像の幅
int camHeight; //カメラから取り込む画像の高さ
};
#endif
[/sourcecode]
testApp.cpp
[sourcecode language=”cpp”]
#include "testApp.h"
void testApp::setup(){
//画面の基本設定
ofBackground(0,0,0);
ofEnableSmoothing();
//画面の混色の設定を加算合成にする
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
//キャプチャするムービーのサイズを指定
camWidth = 480;
camHeight = 320;
vidGrabber.setVerbose(true);
vidGrabber.initGrabber(camWidth,camHeight);
}
void testApp::update(){
//ムービーをカメラからキャプチャする
vidGrabber.grabFrame();
}
void testApp::draw(){
ofSetColor(0xffffff);
vidGrabber.draw(20,20);
//ムービーのビットマップデータを解析し、配列に格納
unsigned char * pixels = vidGrabber.getPixels();
//画像を10ピクセルごとにスキャン
for (int i = 0; i < camWidth; i+=10){
for (int j = 0; j < camHeight; j+=10){
//RGBそれぞれのピクセルの明度を取得
unsigned char r = pixels[(j * camWidth + i)*3];
unsigned char g = pixels[(j * camWidth + i)*3+1];
unsigned char b = pixels[(j * camWidth + i)*3+2];
//取得したRGB値をもとに、円を描画
//取得したピクセルの明るさを、円の半径に対応させている
ofSetColor(255, 0, 0, 100);
ofCircle(camWidth+40 + i,20+j,20.0*(float)r/255.0);
ofSetColor(0, 255, 0, 100);
ofCircle(camWidth+40 + i,20+j,20.0*(float)g/255.0);
ofSetColor(0, 0, 255, 100);
ofCircle(camWidth+40 + i,20+j,20.0*(float)b/255.0);
}
}
}
void testApp::keyPressed (int key){
//「s」キーを押すと、ビデオ取り込みの設定画面を表示
if (key == ‘s’ || key == ‘S’){
vidGrabber.videoSettings();
}
}
/* 後略 */
[/sourcecode]
testApp.hで、ofVideoGrabberクラスのインスタンス、vidGrabberを生成しています。また同時にカメラから画像をとりこむ際の幅と高さを記憶するために、camWidthとcamHeightという変数を用意しています。
testApp.cppでは、まずカメラから取り込む画像の幅と高さを指定しています。その上でカメラ取り込みの初期化を行います。初期化をするには、ofVideoGrabberのインスタンスに「initGrabber(幅、高さ)」というメソッドを実行します。
実際にデータを取り込むには、ofVideoGrabberクラスのインスタンスにgrabFrame()メソッドを実行します。このメソッドを実行するたびに、新規にカメラから1フレーム画像を読み込まれ、インスタンス内にデータとして格納されます。読み込みの際には、動画としてカメラから画像を取得するために、grabFrame()メソッドをtestAppのupdate()内で繰り返し実行するようにしています。読み込まれた画像を画面に表示するにはofVideoGrabberのインスタンスに「draw(表示位置のX座標, 表示位置のY座標)」メソッドを実行します。
カメラから読み込まれが画像データは、ofImageと同じ方法でピクセル単位で解析することが可能です。ofVideoGrabberのインスタンスに「getPixels()」メソッドを実行して、配列に画像のピクセルごとのデータを読み込みます。これを利用することで、カメラからの映像をリアルタイムに解析し加工することが可能となります。サンプルでは、カメラからの映像を利用して、点描風に画像処理しています。
今日の授業はいよいよActionScriptを利用したFlashコンテンツの作成について学んでいきますが、その前にまず先週のタイムラインベースのアニメーションについておさらいしてみましょう。
フレームベースのアニメーションの制作方法のおさらい
ActionScriptは、アドビシステムズ社の製品であるFlashに使用されるプログラミング言語です。ActionScriptを用いることにより、動画や音声のプレイヤーの作成など、コンテンツに複雑な処理や双方向性を持たせたFlashを作成することが可能になります。
この教室にインストールされている Adobe Flash CS4 では、ActionScript 3.0、またはActionScript 2.0 を使
用することができます。ActionScript 3.0はクラスベースのオブジェクト指向言語で、大規模な開発が可能となっています。
まずは、簡単な例でActionScriptによるプログラミングを体験してみましょう。
以下の手順でステージ上に配置したムービクリップシンボルを、ActionScriptを使用して大きさや場所などを操作してみます。
[sourcecode language=”actionscript3″]
//位置を指定
rect.x = 320;
rect.y = 480;
//大きさを変化
rect.width = 200;
rect.height = 50;
//角度を変化
rect.rotation = 30;
[/sourcecode]
フレームに記入するActionScript
この例のようにActionScriptはキーフレームの中に記入することが可能です。(キーフレームにではなく、独立したファイルとしてActionScriptを記入する方法もあります)。キーフレームに記入したActionScriptは、そのフレームを再生する際に実行されます。
この例ではいったい何をしているのか、考えてみましょう。
今回はステージ上に図形を描いてシンボル化する際に、「グラフィック」ではなく「ムービークリップ」という種類を選択しました。ムービークリップでシンボル化すると、自分自身に関する様々な性質をシンボル内に保持することが可能となります。この自分自身に関する性質のことをプロパティ(Property)と呼びます。
ActionScriptからこのプロパティを操作することで、ステージ上に配置した際の性質を後から変更することが可能となります。例えば、位置(X座標とY座標)、大きさ(幅、高さ)、角度などが変更可能です。
このプロパティの操作を使用することで、ActionScriptを利用したアニメーションやインタラクティブなオブジェクトの生成が可能となります。これからActionScriptを使用したコンテンツを制作していく基本となります。
ムービークリップなどのシンボルは、シンボル化した時点で「ライブラリ」に登録されます。ActionScriptを使用することで、ライブラリからステージ上に配置する操作自体を行うことも可能です。
以下の手順で、ActionScriptを使用してムービクリップシンボルをライブラリからステージ上に配置して、様々な属性(プロパィ)を操作してみましょう。
[sourcecode language=”actionscript3″]
//新規に四角形を生成
var r:Rect = new Rect();
//表示する場所を指定
r.x = 320;
r.y = 240;
//ステージ上に追加する
this.addChild(r);
[/sourcecode]
フレームに記入するActionScript
ActionScriptを使用したFlashコンテンツの制作の導入として、ステージ上に配置したムービクリップの性質(プロパティ)を、ActionScriptを使用して操作しました。この方法は今後スクリプトを用いてアニメーションやインタラクションを作成していく際にとても重要となってきますので、しっかりと理解するようにしましょう。
まず先週の最後にまとめた、Webを支える重要な3つの仕組みについて簡単におさらいします。
これからこの授業でWebのページを作成していくということは、この3つの仕組みのなかのHTML(Hyper Text Markup Language)を記述するという作業になります。
では、HTMLとは何なのか、より詳細について考えていきます。
HTMLはHyper Text Markup Languageです。これを直訳すると「ハイパーテキストをマークアップするための言語」となるでしょう。「ハイパーテキスト」はハイパーリンクによって複数の文書を関連づけ結びつける仕組みを意味するということを先週の授業で解説しました。詳細は先週の授業資料を参照してください。
では、HTMLという用語に含まれるもうひとつのキーワード「マークアップ(Markup)」とは何でしょうか? マークアップはもともとは出版物の原稿に印刷に関する指示などを記号として原稿用紙の余白に書き込んだことを意味しています。HTMLにおけるマークアップも、文章の中に目印を記入していくことを意味しています。では、文章の中に、何を目印として記入していくのでしょうか。実際の簡単な文書を例に考えてみます。
例えば下記の図のような運動会のお知らせの文書があったとします。わたしたちは、この文書をちらっと見るだけで全体の構造を瞬間的に把握することが可能です。
この文書を良くみると、その構造を瞬間的に理解させるために様々な工夫がされていることに気付きます。例えば、ヘッダーとフッターにあたる部分は文字を右詰めにして他の部分と区別しています。また文書の見出しは太く大きい文字にすることで、目立たせています。また各段落の間に空白を入れるとで文章の意味のブロックを明確にしています。もし、全く同じ内容の文書を、全く何の変化をつけず同じ文字の大きさで改行も一切入れずに作成したら、おそらくとても読みとりずらい文書になるでしょう。
この文書をその役割ごとに分割して整理すると、下記の図のようになります。
つまり、この「運動会開催のおしらせ」のようなシンプルな文書の中にも、その役割に応じて様々なブロックに分かれています。その文書内のパート毎の役割を強調するために適切なデザインをすることで、文書の構造が明快になり、結果として読みやすい文書になるのです。
マークアップとは、文書の構造を指定して文書のなかに書き込んでいくことを意味します。先程の例でいうと、「ヘッダー」「見出し」「段落」「箇条書き」「フッター」といった構造を文書の中に書き込んでいきます。文書の中にその構造に応じた適切な目印(マークアップ)を記入していくことが、HTML作成の主な作業となります。
文書の中で、ある特定の部分に目印をつける(= マークアップ)には、その始点と終点に目印をつけて、その役割は何かを指示すれば正しく伝えることが可能です。HTMLでも、その要素がどこから始まって(開始)、どこで終わるのか(終了)を目印をつけて指示していきます。HTMLではこの開始位置と終了位置を伝える目印のことを「タグ」と呼んでいます。
「タグ」に記述する内容は下記の3つとなります。
例えば、h1という一番大きな見出しを意味する要素をマークアップするには、下記のように記述します。
HTMLの文書の中では、山括弧 < > が重要な意味を持ちます。この < > で囲まれた部分は、文書内にマークアップした命令を意味しています。これを「タグ」と呼んでいます。要素のはじまりを示す開始タグは、<要素名> 、要素の終わりを示す終了タグは </要素名> とスラッシュ「/」のあり・なしで区別されています。
この例では、h1要素という文書内の「大見出し」を表す要素で説明しましたが、この「h1」の部分にあたる要素名を変えていくことで、様々な文書内の構造を記述することが可能となります。今日はこのなかから頻繁に仕様されるHTMLの代表的な要素をいくつか紹介します。
※ この授業では、XHTML 1の文法に準拠してタグを記述していきます。XHTMLでは全てのタグの要素名は小文字で記述するように決められています。ですので、この授業ではタグの要素名は小文字で記入するものとします。
ほとんどのHTML文書は下記のような構造をしています。
実際にこのHTML文書の大枠の構造を記述してみましょう。テキストエディタ(この授業では、EmEditorを使用します)を起動して、下記のHTMLを記入します。記入を終えたら、「index.html」というファイル名で保存してください。
[sourcecode language=”html”]
<html>
<head>
</head>
<body>
</body>
</html>
[/sourcecode]
では、骨組みだけ記述してまだ内容は空白のHTMLファイルに題名を記入しましょう。文書の題名はその文書自身に関する情報なので、head要素の中に記入します。文書の題名はtitle要素を使用します。
今後ページを公開した際に、このtitle要素をその文書全体の内容を的確に要約した内容で記述することがとても重要です。たとえばユーザがそのページをブックマークすると、title要素の内容がブックマークの名称として利用されます。また、検索エンジンで検索した結果もtitle要素が最初に表示され、該当ページへのリンク部分となります。
では、先程のファイルにtitle要素を付加してみましょう。
[sourcecode language=”html”]
<html>
<head>
<title>はじめてのHTML</title>
</head>
<body>
</body>
</html>
[/sourcecode]
この状態でファイルを保存して、Webブラウザで開いてみましょう。ページの内容は空白ですが、ウィンドウのタイトルにtitle要素で指定したタイトルが表示されているはずです。
それでは、いよいよページの本体、body要素に文章を記述していきましょう。今日は最も基本的な要素で、頻繁に用いられる2つの要素、「段落」と「見出し」についてマスターしましょう。
まず初めに「段落」を記述します。HTMLでは、段落のひとつひとつを要素としてマークアップします。段落を表す要素は、p要素です。
では先程タイトルだけ記入したページに、段落で文章を入れてみましょう。
[sourcecode language=”html”]
<html>
<head>
<title>はじめてのHTML</title>
</head>
<body>
<p>あのイーハトーヴォのすきとおった風、夏でも底に冷たさをもつ青いそら、うつくしい森で飾られたモーリオ市、郊外のぎらぎら光る草の波。あのイーハトーヴォのすきとおった風、夏でも底に冷たさをもつ青いそら、うつくしい森で飾られたモーリオ市、郊外のぎらぎら光る草の波。</p>
<p>あのイーハトーヴォのすきとおった風、夏でも底に冷たさをもつ青いそら、うつくしい森で飾られたモーリオ市、郊外のぎらぎら光る草の波。あのイーハトーヴォのすきとおった風、夏でも底に冷たさをもつ青いそら、うつくしい森で飾られたモーリオ市、郊外のぎらぎら光る草の波。</p>
</body>
</html>
[/sourcecode]
では、この状態でページをプレビューしてみましょう。それぞれの段落が空白行で区切られて、ひとかたまりの段落とわかるようにレイアウトされると思います。HTMLで本文の文章を書く場合には、基本はこのp要素でマークアップしていくことになります。
では次に、段落に分けた文章を要約する「見出し」をつけてみましょう。わたしたちが普段文章を書く場合、見出しはその重要度や包括する内容の広さの段階に応じて、大きな見出しから小さな見出しへと段階をつけて記述しています。HTMLの見出しも同様に全部で6つの段階があります。見出しのレベルが大きい順から、h1, h2, h3, h4, h5, h6 と段階的に定義されています。
実際にこの見出しを順番に記述すると、Webブラウザ側でその見出しのレベルに応じて文字の大きさや太さなどが調整されて、見出しの段階が視覚的にわかるように工夫されています。
しかし、ここで注意しなくてはならないことは、h1〜h6要素は見出しを表現するものであって、文字の大きさや太さを表現するためのものではないという点です。文字を強調したいために見出しの要素を用いたり、あまり大きな文字で表示したくないので、見出しのレベルを低いものから始めたりすることは正しいHTMLの構造ではありません。文字の大きさや太さの設定は、今後の授業で別の方法(スタイルシート)で設定していきます。ですので、この段階では文字の大きさや太さに関しては気にせず、あくまで文書の構造を正確にマークアップするように心掛けてください。
では先程の段落を記述したページに、見出しを入れてみましょう。
[sourcecode language=”html”]
<html>
<head>
<title>はじめてのHTML</title>
</head>
<body>
<h1>はじめに</h1>
<p>あのイーハトーヴォのすきとおった風、夏でも底に冷たさをもつ青いそら、うつくしい森で飾られたモーリオ市、郊外のぎらぎら光る草の波。あのイーハトーヴォのすきとおった風、夏でも底に冷たさをもつ青いそら、うつくしい森で飾られたモーリオ市、郊外のぎらぎら光る草の波。</p>
<p>あのイーハトーヴォのすきとおった風、夏でも底に冷たさをもつ青いそら、うつくしい森で飾られたモーリオ市、郊外のぎらぎら光る草の波。あのイーハトーヴォのすきとおった風、夏でも底に冷たさをもつ青いそら、うつくしい森で飾られたモーリオ市、郊外のぎらぎら光る草の波。</p>
<h2>このページについて</h2>
<p>あのイーハトーヴォのすきとおった風、夏でも底に冷たさをもつ青いそら、うつくしい森で飾られたモーリオ市、郊外のぎらぎら光る草の波。あのイーハトーヴォのすきとおった風、夏でも底に冷たさをもつ青いそら、うつくしい森で飾られたモーリオ市、郊外のぎらぎら光る草の波。</p>
</body>
</html>
[/sourcecode]
このHTMLをWebブラウザで開くと下記のように表示されます。一番大きな見出しh1要素と、次に大きな見出しh2要素が表示されています。
ここまでの解説で、文書の中にに「タグ」と呼ばれる目印を記入していくことで、簡単にWebブラウザで表示可能なHTML文書が作成できることが実感できたと思います。HTMLを学ぶには、まずはHTMLの要素の種類とその用法を憶えていくことになります。理解した要素が増えるほど、よりその文書の意味に忠実な、きめ細かなマークアップをしてくことが可能となります。
ここで、注意しなくてはならない点があります。様々な「タグ」= 要素を知るにつれて、そのタグを組み合せて、文書の体裁やデザインを整えていこうとしてしまう人がいます。たとえば、タグの種類を変化させて、本文の文字の大きさを変えてみたり、文字の配置をタグをトリッキーに駆使してレイアウトしたりといった方法です。
HTMLで文書の体裁(= デザイン)を整えようとするのは、正しい使用法ではありません。HTMLはあくまで文書の「構造」のみを記述する目的で使用すべきです。文書の構造を正しくHTMLで記述することで、環境に依存せずに世界中のより多くの人が共有できるようになります。またサーチエンジンなどのコンピュータのプログラムにとっても理解し易い形式であるといえます。
文書の体裁(= デザイン)を整える方法は、この授業の後半で取り扱います。ですので、それまではWebブラウザで表示される見た目は気にせずに、HTMLを作成する際には、段落はp要素、見出しはh1 〜 h6要素、というように必ず要素の意味と文書の構造を一致させて、正しく構造を写しとるように心掛けてください。
今日の授業では、HTMLの重要な概念「マークアップ」について学んだ上で、簡単なHTMLを作成してみました。その際に、HTMLは文書の構造を記述するための仕組みであるという点に注意してください。
次週は、今日とりあげた「段落」「見出し」といった基本的な要素に加えて、より多様なマークアップのための要素について解説していきます。
この授業ではAdobe Flashを用いて動的なコンテンツを作成していく予定です。
Flashを用いて、インタラクティブで動きのあるコンテンツを制作する手法として、大きく分けて2つの手法 (流派?) が存在します。ひとつは、タイムラインをベースにした開発手法、もうひとつは、スクリプト (ActionScript 3) をベースにした、スクリプトベースの開発手法です。
この「Web動画表現」の講義では、最終的には、後者のスクリプトベースでの開発手法を用いて開発を行っていこうと考えています。スクリプトベースの開発手法のほうが、より自由度が高く、しかも大規模なコンテンツの制作に適していると考えているからです。
しかしながら、Flashに全く初めて触れるという初心者にはこのスクリプトベースの開発手法は、少しハードルが高いのも事実です。ですので、今日の講義では、まず最初により基本的な開発手法であるタイムラインをベースにした開発手法について簡単に触れて、Flashの基本操作について習熟したいと思います。
DVD「ノーマン・マクラレン作品コレクション」パイオニアLDC、より「ノーマン・マクラレンのアニメーション講座 1」を視聴
A地点からB地点まで直線移動するアニメーションを作成する