【Java】移动图片

一、移动过程分析

问题:你觉得当你按了向上的按钮后,它该怎么移动才是一个合理的业务逻辑呢?

image-20240420181447494

正常的业务逻辑应该是将空白的图片往下方移动。

pygry-v7qz9

在这我们就可以得出一个结论:向上移动实际上就是把空白方块下方的图片网上移动。

逻辑我们知道了,那代码该如何实现呢?

我们知道,在代码中,每一张小图片其实都跟一个唯一的数字对应,而这些数字都是存放在二维数组中的。

因此我们只需要找到 013 在二维数组中的位置就行了,然后再做一个数据的交换。

image-20240420182023240

现在,我又要问大家了:你觉得 013 在二维数组中的索引分别是多少呢?

正确答案是:0 —— (1, 1)13 —— (2, 1)

因此,网上移动的本质其实就是:将 13 复制给 (1, 1) 位置,然后再把 (2, 1) 位置变成 0 就行了。

image-20240420182351708

交换完成后,根据数字去添加对应的图片就行了,这就是向上移动。

为了让大家更好的理解数字在二维数组中的位置,下图给每个数字都做了索引标记。

image-20240420182508363


二、给界面添加点击事件

找到 GameJFrame类(游戏的主界面),首先我们要做的其实并不是 上下左右移动,而是需要先给整个界面添加一个键盘监听事件。

因为我们是在整个游戏界面中按 上下左右键 的时候才会去移动图片。

我们让 GameJFrame类implements KeyListener,然后重写里面所有的抽象方法。

image-20240420190738532

此时我们需要找到 initJFrame() 初始化界面的方法,在方法的最后,我们需要去给整个界面去添加 键盘监听事件

当事件被触发时,需要执行 this(即本类) 中的代码。

// 给整个界面添加键盘监听事件
this.addKeyListener(this);

现在我要在游戏中 上下左右移动,既然是移动,那一定会跟这里的 0 发生关系。

image-20240420191525291

向上移动:将 0 下面的图片往上移动。

向下移动:将 0 上面的图片往下移动。

不管你怎么移,你都得先知道 0 这个位置在哪才可以,因此我们需要先去统计一下 0 所在的位置。

这个应该在 initData()(打乱数据) 方法中统计:在将一维数组的值添加到二维数组中时,就可以来做一个 if判断

如果你当前遍历到的位置 为0 的话,此时,就应该将 0 的位置记录下来。

记录 0 的位置我可以在方法前面定义两个变量 x 和 yx 和 y 就表示 0 在二维数组中的位置。

如果你不为 0,我才把数字添加到二维数组中。

// 记录空白方块在二维数组中的位置
int x = 0;
int y = 0;

// 初始化数据(打乱)
private void initData() {
    //5.给二维数组添加数据
    //遍历一维数组tempArr得到每一个元素,把每一个元素依次添加到二维数组当中
    for (int i = 0; i < tempArr.length; i++) {
        if (tempArr[i] == 0) {
            x = i / 4;
            y = i % 4;
        } else { // 如果你不为 `0`,我才把数字添加到二维数组中
            data[i / 4][i % 4] = tempArr[i];
        }
    }
}

PS:建议将成员变量都定义在最上面

它是没有上下顺序的,没有规定谁一定要写在上面。只不过我们平时在写代码的时候,为了方便阅读,一般来讲都是把它们写在最上面的。

image-20240420192545239

接着,找到 keyReleased() 重写方法,在这个方法中我们才能对 上、下、左、右 进行判断。

向左的按钮 对应的数字是 37向上的按钮 所对应的数字是 38向右的按钮 所对应的数字是 39向下的按钮 所对应的数字是 40

那这个是怎么知道的呢?难不成我要背吗? —— 不需要

在这只需要将 code值 打印出来就行了。

@Override
public void keyReleased(KeyEvent e) {
    int code = e.getKeyCode();
    System.out.println(code);
}

然后打开 App 右键运行,然后在整个界面中去按 上下左右,就会出现如下数字。

image-20240420194421742

因此这个数字不需要去记,以后要用了打印一下即可。

这些数字其实是有规律的:从左开始,左上右下分别是37、38、39、40。

接下来,针对于这四种情况,就要来写四个判断。

@Override
public void keyReleased(KeyEvent e) {
    int code = e.getKeyCode();
    if (code == 37) {
        System.out.println("向左移动");
    } else if (code == 38) {
        System.out.println("向上移动");
    } else if (code == 39) {
        System.out.println("向右移动");
    } else if (code == 40) {
        System.out.println("向下移动");
    }
}

