最近做了一个简化版的山寨植物大战僵尸,虽然还有很多功能没有实现,但初步的样子还是有了,下面就谈一下我的收获和遇到的问题吧。
一、总体构架
我的游戏主要实现了在主界面上以图片做背景,通过鼠标的点击控制,种植向日葵,豌豆,通过收获阳光获得武器,并通过豌豆的射击消灭僵尸。下面是我在各个功能实现上的想法。
[1]在有背景的界面上插入有图片的按钮。
这里主要涉及的是将流式布局设为空,然后使用setBounds组件。
// 设置模块
panel = new MyPanel();
Dimension paneldim = new Dimension(1026, 700);
panel.setPreferredSize(paneldim);
panel.setLayout(null);// 以绝对方式添加组件,须将容器panel布局置空
this.add(panel);
mainshow = new JLabel(Config.mainshow);// 背景图片大小:1026*700
mainshow.setBounds(0, 0, 1026, 760);// 坐标从x=0,y=0开始,它将按背景图片大小来占据北部空间
panel.add(mainshow);
//设置豌豆射手按钮
peabutton = new JButton(Config.peaButton);// 图标按钮大小:60*90
peabutton.setBounds(122, 6, 60, 90);
peabutton.setActionCommand("peashooter");// 设置按钮动作监听命令
peabutton.addActionListener(al);
panel.add(peabutton);// peabutton在mainshow的范围内
这样可以摆脱布局的干扰,防止背景图片把按钮挤到窗口之外。但是还会遇到按钮上的图片会被底层背景覆盖,只有当鼠标在上面点击时,图片才会闪现。
[2]在背景图片上插入记分牌。
这个记分牌实际是一个文本输入框,我在输入框上插入图片,并将传入的数字设为变量,并改变了字体和颜色,也就和原版不相上下了。
// 创建并设置文本域
final Image CHAT_TEXT_IMAGE_BG = Config.num.getImage();
JTextField jl = new JTextField(" 100"){
{
setOpaque(false);
}
public void paint(Graphics g) {
g.drawImage(CHAT_TEXT_IMAGE_BG, 0, 0, this);
super.paint(g);
}
};
jl.setBorder(new EtchedBorder());
jl.setForeground(Color.getHSBColor(0,0,0));//设置字体的颜色
jl.setFont(new Font(" ", Font.ROMAN_BASELINE, 20));//设置字体的形状和大小
jl.setBounds(29, 74, 67, 30);//设置文本框的位置和大小
panel.add(jl);
[3]在草地上画太阳花和豌豆射手
采用和五子棋一样的方法,将草地看成数组,给太阳花和射手定义不同的数字,给窗口添加鼠标监听器,当点击时为数组附上相应的数值,并根据数值进行重绘。
// 1.豌豆
// -1.向日葵
// 获取草地上中点的坐标
for (int i = 0; i < Config.ROWS; i++) {
for (int j = 0; j < Config.COLUMNS; j++) {
x2 = Config.X + j * Config.plantwidth;
y2 = Config.Y + i * Config.plantheight;
// 得到离点击位置最近的草坪中点坐标
if ((x1 > x2) && (x1 < (x2 + Config.plantwidth)) && (y1 > y2)
&& (y1 < (y2 + Config.plantheight))) {
try{
if (Config.plant[i][j] == 0) {
if (PKUI.item.equalsIgnoreCase("peashooter")&&number>=100) {
check = 1;
} else if (PKUI.item.equalsIgnoreCase("sunflower")&&number>=50) {
check = -1;
}
putplant(i, j, check);
}
}catch(Exception ef){
ef.getStackTrace();
}
}
}
}
}
/**
* 画植物的方法
*
* @param i
* :
* @param j
* @param check
* ,植物,1;向日葵,2;
*/
public void putplant(int i, int j, int check) {
x = Config.X + j * Config.plantwidth;
y = Config.Y + i * Config.plantheight;
if (check == 1) {
g.drawImage(Config.peashooter.getImage(), x, y, Config.width,
Config.height, null);
// 启动豌豆线程
peashooterThread thread = new peashooterThread(panel,x,y);
thread.start();
Config.pslist.add(thread);
number-=100;
} else if (check == -1) {
g.drawImage(Config.sunflower.getImage(), x, y, Config.width,
Config.height, null);
// 向日葵线程
sunflawerThread thread = new sunflawerThread(panel,x,y);
thread.start();
number-=50;
}
// 标记二维数组
Config.plant[i][j] = check;
// 判断输赢
[4]多线程的控制
这是这个游戏采用最多的方法,每一种植物是一个线程,控制是否喷出豌豆或阳光,再用另一个线程控制豌豆和阳光尖端喷出的频率,僵尸的延时出场以及每隔一段时间出现一批,也是通过可控时间的线程TimerTask类实现的。在线程中要通过对死循环的控制来控制每种植物的出现,在其中我遇到的问题是绘制的僵尸及豌豆和阳光不停地闪烁,原因是在双缓冲中进行了重绘,在线程中又绘制了图片,绘制的多次重叠导致图片不断闪烁。只要将线程中的绘制图片取消就可以解决。
[5]双缓冲解决屏幕重绘时闪烁问题
在主类中再定义一个内部类,实现双缓冲。
class MyPanel extends JPanel {
private Image offScreenImage;
Graphics gImage;
// 重写update方法,先将窗体上的图形画在图片对象上,再一次性显示
public void update(Graphics g) {
if (offScreenImage == null) {
// 截取窗体所在位置的图片
offScreenImage = this.createImage(1026, 760);
}
// 获得截取图片的画布
gImage = offScreenImage.getGraphics();
// 获取画布的底色并且使用这种颜色填充画布(默认的颜色为黑色)
Color c = Color.BLACK;
gImage.setColor(c);
gImage.fillRect(0, 0, 1026, 760); // 有清除上一步图像的功能,相当于gImage.clearRect(0,
// 0, WIDTH, HEIGHT)
// 将截下的图片上的画布传给重绘函数,重绘函数只需要在截图的画布上绘制即可,不必在从底层绘制
paint(gImage);
// 将接下来的图片加载到窗体画布上去,才能得到每次画的效果
g.drawImage(offScreenImage, 0, 0, null);
}
public void paint(Graphics g) {
// 在重绘函数中实现双缓冲机制
offScreenImage = this.createImage(1026, 760);
// 获得截取图片的画布
gImage = offScreenImage.getGraphics();
// 获取画布的底色并且使用这种颜色填充画布,如果没有填充效果的画,则会出现拖动的效果
gImage.setColor(gImage.getColor());
gImage.fillRect(0, 0, 1026, 760); // 有清楚上一步图像的功能,相当于gImage.clearRect(0,
// 0, WIDTH, HEIGHT)
// 调用父类的重绘方法,传入的是截取图片上的画布,防止再从最底层来重绘
super.paint(gImage);
gImage.drawImage(Config.mainshow.getImage(), 0, 0, 1026, 760, null);
for (int i = 0; i < Config.ROWS; i++) {
for (int j = 0; j < Config.COLUMNS; j++) {
int x = Config.X + j * Config.plantwidth;
int y = Config.Y + i * Config.plantheight;
if (Config.plant[i][j] == 1) {
gImage.drawImage(Config.peashooter.getImage(), x, y,
null);
} else if (Config.plant[i][j] == -1) {
gImage.drawImage(Config.sunflower.getImage(), x, y,
null);
}
}
}
// 绘制子弹
for (int i = 0; i < Config.plist.size(); i++) {
peaThread pt = Config.plist.get(i);
gImage.drawImage(Config.pea.getImage(), pt.x, pt.y,
Config.size, Config.size, null);
//panel.repaint();
}
// 绘制太阳
for (int i = 0; i < Config.sunlist.size(); i++) {
sunfThread sf = Config.sunlist.get(i);
gImage.drawImage(Config.sun.getImage(), sf.x, sf.y,
100, Config.sun_width, null);
}
for (int i = 0; i < Config.suntlist.size(); i++) {
suntThread sf = Config.suntlist.get(i);
gImage.drawImage(Config.sun.getImage(), sf.x, sf.y,
100, Config.sun_width, null);
}
//绘制僵尸
for(int i = 0;i<Config.dlist.size();i++){
death2Thread df = Config.dlist.get(i);
gImage.drawImage(Config.death1.getImage(), df.x,df.y, null);
// if((j+1)==(3*i-2)){
// gImage.drawImage(Config.death1.getImage(), df.x,df.y, null);
// }else if((j+1)==(3*i-1)){
// gImage.drawImage(Config.death2.getImage(), df.x,df.y, null);
// }else if((j+1)==3*i){
// gImage.drawImage(Config.death2.getImage(), df.x,df.y, null);
// }
// }
// death2Thread df = Config.dlist.get(i);
// gImage.drawImage(Config.death1.getImage(), df.x,df.y, null);
//
}
// for (int i = 0; i < Config.dlist.size(); i++) {
//
// final death2Thread df = Config.dlist.get(i);
// Runnable rb = new Runnable(){
// int n=1;
// public void run(){
// while(true){
// gImage.drawImage(new ImageIcon("death"+n+".png").getImage(), df.x--, df.y, null);
// n++;
// if(n>3){
// n=1;
// }
// try {
// Thread.sleep(1000);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// }
//
// }
// };
// new Thread(rb).start();
//
//
// }
// 将接下来的图片加载到窗体画布上去,才能考到每次画的效果
g.drawImage(offScreenImage, 0, 0, null);
}
}
[6]控制小球与僵尸碰撞时消去小球和僵尸
主要采用了线程监听类,当碰撞时,在存储的队列中移除对象即可。
二、自己遇到的困难和需要解决的问题
1、图片交换来实现僵尸的动态行走,图片和移动的位置,以及线程所取得的图片未能很好的搭配到一起,所以这个功能没有实现。 2、点击相应的按钮后每种植物只能种植一次,这也是我接下来要解决的问题。
同时由于每一个线程都是一个类,所以类的繁多造成了传参的混乱,也给我的彼岸陈国成造成了不小的麻烦
最后也是最要提的一点就是,一定要把自己的项目及时备份,我的项目在第三天的时候,被自己误删了,不得不从头开始写,这耽误了我很多时间,也是给我一个教训,一定要备份。
分享到:
相关推荐
一个flash a3页游作品 山寨版植物大战僵尸 开2个浏览器窗口,一边使用植物一边使用僵尸(本游戏特色能使用僵尸) 美术 程序 音乐 音效
项目实训 学员作品 山寨版植物大战僵尸demo 有工具栏能放物品定时落阳光和出僵尸.zip
学习java游戏入门的好代码,不容错过。
植物大战僵尸山寨版源码; 关卡不多; 只做了部分的僵尸和植物
植物大战僵尸山寨版可以使用
山寨版qq源码.素材
山寨版qq软件,java爱好者,客户端以及后台数据库服务器支持
山寨版qq源码+素材 山寨版qq源码+素材 IM即时通信 山寨版qq源码+素材 可以自己分析,对于java学习比较好的哈!
两个个很不错的项目适合初学者练手,网络编程,多线程,gui,集合等等
山寨版qq源码和坦克大战游戏源码素材文档
基于网络、io流的java编程。基本实现了QQ常用的所有功能,用户注册,登录,私聊,群聊,窗口抖动,加好友,截图,改变字体,改变聊天窗口背景等,值得借鉴,不足地方还望大家指正。(注:数据库为mysql,用其他的...
山寨版QQ 源代码 QQ2010界面 用JAVA编写 韩顺平JAVA教程视频配套源代码
一个山寨版的 zte认证客户端 ,并不保证每个学校都能用
韩顺平老师课程所讲的山寨版QQ源代码,非常适合学习
VB游戏 山寨版VB游戏 山寨版VB游戏 山寨版VB游戏 山寨版
山寨QQ,能够在公网上运行,绝对采用正规模式构架,包括服务器端和客户端
韩顺平老师的qq项目源代码,供大家参考学习
山寨版qq源码,很全面,韩顺平老师的作品,详细地描述了客户端与服务器端的开发。
坦克大战游戏源码.素材.文档 山寨版qq源码.素材