1. ホーム
  2. Java

JAVAで小さなゲームを書くことを教える

2022-02-26 07:14:36

 先週の授業で、先生からJavaでアプレットを書くように言われました。私は古典的な飛行機ゲームを書きたかったのですが、ふと思い立ってこんなものを書いてみました。

I. 全体的な感想

  1. フォームクラスを継承し、フォームの更新をオーバーライドする
  2. キーストローク・リスナーを追加する
  3. ゲームオーバー画面
  4. キングスクラスとホットドッククラスの移動軌跡計算
  5. 衝突の判定 音楽の再生
  6. フォームの常時更新 処理内容

II. コーディング

1. 画像

まず、Baiduで画像を検索し、それをpsでカットし、背景画像を検索して完了です。

 画像を読み込むためのツールクラスも必要です。getResourceを使って画像のURLパスを取得し、java独自のツールクラスImageIOを使って画像を読み込むことができます。

public class GameUtil {

	public static Image getImage(String path){
		URL url=GameUtil.class.getClassLoader().getResource(path);
		BufferedImage img=null;
		try {
			img = ImageIO.read(url);
		} catch (IOException e) {
			e.printStackTrace();
		}	
		return img;
	}
}

2. フォーム

フォームを継承するクラスを作成し、フォームの幅、高さ、位置、閉じる設定、スケーラビリティを設定します。

updateメソッドとdrawメソッドをオーバーライドして、SiCとhotdogの描画と衝突判定のロジックを記述します。

ボタンリスナーを追加し、Satoshiを動かすためのボタンのクリックとリリースを処理する。

		
	public void launchFrame(){
		setSize(width,height);
		setResizable(false);
		setLocation(200,20);
		setVisible(true);
		addKeyListener(new KeyMoniter());
		addWindowListener(new WindowAdapter(){
			@Override
			public void windowClosing(WindowEvent e) {
				System.exit(0);
			}
		});
	}

        @Override
	public void paint(Graphics graphics) {}
	@Override
	public void update(Graphics g){}

	class KeyMoniter extends KeyAdapter{
		@Override
		public void keyPressed(KeyEvent e) {
	            //Key pressed
		}
		@Override
		public void keyReleased(KeyEvent e) {
		    //Key lifted
		}	
	}


3. SiCとホットドッグ

には、位置を計算し、絵を描くという、実は絶え間ない作業が必要なのです。

SiCのプロパティを定義し、コンストラクタで初期化します。

	boolean left,right,down,up;
	
	public int x,y,width,height;
	
	Image img ;

	public Plane(String img_path,int x, int y){
		this.img = GameUtil.getImage(img_path);
		this.x = x;
		this.y = y;
		width = img.getWidth(null);
		height = img.getWidth(null);
		live = true;
	}

にSiCを描画します。

	public void draw(Graphics g){
		if(live){
			g.drawImage(img, x, y, null);
			move();
		}
	}

次に重要なのは、サトシの位置計算ですが、なぜサトシの描画方法に位置計算を書き込むのでしょうか。一番の理由は、メソッド計算をキーの押し引きに書いてしまうと、サトシの位置を動かそうと思ったらキーボードをめちゃくちゃ押さないといけないし、キーの長押しで移動の有無を制御しようと思ったら、サトシの描画メソッドに移動メソッドを書かないといけないからだそうです。

移動の際に考慮すべき境界線もあります

	public void move(){
		if(left&&x>=10){
			x -= 10;
		}		
		if(up&&y>=30){
			y -= 10;
		}
		if(right&&x<=FeiJiGame.width-60){
			x += 10;
		}
		if(down&&y<=FeiJiGame.height -60){
			y += 10;
		}
	}

	public void KeyPressedControlDirection(KeyEvent e){
		int key_code = e.getKeyCode();
		if(key_code == 37){
			left = true;
		}
		if(key_code == 38){
			up = true;
		}
		if(key_code == 39){
			right = true;
		}
		if(key_code == 40){
			down = true;
		}
	}

	public void KeyRelasedControlDirection(KeyEvent e){
		int key_code = e.getKeyCode();
		if(key_code == 37){
			left = false;
		}
		if(key_code == 38){
			up =false;
		}
		if(key_code == 39){
			right = false;
		}
		if(key_code == 40){
			down = false;
		}
	}

最後に、SiCの生存期間を書き換えるための2つのメソッドが提供されています。

hotdogクラスは、常時動作させ、キーイベントに反応しないようにするのが簡単です。

いくつかのプロパティを定義し、コンストラクタで初期化します。

	double speed=15;
	double degree;

	public double x,y;
	public int width,height;
	Image img;

	public Bullet(String img_path){
		img = GameUtil.getImage(img_path);
		degree = Math.random()*Math.PI*2;
		x=FeiJiGame.width/2;
		y = FeiJiGame.height/2;
		width = 10;
		height = 10;
	}

ランダム度の目的は、最初は異なる向きにすることで、sin関数とcos関数を使って、ホットドッグの動きを制御しつつ、境界を意識することです

	public void draw(Graphics g){
		g.drawImage(img, (int)x, (int)y, null);
		x += speed*Math.cos(degree);
		y += speed*Math.sin(degree);

		if(x>FeiJiGame.width-width||x<width){
			degree=Math.PI-degree;
		}
		if(y>FeiJiGame.height-height||y<height){
			degree=-degree;
		}
	}

