房子到期了,刚搬家,网还没转过去,回去之后一个人只能看看广告,不想被电视台QJ,于是就在公司赖着蹭网、蹭空调。实在太无聊,刚好昨天和@zhzhxtrrk聊到了移动设备上的游戏开发,据说在国外美刀赚很凶的,于是乎就写个玩玩呗,度过漫漫长夜。。。
说实话不想装sdk,于是就java上了,上网down素材。。。。然后开始动工。
先简单介绍下游戏的原理吧。
一、重写jpanel的print方法,在这个方法里面做的事情:1.画障碍物(顶部的尖刺) 2.画地板队列中的所有地板 3.画人 4.画图血条等提示信息
二、新建一个线程,这个线程不停的循环,在这个线程中做的事情:1.更新地板的位置 2.随即生成新的地板 3.更新地板动画 4.删除已经在窗口外面的地板
每次循环休眠一段时间,每个循环是一个时钟周期,不同的时钟周期做不同的事情(35个时钟周期生成一个地板,7个时钟周期更新一次地板动画)
效果图:
核心代码剖析:
view plain copy to clipboard print ?
-
- 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 );
- }
//重写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); }
在画图方法中不需要关注任何信息,只需要获取需要画图的对象,然后调用对象的画图方法即可。这里图片的尖刺是画出来的,如果能在底部图上把这些直接画上也是可以的。这样做也有好处,我们可以在画布的四周全部布满尖刺,如果碰到尖刺就死亡,这个也是可以的。
view plain copy to clipboard print ?
- 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));
- }
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的取值原理
view plain copy to clipboard print ?
-
- public void run() {
- int count = 0 ;
- int num = 0 ;
- while ( true ) {
- if (!man.isDie()) {
-
- for (NFloor floor : floorList) {
- floor.setY(floor.getY() - floor.getSpeed());
- }
-
-
-
- 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();
- }
- }
- }
//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()把更新完成的数据重新在画布上画出来。
地板类:
view plain copy to clipboard print ?
- public void drawFloor(Graphics g1) {
-
- if (style== 5 ){
- g1.setColor(Color.GRAY);
- g1.fillRect(x, y, width, heigth);
- return ;
- }
-
-
- try {
- img = ImageIO
- .read(new File(res.get(String.valueOf(style)).get(String.valueOf(donghua))));
- } catch (IOException e) {
- }
-
- g1.drawImage(img, x, y, null );
- }
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(地板的动画状态)来获取需要画的图片,然后画到画布上。
view plain copy to clipboard print ?
- public void update() {
-
- status = status + 1 ;
-
- donghua=status%statusNum;
- if (donghua== 0 ){
- status=0 ;
- }
- }
public void update() { //每次更新,获取到动画的状态,status计数器自增,statusNum为状态总数 status = status + 1; //取模获取动画的循环状态 donghua=status%statusNum; if(donghua==0){ status=0; } }
每次获取不同的动画状态,供上面的方法在获取图片的时候获取不同的图片。
玩家类:
view plain copy to clipboard print ?
- 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 );
- }
- }
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层的游戏已经搞定了。其实很简单,一个游戏就是不同的始终周期改变用户的位置,然后根据不同位置做出不同逻辑的判断。
好了就到这里吧。下次放出一个小的效验码破解的示例
最后附上源码