Home >> Lecture 10


第10回 画像・音声・アニメーション(12/16)


10−1.タートルと座標

■タートルの位置や角度の取得と設定

☆タートルのgetAngle、getX、getYを使う

タートル・グラフィックスでは、現在タートルがどちらを向いているかを getAngleメソッドで、 現在タートルの中心がどこにいるかの位置を getXメソッドと getYメソッドで 求めることができます。これらは実数で返してくれます。 getIntegerXメソッドと getIntegerYメソッドは、 整数で返してくれます。 これらのメソッドは戻り値がありますので、次のように変数に代入したりして値を得ます。

		
		double  angle = turtle.getAngle( );
		double  x = turtle.getX( );
		double  y = turtle.getY( );
		int  x = turtle.getIntegerX( );
		int  y = turtle.getIntegerY( );
		

これらのメソッドは、タートルの状態を知るために非常に有用です。 なお、タートルの方向はマイナスで表されることもあります。以下の図を参照にして、現在タートルが どちらを向いているのかを把握してプログラムしてください。


図10-1 タートルの方向

以下のプログラムでは、タートルが現在どちらを向いているかを、それぞれの頂点にきたときに、 getAngleメソッドで求めて、それを文字端末(ターミナル)に表示するものです。 このようなタイミングで必要な情報を文字端末に表示させることをデバッグプリント(Debug Print)と 呼びます。 デバッガでいちいちプログラムを止めるよりも、必要な時点での変数やタートルの状態がわかり、 それが表示として残こっていきます。 そのため、多くのプログラマはデバッガを使うときは、よほど根拠が掴めない特殊な場合だけに限る ようにしています。 熟練プログラマは、デバッガを殆ど使わずに、デバッグプリント、つまりプログラム上のある地点に おける変数の値の移り変わりをトレースするだけで問題点を掴み、プログラムを修正していきます。

import sfc.turtle.TurtleFrame;
import sfc.turtle.Turtle;

public class SeaUrchin extends TurtleFrame{

	public static void main(String [ ]  args) { new SeaUrchin( ); }
	
	public void start( ) {
		Turtle  turtle = new Turtle( this );
		turtle.rotate( 6 );
		for ( int i=1; i <= 18 ; i++ ) {
			turtle.forward( 30 );
			printLine( i + "番目の外頂点での角度" + turtle.getAngle( ) );
			turtle.rotate( 168 );
			turtle.forward( 30 );
			printLine( i + "番目の内頂点での角度" + turtle.getAngle( ) );
			turtle.rotate( -168 + 360 / 18 );
		}
	}
}
		

☆実習10-1 うに型を描く

上記の「うに」のような図形を動かしてみなさい。クラス名はPractice1001とします。

☆課題10-1 指定された棘の数を持つうに型を描くプログラム

上記の「うに」のような図形は、棘が18本でしたが、これをユーザが入力できるようにしてみなさい。 値としては、3〜60までの整数値の範囲で受け取るように入力をガードしなさい。 クラス名はSeaUrchinDrawerとします。

☆タートルの大きさ

あまり意味がありませんが、タートルは、次のようなメソッドで大きさを変えることができます。

  setSize( 幅, 高さ )
  setScale( 比率 ) // 1.0はそのままの比率になります
  setScale( x方向の比率, y方向の比率 ) // 1.0はそのままの比率になります
  resetScale( x方向の比率, y方向の比率 ) // 元の大きさに戻ります

☆タートルの位置の設定

タートルは、次のような2つのメソッドで直接ウィンドウの特定の座標に置くことができます。

  setLocation( x座標, y座標 )
  moveTo( x座標, y座標 )

両者の違いは、setLocationがアニメーションを伴わないで、一瞬その位置に置かれるのに対して、 moveToは、タートルが目的地にまず回転して、それから歩いて移動するアニメーションを伴います。 ただし、ペンを上げていないと、初期の位置から、その座標まで軌跡が引かれることになります。 ウィンドウの座標は、左上の端がx座標0, y座標0で、x座標は右側にプラスになるのですが、 「y座標は下方向がプラスになる」という特性を持っています。 ほとんどのウィンドウのプログラミングができるライブラリにおいて、同じような座標系を 持っています。y座標の方向が違うことに注意して下さい。

☆配列の値に応じて軌跡を残すタートル

次のプログラムは、配列の初期値を代入して、タートルを目的の座標に向かわせるものです。 x座標の配列とy座標の配列を持っています。ただし、x座標の配列の要素の値が -10ならばpendown、-20ならばpenupをさせるようにしています。 startメソッドの中だけを記述します。

	public void start(  ) {
		int  tx [ ] = { 100, -10, 300, 300, 220, 220, 180, 180, 100, 100, -20,
			320, -10, 360, 360, 440, 480, 390, 480, 440, 360, 360, 320, 320 };
		int  ty [ ]  = { 100, -10, 100, 140, 140, 300, 300, 140, 140, 100, -20,
			100, -10, 100, 180, 100, 100, 200, 300, 300, 220, 300, 300, 100 };
			
		Turtle  turtle = new Turtle( this );
		
		turtle.setPenColor( Blue );
		turtle.penup( );  // 最初はペンを上げた状態にしておきます
		turtle.moveTo( tx[ 0 ], ty[ 0 ] );  // 最初の位置に移動します。
		for ( int i=1; i < tx.length ; i++ ) {
			if ( tx[ i ] == -10 ) { turtle.pendown( ) ; }  //-10だったら、ペンを下ろす
			else if ( tx[ i ] == -20 ) { turtle.penup( ) ; }  //-20だったら、ペンを上げる
			else{  turtle.moveTo( tx[ i ],  ty[ i ] ); } // それ以外は要素の示す座標へ移動する
		}
	}
		

☆実習10-2 配列で軌跡を残すプログラム

上記のプログラムは、下記の図のようにTKと描くようにしました。 余裕があれば、他にも直線的なアルファベット (AEFHILMNVWXYZ)を描くように配列の初期値を修正してみてください。 クラス名はPractice1002とします。

☆矩形を描く便利なメソッド

