Java游戏开发——对对碰

游戏介绍:

对对碰游戏在n*n的游戏池中进行,每个格子中有一个图案。鼠标连续选中两个横排或竖排相邻的图案,它们的位置会互换,互换后如果横排或者竖排有3个以上相同的图像,则可以消去该图像,并得分。

游戏的基本规则如下:

①交换

玩家选择两个横排或竖排相邻的图案进行位置互换,如果互换成功则能消去图案,否则取消位置交换。

②消去

玩家选择两个横排或竖排相邻的图案进行位置互换,互换后如果横排或者竖排上有超过3个相同的图像,则消去这几个相同的图像,消去图像后的空格由其上面的图案掉下来补齐,每次消去图像,玩家都可以获得分数。

③连锁

玩家消去图像后,上面的图像掉下来补齐,如果此时游戏池里有连续相同的3个或3个以上的图像,则可以消去这些图像。消去后的空格由上面的图像掉下来补充,继续触发连锁,直到不满足连锁条件为止。

本次制作的对对碰运行效果如下图所示:

使用到的素材文件夹:

素材及完整源码链接:https://pan.baidu.com/s/1J1iXvgvVrNZI_Waq3v3xNA 提取码: iefw

游戏设计的思路:

游戏面板是n*n的方块组成,在完成其游戏功能的前提下,需要尽可能保持其扩展性。在这里我设置图案的种类共有n-2种,假如10*10的面板,则有8种图案,假如是8*8的面板,则有6种图案。游戏池数据使用二维数组map保存,存储的是图案种类ID,使用Image数组pics存储各种图案的图像,绘画面板时通过数组信息和图片ID即可对游戏池状况进行绘画。在定时器progress的控制下,推动游戏进行,这里设置游戏时间是100秒。使用isClick变量去标记玩家是不是第二次点击图案,使用clickX、clickY变量记录第一次点击图案的数组下标。

获取图片及显示图片:
使用Toolkit工具类获取图片,存储到Image数组,再遍历map数组,根据数组下标转换成左上角像素坐标,比如说map[3][4],在这里它的左上角x坐标为4*W+leftX,y坐标为3*W+leftY。最后根据左上角坐标和图案ID,绘制边长为W的图案。

private void getPics() {for(int i=0;i<n-1;i++){pics[i] = Toolkit.getDefaultToolkit().getImage("D://Game//SupperzzleGame//pic"+i+".png");}}public void paint(Graphics g){g.clearRect(0, 0, 700, 600);for(int i=0;i<n;i++){for(int j=0;j<n;j++){if(map[i][j]!=EMPTY){g.drawImage(pics[map[i][j]],leftX+W*j,leftY+W*i,W,W,this);}else{g.clearRect(leftX+W*j,leftY+W*i,W, W);}}}}

玩家鼠标点击事件:

先获取鼠标点击处减去偏移量后的x,y坐标,如果不在游戏池面板内,return;

用tempX存储这次点击的二维数组x下标,tempY存储这次点击的二维数组y下标(形如map[x][y])。

①如果是第一次点击:

修改isClick标记变量为true,用clickX,clickY记录此次点击的数组x下标和y下标。

②如果是第二次点击:

判断两次点击的图案是否横排或竖排相邻,如果相邻则先交换两数组元素的值。

使用isThreeLinked方法判断两个图案交换后是否存在可消去图案的情况,如果存在,则消去可消去的图案并使用downAnimal方法补齐游戏池,接着扫描游戏池中是否存在可消除的图案,如果存在则触发连锁消去事件,接着使用downAnimal方法补齐游戏池....直到当前游戏池没有可消去的图案为止。

如果交换后不存在可消去图案的情况,两次点击的图案位置重新换回。

