yoppa.org



ActionScript 3.0 – 変数、くりかえし

変数と型

プログラムで、値を記憶して必要なときに利用するには、変数(variable)を使います。変数を直感的に理解するには、1つの値を格納することのできる箱をイメージすると良いでしょう。この箱を利用するためには、まず箱の名前と箱の中にいれるデータの種類を決めます。これを「変数の宣言」といいます。箱が用意できたら、この箱の中に実際のデータを格納し箱の名前と関連づけます。このデータを箱の中に入れる操作を「代入」といいます。一度箱の中に入れたら、後は箱の名前を指定することで、箱の中身を見ることが可能となります。この箱の名前からデータの中身を見ることを変数の「参照」といいます。

変数の宣言とデータ型

変数を宣言する際には、プログラムの中でどのような名前で変数を用いるのかという名前の宣言と、どのような種類の変数を用いるのかたという情報をあわせて宣言します。この変数の種類のことを「データ型」と呼びます。ActionScript 3.0でよく用いられるデータ型としては以下のようなものがあります。

  • int – 整数(-2,147,483,648〜2,147,483,647)
  • uint – 符号なし整数(0〜4,294,967,295)
  • Number – 整数、符号なし整数、浮動小数点数int、uintの範囲外の値に使う。
  • String – ストリング(文字列)
  • Boolean – trueまたはfalse

変数を使用するにはまず変数名と型を指定して宣言する必要があります。ActionScript 3.0で変数を宣言するには、以下のような書式を使用します。

var 変数名:型;

例えば、以下のように使用します。

var i:int;
var str:String;
var bTest:Boolean;

代入

宣言した変数に値を代入するには、代入演算子「=」を用います。代入演算子は数学でいう等号とは意味が異るので注意が必要です。数学での等号は、イコール「=」を挟んで左辺と右辺は等しいという意味になります。しかし、プログラミングにおいては、イコール「=」を挟んだ場合、右辺の値を左辺の変数に代入する、という意味となります。

変数の代入の例

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

変数を利用して図形を描く

次に変数を利用して図形を描いてみましょう。

まず、先週の授業でやった方法で、FLAファイル(AS3)を作成し、ドキュメントクラスを「Main」に設定します。その上で新規にムービークリップを作成して円を描き、作成したムービークリップにMyCircleというクラスを設定します。

次に、ActionScript 3.0ファイルとして、Main.asをFLAファイルと同じ場所に新規に作成します。以下のようなプログラムを作成してみましょう。

Main.as

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

宣言した変数の値(locX:X座標、locY:Y座標、circleAlpha:透明度)を更新しながら円をくりかえし描画することで、すこしずつ変化する円を順番に描いています。

/wp-content/uploads/2010/10/Main1.swf, 550, 400

繰り返し

現状のサンプルのプログラムを注意深く観察すると、似たような記述の繰り返しになっていることに気がつきます。10回程度までの繰り返しであれば、ここまでのやり方のように繰り返しの数だけ似たような命令を記述しても良いのですが、例えばこれが100回の繰り返し、1000回の繰り返しといったように、繰り返しの数を増やしていった場合、現在のやり方では破綻してしまいます。

この似通った繰り返しの部分を、プログラミングの記述を工夫してすっきりとまとめることが可能です。ActionScript 3.0では、「for文」という文を用いて、繰り返しの構造を記述します。

for文の基本的な構文は以下のようになります。

for(《初期化》; 《ループの継続条件》; 《カウンタ変数の更新》){
    《文》
}

例えば、カウンター変数「i」を用意して、iが0から99まで、合計100回くりかえして同じ処理をする繰り返し文を書く場合は、以下のような構文となります。

var i:int;
for(i = 0; i < 100; i = i + 1){
    《繰り返す処理の内容》
}

カウンタ変数」とは、for文が繰り返されるたびにその繰り返し回数を記録するカウンタのような役割を果たす変数です。

この時変数iは、for文の中で繰り返し処理が1回行われるごとに、1つ増加していきます。この変数iを効果的に用いることで、繰り返し回数に応じて、パラメータを変化させることも可能となります。

さきほどの変数を用いた円の描画のプログラムを注意深く眺めてみると、同じ操作のくりかえしになっている部分があることに気付くでしょう。

  • 円を描く
  • 円のx座標(locX)に80を足す
  • 円の透明度(circleAlpha)から0.2を引く

この、くりかえしの部分をfor文を用いて短く書き直してみたいと思います。

Main.cpp

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;
            }
        }
    }
}
/wp-content/uploads/2010/10/Main2.swf, 550, 400

くりかえしを使用した表現

もう少し工夫して、くりかえしを効果的に使用した表現についてトライしてみましょう。

単純な四角形ではなく、下記のような図形を描いて、「MyMc」というクラス名でムービークリップを作成します。

この図形のX座標を少しずつ左から右に変化させながら、少しずつ回転をしていきたいと思います。

Main.as

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

このスクリプトを実行すると、変化させる回転角度に応じて様々なパターンが描きだされます。

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

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);
                }
            }
        }
    }
}
/wp-content/uploads/2010/10/Main4.swf, 550, 400

サンプルファイルのダウンドーロ

今日とりあげたサンプルは全て下記からダウンロード可能です。

10月27日分サンプルファイル (ZIP, 48KB)


HTML基礎2 – 情報をわかりやすく整理する、リストとテーブル

情報をわかりやすく整理する

前回の授業では、見出し、段落、改行、インライン画像をWebページで使用するための要素を紹介しました。これらの要素を組み合せて、文章を中心にしたWebページを作成することが可能となりました。

今日の授業では、より情報をわかりやすく表現するための手段として、「箇条書き」によって情報を表現する「リスト」と、表を作成する「テーブル」に関係する要素について紹介したいと思います。これらの要素を正しく効果的に用いることで、文章だけで説明するよりも、よりわかり易く情報を表現するためのバリエーションになるでしょう。

情報の整理 1 – リスト

リストの効果

まず、リストに関係する要素について解説します。リストとは箇条書きで情報を表現する手段です。リストは短い文字量でも、表現したい論旨を的確にわかり易く表現することが可能となります。また、視覚的にも読み易くなるという効果も期待できます。

3種類のリスト

HTMLのリストには大きくわけて3種類のタイプが存在ます。項目の順番にはあまり大きな意味を持たず、並列的に列挙する「並列列挙リスト」と、項目の順番が意味を持たせたり、もしくは項目をあとで参照するための番号をつける「順序付きリスト」、そして辞書のように用語とその定義から構成される「定義型リスト」の3種類です。

「並列列挙リスト」には「ul (= Unorderd List)」要素、「順序付きリスト」には「ol (= Orderd List)」要素、「定義型リスト」には「dl (= Definition List)」要素を用います。

リストの書式 1 – 並列列挙型リスト、順序付きリスト

リストをマークアップするには、まずその箇条書きする要素全体を「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>
  1. 一本でも人参
  2. 二足でもサンダル
  3. 三艘でもヨット
  4. 四粒でもごま塩
  5. 五台でもロケット
  6. 六羽でも七面鳥
  7. 七匹でも蜂
  8. 八頭でもクジラ

リストの書式 2 – 定義型リスト

定義型リストは、その全体を「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>
HTML
Hyper Text Markup Languageの略。Webページを記述するためのマークアップ言語。
HTTP
Hyper Text Transfer Protcol。Webサーバとクライアントがデータを送受信するのに使われるプロトコル。
URL
Uniform Rosource Locator。インターネット上の情報の場所を指定する。

リストの入れ子構造