然后打开 App 右键运行,然后在整个界面中去按 左上右下,就会出现如下文字。

image-20240420194836429

因此接下来我们只需要将判断里面的代码去改写一下就OK了。

首先我们先来写向上移动,因为向上移动它比较好理解。


三、向上移动

向上移动实际的业务逻辑应该是:把空白方块下方的数字网上移动。

我们已经知道了 x、y 就表示 空白方块,那空白方块下面的,我该怎么表示呢? —— (x + 1, y)

因此在代码中,我们只需要将 (x, y)(x + 1, y) 两个位置的数据来做一个交换就行了。

最后调用方法,按照最新的数字去加载图片即可。

@Override
public void keyReleased(KeyEvent e) {
    int code = e.getKeyCode();
    if (code == 37) {
        System.out.println("向左移动");
    } else if (code == 38) {
        System.out.println("向上移动");
        // 把空白方块下方的数字赋值给空白方块
        // PS:这里不用使用到临时变量
        // int tmp = data[x][y];
        data[x][y] = data[x + 1][y];
        // data[x + 1][y] = tmp;
        data[x + 1][y] = 0;
        x++;
        // 调用方法按照最新的数字加载图片
        initImage();
    } else if (code == 39) {
        System.out.println("向右移动");
    } else if (code == 40) {
        System.out.println("向下移动");
    }
}

代码写完后,打开 App 右键运行,会发现,当你按了 向上的键 后,事件被触发了,但是游戏界面并没有动,这是为什么呢?

这就是我们最后的一个小细节:在 initImage() 方法中,我们还要做一个操作。

在一开始,我们应该清楚原本已经出现的所有图片,并且在方法的最后,需要去刷新一下界面。

// 初始化图片
private void initImage() {
    // 情况原本已经出现的所有图片
    this.getContentPane().removeAll();

    // 之前的代码
    ...............

    // 刷新一下界面
    this.getContentPane().repaint();
}

代码写完后,打开 App 右键运行,然后按住 上键 ,可以发现图片已经上下移动了。

bue4w-owkw3

四、将 下、左、右 三个方向做一个代码实现

向下移动 的逻辑:跟向上移动的逻辑是反过来的,它其实就是把空白方块上方的图片往下移动。

向左移动 的逻辑:将 0 右边的图片向左移动。

向右移动 的逻辑:将 0 左边的图片向右移动。

//松开按键的时候会调用这个方法
@Override
public void keyReleased(KeyEvent e) {
    //对上,下,左,右进行判断
    //左:37 上:38 右:39 下:40
    int code = e.getKeyCode();
    System.out.println(code);
    if (code == 37) {
        System.out.println("向左移动");
        //逻辑:
        //把空白方块右方的数字往左移动
        data[x][y] = data[x][y + 1];
        data[x][y + 1] = 0;
        y++;
        //调用方法按照最新的数字加载图片
        initImage();
    } else if (code == 38) {
        System.out.println("向上移动");
        //逻辑:
        //把空白方块下方的数字赋值给空白方块
        data[x][y] = data[x + 1][y];
        data[x + 1][y] = 0;
        x++;
        //调用方法按照最新的数字加载图片
        initImage();
    } else if (code == 39) {
        System.out.println("向右移动");
        //逻辑:
        //把空白方块左方的数字往右移动
        data[x][y] = data[x][y - 1];
        data[x][y - 1] = 0;
        y--;
        //调用方法按照最新的数字加载图片
        initImage();
    } else if (code == 40) {
        System.out.println("向下移动");
        //逻辑:
        //把空白方块上方的数字往下移动
        data[x][y] = data[x - 1][y];
        data[x - 1][y] = 0;
        x--;
        //调用方法按照最新的数字加载图片
        initImage();
    }
}

代码写完后,打开 App 右键运行,此时我们就可以完整的去玩一下这个游戏了。


五、优化代码

空白的格子 在最下面时,此时我再去按 上键,程序虽然没有崩,但是在控制台就报异常了。

image-20240420201653624

"AWT-EventQueue-0" 里面出现了一个叫 ArrayIndexOutOfBoundsException:数组的索引越界异常。

这些异常我们该如何看呢?其实有一个小技巧,我们可以看前面的包名,从第三行开始的代码,包名都是 java.desktop/java.awt.Component..... 包下的,也就是说下面的这些代码不是我们写的,而是Java写的,Java那些大佬他们也有可能会发生错误,但是不会有这么低级的问题。因此肯定是我们写的代码出问题了。

