/* * Javaらしくクラス概念を取り入れたブロック崩し * * 著作権放棄、改変自由 * * カプセル化も中途半端ですし、クラス概念の勉強用には * 役立たないかも知れません。参考程度に留めておいてください。 */ /* 現在判明しているバグ * ・板の厚さが厚すぎると(ボールの半径以上の時) *   板の端にボールが当った時に動きが奇妙になる。 * ・再起動するとボールの速度が著しく変ることがある。 *  (アプレットビューワ時のみ、ブラウザでは正常) * ・ボールの速度がボール半径を越えると正常に動作しない。 * * 以上のバグのうまい解決法がありましたら * maraiko@nexon.co.jp まで御教授いただければ幸いです。 * 特に「再起動すると〜」のバグについては浅学ゆえか * 原因すらも全く分かりません。勉強不足と言われてしまえば * それまでなのですが、「○○について勉強すれば良い」 * というヒントだけでもいただければ非常にありがたいです。 */ import java.applet.*; import java.awt.*; import java.io.*; import java.lang.*; import java.awt.event.*; public class BKwithClass extends Applet implements Runnable { // グローバル変数 Thread thread = null; // スレッド Graphics bg; // ダブルバッファリング用 Image backimage; int setTime; // スレッド処理間隔 Ball ball; Board board; static int wx, wy; // ウィンドウサイズ static boolean ifBallFired; // trueならばボール発射済み static boolean ifGameOver; // trueならばゲームオーバー static boolean ifGameClear; // trueならばゲームクリア public void init() { // ウィンドウサイズをブラウザから取得 wx = getSize().width; wy = getSize().height; // 板オブジェクト作成 board = new Board( 200, 430, 80, 4); // ダブルバッファリング準備 backimage = createImage( wx, wy); bg = backimage.getGraphics(); setTime = 25; ifBallFired = false; ifGameOver = false; ifGameClear = false; // ブロックマップ初期化 Block.formatMap( 10, 20, 5); // マウス処理アクションリスナー定義と登録 addMouseListener(new MouseAction()); addMouseMotionListener(new MouseMotionAction()); } // init() // 各種セッター public static void setIfBallFired(boolean flg) { ifBallFired = flg; } public static void setIfGameOver(boolean flg) { ifGameOver = flg; } public static void setIfGameClear(boolean flg) { ifGameClear = flg; } public void paint(Graphics g) { // 画面を白で初期化 bg.setColor(Color.white); bg.fillRect( 0, 0, wx, wy); // ブロック描画 // グラフィックスコンテキスト(GC)を渡している Block.drawBlock( bg ); // ボールと板の描画 // グラフィックスコンテキスト(GC)を渡している if(ball != null) ball.drawBall( bg ); board.drawBoard( bg ); // ゲームクリア時の描画 if(ifGameClear){ bg.setFont(new Font("Dialog", Font.BOLD, 30)); bg.setColor(Color.black); bg.drawString("Congratulation!", 155, 200); bg.setFont(new Font("Dialog", Font.PLAIN, 15)); bg.drawString("Now, click to start again.", 180, 300); } // ゲームオーバー時の描画 if(ifGameOver){ bg.setFont(new Font("Dialog", Font.BOLD, 30)); bg.setColor(Color.black); bg.drawString("Ball failed", 180, 200); bg.setFont(new Font("Dialog", Font.PLAIN, 15)); bg.drawString("Click to fire new ball.", 180, 300); } g.drawImage( backimage, 0, 0, null); } public void update(Graphics g) { // 再描画 paint( g ); } public void start() { if (thread == null) { // スレッド生成&開始 thread = new Thread(this); thread.start(); } } public void stop() { // スレッド終了 thread = null; } public void run() { while(thread != null) { if(ifBallFired){ // スレッド本処理 // ボール移動 ball.moveBall(); // ボールと板との当り判定 // 変数board, ball は bkwithclass の変数であるために // Ballクラス内(Boardクラス内)からはボールと板との // 衝突判定を行うことが出来ないのでここで行っている。 if( board.checkCollision( ball.ballX, ball.ballY) ) { ball.vectorYChange(); } // 再描画 repaint(); try { // スレッド一時停止 thread.sleep(setTime); } catch (InterruptedException e) { } if(ifGameOver){ ball = null; setIfBallFired(false); repaint(); } if(ifGameClear){ball = null; setIfBallFired(false); repaint(); } } // if } // while } // run() class MouseAction extends MouseAdapter { public void mousePressed(MouseEvent e) { // マウスダウン if(!ifBallFired){ // ボールオブジェクト作成 if(ball == null) ball = new Ball( e.getX(), 420, 5, 5); // ゲームクリア済みだった場合はブロック初期化 if(ifGameClear) Block.formatMap( 10, 20, 5); // 各変数を初期化 setIfBallFired(true); setIfGameOver(false); setIfGameClear(false); } } } // MouseAction class MouseMotionAction extends MouseMotionAdapter { public void mouseMoved(MouseEvent e) { // マウスムーブ int ex = e.getX(); board.moveBoard( ex ); // ボールが発射されるまではスレッドによる再描画が無いため、 // マウスムーブ時にも最描画をするようにしている。 repaint(); } } // MouseMotionAction } // BKwithClass // -----------------------ウィンドウ全般 class windowData{ // ウィンドウサイズ(定数) final static int WINDOWX = BKwithClass.wx; final static int WINDOWY = BKwithClass.wy; } // windowData // -----------------------ブロック // ブロックを複数作ってもしょうがないので static class Block extends windowData{ private static int map[][]; // ブロックマップ private static int blockX, blockY; // マップの要素数 private static int blockLine; // 壁を入れたブロックの列数 private static int blockSizeX, blockSizeY; // ブロックサイズ // 板用に板が存在できる領域を示す。 private static int bExistLx, bExistRx; static void formatMap(int numx, int numy, int numline) { // ブロックマップの初期化 // [numx][numy+1]の2次元配列 map を生成し、 // 上から numline 列をブロック化し、 // さらに下部を残して各端を壁で囲む。 // 0 ・・・ 通常 // 1〜3 ・・・ ブロック // 9 ・・・ 壁 // ちなみに縦に numy+1列作っているのはボールが画面外に消えるまで // ArrayIndexOutOfBoundsExceptionを起こしたくないため。 blockX = numx; blockSizeX = WINDOWX / blockX; blockY = numy; blockSizeY = WINDOWY / blockY; blockLine = numline; map = new int[numx][numy+1]; bExistLx = blockSizeX; bExistRx = WINDOWX - blockSizeX; // 0で初期化 for(int ix=0; ix 0.5){ vectorX = speed; } else { vectorX = -speed; } vectorY = -speed; } // Ball() synchronized void moveBall() { // ボール移動 // 横・縦移動ともに移動先が壁の内部である場合は // 方向ベクトルの符号を変え、座標を移動前に戻す ballX += vectorX; if(Block.checkCollisionWithBlock(ballX, ballY)) { vectorX = -vectorX; ballX += vectorX; } ballY += vectorY; if(Block.checkCollisionWithBlock(ballX, ballY)) { vectorY = -vectorY; ballY += vectorY; } } // move() synchronized void vectorYChange() { // ボールのベクトル変更(Y) vectorY = -vectorY; } // vectorChange() void drawBall(Graphics bg) { // ボール描画 // Javaでは円を囲んだ四角形の左上を描画の基準座標とするので // 円の中心からradiusだけずれた位置に円を表示している。 bg.setColor(ballColor); bg.fillOval( ballX-radius, ballY-radius, radius*2, radius*2); } } // Ball // -----------------板 class Board extends windowData{ private int boardX, boardY; // (x,y)座標 private int range; // 幅 private int thickness; // 厚さ private Color boardColor = Color.black; // 色 Board (int x, int y, int r, int t) { // コンストラクタ boardX = x; boardY = y; range = r; thickness = t; } // Board() void moveBoard(int x) { // 板移動 boardX = x - range/2; // 板がブロックと重なるのを防ぐ int elx = Block.get_bExistLx(); int erx = Block.get_bExistRx(); if ( x-range/2 <= elx) boardX = elx; if ( x+range/2 >= erx) boardX = erx - range; } boolean checkCollision(int x, int y) { // ボール(x,y)との当り判定を調べ // 衝突していた場合は true を返す。 // y座標が板の存在範囲外にある場合はfalse if ( y < boardY || y > boardY+thickness) return false; // x座標が板の存在範囲外にある場合はfalse if ( x < boardX || x > boardX+range) return false; return true; } // checkCollision void drawBoard(Graphics bg) { // 板描画 bg.setColor(boardColor); bg.fillRect( boardX, boardY, range, thickness); } // drawBoard() } // Board