12/21 ゲームプログラム,アルゴリズムと効率


質問と回答

Q.
以下のプログラムを作りましたが、メニューバーは表示されても、月やら雪だるまやらの絵は表示されません。どこが悪いんでしょうか?
// AWTを使うので java.awt.*を import する
import java.awt.*;
// イベント駆動関係のクラスを用いるため
import java.awt.event.*;

// 独立したウィンドウを開くので,Frameクラスのサブクラスにする
class Yukidaruma extends Frame implements KeyListener,MouseListener,ActionListener{
    // 表示する際に必要なインスタンス変数を宣言しておく
    int mx=100,my=100;
    MenuItem mi1,mi2,mi3;
    Frame f;
    Color col;
    public Yukidaruma(String title){
	super(title);
	// GUI部品と,Event Listenerを関連づける
	addKeyListener(this);
	addMouseListener(this);
    }
    // KeyListenerを実装するためのメソッド
    public void keyPressed(KeyEvent e){
	int key=e.getKeyChar();
	System.out.println("keyPressed("+e+","+key+")");
	if(key=='q') System.exit(0);
    }
    // 要らないイベントに対応するメソッドも中身は空で書いておく必要がある.
    public void keyReleased(KeyEvent e){}
    public void keyTyped(KeyEvent e){}
    // MouseListenerを実装するためのメソッド
    public void mousePressed(MouseEvent e){
	// 押された時のマウスカーソルの位置を得る
	mx=e.getX();
	my=e.getY();
	// 再表示をおこなう
	repaint();
    }
    // 要らないイベントに対応するメソッドも中身は空で書いておく必要がある.
    public void mouseReleased(MouseEvent e){}
    public void mouseExited(MouseEvent e){}
    public void mouseEntered(MouseEvent e){}
    public void mouseClicked(MouseEvent e){}
    Yukidaruma() {
	f=new Frame("Yukidaruma");
	// フレームにレイアウトマネージャを設定。
	f.setLayout(new FlowLayout(FlowLayout.LEFT,20,20));
	// メニューバーmbの作成。
	MenuBar mb = new MenuBar();
	// メニューバーmbをフレーム内に追加。
	f.setMenuBar(mb);
	// メニューmの作成。
	Menu m = new Menu("メニュー");
	// メニューmをメニューバー内に追加。
	mb.add(m);
	// サブメニューm1の作成。
	Menu m1 = new Menu("未定");
	// サブメニューm1をメニュー内に追加。
	m.add(m1);
	// セパレータをメニューmに追加。
	m.addSeparator(); 
	// サブメニューm2の作成。
	Menu m2 = new Menu("月の色");
	// サブメニューm2をメニュー内に追加。
	m.add(m2);
	// メニュー項目の作成。
	mi1 = new MenuItem("赤");
	mi2 = new MenuItem("黄");
	mi3 = new MenuItem("青");
	// メニュー項目をサブメニュー2内に追加。
	m2.add(mi1);
	m2.add(mi2);
	m2.add(mi3);
	f.setSize(400,400);
	f.setVisible(true);
	// イベントリスナの登録。
	mi1.addActionListener(this); 
	mi2.addActionListener(this); 
	mi3.addActionListener(this); 
    }
    // ActionListenerインターフェースのメソッド。
    public void actionPerformed(ActionEvent e) {
	if( e.getSource() == mi1 ) {col=Color.red;}
	if( e.getSource() == mi2 ) {col=Color.yellow; }
	if( e.getSource() == mi3 ) {col=Color.blue; }
	this.repaint();
    }
    public void paint(Graphics g){
	// インスタンス変数にしたがって描画するコードを書く
	g.setColor(Color.black);
	g.fillRect(0,0,400,400);
	g.setColor(col);
	g.fillOval(100,200,200,200);
	g.setColor(new Color(200,200,100));
	g.fillOval(130,220,40,40);
	g.fillOval(120,300,50,50);
	g.fillOval(180,280,100,100);
	g.setColor(Color.white);
	g.fillOval(mx,my,120,120);
	g.fillOval(mx+20,my-80,80,80);
	g.setColor(Color.red);
	g.fillOval(mx+20,my-45,10,10);
	g.fillOval(mx+30,my-45,10,10);
	g.drawLine(mx+80,my+50,mx+120,my+20);
    }
    public static void main(String[] args) {
	Yukidaruma window = new Yukidaruma();
    }
}
A.
クラスYukidarumaはFrameのサブクラスなのですが,プログラムを見ると, Yukidarumaのコンストラクタの中で,
f=new Frame("Yukidaruma");
として,別のFrameを作ってそれを表示するというコードになっていますね. 作られたfはpaintメソッドもオーバーダイドされていないし(雪だるまを書くメソッドではなく,背景を描くだけ), メニューバーもつけられていないので,あのような動作になります. Yukidarumaクラス自体を表示させるには,
f=new Frame("Yukidaruma");
としている行を取り,コンストラクタの最初で,
super("Yukidaruma");
とした上で,
f.setVisible(true);
などとしているところをすべて,
setVisible(true);
のように書き換えてください.
Q.
おっしゃったとおりに書き直しましたが、今度は雪だるまが動かなくなってしまいました。どうしてでしょうか?
A.
前回は見落としていましたが,Yukidarumaのコンストラクタが,
public Yukidaruma(String title)
Yukidaruma() 
と2カ所で定義されているのですね.前者では,
addKeyListener(this);
addMouseListener(this);
を実行していて,後者では実行していません.mainの方で呼び出しているのは後者のコンストラクタなので, 前者の定義は取り除いて,後者でも
addKeyListener(this);
addMouseListener(this);
を実行するようにすると動くようになるはずです.
Q.
動く雪を表現しようとして、www資料のリアルタイムゲームのサンプルを真似てGUI雪だるまに組み込んだのですが、timeTick()メソッドに あたる部分に「この文に制御が移ることはありません」というエラーメッセージが出ました。これはどういうエラーなのでしょうか?
A.
たとえば,
class Test {
  public static void main(String[] args){
    for(int i=0;;i++){
      if(i==100) return;
    }
    System.out.println("Hello world");
  }
}
をコンパイルすると,
Test.java:6: この文に制御が移ることはありません。
System.out.println("Hello world");
^
エラー 1 個
というエラーが出るはずです.プログラムを見るとわかりますが,前のfor文から抜ける時は,mainからreturn で抜けてしまうので,6行目が実行されることがないことをコンパイラが検出してエラーにしたというものです. おそらく作成したプログラムにも同様のプログラム上のミスがあるものと思われます.
Q.
appletを使ってMakeBoard.javaを作ってみたのですが、コンパイルできるもののうまく実行されません。
appletviewer MakeBoard.java
load: AppTest は public ではありません。あるいは public なコンストラクタを持って
いません。
java.lang.IllegalAccessException: Class sun.applet.AppletPanel can not access a
member of class AppTest with modifiers ""
at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:57)
at java.lang.Class.newInstance0(Class.java:302)
at java.lang.Class.newInstance(Class.java:261)
at sun.applet.AppletPanel.createApplet(AppletPanel.java:619)
at sun.applet.AppletPanel.runLoader(AppletPanel.java:548)
at sun.applet.AppletPanel.run(AppletPanel.java:299)
at java.lang.Thread.run(Thread.java:534)"
あるいはブラウザ上で”Javaアップレットの読み込みに失敗しました。” というエラーが出ます。どうすればよいのでしょうか?
A.
appletviewer MakeBoard.java
> load: AppTest は public ではありません。あるいは public なコンストラクタを持って
> いません。
という実行時エラーは,アプレットプログラミング のページに書かれている という条件が満たされない時に出ます.同じページのサンプルプログラムAppTest中に以下のように書かれていると思いますが,
 // アプレットはpublicである必要
 public class AppTest extends Applet implements ActionListener{
class宣言の前にpublicを入れてコンパイルすると,このエラーは出なくなると思います.

過去の課題について


今日の内容


最終課題について


今日の課題


次回(1/11)