用java模拟多球运动及碰撞

哈哈...经过上两篇文章以后,现在可以模拟出Vista七彩泡泡屏保的效果了(虽然模拟出来的效果跟屏保的效果暂时还相差很大,但原理是出来啦,而且更重要的是,我们能够模拟出来了),下面是代码.

/*
 * @(#)Ball.java 2008年2月27日
 */
import java.awt.Graphics;
import java.awt.Color;

/**
 * Ball类代表一个球
 * @author Bird
 * @version 1.0 2008年2月27日
 * @since jdk1.6.0
 */
public class Ball {
 
 private final int RADIUS = 40;     //球的半径
 private int ballX, ballY;         //球目前的位置
 private double xSpeed, ySpeed;         //球在X轴及Y轴的分速度
 private int screenWidth, screenHeight;   //屏宽,高
 
 public Ball(double xSpeed, double ySpeed, int w, int h){
  this.xSpeed = xSpeed;
  this.ySpeed = ySpeed;
  screenWidth = w;
  screenHeight = h;
 }
 /**
  * 画出当前球
  * @param g
  */
 public void paint(Graphics g){
  g.setColor(Color.RED);
  g.fillArc(ballX, ballY, RADIUS*2, RADIUS*2, 0, 360);
 }
 /**
  * 取得当前球的x坐标
  * @return x坐标
  */
 public int getBallX(){
  return ballX;
 }
 /**
  * 取得当前球的y坐标
  * @return y坐标
  */
 public int getBallY(){
  return ballY;
 }
 /**
  * 设置新的x坐标
  * @param x
  */
 public void setBallPos(int x, int y){
  ballX = x;
  ballY = y;
 }
 /**
  * 取得X轴上的分速度
  * @return
  */
 public double getXSpeed(){
  return xSpeed;
 }
 /**
  * 取得y轴上的分速度
  * @return
  */
 public double getYSpeed(){
  return ySpeed;
 }
 /**
  * 设置新的x轴分速度
  * @param xs
  */
 public void setSpeed(double xs, double ys){
  xSpeed = xs;
  ySpeed = ys;
 }
 
 /**
  * 重新设置屏幕大小
  */
 public void setScreen(int w, int h){
  screenWidth = w;
  screenHeight = h;
 }
 
 /**
  * 球运动
  */
 public void ballMove(){
  if(ballX + xSpeed + 2*RADIUS > screenWidth ||
    ballX + xSpeed < 0){         //当在X轴上碰到墙时,X轴行进方向改变
   xSpeed*=-1;
  }else{
   ballX += xSpeed;                 //没碰壁时继续前进
  }
  if(ballY + ySpeed + 2*RADIUS > screenHeight ||
    ballY + ySpeed < 0){         //当在Y轴上碰到墙时,Y轴行进方向改变
   ySpeed*=-1;
  }else{
   ballY += ySpeed;
  }
 }
 