タートルグラフィックスでは、4角形や角の取れた4角形を描くのに非常に苦労します。そこで、 使い捨てのタートルを使って(軌跡を残すためだけにしか使われない)、矩形(4角形のこと)を描く 便利なメソッドを定義してみましょう。左上のx, y座標と、幅(width)と高さ(height)の4つの引数を 渡すようにします。 下記のメソッドの記述で、hideTurtle は、軌跡は残して、タートルだけは表示しないという機能を実行するメソッドです。

	void drawRectangle( int x, int y, int width, int height ) {
		Turtle  turtle = new Turtle( this );
		turtle.penup( );
		turtle.setLocation( x, y );
		turtle.pendown( );
		int i=1;
		while (  i <= 4  ) {
			turtle.rotate( 90 );
			if ( i % 2 == 1 ) { turtle.forward(  width ) ; }  // 奇数だったら幅分だけ移動
			else{  turtle.forward( height ); } // 偶数だったら高さ分だけ移動
			i = i + 1;
		}
		turtle.hideTurtle( );
	}
		

これでは、黒の矩形しか描きませんので、java.awt.Colorクラスを使って、色の指定ができるように します。そうすると、上記のメソッドは次のように記述することができます。Javaでは同じ名前のメソッドでも、 引数の数が違えば、シグネチャが異なりますので、違うメソッドとして認識してくれます。

	void drawRectangle( int x, int y, int width, int height, java.awt.Color c ) {
		Turtle  turtle = new Turtle( this );
		turtle.penup( );
		turtle.setLocation( x, y );
		turtle.setPenColor( c );
		turtle.pendown( );
		int i=1;
		while (  i <= 4  ) {
			turtle.rotate( 90 );
			if ( i % 2 == 1 ) { turtle.forward(  width ) ; }  // 奇数だったら幅分だけ移動
			else{  turtle.forward( height ); } // 偶数だったら高さ分だけ移動
			i = i + 1;
		}
		turtle.hideTurtle( );
	}
		

上記のプログラムは枠だけですが、前のように、塗りつぶすメソッドを記述してみましょう。

	void fillRectangle( int x, int y, int width, int height, java.awt.Color c ) {
		Turtle  turtle = new Turtle( this );
		turtle.penup( );
		turtle.setLocation( x, y );
		turtle.setPenColor( c );
		turtle.rotate( 90 );
		turtle.pendown( );
		int i=1;
		while (  i <= height  ) { // 高さ分だけ繰返しを実行
			 turtle.forward( width ); // 横線で描画していきますので、幅分だけ移動
			 if ( i % 2 == 1 ) { turtle.rotate( 90 ); }  // 奇数回は時計回り、偶数回は半時計回り
			 else{ turtle.rotate( - 90 ); }  
			 if ( i < height ) {
			 	turtle.forward( 1 ); // 1ドット下に降ります
				if ( i % 2 == 1 ) { turtle.rotate( 90 ); } else{ turtle.rotate( - 90 ); }
			 }
			 i = i + 1;
		}
		turtle.hideTurtle( );
	}
		

さて、次はボタンなどに使われるちょっと角の取れた矩形を描くようにします。角の丸みをどれくらいに するかを併せて指定できるようにします。丸みを帯びた角は、10度ずつ9回、90度分の円弧を描くようにしています。

	void drawRoundRectangle( int x, int y, int width, int height, int r, java.awt.Color c ) {
		Turtle  turtle = new Turtle( this );
		turtle.penup( );
		turtle.setLocation( x, y );
		turtle.setPenColor( c );
		turtle.rotate( 90 );
		turtle.forward( r );
		turtle.pendown( );
		int i=1;
		while (  i <= 4  ) {
			if ( i % 2 == 1 ) { turtle.forward( width - 2 * r ); }  // 左右の丸み分だけ移動距離は少なくなります
			else {  turtle.forward( height - 2 * r ); } // 上下の丸み分だけ移動距離は少なくなります
			int j=1;
			while (  j <= 9 ) { // 角の丸みを描いています
				turtle.rotate( 10 );
				turtle.forward( 0.5 * r * Math.PI / 9 );
				j = j + 1;
			}
			i = i + 1;
		}
		turtle.hideTurtle( );
	}
		

これらのメソッドをstartメソッドから呼び出して使ってみます。

	public void start( ) {
		drawRectangle( 10, 10, 80, 40 );
		drawRectangle( 100, 10, 80, 40, Red );
		fillRectangle( 190, 10, 80, 40, Magenta );
		drawRoundRectangle( 280, 10, 80, 40, 6, Blue );
	} 
		

☆実習10-3 矩形を描くプログラム

上記のメソッドをもつプログラムを完成させて、実行しなさい。 クラス名はPractice1003とします。

☆課題10-2 グラデーションで矩形を塗りつぶすメソッド

矩形を塗り潰すときは、同じ色で塗りつぶしましたが、これを、白から赤へのグラデーション (Colorオブジェクトを作るときに赤色成分だけを変化させる)で塗りつぶすようなメソッドを作ってみなさい。 1つの横線を描くたびに色を変えていきます。余裕がある人だけがやってください。 クラス名はGradationDrawerとします。

☆課題10-3 カラーテーブルを作ってみる

矩形(四角形)領域を軌跡で塗りつぶすプログラムを利用して、緑色は0で構いませんから、青色、赤色をそれぞれ、 0から240まで16ずつ変化させながら、格子を描くプログラムを記述して見なさい。格子を描く開始位置を指定するのには、 setLocation( ) メソッドを使っても良いでしょう。クラス名は、ColorTableとします。 以下の記述を参照しなさい。

		turtle.setPenColor(  new java.awt.Color( red, 0, blue ) );   // 軌跡の色をred, 0, blueの3原色で混色される色にする。
		

☆円を描くメソッド

