解答編は年明けに公開します。 それまでに、メニュー付きバージョンができた人は、 2017年12月31日までに提出してください。 ダブルバッファの節は無視して良いです。
SimpleAnime.javaとSimpleAnime.classを出席番号+名前のフォルダにいれて、ZIP圧縮して提出してください。 (sshで接続すれば自宅からでも提出可能だと思います。冬休み前にやり方を確認しておいてください。) 締め切りは12月31日11:59pmです。
解答編を年明けに公開します。それを見て、動かして、完成したものを、1月18日の授業開始時に提出お願いします。年末に提出した人も、この時間にもう一度同じものを提出してください。
import java.awt.*; import javax.swing.*; import java.awt.event.*; class SimpleAnime extends JFrame { private void init() { this.setTitle("SimpleAnime"); this.setSize(300,200); this.setVisible(true); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } public static void main(String[] args) { SimpleAnime frame = new SimpleAnime(); frame.init(); } }
import java.awt.*; import javax.swing.*; import java.awt.event.*; class SimpleAnime extends JFrame { JPanel panel; Graphics g; private void init() { this.setTitle("SimpleAnime"); this.setSize(300,200); panel = new JPanel(); this.getContentPane().add(panel); this.setVisible(true); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); g=panel.getGraphics(); g.setColor(Color.blue); int x=0, xdelta=10; while(true) { g.fillOval(x,80,50,50); try{Thread.sleep(50);}catch(Exception e){} g.clearRect(x, 80, 52,52); x+=xdelta; if(x>250) xdelta=-10; if(x<0) xdelta=10; } } public static void main(String[] args) { SimpleAnime frame = new SimpleAnime(); frame.init(); } }
Javaでは次のようにしてマルチスレッドを実現します
例えば以下のプログラムではメインとサブのスレッドが別個にに数字を表示していきます。
import java.lang.Thread; class SubThread implements Runnable { public void run() { int i=0; while(true) { System.out.println("this is sub:" + i++); try {Thread.sleep(1000);}catch(Exception e){} } } } class ThreadTest { public static void main(String[] args) { SubThread sub=new SubThread(); new Thread(sub).start(); for(int i=0;;i++) { System.out.println("this is main:" + i); try {Thread.sleep(2000);}catch(Exception e){} } } }
上記のボールを動かすプログラムでは、main()で、frame.init()したあと、このメソッドで無限にアニメーション書き換えを行うことになります。なので、二度とmain()には戻ってきません。(以下で示した、メニューは、また別のスレッドで動くので、このままでも動きます)
アニメーションだけをするなら、これでも良いのですが、他にも仕事をしたい場合には難しいですし、やれないことはないですが、タイミングを計るのが難しいです。ということで、アニメーションする部分は、別のスレッドにして、そちらに任せてしまうのが通常です。
以下のように、別のインスタンスを別スレッドで動かします。別スレッドで動かすインスタンスを作るために、Animatorという名前のクラスを用意しました。別スレッドで動かすためには、Runnableをimplementする必要があります。ここで必須のrunというメソッドが、裏で実行されるので、そこに、上記のプログラムのアニメーション描画部分をそっくり移動させます。Graphics gの情報を伝えておく必要があるので、それを設定するメソッドも作りました。アニメーションしつつ、main()の方で数字を表示しています。
import java.awt.*; import javax.swing.*; import java.awt.event.*; import java.lang.Thread; class Animator implements Runnable { Graphics g; public void setGraphics(Graphics animeG) { g=animeG; } public void run() { int x=0, xdelta=10; while(true) { g.fillOval(x,80,50,50); try{Thread.sleep(50);}catch(Exception e){} g.clearRect(x, 80, 52,52); x+=xdelta; if(x>250) xdelta=-10; if(x<0) xdelta=10; } } } class SimpleAnime extends JFrame { JPanel panel; Graphics g; Animator animator; private void init() { animator=new Animator(); this.setTitle("SimpleAnime"); this.setSize(300,200); panel = new JPanel(); this.getContentPane().add(panel); this.setVisible(true); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); g=panel.getGraphics(); g.setColor(Color.blue); animator.setGraphics(g); new Thread(animator).start(); } public static void main(String[] args) { SimpleAnime frame = new SimpleAnime(); frame.init(); for(int i=0;;i++) { System.out.println(i); try {Thread.sleep(500);}catch(Exception e){} } } }
上記のプログラムにメニューを追加して、 ボールの色と速さをメニューで指定するようにします。
2018年1月4日あたりに解答編として公開します。
アニメーションの途中で、ボールがちらつくことがあります。ボールの場所を矩形で消して、新しいボールを描いているので、その途中の作業が見えてしまうからです。これを無くすには、ダブルバッファの手法を用います。すなわち、描画する面をもう一枚用意して、そちらに描画し、描画が終わったところで、一気に更新する方法です。
ダブルバッファの手法は、授業の最終課題である「お絵かきプログラム」のところで説明します。