リストはまた、複数のレベルを持つことが可能です。リストのレベルを表現するには、リストの項目の中に、新たなリスト(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>
  • リスト1
    • リスト1.1
    • リスト1.2
    • リスト1.3
  • リスト2
    • リスト2.1
    • リスト2.2
    • リスト2.3

リストが多重構造になると、開始タグと終了タグの関係性が複雑に組み合わされます。必ず開始した要素は終了しているように、またその対応の順番が混乱しないように、インデントを使用しながら整理して記述するように心掛けましょう。

テーブルによる情報の表現

HTMLで行と列から成る「表」で情報を表現する際には「table要素」を使用します。HTMLでは、表(= table要素)の中に1つ以上の「行(縦の並び)」があり、その行の中に1つ以上の項目(セル、データ)があると考えます。

table要素の中の行を「tr (= Table Row) 要素」、セルを「td (= Table Data) 要素」でマークアップします。

tableの書式

では、簡単な表を作成してみましょう。

<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を複雑に使用し過ぎると、ページのレンダリングに時間がかかります
  • SEOに不向き:サーチエンジンでページの構造を解析することが困難なため、検索ページで上位になりずらい
  • ファイルサイズが増える:無駄なタグが多く、HTMLのファイルサイズを無駄に浪費します
  • HTMLファイルの判読が困難:凝ったtableレイアウトは、後から人間が判読するのが非常に困難となります

こうした様々なデメリットから、table要素をレイアウトの手段として使用することは、現在では賢いやり方とはいえません。この授業でもtable要素はあくまで「表形式」のデータを表現するための手法として使用するに留め、ページ全体のレイアウトや細かな位置の調整は授業の後半で学ぶCSS(カスケーディング・スタイルシート)という仕組みで一括して行うようにします。

今日のまとめ

今日の授業では、リストと表という形式を用いて、情報を整理して表現する手法について学びました。リストや表をその意味や構造に忠実に、効果的に用いることで、情報を視覚的にわかり易く表現することが可能となります。


openFrameworksで、オブジェクト指向プログラミング(OOP) 前編

オブジェクト指向プログラミングとopenFrameworks

オブジェクト指向プログラミング(OOP)とは相互にメッセージを送りあうオブジェクトの集まりとしてプログラムを構成するプログラミングの技法です。現在使用されているプログラミング言語の多くは、OOPのパラダイムをベースに設計されていて、現在主流のプログラミングの手法と言えるでしょう。オブジェクト指向プログラミング言語を列挙してみると、Smalltalk、C++、Objective-C、Python、Ruby、Perl(5.0以降)、Java、JavaScript、C#など数多くの言語があげられます。

openFrameworksはC++をベースにしたフレームワークです。ですので、openFrameworksもオブジェクト指向のプログラミング言語です。OOPの利点を最大限に利用することで、より効率的に強力なプログラムをしていくことが可能となります。

「プロパティ」「メソッド」「インスタンス」「カプセル化」「ポリモーフィズム」など慣れない言葉が頻出するので、最初はとまどうかもしれませんが、その考え方の根本はシンプルなものです。今回はopenFrameworksを用いてOOPをしていくための基本についてじっくりとやっていきたいと思います。

オブジェクト指向プログラミングの基本概念

このセクションでは、まずオブジェクト指向プログラミングの根幹を成す「オブジェクト」という概念自身について考えていきます。

オブジェクトとメッセージ

オブジェクト指向プログラミングとは、オブジェクトというプログラム機能の部品の集合が、相互にメッセージを送りあいながらプログラムを構成していくというプログラミング技法です。それぞれのオブジェクトは独立していて、自分自身で値や処理手順を保持しています。オブジェクトの一つ一つが、小さなプログラムのモジュールあると考えられます。そうした小さなモジュール同士がが相互にメッセージを送りあうことで大きなプログラムを構成するというのが、オブジェクト指向の基本的な考えかたとなります。

オブジェクト = プロパティ(属性) + メソッド(動作)

オブジェクト指向の考え方を、より日常的で具体的な例で考えてみましょう。オブジェクトは日本語にすると「物」です。オブジェクト指向プログラミングの構成単位であるこのオブジェクトは、私達の周囲にある物で例えてみることができます。オブジェクト指向プログラミングでは、物を二つの観点から整理します。一つはその物固有の「属性」、もう一つはその物自身に対する「動作」です。属性のことを「プロパティ」、動作のことを「メソッド」と言い換えることもできます。この複数のプロパィとメソッドから構成されるオブジェクトの概念を図示すると、次のようになります。

この考えかたは、実際に身の回りのものに当てはめてみるとより容易に理解可能になるでしょう。

  • オブジェクト:犬
    • 属性(プロパティ):犬種、年齢、性別
    • 動作(メソッド):歩く、吠える、餌を食べる、寝る
  • オブジェクト:テレビ
    • 属性(プロパティ):画面サイズ、チャンネル、ボリューム
    • 動作(メソッド):点ける、消す、チャンネル変更、音量変更
  • オブジェクト:リンゴ
    • 属性(プロパティ):色、重量、味
    • 動作(メソッド):成長する、熟す、落下、腐る

もちろん、現実の犬やテレビやリンゴはこんなに単純ではありません。もっと沢山の属性や動作を持っています。しかし重要なことは、物の全ての性質を写しとるのではなく、プログラムで必要となる属性と動作を抽出するという操作です。プログラムの中のオブジェクトは、あくまでも物そのものではなく、現実世界の中からそのプログラムの機能で必要なものだけをとりだした抽象化された「物 = オブジェクト」なのです。

クラス = オブジェクトの設計図

では、どのようにしたらオブジェクトをプログラム内に生成することができるのでしょうか。オブジェクト指向プログラミングでは、オブジェクトを生成するには、まずその設計図を作成しなければなりません。この設計図のことを「クラス」と呼びます。クラスに属性と動作の詳細を記述することで、オブジェクトのふるまいを決定します。

クラスは設計図なので、そのままではプロブラムのモジュールとして動作することはできません。クラスを実際に使用するには、クラスをオブジェクトとして生成する必要があります。この操作を「インスタンス化」といいます。

クラスからインスタンス化されるオブジェクトは一つとは限りません。一つのクラスから、必要に応じて複数のオブジェクトを生成することも可能です。例えば、車の設計図を一つ作成すれば、それを元にして何台でも車を製造することが可能となるということと同じです。このようにして、共通の属性を持ち同じ動作が可能なオブジェクトを、一度に沢山生成することが可能となります。

オブジェクト指向プログラミングの特徴

カプセル化

カプセル化とは、外部からアクセスする必要の無い場合、オブジェクトのメソッドやプロパティを外部からは見えないように隠す(隠蔽)という概念です。このことにより、プログラムの変更に対する耐久性が上がったり、プログラムの抽象度が上がるという利点があります。

これは時計を例にとるとわかりやすいかもしれません。我々は時計を利用する際には、時刻を調整する機能(メソッド)や、時刻を知るための針の角度の状態(プロパティ)を知ることができます。しかし、時計の内部でどのような機構によってその機能が実現されているのかは、時計を使用する範囲においては知る必要はありません。ですので時計は必要な機能以外には触れることができないようにその機能を隠しています。これがカプセル化の考え方です。

カプセル化した結果、外部からアクセスできなくなった領域をプライベート(Private)、外部に公開してアクセス可能にした領域をパブリック(Public)と呼びます。

インヘリタンス (継承)

継承とは、既存のクラスから、その機能や構造を共有する新たなクラスをサブクラスとして派生することができるという機能です。そのようなクラスは「親クラス(スーパークラス)を継承した」と表現します。

例えば、「車」という既存のクラスを派生して「パトカー」「消防車」「タクシー」といった、その機能を共有し新たな機能を付加したクラスを生成することが可能となります。

ポリモーフィズム (多態性、多相性)

ポリモーフィズムとは、オブジェクトの処理の実態は、メッセージの名前からではなく、オブジェクトごとに決定された方法で異った処理を行うことが可能であるという性質です。

用語が難解なので難しく考えがちなのですが、簡単に言えば「メッセージに対する処理の方法はオブジェクト自身が決めてよい」ということです。例えば、「犬」「猫」「ライオン」というオブジェクトがあったとします。ここに「鳴く」という共通のメッセージをそれぞれ送った際に、犬「ワンワン」、猫「ニャー」、ライオン「ガオー」とそれぞれ異った鳴きかたをします。それぞれのオブジェクトによって異なる鳴き方(メソッド)が決められているからです。これがポリモーフィズムです。

オブジェクト指向プログラミングの視点からtestAppを見直す

オブジェクト指向プログラミングの考えかたを一通り理解した上で、もう一度、いままで使用してきた testApp.h について眺めてみましょう。

testApp.h

#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

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となります。

UMLクラス図を作成する

XCodeでopenFrameworksのプロジェクトを開き、プロジェクトのアイコンを選択した上で、メニューから「設計」→「クラスモデル」→「クイックモデル」を選択します。すると、自動的に現在の状態を図に整理されます。この生成される図を、「UMLクラス図」と呼びます。それぞれの四角のブロックがひとつのクラスを表しています。クラスの中は3つの領域に分割されていて、上からそのクラスのクラス名、属性(プロパティ)、動作(メソッド)が記述されています。また、それぞれのクラスの継承関係も図の中で表現されています。

openFrameworksで、クラスを作成する

では、いよいよopenFrameworksで、オリジナルのカスタムクラスを追加して、それをtestAppから呼びだして操作してみましょう。

まずは簡単な例から、徐々に複雑なサンプルへと変化さえていきたいと思います。

円を表示するクラス

まずはじめに、円を表示するだけのシンプルな機能のクラスを作成してみたいと思います。円を表示するために下記のようなクラスを設計してみます。

  • クラス名:MyCircle
  • プロパティ:なし
  • メソッド:draw() – 円を描画する

XCodeに新規にクラスファイルを追加する

クラスを作成するには、まずXCode左コラムの「グループとファイル」のリストの中から「src」フォルダを右クリックします。表示されるメニューから「追加」→「新規ファイル」を選択します。

すると新規ファイルのテンプレートを選択する画面が表示されます。まず、「C and C++」>「C++ File」を選択し、ファイル名を「MyCircle.cpp」にします。その下にある「同時に”MyCircle.h”も作成」のチェックボックスをチェックします。保存場所やその他の設定はそのままで、「完了ボタン」を押します。この操作で、Sourceフォルダの中にMyCircle.hとMyCircle.cppの2つのファイルが追加されます。

クラスのヘッダーファイルのテンプレート

まず、クラスのヘッダーファイルを記述します。クラスのヘッダーファイルは、下記のような構造になっています。

クラステンプレート

#ifndef /* ユニークな名前 */ //インクルードガード
#define /* ユニークな名前 */

#include "ofMain.h" //ofMain.hをインクルード

class /* クラス名 */ { //クラスの開始

public: 
    //公開のプロパティ、メソッドを宣言する
    
private:
    //非公開のプロパティ、メソッドを宣言する

}; //セミコロンを忘れずに!!

#endif //インクルードガードの終了

最初の2行は「インクルードガード」と呼ばれる仕組みです。これはコンパイルする際にヘッダーファイルを二重に読み込んでしまわないようにするための仕組みです。#ifdef と #define の後ろには大文字の英文字とアンダーバー「_」を組み合わせて、そのクラスを定義する一意の名前を命名します。例えばMyCircleクラスの場合は「_MY_CIRCLE」などとするのが良いでしょう

また、openFrameworksの機能を利用したクラスを生成する場合には、必ず include文を使用して「ofMain.h」を読み込むようにします。この「ofMain.h」ファイルはopenFrameworksの様々な機能(アニメーション基本機能、図形の描画、音声、動画、フォントなど)を一括して読みこむためのヘッダーファイルとなっています。

では、このテンプレートにMyCircle.hの情報をあてはめてみましょう。

MyCircle.h

#ifndef _MY_CIRCLE
#define _MY_CIRCLE

#include "ofMain.h"

class MyCircle {

public: 
    
private:

};
#endif

メソッドを追加する

では、ヘッダーファイルに円を描画するメソッドdraw()を追加してみましょう。draw() メソッドは、外部から参照される必要があるので公開(public)の関数として記述します。

ヘッダーファイルの関数は、下記のような記述のルールになっています。

戻り値の型 関数名(引数1, 引数2, 引数3...);

引数がない場合は、空白の括弧 () にします。また関数の戻り値がない場合は、「void」という記述を戻り値の型に記述します。このルールを守って、ヘッダーファイルにdraw() メソッドを記入します。

MyCircle.h

#ifndef _MY_CIRCLE
#define _MY_CIRCLE

#include "ofMain.h"

class MyCircle {

public: 
    void draw();
    
private:

};

#endif

ソースファイルを記述する

次にヘッダーファイルで設計した情報に基いて、実際の処理内容を記述するソースファイルを記述します。ソースファイル内のメソッド(関数)は、「戻り値の型 クラス名::関数名(引数1, 引数2, 引数3 …) { 処理の内容 }」というような記述をする必要になります。例えば、MyCircleクラスのdrawメソッドの場合は下記のようになります。

void MyCircle::draw()
{
    /* 処理の内容 */
}

また、ソースファイルの先頭には必ず、include文を使用して自身のヘッダーファイルを読み込むようにします。

メソッド draw() の処理はまずはシンプルに ofSetColorで色を指定して、ofCircleで画面の中心に円を描画するようにしてみましょう。

MyCircle.cpp

#include "MyCircle.h"

void MyCircle::draw()
{
    //色を指定
    ofSetColor(31, 127, 255); 
    //画面の中央に円を描く
    ofCircle(ofGetWidth()/2, ofGetHeight()/2, 100);
}

UMLクラス図を確認

XCodeの機能として、現在作成しているプロジェクトのクラス図を自動作成する機能が搭載されています。この機能を利用して現在のクラズ図を書出してみましょう。XCodeのメニューから「設計」→「クラスモデル」→「クイックモデル」を選択します。すると、しばらくレンダリングした後にクラス図が生成されます。

これでMyCircleクラスの実装は完了です。

作成したクラスを使用する

クラスの実装は完了したのに、現在のプロジェクトをビルドしてみても画面には何も表示されません。冒頭のオブジェクト指向プログラミング解説に書いたようにクラスというのはあくまで設計図に過ぎません。クラスはインスタンス化という処理を行うことにより実体化して始めて、実際に機能するプログラムの機能単位として動きます。作成したクラスをインスタンス化するには、メインのクラスであるtestAppクラスからインスタンス化の処理をする必要があります。

インスタンス化

実際のインスタンス化の処理は簡単です。まずメインのクラスtestAppとMyCircleの定義を関連させるために、testApp.hの冒頭で、include文を使用してMyCircleのヘッダーファイルであるMyCirle.hを読み込みます。

その上で、変数を定義するようにクラスを宣言することでインスタンス化は完了します。例えばMyCircleクラスをインスタンス化して、circleというインスタンス(実体)を生成するには下記のように行います。

MyCircle circle;

ではtestApp.hにMyCircleのインスタンス化の処理を加えてみましょう。

testApp.h

#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

メソッドの実行

クラスのインスタンス化が完了したので、そのインスタンスのメソッドを呼び出すことでクラス内で定義された処理が実行されます。インスタンスのメソッドを呼びだすには、下記のように記述します。

インスタンス名.関数名(引数1, 引数2, 引数3...)

この書式に従って、testAppのdraw()関数より、MyCircleのdraw()メソッドを呼びだして円を描画してみましょう。

testApp.cpp

#include "testApp.h"

//--------------------------------------------------------------
void testApp::setup(){
    ofBackground(0, 0, 0);
    ofSetCircleResolution(64);
}

//--------------------------------------------------------------
void testApp::update(){

}

//--------------------------------------------------------------
void testApp::draw(){
    //MyCircleクラスのインスンタンスcircleのdraw()メソッドを実行
    circle.draw();
}

/* 後略 */

また、プロジェクトのUMLクラス図も更新されています。testAppクラスにMyCircleのインスタンスcircleがプロパティとして加わりました。

プロパティを操作する

では、このMyCircleで描画される円の色、位置、半径をクラスの公開されたプロパティとして定義して、testAppから操作できるようにしてみましょう。それぞれのプロパティの名前と型を下記のように定義するものとします。

  • プロパティの内容 – プロパティ名:型
  • 円の色 – color:ofColor
  • 円の位置 – pos:ofPoint
  • 円の半径 – radius:float

プロパティを操作するには、公開された(public)プロパティを直接操作する方法と、非公開の(private)プロパティを、メソッドを介して操作するという2種類の方法があります。

Publicなプロパティを直接操作する

シンプルな方法は、操作したいプロパティをPublicとして宣言し、直接操作する方法です。Publicなプロパティを操作する方法はメソッドの実行とよく似ています。

インスタンス名.プロパティ = 設定する値;

この方法で、testAppから、MyCircleのcolor、pos、radiusのそれぞれを操作してみましょう。

MyCircle.h

#ifndef _MY_CIRCLE
#define _MY_CIRCLE

#include "ofMain.h"

class MyCircle {

public: 
    //円を描く
    void draw();

    //円の色
    ofColor color; 
    //円の位置
    ofPoint pos;
    //円の半径
    float radius;
    
private:

};

#endif

MyCircle.cpp

#include "MyCircle.h"

void MyCircle::draw()
{
    //colorで指定された色を塗る
    ofSetColor(color.r, color.g, color.b); 
    //指定した場所に、指定した半径で円を描画
    ofCircle(pos.x, pos.y, radius);
}

testApp.h はそのまま変化なし。

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

/* 後略 */

クラスMyCircleにプロパティが3つ追加されました。その結果として、このプログラムのUMLクラス図は下記のようになります。

プログラムを実行すると、指定した色、位置、半径で円が描画されます。

アクセサを介してプロパティの操作する – setterとgetter

クラスのプロパティを操作する方法として、もう一つの手法を紹介します。クラスのメソッドを介してPrivateなプロパティにアクセスするという方法です。プロパティを取得したり設定したりするメソッドのことを、アクセサ(Accessor)と呼びます。また、取得するメソッドのことをgetter、設定するメソッドはsetterと呼び、それぞれgetXXX、setXXXという関数名をにするのが一般的です。

アクセサを用いる利点はいくつか挙げられます。

  • 値のチェック:setterで値を設定する際に、不正な値をsetterの中でチェックして正しい値に修正することができる
  • デバッグの利便性:setterやgetterの中にログを出力する機能を備えておくと、デバッグの際にとても便利
  • 更新性:setterやgetterの処理内容が変化した際にも、呼び出し側ではなくsetter、getter側で対処することで、他のクラスへの波及を防ぐことができる

これらの利点を考えると、多少面倒でもアクセサを利用するほうが良いでしょう。先程の3つのプロパティ、color、pos、radiusを、アクセサを介して設定できるようにしましょう。今回は値を設定するだけなので、setterだけを定義してみます。それぞれsetColor()、setPos()、setRadius()というメソッドとなります。

MyCircle.h

#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

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

testApp.h はそのまま変化なし。

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

/* 後略 */

MyCircleクラスにアクセサが追加されました。URLクラス図も変更されています。

複数のインスタンスを生成する

ひとつのクラスから複数のインスタンスを生成することも可能です。方法は簡単で、インスタンス名を変えて、それぞれに対してインスタンス化をするだけです。例えば、MyCircleクラスから3つのインスタンス、circle1, circle2, circle3を生成してみましょう。

MyCircle.hとMyCircle.cppはそのまま変化なし。

testApp.h

#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

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

/* 後略 */

UMLクラス図

インスタンスの配列を生成する

クラスから生成するインスタンスの数が増えてくると、それぞれインスタンスとして生成して操作していくことに限界が生じてきます。いままで用いてきた複数の変数を配列にする方法と同じ方法で、クラスのインスタンスの配列を使用することも可能です。MyCircleの配列、circlesを生成して一気に大量のクラスを生成してみましょう。

MyCircle.hとMyCircle.cppはそのまま変化なし。

testApp.h

#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

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

/* 後略 */

UMLクラス図

今日のまとめ

今回の授業では、オブジェクト指向プログラミングの概念について解説しました。また概念を理解した上で、openFrameworksでカスタムのクラスを設計し、そのクラスのインスタンスを生成し、プロパティやメソッドを操作するということを行いました。

しかしながら、今回のプログラム例はあくまでオブジェクト指向プログラミングの理解のためのサンプルという意味合いが強く、あまり現実的なものではありませんでした。円を描くというだけの機能であれば、わざわざクラスに分割する必要性はないでしょう。

次回はオブジェクト指向プログラミングの後編ということで、より実践的なプログラムを通して、オブジェクト指向プログラミングの利便性をより強く実感するような内容を取り扱っていく予定です。またその過程でC++における最も難解な部分である、ポインタという概念や、メモリの管理について併せて学んでいきたいと思います。

サンプルファイルのダウンロード

今日とりあげたプログラムは全て下記のリンクからダウンロードできます。


ActionScript 3.0 – ドキュメントクラスを使用する

ActionScriptを外部ファイル化する

先週の授業では、ムービークリップの属性(プロパティ)を操作する際に、ActionScriptをタイムラインのキーフレーム内に記述していました。このフレームにActionScriptを書き込む手法をフレームスクリプトと呼んでいます。

フレームスクリプトは手軽にスクリプトを記述できて便利なのですが、いくつかの欠点もあります。

  • 複数の場所にコードが散在してしまい、コードの整理と管理が困難になり易い
  • 既存のFLAのコードに対する拡張が困難
  • コードの変更はFLA内でのみ可能
  • FLAファイルの作業を行えるのは、一度に1ユーザだけ

これらの欠点を解決するために、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ファイルの「制御」→「ムービープレビュー」を実行してみましょう。

package {
	import flash.display.Sprite;
	public class Main extends Sprite {
		public function Main() {
			trace("こんにちは、世界!");
		}
	}
}

すると、出力パネルに「こんにちは、世界!」というメッセージが表示されるはずです。これは、SWFが実行される際に正しくドキュメントクラス「Main.as」が読み込まれ実行されたことを意味しています。

ライブラリに格納したムービクリップをステージに配置

先週の授業では、タイムラインスクリプトから、ライブラリに格納したムービークリップシンボルを呼び出してステージに表示するというサンプルについて紹介しました。

これと同じ操作を、ドキュメントクラスから行うことも可能です。やり方はタイムラインスクリプトから呼び出す際と同様に、まず作成したムービークリップにクラス名を割り当て(例:MyCircle)、ドキュメントクラスからそのクラスを呼び出し実体を生成(インスタンス化)します。あとは、addChild() を使用して円のインスタンスをステージ上に配置すると、画面に表示されます。

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);
		}
	}
}
/wp-content/uploads/2010/10/ClassTest02.swf, 400, 300

