房子到期了,刚搬家,网还没转过去,回去之后一个人只能看看广告,不想被电视台QJ,于是就在公司赖着蹭网、蹭空调。实在太无聊,刚好昨天和@zhzhxtrrk聊到了移动设备上的游戏开发,据说在国外美刀赚很凶的,于是乎就写个玩玩呗,度过漫漫长夜。。。
说实话不想装sdk,于是就java上了,上网down素材。。。。然后开始动工。
先简单介绍下游戏的原理吧。
一、重写jpanel的print方法,在这个方法里面做的事情:1.画障碍物(顶部的尖刺) 2.画地板队列中的所有地板 3.画人 4.画图血条等提示信息
二、新建一个线程,这个线程不停的循环,在这个线程中做的事情:1.更新地板的位置 2.随即生成新的地板 3.更新地板动画 4.删除已经在窗口外面的地板
每次循环休眠一段时间,每个循环是一个时钟周期,不同的时钟周期做不同的事情(35个时钟周期生成一个地板,7个时钟周期更新一次地板动画)
效果图:
核心代码剖析:
//重写PAINT方法 public void paint(Graphics g) { g1.clearRect(0, 0, 300, 400); //画顶部的尖刺 for (int i = 0; i < 300 / 15; i++) { g1.drawImage(chi, 15 * i, 30, null); } man.drawMan(g1); NFloor tempFloor = null; //画所有地板 for (int i = 0; i < floorList.size(); i++) { tempFloor = (NFloor) floorList.get(i); tempFloor.drawFloor(g1); } g1.setColor(Color.ORANGE); g1.setFont(new Font("Courier", Font.BOLD, 20)); g1.drawString("已经:" + man.getFloor() + "层", 10, 60); g1.drawString("血:", 10, 85); g1.setColor(Color.RED); g1.fillRect(40, 75, man.getHealth(), 10); g1.setColor(Color.YELLOW); g1.fillRect(40+man.getHealth(), 75, 100-man.getHealth(), 10); g.drawImage(buffimg, 0, 0, this); }
在画图方法中不需要关注任何信息,只需要获取需要画图的对象,然后调用对象的画图方法即可。这里图片的尖刺是画出来的,如果能在底部图上把这些直接画上也是可以的。这样做也有好处,我们可以在画布的四周全部布满尖刺,如果碰到尖刺就死亡,这个也是可以的。
if (floorList.size() < 10) { num++; int floor_x = (int) (Math.random() * (300 - 100)); floorList.add(new NFloor(floor_x, 400 + (int) (Math.random() * 3), (int) (Math.random() * 6), num)); }
地板的生成:地板队列(floorList)小于10的时候(表明同事出现的地板不超过10个),随即生成一个NFloor,X坐标为(int) (Math.random() * (300 - 100))因为画布宽度为300,地板为100,所以我们不能让地板的最右边超出画布,所以就是300-100之间的随机数。请看下图X的取值原理
//RUN 方法 public void run() { int count = 0; int num = 0; while (true) { if (!man.isDie()) { //更新地板的位置 for (NFloor floor : floorList) { floor.setY(floor.getY() - floor.getSpeed()); } //生成新地板,35个周期生成一个 if (count % 35 == 0) { if (floorList.size() < 10) { num++; int floor_x = (int) (Math.random() * (300 - 100)); floorList.add(new NFloor(floor_x, 400 + (int) (Math.random() * 3), (int) (Math.random() * 6), num)); } count = 0; } if(count % 7 == 0){ //更新地板动画 for (NFloor floor : floorList) { floor.update(); } } count++; //删除地板 for (Iterator<NFloor> it = this.floorList.iterator(); it.hasNext();) { NFloor floor = (NFloor) it.next(); if (floor.getY() < 40) { it.remove(); } } //重画 repaint(); }else{ //清空数据 floorList.removeAllElements(); count = 0; num = 0; man.setHealth(100); } try { Thread.sleep(20); } catch (InterruptedException e) { e.printStackTrace(); } } }
新启动的这个线程中会控制时钟周期(每次时钟周期休眠一段时间,这个值可以看效果订),每个时钟都会更新地板的位置,地板位置的计算公式:x坐标不变(地板X轴始终是不变的)y=y-speed(Y轴的坐标会每次以一定的速度减少,看到的效果就是地板在不停的上升)。同时每35个时钟周期会随即生成一个地板。
同时我们会在每个时钟周期判断一个地板是否超过了画布,如果超过了就直接删除掉。
由于画板中有些地板是有动画效果的,所以需要每7个时钟周期更新一下地板的状态,做出动画效果floor.update()这个随后讲地板的时候在介绍
最后会掉一次repaint()把更新完成的数据重新在画布上画出来。
地板类:
public void drawFloor(Graphics g1) { //貌似没有找到空心的资源,所以手动画之。。。 if(style==5){ g1.setColor(Color.GRAY); g1.fillRect(x, y, width, heigth); return; } //其他类型的图片都在资源文件里面,这里只需要根据不同的style(地板类型)、donghua(地板的动画状态)来获取需要画的图片即可 try { img = ImageIO .read(new File(res.get(String.valueOf(style)).get(String.valueOf(donghua)))); } catch (IOException e) { } g1.drawImage(img, x, y, null); }
每次画地板的时候根据地板的不同style(地板类型)、donghua(地板的动画状态)来获取需要画的图片,然后画到画布上。
public void update() { //每次更新,获取到动画的状态,status计数器自增,statusNum为状态总数 status = status + 1; //取模获取动画的循环状态 donghua=status%statusNum; if(donghua==0){ status=0; } }
每次获取不同的动画状态,供上面的方法在获取图片的时候获取不同的图片。
玩家类:
public void drawMan(Graphics g) { //角色与顶端的碰撞处理 if (y < 40 || y > 400 || health <= 0) { isDie = true; } if (!isDie) { for (int i = 0; i < game.floorList.size(); i++) { this.isManOnFloor = false; //初始化角色悬空 floor1 = (NFloor) game.floorList.get(i); //判断角色是否在地板上以及处理 if (x >= floor1.getX() - MAN_WIDTH && x < floor1.getX() + floor1.width && y >= floor1.getY() - MAN_HEIGHT && y < floor1.getY() - MAN_HEIGHT + floor1.heigth) { this.isManOnFloor = true; floor = floor1.getNum(); ySpeed = floor1.getSpeed(); y = floor1.getY() - MAN_HEIGHT; break; } } //当人物落在地板上时对应的处理 if (isManOnFloor) { if (floor1 != null) { switch (floor1.getStyle()) { //判断落在的地板时什么类型,以及相应的处理方式 case 0: //在刺板上,掉血一次 if (!onDingChi) { health -= 20; } onDingChi = true; break; case 1: //左移动的板子,用户的速度左 xFloorSpeed = -1; break; case 2: //右移动的板子,用户的速度右 xFloorSpeed = 1; break; case 3: //石头板子,不做处理 break; case 4: //跳板,让用户跳一下(板子上升太快,人下落太快,于是乎,出现狂跳现象) y -= 20; break; case 5: //空心板,害人了。。把用户正常往下掉。 y = y + floor1.heigth; break; } } } else { //如果不是在板子上,清空板子上的状态 ySpeed = -4; xFloorSpeed = 0; onDingChi = false; } try { img = ImageIO.read(new File("Resources/ManStand.PNG")); } catch (IOException e) { } //左右墙的处理 if (x >= 300 - MAN_WIDTH) { x = 300 - MAN_WIDTH; } if (x <= 0) { x = 0; } g.drawImage(img, x = x + xSpeed + xFloorSpeed, y = y - ySpeed, null); } }
在画玩家的时候会判断玩家1.是否还活着(没碰壁,血不为0) 2.玩家是否在地板上(在地板上根据不同的地板做出不同的人的坐标和速度的变化) 3.画人(根据坐标和速度来画)
好了到这里一个下100层的游戏已经搞定了。其实很简单,一个游戏就是不同的始终周期改变用户的位置,然后根据不同位置做出不同逻辑的判断。
好了就到这里吧。下次放出一个小的效验码破解的示例
最后附上源码