ブロック崩し Step2-1 ボールを描画し アニメーションで動かす ( 壁やパドルで反射するようにする ) < アニメーションの復習 > アニメーションは アプリケーションが指定する間 一定間隔でどんどん画像をおきかえていくもの Swing では Timer によって一定間隔でイベントを発生させ イベント処理をするメソッド ( 関数 ) に画像を描画しなおす処理を記述すると アニメーションになる 発生するイベントは ActionEvent なので ActionListener(actionPerformed メソッドをもったクラス ) が必要になる Timer をつくると actionperformed メソッドが一定間隔で何度も呼ばれる < ボールの表現方法 > ボールには 円を表す Circle というクラスを Rectangle の思想をまねて自作する Circle クラスは 円に外接する矩形の左上座標と半径をもっている (x,y) radius < 作成手順 > 1. 円を表す Circle クラスの作成 animation パッケージを選択 右クリック 新規 クラス クラス名 Circle Circle.java を参考にソースを記述 2. の編集 (4 ページ以降を参照 ) 3.BlockBreakerFrame.java の編集 ( ボタンが押された時の動作を記述 ) startbutton.addactionlistener(new ActionListener() { public void actionperformed(actionevent evt) { centerpanel.start(); ); ( 中略 ) stopbutton.addactionlistener(new ActionListener() { public void actionperformed(actionevent evt) { centerpanel.stop(); ); 4. 実行して以下を確認 START ボタンでボールが動き 反射すること STOP ボタンでゲームを中断できること -1-
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 Circle.java package animation; import java.awt.point; * 円を表すクラス <br> * Rectangleをまねて左上の点と半径をもつ設計にする public class Circle { public int x; // 円の左上のx 座標 public int y; // 円の左上のy 座標 public int radius; // 円の半径 private Point centerpoint; // 円の中心座標 private Point toppoint, bottompoint, leftpoint, rightpoint; // 上下左右の点 ( 計算で求める ) * コンストラクタ * @param radius public Circle(int radius) { this.radius = radius; centerpoint = new Point(); toppoint = new Point(); bottompoint = new Point(); leftpoint = new Point(); rightpoint = new Point(); * pointがこの円内に存在するか * @param point * @return public boolean contains(point point) { centerpoint.setlocation(x+radius, y+radius); return radius*radius < (centerpoint.x-point.x)*(centerpoint.x-point.x) + (centerpoint.y-point.y)*(centerpoint.y-point.y); * 円の左上座標を設定 * @param x * @param y public void setlocation(int x, int y) { this.x = x; this.y = y; * 円の左上座標を設定 -2-
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 Circle.java * @param p public void setlocation(point p) { x = p.x; y = p.y; * 円の左上座標を返す * @return public Point getlocation() { return new Point(x, y); /* getter***************************************** public Point getcenterpoint() { centerpoint.setlocation(x+radius, y+radius); return centerpoint; public Point gettoppoint() { toppoint.setlocation(x+radius, y); return toppoint; public Point getbottompoint() { bottompoint.setlocation(x+radius, y+2*radius); return buttompoint; public Point getleftpoint() { leftpoint.setlocation(x, y+radius); return leftpoint; public Point getrightpoint() { rightpoint.setlocation(x+2*radius, y+radius); return rightpoint; -3-
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 package animation; import java.awt.color; import java.awt.gradientpaint; import java.awt.graphics; import java.awt.graphics2d; import java.awt.point; import java.awt.rectangle; import java.awt.event.actionevent; import java.awt.event.actionlistener; import java.awt.event.keyevent; import java.awt.event.keylistener; import javax.swing.joptionpane; import javax.swing.jpanel; import javax.swing.timer; public class BlockBreakerPanel extends JPanel implements KeyListener, ActionListener{ private Rectangle paddle; // パドル private int pwidth = 50; // パドルの幅 private int pheight = 10; // パドルの高さ private Circle ball; // ボール private int radius = 5; // ボールの半径 private int initd[] = {3, 3; // ボールの移動量の初期値 private int dx=initd[0], dy=initd[1]; // ボールの移動量 private Point pstartpoint, bstartpoint; // パドル ボールの初期位置 private Timer timer; * コンストラクタ public BlockBreakerPanel() { // ボールを表す円を生成 ball = new Circle(radius); // タイマー生成 timer = new Timer(15, this); addkeylistener(this); * ゲームをスタートさせる public void start() { // タイマースタート timer.start(); // フォーカスを要求 (Keyイベントを有効にするため) if (!isfocusowner()) requestfocus(); * ゲームを中断させる public void stop() { // タイマーストップ timer.stop(); // フォーカスを要求 (Keyイベントを有効にするため) if (!isfocusowner()) requestfocus(); -4-
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 * 描画メソッド public void paintcomponent(graphics g){ // ボタンなど配置していなければいらないが 忘れずに書く習慣にする super.paintcomponent(g); // より高度な描画を可能にするようキャスト Graphics2D g2d = (Graphics2D)g; // 各描画対象の初期位置を計算して設定 /* このパネルがフレームに貼り付けられないとサイズが確定しないので * コンストラクタの中ではgetWidth() などに正しい値がはいらない * そのため ここでパドルの初期化と初期位置の決定を行う if (paddle == null) { // パドルをRectangleオブジェクトとして使う paddle = new Rectangle(pWidth, pheight); // パドルの初期位置はx 方向は中心 y 方向は下から20あけたところ paddle.setlocation( x 座標, y 座標 ); // ボールの初期位置 ball.setlocation(10, getheight()/2); // パドル ボールの初期位置を覚えておく pstartpoint = paddle.getlocation(); bstartpoint = ball.getlocation(); /* 白色背景 --------------------------------------- // 白をセット g2d.setcolor(color.white); // 四角で塗りつぶす fillrect( 左上のx 座標, 左上のy 座標, 幅, 高さ ) g2d.fillrect(0, 0, getwidth(), getheight()); /* パドル描画 ------------------------------------- // グラデーションパターンを指定する // new GradientPaint( 点 1のx 座標, 点 1のy 座標, 色 1, 点 2のx 座標, 点 2のy 座標, 色 2, 循環するか ) GradientPaint gp = new GradientPaint(paddle.x, paddle.y, Color.BLUE, paddle.x, paddle.y+paddle.height/2, Color.WHITE, true); g2d.setpaint(gp); // パドルを描画 g2d.fill(paddle); /* ボール描画 ------------------------------------- // グラデーションパターンを指定する ( ライトグレー ダークグレー ) gp = new GradientPaint(ball.x, ball.y, Color.LIGHT_GRAY, ball.x+ball.radius*2, ball.y+ball.radius*2, Color.DARK_GRAY, true); g2d.setpaint(gp); // 塗りつぶしで円描画 /* filloval( 左上の点のx 座標, y 座標, 幅, 高さ ) g2d.filloval(ball.x, ball.y, 2*radius, 2*radius); // 円のエッジをダークグレイにする g2d.setcolor(color.dark_gray); g2d.drawoval(ball.x, ball.y, 2*radius-1, 2*radius-1); * キーを押すと呼ばれる <br> * 矢印キーでパドルが左右に動く public void keypressed(keyevent e) { int pdx = 10; // 1 回のキー操作でずらす量 -5-
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 // 押されたキーのコードを取得 int keycode = e.getkeycode(); // キーコードで分岐 switch (keycode) { // 左矢印キーだったら case KeyEvent.VK_LEFT: // パドルが左に10ずれる ( 壁にめりこまない ) // Rectangleクラスの位置指定メソッド setlocation( 左上の点のx 座標, y 座標 ) if (paddle.x < pdx) paddle.setlocation(0, paddle.y); else paddle.setlocation(paddle.x-pdx, paddle.y); // 再描画 repaint(); break; // 右矢印キーだったら case KeyEvent.VK_RIGHT: // パドルが右に10ずれる ( 壁にめりこまない if (getwidth()-paddle.x-paddle.width < pdx) paddle.setlocation(getwidth()-paddle.width, paddle.y); else paddle.setlocation(paddle.x+pdx, paddle.y); // 再描画 repaint(); default: break; * タイマーによって一定間隔で呼ばれる public void actionperformed(actionevent e) { // ボールの位置決定 ball.setlocation(ball.x+dx, ball.y+dy); // 壁にぶつかったら反射 // 左右の壁ならx 方向の速度をかえる if (ball.getleftpoint().x <= 0 ) { ball.setlocation(0, ball.y); // 壁にめり込まない dx = -dx; else if (ball.getrightpoint().x >= getwidth()) { ball.setlocation(getwidth()-2*ball.radius, ball.y); // 壁にめり込まない dx = -dx; // 上の壁ならy 方向の速度をかえる if (ball.gettoppoint().y <= 0) { ball.setlocation(ball.x, 0); // 壁にめり込まない dy = -dy; // パドルにあたったら反射 if (paddle.contains(ball.getbottompoint())) { ball.setlocation(ball.x, paddle.y-2*ball.radius); dy = -dy; // 落ちたらゲーム終了 メッセージ表示 else if (ball.gettoppoint().y >= getheight()) { terminate("game OVER", "\tゲーム終了です "); // 再描画 repaint(); -6-
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 * ゲームを終了する * @param title メッセージボックスのタイトル * @param message private void terminate(string title, String message) { // タイマーを止める stop(); // メッセージ表示 JOptionPane.showMessageDialog(getParent(), message, title, JOptionPane.PLAIN_MESSAGE); // 初期状態に戻す paddle.setlocation(pstartpoint); ball.setlocation(bstartpoint); dx = initd[0]; dy = initd[1]; /* 以下の2つのメソッドはKeyListenerを * implementsしたため必要だが 空でよい public void keyreleased(keyevent e) { public void keytyped(keyevent e) { -7-