 /**
  * 检查两球是否发生碰撞,若碰撞进行碰撞处理
  * @param ball 另一个球s
  */
 public void ballCollide(Ball ball){
  
  int bx = ball.getBallX();               //位置
  int by = ball.getBallY();
  
  double bXSpeed = ball.getXSpeed();       //速度
  double bYSpeed = ball.getYSpeed();
  
  double sx = ballX - bx;                  //两球距离
  double sy = ballY - by;
  
  int s = (int)Math.hypot(sx, sy);
  if(s > 2.*RADIUS) return;          //不发生碰撞
  
  // 求出s1
  double s1x = sx / Math.sqrt(sx*sx + sy*sy) ;
  double s1y = sy / Math.sqrt(sx*sx + sy*sy) ;
  // 求出t'
  double tx = -sy ;
  double ty = sx ;
  // 求出t1
  double t1x = tx / Math.sqrt(tx*tx + ty*ty) ;
  double t1y = ty / Math.sqrt(tx*tx + ty*ty) ;
  // 求v1a在s1上的投影v1s
  double v1s = xSpeed * s1x + ySpeed * s1y ;
  // 求v1a在t1上的投影v1t
  double v1t = xSpeed * t1x + ySpeed * t1y ;
  // 求v2a在s1上的投影v2s
  double v2s = bXSpeed * s1x + bYSpeed * s1y ;
  // 求v2a在t1上的投影v2t
  double v2t = bXSpeed * t1x + bYSpeed * t1y ;
  // 用公式求出v1sf和v2sf
  double v1sf = v2s ;
  double v2sf = v1s ;
  // 最后一步,注意这里我们简化一下,直接将v1sf,v1t和v2sf,v2t投影到x,y轴上,也就是v1'和v2'在x,y轴上的分量
  // 先将v1sf和v1t转化为向量
  double nsx = v1sf * s1x ;
  double nsy = v1sf * s1y ;
  double ntx = v1t * t1x ;
  double nty = v1t * t1y ;
  // 投影到x轴和y轴
  
  xSpeed = nsx + ntx ;
  ySpeed = nsy + nty ;
  // 然后将v2sf和v2t转化为向量
  nsx = v2sf * s1x ;
  nsy = v2sf * s1y ;
  ntx = v2t * t1x ;
  nty = v2t * t1y ;
  // 投影到x轴和y轴
 
  bXSpeed = nsx + ntx ;
  bYSpeed = nsy + nty ;
  
  while(Math.sqrt((ballX-bx)*(ballX-bx) +              //仍旧处于碰撞时的位置
    (ballY-by)*(ballY-by)) < 2*RADIUS){
   if(ballX + xSpeed + 2*RADIUS > screenWidth ||
     ballX + xSpeed < 0){         //当在X轴上碰到墙时,X轴行进方向改变
    xSpeed*=-1;
   }else{
    ballX += xSpeed;                 //没碰壁时继续前进
   }
   if(ballY + ySpeed + 2*RADIUS > screenHeight ||
     ballY + ySpeed < 0){         //当在Y轴上碰到墙时,Y轴行进方向改变
    ySpeed*=-1;
   }else{
    ballY += ySpeed;
   }
   if(bx + bXSpeed + 2*RADIUS > screenWidth ||
     bx + bXSpeed < 0){         //当在X轴上碰到墙时,X轴行进方向改变
    bXSpeed*=-1;
   }else{
    bx += bXSpeed;                 //没碰壁时继续前进
   }
   if(by + bYSpeed + 2*RADIUS > screenHeight ||
     by + bYSpeed < 0){         //当在Y轴上碰到墙时,Y轴行进方向改变
    bYSpeed*=-1;
   }else{
    by += bYSpeed;
   }
  }
  ball.setSpeed(bXSpeed, bYSpeed);   //重新设置速度及位置
  ball.setBallPos(bx, by);          
 }
}
/*
 * @(#)ManyBalls.java 2008年2月27日
 */
import java.util.Random;
import java.awt.Graphics;

/**
 * ManyBalls类表示一群球
 * @author Bird
 * @version 1.0 2008年2月27日
 * @since jdk1.6.0
 */
public class ManyBalls extends Thread {
 
 private final int BALL_NUMBER = 10;         //球的个数
 private final int RADIUS = 40;              //球的半径
 private Ball b[] = new Ball[BALL_NUMBER];
 
 private Random r;
 
 private boolean move = true;
 
 public ManyBalls(int w, int h){
  r = new Random();
  for(int i =0; i < b.length; i ++){
   b[i] = new Ball(r.nextInt(8), r.nextInt(7),w,h);
   b[i].setBallPos((2*i+2)*RADIUS, h/2);         //此处确保各个球不会发生碰撞
  }
 }
 /**
  * 将所有球画到画面上
  * @param g
  */
 public void paint(Graphics g){
  for(int i = 0; i < b.length; i++){
   b[i].paint(g);
  }
 }
 public void run(){
  while(move){
   for(int i = 0; i < b.length; i++){
    b[i].ballMove();
    for(int j = 0; j< b.length; j++){
     if(i != j){
      b[i].ballCollide(b[j]);
     }
    }
   }
   try{
    Thread.sleep(5);
   }catch(InterruptedException e){
    
   }
  }
 }
 /**
  * 重设屏幕大小
  * @param w 屏宽
  * @param h 屏高
  */
 public void screenResize(int w, int h){
  for(int i = 0; i < b.length; i++){
   b[i].setScreen(w, h);
  }
 }
 /**
  * 退出程序
  */
 public void exit(){
  move = false;
 }
}
/*
 * @(#)BallCanvas.java 2008年2月27日
 */