次は中心のx, y座標と半径、および色を指定して、円(正確には正60角形)を描画するメソッドを 定義してみましょう。

	public void drawCircle( int x, int y, int r, java.awt.Color c ) {
		Turtle  turtle = new Turtle( this );
		turtle.penup( );
		turtle.setLocation( x, y );   // まず中心座標に行って
		turtle.forward( r );  // 半径分だけ上に登ります
		turtle.rotate( 90 );  // そこで右に向いて
		turtle.setPenColor( c ); // ペンの色を設定して
		turtle.pendown( ); // 描き始めです
		int i=1;
		while (  i <= 60 ) {
			turtle.forward( 2 * Math.PI * r / 60 );  // 1回分の進む距離を求めています
			turtle.rotate( 6 );
			i = i + 1;
		}
		turtle.hideTurtle( );
	}
		

円は、必ず中心を指定するだけはなく、矩形と同じように左上の座標を指定する場合があります。 正方形に内接する円(円弧がちょうど正方形の各辺のどこかに触れる円)は一意に指定できます。 このことを利用して、左上のx, y座標と直径、および色を指定して、円を描画するメソッドを定義して みましょう。半径が直径になった以外は、単に最初に行く場所の記述の式が違うだけです。はい。

	public void drawCircleSquare( int x, int y, int size, java.awt.Color c ) {
		Turtle  turtle = new Turtle( this );
		turtle.penup( );
		turtle.setLocation( x+size/2, y+size/2 );  // 左上の座標から、中心の位置に行きます。
		turtle.forward( size/2 );  // 直径の半分、すなわち半径分だけ上に登ります。
		turtle.rotate( 90 );
		turtle.setPenColor( c );
		turtle.pendown( );
		int i=1;
		while (  i <= 60 ) {
			turtle.forward( Math.PI * size / 60 );
			turtle.rotate( 6 );
			i = i + 1;
		}
		turtle.hideTurtle( );
	}
		

円を塗りつぶしてみましょう。これは、中心から、1ドットずつ移動して、同心円を描いていきます。 うまく塗りつぶせるでしょうか。

	public void concentricCircle( int x, int y, int r, java.awt.Color c ) {
		Turtle  turtle = new Turtle( this );
		turtle.penup( );
		turtle.setLocation( x, y ); // まずは中心座標に行き
		turtle.setPenColor( c );
		turtle.pendown( );
		int i=1;
		while (  i <= r ) { // 1ドット登らせます
			turtle.forward( 1 );
			turtle.rotate( 90 );
			int j=1;
			while (  j <= 60  ) { // 円(同心円)を描かせます
				turtle.forward( 2 * Math.PI * i / 60 ); // 1回に進む距離は中心からの離れぐらいで異なります
				turtle.rotate( 6 );
				j = j + 1;
			}
			turtle.rotate( -90 );
			i = i + 1;
		}
		turtle.hideTurtle( );
	}
		

塗りつぶしをするものがいま一つな感じがあります。やはり、三角関数と逆三角関数を用いて横の長さを計算して、 矩形のときのように横に線を引いて塗りつぶしていきましょうか。そうしたものが以下のメソッドになっています。

	public void fillCircle( int x, int y, int r, java.awt.Color c ) {
		Turtle  turtle = new Turtle( this );
		turtle.penup( );
		turtle.setLocation( x, y );   // まずは中心座標に行き
		turtle.forward( r ); // 半径分だけ縦に登らせます
		turtle.setPenColor( c );
		turtle.rotate( 90 );
		int i=1; 
		while ( i <= 2 * r ) {
			double size = Math.sin( Math.acos( (r-i) / (double)r ) ) * r ;  // この式は横の長さを求めています
			turtle.forward( size );  // 右側に移動しておき
			turtle.rotate( 180 );  // くるりときびすを返します
			turtle.pendown( );
			turtle.forward( size * 2);  // この左側への移動で横線を描画します
			turtle.penup( );
			turtle.rotate( 180 );
			turtle.forward( size );  // 左端から、また真ん中に戻ってきます
			turtle.rotate( 90 );
			turtle.forward( 1 );  // 1ドット分下に降ります
			turtle.rotate( -90 );
			i = i + 1;
		}
		turtle.hideTurtle( );
	}

		

上記の4つの円を描くメソッドをstartメソッドから呼び出して使ってみます。

	public void start( ) {
		drawCircle( 60, 60, 40, Red );
		drawCircleSquare( 120, 20, 80, Magenta );
		concentricCircle( 260, 60, 40, Orange );
		fillCircle( 360, 60, 40, Blue );
	}
		

☆実習10-4 正円を描く・塗りつぶすプログラム

上記のメソッドをもつプログラムを完成させて、実行しなさい。 クラス名はPractice1004とします。

☆課題10-4 グラデーションで円を塗りつぶすメソッド

円を塗り潰すときは、同じ色で塗りつぶしましたが、これも、中心が白で外側に行くに従って赤へ変化するグラデーション (Colorオブジェクトを作るときに赤色成分だけを変化させる)で塗りつぶすようなメソッドを作ってみなさい。 1つの同心円を描くたびに、色を変えながら描画していきます。余裕がある人だけがやってください。 クラス名はGradationCircleとします。

☆課題10-5 サインカーブを円で描く

タートルを複数作ります。1つのタートルは、forwardとrotateで円を描かせるようにします。もう1つのタートルは、 円を描いているタートルのy座標をgetY( )メソッドで取り出して、moveToメソッドで、 向かうべきx座標とy座標を指定してサインカーブを描かせるようにします。 また、サインカーブを描くタートルは、そのx座標をgetX( )メソッドで取り出せますので、繰返しの際に それに一定の値を足していきます。クラス名はDrawSineとします。

■タートルの画面について

☆タートルのあるウィンドウの幅と高さを調べたり、設定したりする

プログラム中から、ウィンドウの幅(Width)や高さを調べたり、設定するのには、つぎの メソッドが用意されています。すべて、整数で値を返したり、引数を指定したりします。 タートルの方は実数なので違います。注意して下さい。
	int  width = getWidth( );  // ウィンドウの幅を得る
	setWidth( width * 2 ); // 現在の幅の2倍の幅にする
	setHeight( 400 ); // ウィンドウの高さを400に固定する
		

☆タートルのウィンドウの背景色を変える

タートルのウィンドウの背景色を変えるのには、 setBackgroundColorメソッドが使えます。
	setBackgroundColor( Red );  // 赤に設定する
	setBackgroundColor( new java.awt.Color( 244, 20, 128 ) );  // 3原色を指定して
		

