哈哈...經過上兩篇文章以後,現在可以模擬出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改變其大小
}
}
這次貼出全部代碼,代碼是比較多,而且在畫布上畫圖的時候用到了雙緩衝,這樣可以有效地防止畫面抖動,可是也正是因爲雙緩衝的關係導致了模擬出來的效果比較不真實.而且球的運動速度也比較慢(原因在於有大量的計算及畫圖工作)但速度是可以提高的,這個只能等到以後再慢慢改進了.