複数のムービクリップをステージに配置する

ライブラリに格納した複数のムービクリップを、それぞれ配置することも簡単です。例えば、丸、四角形、三角形の形をそれぞれMyCircle、MyRect、MyTriangleとうクラス名でMovieClipとしてシンボル化し、ライブラリに格納します。この例では、それをドキュメントクラスから順番に呼び出して、ステージ上に配置しています。

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);
		}
	}
}
/wp-content/uploads/2010/10/ClassTest03.swf, 400, 300

今日のまとめ

今日の授業では、FLAファイルとActionScriptファイルを分離する方法として、ドキュメントクラスの作成について学びました。

ドキュメントクラスを使用することで、ActionScriptとFLAファイルを分離することで、FLAファイルはムービークリップや画像、音声などFlashに必要となるリソースを格納するためだけの機能として使用し、その動きやインタラクション、画面遷移などのロジックは全て外部ファイルのActionScriptとしてプロジェクト全体を構成していくことが可能となります。

Flashのデザインに必要なパーツとロジックを分離することで、複数人での共同作業や、大規模で更新可能なFlashプロジェクトの作成などが可能となります。本格的なFlashコンテンツを制作する際には必須のテクニックと言えるでしょう。

サンプルファイルのダウンロード

今日の授業で紹介したサンプルは下記からダウンロードしてください。