来看第二行我们自己写的代码的包名:com.itheima.ui 包下的 GameJFramekeyReleased() 方法中的 191行 出问题了。

Exception in thread "AWT-EventQueue-0" java.lang.ArrayIndexOutOfBoundsException: Index 4 out of bounds for length 4
	at com.itheima.ui.GameJFrame.keyReleased(GameJFrame.java:191)
	at java.desktop/java.awt.Component.processKeyEvent(Component.java:6587)
	at java.desktop/java.awt.Component.processEvent(Component.java:6403)
	at java.desktop/java.awt.Container.processEvent(Container.java:2266)
	at java.desktop/java.awt.Window.processEvent(Window.java:2056)
	at java.desktop/java.awt.Component.dispatchEventImpl(Component.java:5001)
	at java.desktop/java.awt.Container.dispatchEventImpl(Container.java:2324)
	at java.desktop/java.awt.Window.dispatchEventImpl(Window.java:2780)
	at java.desktop/java.awt.Component.dispatchEvent(Component.java:4833)

点击一下出问题的蓝色字体,它就会自动跳到出问题的行。

image-20240420202233219

你需要想:为什么会出问题呢?

假设现在的空白区域是最下方:(3, 0), (3, 1), (3, 2),如果在这种情况下,x 继续 + 1 时,数组就超出索引了。

因此在上面我们需要对它的边界情况做一些判断:如果超出索引了,就需要直接 return

以向上移动为例:if(x == 3) return :表示空白方块已经在最下方了,他的下面没有图片再能移动了

//松开按键的时候会调用这个方法
@Override
public void keyReleased(KeyEvent e) {
    //对上,下,左,右进行判断
    //左:37 上:38 右:39 下:40
    int code = e.getKeyCode();
    System.out.println(code);
    if (code == 37) {
        System.out.println("向左移动");
        if(y == 3){
            return;
        }
        //逻辑:
        //把空白方块右方的数字往左移动
        data[x][y] = data[x][y + 1];
        data[x][y + 1] = 0;
        y++;
        //调用方法按照最新的数字加载图片
        initImage();
    } else if (code == 38) {
        System.out.println("向上移动");
        if(x == 3){
            //表示空白方块已经在最下方了,他的下面没有图片再能移动了
            return;
        }
        //逻辑:
        //把空白方块下方的数字往上移动
        //x,y  表示空白方块
        //x + 1, y 表示空白方块下方的数字
        //把空白方块下方的数字赋值给空白方块
        data[x][y] = data[x + 1][y];
        data[x + 1][y] = 0;
        x++;
        //调用方法按照最新的数字加载图片
        initImage();
    } else if (code == 39) {
        System.out.println("向右移动");
        if(y == 0){
            return;
        }
        //逻辑:
        //把空白方块左方的数字往右移动
        data[x][y] = data[x][y - 1];
        data[x][y - 1] = 0;
        y--;
        //调用方法按照最新的数字加载图片
        initImage();
    } else if (code == 40) {
        System.out.println("向下移动");
        if(x == 0){
            return;
        }
        //逻辑:
        //把空白方块上方的数字往下移动
        data[x][y] = data[x - 1][y];
        data[x - 1][y] = 0;
        x--;
        //调用方法按照最新的数字加载图片
        initImage();
    }
}

代码写完后,打开 App 右键运行,一直按 向下 或者其他的方向键,在控制台也不会出现报错信息了。


六、总结

在刚刚,我们是这么做的

1、让本类实现 KeyListener接口,并重写所有的抽象方法

2、给整个游戏界面添加键盘监听事件

因为我们是在游戏界面中按 上下左右键 去移动数字方块的。

3、在移动的时候,其实就是把空白对应的数字 0 跟它 上下左右 的数字进行一个交换

首先我们需要来统计一下空白方块对应的数字 0 在二维数组中的位置。

4、知道了位置后,就可以在 keyReleased() 重写方法中实现移动的逻辑

说到这里有同学就会想:keyReleased() 是按键抬起来才会触发的,那我能不能在 keyPressed() (按下) 的方法中写呢?

其实也可以,但是如果你按下但不松开,它就会反复去调用 keyPressed() 方法,这不是我想要的,我需要的一个是:按一下就移一次,因此我们应该在 keyReleased()(抬起来) 的方法中去实现 上下左右 移动的业务逻辑。

5、Bug修复:

  • 当空白方块在最下面的时候,它是无法上移的
  • 当空白方块在最上面的时候,它是无法下移的
  • 当空白方块在最左面的时候,它是无法右移的
  • 当空白方块在最右面的时候,它是无法左移的
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值