游戏基本规则:
1、初始分数1000分;
2、每打掉一个字母加50分;
3、打错字母或者字母落地一个扣除100分;
4、录入的键盘字母与屏幕上的字母核对,需要区分大小写;
5、当总分数小于0时,游戏结束,统计游戏存活时间。
用到的素材:
实现原理:我们只需要自定义一张游戏画布,在该画布中完成游戏逻辑与绘制,最后将画布(JPanle)加入到窗体中即可(JFrame)。屏幕中的字母是向下掉落,我们需要以一定的速度来改变这些字母的y坐标,并重新绘制到画布上,由于绘制速度过快,人眼睛自然会产生字母向下平移的视觉错觉,实现字母的平移效果完全可以使用线程来完成,而最近刚接触 定时任务(Timer、TimerTask),所以尝试用这两个类完成。
实现键盘录入的字母与屏幕字母的对照,则加入键盘监听即可,JPanle子类实现KeyListener接口中的KeyPressed()方法来对比屏幕中的字母。
代码如下(注释已加):
- package playchar.game;
- import java.awt.Color;
- import java.awt.Font;
- import java.awt.Graphics;
- import java.awt.Image;
- import java.awt.Toolkit;
- import java.awt.event.KeyEvent;
- import java.awt.event.KeyListener;
- import java.util.Timer;
- import java.util.TimerTask;
- import javax.swing.JPanel;
- public class CharJPanel extends JPanel implements KeyListener{
- private static final long serialVersionUID = 1L;
-
- //背景图片
- Image back=Toolkit.getDefaultToolkit().getImage(CharJPanel.class.getResource("back.jpg"));
- //爆炸图片
- Image b0=Toolkit.getDefaultToolkit().getImage(CharJPanel.class.getResource("b0.jpg"));
- Image b1=Toolkit.getDefaultToolkit().getImage(CharJPanel.class.getResource("b1.jpg"));
- Image b2=Toolkit.getDefaultToolkit().getImage(CharJPanel.class.getResource("b2.jpg"));
- Image b3=Toolkit.getDefaultToolkit().getImage(CharJPanel.class.getResource("b3.jpg"));
-
- //用于统计开始时间,以及结束时间
- boolean firstStart =false; //首次启动标记
- int num=3; //倒计时3秒标记
- long start=0; //用于存放游戏开始时间
- long end =0; //用于存放游戏结束时间
-
- int score=1000; //初始分数1000分
-
- //用于存放屏幕中10个字母以及所对应的坐标,大小写
- char[] c=new char[10];
- int[] x=new int [c.length];
- int[] y=new int[c.length];
- int[] caps=new int[c.length];
-
- Timer timer=null; //定义定时任务对象
-
-
- //xx,yy所代表的坐标是否被玩家打掉,被打掉时playflag置为true,其他时间false
- int xx=0;
- int yy=0;
- boolean playflag=false;
-
-
- public CharJPanel(){ //构造时初始化
- firstStart=true; //画板实例时,将第一次启动标志改变
- this.init(); //开始初始化
- start=System.currentTimeMillis(); //画板被实例时,就获得计时开始
- }
-
- public void init(){ //初始化方法
- for(int i=0;i<c.length;i++){
- c[i]=(char)(Math.random()*26+97); //获得随机26个字母
- caps[i]=(int)(Math.random()*2); //会的随机大小写,值为0小写,值为1大写
- x[i]=checkX((int)(Math.random()*450+12),x);
- y[i]=(int)(Math.random()*300);
- }
- timer =new Timer(); //实例定时任务对象
- }
-
- public int checkX(int temp_x,int x[]){ //检查字母坐标是否有重叠,有则重新生成坐标
- int xxx=temp_x;
- for(int i=0;i<c.length;i++){
- if(x[i]!=0){
- if(Math.abs(xxx-x[i])<20){
- xxx=checkX((int)(Math.random()*450+12),x);
- }
- }
- }
- return xxx;
- }
-
- public void paint(Graphics g) //绘图
- {
- super.paint(g); //获得父类的绘图功能
-
-
- g.drawImage(back,0, 0, this); //绘制背景图片
-
- if(firstStart){ //启动游戏时,倒计时3秒
- g.setColor(Color.red);
- g.setFont(new Font("宋体",Font.BOLD,80));
- g.drawString(""+num, 200, 200);
- num--;
- try {
- Thread.sleep(1000); //画1次休息1秒
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- if(num==-1){ //此处判断-1 而不是0 纯粹只是运行时效果较好原因
- firstStart=false; //倒计时结束标志
- }else{
- //没结束则等待下一次绘制
- return;
- }
- }
-
- if(playflag){ //如果有打掉字母,则在此字母位置显示爆炸效果
- g.drawImage(b0,xx, yy-50, this);
- g.drawImage(b1,xx, yy-50, this);
- g.drawImage(b2,xx, yy-50, this);
- g.drawImage(b3,xx, yy-50, this);
- playflag=false;
- }
-
- if(score<0){ //分数已负,游戏结束
- end=System.currentTimeMillis(); //获得结束时间
- //绘制结束背景
- Image black=Toolkit.getDefaultToolkit().getImage(CharJPanel.class.getResource("black.jpg"));
- Image over=Toolkit.getDefaultToolkit().getImage(CharJPanel.class.getResource("over.jpg"));
- g.drawImage(black,0, 0, this);
- g.drawImage(over, 0, 200, this);
- //设置 Game Over 提示
- g.setColor(Color.red);
- g.setFont(new Font("宋体",Font.BOLD,80));
- g.drawString("Game Over",50,300);
- //游戏结束时,将分数也变为红色值为0,并计算打印存活时间
- g.setFont(new Font("宋体",Font.BOLD,30));
- g.drawString("分数:0",170,50);
- g.drawString("您共存活了:"+(end-start)/1000+"秒", 130, 150);
- timer.cancel(); //关闭定时任务
- return ; //停止此次绘图
- }
-
- //设置屏幕字母基本属性
- g.setColor(Color.black); //颜色
- g.setFont(new Font("Vineta BT",Font.BOLD,25)); //字体
- for(int i=0;i<c.length;i++){ //绘制字母
- if(caps[i]==1)
- g.drawString(((c[i]=Character.toUpperCase(c[i]))+""),x[i],y[i]);
- else{
- g.drawString(c[i]+"",x[i],y[i]);
- }
- }
-
- //绘制分数
- g.setColor(Color.blue);
- g.setFont(new Font("宋体",Font.BOLD,30));
- g.drawString("分数:"+score,170,50);
- }
-
-
-
- //定义一个方法,启动游戏
- public void go(){
- //制定定时任务
- timer.schedule(new TimerTask(){
- //new TimerTask(){}匿名内部类,此处使用匿名内部类,完全是在他人的提醒下对代码的改进,并不利于理解,诺对此处产生困惑
- //建议还是写成内部类格式,并继承TimerTask类,覆写run()方法, 在此处就直接new 此内部类实例即可
- public void run() {
-
- for(int i=0;i<c.length;i++){ //字母下落
- y[i]++;
- if(y[i]>560){ //如果已经掉落到底边界,则扣分,并从窗体最上方生成一个新的随机字母
- score-=100;
- y[i]=0;
- x[i]=checkX((int)(Math.random()*450+12),x);
- c[i]=(char)(Math.random()*26+97);
- caps[i]=(int)(Math.random()*2);
- }
- }
- repaint(); //重画
- }
- },0,50) ; //现在就启动,以后每50毫秒字母向下移动1像素并绘制一次
-
-
- }
- //键盘监听
- public void keyPressed(KeyEvent key) { //按下键盘某个键时,监听键盘输入的字符
-
- char ckey=key.getKeyChar(); //获得键盘录入字母
-
- if(ckey!=65535){ //排除大小写转换时扣分情况
- int index=-1;
- int temp_y=-1;
-
- for(int i=0;i<c.length;i++){ //遍历屏幕字母,是否有相同的
- if((ckey==c[i])){
- if(y[i]>temp_y){
- temp_y=y[i];
- index=i;
- }
- }
- }
- if(index!=-1){ //如果有相同的,则加分,并消除掉此字母,在窗体最上处生成新的字母替换
- score+=50;
- //保存被打掉的字母坐标,并将playflag 置 true
- xx=x[index];
- yy=y[index];
- playflag=true;
-
- //生成新的坐标
- y[index]=0;
- x[index]=checkX((int)(Math.random()*450+12),x);
- c[index]=(char)(Math.random()*26+97);
- caps[index]=(int)(Math.random()*2);
- }else{
- //否则日录入的字母并不存在,扣100分
- score-=100;
- }
- }
- }
- //以下两个监听事件,并未用到,置成空方法体即可
- public void keyReleased(KeyEvent arg0) {}
-
- public void keyTyped(KeyEvent arg0) {}
- }
复制代码程序入口:
- package playchar.game;
- import java.awt.Dimension;
- import java.awt.Toolkit;
- import javax.swing.JFrame;
- public class PlayCharGame {
- public static void main(String[] args) {
- JFrame frame=new JFrame("PlayCharGame(区分大小写版)"); //游戏窗体
- frame.setSize(500,600);
- frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
- frame.setMinimumSize(new Dimension(500,600)); //设置最小大小
- //设置窗体logo
- frame.setIconImage(Toolkit.getDefaultToolkit().getImage(PlayCharGame.class.getResource("logo.png")));
-
- CharJPanel pan=new CharJPanel(); //绘画面板(自定义画布)
- pan.go(); //开始游戏,启动定时任务
-
- frame.add(pan); //向窗体添加面板
- frame.addKeyListener(pan); //绑定键盘事件
- frame.setVisible(true); //显示窗体
- }
- }
复制代码