#author("2018-01-10T01:49:20+00:00","ocha","ocha") #author("2018-01-10T02:05:02+00:00","ocha","ocha") [[How2Computing]] *2017年冬休み宿題 [#w0c3dfee] 解答編は年明けに公開します。 それまでに、メニュー付きバージョンができた人は、 2017年12月31日までに提出してください。 ダブルバッファの節は無視して良いです。 **2017年12月31日までに完成した人 [#ld66cd3d] SimpleAnime.javaとSimpleAnime.classを出席番号+名前のフォルダにいれて、ZIP圧縮して提出してください。 (sshで接続すれば自宅からでも提出可能だと思います。冬休み前にやり方を確認しておいてください。) 締め切りは12月31日11:59pmです。 **2017年12月31日までに完成しなかった人 [#ube93f76] 解答編を年明けに公開します。それを見て、動かして、完成したものを、1月18日の授業開始時に提出お願いします。年末に提出した人も、この時間にもう一度同じものを提出してください。 *なにもしないウィンドウを出す [#ld27966b] 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(); } } *動くボールを出す [#n2c9292e] 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(); } } http://gyazo.com/12d1f047af8d4f87f7199c8a7db66080.png *マルチスレッドについて [#r0f3f212] Javaでは次のようにしてマルチスレッドを実現します +Runnableをimplementsしたクラスを作る +そのインスタンスを作る +そのインスタンスを引数にしてThreadクラスのインスタンスを作る +そのインスタンスのstart()メソッドを呼ぶ 例えば以下のプログラムではメインとサブのスレッドが別個にに数字を表示していきます。 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){} } } } *マルチスレッド化する [#cda2cb18] 上記のボールを動かすプログラムでは、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){} } } } *ボールの色と早さをメニューで指定する [#sb0a46ba] 上記のプログラムにメニューを追加して、 ボールの色と速さをメニューで指定するようにします。 2018年1月4日あたりに解答編として公開します。 http://gyazo.com/d5db8ca6fa153520e94ee7d2b7a93915.png *ボールの色と早さをメニューで指定する(回答編) [#sanmple] *ボールの色と早さをメニューで指定する(回答編) [#sample] 上記のプログラムにメニューを追加して、 ボールの色と速さをメニューで指定するようにしました。 メニューはメインのJFrameインスタンスで作ってこれに貼りつけていますが、 Action Listenerは、別スレッドで動いているAnimatorクラスのインスタンスとしました。 import java.awt.*; import javax.swing.*; import java.awt.event.*; import java.lang.Thread; class Animator implements Runnable, ActionListener { Graphics g; int xdelta =5; public void setGraphics(Graphics animeG) { g=animeG; } public void actionPerformed(ActionEvent e) { String command = e.getActionCommand(); if(command !=null) { System.out.println(command); } if(command=="red") g.setColor(Color.red); if(command=="blue") g.setColor(Color.blue); if(command=="yellow") g.setColor(Color.yellow); if(command=="fast") if(xdelta>0) xdelta=30; else xdelta=-30; if(command=="slow") if(xdelta>0) xdelta=5; else xdelta=-5; } public void run() { int x=0; 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=-xdelta; if(x<0) xdelta=-xdelta; } } } class SimpleAnime extends JFrame { JPanel panel; Graphics g; Animator animator; private void makeMenu() { JMenuBar menubar = new JMenuBar(); JMenu menu = new JMenu("color"); JMenu menuSpeed = new JMenu("speed"); JMenuItem item1 = new JMenuItem("red"); item1.addActionListener(animator); item1.setActionCommand("red"); JMenuItem item2 = new JMenuItem("blue"); item2.addActionListener(animator); item2.setActionCommand("blue"); JMenuItem item3 = new JMenuItem("yellow"); item3.addActionListener(animator); item3.setActionCommand("yellow"); menu.add(item1); menu.add(item2); menu.add(item3); JMenuItem item4 = new JMenuItem("fast"); item4.addActionListener(animator); item4.setActionCommand("fast"); JMenuItem item5 = new JMenuItem("slow"); item5.addActionListener(animator); item5.setActionCommand("slow"); menuSpeed.add(item4); menuSpeed.add(item5); menubar.add(menu); menubar.add(menuSpeed); this.setJMenuBar(menubar); } private void init() { animator = new Animator(); this.setTitle("SimpleAnime"); this.setSize(300,200); this.makeMenu(); 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){} } } } http://gyazo.com/d5db8ca6fa153520e94ee7d2b7a93915.png *この先改良すべきこと [#u99ec852] アニメーションの途中で、ボールがちらつくことがあります。ボールの場所を矩形で消して、新しいボールを描いているので、その途中の作業が見えてしまうからです。これを無くすには、ダブルバッファの手法を用います。すなわち、描画する面をもう一枚用意して、そちらに描画し、描画が終わったところで、一気に更新する方法です。 ダブルバッファの手法は、授業の最終課題である「お絵かきプログラム」のところで説明します。