java听课记笔记,听完面向对象之后这个课开始了一个小游戏的教学,用到了基本前面的面向对象的所有知识,结果也比较有意思,因此分享一下。
AWT和Swing是java中常见的GUI技术,非常简单,但是实际开发中很少用到(实际开发中用的是JavaEE,立面涉及的UI都是网页),在整个小游戏里只是用他们来做图形窗口。
一、创建窗口:
import javax.swing.JFrame;
public class myGameFrame extends JFrame {
//继承了一个JFrame类,用于加载窗口
//初始化窗口
public void launchFrame() {
this.setTitle("john's ariplane");
this.setVisible(true);//让窗口可见
}
public static void main(String[] args) {
myGameFrame f=new myGameFrame();
f.launchFrame();
}
}
可以看到他已经出现了,不过很小,因此需要设置窗口的大小:
在launchFrame方法里调用父类JFrame的方法,加入代码:
this.setSize(500,500);//设置窗口的宽度和高度
this.setLocation(300,300);//设置位置的坐标定位,这里的坐标300,300定了窗口的左上角的位置,然后从这个点发散出高度和宽度,区别是这个y轴是向下的
可以看到的是,在关闭窗口之后,其实程序的运行并没有结束:
所以代码中还要加入一个控制,匿名内部类(这里没有详细解释),重写父类的一个windowClosing方法,这样就有了关闭动作让程序结束运行:
this.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(java.awt.event.WindowEvent e) {
System.exit(0);
}
});
再次运行:
点了错号之后也会结束程序运行。
二、加入图形和文本绘制:
要在窗口中画图、写字以及显示一些别的内容,我们需要在类里加入**paint(Graphics g)**方法,
@Override
//paint是画出整个窗口及内部内容,它是被系统自动调用的,我们不需要去调用
public void paint(Graphics g) {
// TODO Auto-generated method stub
super.paint(g);
}
g变量相当于一个画笔,在窗口里显示的东西都是通过画笔画上去的,这里例如简单的drawLine方法就可以显示一条线:
同样也可以用Graphics里面的其他方法画其他的数学图形或者字符串等等等等,进去。
Graphics里面还有改动颜色,大小的方法。Color类里有很多静态变量可以直接去使用来改变颜色:
g.setColor(Color.BLUE);//改成蓝色
但是要注意画笔,g本身是一个对象,本来就是一个颜色,在改动g的时候会彻底改变他,因此我们的习惯是在paint方法里,最开始和方法最后分别加上获取他初始颜色和最后改回去颜色的语句,这个习惯对于字体也是一样:
Font f=g.getFont();//开始获取g的字体
Color c=g.getColor();//开始先获取g画笔的初始颜色
//中间操作
g.setFont(f);
g.setColor(c);//最后返回初值
实现图片加载可以用ImageIO类,并且图片加载的方法封装到了GameUtil工具类里,可以直接调用(这个GameUtil类的代码完全可以直接拷贝,目前不用考虑)。
public class GameUtil {
// 工具类最好将构造器私有化。
private GameUtil() {
}
/**
* 返回指定路径文件的图片对象
*/
public static Image getImage(String path) {
BufferedImage bi = null;
try {
URL u = GameUtil.class.getClassLoader().getResource(path);
bi = ImageIO.read(u);
} catch (IOException e) {
e.printStackTrace();
}
return bi;
}
}
首先把需要用到的图片拷贝到项目的src下,可以建立一个专门存放所有图片的文件夹:
然后在窗口类的最开始就进行加载:
Image test=GameUtil.getImage("images/test.gif");//注意一定要写好图片的路径
这时候我们就可以在窗口方法里面加入画入这个图片的语句:
g.drawImage(test, 200, 200,null);//最后一项参数还是null,目前不涉及
可以看到结果:
三、利用多线程和内部类实现动画
前面的实现到目前为止窗口依然是静态的,我们可以结合多线程实现动画效果。
首先我们还是先导入真个页窗的背景和前面要动的飞机的照片:
Image plane=GameUtil.getImage("images/plane.png");
Image bg=GameUtil.getImage("images/bg.jpg");
然后把这两个图片在paint方法里面画进去:
g.drawImage(bg, 0, 0, null);
g.drawImage(plane, 300, 300, null);
可以看到已经有了,但是依然是静态的图片:
我们接下来,定义一个内部类(注意这个类是在我们的大类myGameFrame类里面的),继承线程Thread类,然后重写里面的run方法,让在一定的时间间隔里面重画,这样的话飞机的位置就可以动;(具体的内部类,以及捕获异常这里是eclipse会自动提示我们加上,先不做过深了解)
class PaintThread extends Thread {
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
while(true) {
repaint();//重画
try {
Thread.sleep(40);//1s=1000ms加上时间间隔
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}//
}
}
}
然后在初始化窗口的方法里调用这个方法,直接加入:
new PaintThread().start();
现在还没有具体的动画,我们可以在重画前面加入一个sout语句检测一下是否执行了线程:
现在希望在背景前面的我们的飞机图像动起来,那么就是要操作他的下标,我们在前面定义两个变量是飞机位置的初始值;
int x=300,y=300;
然后在画飞机的drawImage方法里把坐标数字改成x和y;
g.drawImage(plane, x, y, null);
这样只要加一个x++语句,再次执行的时候坐标变化,飞机就会开始动了;
四、GameObject类的设计
从小游戏里的飞机,以及障碍物的行动轨迹,物体的高度宽度,所有的物体在页窗里有共同的一些属性和行为,因此我们需要设计一个父类,简化整个项目代码的书写。
public class GameObject {
Image img;
double x,y;
int speed;
int width,height;
public void drawSelf(Graphics g) {
g.drawImage(img, (int)x, (int)y, null);
}
public GameObject(Image img, double x, double y) {
super();
this.img = img;
this.x = x;
this.y = y;
}
public GameObject() {
//再加入一个无参构造器
}
}
接下来我们一个一个写具体的物体类,首先是飞机类:
对于飞机类,由于飞机需要自己动,那么drawImage那个方法需要重写,加入动作:
@Override
public void drawSelf(Graphics g) {
// TODO Auto-generated method stub
g.drawImage(img, (int)x, (int)y, null);
x++;
}
再加入飞机类自己的构造器:
public Plane(Image img,double x,double y) {
this.img=img;
this.x=x;
this.y=y;
}
然后我们在myGameFrame类里就可以重新针对飞机类进行调用:
Plane plane=new Plane(planeImg,300,300);
plane.drawSelf(g);//调用飞机对象画自己
这个时候运行就已经能看到一样的飞机在背景图上动的效果了(其实这就是完成了一次封装而已),这样的好处就是可以很容易的能new很多个飞机出来跑来跑去了,在设计其他障碍物等等的行动也可以和飞机类一样去继承GameObject类。