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