HTML基礎1 – 文書型宣言、文字コードの指定、インラインイメージ

先週の復習

先週は、HTMLの導入としてHTML(= Hyper Text Markup Language)の重要な概念である「マークアップ」について解説しました。またその上で、HTMLのマークアップの基礎、要素と、開始タグ・終了タグについて学び、基本的な要素をいくつか紹介しました。

まず始めに先週学んだ要素について復習してみましょう。

  • HTMLの骨組みを構成する要素
    • html要素:HTML文書のルート要素、全てのHTML書類の土台となる
    • head要素:HTML文書自身に関する情報(例:タイトル、要約、言語、文字コードなど)を指定する
    • body要素:HTML文書の本体部分、Webブラウザに表示される部分
  • 頻出する、基本的な要素
    • title要素:文書のタイトルを指定する要素、head要素内に記入する
    • p要素:段落を構成する
    • h1 – h6要素:見出し、h1から順番に大きな見出しから小さな見出しとなる
    • br:改行位置を指定する

先週学んだ要素を使用して、下記のようなHTML書類を作成することができました。

<html>
  <head>
    <title>はじめてのHTML</title>
  </head>
  <body>
    <h1>はじめに</h1>
    <p>あのイーハトーヴォのすきとおった風、夏でも底に冷たさをもつ青いそら、うつくしい森で飾られたモーリオ市、郊外のぎらぎら光る草の波。あのイーハトーヴォのすきとおった風、夏でも底に冷たさをもつ青いそら、うつくしい森で飾られたモーリオ市、郊外のぎらぎら光る草の波。</p>
    <p>あのイーハトーヴォのすきとおった風、夏でも底に冷たさをもつ青いそら、うつくしい森で飾られたモーリオ市、郊外のぎらぎら光る草の波。あのイーハトーヴォのすきとおった風、夏でも底に冷たさをもつ青いそら、うつくしい森で飾られたモーリオ市、郊外のぎらぎら光る草の波。</p>
    <h2>このページについて</h2>
    <p>あのイーハトーヴォのすきとおった風、夏でも底に冷たさをもつ青いそら、うつくしい森で飾られたモーリオ市、郊外のぎらぎら光る草の波。あのイーハトーヴォのすきとおった風、夏でも底に冷たさをもつ青いそら、うつくしい森で飾られたモーリオ市、郊外のぎらぎら光る草の波。</p>
  </body>
</html>

今日は、このHTMLをより「正確に」宣言するための方法と、さらにいくつかの基本的な要素について学んでいきます。

より「正確な」HTMLにする 1 – 文書型宣言

初回の授業で解説したように、ひとくちにHTMLといっても様々なバージョンが存在します。それぞれのバージョンで使用できる要素や文法規則に差異があります。

様々な環境で正しく文書を閲覧してもらうには、その文書がどのバージョンのHTMLに準拠して記述されているのかを宣言する必要があります。この、記述されているHTMLがどの定義(= DTD)に基づいて記述されているかを示すことを、文書型宣言と呼びます。文書型宣言は、html要素の手前に記入します。

文書型宣言は、使用したいHTMLのバージョンによって異なります。いくつかの代表的な文書型宣言を紹介します。

HTML4.01 Transitional

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
"http://www.w3.org/TR/html4/loose.dtd">

HTML4.01 Strict

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">

XHTML1.0 Transitional

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

XHTML1.0 Strict

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

XHTML1.1 Strict

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" 
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">

HTML 5

<!DOCTYPE html>

こうして過去のバージョンからの文書型宣言の変遷を眺めてみると、HTML 5 で急激に簡略化された記法になったことがわかると思います。HTML 5 では以前使用されていた、定型句を極力省略しようという方針があります。ですので、この文書型宣言に関しても、とてもシンプルに記述されるようになりました。

この授業では、HTML 5 に準拠してHTMLを記述していきますので、HTML 5 の文書型宣言を使用するようにします。先週作成したHTMLの冒頭ににHTML 5 の文書型宣言を記入しましょう。

<!DOCTYPE html>
<html>
  <head>
    <title>はじめてのHTML</title>
  </head>
  <body>
    <h1>はじめに</h1>
    <p>あのイーハトーヴォのすきとおった風、夏でも底に冷たさをもつ青いそら、うつくしい森で飾られたモーリオ市、郊外のぎらぎら光る草の波。あのイーハトーヴォのすきとおった風、夏でも底に冷たさをもつ青いそら、うつくしい森で飾られたモーリオ市、郊外のぎらぎら光る草の波。</p>
    <p>あのイーハトーヴォのすきとおった風、夏でも底に冷たさをもつ青いそら、うつくしい森で飾られたモーリオ市、郊外のぎらぎら光る草の波。あのイーハトーヴォのすきとおった風、夏でも底に冷たさをもつ青いそら、うつくしい森で飾られたモーリオ市、郊外のぎらぎら光る草の波。</p>
    <h2>このページについて</h2>
    <p>あのイーハトーヴォのすきとおった風、夏でも底に冷たさをもつ青いそら、うつくしい森で飾られたモーリオ市、郊外のぎらぎら光る草の波。あのイーハトーヴォのすきとおった風、夏でも底に冷たさをもつ青いそら、うつくしい森で飾られたモーリオ市、郊外のぎらぎら光る草の波。</p>
  </body>
</html>

より「正確な」HTMLにする 2 – 文字コードを指定する

日本語の文書をインターネット上で流通させる際に、ひとつ面倒な問題が存在します。それは文字コードに関する問題です。コンピュータで文字を扱う際には、その各文字に対してコードを割り振っていく必要があります。日本語に関しても全ての文字と対応した文字コードの基準が存在していて、それを元にコンピュータ上でテキストを扱っています。