public void mousePressed(MouseEvent e) {int x = e.getX()-leftX;int y = e.getY()-leftY;if(x<0||y<0||x>=50*n||y>=50*n){return ;}int tempX = y/W;int tempY = x/W;if(isClick){//第二次点击if((tempX==clickX&&(tempY==clickY+1||tempY==clickY-1))||(tempY==clickY&&(tempX==clickX+1||tempX==clickX-1))){//如果两次点击的图案相邻//交换int help = map[tempX][tempY];map[tempX][tempY] = map[clickX][clickY];map[clickX][clickY] = help;repaint();if(isThreeLinked(tempX,tempY)||isThreeLinked(clickX,clickY)){//判断是否存在可消去的方块//	System.out.println("可以消去");if(isThreeLinked(tempX,tempY)){removeThreeLinked(tempX,tempY);}if(isThreeLinked(clickX,clickY)){removeThreeLinked(clickX,clickY);						}downAnimal();updateAnimal();repaint();while(globalSearach(1)){globalSearach(2);downAnimal();updateAnimal();repaint();}}else{//System.out.println("没有可消去的方块");//交换回来help = map[tempX][tempY];map[tempX][tempY] = map[clickX][clickY];map[clickX][clickY] = help;					}isClick = false;}else{//不相邻或者就是点击的还是自身isClick = true;clickX = tempX;clickY = tempY;drawSelectedBlock(tempY*W+leftX, tempX*W+leftY, this.getGraphics());}}else{isClick = true;clickX = tempX;clickY = tempY;drawSelectedBlock(tempY*W+leftX, tempX*W+leftY, this.getGraphics());}		}

判断map[x][y]处是否存在可消去图案:

使用count变量记录连续相同的图案数目,count=1,先水平方向判断是否存在三个以上相邻图案,如果count>=3则返回true;

否则再重置count=1,从垂直方向判断是否存在三个以上相邻图案,如果count>=3则返回true。

如果还不存在可消去图案,返回false:

	//检测是否存在三个以上相连的方块private boolean isThreeLinked(int x, int y) {int count = 1;if(x+1<n){for(int i=x+1;i<n;i++){if(map[i][y]==map[x][y]){count++;}else{break;}}}if(x-1>=0){for(int i=x-1;i>=0;i--){if(map[i][y]==map[x][y]){count++;}else{break;}}}if(count>=3){return true;}count = 1;if(y+1<n){for(int i=y+1;i<n;i++){if(map[x][i]==map[x][y]){count++;}else{break;}}}if(y-1>=0){for(int i=y-1;i>=0;i--){if(map[x][i]==map[x][y]){count++;}else{break;}}}if(count>=3){return true;}return false;}

消除map[x][y]处三个以上相连的图案:

使用count记录可消去的图案数量,linked作为标记水平方向或竖直方向上相连的图案数量,

先判断竖直方向上相连的图案数量是否>=3,如果是,则消除掉竖直方向相连的图案并且count++;

接着置linked为1,判断水平方向上相连的图案数量是否>=3,如果是,则消除水平方向相连的图案并且count++;

最后置map[x][y]为空,分数+=count*10;

//消除三个以上相连的方块private void removeThreeLinked(int x, int y) {int count = 1;int linked = 1;if(x+1<n){//向下探测for(int i=x+1;i<n;i++){if(map[i][y]==map[x][y]){linked++;}else{break;}}}if(x-1>=0){//向上探测for(int i=x-1;i>=0;i--){if(map[i][y]==map[x][y]){linked++;}else{break;}}}if(linked>=3){//上下相邻超过三个方块for(int i=x-1;i>=0;i--){if(map[i][y]==map[x][y]){count++;map[i][y] = EMPTY;}else{break;}}for(int i=x+1;i<n;i++){if(map[i][y]==map[x][y]){count++;map[i][y] = EMPTY;					}else{break;}}}linked = 1;if(y+1<n){//向右探测for(int i=y+1;i<n;i++){if(map[x][i]==map[x][y]){linked++;}else{break;}}}if(y-1>=0){//向左探测for(int i=y-1;i>=0;i--){if(map[x][i]==map[x][y]){linked++;}else{break;}}}if(linked>=3){//左右相邻超过三个方块for(int i=y-1;i>=0;i--){if(map[x][i]==map[x][y]){count++;map[x][i] = EMPTY;}else{break;}}for(int i=y+1;i<n;i++){if(map[x][i]==map[x][y]){count++;map[x][i] = EMPTY;}else{break;}}}map[x][y] = EMPTY;score+=count*10;HelpPanel.score.setText(score+"");}

扫描游戏池:

如果flag==1,只判断游戏池中是否存在可消除的图案,如果存在返回true;

否则消除游戏池中可消除的所有图案。

//1扫描是否存在可消除方块//2扫描并消除可消除方块private boolean globalSearach(int flag) {if(flag == 1){for(int i=0;i<n;i++){for(int j=0;j<n;j++){if(isThreeLinked(i, j)){return true;}}}			}else{for(int i=0;i<n;i++){for(int j=0;j<n;j++){if(isThreeLinked(i, j))removeThreeLinked(i, j);}}}return false;}

图案下落填充:
从最后一行向上扫描游戏池,如果数组元素为空,则找到和它同一列,在它上方的第一个非空元素进行交换

	//图案下落private void downAnimal() {for(int i=n-1;i>=0;i--){for(int j=0;j<n;j++){if(map[i][j]==EMPTY){int temp = i;while(temp>=0){if(map[temp][j]!=EMPTY){int help = map[i][j];map[i][j] = map[temp][j];map[temp][j] = help;break;}temp--;}}}}}

更新游戏池状况:

图案下落后,此时空块都位于最上层,可以直接随机生成图案ID赋值给空的数组元素:

	//更新图案数组private void updateAnimal() {for(int i=0;i<n;i++){for(int j=0;j<n;j++){if(map[i][j]==EMPTY){map[i][j] = (int) (Math.random()*(n-2));}}}}

画选中框:

根据左上角x,y像素坐标,画框:

//画选中框private void drawSelectedBlock(int x, int y, Graphics g) {Graphics2D g2 = (Graphics2D) g;//生成Graphics对象BasicStroke s = new BasicStroke(1);//宽度为1的画笔g2.setStroke(s);g2.setColor(Color.RED);g.drawRect(x+1, y+1, 48, 48);}

历史记录读写:

基础的文件IO操作,如果文件不存在自动新建:

//读取历史记录public int getBestScore(){File file = new File("D://GameRecordAboutSwing");if(!file.exists()){file.mkdirs();}File record = new File("D://GameRecordAboutSwing/SupperzzleGame.txt");try{if(!record.exists()){//如果不存在,新建文本record.createNewFile();fos = new FileOutputStream(record);dos = new DataOutputStream(fos);String s = "0";dos.writeBytes(s);}//读取记录fis = new FileInputStream(record);dis = new DataInputStream(fis);String str = dis.readLine();bestScore = Integer.parseInt(str);}catch(Exception e){e.printStackTrace();}finally{try {if(fis!=null)fis.close();if(dis!=null)dis.close();			if(fos!=null)fos.close();if(dos!=null)dos.close();				} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}return bestScore;}//更新历史记录public void updateBestScore(){File record = new File("D://GameRecordAboutSwing/SupperzzleGame.txt");try {//清空原有记录FileWriter fileWriter =new FileWriter(record);fileWriter.write("");fileWriter.flush();fileWriter.close();//重新写入文本fos = new FileOutputStream(record);dos = new DataOutputStream(fos);String s = score.getText();bestScore = Integer.parseInt(score.getText());dos.writeBytes(s);} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}finally{try {if(fos!=null)fos.close();if(dos!=null)dos.close();				} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}HelpPanel.record.setText(bestScore+"");}

开始游戏:

设定进度条progress最大值为100,线程每秒自增1,游戏开始时初始化进度条progress并为游戏面板添加鼠标监听和键盘事件,进度条满之后,移除游戏面板的监听并提示玩家游戏成绩,如果当前分数高于历史记录则进行历史记录的更新。

	public MyFrame(){actionPanel.setLayout(new FlowLayout()); actionPanel.add(buttonRestart,BorderLayout.CENTER);this.getContentPane().setLayout(new BorderLayout());this.getContentPane().add(helpPanel,BorderLayout.NORTH);this.getContentPane().add(gamePanel,BorderLayout.CENTER);this.getContentPane().add(actionPanel,BorderLayout.SOUTH);    this.setSize(700,700);this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);this.setTitle("对对碰");this.setVisible(true);buttonRestart.addMouseListener(new MouseAdapter(){public void mouseClicked(MouseEvent e){if(flag)return ;flag = true;gamePanel.addKeyListener(gamePanel);gamePanel.addMouseListener(gamePanel);gamePanel.startGame();buttonRestart.setEnabled(false);HelpPanel.score.setText(0+"");new Thread(new Runnable(){@Overridepublic void run() {nowTime = 0;while(true){try {Thread.currentThread().sleep(1000);nowTime++;HelpPanel.setTime(nowTime);if(nowTime==100){gamePanel.removeMouseListener(gamePanel);gamePanel.removeKeyListener(gamePanel);int score = Integer.parseInt(helpPanel.score.getText());int record = Integer.parseInt(helpPanel.record.getText()); if(score>record){JOptionPane.showMessageDialog(null, "游戏结束,你的得分是"+score+",刷新了历史记录"+record);helpPanel.updateBestScore();}else{JOptionPane.showMessageDialog(null, "游戏结束,你的得分是"+HelpPanel.score.getText());}buttonRestart.setEnabled(true);flag = false;break;}} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}}).start();;}});}

主要游戏功能到这里已经介绍完毕,玩家可以使用A键打乱游戏池,游戏保证了开始时不存在图案连锁消除的情况。

由于完整源码篇幅过长,这里不再贴出,素材和工程均已上传到网盘。

素材与完整源码链接:https://pan.baidu.com/s/1J1iXvgvVrNZI_Waq3v3xNA 提取码: iefw

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/353105.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

连连看游戏的设计与实现——基于JAVA语言的小游戏

说明&#xff1a;本篇博客主要讲述练练看游戏的设计与实现。前半部分为分析与类和属性的说明&#xff0c;后半部分为程序的实现与程序代码。第一次写小游戏&#xff0c;仍存在许多问题&#xff0c;也借鉴了CSDN前辈的代码想法&#xff0c;如有不妥&#xff0c;还望多批评指正。…

Java写的第一个小游戏(续)

优化代码(数据维护): 注:此为小游戏最终版本 代码做到最优最简 基于之前发表的文章详情可见 点击查看前文 回顾之前的代码我们可以看到很多的固定常量值或是属性我们都会大量的重复使用,这并不符合我们代码简洁易懂的特点 所以我们可以把重复出现的量或是属性或是方法使用面向…

java小游戏超级玛丽:06.第二关的设计

第二关预览图&#xff1a; 判断是否为第二关 if (sort 2) {} 砖块位置图&#xff1a; 砖块坐标&#xff1a; A(240,390) B(270,360) C(300,330) D(270,360) E(300,360) F(330,390) G(360,360) H(390,390) I(420,390) f1(240,300) 空1&#xff08;420&#xff0c;270&a…

java小游戏超级玛丽:07.第三关的设计

第三关预览图 砖块位置图&#xff1a; 砖块坐标&#xff1a; A(290,390) B(320,360) C(350,330) D(320,390) E(350,480) F(350,390) G(380,300) H(380,330) I(380,360) J(380,390) K(410,270) L(410,300) M(410,330) N(410,360) O(410,390) P(60,390) Q(90,360) R(90…

【Java十大热门游戏合集】Java经典游戏项目

家人们&#xff0c;今天我们来看一下学Java必练的10款游戏项目&#xff01; 大家都知道学习编程必须要做的就是敲代码和做项目练手了&#xff0c;那项目有难有易&#xff0c;很多小伙伴不知道从哪里找项目来练习&#xff0c;今日我们来看一下初级项目中都有哪些能让我们来练手…

Java游戏开发——开心农场

游戏介绍&#xff1a; “开心农场”是一款以种植为主的社交游戏。用户可以扮演一个农场的农场主&#xff0c;在自己的农场里开垦土地&#xff0c;种植各种水果蔬菜。本次开发了一个“开心农场”游戏&#xff0c;运行程序&#xff0c;效果如下图所示。鼠标先选定指定土地&#…

JAVA 实现《超级玛丽》游戏

前言 在你的童年记忆里&#xff0c;是否有一个蹦跳、顶蘑菇的小人&#xff1f; 如果你回忆起了它&#xff0c;你定然会觉得现在它幼稚、无聊&#xff0c;画面不漂亮&#xff0c;游戏不精彩……但请你记住&#xff1a;这才是真正的游戏&#xff0c;它给了你无限的欢乐&#xf…

Java游戏开发——连连看

游戏介绍&#xff1a; “连连看”是一款来源于我国台湾的桌面小游戏&#xff0c;主要考验的是玩家们的眼力&#xff0c;在有限的时间内&#xff0c;只要能把所有能连接的相同图案&#xff0c;两个两个的找出来&#xff0c;每找到一对&#xff0c;它们就会自动消失&#xff0c;只…

java小游戏超级玛丽:05.第一关的设计

第一关效果图&#xff1a; 目录 判断是否为第一关 绘制第一关的场景 绘制地面&#xff08;for循环&#xff09; 绘制砖块 绘制水管 生成Obstatic列表的getter方法 绘制障碍物 判断是否为第一关 if(sort 1){ } 在上面有定义变量sort 绘制第一关的场景 绘制地面&…

程序员从0到收获心仪offer,我靠训练营实现了180度逆袭!

我相信&#xff0c;在未来的职场中&#xff0c;我也能通过这段时间养成的学习习惯和生活习惯让自己一步步成为更好的自己&#xff0c;以自己为荣 我在大学里主修计算机科学与技术&#xff0c;一个普通的院校&#xff0c;一个算是常见的专业&#xff0c;我知道我的学历和一些其他…

10 【组件编码流程 组件自定义事件 全局事件总线】

1.组件编码流程 组件化编码流程&#xff1a; ​ (1).拆分静态组件&#xff1a;组件要按照功能点拆分&#xff0c;命名不要与html元素冲突。 ​ (2).实现动态组件&#xff1a;考虑好数据的存放位置&#xff0c;数据是一个组件在用&#xff0c;还是一些组件在用&#xff1a; ​ 1…

QQ互联地址 中注册的QQ开发者 在哪里提交审核?

QQ互联地址&#xff1a;https://connect.qq.com 废话不说&#xff0c;直接上图&#xff1a; 有时点击后没反应&#xff0c;耐心 多试几次&#xff01; 然后 就会看到 提交审核的表单了&#xff0c; 有没发现 这个提交审核的 链接藏的有点深&#xff01;&#xff01; 哈哈哈&am…

QQ小程序打开指定QQ群

官方提供了接口&#xff1a; <view class"footer"><button open-type "openGroupProfile" class"footer-tips" group-id"620033746">v1.0 by&#xff1a;IWH <text>加群玩&#xff01;</text></button&g…

打开图片链接直接打开QQ对话框

<a href"tencent://message/?uin7530****&SiteQQ交谈&Menuyes" target"blank"> <img border"0" src"http://wpa.qq.com/pa?p1:7530*****:7" alt"有事Q我吧" width"71" height"2…

QQ的分享

使用的sdk版本&#xff1a; V2.2.2&#xff08;发布日期&#xff1a;2014.3.17&#xff09; 弱弱的提醒下&#xff1a; 1.配置清单等环境搭建&#xff0c;这里就不多说了&#xff0c;可以去看看我的另外一篇博客&#xff0c;QQ的第三方登录&#xff0c;那里有介绍。 2.这里集…

QQ圈子:从哪里来,到哪里去

2019独角兽企业重金招聘Python工程师标准>>> 摆脱顿巴数的魔咒 社 交是人类的一个最基本的需求。但是&#xff0c;自然给我们人类的大脑&#xff0c;只能让我们维系150-200个左右的好友。超出这个范围&#xff0c;就会有好友慢慢地被淡忘。很多社会 群体的平均大小是…

html 打开腾讯qq,qq软件管理器 QQ应用管理器里面的腾讯软件打不开

手机QQ中的消息管理器在哪里 打开的qq软件主界面&#xff0c;点击左下角的“主菜单”按钮。 在打开的qq主菜单中&#xff0c;点击“设置”菜单项。 手机版qq怎么进消息管理器QQ右下角...有一个查找。 查找的左边,有一个小喇叭. 那个就是信息管理器。 腾讯qq上的应用管理器在哪 …

打开qq聊天界面的url

2019独角兽企业重金招聘Python工程师标准>>> 打开qq聊天界面的url 北极熊爱企鹅 10:43:39 NSString *open_URL "mqq://im/chat?chat_typewpa&uin501863587&version1&src_typeweb"; 转载于:https://my.oschina.net/LiuChongYang/blog/51829…

如何创建圈子

首先您需要登录您在CSDN Blog的个人管理后台,点击“圈子”.如图所示: 点击"创建一个新的圈子" 输入名称和标题&#xff08;名称将是URL的一部分&#xff0c;例如experts就是 http://blog.csdn.net/group/experts/的名称&#xff1b;而“CSDN专家群”则是标题&#x…

Numpy---创建多维数组、创建正态分布、创建均匀分布

1. 创建一个随机整数的多维数组 np.random.randint(low, highNone, sizeNone, dtypel) 参数说明: low : 最小值 highNone: 最大值 highNone时&#xff0c;生成的数值在【0, low&#xff09;区间内 如果使用high这个值&#xff0c;则生成的数值在【low, high&#xff09;区…