情報学部 菅沼ホーム Java 目次 基礎技術目次 索引

速度と加速度(その1)

  1. 速度

      x 軸上の初期位置を x0,x 軸方向の速度を v / t としたとき,摩擦等の外力がなければ,物体は等速直線運動を行い,t 秒後の位置 x は (1) 式のようになります.従って,一定時間( dt )毎に位置を計算する場合は,現在の位置を x(t),dt 時間後の位置を x(t+dt) としたとき,(2) 式のように表せます.
    x = x0 + vt  (1)
    x(t+dt) = x(t) + v・dt  (2)			
      以下のプログラムは,Thread クラスを利用して,等速直線運動を描画したものです.この方法が,Java におけるアニメーションanimation )作成の基本となります.
    001	import java.awt.*;
    002	import java.awt.event.*;
    003	import javax.swing.*;
    004	
    005	public class Test {
    006		public static void main (String[] args)
    007		{
    008			Win win = new Win("等速直線運動");
    009		}
    010	}
    011	
    012	class Win extends JFrame
    013	{
    014		/******************/
    015		/* コンストラクタ */
    016		/******************/
    017		Win(String name)
    018		{
    019						// JFrameクラスのコンストラクタ(Windowのタイトルを引き渡す)
    020			super(name);
    021						// Windowの大きさ
    022			setSize(640, 470);   // +40, +70
    023						// ContentPane の取得と MainPanel の追加
    024			Dimension d  = getSize();   // Windowの大きさ
    025			d.width     -= 40;
    026			d.height    -= 70;
    027			MainPanel pn = new MainPanel(d);   // MainPanel オブジェクトの生成
    028			getContentPane().add(pn);   // MainPanel オブジェクトを ContentPane に追加
    029						// ウィンドウを表示
    030			setVisible(true);
    031						// イベントアダプタ
    032			addWindowListener(new WinEnd());
    033		}
    034	
    035		/******************************/
    036		/* 上,左,下,右の余白の設定 */
    037		/******************************/
    038		public Insets getInsets()
    039		{
    040			return new Insets(50, 20, 20, 20);
    041		}
    042	
    043		/************/
    044		/* 終了処理 */
    045		/************/
    046		class WinEnd extends WindowAdapter
    047		{
    048			public void windowClosing(WindowEvent e) {
    049				System.exit(0);
    050			}
    051		}
    052	}
    053	
    054	class MainPanel extends JPanel implements Runnable
    055	{
    056		boolean state = true;
    057		double x, y, x0 = 0.0, t = 0.0, dt = 0.1, v = 20.0;
    058		Dimension d;
    059		Thread th;
    060	
    061		MainPanel(Dimension d1)
    062		{
    063			d = d1;
    064			x = x0;
    065			y = d.height / 2;
    066			setBackground(new Color(238, 255, 238));   // 背景色の設定
    067			th = new Thread(this);   // スレッドの生成とスタート
    068			th.start();
    069		}
    070						// 他ページへ移動の際,一時的にスレッドを停止
    071		public void stop()
    072		{
    073			state = false;
    074		}
    075						// スレッドの実行
    076		public void run()
    077		{
    078			while (state) {
    079				try {
    080					th.sleep(33);
    081				}
    082				catch (InterruptedException e) {}
    083				if (x < d.width) {
    084					t += dt;
    085					x  = v * t;   // x += v * dt; でも良い
    086				}
    087				else {
    088					x = x0;
    089					t = 0;
    090				}
    091				repaint();
    092			}
    093		}
    094						// 描画
    095		public void paintComponent(Graphics g)
    096		{
    097			super.paintComponent(g);   // 親クラスの描画
    098	
    099			g.setColor(Color.green);
    100			g.fillOval((int)x-20, (int)y-20, 40, 40);
    101		}
    102	}
    			
    024 行目

      getSize メソッドによって,パネルの大きさを取得しています.大きさは,Dimension クラスのオブジェクトとして得られ,d.width に幅,d.height に高さが入っています.

    057 行目

      円の中心の位置( x, y ),円の水平方向の初期位置( x0 ),時間,時間の刻み幅,水平方向の速度を表す変数です.

    67 行目~ 68 行目

      Thread クラスのオブジェクトを生成し,Thread クラスのメソッド start を使用して,その実行を開始しています.スレッドとは,プログラム内で実行する別のプログラムです.Java では,並列に複数のプログラムを実行することができます.スレッドを生成し実行するには,2 つの方法があります.一つは,あるクラスを Thread クラスの子として定義する方法であり,また,他の一つは,Runnable インタフェースを利用する方法です

      Java では,C++ とは異なり,複数の親を継承することができません.054 行目に示すように,MainPanel クラスは既に JPanel クラスを継承していますので,Thread クラスを継承できません.このような問題を解決するのがインターフェースです.クラスは,複数のインタフェースを継承可能です( implements の後ろに複数のインタフェースをカンマで区切って並べることができます).ただし,継承可能なものは,メソッドの宣言と static な変数(フィールド)だけであり,継承したクラスが抽象クラスでない限り,インタフェース内で宣言されたメソッドの内容を定義(オーバーライド)してやる必要があります.ここでは,054 行目において,Runnable インタフェースを継承し,076 行目~ 093 行目において,そのメソッド run をオーバーライドしています.

    079 行目~ 082 行目

      trycatch で構成されるブロックを例外処理と呼びます.try ブロック内で何らかの例外(この例の場合は,sleep メソッドの実行によって起こる可能性がある InterruptedException 例外)が起こった場合,その例外に対応する catch ブロック内でその例外に対応した何らかの処理を行います(この例の場合は,082 行目の {} 内に何も記述されていないため,何も行いません).try ブロック内で複数の例外が起こる可能性がある場合は,各例外に対応した複数の catch ブロックを記述することになります.

      sleep メソッドは,Thread クラスのメソッドであり,引数に与えられた時間(単位は ms )だけ実行を停止することを意味しています.このメソッドでは,InterruptedException 例外に対応する処理を必ず記述する必要があるため,この例のような記述になっています.sleep メソッドの実行により,083 行目から 091 行目までの処理が,33 ms 毎に行われることになります.

    083 行目~ 091 行目

      円が右端に達していない場合は円の位置を修正し,また,右単に達した場合は,初期状態に戻した後,再描画しています.

    100 行目

      円の位置 (x, y) は double 型で宣言してあるため,int 型に変換しています( 型変換( cast 演算子)).

  2. 加速度

      x 軸上の初期位置を x0,x 軸方向の初期速度を v0 / t,かつ,x 軸方向の加速度を a / t2 としたとき,摩擦等の外力がなければ,物体は等加速度運動を行い,t 秒後の速度 v,及び,位置 x は (3) 式,(4) 式のようになります.従って,一定時間( dt )毎に位置を計算する場合は,現在の位置及び速度を x(t),v(t),dt 時間後の位置及び速度を x(t+dt),v(t+dt) としたとき,(5) 式,(6) 式のように表せます.
    v = v0 + at  (3)
    x = x0 + v0t + 0.5at2  (4)
    v(t+dt) = v(t) + a・dt  (5)
    x(t+dt) = x(t) + 0.5(v(t) + v(t+dt))・dt  (6) ( dt が小さいときの近似式)			
      以下のプログラム( Test.java )は,Thread クラスを利用して,等加速度運動を描画したものです.なお,087 行目~ 090 行目は,(5) 式,(6) 式を使用して計算した場合を表しています.
    001	import java.awt.*;
    002	import java.awt.event.*;
    003	import javax.swing.*;
    004	
    005	public class Test {
    006		public static void main (String[] args)
    007		{
    008			Win win = new Win("等加速度運動");
    009		}
    010	}
    011	
    012	class Win extends JFrame
    013	{
    014		/******************/
    015		/* コンストラクタ */
    016		/******************/
    017		Win(String name)
    018		{
    019						// JFrameクラスのコンストラクタ(Windowのタイトルを引き渡す)
    020			super(name);
    021						// Windowの大きさ
    022			setSize(640, 470);   // +40, +70
    023						// ContentPane の取得と MainPanel の追加
    024			Dimension d  = getSize();   // Windowの大きさ
    025			d.width     -= 40;
    026			d.height    -= 70;
    027			MainPanel pn = new MainPanel(d);   // MainPanel オブジェクトの生成
    028			getContentPane().add(pn);   // MainPanel オブジェクトを ContentPane に追加
    029						// ウィンドウを表示
    030			setVisible(true);
    031						// イベントアダプタ
    032			addWindowListener(new WinEnd());
    033		}
    034	
    035		/******************************/
    036		/* 上,左,下,右の余白の設定 */
    037		/******************************/
    038		public Insets getInsets()
    039		{
    040			return new Insets(50, 20, 20, 20);
    041		}
    042	
    043		/************/
    044		/* 終了処理 */
    045		/************/
    046		class WinEnd extends WindowAdapter
    047		{
    048			public void windowClosing(WindowEvent e) {
    049				System.exit(0);
    050			}
    051		}
    052	}
    053	
    054	class MainPanel extends JPanel implements Runnable
    055	{
    056		boolean state = true;
    057		double x, y, x0 = 0.0, t = 0.0, dt = 0.04, v0 = 0.0, v, a = 100.0;
    058		Dimension d;
    059		Thread th;
    060	
    061		MainPanel(Dimension d1)
    062		{
    063			d = d1;
    064			x = x0;
    065			y = d.height / 2;
    066			v = v0;
    067			setBackground(new Color(238, 255, 238));   // 背景色の設定
    068			th = new Thread(this);   // スレッドの生成とスタート
    069			th.start();
    070		}
    071						// 他ページへ移動の際,一時的にスレッドを停止
    072		public void stop()
    073		{
    074			state = false;
    075		}
    076						// スレッドの実行
    077		public void run()
    078		{
    079			while (state) {
    080				try {
    081					th.sleep(40);
    082				}
    083				catch (InterruptedException e) {}
    084				if (x < d.width) {
    085					t += dt;
    086					x  = v0 * t + 0.5 * a * t * t;
    087	//				double v1 = v;
    088	//				double v2 = v + a * dt;
    089	//				x += 0.5 * (v1 + v2) * dt;
    090	//				v = v2;
    091				}
    092				else {
    093					x = x0;
    094					v = v0;
    095					t = 0;
    096				};
    097				repaint();
    098			}
    099		}
    100						// 描画
    101		public void paintComponent(Graphics g)
    102		{
    103			super.paintComponent(g);   // 親クラスの描画
    104	
    105			g.setColor(Color.green);
    106			g.fillOval((int)x-20, (int)y-20, 40, 40);
    107		}
    108	}
    			

  3. 自由落下

      速度と加速度の応用として,物を投げ上げた場合の運動について考えてみます.摩擦がなければ,x 軸方向に対しては等速運動を行います.また,y 軸方向に対しては,重力の加速度 g / t2 がかかりますので,等加速度運動を行うことになります.投げ上げた時点の水平方向( x 軸方向)の位置を x0,高さ( y 軸方向の位置)を y0,x 軸方向の初期速度を vx0 / t,y 軸方向の初期速度を vy0 / t,かつ,重力の加速度を g / t2 としたとき,t 秒後の x 軸方向の速度 vx,y 軸方向の速度 vy,位置 x,及び,高さ y は以下のようになります.なお,画面上では,実世界と上下方向が逆ですので,上に投げた場合,y 軸方向の初期速度 vy0 は負に,また,加速度 g は正の方向に働きます.
    vx = vx0
    vy = vy0 - gt
    x = x0 + vx0t
    y = y0 + vy0t - 0.5gt2			
      以下のプログラム( Test.java )は,Thread クラスを利用して,自由落下運動を描画したものです.なお,090 行目~ 094 行目は,(5) 式,(6) 式を使用して計算した場合を表しています.
    001	import java.awt.*;
    002	import java.awt.event.*;
    003	import javax.swing.*;
    004	
    005	public class Test {
    006		public static void main (String[] args)
    007		{
    008			Win win = new Win("自由落下");
    009		}
    010	}
    011	
    012	class Win extends JFrame
    013	{
    014		/******************/
    015		/* コンストラクタ */
    016		/******************/
    017		Win(String name)
    018		{
    019						// JFrameクラスのコンストラクタ(Windowのタイトルを引き渡す)
    020			super(name);
    021						// Windowの大きさ
    022			setSize(640, 470);   // +40, +70
    023						// ContentPane の取得と MainPanel の追加
    024			Dimension d  = getSize();   // Windowの大きさ
    025			d.width     -= 40;
    026			d.height    -= 70;
    027			MainPanel pn = new MainPanel(d);   // MainPanel オブジェクトの生成
    028			getContentPane().add(pn);   // MainPanel オブジェクトを ContentPane に追加
    029						// ウィンドウを表示
    030			setVisible(true);
    031						// イベントアダプタ
    032			addWindowListener(new WinEnd());
    033		}
    034	
    035		/******************************/
    036		/* 上,左,下,右の余白の設定 */
    037		/******************************/
    038		public Insets getInsets()
    039		{
    040			return new Insets(50, 20, 20, 20);
    041		}
    042	
    043		/************/
    044		/* 終了処理 */
    045		/************/
    046		class WinEnd extends WindowAdapter
    047		{
    048			public void windowClosing(WindowEvent e) {
    049				System.exit(0);
    050			}
    051		}
    052	}
    053	
    054	class MainPanel extends JPanel implements Runnable
    055	{
    056		boolean state = true;
    057		double x, y, x0 = 0.0, y0, t = 0.0, dt = 0.04, vx0 = 100.0, vy0 = -200.0, vy, g = 98.0;
    058		Dimension d;
    059		Thread th;
    060	
    061		MainPanel(Dimension d1)
    062		{
    063			d  = d1;
    064			y0 = d.height / 2;
    065			x  = x0;
    066			y  = y0;
    067			vy = vy0;
    068			setBackground(new Color(238, 255, 238));   // 背景色の設定
    069			th = new Thread(this);   // スレッドの生成とスタート
    070			th.start();
    071		}
    072						// 他ページへ移動の際,一時的にスレッドを停止
    073		public void stop()
    074		{
    075			state = false;
    076		}
    077						// スレッドの実行
    078		public void run()
    079		{
    080			while (state) {
    081				try {
    082					th.sleep(40);
    083				}
    084				catch (InterruptedException e) {}
    085				if (x < d.width && y < d.height) {
    086					t += dt;
    087					x  = vx0 * t;   // この行と次の行の代わりに,それ以降の5行でも良い
    088					y  = y0 + vy0 * t + 0.5 * g * t * t;
    089	//				x += vx0 * dt;
    090	//				double v1 = vy;
    091	//				double v2 = vy + g * dt;
    092	//				y += 0.5 * (v1 + v2) * dt;
    093	//				vy = v2;
    094				}
    095				else {
    096					x  = x0;
    097					y  = y0;
    098					vy = vy0;
    099					t  = 0;
    100				};
    101				repaint();
    102			}
    103		}
    104						// 描画
    105		public void paintComponent(Graphics g)
    106		{
    107			super.paintComponent(g);   // 親クラスの描画
    108	
    109			g.setColor(Color.green);
    110			g.fillOval((int)x-20, (int)y-20, 40, 40);
    111		}
    112	}
    			

  4. もう一つの例

      この例では,上 20 度の方向に向かう円が等速度運動,下 20 度に向かう円が等加速度運動をしています.
    001	import java.awt.*;
    002	import java.awt.event.*;
    003	import javax.swing.*;
    004	
    005	public class Test {
    006		public static void main (String[] args)
    007		{
    008			Win win = new Win("等速度運動と等加速度運動");
    009		}
    010	}
    011	
    012	class Win extends JFrame
    013	{
    014		/******************/
    015		/* コンストラクタ */
    016		/******************/
    017		Win(String name)
    018		{
    019						// JFrameクラスのコンストラクタ(Windowのタイトルを引き渡す)
    020			super(name);
    021						// Windowの大きさ
    022			setSize(640, 470);   // +40, +70
    023						// ContentPane の取得と MainPanel の追加
    024			Dimension d  = getSize();   // Windowの大きさ
    025			d.width     -= 40;
    026			d.height    -= 70;
    027			MainPanel pn = new MainPanel(d);   // MainPanel オブジェクトの生成
    028			getContentPane().add(pn);   // MainPanel オブジェクトを ContentPane に追加
    029						// ウィンドウを表示
    030			setVisible(true);
    031						// イベントアダプタ
    032			addWindowListener(new WinEnd());
    033		}
    034	
    035		/******************************/
    036		/* 上,左,下,右の余白の設定 */
    037		/******************************/
    038		public Insets getInsets()
    039		{
    040			return new Insets(50, 20, 20, 20);
    041		}
    042	
    043		/************/
    044		/* 終了処理 */
    045		/************/
    046		class WinEnd extends WindowAdapter
    047		{
    048			public void windowClosing(WindowEvent e) {
    049				System.exit(0);
    050			}
    051		}
    052	}
    053	
    054	class MainPanel extends JPanel implements Runnable
    055	{
    056		boolean state = true;
    057		double x0 = 0.0, x1, x2, y1, y2;   // x方向の初期位置と各物体の位置
    058		double t = 0.0, dt = 0.04;   // 時間とその刻み幅
    059		double v0 = 50.0, v1, v2;   // 初期速度と各物体の速度
    060		double a = 30.0;   // 加速度
    061		double ang1 = 20.0 * Math.PI / 180.0, ang2 = -20.0 * Math.PI / 180.0;   // 進行方向
    062		Dimension d;   // パネルの幅と高さ
    063		Thread th;
    064	
    065		MainPanel(Dimension d1)
    066		{
    067			d  = d1;
    068			x1 = x0;
    069			y1 = d.height / 2;
    070			v1 = v0;
    071			x2 = x0;
    072			y2 = d.height / 2;
    073			v2 = v0;
    074			setBackground(new Color(238, 255, 238));   // 背景色の設定
    075			th = new Thread(this);   // スレッドの生成とスタート
    076			th.start();
    077		}
    078						// 他ページへ移動の際,一時的にスレッドを停止
    079		public void stop()
    080		{
    081			state = false;
    082		}
    083						// スレッドの実行
    084		public void run()
    085		{
    086			while (state) {
    087				try {
    088					th.sleep(40);
    089				}
    090				catch (InterruptedException e) {}
    091				if (x2 < d.width) {
    092					x1 += v1 * dt * Math.cos(ang1);
    093					y1 += -v1 * dt * Math.sin(ang1);
    094					double t1 = v2;
    095					double t2 = v2 + a * dt;
    096					v2  = t2;
    097					x2 += 0.5 * (t1 + t2) * dt * Math.cos(ang2);
    098					y2 += -0.5 * (t1 + t2) * dt * Math.sin(ang2);
    099				}
    100				else {
    101					x1 = x0;
    102					v1 = v0;
    103					y1 = d.height / 2;
    104					x2 = x0;
    105					v2 = v0;
    106					y2 = d.height / 2;
    107					t  = 0;
    108				};
    109				repaint();
    110			}
    111		}
    112						// 描画
    113		public void paintComponent(Graphics g)
    114		{
    115			super.paintComponent(g);   // 親クラスの描画
    116	
    117			g.setColor(Color.green);
    118			g.fillOval((int)x1-20, (int)y1-20, 40, 40);
    119			g.setColor(Color.red);
    120			g.fillOval((int)x2-20, (int)y2-20, 40, 40);
    121		}
    122	}
    			
    061 行目

      20 度をラジアンに変換しています.PI は,Math クラスのフィールドであり,円周率 π の値です.Math クラスでは,すべてのフィールド(変数)に対して static 宣言がなされています,そのため,他のクラスから Math.PI のように,「クラス名.フィールド名」の形で参照する必要があります.static 宣言されたフィールドやメソッド(スタティック変数スタティックメソッド)の参照は,「オブジェクト.フィールド名」「オブジェクト.メソッド名」でないことに注意してください.092,093 行目の Math クラスのメソッド sincos においても同様です.

    092,093 行目

      上 20 度の方向に向かう円の x 座標,及び,y 座標を,(2) 式を利用して計算しています.

    094 行目~ 098 行目

      下 20 度の方向に向かう円の x 座標,及び,y 座標を,(5) 式,(6) 式を利用して計算しています.

情報学部 菅沼ホーム Java 目次 基礎技術目次 索引