ところが、日本語を表現するための文字コードは、その歴史的な経緯から複数の文字コードが混在してしまっています。良く使用される文字コードとしては、下記のものがあげられます。

  • 日本語固有の文字コード
    • ISO-2022-JP:俗に「JISコード」と呼ばれる、主にインターネット上での文字の流通(特に電子メール)などで使われている
    • Shift_JIS:日本におけるパソコン用の文字コードとして発展、Windows系のOSを中心に使用されてきた
    • EUC-JP:UNIX系のOSで日本語の文字を扱う際に使用されてきた文字コード
  • 多言語用文字コード (Unicode)
    • UTF-8:言語間での互換性問題を解決するためにすべての言語の文字コードを1つの文字コードで対応したもの、現在インターネットではもっとも一般的に利用されている

ここに取り上げた以外にも数多くの日本語用の文字コードが存在します。このように、日本語を表記するだけでも、様々な文字コードが存在しており、きちんと指定しないといわゆる「文字化け」という現象が起きてしまします。どの文字コードを使用するのが正解というものはないのですが、この授業では、XHTMLで標準的に用いられている、UTF-8を標準の文字コードとして使用するものとします。

HTML 5 の規格にのっとって文字コードを指定してみましょう。文字コードを指定するには、head要素の中にmeta要素を使用して指定します。UTF-8を指定する場合には、meta要素は、下記のような記述になります。

<meta charset="utf-8" />

この文字コードの記法も、HTML 5 から大幅に簡略化されました。では、作成した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>

これにより、ほとんどのブラウザで「文字化け」を防ぐことができるでしょう。

画像を貼りつける – img要素

WWWは、ハイパーテキストだけでなく、画像や動画、音声なども文書に貼りつけることで表現できることに大きな特徴があります。画像や動画など様々なメディアを効果的に用いることでページを効果的に見せることが可能です。

メディアの利用の第一歩として、画像をページに貼りつけてみましょう。

画像をWebページに貼りつけるには、img要素を使用します。具体的には下記のように記述します。img要素もbr要素と同様に、終了タグのない内容をもたない空要素ですので、末尾は「/>」で閉じる必要があります。

<img src="画像ファイルのURL" alt="画像の説明" />

alt属性は、代替文字を用意して、画像を表示しない利用者のブラウザで代わりにその文字が表示するためのものです。alt属性は様々な状況で正しく情報を提供するためにとても重要なもので、HTML4以降では必須となっています。必ず画像の内容を要約する内容を記述するようにします。

相対URLと絶対URL

src属性には画像ファイルの場所(URL)を示します。このURLは、Webブラウザのアドレス欄に入力するURLと同じものです。例えば、千葉商科大学のトップページの大学のロゴの画像の場所は「http://www.cuc.ac.jp/img/logo_top.gif」にあります。この画像を自分の作成するWebページに貼りつける場合は、下記のようになります。

<img src="http://www.cuc.ac.jp/img/logo_top.gif" alt="千葉商科大学" />

千葉商科大学

画像ファイルはこの千葉商科大のロゴの画像のように、Webサーバーのマシン名から始めた完全なURLで記述することも可能ですが、もし画像ファイルが同じWebサーバ内に格納されている場合は、画像を貼りつけるHTMLファイルから見た相対的な場所で指定することも可能です。このような貼り付けるファイルからみた場所の指定のやりかたを「相対URL」と呼びます。それに対して先程の千葉商科大のロゴのように、先頭からの完全なアドレスで指定する方法を「絶対URL」と呼びます。

相対URLの例をいくつかみてみましょう。

画像ファイルとそれを貼りつけるHTMLファイルが同じディレクトリ(フォルダ)にある場合には、相対URLはファイル名を指定するだけです。例えば、画像ファイル名が「photo.jpg」だったとすると、URLを入れたimg要素は下記のようになります。

<img src="photo.jpg" alt="写真" />

HTMLファイルと同じ場所にひとつ新たにディレクトリを作成して、その中に画像ファイルを入れた場合は、階層構造の区切りをスラッシュ「/」で表現します。HTMLファイルと同じ場所にあるディレクトリの名前を「image」その中に配置した画像ファイルが「photo.jpg」とした場合、相対URLは「img/photo.jpg」となります。よって、img要素は下記のように記述されます。

<img src="image/photo.jpg" alt="写真" />

より複雑な相対URLについて考えてみます。

HTMLファイルのひとつ上の階層に、画像を保存するディレクトリ「image」があり、その中に「photo.jpg」があったとします。その際には、相対URLは「自分自身から一つ上の階層にあるimageディレクトリの下のphoto.jpg」というように考えます。一つ上の階層を表現するには「../」という記号を使用します。よって、一つ上の階層のimageフォルダの下のphoto.jpgファイルを相対URLで表現すると「../image/photo.jpg」となります。

<img src="../image/photo.jpg" alt="写真" />

この、ひとつ上のを表現する「../」という記号を重ねてどんどん上の階層へ辿ることも可能です。例えば2つ上の階層は「../../」3つ上の階層は「../../../」というように表現します。

実習 – 自己紹介のページを紹介する

ここまで学んだ要素を駆使して、簡単な自己紹介のページを作成してみましょう。下記の要素を盛り込んでページを作成するものとします。

  • 自分自身の顔写真、もしくは自分で撮影した写真
  • 名前
  • 所属、学籍番号
  • 自分自身の簡単な自己紹介を複数の段落に分けて記入する
    • 必要に応じて見出しを使用してわかりやすく、読みやすいように工夫する

openFrameworks – メディアファイルを読み込む – 音声、画像、動画

外部メディアファイルを読み込む

openFrameworkに、音声や、画像、動画ファイルを読み込んでプログラムの中で使用することが可能です。様々なメディアを読み込んで活用する方法について解説していきます。

サウンドファイルの再生 (ofSoundPlayer)

まず始めにサウンドを扱ってみましょう。既存のサウンドファイルのデータを読み込んで再生します。openFrameworksでは、サウンドファイルを読みこんで再生するためのofSoundPlayerというクラスが用意されています。ofSoundPlyaerは、Waveファイル(.wav)、Aiffファイル(.aif)、MP3ファイル(.mp3)、RAWファイル(.raw)など様々なファイル形式のサウンドファイルを読み込んで再生することが可能です。

まずはじめに、サウンドファイルを再生する簡単なサンプルを作成してみましょう。

testApp.h

#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

testApp.cpp

#include "testApp.h"

void testApp::setup(){
    mySound.loadSound("glitch_loop.wav"); //サウンドファイルの読込み
    mySound.setLoop(true); //ループ再生をONに
    mySound.play(); //サウンド再生開始
}
/* 後略 */

ofSoundPlayerはクラスなので、使用するためにはまずインスンタンス化する必要があります。このサンプルでは、インスタンス化はtestApp.hの中で行っています。次に、ofSoundPlayerのインスタンスのメソッドを利用して、サウンドファイルの読み込みをします。再生するサウンドファイルは、アプリケーションの実行ファイルと同じ場所にある「data」フォルダの中に配置します。

openFrameworksのプロジェクト内では、サウンドファイルの配置場所は

  • 《プロジェクトフォルダ》/bin/data/ になります。

ofSoundPlayerを利用すると、サウンドファイルの読み込み、再生の他にも様々な指定が可能です。先程のサンプルでは、再生の際にループするように設定していました。

  • 《ofSoundPlayerのインスタンス》.loadSound(《サウンドファイル名》);
    • サウンドファイルの読み込み
  • 《ofSoundPlayerのインスタンス》.play()
    • サウンドの再生開始
  • 《ofSoundPlayerのインスタンス》.stop()
    • サウンドの再生終了
  • 《ofSoundPlayerのインスタンス》.setVolume()
    • サウンドの音量を設定
  • 《ofSoundPlayerのインスタンス》.setPan()
    • サウンドの定位(左右の音量のバランス)を設定
  • 《ofSoundPlayerのインスタンス》
    • サウンドの再生のスピードを設定、再生スピードを上げるとピッチも上がる
  • 《ofSoundPlayerのインスタンス》.setLoop()
    • ループ再生をするかどうか設定
  • 《ofSoundPlayerのインスタンス》.setMultiPlay()
    • 同じサウンドを一度に重ねて再生するかどうか設定

もう少し、サウンドファイルを操作できるように改良してみましょう。画面上でマウスボタンを押すと再生開始、マウスボタンを離すと再生終了するにしてみます。また、マウスのy軸上の位置で再生スピードを変更、マウスのx軸上の位置で、左右の音量バランスを変更できるようにもしてみましょう。

testApp.h

#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

testApp.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(); //サウンド再生終了
}

/* 後略 */

さらに工夫を加えてみます。現在再生しているサウンドの音量を取得して、その音量にあわせて円の半径を変化させてみましょう。音を再生する様子を視覚的に捉えることができるようになります。

testApp.h

#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

testApp.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(); //サウンド再生終了
}

/* 後略 */

画像ファイルを扱う (ofImage)

音に続いて、画像データを扱ってみましょう。静止したデジタル画像データは、コンピュータ画面上に図形を描画する際と同様に、X軸方向とY軸方向にグリッド状に整列したピクセルの集合体です。それぞれのピクセルの色の濃度を数値として記録しています。現在のほとんどのコンピュータは、32bitのカラーを表示できる性能があります。32bitカラーの場合、RGBA(Red、Green、Blue、Alpha)それぞれの色の値で8bitずつ、つまり256段階で色の階調を記録しています。

画像データをハードディスクなどの記憶装置などに保存する際には、ピクセルの数値データをそのまま保存するとファイルの容量が巨大になってしまうため、計算処理によりデータを圧縮して容量を削減して保存する場合がほとんどです。情報の圧縮の方法、保存の形式などによって、様々な画像ファイルの形式が存在します。そのため、画像ファイルを読み書きするためには、こうした画像フォーマットの規格に沿って、データを取り扱う必要があります。