import java.awt.Canvas;
import java.awt.Graphics;
import java.awt.Image;

/**
 * BallCanvas类是画布类
 * @author Bird
 * @version 1.0 2008年2月27日
 * @since jdk1.6.0
 */
public class BallCanvas extends Canvas implements Runnable {
 
 private ManyBalls balls;
 private int screenWidth, screenHeight;           //屏宽及高
 private boolean running = true;                 //标志程序是否运行
 private Image image;                            //双缓冲区
 private Graphics bg;
 
 public BallCanvas(int w, int h){
  screenWidth = w;
  screenHeight =h;
  balls = new ManyBalls(w,h);
  balls.start();
 }
 /**
  * 为了避免屏幕闪动,用双缓冲
  * 此处重载update()而不是repaint(),重载repaint()仍然会使屏幕闪烁
  */
 public void update(Graphics g){
  if(image == null){
   image = this.createImage(screenWidth, screenHeight);
   bg = image.getGraphics();           //当当前组件不可视时,bg将返回null,
                                       //所以若在构造子中用这两条语句将导致程序异常
  }
  bg.setColor(this.getBackground());
  bg.fillRect(0, 0, screenWidth, screenHeight);
  balls.paint(bg);
  g.drawImage(image, 0, 0, this);
  /*g.setColor(Color.WHITE);
  g.fillRect(0, 0, screenWidth, screenHeight);
  balls.paint(g);*/
 }
 
 public void run() {
  while(running){
   repaint();
   try{
    Thread.sleep(5);
   }catch(InterruptedException e){
    
   }
  }
 }
 /**
  * 退出游戏
  */
 public void exit(){
  running =false;
  balls.exit();
 }
 /**
  * 重新设置屏幕大小
  */
 public void canvasResize(){
  screenWidth = this.getWidth();
  screenHeight =this.getHeight();
  balls.screenResize(screenWidth, screenHeight);
 }

}
/*
 * @(#)Main.java 2008年2月27日
 */
import javax.swing.JFrame;
import java.awt.Container;
import java.awt.event.WindowAdapter;
import java.awt.event.ComponentAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.ComponentEvent;

/**
 * Main类是程序的主类
 * @author Bird
 * @version 2008年2月27日
 * @since jdk1.6.0
 *
 */
public class Main extends JFrame{
 
 private int screenWidth,screenHeight;
 private BallCanvas canvas = null;
 
 public Main(){
  
  setTitle("运动的球");
  setSize(1000,700);
  Container pane = this.getContentPane();
  
  screenWidth = this.getWidth();
  screenHeight = this.getHeight();
  
  canvas = new BallCanvas(screenWidth, screenHeight);
  //canvas = new BallCanvas();
  Thread t = new Thread(canvas);
  t.start();
  //当退出时,先结束Cavans所在线程
  this.addWindowListener(new WindowAdapter(){
    public void windowClosed(WindowEvent e){
     canvas.exit();
    }
  });
  //监听窗口大小改变
  this.addComponentListener(new ComponentAdapter(){
   public void componentResized(ComponentEvent e){
    resize();
   }
  });
  pane.add(canvas);
  this.setVisible(true);
 }
 /**
  * 主函数
  */
 public void main(){
  Main m = new Main();
 }
 /**
  * 改变窗口的大小
  */
 public void resize(){
  screenWidth = this.getWidth();   //获得新的窗口大小
  screenHeight = this.getHeight();
  canvas.canvasResize();            //通知Canvas改变其大小
 }
}
 这次贴出全部代码,代码是比较多,而且在画布上画图的时候用到了双缓冲,这样可以有效地防止画面抖动,可是也正是因为双缓冲的关系导致了模拟出来的效果比较不真实.而且球的运动速度也比较慢(原因在于有大量的计算及画图工作)但速度是可以提高的,这个只能等到以后再慢慢改进了.

  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值