☆タートルの速さを設定する

プログラム中で、必要なときだけ、タートルの動きを待たないようにさせることができます。 これには、setWaitTurtleメソッドが使えます。引数として、論理値のtrueとfalseが使えます。 trueにすると、通常の速度(Normal)で実行します。falseにすると、描画時間を待たない(No Wait)で 実行することになります。
	setWaitTurtle( false );  // タートルを待たないようにする
	fillRectangle( 10, 100, 200, 40 ); // Lecture 8で作った矩形塗りつぶしメソッドを呼ぶ
	setWaitTurtle( true );  // この後は通常の速度で
		

☆実習10-5 タートルウィンドウの背景をグラデーションで塗りつぶす

最後にこれらのメソッドを用いて、下半分を地面に、グラデーションで上半分を空にしてみるプログラムを書いてみましょう。
import sfc.turtle.TurtleFrame;
import sfc.turtle.Turtle;

public class SkyAndGround extends TurtleFrame {

	public static void main(String [ ]  args) { new SkyAndGround( ); }
	
	public void start( ) {
		setWidth( 800 );
		setHeight( 400 );
		setWaitTurtle( false );
		setBackgroundColor( new java.awt.Color( 200, 128, 80 ) );
		Turtle  turtle = new Turtle( this );
		int halfheight = getHeight( ) / 2;
		turtle.penup( );
		turtle.setLocation( 0, halfheight );
		turtle.rotate( 90 );
		turtle.pendown( );
		for ( int i=0; i <= halfheight ; i++ ) {
			turtle.setPenColor( new java.awt.Color( 0, 255-(255*i/halfheight), 255 ) );
			turtle.forward( getWidth( ) );
			turtle.rotate( (i % 2 == 0) ? -90 : 90 );
			turtle.forward( 1 );
			turtle.rotate( (i % 2 == 0) ? -90 : 90 );
		}
		turtle.hideTurtle( );
	}
}
		

10−2.画像やテキストを表示するタートル

■画像タートル(ImageTurtleクラス)

☆画像を表示してタートルとして動かす

画像を使うのは非常に簡単で、画像を実行フォルダと同じフォルダに置いておきます。 そして、それを読み込むだけです。読み込むのは、Turtleクラスを使う替わりにImageTurtleクラスを 使います。
 ImageTurtle 変数名= new ImageTurtle( 画像ファイルの文字列, this );
ImageTurtleは、Turtleクラスのサブクラスになっていますので、Turtleクラスでできたことは、何でも できます。ただ、setTurtleColorは効かないと思います。 なお、ImageTurtleでは、画像を読み込むのに少し時間が掛かりますので注意して下さい。 読み込めるビットマップ画像(専門的にはラスター画像と呼びます)形式の種類は以下の通りです。
 JPEG形式の画像:一般的な画像形式です。圧縮率を変化させて、データサイズを小さくすることができます。
 GIF形式の画像:背景に透明色を指定できます。ただし、256色までしか、使えません。
 PNG形式の画像:背景に透明色を指定できます。フルカラー1677万色も使えますし、透明度も256段階で指定できます。
注意して欲しいのは、Windows標準のBMP形式の画像は読み込めないという点です。 Windowsの「ペイント」などのプログラムは、標準ではBMP形式で保存するので注意して下さい。 画像ファイルが、プログラム(クラスファイル)のあるフォルダにあるときは、次のようにして、 画像を表示させることができます。次のプログラムは、startメソッドだけ、記述しています。
	public  void start(  ) {
		ImageTurtle turtle = new ImageTurtle( "sample.png", this );
		for ( int i=0; i<=60; i++ ) { turtle.rotate( 6 ); }
	}
	
		

以下はイメージの例です。このファイルを、Safariなどではドラッグアウト(ウィンドウの外にドラッグして デスクトップ上で離す)などをすると、デスクトップに保存されます。あるいは、右クリックして、コンテンツ・ メニューから「イメージを別名で保存」などを選んで、目的のフォルダに保存して下さい。

image.png

☆実習10-6 画像タートルを動かしてみる

上記の画像を、プロジェクトのあるフォルダにコピーして実行してみてください。 PNG形式の画像で、背景を透明にして作ります。やってみましょう。クラス名は、Practice1006とします。

☆画像のサイズを変える

作った画面を縮小したり、拡大して表示したい場合があります。 このときは、タートルの拡大縮小を行なうrescaleメソッドを使うのが良いでしょう。 また、現在のタートルの大きさから、相対的に拡大縮小を行なうsetScaleメソッドもあります。
 タートル変数名.rescale( 拡縮率 );
 タートル変数名.setScale( 拡縮率 );
拡縮率は、1.0が元のサイズです。 画像は最初は100×100の大きさで表示されてしまうので、rescale( 1.0 )をしておくと無難だと思います。 なお、resetScale( ) で、元の大きさにすることができます。 タートルのサイズを変えるのに、 setWidth( )setHeight( )も使えます。 同時に、幅と高さを変える setSize( ) もあります。 また、タートルの幅や高さを得るためには、 getWidth( )getHeight( )メソッドがあります。 これらは、実数で指定したり、実数で大きさを受け取ります。 なお、何もない、getWidth( ), getHeight( )は、ウィンドウの幅や高さを得るもので、整数で受け取ることになります。 次のプログラムは、startメソッドだけ、記述しています。
	public  void start(  ) {
		setBackgroundColor( Blue );
		ImageTurtle turtle = new ImageTurtle( "star.png", this );
		turtle.rescale( 0.5 ); // 半分の大きさで
		for ( int i=0; i<=60; i++ ) { 
			turtle.rotate( 6 );
			turtle.setScale( 1.01 );
		}
	}
		

☆実習10-7 画像のサイズを変える

画像を、Illustratorで作り、「ファイル」メニューの「Webやデバイス用に保存」を選んで、 PNG形式の画像で、背景を透明にして作ります。クラス名はPractice1007として、上記のプログラムを完成させて、 実行してみましょう。

☆画像の移動方向をsetDirectionメソッドで変える