openFrameworksでは、画像ファイルの読み込みと書き出しに「freeImage」という既存のライブラリをopenFrameworksから利用できるようにした、ofImageクラスを利用します。ofImageクラスの機能を介して、PNG、BMP、Jpeg、TIFFなどの主要な形式の画像をデータとして読み込んだり、生成した画像データをファイルとして書き出すことが可能となります。

では、実際にofImageを利用して画像ファイルを読み込んで画像データとして利用したり、画面をキャプチャーしたデータを画像ファイルとして保存してみましょう。

新規プロジェクト「ShowImage」を作成します。

testApp.h

#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

testApp.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");
    }
}
/* 後略 */

まず、testApp.hで、ofImageのインスタンスを2つ生成しています。myImageは、既存の画像ファイルを読み込んで、データとして利用するためのものです。また、grabbedImageはopenFrameworksで生成した画面の領域を指定して、その枠内の画像データをキャプチャーして画像データとして保存するため使用します。

testApp.cppでは、ofImageを利用した画像ファイルの読み込み、画像ファイルの解析、画像ファイルの書き出しという一連の処理を行っています。

ofImageのインスタンスに対して、「loadImage(“ファイル名”)」というメソッドを実行して、指定した場所にある画像ファイルを画像データとして読み込んでいます。ここで読みこむ画像ファイルは、音声データと同様に、openFrameworksのプロジェクトフォルダ内にある「bin」→「data」フォルダ内に配置します。読込むファイルの画像形式は、そのファイルの拡張子から類推して自動的に判別しています。

  • PNG – .png
  • JPEG – .jpg .jpeg
  • TIFF – .tiff .tif
  • TGA – .tga .tpic
  • GIF – .gif

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形式で保存しています。

動画の再生 (ofVideoPlayer)

次に、動画ファイルを読み込んで再生してみましょう。openFrameworksでは、QuickTimeのライブラリを利用して動画の読み込み、再生を行っています。OpenFrameworksからQuickTimeを利用するためには、ofVidePlayerクラスを使用します。ofMoviePlayerクラスを介して、QuickTime形式のファイルを読み込み、画面上で再生したり解析することが可能です。openFramwroksで扱う動画は、パラパラマンガのように、変化する画像データを指定した間隔で次々に表示していると考えると良いでしょう。

testApp.h

#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

testApp.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){
}

testApp.hでは動画を再生するためのofVideoPlayerクラスのインスタンスfingersMovieを生成しています。

testApp.cppで、実際に動画ファイルを読み込んで再生します。ムービーファイルを読み込むには、ofMoviePlayerクラスのインスタンスに対して「loadMovie(“ムービーファイル名”)」というメソッドを実行します。ムービーファイルのデータは画像やフォントと同様に、「bin」→「data」フォルダに格納します。

読み込んだムービーを再生するには、いくつかの手続きが必要です。まず、読み込んだムービーを再生状態にします。ムービーの再生には「play()」メソッドを使用します。この命令によって読み込んだムービーの再生を開始します。再生状態になったムービーに対して、testAppのupdate()メソッド内では「idleMovie()」メソッドを使用して待機状態にしておく必要があります。画面上に再生しているムービーを表示するために、draw()関数の中でofVideoPlayerのインスタンスに対して「draw(表示位置のX座標, 表示位置のY座標)」というメソッドを使用します。この命令によって、ムービファイルの内容を画面に表示されます。

動画データが画像やフォントと大きく異なる点は、時間軸をもったデータだというところでしょう。ofVideoPlayerクラスには、ムービーの時間軸を操作するためのメソッドがいくつか用意されています。

  • ofVideoPalyerのインスタンス.play()
    • 動画の再生
  • ofVideoPalyerのインスタンス.stop()
    • 動画の停止
  • ofVideoPalyerのインスタンス.idleModvie()
    • 動画を待機状態にする
  • ofVideoPalyerのインスタンス.firstFrame()
    • 動画の最初のページに戻る
  • ofVideoPalyerのインスタンス.setPosition()
    • 動画の指定した時間へ移動

このサンプルでは、キーボードから「0」キーを押すと、最初のフレームに戻り、マウスをドラッグするマウスポインタのX軸上の位置に応じてムービーの再生位置を変化させることで、動画をタイムラインで捜査できるようにしています。

ofVideoPlayerで取得したムービーデータは、画像ファイルと同様に、ピクセル単位で解析することが可能です。そのためには、ofVideoPlayerのインスタンスに対して「getPixels()」というメソッドを実行することで、RGBに分解された状態で、それぞれのピクセルの濃度を取得することができます。サンプルではその値を利用して、映像を点描風にピクセレイトしています。

動画のキャプチャー (ofVideoGrabber)

次にコンピュータに接続したカメラから動画をリアルタイムに読み込んで利用してみましょう。動画をカメラから読み込むには、ofVideoGrabberクラスを利用します。コンピュータにUSBやFireWire(IEEE 1394)で接続したライブカメラや、コンピュータに内蔵されたカメラを利用することが可能です。

testApp.h

#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

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

/* 後略 */

testApp.hで、ofVideoGrabberクラスのインスタンス、vidGrabberを生成しています。また同時にカメラから画像をとりこむ際の幅と高さを記憶するために、camWidthとcamHeightという変数を用意しています。

testApp.cppでは、まずカメラから取り込む画像の幅と高さを指定しています。その上でカメラ取り込みの初期化を行います。初期化をするには、ofVideoGrabberのインスタンスに「initGrabber(幅、高さ)」というメソッドを実行します。

実際にデータを取り込むには、ofVideoGrabberクラスのインスタンスにgrabFrame()メソッドを実行します。このメソッドを実行するたびに、新規にカメラから1フレーム画像を読み込まれ、インスタンス内にデータとして格納されます。読み込みの際には、動画としてカメラから画像を取得するために、grabFrame()メソッドをtestAppのupdate()内で繰り返し実行するようにしています。読み込まれた画像を画面に表示するにはofVideoGrabberのインスタンスに「draw(表示位置のX座標, 表示位置のY座標)」メソッドを実行します。

カメラから読み込まれが画像データは、ofImageと同じ方法でピクセル単位で解析することが可能です。ofVideoGrabberのインスタンスに「getPixels()」メソッドを実行して、配列に画像のピクセルごとのデータを読み込みます。これを利用することで、カメラからの映像をリアルタイムに解析し加工することが可能となります。サンプルでは、カメラからの映像を利用して、点描風に画像処理しています。

サンプルファイルのダウンロード


ActionScript 3.0 入門1 – ムービークリップのプロパティを操作する

先週の復習

今日の授業はいよいよActionScriptを利用したFlashコンテンツの作成について学んでいきますが、その前にまず先週のタイムラインベースのアニメーションについておさらいしてみましょう。

フレームベースのアニメーションの制作方法のおさらい

  • Flashファイル (AS 3.0) で新規に書類作成
  • 図形を描いて、シンボル化
  • モーショントゥイーンを作成
  • 図形をマウスでドラッグして移動する
  • キーフレームとキーフレームの間がスムースに補完される
  • モーションパスを操作して曲線にすることも
  • キーフレームを後から追加して複雑な動きに
  • 複数の物体を動かすには、レイヤーを追加

ActionScriptとは

ActionScriptは、アドビシステムズ社の製品であるFlashに使用されるプログラミング言語です。ActionScriptを用いることにより、動画や音声のプレイヤーの作成など、コンテンツに複雑な処理や双方向性を持たせたFlashを作成することが可能になります。

この教室にインストールされている Adobe Flash CS4 では、ActionScript 3.0、またはActionScript 2.0 を使
用することができます。ActionScript 3.0はクラスベースのオブジェクト指向言語で、大規模な開発が可能となっています。

スクリプトでシンボルを操作する

まずは、簡単な例でActionScriptによるプログラミングを体験してみましょう。

以下の手順でステージ上に配置したムービクリップシンボルを、ActionScriptを使用して大きさや場所などを操作してみます。

  • Flashファイル (AS 3.0) で新規に書類作成
  • 図形を描いて「ムービクリップ」としてシンボル化
  • シンボルをステージ上に配置する
  • インスタンス名をつける、例:rect
  • レイヤーを追加して、アクションを開く
  • タイムラインの1フレームを選択し、右クリックして「アクション」を選択
  • スクリプトを入力
  • ActionScriptでシンボルの場所を変化させる – rext.x, rext.y
  • 大きさを変化させる – rect.width, rect.height, rect.scaleX, rect.scaleY
  • 角度を変化させる – rect.rotation

ムービークリップとしてシンボルに変換

インスタンス名の設定

空白のキーフレームからアクションを開く

アクションが追加された状態

//位置を指定
rect.x = 320;
rect.y = 480;

//大きさを変化
rect.width = 200;
rect.height = 50;

//角度を変化
rect.rotation = 30;

フレームに記入するActionScript

この例のようにActionScriptはキーフレームの中に記入することが可能です。(キーフレームにではなく、独立したファイルとしてActionScriptを記入する方法もあります)。キーフレームに記入したActionScriptは、そのフレームを再生する際に実行されます。

ムービクリップシンボルとプロパティ(Property)

この例ではいったい何をしているのか、考えてみましょう。

今回はステージ上に図形を描いてシンボル化する際に、「グラフィック」ではなく「ムービークリップ」という種類を選択しました。ムービークリップでシンボル化すると、自分自身に関する様々な性質をシンボル内に保持することが可能となります。この自分自身に関する性質のことをプロパティ(Property)と呼びます。