4. 衝突判定と音楽再生

javaのawtに矩形クラスがあり、2つの矩形が重なる部分があるかどうかを判断し、重なる部分があれば衝突するようになっている

Rectangle bulletRectangle = new Rectangle((int)bullet.x,(int)bullet.y,bullet.width,bullet.height);
Rectangle planeRectangle = new Rectangle(plane.x,plane.y,plane.width,plane.height);
boolean collide= bulletRectangle.intersects(planeRectangle);

音楽はサードパーティーのライブラリ、jl-1.0.1.jarを使って再生することができますが、ホットドッグを食べ続ける可能性があるので、マルチスレッドにする必要があります

	class MusicPlayer implements Runnable{
		@Override
		public void run() {
			try {
				new Player(new FileInputStream(FeiJiGame.class.getClassLoader().getResource("raw/music.mp3").getPath().substring(1)))).play ();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}


5. 仕上げ

あとはフォームを更新するだけですが、これはスレッド

	class PaintThread extends Thread {
		@Override
		public void run() {
			while(!Thread.currentThread().isInterrupted()){
				repaint();
				try {
					Thread.sleep(40);
				} catch (InterruptedException e) {
					Thread.currentThread().interrupt();
				}
			}
		}	
	}

右上に現在のゲーム時間を表示し、最後にはテキスト情報も表示する必要があり、ツールクラスを書きました

fontでフォントを制御し、graphics.setFontでフォントを設定し、drawStringで描画します。

	public void printInfo(Graphics g,String message,int size,int x,int y){
		g.setColor(Color.white);
		Font f = new Font("宋体",Font.BOLD,size);
		g.setFont(f);
		g.drawString(message, x,y);
	}

ゲーム終了時に情報を表示し、時間に応じてレベルを表示します。

	private void gameOver(Graphics graphics) {
		printInfo(graphics,"GAME OVER",80,270,300);
		int survivalTime = (int)(endTime.getTime()-starTime.getTime())/1000;
		printInfo(graphics,"Time to eat hot dog: "+survivalTime+"seconds",40,300,400);

		switch(survivalTime/10){
			case 1:
				printInfo(graphics,"Doktor",50,350,500);
				break;
			case 2:
				printInfo(graphics,"Ascend",50,350,500);
				break;
			case 3:
				printInfo(graphics,"little success",50,350,500);
				break;
			default:
				printInfo(graphics,"First time in the world",50,350,500);
				break;
		}
		paintThread.interrupt();
	}

フォームの初期化時にホットドッグを追加して再描画スレッドを開始するには、タイマーを起動します。

		for(int i=0;i<15;i++){
			Bullet bullet = new Bullet("images/hotdog.png");
			bulletList.add(bullet);
 		}
		starTime = new Date();
		endTime = new Date();
		paintThread = new PaintThread();
		paintThread.start();


塗り分けと更新の重複

        @Override
	public void paint(Graphics graphics graphics) {
		graphics.drawImage(bg, 0, 0, null);
		plane.draw(graphics);
		endTime = new Date();
		if(gameState){
			for(int i=0;i<bulletList.size();i++){
				Bullet bullet=bulletList.get(i);
				bullet.draw(graphics);

				Rectangle bulletRectangle = new Rectangle((int)bullet.x,(int)bullet.y,bullet.width,bullet.height);
				Rectangle planeRectangle = new Rectangle(plane.x,plane.y,plane.width,plane.height);
				boolean collide= bulletRectangle.intersects(planeRectangle);

				if(collide){
					if (bulletList.size()! =0){
						executorService.execute(musicPlayer);
						bulletList.remove(i);
						if (bulletList.size()==0){
							gameState = false;
						}
					}
				}
			}
		}else {
			endTime = new Date();
			gameOver(graphics);
			paintThread.interrupt();
		}

		int count_time = (int)(endTime.getTime() - starTime.getTime())/1000;
		printInfo(graphics,"You've eaten"+count_time+"seconds",20,750,50);
	}

	Image ImageBuffer = null;  
	Graphics GraImage = null;
	@Override
	public void update(Graphics g){
	    ImageBuffer = createImage(this.getWidth(), this.getHeight());
	    GraImage = ImageBuffer.getGraphics();
	    paint(GraImage);
	    GraImage.dispose();
	    g.drawImage(ImageBuffer, 0, 0, this);
	}  

キーリスナー内部で SiS のキープレスとリリースのメソッドを呼び出す

	class KeyMoniter extends KeyAdapter{
		@Override
		public void keyPressed(KeyEvent e) {
			plane.KeyPressedControlDirection(e);
		}
		@Override
		public void keyReleased(KeyEvent e) {
			plane.KeyRelasedControlDirection(e);
		}	
	}

いよいよゲームスタート

	public static void main(String[] args) {
		FeiJiGame game = new FeiJiGame();
		game.loadGame();
	}

<イグ

github https://github.com/s15603333319/SiCongEatHotDog

csdn. https://download.csdn.net/download/qq_37482202/11084561