画像が必ずしも上から見た画像ではなく、横向きの画像もあると思います。 forwardでは、12時方向を0度として動きますので、それでは不便な場合があると思います。 そのため、画像の向きに動く方向を合わせるためにsetDirectionメソッドが用意されています。
 変数名.setDirection( 方向 );
たとえば、右方向を向いている画像のときは-90、左方向を向いている画像のときは90を指定します。

☆画像を背景画の前で動かす

GIF形式やPNG形式の画像は、背景を透明にさせることができます。 それを使えば、最初に背景を表示しておき、その上に透明部分を持つ、画像を重ねて表示させることができます。 タートルは最初に作られたものが、後ろ側に表示されます。以下は、startメソッドの中だけを記述します。
	public  void start(  ) {
		ImageTurtle   turtle, back;
		back = new ImageTurtle( "back.jpg", this );
		back.setLocation( getWidth()/2, getHeight( )/2 );  // ウィンドウ中央に配置
		turtle = new ImageTurtle( "airplane.png", this );
		turtle.rescale( 0.5 ); // 半分の大きさで
		turtle.setDirection( -90 );  // 画像にあわせて左方向を0度とする
		for ( int i=0; i<=60; i++ ) {
			turtle.forward( 5 );
			turtle.rotate( 6 );
		}
	}
		

☆実習10-8 背景画の前で描画されるタートルを作る

背景はPhotoshopで作ります。「フィルタ」メニューの「描画」サブメニューから「雲模様1」を選びます。 保存は同じく「ファイル」メニューの「Webまたはデバイス用に保存」で、今度はJPEG形式で高画質を選びます。 「back.jpg」という名前で保存してみて下さい。上のプログラムにある「airplane.png」は下の画像を 保存(ドラッグアウトか右クリックで「イメージを別名で保存」)して、使ってみて下さい。

airplane.png

これを雲模様の中で空中回転させてみます。クラス名はPractice1008として、プログラムを作成、実行してみなさい。

■テキストタートル(TextTurtleクラス)

☆テキストを表示してタートルとして動かす

これも、Turtleの替わりにTextTurtleを作るだけです。 TextTurtleは、ImageTurtleのサブクラスなので、Turtleでできることや、ImageTurtleで出来ることは、 なんでもできます。 テキストタートルは、基本的には画像タートルと扱い方が同じだと思っておくと良いでしょう。 テキスト以外の部分は、透明で後ろが見えるのは、PNG形式やGIF形式の画像で透明部分を設定しているのと同じです。 もし背景色を設定したいときは、背景をsetBackgroundColorか、 Lecture 10で作ったようなfillRectangleメソッドを使って塗りつぶしておく必要があります。
 TextTurtle 変数名= new TextTurtle( 文字列, this );
なお、TextTurtleの場合は、指定された文字列から構成される画像を作ります。 画像を作るのに少し時間が掛かりますので注意して下さい。 ちなみに、ImageTurtleでは効かなかった、 setTurtleColorによって文字の色を変えることができます。 なお、以下はstartメソッドの中だけを記述しています。
	public  void start(  ) {
		TextTurtle   turtle = new TextTurtle( "Sample", this );
		turtle.setTurtleColor( Blue );
		for ( int i=0; i< 60; i++ ) {
			turtle.rotate( 6 );
		}
	}
		

☆実習10-9 回転するテキストタートル

やってみましょう。クラス名はPractice1009とします。

☆テキスト・タートルのフォントやサイズを変える

TextTurtleの場合は、フォントを名前を指定してみせることができたり、ポイント数も変えることができます。 変更した場合、画像を作り直しますのでちょっと時間が掛かります。
 setTypeface( フォントの名前 );
 setFontSize( ポイント数 );

テキストタートルが既に作成されて、回転されていたり、拡大縮小されている場合があると思いますが、 この2つのメソッドを呼び出しますと、テキストの画像を作り直しますので、回転や拡大縮小が元に戻って しまいます。注意してください。

☆システムで使うことのできるフォントの一覧を表示する

システムで使うことができるフォントの名前の一覧を表示するプログラムを以下に示します。 これを利用して、フォントの名前を見て下さい。これは、「アプリケーション」としてクラスを作成して下さい。 また、BlueJのターミナルの「プロパティ」メニューで「バッファを制限しない」にチェックマークを入れておいて ください。そうしないと、システムにインストールされているフォントの数が多いときに表示しきれなくなります。
		import java.awt.*;

		public class SystemFontList {

			public static void main(String [ ]  args) {
				GraphicsEnvironment    env = GraphicsEnvironment.getLocalGraphicsEnvironment( );
				Font   allfonts [ ]  = env.getAllFonts( );
				for ( int i=0; i < allfonts.length; i++ ) {
					System.out.println( allfonts[ i ].getName( ) );
				}
			}
		}
		
なお、プログラム開発環境に置かれているフォントが、実行環境に必ずしも置かれていないことに注意して下さい。 Javaで、標準的に使われるのは、次のような名前のフォントです。
"Serif":装飾のあるフォント、英字の紙面に使われているTimesフォントなどが代表的です。
"SansSerif":装飾のないフォント、Helveticaフォントなどが代表的です。
"Dialog":これは、"SansSerif"と同じ場合が多いようです。
"MonoSpaced":すべての字の幅が同じフォント、Courierフォントなどが代表的です。

☆実習10-10 フォントの一覧表示

上記のシステムフォントの名前を全部表示するプログラムを動かしてみましょう。 なお、ターミナルの設定で、「バッファを制限しない」にチェックをいれておいて下さい。

☆配列のすべての要素をテキストタートルとして表示する

テキストタートルは、文字列だけでなく、整数、長桁整数、実数なども生成時に指定することができます。
 TextTurtle 変数名= new TextTurtle( 整数, this );
 TextTurtle 変数名= new TextTurtle( 長桁整数, this );
 TextTurtle 変数名= new TextTurtle( 実数, this );