ActionScriptからこのプロパティを操作することで、ステージ上に配置した際の性質を後から変更することが可能となります。例えば、位置(X座標とY座標)、大きさ(幅、高さ)、角度などが変更可能です。

このプロパティの操作を使用することで、ActionScriptを利用したアニメーションやインタラクティブなオブジェクトの生成が可能となります。これからActionScriptを使用したコンテンツを制作していく基本となります。

ステージへの配置もスクリプトで行う

ムービークリップなどのシンボルは、シンボル化した時点で「ライブラリ」に登録されます。ActionScriptを使用することで、ライブラリからステージ上に配置する操作自体を行うことも可能です。

以下の手順で、ActionScriptを使用してムービクリップシンボルをライブラリからステージ上に配置して、様々な属性(プロパィ)を操作してみましょう。

  • Flashファイル (AS 3.0) で新規に書類作成
  • 図形を描いて「ムービクリップ」としてシンボル化
  • 作成したシンボルを「ライブラリパネル」から選択し、プロパティを開く
  • 詳細メニューを表示する
  • クラス名を指定する 例:Rect
  • 基本クラスは、flash.display.MovieClip のままで
  • シンボルはステージには配置しない
  • スクリプトを入力

ライブラリに追加された四角形

シンボルにクラスを適用

//新規に四角形を生成
var r:Rect = new Rect();

//表示する場所を指定
r.x = 320;
r.y = 240;

//ステージ上に追加する
this.addChild(r);

フレームに記入するActionScript

今日のまとめ

ActionScriptを使用したFlashコンテンツの制作の導入として、ステージ上に配置したムービクリップの性質(プロパティ)を、ActionScriptを使用して操作しました。この方法は今後スクリプトを用いてアニメーションやインタラクションを作成していく際にとても重要となってきますので、しっかりと理解するようにしましょう。


HTML入門

先週の復習:Webを支える重要な3つの仕組み

まず先週の最後にまとめた、Webを支える重要な3つの仕組みについて簡単におさらいします。

  • URL
    • Uniform Resource Locator
    • ネットワーク上の情報を一意に特定するアドレスの指定方法
  • HTTP
    • HyperText Transfer Protocol
    • コンピュータ同士が情報をやりとりするルール
  • HTML
    • HyperText Markup Language
    • 環境にかかわりなく、WWWを記述するための文書記述言語

これからこの授業でWebのページを作成していくということは、この3つの仕組みのなかのHTML(Hyper Text Markup Language)を記述するという作業になります。

では、HTMLとは何なのか、より詳細について考えていきます。

HTML = ハイパーテキスト(Hyper Text) + マークアップ(Markup)

HTMLはHyper Text Markup Languageです。これを直訳すると「ハイパーテキストをマークアップするための言語」となるでしょう。「ハイパーテキスト」はハイパーリンクによって複数の文書を関連づけ結びつける仕組みを意味するということを先週の授業で解説しました。詳細は先週の授業資料を参照してください。

マークアップ(Markup)とは何か?

では、HTMLという用語に含まれるもうひとつのキーワード「マークアップ(Markup)」とは何でしょうか? マークアップはもともとは出版物の原稿に印刷に関する指示などを記号として原稿用紙の余白に書き込んだことを意味しています。HTMLにおけるマークアップも、文章の中に目印を記入していくことを意味しています。では、文章の中に、何を目印として記入していくのでしょうか。実際の簡単な文書を例に考えてみます。

例えば下記の図のような運動会のお知らせの文書があったとします。わたしたちは、この文書をちらっと見るだけで全体の構造を瞬間的に把握することが可能です。

この文書を良くみると、その構造を瞬間的に理解させるために様々な工夫がされていることに気付きます。例えば、ヘッダーとフッターにあたる部分は文字を右詰めにして他の部分と区別しています。また文書の見出しは太く大きい文字にすることで、目立たせています。また各段落の間に空白を入れるとで文章の意味のブロックを明確にしています。もし、全く同じ内容の文書を、全く何の変化をつけず同じ文字の大きさで改行も一切入れずに作成したら、おそらくとても読みとりずらい文書になるでしょう。

この文書をその役割ごとに分割して整理すると、下記の図のようになります。

つまり、この「運動会開催のおしらせ」のようなシンプルな文書の中にも、その役割に応じて様々なブロックに分かれています。その文書内のパート毎の役割を強調するために適切なデザインをすることで、文書の構造が明快になり、結果として読みやすい文書になるのです。

マークアップとは、文書の構造を指定して文書のなかに書き込んでいくことを意味します。先程の例でいうと、「ヘッダー」「見出し」「段落」「箇条書き」「フッター」といった構造を文書の中に書き込んでいきます。文書の中にその構造に応じた適切な目印(マークアップ)を記入していくことが、HTML作成の主な作業となります。

マークアップの基本:どこから(開始)どこまで(終了)が、何の要素か

文書の中で、ある特定の部分に目印をつける(= マークアップ)には、その始点と終点に目印をつけて、その役割は何かを指示すれば正しく伝えることが可能です。HTMLでも、その要素がどこから始まって(開始)、どこで終わるのか(終了)を目印をつけて指示していきます。HTMLではこの開始位置と終了位置を伝える目印のことを「タグ」と呼んでいます。

「タグ」に記述する内容は下記の3つとなります。

  1. どこから:開始タグ
  2. どこまで:終了タグ
  3. 要素の種類:要素タイプ

例えば、h1という一番大きな見出しを意味する要素をマークアップするには、下記のように記述します。

HTMLの文書の中では、山括弧 < > が重要な意味を持ちます。この < > で囲まれた部分は、文書内にマークアップした命令を意味しています。これを「タグ」と呼んでいます。要素のはじまりを示す開始タグは、<要素名> 、要素の終わりを示す終了タグは </要素名> とスラッシュ「/」のあり・なしで区別されています。

この例では、h1要素という文書内の「大見出し」を表す要素で説明しましたが、この「h1」の部分にあたる要素名を変えていくことで、様々な文書内の構造を記述することが可能となります。今日はこのなかから頻繁に仕様されるHTMLの代表的な要素をいくつか紹介します。

※ この授業では、XHTML 1の文法に準拠してタグを記述していきます。XHTMLでは全てのタグの要素名は小文字で記述するように決められています。ですので、この授業ではタグの要素名は小文字で記入するものとします。

HTML文書の骨組みを記述する – <html> <head> <body>

ほとんどのHTML文書は下記のような構造をしています。

  • html要素 – 文書全体を包括する要素。その文書がHTMLであることを示している。
  • head要素 – その文書自身の情報を記述する要素。タイトル、文字コード、キーワード、要約などが入る。
  • body要素 – 文書の本体を表す要素。Webブラウザ内に表示される部分。

実際にこのHTML文書の大枠の構造を記述してみましょう。テキストエディタ(この授業では、EmEditorを使用します)を起動して、下記のHTMLを記入します。記入を終えたら、「index.html」というファイル名で保存してください。

<html>
  <head>
  </head>
  <body>
  </body>
</html>

文書の題名を記述する – <title>

では、骨組みだけ記述してまだ内容は空白のHTMLファイルに題名を記入しましょう。文書の題名はその文書自身に関する情報なので、head要素の中に記入します。文書の題名はtitle要素を使用します。

今後ページを公開した際に、このtitle要素をその文書全体の内容を的確に要約した内容で記述することがとても重要です。たとえばユーザがそのページをブックマークすると、title要素の内容がブックマークの名称として利用されます。また、検索エンジンで検索した結果もtitle要素が最初に表示され、該当ページへのリンク部分となります。

では、先程のファイルにtitle要素を付加してみましょう。

<html>
  <head>
    <title>はじめてのHTML</title>
  </head>
  <body>
  </body>
</html>

この状態でファイルを保存して、Webブラウザで開いてみましょう。ページの内容は空白ですが、ウィンドウのタイトルにtitle要素で指定したタイトルが表示されているはずです。

段落を記述する – <p>

それでは、いよいよページの本体、body要素に文章を記述していきましょう。今日は最も基本的な要素で、頻繁に用いられる2つの要素、「段落」と「見出し」についてマスターしましょう。

まず初めに「段落」を記述します。HTMLでは、段落のひとつひとつを要素としてマークアップします。段落を表す要素は、p要素です。

では先程タイトルだけ記入したページに、段落で文章を入れてみましょう。

<html>
  <head>
    <title>はじめてのHTML</title>
  </head>
  <body>
    <p>あのイーハトーヴォのすきとおった風、夏でも底に冷たさをもつ青いそら、うつくしい森で飾られたモーリオ市、郊外のぎらぎら光る草の波。あのイーハトーヴォのすきとおった風、夏でも底に冷たさをもつ青いそら、うつくしい森で飾られたモーリオ市、郊外のぎらぎら光る草の波。</p>
    <p>あのイーハトーヴォのすきとおった風、夏でも底に冷たさをもつ青いそら、うつくしい森で飾られたモーリオ市、郊外のぎらぎら光る草の波。あのイーハトーヴォのすきとおった風、夏でも底に冷たさをもつ青いそら、うつくしい森で飾られたモーリオ市、郊外のぎらぎら光る草の波。</p>
  </body>
</html>

では、この状態でページをプレビューしてみましょう。それぞれの段落が空白行で区切られて、ひとかたまりの段落とわかるようにレイアウトされると思います。HTMLで本文の文章を書く場合には、基本はこのp要素でマークアップしていくことになります。

