001:// 跳ね返るボール
002:import java.awt.*; // AWT
003:import java.awt.event.*; // AWT Event
004:import javax.swing.*; // JPanel, JFrame
005:import java.applet.AudioClip; // Audio Clip
006:import java.applet.Applet; // Audio Clip play
007:import java.net.*; // URL
008:import java.io.*;
009:import java.security.*; // AccessControlException
010:
011:class Ballx {
012: public int x, y; // ボールの位置
013: public int d; // ボールの直径
014: public int cnx, cny; // ボールが1ドット動くまでのコマ数
015: public int cdx, cdy; // ボールの移動方向
016: public Color c; // ボールの色
017: public int cx, cy; // ボールの移動タイマ
018:
019: Ballx(int xx, int yy, int dd, int dxx, int dyy, Color cc) {
020: x = xx;
021: y = yy;
022: d = dd;
023: if(dxx>0) { cnx = cx = dxx;
024: cdx = 1; }
025: else { cnx = cx = -dxx;
026: cdx = -1; }
027: if(dyy>0) { cny = cy = dyy;
028: cdy = 1; }
029: else { cny = cy = -dyy;
030: cdy = -1; }
031: c = cc;
032: }
033:
034: void move(int w1, int w2, int h1, int h2){
035: if(--cnx < 0) { // x 方向に移動する時間になった
036: cnx = cx;
037: x += cdx;
038: }
039: if(--cny < 0) { // y 方向に移動する時間になった
040: cny = cy;
041: y += cdy;
042: }
043: if(cdx>0 && x+d > w2) cdx = -cdx; // 右の縁にぶつかった
044: if(cdx<0 && x < w1) cdx = -cdx; // 左
045: if(cdy>0 && y+d > h2) cdy = -cdy; // 下の縁にぶつかった
046: if(cdy<0 && y < h1) cdy = -cdy; // 上
047: } // method move
048:} // class Ballx
049:
050:public class Ballq extends JPanel implements Runnable {
051: final static int psize=300; // パネルの大きさ
052: final static int bwid = 10; // パネルの縁の厚さ
053: final static Color[] cs = { Color.red, Color.blue, Color.yellow, Color.green,
054: Color.lightGray,
055: Color.cyan, Color.orange, Color.magenta, Color.pink,
056: Color.gray };
057: Thread th; // スレッド
058: int w, h; // パネルの幅と高さ
059: static int num; // ボールの個数
060: Ballx[] b = new Ballx[30]; // ボール
061: static int tws; // ボールを最初に動かす方向
062: static int bsize; // ボールの大きさ
063: static URL url; // ボールの衝突音のファイル
064: static AudioClip clip; // ボールの衝突音のクリップ
065:
066: public Ballq(){
067: setBackground(Color.white); // パネルの背景色
068: setBorder(BorderFactory.createLineBorder(Color.lightGray, bwid));
069: setMinimumSize(new Dimension(psize, psize)); // パネルの最小サイズ
070: setPreferredSize(new Dimension(psize, psize)); // パネルの希望サイズ
071: w=psize; // パネルの幅
072: h=psize; // パネルの高さ
073:
074: for(int j=0; j<num; j++) { // ボールの生成
075: b[j] = new Ballx(w/2, h/2, bsize, j, (j-tws) % num, cs[j % cs.length]);
076: }
077:
078: try{ // カレントディレクトリの音声ファイル
079: String filepath = System.getProperty("user.dir") + "/Ding.wav";
080: File file = new File( filepath );
081: if(file.exists()) {
082: url = new URL("file:" + filepath);
083: clip = Applet.newAudioClip(url); // 音声クリップ;
084: }
085: else {
086: clip = null;
087: System.out.println("Ding.wav not found.");
088: }
089: System.out.println( "URL " + url );
090: } catch( MalformedURLException e) {
091: System.out.println("Malformed URL Exception.");
092: }
093: catch( AccessControlException e) {
094: System.out.println("Access Control Exception.");
095: }
096: }
097:
098: public void paintComponent(Graphics g){
099: int dt, dtx, dty, dtt; // ボール間の距離の計算用
100: int di, dj, t; // 衝突したボール
101: boolean ding; //
102: super.paintComponent(g); // JPanel 自体の描画
103: for(int i=0; i<num-1; i++) { // 片側のボール
104: dt = b[i].d; // 2つのボールの半径の和
105: for(int j=i+1; j<num; j++) { // 他方のボール
106: dtx = b[i].x - b[j].x; // x 方向の距離
107: dty = b[i].y - b[j].y; // y 方向の距離
108: dtt = dtx * dtx + dty * dty; //
109: ding = false;
110: if( dtt < dt*dt ) { // 衝突したか、または衝突直後
111: if(b[i].x < b[j].x) { di = i; dj = j; } // 左側をdi、右側をdj
112: else { di = j; dj = i; }
113:
114: if( ( b[di].cdx > 0 && ( b[dj].cdx < 0 || b[dj].cx > b[di].cx ) ) ||
115: ( b[di].cdx < 0 && b[dj].cdx < 0 && b[dj].cx < b[di].cx)
116: ) {
117: t = b[di].cdx; // x 方向の移動方向を交換
118: b[di].cdx = b[dj].cdx;
119: b[dj].cdx = t;
120: t = b[di].cx; // x 方向の移動間隔を交換
121: b[di].cx = b[dj].cx;
122: b[dj].cx = t;
123: b[di].cnx = 1; // 次サイクルで必ず移動
124: b[dj].cnx = 1; //
125: if(clip!=null) ding = true; // 衝突音が必要
126: }
127: if(b[i].y < b[j].y) { di = i; dj = j; } // 上側のdi、下側をdj
128: else { di = j; dj = i; }
129: if( ( b[di].cdy > 0 && ( b[dj].cdy < 0 || b[dj].cy > b[di].cy ) ) ||
130: ( b[di].cdy < 0 && b[dj].cdy < 0 && b[dj].cy < b[di].cy )
131: ) {
132: t = b[di].cdy; // y 方向の移動方向を交換
133: b[di].cdy = b[dj].cdy;
134: b[dj].cdy = t;
135: t = b[di].cy; // y 方向の移動間隔を交換
136: b[di].cy = b[dj].cy;
137: b[dj].cy = t;
138: b[di].cny = 1; // 次サイクルで必ず移動
139: b[dj].cny = 1;
140: if(clip!=null) ding = true; // 衝突音が必要
141: }
142: if(ding) { clip.play(); // 衝突音をならす
143:// System.out.println("i: " + i + " j: " + j);
144: }
145: break;
146: }
147: }
148: }
149: for(int i=0; i<num; i++) { // ボールの描画
150: b[i].move(bwid, w-bwid, bwid, h-bwid);
151: g.setColor(b[i].c);
152: g.fillOval(b[i].x, b[i].y, b[i].d, b[i].d);
153: }
154: } // msthod paintComponent
155:
156: public void run(){ // スレッドの実行
157: while(true){ // 永久ループ
158: try{
159: Thread.sleep(2); // 表示間隔
160: }catch(InterruptedException e){
161: e.printStackTrace();
162: }
163: repaint(); // 描画
164: }
165: } // method run
166:
167: public static void main(String args[]){
168: if(args.length < 1) num = 5; // ボールの個数
169: else num = Integer.parseInt(args[0]);
170: if(num>29) num = 29;
171: if(args.length < 2) tws = 3; // ボールの最初の移動方向
172: else tws = Integer.parseInt(args[1]);
173: if(tws>num) tws = 3;
174: if(args.length < 3) bsize = 20; // ボールの大きさ
175: else bsize = Integer.parseInt(args[2]);
176: if(bsize>30) bsize = 30;
177: JFrame f = new JFrame("Ball Sample");
178: f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // ウィンドウのクローズ方法の登録
179: Ballq pan = new Ballq();
180: Thread th = new Thread(pan); // スレッドの生成
181: th.start(); // スレッドの実行開始
182: f.getContentPane().add(pan, BorderLayout.CENTER);
183: f.pack();
184: f.setVisible(true);
185: } // method main
186:
187:} // class Ballq