ただし、文字列の場合は、生成するときに次のようにサイズ、フォント名、色を指定することができます フォント名を指定するときは、システムにあるフォントかどうか確かめるか、標準のフォント名を使いましょう。
 TextTurtle 変数名= new TextTurtle( 文字列, サイズ, this );
 TextTurtle 変数名= new TextTurtle( 文字列, 色, サイズ, this );
 TextTurtle 変数名= new TextTurtle( 文字列, フォント名, サイズ, this );
 TextTurtle 変数名= new TextTurtle( 文字列, 色, フォント名, サイズ, this );

上記の指定で、サイズは整数(の式)で、色はColorオブジェクト、フォント名は文字列で指定します。

生成時に色とサイズを指定できるテキストタートルを使って、 カレンダー的に配列の数をそのまま表示してみましょう。横に8個ずつ並べます。 1つの数を表示するのに、幅30ドット、高さ15ドットを確保しています。 y座標は20の位置から表示を始めることとします。 startメソッドだけを記述します。

	public void start( ) {
		int a [  ] = { 10, 34, 82, 95, 3, 4 ,5 ,1,32, -32, 1, 3, 12, 45, 11, 32, 19, -6, 23, 21, 33 };
		int width = 60,  height = 30;
		for  (  int  i =0;  i < a.length; i++ ) {
			TextTurtle  t = new TextTurtle(  a[ i ],  Blue, 18,  this );
			t.penup( );
			t.setLocation( i % 8 * width + 40 , i / 8 * height + 20 );
		}
	}
		
要素の値を表示するx座標とy座標を技巧的に計算しています。剰余演算はこのように周期的にサイクルさせたいときに使います。また整数除算は、周期毎に1つ値を増やすような場合に使います。次の周期のときには、割り算の結果が1つ大きな値になりますから、表示されるy座標は次の段になります。

☆実習10-11 配列の各要素をカレンダー的にテキストタートルで表示する

やってみましょう。クラス名はPractice1011とします。


10−3.音声の再生

■サウンドを鳴らす

☆鳴らすことができるサウンドの形式

Java2から、次のような一般的な音声ファイルをサポートすることができるようになりました。 このため、多くの音声ファイルは変換する必要はありません。 また、MIDI形式のファイルも次のようなファイルをサポートしています。 残念ながら、Macintosh固有の音声ファイル形式であるAIFF形式には対応していません。 Audacityというアプリケーションで変換して下さい。

音声ファイルの形式:MP3, WAVE, AU
音声ファイルのコーディング方法:PCM(リニア), μ-law,その他
音声ファイルの標本周波数:8kHz 〜 48kHz
音声ファイルの 量子化ビット数:8bitまたは16 bit
音声ファイルのチャンネル数:Mono (=1)または Stereo(=2)

MIDI形式(楽譜形式)ファイル:MIDI Type 0, MIDI Type 1, RMF

Javaでは音声ファイルの再生は、標準では16ビットのステレオで、22kHzの周波数で行なわれます。 ただし、実行されるコンピュータで、この品質で再生できないときは、8ビットのモノラル、 8kHzでの再生になってしまいます。 MIDIファイルに関しては、内蔵のデジタルシンセサイザー音源を使って再生されます。

☆アプレットに用意されているオーディオ用のクラスAudioClipについて

java.appletパッケージでは、音声ファイルを再生するためのAudioClipクラスが用意されています。 複数の音声ファイルを同時に再生させることができます。 タートル・グラフィックスでは、AudioClipに関しては次のようなメソッドが用意されています。
サウンドファイルをロードする:
java.applet.AudioClip <変数名> = getAudioClip( <ファイル名の文字列> );
アプレットで、音声ファイルをロードしてAudioClipクラスのオブジェクトを用意してくれるものです。 実行ファイルのあるフォルダに置かれている音声ファイルを指定することができます。 たとえば、次のように記述します。
		java.applet.AudioClip   au = getAudioClip(  "spacemusic.au" );
		
AudioClipクラスのオブジェクトには次の3つのメソッドが用意されています。
サウンドファイルの再生・停止のためのメソッド:
 
  play( )  1回だけ再生する
  loop( )  繰り返し何回も再生する(stopメソッドが呼ばれるまで)
  stop( )  再生を止める
たとえば、上記の変数auが指し示すAudioClipクラスのオブジェクトを使った場合は、次のように記述します。
		au.play( );   au.loop( );  au.stop( );
		

☆実習10-12 AudioClipを使って音を鳴らしてみる。

次のファイルをダウンロードしてみて下さい(音声ファイルへのリンク)。 やってみましょう。リンク先の音声ファイルは、QuickTimePlayer Proのプラグインがインストールされていれば、 鳴らしてみてから、右端の▼メニューから「ファイルを保存」で保存できる筈です。 実習教室ではQuickTime Proが導入されていないので、Safariなどでは、上記のリンクを リンクを右クリックして、コンテンツ・メニューを出して「リンク先のファイルを別名で保存」などを 使ってダウンロードしてみてください。プログラム(.classファイル)のある フォルダに置かれていれば、次のようなプログラムで鳴らすことができます。

		import java.applet.*;		// AudioClipクラスは、java.appletパッケージに入っています。
		import sfc.turtle.TurtleFrame;

		// クラス名を指定するときに「PlayOnce」という名前は、「Practice1012」でも構いません。
		public class PlayOnce  extends TurtleFrame {

			public static void main(String [ ]  args) { new PlayOnce( ) ;  }
			
			public void start( ) {
				AudioClip   au = getAudioClip(  "spacemusic.au" );
				au.play( );				// au.loop( ); にすると鳴り続けますので要注意。
			}
		}
		

なお、音声に関しては、λ21教室ではイヤホンで聴くことなります。 Macintoshでは、鳴らすための設定をする必要があります(以下の記述は、実習室で調整します)。 通常の実習室のMacintoshでは、システム環境設定を出して、「サウンド」を選び「出力」のタブで「内蔵スピーカー」を選びます。 ただし、教卓にあるMacintoshでは、これではうまくいかない場合もあります(OnyxのFirewireを選ぶなど)ので、注意して下さい。 録音・再生については、Audacityを用います。 これは、Mac OS X版(1.3.6版でないと実習室では動きません)も、Windows版もあるソフトウェアです。 Audacityを使った音声の録音や音声ファイルの変換については、 外部リンクですが、Clan Pohwa メンバーのYOUさんが書かれた(Windows版で書かれていますが、Mac OS Xでもだいたい同じです) 「Audacityの初心者お助け講座」 を参照して下さい。


