这个其实是之前看视频的时候老师教的一个项目,但是也敲了,但是只能看懂代码不能自己设计出来,并且还苦于没有找到图片素材也就没有继续下去了;
今天抽了一下时间;现在网上找一些图片然后利用之前学的那皮毛都不到的ps,p了一下图,虽然依旧没有那么好但是也够用了落;
先分析一下这个面向对象设计的代码的一些特性的展示;以及自己学会了什么;
1;利用建图片工具包,给这段代码带来很好的移值性,
2;充分利用继承,很好的减少了重复编写相同代码,并且很好的联系;
3;很好的扩展性,可以扩展到银河系,因为将太阳只是作为Star的一个对象;因此还可以创造其他对象来建立其他系别;plant可以即包括行星也包括卫星,因为设置了围绕旋转的星球这个可以改变的;
4;构造方法的创建;
5;方法的创建,使代码可读性更高;
先看一下运行结果,因为是动态的,我只截了3张图来表示;
再看一下分析思路吧,设计也差不多,只是要先宏观概括更好
关于其设计;
其程序的运行步骤就是;
先创建对象,然后画出;
这里分开,背景可直接画出,
太阳在Star类中draw画出;
行星在Plant类画出,并且因为存在继承关系并且它还有画出其椭圆轨迹以及移动,
最后在showFrame方法中不断开启线程到达图片移动的效果;
因为线程的不断开启就引入了双缓冲;
至于各自的构造方法就看自己了;
第一部分;在gameUtil包中;
1;Constant类,常量类,设置了窗口的长宽,方便后期修改,提高程序的健壮性和可
扩展性;
package gameUtil;
/**
* 设置常量类,提高程序的健壮性;
* @author Administrator
*
*/
public class Constant {
/**
* 将窗口的长宽设置为常量,已供其他类使用,也便于修改;
*/
public static final int WIGHT = 788;//窗口的宽度
public static final int HIGTH = 610;//窗口的高度
}
2;GamepictureUtil类;图片加载类;得到图片路径将该图片变成image对象返回;
package gameUtil;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import javax.imageio.ImageIO;
/**
* 这个有点点其他知识;
* 通过这个类的static方法实现通过输入图片路径(scr中的路径)返回图片对象;
* @author Administrator
*
*/
public class GamePictureUtil {
/**
* 设置为私有构成方法,不允许对象创建;
*/
private GamePictureUtil() {
}
/**
* 接收图片路径返回图片对象;
* @param path
* @return
*/
public static Image getImages(String path){
URL u = GamePictureUtil.class.getClassLoader
().getResource(path);
BufferedImage img = null;
try {
img = ImageIO.read(u);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return img;
}
}
后期的使用就是例如
Image bg = GamePictureUtil.getImages(“路径(scr中的路径)”);
就将这个路径下的地址通过这个静态方法将图片变成Image对象返回;bg为这个对象的
引用;
3;MyFrame类;继承java.awt.Frame类;这个类就实现了窗口中一些基本的设置;
例如;设置窗口的大小,位置,是否可见,以及窗口是否可以关闭,这里因为后面要加
载图片因此还需要一个内部类来启动线程,不断重画窗口,来到达窗口是不消失的,因
为在不断重画;
package gameUtil;
import java.awt.Frame;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
/*
* 显示窗口;
*/
public class MyFrame extends Frame{
/**
* 显示出现窗口
* 线程实现,动态图片。
*/
public void showFrame(){
setSize(Constant.WIGHT,Constant.HIGTH);//设置窗口大小,以
Eclipse的左上角为原点、
setLocation(100, 100);//设置坐标,这里的坐标也是左上角
setVisible(true);//设置窗口可见,默认是不可见的
new PaintThread().start();//启动线程,反复调用paint。
addWindowListener(new WindowAdapter() {//实现窗口关闭内部
类
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
}
/**
* 定义一个重画窗口的内部类(线程实现);了解即可,懂得调用即可。
* @author Administrator
*
*/
class PaintThread extends Thread{//通过线程,内部类
public void run(){
while(true){
try {
repaint();
Thread.sleep(30);//1s等于100s,一
秒休息25次;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
4;为什么要创建一个gameUtil包,并且在里面添加这些类呢;
可移值性;因为这个包里面的类,基本上是实现游戏窗口时都需要的类因此单独成一个
包,下次则直接将这个包拷贝过去,就完成了。
第二部分;就要实现模拟太阳系的具体类了;
可分为;太阳和行星(卫星);
当然我们也是需要一个窗口类,类实现操作的。
现在分析;其实面向对象和面向过程一样,都是从main函数开始分析的;
在SolarFarame中main方法中创建了一个SolarFarame对象的时候,则此时就进行了画
图得一系列工作,再调用Frame中的showFrame方法,完成了窗口的显示,注意这里还不
断重启线程,这里就可以完成行星的移动了,只需改变其坐标即可,下次线程他就会自
动再次画出即显示在其他地方,具体的流程就是这样的;
至于Frame中的showFrame方法中,是比较简单的,就是完成一些最后窗口的显示;
细说一下创建对象时太阳系模型的显示吧?
关于其显示就是
先显示背景,就是先通过路径得到对象,引用指向之后再画出即可;
太阳,行星也是一样的,不同的是他们不同背景,他们创建了单独的类来通过构成方法
来设置参数,最后还是画出来;
大概的流程就是这样,至于细节就十分重要了,这也是体现面向对象多种好处的时候;
1;因为无论是背景还是太阳,还是行星我们都要将其画出显示在窗口上,因此我们可
以在SolarFarame类中创建一个专门显示图片的方法;paint
public void paint(Graphics g){
g.drawImage(bg, 0, 0, null);
sun.draw(g);
earth.draw(g);
}
因为太阳行星不同于背景,创建了独立的类,因此太阳和行星就调用自己的类来显示图
片,(还有一个原因是,他们要设置的参数都不同,因此单独成类创建方法画出也是很
好的;)
进一步分析显示太阳行星;
这里又有注意了,太阳和行星这间也存在相同的地方;
例如都是图片需要显示,只是行星多了需要画出运动轨迹,需要移动;
因此可以得出,行星可以继承太阳;
因此单独的对于画出图片,只需要太阳类中创建显示的方法即可;
Image img;
double x,y;//坐标
double width,higth;
public void draw(Graphics g){
g.drawImage(img, (int)x, (int)y, null);
}
子类行星也可以这样画出,我们也看下行星是怎么画出的吧;
public void draw(Graphics g){
super.draw(g);
drawTrace(g);
move();
}
关于画出,它重写了父类的draw方法,因为行星还需要画出其运动轨迹,并且他还是移
动的,因此要不断在不同的位置显示,因此他需要重写;
接下来我们按着思路看他轨迹画出吧;椭圆的画出;
在awt中都提供了这些方法;
public abstract void drawOval(int x, int y, int width, int height);
我们所需要做的是,计算出他这些参数即可,坐标xy已经长轴短轴
public void drawTrace(Graphics g){
double ovalX,ovalY,ovalWidth,ovalHeight;
ovalWidth = longAxis*2;
ovalHeight = shortAxis*2;
ovalX = (center.x+center.width/2)-longAxis;
ovalY = (center.y+center.higth/2)-shortAxis;
Color c = g.getColor();
g.setColor(Color.blue);
g.drawOval((int)ovalX, (int)ovalY, (int)ovalWidth, (int)
ovalHeight);
g.setColor(c);
}
中间那个是设置颜色,注意设置颜色前要保存原颜色,之后要还原回去;注意;防止画
笔颜色出现混乱;
我们再看他的移动吧;按照其运动轨迹移动就ok了;关于行星的移动,前面也说了一点
,我们只需改变其图片的xy坐标即可,在下个线程的时候,再次画出的时候就可以表示
成移动了;
public void move(){
x = center.x+center.width/2+longAxis*Math.cos(degree)-
img.getWidth(null)/2;
y = center.y+center.higth/2+shortAxis*Math.sin(degree)-
img.getHeight(null)/2;
degree += speed;//不需要再画出,在showFrame不断重启线程就可
以完成改变;
}
关于太阳系模型的基本思路就几乎完成了,几乎,我们还缺少分享构造方法了。并且在
这里我们构造方法的设置应该是几乎接近源码那样吧?这个不多说自己看代码吧;
按照这个思路下来我们运行的结果应该是闪的,我们还少了一个东西,直接百度图片加
载时的双缓冲即可完成;因为都需要,这放到SolarFrame类即可;
Image ImageBuffer = null;
Graphics GraImage = null;
public void update(Graphics g){ //覆盖update方法,截取默认的调
用过程
ImageBuffer = createImage(this.getWidth(), this.getHeight());
//创建图形缓冲区
GraImage = ImageBuffer.getGraphics(); //获取图形缓冲区的
图形 上下文
paint(GraImage); //用paint方法中编写的绘图过程对图形缓冲
区绘图
GraImage.dispose(); //释放图形上下文资源
g.drawImage(ImageBuffer, 0, 0, this); //将图形缓冲区绘制到屏
幕上
}
贴一下所有代码吧;
package gameUtil;
/**
* 设置常量类,提高程序的健壮性;
* @author Administrator
*
*/
public class Constant {
/**
* 将窗口的长宽设置为常量,已供其他类使用,也便于修改;
*/
public static final int WIGHT = 788;//窗口的宽度
public static final int HIGTH = 610;//窗口的高度
}
package gameUtil;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import javax.imageio.ImageIO;
/**
* 这个有点点其他知识;
* 通过这个类的static方法实现通过输入图片路径(scr中的路径)返回图片对象;
* @author Administrator
*
*/
public class GamePictureUtil {
/**
* 设置为私有构成方法,不允许对象创建;
*/
private GamePictureUtil() {
}
/**
* 接收图片路径返回图片对象;
* @param path
* @return
*/
public static Image getImages(String path){
URL u = GamePictureUtil.class.getClassLoader().getResource(path);
BufferedImage img = null;
try {
img = ImageIO.read(u);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return img;
}
}
package gameUtil;
import java.awt.Frame;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
/*
* 显示窗口;
*/
public class MyFrame extends Frame{
/**
* 显示出现窗口
* 线程实现,动态图片。
*/
public void showFrame(){
setSize(Constant.WIGHT,Constant.HIGTH);//设置窗口大小,以Eclipse的左上角为原点、
setLocation(100, 100);//设置坐标,这里的坐标也是左上角
setVisible(true);//设置窗口可见,默认是不可见的
new PaintThread().start();//启动线程,反复调用paint。
addWindowListener(new WindowAdapter() {//实现窗口关闭内部类
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
}
/**
* 定义一个重画窗口的内部类(线程实现);了解即可,懂得调用即可。
* @author Administrator
*
*/
class PaintThread extends Thread{//通过线程,内部类
public void run(){
while(true){
try {
repaint();
Thread.sleep(30);//1s等于100s,一秒休息25次;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
package solarFrame;
import java.awt.Graphics;
import java.awt.Image;
import gameUtil.Constant;
import gameUtil.GamePictureUtil;
import gameUtil.MyFrame;
public class SolarFrame extends MyFrame{
Image bg = GamePictureUtil.getImages("img/background1.png");
Star sun = new Star("img/sun.jpg",Constant.WIGHT/2,Constant.HIGTH/2);
Plant earth = new Plant("img/earth.jpg",300,200,0.01,sun);
Plant mercury = new Plant("img/mercury.jpg",100,50,0.1,sun);
Plant mras = new Plant("img/mras.jpg",150,100,0.05,sun);
Plant moon = new Plant("img/moon.jpg",50,50,0.05,earth);
Image ImageBuffer = null;
Graphics GraImage = null;
public void update(Graphics g){ //覆盖update方法,截取默认的调用过程
ImageBuffer = createImage(this.getWidth(), this.getHeight()); //创建图形缓冲区
GraImage = ImageBuffer.getGraphics(); //获取图形缓冲区的图形 上下文
paint(GraImage); //用paint方法中编写的绘图过程对图形缓冲区绘图
GraImage.dispose(); //释放图形上下文资源
g.drawImage(ImageBuffer, 0, 0, this); //将图形缓冲区绘制到屏幕上
}
public void paint(Graphics g){
g.drawImage(bg, 0, 0, null);
sun.draw(g);
earth.draw(g);
mercury.draw(g);
mras.draw(g);
moon.draw(g);
}
public static void main(String[] args) {
new SolarFrame().showFrame();
}
}
package solarFrame;
import java.awt.Graphics;
import java.awt.Image;
import gameUtil.GamePictureUtil;
public class Star {
Image img;
double x,y;//坐标
double width,higth;
public void draw(Graphics g){
g.drawImage(img, (int)x, (int)y, null);
}
Star(){
}
Star(Image img){
this.img = img;
this.width = img.getWidth(null);
this.higth = img.getHeight(null);
}
Star(Image img, double x, double y){
this(img);
this.x = x;
this.y = y;
}
Star(String imgpath, double x, double y){
this(GamePictureUtil.getImages(imgpath), x, y);
}
}
package solarFrame;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.util.Arrays;
import gameUtil.GamePictureUtil;
public class Plant extends Star{
double longAxis;
double shortAxis;
double degree;
double speed;
Star center;
public void draw(Graphics g){
super.draw(g);
drawTrace(g);
move();
}
public void drawTrace(Graphics g){
double ovalX,ovalY,ovalWidth,ovalHeight;
ovalWidth = longAxis*2;
ovalHeight = shortAxis*2;
ovalX = (center.x+center.width/2)-longAxis;
ovalY = (center.y+center.higth/2)-shortAxis;
Color c = g.getColor();
g.setColor(Color.blue);
g.drawOval((int)ovalX, (int)ovalY, (int)ovalWidth, (int)ovalHeight);
g.setColor(c);
}
public void move(){
x = center.x+center.width/2+longAxis*Math.cos(degree)-img.getWidth(null)/2;
y = center.y+center.higth/2+shortAxis*Math.sin(degree)-img.getHeight(null)/2;
degree += speed;//不需要再画出,在showFrame不断重启线程就可以完成改变;
}
public Plant(String imgpath, double longAxis, double shortAxis,double speed,
Star center) {
super(GamePictureUtil.getImages(imgpath));
this.center = center;
this.x = center.x+longAxis;
this.y = center.y+shortAxis;
this.img = GamePictureUtil.getImages(imgpath);
this.longAxis = longAxis;
this.shortAxis = shortAxis;
this.speed = speed;
}
Plant(Image img, double x, double y) {
super(img, x, y);
}
Plant(String imgpath, double x, double y){
super(imgpath,x,y);
}
}