見出し – <h1> 〜 <h6>

では次に、段落に分けた文章を要約する「見出し」をつけてみましょう。わたしたちが普段文章を書く場合、見出しはその重要度や包括する内容の広さの段階に応じて、大きな見出しから小さな見出しへと段階をつけて記述しています。HTMLの見出しも同様に全部で6つの段階があります。見出しのレベルが大きい順から、h1, h2, h3, h4, h5, h6 と段階的に定義されています。

実際にこの見出しを順番に記述すると、Webブラウザ側でその見出しのレベルに応じて文字の大きさや太さなどが調整されて、見出しの段階が視覚的にわかるように工夫されています。

しかし、ここで注意しなくてはならないことは、h1〜h6要素は見出しを表現するものであって、文字の大きさや太さを表現するためのものではないという点です。文字を強調したいために見出しの要素を用いたり、あまり大きな文字で表示したくないので、見出しのレベルを低いものから始めたりすることは正しいHTMLの構造ではありません。文字の大きさや太さの設定は、今後の授業で別の方法(スタイルシート)で設定していきます。ですので、この段階では文字の大きさや太さに関しては気にせず、あくまで文書の構造を正確にマークアップするように心掛けてください。

では先程の段落を記述したページに、見出しを入れてみましょう。

<html>
  <head>
    <title>はじめてのHTML</title>
  </head>
  <body>
    <h1>はじめに</h1>
    <p>あのイーハトーヴォのすきとおった風、夏でも底に冷たさをもつ青いそら、うつくしい森で飾られたモーリオ市、郊外のぎらぎら光る草の波。あのイーハトーヴォのすきとおった風、夏でも底に冷たさをもつ青いそら、うつくしい森で飾られたモーリオ市、郊外のぎらぎら光る草の波。</p>
    <p>あのイーハトーヴォのすきとおった風、夏でも底に冷たさをもつ青いそら、うつくしい森で飾られたモーリオ市、郊外のぎらぎら光る草の波。あのイーハトーヴォのすきとおった風、夏でも底に冷たさをもつ青いそら、うつくしい森で飾られたモーリオ市、郊外のぎらぎら光る草の波。</p>
    <h2>このページについて</h2>
    <p>あのイーハトーヴォのすきとおった風、夏でも底に冷たさをもつ青いそら、うつくしい森で飾られたモーリオ市、郊外のぎらぎら光る草の波。あのイーハトーヴォのすきとおった風、夏でも底に冷たさをもつ青いそら、うつくしい森で飾られたモーリオ市、郊外のぎらぎら光る草の波。</p>
  </body>
</html>

このHTMLをWebブラウザで開くと下記のように表示されます。一番大きな見出しh1要素と、次に大きな見出しh2要素が表示されています。

HTMLは文書の構造の記述に専念する

ここまでの解説で、文書の中にに「タグ」と呼ばれる目印を記入していくことで、簡単にWebブラウザで表示可能なHTML文書が作成できることが実感できたと思います。HTMLを学ぶには、まずはHTMLの要素の種類とその用法を憶えていくことになります。理解した要素が増えるほど、よりその文書の意味に忠実な、きめ細かなマークアップをしてくことが可能となります。

ここで、注意しなくてはならない点があります。様々な「タグ」= 要素を知るにつれて、そのタグを組み合せて、文書の体裁やデザインを整えていこうとしてしまう人がいます。たとえば、タグの種類を変化させて、本文の文字の大きさを変えてみたり、文字の配置をタグをトリッキーに駆使してレイアウトしたりといった方法です。

HTMLで文書の体裁(= デザイン)を整えようとするのは、正しい使用法ではありません。HTMLはあくまで文書の「構造」のみを記述する目的で使用すべきです。文書の構造を正しくHTMLで記述することで、環境に依存せずに世界中のより多くの人が共有できるようになります。またサーチエンジンなどのコンピュータのプログラムにとっても理解し易い形式であるといえます。

文書の体裁(= デザイン)を整える方法は、この授業の後半で取り扱います。ですので、それまではWebブラウザで表示される見た目は気にせずに、HTMLを作成する際には、段落はp要素、見出しはh1 〜 h6要素、というように必ず要素の意味と文書の構造を一致させて、正しく構造を写しとるように心掛けてください。

今日のまとめ

今日の授業では、HTMLの重要な概念「マークアップ」について学んだ上で、簡単なHTMLを作成してみました。その際に、HTMLは文書の構造を記述するための仕組みであるという点に注意してください。

次週は、今日とりあげた「段落」「見出し」といった基本的な要素に加えて、より多様なマークアップのための要素について解説していきます。


Flash入門:タイムラインベースのアニメーションを作成する

Flashでのコンテンツ開発、2つの手法

この授業ではAdobe Flashを用いて動的なコンテンツを作成していく予定です。

Flashを用いて、インタラクティブで動きのあるコンテンツを制作する手法として、大きく分けて2つの手法 (流派?) が存在します。ひとつは、タイムラインをベースにした開発手法、もうひとつは、スクリプト (ActionScript 3) をベースにした、スクリプトベースの開発手法です。

この「Web動画表現」の講義では、最終的には、後者のスクリプトベースでの開発手法を用いて開発を行っていこうと考えています。スクリプトベースの開発手法のほうが、より自由度が高く、しかも大規模なコンテンツの制作に適していると考えているからです。

しかしながら、Flashに全く初めて触れるという初心者にはこのスクリプトベースの開発手法は、少しハードルが高いのも事実です。ですので、今日の講義では、まず最初により基本的な開発手法であるタイムラインをベースにした開発手法について簡単に触れて、Flashの基本操作について習熟したいと思います。

アニメーションのしくみを学ぶ

DVD「ノーマン・マクラレン作品コレクション」パイオニアLDC、より「ノーマン・マクラレンのアニメーション講座 1」を視聴

  • 映画のフィルムとコマ
  • あるコマと次のコマの違いが「動き」となる
  • 撮影台を使用した、アニメーション撮影の手順
  • 直線AからBまで移動するアニメーション
  • 何回の動きでAからBまで移動するのか
  • アニメーションの重要な要素「テンポ」
  • 2地点間の分割数によるテンポのスペクトラム
  • 「動きの距離」がアニメーターにとってとても重要

従来のアニメーションとFlashでのアニメーションの違い

  • 基本的な原理は従来のアニメーションと同じ
  • あるコマと次のコマとの差が動きとなる
  • しかし、より効率的に、なめらかなアニメーションが作成できるよう工夫されている
  • 同じ動作を何度もくりかえすのはコンピュータが得意とすること
  • その部分は人間がやるのではなく、自動化する

トゥイーンとは

  • トゥイーンという概念が重要
  • もともとはアニメーション用語、主要アニメーターが重要なフレーム (主要な変更を含むフレーム) だけを描画し、アシスタントがその間のフレームを描画するという工程を指したもの
  • Flashでも同様に、主要となるコマ(キーフレームと呼ぶ)を描くだけで、あとはアシスタント(コンピュータ)が残りのフレームを自動的に描いてくれる
  • モーショントゥイーン:「動き」のトゥイーン、ステージに配置したもの(インスタンス)を、移動、拡大、縮小、回転などすることができる
  • シェイプトゥイーン:「形」のトゥイーン、ステージに配置したもの(インスタンス)の形状を変化することができる

実際にアニメーションを作ってみる

A地点からB地点まで直線移動するアニメーションを作成する

  • 「Flashファイル(AS3.0)」で、新規作成

  • ツールボックスの楕円ツールを選択して、ステージ上に円を描く

  • 矢印ツールで描いた円の全体を選択する。網点で選択範囲が表示される

  • メニューから「挿入」→「シンボルに変換」を選択。ダイアログボックスが表示されるので、このエレメントの名前をつけ、タイプを「グラフィック」にして[OK]ボタンを押す

  • 作成した物体(インスタンス)が選択された状態で、メニューから「挿入」→「モーショントゥイーン」を選択

  • 自動的に1秒分の長さのモーショントゥイーンが作成される

  • 最終フレームを選択した状態で、物体(インスタンス)を移動する。移動経路(モーションパス)が表示される。

  • モーションパスは変形することも可能、いろいろな動きを作ってみる

  • 最後のフレームの右端にマウスポインタを重ねて、マウスポインタが「←→」という表示になった状態で左右にドラッグすると、アニメーションの長さ(テンポ)を変更できる

  • メニューから「制御」→「再生」または、[Enter]キーで、アニメーションを再生する

拡大、縮小、回転

  • 自由変形ツールを使用すると、移動だけでなく、拡大、縮小、回転することも可能
  • 表示される枠の周囲8箇所にあるコントロールポイントで、拡大、縮小、変形することが可能
  • 枠の角にマウスポインタを近づけて、矢印が回転する表示になったポイントで、物体を回転することができる
  • 変形、回転を使用して、いろいろな動きを試してみる

自由変形ツール

レイヤーの活用

  • 複数のシンボルをアニメーションさせるには
  • ひとつのモーショントゥイーンには1つのシンボルしか使用してはいけない
  • 2つ以上のシンボルがあると、トゥイーンは生成されないはず
  • 複数のシンボルをトゥイーンするには、レイヤーを活用する
  • レイヤー = 層、ステージ上に複数の透明なセルが積み重なっているイメージ
  • レイヤーを重ね合わせることで、複数の動きを同時に表現可能
  • レイヤーの追加は、タイムラインの左下にある、レイヤーの追加ボタンから