10−4.アニメーション

■startメソッドでの無限ループ

startメソッドで無限ループにしておいて、アニメーションを表示させます。
 public void start( ) {
  // 最初にしておきたいことを記述しておく
  
  while( true ) { //無限の繰返し
    // アニメーションの中での変更点を記述する
    
    update( ); //変更を描画させる
    sleep( 秒数 ); // 何秒か休む(たとえば、0.5秒など)
  }
 }

■アニメーションを使ったサンプル例

☆時刻を刻々と変化させて表示させる

1秒単位で時間を更新しながら回転表示していくものです。 テキストタートルでは、 setText( ) メソッドを使って、新しい文字列を設定することができます。 また、getText( ) は、テキストタートルの中に設定されている文字列を返してきます。 以下のプログラムは、startメソッドの中だけを記述しています。
	public  void start(  ) {
		TextTurtle   turtle = new TextTurtle( getDateString( ), "Optima-Bold", 24, this );
		int  angle = 0;
		while ( true ) {
			turtle.setText( getDateString(  ) );  //現在時刻の文字列でタートルを作り直します。
			turtle.setAngle( angle ); // タートルを作り直すと0度に戻るので、setAngleで変更させます。
			turtle.forward( 0 );  // 描画に画像の角度変更を反映させるために、forwardを入れています。
			angle = (angle + 6) % 360;  // 360度になったら、0度に戻るようにさせています。
			update( ); 
			sleep( 1 );
		}
	}
		

☆実習10-13 表示される時刻が変わる(←普通変わるじゃん!)時計

上記のサンプルを実行して見なさい。クラス名は、Practice1013とします。

☆画像を背景画の前で動かし続ける

画像を読み込み、動かして(回転運動をさせて)います。No Waitで実行させると良いでしょう。 startメソッドだけを記述しています。
	public  void start(  ) {
		setWaitTurtle( false );
		ImageTurtle   turtle, back;
		back = new ImageTurtle( "back.jpg", this );
		back.setLocation( getWidth()/2, getHeight( )/2 );  // ウィンドウ中央に配置
		turtle = new ImageTurtle( "airplane.png", this );
		turtle.setDirection( -90 );
		turtle.rescale( 0.3 );
		while ( true ) {
			turtle.forward( 5 );
			turtle.rotate( 6 );
			update( );
			sleep( 0.5 );
		}
	}
		

☆実習10-14 背景画の中でタートルグラフィックスの方を動かす

上記のサンプルを実行して見なさい。クラス名はPractice1014とします。

☆複数のタートルの衝突を検知する

intersectsというメソッドで、他のタートルと衝突しているかどうかがわかります。
 タートルを示す変数.intersects( 他のタートルを示す変数 )
このメソッドは、衝突していれば、論理値のtrue、衝突していなければ、 論理値のfalseを返してきてくれます。 以下のプログラムの記述は、2つの画像ファイルを読み込んで、動かして、衝突したかどうかをアニメーションの中で判定しています。 衝突していれば、45度回転させながら移動させいます。 startメソッドの中だけを記述しています。
	public  void start(  ) {
			ImageTurtle   turtle1, turtle2, back;
				back = new ImageTurtle( "back.jpg", this );
				back.setLocation( getWidth()/2, getHeight( )/2 );  // ウィンドウ中央に配置
				back.rescale( 1.0 );
				turtle1 = new ImageTurtle( "airplane1.png", this );
				turtle2 = new ImageTurtle( "airplane2.png", this );
				turtle1.rescale( 0.5 );
				turtle2.rescale( 0.5 );
				turtle1.penup( );
				turtle2.penup( );
				turtle1.setLocation( 10, getHeight( ) /2 );
				turtle2.setLocation( getWidth( )-10, getHeight( ) / 2);
				turtle1.setDirection( 90 );
				turtle2.setDirection( -90 );
				turtle1.setPenColor( Red );
				turtle2.setPenColor( Green );
				turtle1.pendown( );
				turtle2.pendown( );
				while ( true ) {
					turtle1.forward( 5 );
					turtle2.forward( 5 );
					if ( turtle1.intersects( turtle2 ) ) { // 衝突したかどうか
						turtle1.rotate( 45 ); turtle2.rotate( 45 );  // それぞれ45度回転させる
						// 触れ続けている間は、両機とも5ドットずつ前に移動させる
						while ( turtle1.intersects( turtle2 ) ) { turtle1.forward( 5 ); turtle2.forward( 5 ); }
					}
					// 壁にぶつかったら180度回転させる
					if ( turtle1.getX( ) < 0 || turtle1.getX( ) > getWidth( ) || 
						turtle1.getY( ) < 0 || turtle1.getY( ) > getHeight( ) ) { turtle1.rotate( 180 ); }
					if ( turtle2.getX( ) < 0 || turtle2.getX( ) > getWidth( ) || 
						turtle2.getY( ) < 0 || turtle2.getY( ) > getHeight( ) ) { turtle2.rotate( 180 ); }
					update( );
					sleep( 0.1 );
				}
			}
		}
		

☆実習10-15タートルの衝突

上記のサンプルを実行して見なさい。それから、斜め30度や60度で動く形に拡張しても良いでしょう。 airplane2.pngは、Photoshopなどで、airplane.pngなどを左右反転させて作ってみて下さい。 クラス名はPractice1015とします。

☆背景画の方を動かし続ける

下記のプログラムで、背景画は左右反転した画像で作っておくのもいいでしょう。 2つの背景画像のサイズは同じにしておきます。2つの背景画像で、back1の左側がback2の右側と連続している ような状態で作っておきます。

	public  void start(  ) {
		ImageTurtle   turtle, back1, back2;
		back1 = new ImageTurtle( "back1.jpg", this );
		back2 = new ImageTurtle( "back2.jpg", this );
		back1.penup( );
		back2.penup( );
		setWidth( (int)( back1.getWidth( ) ) -10 );	// 背景画像のサイズでウィンドウのサイズを設定 -10はスクロールバー分
		setHeight( (int)( back1.setHeight( ) ) );
		double  x1 = back1.getWidth( )/2;
		double  x2 =  -back2.getWidth( )/2;
		double  y = getHeight( )/2;
		back1.setLocation( x1, y );  // ウィンドウ中央に配置
		back2.setLocation( x2, y );  // ウィンドウから左側にはみ出て配置

		turtle = new ImageTurtle( "airplane.png", this );
		while ( true ) {
			x1 += 10;
			x2 += 10;
			if ( x1 >= getWidth( ) + back1.getWidth( ) / 2 ) { x1 = -back1.getWidth( ) / 2; } // 右側にはみ出したら左側に
			if ( x2 >= getWidth( ) + back2.getWidth( ) / 2) { x2 = -back2.getWidth( ) / 2; } // 同上
			back1.setLocation( x1, y ); 
			back2.setLocation( x2, y );
			turtle.forward( 0 );  // アニメーションのメソッドを入れないと反映されない
 			update( );
			sleep( 0.5 );
		}
	}
		

☆実習10-16 背景画のタートルグラフィックスの方を動かす

上記のサンプルプログラムをクラス名をPractice1016で作成し、実行して見なさい。 可能であれば、斜め45度とかに動かしてみるようにしてみてください(その場合は4つ反転された背景が必要かも)。

☆時間計測

java.utilパッケージにあるCalendarクラスのオブジェクトには、 1970年の1月1日0時0分0秒からの経過時間を求めるgetTimeInMillisメソッドがあり、 タートル・グラフィックスでも、 getTotalMilliSecondでこの値を求めることができます。 経過時間の型はlongの整数型でミリ秒単位で求められます。下の代入文では、その経過時間を求めています。 2つの時刻に対して、この方法を使って経過時間を求め、その差分を出せば、どの程度時刻が過ぎたかわかります。 また、求められた時間差に対して、1000で割ると秒数が、更にその結果を60で割ると 分数(更にその結果を60で割ると時間)を得ることができます。

		long  millisecond = getTotalMilliSecond( );
		printLine( millisecond );
		

次のプログラムは、1/10秒ごとに時間差を表示をするものです。プログラムが開始されたら計測を開始します。 開始時間に対する経過ミリ秒を変数starttimeに求めています。 また、刻々と変わる時間の経過ミリ秒を変数currentに求めています。 この2つの変数の差を1000で割った時間差が経過秒で変数secondに計算しています。 また、同じ差を1000で剰余を求めて、100で割ったものは、1/10秒の部分になります。 テキストタートルは使わずに、文字端末に表示をさせています(テキストタートルは作るのに時間が掛かるので)。 startメソッドと、printDifferenceメソッドを記述しています。

	public  void start( ) {
		long  starttime = getTotalMilliSecond( );
		long  current = getTotalMilliSecond( );
		printLine( makeDifference( starttime, current ) );
		while ( current-starttime < 30000 ) {
			current = getTotalMilliSecond( );
			printDifference( starttime, current );
			sleep( 0.05 );
		}
	}

	void printDifference( long first, long current ) {
		long minute = (current-first)/1000/60;
		long second = (current-first)/1000%60;
		long decisecond = (current-first)%1000/100;
		printLine(  formatInteger( 2, 5, minute )+":"+ formatInteger( 2, 2, second ) +":"+ 
					formatInteger( 2, 2, decisecond*10 ) );
	}
		

☆実習10-17 時間計測を表示する

上記のサンプルを実行して見なさい。アプリケーションで作成します。 Practice1017というクラス名にしなさい。

☆30秒間ラフにカウントする

先程のプログラムは、厳密に時間差を計算するものでしたが、ここではもっと簡単に時間を カウントするアプレットを記述してみます。このプログラムでは、sleepメソッドを呼び出して、 それを何回行なったかで、時間をカウントします。 厳密には時間を求めることはできませんし、Mac OS Xのスケジューラによって実際には、 指定した以上の時間が掛かってしまうかも知れませんが、たとえば30秒間ぐらいだけ何かさせる分には これで充分でしょう。以下のアプレットは、1秒ずつ休みながら秒数をカウントダウンしています。
		

	public  void start( ) {
		int  count=30;
		TextTurtle  turtle = new TextTurtle( count, this );
		turtle.setTypeface( "Lucida Grande" );
		turtle.setFontSize( 64 );
		while ( count > 0 ) {
			if ( count < 10 ) { turtle.setTurtleColor( Red ); }
			turtle.setText( count );
			count--;
			update( );
			sleep( 1 );
		}
		turtle.setTurtleColor( Black );
		turtle.setText( 0 );
		turtle.rescale( 2 );
		turtle.rotate( 360 );
	}
		

☆実習10-18 30秒ラフにカウントする

上記のサンプルプログラムをクラス名Practice1018という名前で作成し、実行してみなさい。

☆課題10-6 ストップウォッチ

時間計測のプログラムを参考にして、2分間だけ、1秒ずつカウントアップ(あるいはカウントダウン)する プログラムを作りなさい。 プログラムが実行されたらカウントアップ(あるいはカウントダウン)を開始します。 テキストタートルを使って表示させてみて下さい。テキストタートルが遅ければ、ターミナルに表示させなさい。 60秒から始めて、0秒になったら、プログラムを停止させます。また、1/10秒ではなくて、 1/100秒まで表示するプログラムを作りなさい。クラス名は、StopWatchにて。

☆課題10-7 電光掲示板

何らかのメッセージを表示するプログラムを作ります。1つのテキストタートルを用いて、5秒に1回、 別の色でそのメッセージを表示させます。また、2秒に10ドットの割合で、左に移動して表示させます。 表示が左の端から消えたら、右側からまた表示させるようにします。クラス名は、MessageBoardにて。

ヒント:2つの動きの最小公約数の1秒単位で繰返しを構成するようにします。 繰返しの中で、5回に1回の割合で色を変えるようにします。 また、2回に1回の割合で左に移動させるようにします。


<<Previous Lecture >>Next Lecture