Java基本ステージプロジェクト - ジグソーパズルゲーム(解説・ソースコード含む)

声明

このプロジェクトはJavaをベースにしたステージプロジェクトです. このプロジェクトには, 基本的な文法, ジャッジメント, ループ, 配列, 文字列, コレクションなどの文法基礎などのオブジェクト指向の知識; カプセル化, 継承, ポリモーフィズムなどのオブジェクト指向が含まれます. 、抽象化 クラス、インターフェース、内部クラスなど...すべてが関係しています。このプロジェクトには多くのコンテンツが含まれます。初心者として、以前の知識をうまく組み合わせることができます。このプロジェクトは、これまでの基礎知識を実践および復習するのに適しています。

ゲームショー

ここに画像の説明を挿入ここに画像の説明を挿入

資料取得

注: この資料の各写真は 15 の部分に分割され、各写真は正方形で、縦横は 105 ピクセルです
リンク: https://pan.baidu.com/s/16maOd105xKqrWZSd6IScig?pwd=1234
抽出コード: 1234

ゲームウィンドウ

ゲームのウィンドウは主に Java が提供する JFrame クラスを使用します. このクラスはウィンドウを提供することができます. このウィンドウのサイズ, 位置, 表示するかどうかなどを設定できます. 以下はそのウィンドウの外観です.インターフェイス. 私たちの背後にあるすべてのビジネス
ここに画像の説明を挿入
ロジックはこのウィンドウに実装する必要があります. 以下は実装されたコードです:

JFrame jf = new JFrame();
jf.setSize(400, 400);
jf.setVisible(true);

JFrame は Java が提供するクラスなので、JFrame を直接使用してオブジェクトを作成できます.その中の setSize() メソッドは、ウィンドウのサイズを設定するもので、単位はピクセルです.ここで注意する必要があるのは、サイズを設定した後に実行することはできません 表示されます。JFrame はデフォルトでインターフェイス ウィンドウを非表示にします。表示するには setVisible() メソッドを使用する必要があります。true は表示し、それ以外の場合は false はピリオドを非表示にします

ゲームでは、ゲーム インターフェース、ログイン インターフェース、登録インターフェースの 3 つのインターフェースを表示する必要があります。

3 つの JFrame オブジェクトを作成する必要がありますが、ここで 1 つの質問について考える必要があります: 新しいクラス ファイルを作成し、メイン メソッドで 3 つの JFrame オブジェクトを作成し、メイン メソッドですべてのビジネス ロジックを記述しますか?結果は NO 、ビッグ NO、特別な NO、それまでに 1 つの main メソッドに 1000 行を超えるコードが記述され、それまでにバグが発生し、どこにあるのかわかりません。そこで、ゲーム インターフェース、ログイン インターフェース、登録インターフェースの 3 つのカテゴリを提案します. 次に、新しいクラスを作成し、メイン メソッドを使用してゲームを実行します. メイン メソッドでは、設定などの Call メソッド用の 3 つのウィンドウ クラス オブジェクトを作成します。ウィンドウサイズなど。このようにして、そのウィンドウでビジネス ロジックを実行する必要がある場合は、対応するクラスに移動するだけで実行できます。一般的な考え方は次のとおりです。
これは、ゲーム プログラムの実行エントリです。

import ui.GameFrame;
import ui.LoginJFrame;
import ui.RegisterJFrame;

public class App {
    
    
    public static void main(String[] args) {
    
    
        //表示程序的启动入口
        new GameFrame();

        new LoginJFrame();

        new RegisterJFrame();
    }
}

以下は GameFrame (ゲーム インターフェイス) クラスです.
ここでは空のパラメーター構造体を使用してウィンドウ サイズと表示インターフェイスを設定します. メイン メソッドで GameFrame オブジェクトを作成するときに、GameFrame の空のパラメーター構造体を呼び出して設定します.メインメソッドで設定する必要がないように、インターフェース

package ui;
import javax.swing.*;

public class GameFrame extends JFrame {
    
    

    public GameFrame(){
    
    
        this.setSize(603, 680);
        this.setVisible(true);
    }
}

以下は LoginFrame (ログイン インターフェイス) クラスで、
GameFrame (ゲーム インターフェイス) クラスと同じです。

package ui;
import javax.swing.*;
public class LoginJFrame extends JFrame {
    
    

    public LoginJFrame(){
    
    
    
        this.setSize(488, 430);
        this.setVisible(true);
    }
}

以下はRegisterFrame(登録インターフェース)クラスで、
考え方はGameFrame(ゲームインターフェース)クラスと同じです。

package ui;
import javax.swing.*;
public class RegisterJFrame extends JFrame {
    
    

    public RegisterJFrame(){
    
    
        this.setSize(488, 500);
        this.setVisible(true);
    }
}

一般的なフレームワークが構築された後は、対応するクラスに対応するビジネス ロジックを構築するだけで済み、背後にあるほとんどすべてのビジネス ロジックはこれら 3 つのウィンドウで実装されます。

ウィンドウ設定

ゲーム ウィンドウを表示したら、それに応じてゲーム ウィンドウを設定し、より良いゲーム体験と視覚効果を提供する必要があります.次に、ゲーム インターフェイス用に次の関数とコードを設定する必要があります: 1. ゲーム
タイトル
ゲームタイトルは私たちのゲームの名前で、通常はゲーム インターフェイスの左上に書かれています。効果は下の図に示すとおりです。タイトル設定は
ここに画像の説明を挿入
JFrame のメソッドを使用します。コードは次のとおりです。

//设置界面的标题
this.setTitle("拼图游戏单机版 V1.0");

2. ゲームのメイン インターフェイスの上部に固執する機能は、
すべての人によく理解されている必要があります。つまり、他のソフトウェアを開いたときに、上部に固執することで、ここでは JFrame メソッドで 1 つを使用します。

//设置界面置顶
//盖住其他所有软件
this.setAlwaysOnTop(true);

3. ゲームのメイン インターフェイスの中心
ゲーム インターフェイスの中心とは、ゲームを開くと、インターフェイスが常にコンピューター画面の中央に表示されることを意味します。

//设置界面居中
this.setLocationRelativeTo(null);

このメソッドはコンポーネント パラメータを渡す必要があるため、ここでは null を入力するだけです。
4. ゲームのシャットダウン モードを設定する ゲームの
シャットダウン モードでは、JFrame がシャットダウン メソッドを提供します

//设置游戏的关闭模式
this.setDefaultCloseOperation(3);

このメソッドは、int 型のパラメータを渡すことができます。このメソッドには、数値 0、1、2、および 3 に対応する 4 つの終了モードがあります。4 つの終了モードは、このメソッドのソース コードを入力することで表示できます。ここでは、次のことだけを説明します。番号 3 はシャットダウン モードに対応します: ウィンドウの 1 つを閉じると、仮想マシンの実行が停止します. ゲーム ウィンドウの 1 つを閉じると、他のログイン ウィンドウと登録ウィンドウが閉じられることが理解できます。となり、仮想マシンは実行を停止します

注: これは、これが配置されているクラスを表します。JFrame から 3 つのウィンドウ クラスを継承しているため、これを直接使用して、親クラスのメソッドを呼び出すことができます。

メニュー構成

ゲームには欠かせないメニューです、リゲーム、リログインなどがメニューに表示されます、下図はゲームメニューのスタイルです このようなメニューを作る前に、まずはJavaを理解する必要があり
ここに画像の説明を挿入
ますJMenuBar クラス、JMenu クラス、JMenuItem クラスが提供されているので、これら 3 つのタイプの関係を理解できるように、以下のいくつかの図を使用します これら 3 つのタイプを理解したら、これら 3 つのタイプを接続して GameFrame に配置する必要があります
ここに画像の説明を挿入
ここに画像の説明を挿入
。実装手順:
1. 最初に JMenuBar を作成します
。 2. 次に JMenu を作成します
。 3. 次に JMenuItem を作成します。
4. JMenuItem を JMenu
に配置します。
5. JMenu を JMenuBar に配置します。

コードの実装は次のとおりです。

	//初始化菜单
    public void initJMenuBar(){
    
    
        //创建整个的菜单对象
        JMenuBar jMenuBar = new JMenuBar();

        //创建菜单上面的两个选项的对象(功能  关于我们)
        JMenu functionJMenu = new JMenu("功能");
        JMenu aboutJMenu = new JMenu("关于我们");

        //创建选项下面的条目对象
        JMenuItem replayItem = new JMenuItem("重新游戏");
        JMenuItem reLoginItem = new JMenuItem("重新登录");
        JMenuItem closeItem = new JMenuItem("关闭游戏");

        JMenuItem accountItem = new JMenuItem("公众号");

        //将每一个选项下的条目添加到选项当中
        functionJMenu.add(replayItem);
        functionJMenu.add(reLoginItem);
        functionJMenu.add(closeItem);

        aboutJMenu.add(accountItem);

        //将菜单里面的两个选项添加到菜单中
        jMenuBar.add(functionJMenu);
        jMenuBar.add(aboutJMenu);


        //给整个界面设置菜单
        this.setJMenuBar(jMenuBar);
    }

ここでは、初期化メニューをメソッドとして作成し、それを GameFrame の空のパラメーター構造体で直接呼び出して、次の効果を実現しています: 想像力を働かせて、メニュー バーに他のメニューを追加することもできます. 以下は、
ここに画像の説明を挿入
構造
と現在の GameFrame クラスのコード

public class GameFrame extends JFrame {
    
    

    public GameFrame(){
    
    

        //初始化界面
        initJFrame();

        //初始化菜单
        initJMenuBar();

        //让界面显示出来,建议放到最后
        this.setVisible(true);
    }

    //初始化界面
    public void initJFrame(){
    
    
        //设置界面的宽高
        this.setSize(603, 680);

        //设置界面的标题
        this.setTitle("拼图游戏单机版 V1.0");

        //设置界面置顶
        //盖住其他所有软件
        this.setAlwaysOnTop(true);

        //设置界面居中
        this.setLocationRelativeTo(null);

        //设置游戏的关闭模式
        this.setDefaultCloseOperation(3);
    }


    //初始化菜单
    public void initJMenuBar(){
    
    
        //创建整个的菜单对象
        JMenuBar jMenuBar = new JMenuBar();

        //创建菜单上面的两个选项的对象(功能  关于我们)
        JMenu functionJMenu = new JMenu("功能");
        JMenu aboutJMenu = new JMenu("关于我们");

        //创建选项下面的条目对象
        JMenuItem replayItem = new JMenuItem("重新游戏");
        JMenuItem reLoginItem = new JMenuItem("重新登录");
        JMenuItem closeItem = new JMenuItem("关闭游戏");

        JMenuItem accountItem = new JMenuItem("公众号");

        //将每一个选项下的条目添加到选项当中
        functionJMenu.add(replayItem);
        functionJMenu.add(reLoginItem);
        functionJMenu.add(closeItem);

        aboutJMenu.add(accountItem);

        //将菜单里面的两个选项添加到菜单中
        jMenuBar.add(functionJMenu);
        jMenuBar.add(aboutJMenu);


        //给整个界面设置菜单
        this.setJMenuBar(jMenuBar);
    }
}

写真を追加

ウィンドウとメニューが大まかに構築されたら、メイン ウィンドウに写真を追加してみましょう. ゲーム ウィンドウは GameFrame ウィンドウにあるため、後続のビジネス ロジックも GameFrame クラスで完了します. 写真を追加する前に
、ゲームウィンドウを理解する
ここに画像の説明を挿入
ここのゲームウィンドウは、実際には 3 つの部分に分かれています
1. タイトル部分
2. メニュー部分
3. 隠しコンテナ部分
ここの隠しコンテナ部分は、JFrame オブジェクトを作成するときに既に存在するので、必要ありません。 to recreate the object, we can just use this.getContentPane() method to call it, and then use it to call the add() method to add pictures to it. 特別な要件がない場合は、ポジティブに配置されます中央では、別の場所に配置する場合は、デフォルトのスイッチをオフにする必要があります. オフにするには、次の方法を使用できます.

//取消默认的居中放置,只有取消了才会按照XY轴的形式添加组件
this.setLayout(null);

非表示のコンテナは JFrame オブジェクトの作成時に生成されるため、初期化インターフェイスである initFrame() でこの setLayout メソッドを使用できます。後で、デフォルトで中央に配置することなく、XY 軸の非表示のコンテナに画像を配置できます。

非表示のコンテナのデフォルトのセンタリング方法をキャンセルした後、次に画像を追加できます

JFrame に画像を追加するには、単に画像アドレスを直接 JFrame に入れるのではなく、ImageIcon および JLabel クラスを使用します. 以下は
実装手順
1 です. まず、配置したい画像に相当する ImageIcon オブジェクトを作成する必要があります.渡されるパラメータは、画像アドレスにすることができます
2. 画像である ImageIcon オブジェクトを格納する JLabel クラスを作成します. この JLabel は、画像やテキストなどを格納できる管理コンテナ コンテナです. 3. JLabel によって作成されたオブジェクトを使用します
.画像を指定するクラス 位置、つまり XY 軸。ここでは JLabel の setBounds メソッドを使用します。ここで渡すパラメーターは (x、y、幅、高さ)、つまり XY 軸と幅と画像の高さが設定されており、ここでの XY 軸は直角ではありません 座標系の場合、原点は JFrame ウィンドウの左上隅にあります. 次の図は、理解を深めることができます 4. 最後に
ここに画像の説明を挿入
、 JFrame の隠しコンテナに JLabel によって作成されたオブジェクト

以下のコードを使用して、上記の機能を実現します

    //初始化图片
    public void initImage(){
    
    

        //创建一个图片ImageIcon对象
        ImageIcon icon = new ImageIcon("D:\\Java Code\\puzzlegame\\image\\animal\\animal3\\1.jpg");
        //创建一个JLabel的对象(管理容器)
        JLabel jLabel = new JLabel(icon);
        //指定图片位置
        jLabel.setBounds(0, 0 , 105, 105);
        //把管理容器添加到界面中
        this.getContentPane().add(jLabel);
    }

ここでは、GameFrame クラスにメソッドを作成し、それを GameFrame クラスの空のパラメーター構築で呼び出しました。

こう書くとこんな感じになるはず
ここに画像の説明を挿入
.setBoundsメソッドで設定するパラメータはx=0,y=0なので原点に配置する.ここで座標に注意する必要がある.また、画像の座標は、画像の左上隅に対応する座標. 明らかに、結果は正しい.

JFrame に画像を追加する方法がわかったら、他の画像を追加しましょう

私はあなたが考えていることを知っています, やめて!!!, あなたは本当に上記のコードを何十回も書いてすべての画像を追加したくありませんよね? 答えはノーです!!! 本当に試してみたい
なら
, 3枚の写真を追加するとき、この時点でコードを見て、何かルールを見つけますか? 誰もが見やすいように、3枚の写真を追加した後のコードは次のとおりです

        //创建一个JLabel的对象(管理容器)放入ImageIcon对象
        JLabel jLabel1 = new JLabel(new ImageIcon("D:\\Java Code\\puzzlegame\\image\\animal\\animal3\\1.jpg"));
        //指定图片位置
        jLabel1.setBounds(0, 0, 105, 105);
        //把管理容器添加到界面中
        this.getContentPane().add(jLabel1);

        //创建一个JLabel的对象(管理容器)放入ImageIcon对象
        JLabel jLabel2 = new JLabel(new ImageIcon("D:\\Java Code\\puzzlegame\\image\\animal\\animal3\\2.jpg"));
        //指定图片位置
        jLabel2.setBounds(105, 0, 105, 105);
        //把管理容器添加到界面中
        this.getContentPane().add(jLabel2);

        //创建一个JLabel的对象(管理容器)放入ImageIcon对象
        JLabel jLabel3 = new JLabel(new ImageIcon("D:\\Java Code\\puzzlegame\\image\\animal\\animal3\\3.jpg"));
        //指定图片位置
        jLabel3.setBounds(210, 0, 105, 105);
        //把管理容器添加到界面中
        this.getContentPane().add(jLabel3);

ここでは便宜上、作成した ImageIcon オブジェクトを JLabel オブジェクト作成のパラメーターに直接入れています。

ここで、これら 3 つのコードを注意深く観察し、ルールを見つけたかどうかを教えてください?
画像が追加されるたびに、画像の位置と画像のアドレスのみが変更されていることがわかります。つまり、画像の各行の x 軸は 1 つずつ追加されます。つまり、最初の行の最初の x は 0、2 番目の x は 105 です。 、3 番目は 210... など、最初の行の n 番目の Zhang の x は 105 * (n - 1) であり、y は常に 0 です; 2 行目は、最初の行と同様に、y は 105 になり、 3 行目の y は 210... というように、最初の行 n 行の y は 105 * (n - 1) です。

上記のアイデアで、ループを考える必要があります。4 つの行と 4 つの列があるため、行で 4 回ループし、次に列で 4 回ループすることができるため、ループのネスト、外側のループ、内側のループを使用できます。画像を追加するためのコードのデモは次のとおりです。

    public void initImage(){
    
    

        int number = 1;
        //外循环 --- 四行
        for (int i = 0; i < 4; i++) {
    
    
            //内循环 --- 一行的四张图片
            for (int j = 0; j < 4; j++) {
    
    
                //创建一个图片ImageIcon对象
                //创建一个JLabel的对象(管理容器)放入ImageIcon对象
                JLabel jLabel = new JLabel(new ImageIcon("D:\\Java Code\\puzzlegame\\image\\animal\\animal3\\" + number + ".jpg"));
                //指定图片位置
                jLabel.setBounds(105 * j, 105 * i, 105, 105);
                //把管理容器添加到界面中
                this.getContentPane().add(jLabel);

                //number自增表示图片变化
                number++;
            }
        }
    }

この関数を GameFrame クラスの initImage() メソッドに記述し、空のパラメーター構築で呼び出しました。

上記のコードでわかるように、外側のループは行数を表すために使用され、内側のループは列数を表すために使用されます. このネストされたループは、ループを開始すると i = 0 が表すことを大まかに意味します. j は列の数を表し、j は列の数が徐々に増加していることを意味します. setBounds メソッドで示されている x は徐々に増加しており、105 まで徐々に増加しており、y はちょうど 0 です。 , これはちょうど私たちの画像配置ルールを満たしています. 画像の最初の行が追加された後, i は自動的にインクリメントは 2 番目の行を意味し、次に内側のループを実行し、2 番目の行に 4 つの画像を追加します.写真で追加されます

数字の意味を尋ねる人もいますが、ここでもう一度言いたいのですが、写真の名前を追加するのに便利なように、次の形式で設定しようとしています 注: ここでは、写真を 15 の部分に分割し
、 JFrame,
ここに画像の説明を挿入
ここに画像全体を入れることはできません. ImageIcon オブジェクトを作成してアドレスを渡すと、数値を使用して徐々に増加してすべての画像を表し、すべての画像をコンテナーに追加して、
アドレス値として使用される数字の意味を理解し、対応する画像をコンテナに追加します。数字をアドレスに書き込むときは、「+number+」と書くことを忘れないでください。数字を直接書き込まないでください。すると次のような結果が得られます.
ここに画像の説明を挿入
画像を 15 分割したので最後の画像が欠けています. i = 3, j = 3 までループを実行すると, 該当する画像がなく, 空白の画像が表示されます.画像を移動する空白の位置

写真の順番をシャッフルする

JFrame に写真を追加する方法がわかったので、次は写真の順序をごちゃまぜにする必要があります. 結局、これはジグソー パズル ゲームであり、それ自体が写真の順序をごちゃまぜにするものです.移動して元に戻します. 絵の順序をごちゃまぜにしたい場合は
, int型の数値変数を使って連続的に増やしていき, 一枚ずつ絵を入れていきます. もちろん, 絵の名前は定期的に設定する必要があります.私たちの絵はnumberの値に応じて加算され、numberの値は1から16まで順番に加算されるので、数字の順番を崩すと絵の順番を崩すことはできますか?次にやってみます

1 から 16 の順序を乱すには、長さが 16 の 1 次元配列を設定し、インデックスを交換して順序を乱すのが最も簡単な方法です。

		//创建一个一维数组
        int[] Arr = {
    
    0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};

        Random r = new Random();
        //遍历数组,将索引进行交换
        for (int i = 0; i < Arr.length; i++) {
    
    
            int index = r.nextInt(Arr.length);

            int temp = Arr[i];
            Arr[i] = Arr[index];
            Arr[index] = temp;
        }

        //遍历一维数组
        for (int i = 0; i < Arr.length; i++) {
    
    
            System.out.print(Arr[i] + " ");
        }

ここで注意する必要があるのは、1 次元配列に対して 1 から 16 を押さずに 0 から 15 を押す理由です。なぜなら、私の写真の名前は 1 から 15 しかなく、余分な 0、つまり 0 が認識されたときだからです。 、素材に見つからない場合は、自動的に空になります。つまり、画像を移動すると白いフレームである空白の画像になります。

最終的な出力結果:
ここに画像の説明を挿入
明らかに、順序が完全に乱れています. 順序が乱れた後、初期化画像メソッドに直接入れることができますか? いいえ、いいえ、いいえ、画像インデックスをよりエレガントにする必要があります。 4 × 4 で表示されているのに、インデックスも 4 × 4 で表示されないのはなぜですか

4×4の添字配置 最初に思いつくのは2次元配列なので、1次元配列を2次元配列に変換するだけでいいので、ループの入れ子を使って表現し、徐々に変換していきます1 次元配列内に要素を追加します。実装されたコードは次のとおりです。

		//将一维数组添加到二维数组中
        int[][] newArr = new int[4][4];

        int index = 0;
        for (int i = 0; i < 4; i++) {
    
    
            for (int j = 0; j < 4; j++) {
    
    
                newArr[i][j] = Arr[index];
                index++;
            }
        }

        //遍历二维数组
        for (int i = 0; i < newArr.length; i++) {
    
    
            for (int j = 0; j < newArr[i].length; j++) {
    
    
                System.out.print(newArr[i][j] + " ");
            }
            System.out.println();
        }

最終的な出力結果 (1 次元配列と 2 次元配列)最後に、この2 次元
ここに画像の説明を挿入
配列のデータをイメージの初期化メソッドに代入するだけで済みます。
ここでは表示の都合上、初期化データメソッドに書きますが、初期化イメージメソッドで使う必要がある二次元配列は、メンバー位置に書けば使えるので、したがって、この初期化データ メソッドは 2 次元配列の割り当てを与えるのと同じです。ピクチャを初期化するメソッドは、2 次元配列のデータを直接使用できます。

イメージの初期化メソッドで 2 次元配列を使用して、イメージの順序をシャッフルする方法を次に示します。

		//外循环 --- 四行
        for (int i = 0; i < 4; i++) {
    
    
            //内循环 --- 一行的四张图片
            for (int j = 0; j < 4; j++) {
    
    
                //获取加载当前图片的序号
                int num = newArr[i][j];

                //创建一个图片ImageIcon对象
                //创建一个JLabel的对象(管理容器)放入ImageIcon对象
                JLabel jLabel = new JLabel(new ImageIcon("D:\\Java Code\\puzzlegame\\image\\animal\\animal3\\" + num + ".jpg"));
                //指定图片位置
                jLabel.setBounds(105 * j, 105 * i, 105, 105);
                //把管理容器添加到界面中
                this.getContentPane().add(jLabel);

            }
        }

以前は数値で画像アドレスを呼んでいましたが、今度は2次元配列の乱れた値で呼びます.たまたまここでループの入れ子になっているので、直接numを使って2次元配列の値を取得することができます.次元配列. そのため、以前に 1 次元配列を 2 次元配列に変換する必要がありました.
最終結果はこのようなものです.
ここに画像の説明を挿入
結果は確かに私たちの期待に沿っており、前の図と比較すると完全に混乱しています.ハスキー。

インターフェイスを美しくする

写真の順序を台無しにしたら、インターフェイスの美化を開始できます. ゲーム インターフェイスの主な美化は、ゲームの背景画像を追加し、画像にサイド スパンを追加し、画像と背景画像を次の場所に移動することです。真ん中と下の位置(複数ある場合は最初に試してください。真ん中と下の位置が最適です)
1.写真の位置を移動します

//指定图片位置
jLabel.setBounds(105 * j + 83, 105 * i + 134, 105, 105);

上記は、画像を追加するときの入れ子ループ内のコード、つまり画像の位置を調整するコードです. 最適な位置を自分でデバッグできます. 全体として 83 ピクセルを右に移動することを選択しました.つまり、x+83; そして、全体として 134 ピクセル下に移動します。つまり、y+134 の位置が最適です。

2. ゲームの背景画像を追加する
ゲームの背景画像を追加することは、写真を追加する方法のままですが、非常に重要な点が 1 つあり
ます。背景画像を追加するコードは、画像を追加するサイクルの後に記述する必要があります。 initImage() メソッド内では、次のコードが実装されます。

//添加背景图片
//创建ImageIcon对象
ImageIcon bg = new ImageIcon("image\\background.png");
//创建JLabel容器对象
JLabel backgound = new JLabel(bg);
backgound.setBounds(40,40,508,560);
//把背景图片添加到界面当中
this.getContentPane().add(backgound);

背景画像は試してみたところ、x=40、y=40 に配置したときに最もよく見えます

3. ピクチャ ボーダーの追加
ピクチャ ボーダーの追加では、ボーダーを見つけて追加する必要はありません. JLabel はピクチャを格納するためのものであり、テキスト コンテナは setBorder() メソッドを提供します. これを直接使用できます. 次のコードは
のネストされたループ内に画像を追加することで書かれています

JLabel jLabel = new JLabel(new ImageIcon("image\\animal\\animal3\\" + num + ".jpg"));
//指定图片位置
jLabel.setBounds(105 * j + 83, 105 * i + 134, 105, 105);
//给图片添加边框
//0:让图片凸起来
//1:让图片凹下去
jLabel.setBorder(new BevelBorder(1));
//把管理容器添加到界面中
this.getContentPane().add(jLabel);

setBorder() メソッドは BevelBorder オブジェクトを渡すことができます. 内部のパラメータには 0 と 1 の 2 つのオプションがあり, 0 は画像を凸にするボーダーを追加し、1 は画像を凸にするボーダーを追加します.写真の凹面は自分で試してみてください、個人的には凹面の方が見栄えが良いと思います

上記の美化コードを完成させた結果、下の画像は
ここに画像の説明を挿入
以前よりもはるかに見栄えが良くなりました

イベントリスナー

イベント監視を簡単に理解すると、このゲームのように、マウスやキーボードでさまざまな操作が行われたことを java が認識して応答し、↑ キーを押すと、空白の下の図がこのように上に移動し、 Java は、↑ キーが押されたことを認識し、下の空白の画像を上に移動します。これは、イベント監視と呼ばれます。イベント監視に関して、Java は、直接使用できる 3 つの
インターフェイスを提供します。モニタリング)、KeyListener(キーボードモニタリング)、アクションモニタリングは他の2つの簡略化されたバージョンで、マウスの左ボタンクリックとスペースキーボードのみを認識でき、マウスモニタリングはクリック、プレスアンドホールド、リリース、ドローインを認識できます。キーボードモニターは、長押し、タイピング、リリースを認識することができます. この側面に関する情報を確認できます. ここでは詳細には触れません. ここでは、キーボードモニターのリリースのみを使用します.

では早速実践してみましょう
イベントリスナはインターフェースなので GameFrame クラスで直接インターフェースを呼び出します

public class GameFrame extends JFrame implements KeyListener

インターフェイスを呼び出した後、インターフェイスのすべてのメソッドを書き換える必要があります。メソッドは全部で 3 つあります。

    @Override
    public void keyTyped(KeyEvent e) {
    
    

    }

    @Override
    public void keyPressed(KeyEvent e) {
    
    
        
    }

    @Override
    public void keyReleased(KeyEvent e) {
    
    
        
    }

ここでは主にkeyReleased メソッド、つまり release を使用します

インデックスに従って画像を追加します. 次にインデックスに従って画像を移動します. 移動後のデータを初期化画像メソッドに渡せば実現できます. 画像を移動するので, おおよそ実装するロジックは
1.空白の絵の位置を取得する.
以下は初期化データメソッドで2次元配列に1次元配列を加算するコードです.ここでは主にindexが0のとき空白と判断します.写真が撮影され、そのインデックスが取得されます.空白の写真の位置を記録するためにメンバー位置に x と y を設定します (キーボード モニターのオーバーライド メソッドも x と y を使用するため), ここで x と y y は実際には先ほど設定した2次元配列のiとj、4×4の表として並べると実際は行と列

        //将一维数组添加到二维数组
        int index = 0;
        for (int i = 0; i < 4; i++) {
    
    
            for (int j = 0; j < 4; j++) {
    
    
                if(Arr[index] == 0){
    
    
                    x = i;
                    y = j;
                }else{
    
    
                    newArr[i][j] = Arr[index];
                }
                index++;
            }
        }

2. keyReleased メソッドで移動ロジックを実現します.
画像の移動の原理は, 実際には空白の画像を周囲の画像と交換することです. 以前に空白の画像の位置を知っています. 今は主に
ここで交換を実現します. int code = e .getKeyCode(); インターフェイスはキーを識別するためのメソッドを提供します. キーボード上のほとんどすべてのキーに名前を付けます. 名前は int 型の番号なので, 後でコードを直接判断して対応する交換を実行できます. exchange はインデックスの交換です。たとえば、下に移動、つまり、空白の画像が上の画像と交換され、空白の画像が上に移動します。空白の画像の x は変更されず、空白の画像の y は - 1 になります。 、交換する画像を下に移動、つまり、交換する画像の x 変更なし、y を + 1 にする必要がある; 交換する画像の位置が空白の画像と交換されるので、そのインデックスを に設定するだけです0. 最後に、空白の画像が移動した後、その位置も変更されることを忘れないでください。空白の画像

    @Override
    public void keyReleased(KeyEvent e) {
    
    
        //对上,下,左,右进行判断
        //左:37, 右:39, 上:38, 下:40
        int code = e.getKeyCode();
        
        if(code == 37){
    
    
            System.out.println("向左移动");
            if(y == 3){
    
    
                return;
            }

            newArr[x][y] = newArr[x][y + 1];
            newArr[x][y + 1] = 0;
            y++;
            //调用方法按最新的方法加载图片
            initImage();


        }else if(code == 38){
    
    
            System.out.println("向上移动");
            if(x == 3){
    
    
                //表示方块已经已经在最下方了,他的下面没有图片再能移动了
                return;
            }

            //把空白方块下方的数字赋值给空白方块
            newArr[x][y] = newArr[x + 1][y];
            newArr[x + 1][y] = 0;
            x++;
            //调用方法按最新的方法加载图片
            initImage();


        }else if(code == 39){
    
    
            System.out.println("向右移动");

            if(y == 0){
    
    
                return;
            }

            newArr[x][y] = newArr[x][y - 1];
            newArr[x][y - 1] = 0;
            y--;
            //调用方法按最新的方法加载图片
            initImage();


        }else if(code == 40){
    
    
            System.out.println("向下移动");
            if(x == 0){
    
    
                return;
            }

            newArr[x][y] = newArr[x - 1][y];
            newArr[x - 1][y] = 0;
            x--;
            //调用方法按最新的方法加载图片
            initImage();
        }
    }

3. 移動後の効果の表示と更新
上記のコードを実行した後、実行後に移動しないことがわかります. それは間違っていますか? 実際にはそうではありません. 前に述べたように、 initImage メソッドを呼び出しましたインデックスを変更した後、この時点での移動後の効果は再びメイン インターフェイスに配置されますが、最初の初期化画像でカバーされます。つまり、最初に追加された画像が上にあり、後で追加された画像がを忘れないように、移動するたびに移動後のコンテンツが表示されるように、initImage メソッドの前に前のメイン インターフェイスのコンテンツのクリアを実装する必要があります。

//清空原本已经出现的所有图片
//清空以后才会出现移动后的图片,不然被覆盖在下面了
this.getContentPane().removeAll();

上記のコードは initImage() 内に記述し、最初に記述し、
次にリフレッシュして、initImage() メソッドの下部に記述する必要があります。

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

4. バグの修正
上記のコードをすべて書いた後、実際にゲームをプレイできるようになりましたが、ゲーム中に空白の絵が上にあるときに ↓ キーを押すと、プログラムが例外をスローすることがわかりました。 , 普通に再生できるのに. , それでも少し違和感. どうしたものか. 実は
絵を動かしたときに白紙の絵の位置を考えていなかった. 境界にある場合は、私たちが主張する場合境界の外に移動させると、絶対に移動できなくなります.このとき、例外がスローされますが、実際には非常に簡単に修正できます.keyReleased メソッドの 4 つの重要な判断ステートメントでのみ使用されます。 x と y が境界上にあるかどうかを判断し、境界上にある場合は、モバイル コードを実行せずに終了するだけで、実装コードは2. keyReleased メソッドに移動ロジックを実装します。

全体像を見る

ゲームをしていると、ゲームの全体像がわからず、パズルの動かし方がわからないことがあるので、ショートカットキーを押して全体像を表示する機能を実現する必要があります。例としてAキーを押すと、Aボタンを押したままにすると、現在のパズルの完全な画像がゲームのメインインターフェイスに表示され、送信するとプレイ中のインターフェイスが再び表示されますあちらへ。1.
キーボード モニタリングは
A キーを押すことによって表示されるため、キーボード モニタリングを使用する必要があります. キーボードを移動するロジックを実装したときに、キーボード モニタリングのすべてのメソッドを書き直したので、直接書き換えメソッドを使用する
2.
A ボタンを長押しすると全体像が表示され、離すとゲーム インターフェイスに戻ります ここで、キーを押したときに keyPressed() メソッドと keyReleased() メソッドにコードを記述する必要があります。それを離すと、keyPressed() メソッドの内容が実行され、離すと keyReleased() メソッドの内容が実行されます
3. インターフェイスをクリアし、画像を表示します
. 押されると、のすべての画像がゲームがクリアされ、全体像がメイン インターフェイスに表示されます。リリースすると、initImage() メソッドを直接呼び出して、以前のゲーム コンテンツを表示できます。

以下はコードの実装です

    @Override
    public void keyPressed(KeyEvent e) {
    
    
        int code = e.getKeyCode();
        if(code == 65){
    
    
            //把界面中的所有图片全部清除
            this.getContentPane().removeAll();
            //加载第一张完整的图片
            JLabel all = new JLabel(new ImageIcon("image\\animal\\animal3\\all.jpg"));
            all.setBounds(83, 134, 420, 420);
            this.getContentPane().add(all);

            //添加背景图片
            JLabel background = new JLabel(new ImageIcon("image\\background.png"));
            background.setBounds(40,40,508,560);
            //把背景图片添加到界面当中
            this.getContentPane().add(background);

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

上記の段落は、A キーを押したときを認識し、画像のすべてのコードをクリアして更新することです。

else if(code == 65){
    
    
      initImage();

上記の段落は keyReleased() メソッドの else if() ですが、ボタンが絵を動かすと判断されたときに判断が追加されたので、今はすぐ後ろに else if() を書き、直接呼び出すだけで十分です。 initImage() メソッド、このメソッドは以前のコンテンツをすべてクリアし、ゲーム画像と背景画像を再表示します

画像パスの最適化

使っている絵が決まっていて、パスが決まっていて、変わらないことがわかります.絵を変えると、クラスのパスも全部書き直さなければならず、とても面倒です.今から最適化して、もっと便利にしましょう.便利なのは、文字列型のパス変数をクラスのメンバー位置に追加して、あとでパスを使う必要があるメソッド内でパスを直接呼び出すことです。パスを直接変更するだけです。次の
コードを実行します。

String path = "image\\animal\\animal3\\";

次に、パスが必要なパスに変更します

JLabel jLabel = new JLabel(new ImageIcon(path + num + ".jpg"));
JLabel all = new JLabel(new ImageIcon(path + "all.jpg"));

このようにして、将来画像を変更したい場合は、パスを直接変更できます

チートコード

ここのチートコードは誰もが知っているので、詳細には触れません。ここに直接コードがあります

else if(code == 87){
    
    
            //作弊码
            //重写给二维数组赋值,初始化二维数组
            newArr = new int[][]{
    
    
                    {
    
    1,2,3,4},
                    {
    
    5,6,7,8},
                    {
    
    9,10,11,12},
                    {
    
    13,14,15,0},
            };
            //调用上面的二维数组进行初始化图片,直接通关
            initImage();
        }

keyReleased() メソッドで else if() を引き続き使用します。ここでは W キー (87 に対応) を設定します。2 次元配列を直接初期化します。つまり、通関の順序に従って 2 次元配列を設定します。 、次に initImage() を直接呼び出します。メソッドは、下図に示すように、通関後の外観を表示できます。
ここに画像の説明を挿入

裁判官の勝利

勝敗の判定は実はチートコードと非常に似ており、現在の二次元配列データが全体像のデータと同じかどうか、つまりチートコードの二次元配列と同じかどうかを判断することです。同じなら勝利アイコンが表示されます.同じでなければ1に進みます.
このデータを複数のメソッドで使用する必要があるため,正しい二次元配列winを定義してメンバーの位置に設定します.

//定义一个二维数组,存储正确的数据
    int[][] win = {
    
    
            {
    
    1,2,3,4},
            {
    
    5,6,7,8},
            {
    
    9,10,11,12},
            {
    
    13,14,15,0}
    };

2. 画像を読み込む関数とゲーム インターフェイスを表示する関数はすべて initImage() メソッド内にあるため、画像を読み込む前に、まず 2 次元配列の数値が win 配列の数値と同じかどうかを判断します。ゲームを続行したい場合は、引き続き表示する必要があります。ゲーム インターフェイスでは、最初に勝利があるかどうかを識別する必要があります。

if(victory()){
    
    
            //显示胜利的图标
            JLabel winJLabel = new JLabel(new ImageIcon("D:\\Java Code\\puzzlegame\\image\\win.png"));
            winJLabel.setBounds(203, 283, 197, 73);
            this.getContentPane().add(winJLabel);
        }

3. 勝てば正しいアイコンがそのまま表示され、そうでなければ間違ったアイコンが表示されます. 勝てない場合は initImage() メソッドのコードが引き続き実行され、ゲームのパズルが表示されます. 4. バグの
修復. すべてを書いてみると、勝つと正しいアイコンが表示されますが、絵はまだ動くことがわかりました。成功すると、終了するか次のラウンドをプレイしない限り、ゲームは動かないと思います。
これは keyReleased() メソッドで判断する必要があります. 勝った場合は、メソッドを直接終了します. 終了すると、キーを判断するために使用したコードを実行できないことを意味します, つまり、画像は再び移動しません.

//判断游戏是否胜利,如果胜利,此方法需要直接结束,不能再执行下面的移动代码了
        if(victory()){
    
    
            //结束方法
            return;
        }

上記のコードはkeyReleased()メソッドの先頭に書くべきで、このメソッドの下に上下左右のキーを判定するコードがあり、勝った後はこれらのキーは使えないので判定しなければなりません。メソッドを直接終了し、次の判定ボタンを実行せず、勝利がない場合は、勝つまで次の判定を実行し続けます

統計的ステップ

ゲームの楽しさを増すために、ゲームの横に歩数を表示するカウンターを追加し、移動するたびに 1 つ追加して、誰の歩数が最も少ないかを比較できるようにします。ステップをカウントする非常に興味深いロジック
実装は比較的簡単です. メンバー位置にカウンター変数を定義し、キーボード監視の keyReleased() メソッドで移動キーを判断するときにカウンター変数を自己インクリメントに追加します.最後に、initImage() メソッドでカウンターの内容を表示します。OK、コードの実装は次のとおりです。

//定义变量用来统计步数
int step = 0;

メンバー位置で電卓変数を定義する

//添加计数器到主界面中
JLabel stepCount = new JLabel("步数:" + step);
stepCount.setBounds(50, 30, 100, 20);
this.getContentPane().add(stepCount);

initImage()メソッドでカウンターを表示する機能を実現
最後に上下左右のキーを判定するコードの末尾にstep++を追加して各ステップを実現 ゲームの左上隅下の図に示すように、インターフェイスには移動ステップの数が表示されます
ここに画像の説明を挿入

メニューバー

再ゲーム

ゲームを再開するには、メニュー バーの [リプレイ] をクリックする必要があります。また、写真の順序が乱れます。再開するには、メニュー バーのデータをクリックするため、データに監視イベントを追加する必要があります
。これらのメニューで. 最も簡単な方法は、それらを与えることです. ActionListener を使用すると、マウスのクリックとキーボードのスペースしかないため、initJMenuBar() メソッドでイベントリスナーをこれらの項目にバインドする必要があります
.インターフェース

public class GameFrame extends JFrame implements KeyListener, ActionListener

次に、エントリにイベントを追加します

//给条目绑定事件
replayItem.addActionListener(this);
reLoginItem.addActionListener(this);
closeItem.addActionListener(this);
accountItem.addActionListener(this);

ActionListener インターフェース メソッドを書き換える前に、以前に定義した項目オブジェクト、つまり JMenuItem をメンバーの位置に移動する必要があることに注意してください。 ActionListenerインターフェースの書き換え
メソッドは
こちら リゲームのentryオブジェクトのreplayItemをクリックした際に実行されるコードを判断する

    @Override
    public void actionPerformed(ActionEvent e) {
    
    
        //获取当前被点击的条目对象
        Object obj = e.getSource();
        //判断
        if(obj == replayItem){
    
    
            System.out.println("重新游戏");

            //计数器清零
            step = 0;
            //再次打乱二维数组中的数据
            initData();
            //重新加载图片
            initImage();

ここで、カウンタのリセットは初期化データと初期化ピクチャの上に配置する必要があることに注意してください。これは、カウンタステップが初期化ピクチャで使用されるためです。上でクリアされていない場合、カウンタは引き続き前のゲーム

再登録

再ログインするには、メニューバーの [再ログイン] をクリックします. 再ログインのためのイベント監視を追加したので、 ActionListener インターフェースによって書き換えられたメソッドで判断するだけで済みます. 以下はコードの実装です.

else if(obj == reLoginItem){
    
    
            System.out.println("重新登录");
            //关闭当前的游戏界面
            this.setVisible(false);
            //打开登录界面
            new LoginJFrame();

ゲームを閉じる

ゲームの終了は非常に簡単です. 書き換え方法では、クリックしてゲームを終了するかどうかを判断します. クリックした場合は、仮想マシンを終了するだけです.

else if(obj == closeItem){
    
    
            System.out.println("关闭游戏");
            System.exit(0);

私たちに関しては

About us, we can display our QR code. つまり、プレーヤーが About Us をクリックすると、箇条書きボックスがポップアップして QR コード画像が表示されます. 箇条書きボックスは新しいインターフェイスであり、サイズを変更して中央に配置する必要があります , top、表示するかどうかなどは、本質的に JFrame に似ています。以下はコードの実装です

else if(obj == accountItem){
    
    
            System.out.println("公众号");

            //创建一个弹框对象
            JDialog jDialog = new JDialog();
            //创建一个管理图片的容器对象JLabel
            JLabel jLabel = new JLabel(new ImageIcon("image\\about.png"));
            //设置位置和宽高
            jLabel.setBounds(0,0,258,258);
            //把图片添加到弹框当中
            jDialog.getContentPane().add(jLabel);
            //给弹框设置大小
            jDialog.setSize(344, 344);
            //让弹框置顶
            jDialog.setAlwaysOnTop(true);
            //让弹框居中
            jDialog.setLocationRelativeTo(null);
            //弹框不关闭则无法操作下面的界面
            jDialog.setModal(true);
            //让弹框显示出来
            jDialog.setVisible(true);

        }

以下はレンダリングです
ここに画像の説明を挿入

ゲームの最適化

ゲームの一般的な内容を書き終えました. ゲームの楽しさを増すために, さらにいくつかの写真を追加して写真を分類することができます. プレイヤーは, 美人の写真, 動物の写真, スポーツの写真などを選択できます.私が与えた材料の中には良い材料がありますいくつか、この効果は比較的単純で、以前の初期化画像を理解した後は、美しさ、動物、スポーツなどのオプションをリゲームメニューに追加できます。対応するオプションをクリックして、対応する画像をランダムに選択します 画像でゲームをプレイすると、ステップ数が0にリセットされ、画像がランダムになります

実現の一般的なプロセス:
1. re-game と呼ばれる JMenu を再定義し、JMenu にピクチャ タイプの JMenuItem を追加し (re-game)、最後に JMenu に JMenu を追加して (関数)、マルチレベル メニューを実現します
。アクション監視イベントを写真の種類に適用し、クリックされたボタンを判断し、対応するボタンに対応する操作を実装します. たとえば、美容をクリックすると、美容写真からランダムに写真を選択し、データを初期化し、写真を初期化します. , と更新. このとき, 私たちは以前に定義されたパス変数が使用されます, パスの値が判定で変更されている限り, つまり画像アドレス, 画像タイプを選択できます. 3. 初期化する前にデータ
と画像の初期化、ステップを 0 として定義し、ステップ数が 0 にクリアされるようにします。

要約する

ここではログイン インターフェイスと登録インターフェイスを書きませんでした. 興味がある場合は、自分で調べてください. コードは他にもあります. 自分で書いてみることもできます. 面白いゲームプレイを追加して、ゲームをより多様にすることもできます.

コード

package ui;

import javax.swing.*;
import javax.swing.border.BevelBorder;
import javax.xml.crypto.Data;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Random;

public class GameFrame extends JFrame implements KeyListener, ActionListener {
    
    
    //JFrame 界面,窗体
    //子类也可以表示界面和窗体
    //那么以后GameFrame就表示游戏的主界面
    //以后跟游戏相关的所有逻辑都写在这个类中

    Random r = new Random();

    //创建选项下面的条目对象
    JMenuItem girlItem = new JMenuItem("美女");
    JMenuItem animalItem = new JMenuItem("动物");
    JMenuItem sportItem = new JMenuItem("运动");
    JMenuItem reLoginItem = new JMenuItem("重新登录");
    JMenuItem closeItem = new JMenuItem("关闭游戏");

    JMenuItem accountItem = new JMenuItem("公众号");


    //定义变量用来统计步数
    int step = 0;

    //定义一个二维数组,存储正确的数据
    int[][] win = {
    
    
            {
    
    1,2,3,4},
            {
    
    5,6,7,8},
            {
    
    9,10,11,12},
            {
    
    13,14,15,0}
    };


    String path = "image\\animal\\animal3\\";

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

    //创建一个二维数组供初始化数据方法添加数据,和供初始化图片方法使用
    int[][] newArr = new int[4][4];


    public GameFrame(){
    
    

        //初始化界面
        initJFrame();

        //初始化菜单
        initJMenuBar();

        //初始化数据(打乱图片顺序的数据)
        initData();

        //初始化图片
        initImage();

        //让界面显示出来,建议放到最后
        this.setVisible(true);
    }

    //初始化界面
    private void initJFrame(){
    
    
        //设置界面的宽高
        this.setSize(603, 680);

        //设置界面的标题
        this.setTitle("拼图游戏单机版 V1.0");

        //设置界面置顶
        //盖住其他所有软件
        this.setAlwaysOnTop(true);

        //设置界面居中
        this.setLocationRelativeTo(null);

        //设置游戏的关闭模式
        this.setDefaultCloseOperation(3);

        //取消默认的居中放置,只有取消了才会按照XY轴的形式添加组件
        this.setLayout(null);

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


    //初始化菜单
    private void initJMenuBar(){
    
    
        //创建整个的菜单对象
        JMenuBar jMenuBar = new JMenuBar();

        //创建菜单上面的两个选项的对象(功能  关于我们)
        JMenu functionJMenu = new JMenu("功能");
        JMenu aboutJMenu = new JMenu("关于我们");
        JMenu replayJMenu = new JMenu("重新游戏");


        //将图片类型添加到重新游戏菜单中
        replayJMenu.add(girlItem);
        replayJMenu.add(animalItem);
        replayJMenu.add(sportItem);

        //将每一个选项下的条目添加到选项当中
        functionJMenu.add(replayJMenu);
        functionJMenu.add(reLoginItem);
        functionJMenu.add(closeItem);

        aboutJMenu.add(accountItem);

        //给条目绑定事件
        reLoginItem.addActionListener(this);
        closeItem.addActionListener(this);
        accountItem.addActionListener(this);
        girlItem.addActionListener(this);
        animalItem.addActionListener(this);
        sportItem.addActionListener(this);

        //将菜单里面的两个选项添加到菜单中
        jMenuBar.add(functionJMenu);
        jMenuBar.add(aboutJMenu);


        //给整个界面设置菜单
        this.setJMenuBar(jMenuBar);
    }


    //初始化图片
    //添加图片的时候,需要按照二维数组中管理的数据添加图片
    private void initImage(){
    
    

        //清空原本已经出现的所有图片
        //清空以后才会出现移动后的图片,不然被覆盖在下面了
        this.getContentPane().removeAll();


        if(victory()){
    
    
            //显示胜利的图标
            JLabel winJLabel = new JLabel(new ImageIcon("D:\\Java Code\\puzzlegame\\image\\win.png"));
            winJLabel.setBounds(203, 283, 197, 73);
            this.getContentPane().add(winJLabel);
        }

        //添加计数器到主界面中
        JLabel stepCount = new JLabel("步数:" + step);
        stepCount.setBounds(50, 30, 100, 20);
        this.getContentPane().add(stepCount);


        //添加背景图片
        //先加载的图片在上方,后加载的图片在下方

        //外循环 --- 四行
        for (int i = 0; i < 4; i++) {
    
    
            //内循环 --- 一行的四张图片
            for (int j = 0; j < 4; j++) {
    
    
                //获取加载当前图片的序号
                int num = newArr[i][j];

                //创建一个图片ImageIcon对象
                //创建一个JLabel的对象(管理容器)放入ImageIcon对象
                JLabel jLabel = new JLabel(new ImageIcon(path + num + ".jpg"));
                //指定图片位置
                jLabel.setBounds(105 * j + 83, 105 * i + 134, 105, 105);
                //给图片添加边框
                //0:让图片凸起来
                //1:让图片凹下去
                jLabel.setBorder(new BevelBorder(1));
                //把管理容器添加到界面中
                this.getContentPane().add(jLabel);

            }
        }

        //添加背景图片
        //创建ImageIcon对象
        ImageIcon bg = new ImageIcon("image\\background.png");
        //创建JLabel容器对象
        JLabel backgound = new JLabel(bg);
        backgound.setBounds(40,40,508,560);
        //把背景图片添加到界面当中
        this.getContentPane().add(backgound);


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



    //初始化数据
    private void initData(){
    
    
        //打乱一个数组里数字的顺序,并构成二维数组

        int[] Arr = {
    
    0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};

        Random r = new Random();
        //遍历数组进行交换
        for (int i = 0; i < Arr.length; i++) {
    
    
            int index = r.nextInt(Arr.length);

            int temp = Arr[i];
            Arr[i] = Arr[index];
            Arr[index] = temp;
        }


        //将一维数组添加到二维数组
        int index = 0;
        for (int i = 0; i < 4; i++) {
    
    
            for (int j = 0; j < 4; j++) {
    
    
                if(Arr[index] == 0){
    
    
                    x = i;
                    y = j;
                }

                newArr[i][j] = Arr[index];

                index++;
            }
        }
    }

    @Override
    public void keyTyped(KeyEvent e) {
    
    

    }

    @Override
    public void keyPressed(KeyEvent e) {
    
    
        int code = e.getKeyCode();
        if(code == 65){
    
    
            //把界面中的所有图片全部清除
            this.getContentPane().removeAll();
            //加载第一张完整的图片
            JLabel all = new JLabel(new ImageIcon(path + "all.jpg"));
            all.setBounds(83, 134, 420, 420);
            this.getContentPane().add(all);

            //添加背景图片
            JLabel background = new JLabel(new ImageIcon("image\\background.png"));
            background.setBounds(40,40,508,560);
            //把背景图片添加到界面当中
            this.getContentPane().add(background);

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

    @Override
    public void keyReleased(KeyEvent e) {
    
    
        //判断游戏是否胜利,如果胜利,此方法需要直接结束,不能再执行下面的移动代码了
        if(victory()){
    
    
            //结束方法
            return;
        }


        //对上,下,左,右进行判断
        //左:37, 右:39, 上:38, 下:40
        int code = e.getKeyCode();
        if(code == 37){
    
    
            System.out.println("向左移动");
            if(y == 3){
    
    
                return;
            }

            newArr[x][y] = newArr[x][y + 1];
            newArr[x][y + 1] = 0;
            y++;
            //每移动一次,计数器自增一次
            step++;
            //调用方法按最新的方法加载图片
            initImage();


        }else if(code == 38){
    
    
            System.out.println("向上移动");
            if(x == 3){
    
    
                //表示方块已经已经在最下方了,他的下面没有图片再能移动了
                return;
            }

            //逻辑:
            //把空白方块下方的数字往上移动
            //x , y 表示空白方块
            //x+1 , y表示空白下方的方块

            //把空白方块下方的数字赋值给空白方块
            newArr[x][y] = newArr[x + 1][y];
            newArr[x + 1][y] = 0;
            x++;
            //每移动一次,计数器自增一次
            step++;
            //调用方法按最新的方法加载图片
            initImage();

        }else if(code == 39){
    
    
            System.out.println("向右移动");

            if(y == 0){
    
    
                return;
            }

            newArr[x][y] = newArr[x][y - 1];
            newArr[x][y - 1] = 0;
            y--;
            //每移动一次,计数器自增一次
            step++;
            //调用方法按最新的方法加载图片
            initImage();

        }else if(code == 40){
    
    
            System.out.println("向下移动");
            if(x == 0){
    
    
                return;
            }

            //逻辑
            //把空白方块往下移
            newArr[x][y] = newArr[x - 1][y];
            newArr[x - 1][y] = 0;
            x--;
            //每移动一次,计数器自增一次
            step++;
            //调用方法按最新的方法加载图片
            initImage();
        }else if(code == 65){
    
    
            initImage();
        }else if(code == 87){
    
    
            //作弊码
            //重写给二维数组赋值,初始化二维数组
            newArr = new int[][]{
    
    
                    {
    
    1,2,3,4},
                    {
    
    5,6,7,8},
                    {
    
    9,10,11,12},
                    {
    
    13,14,15,0},
            };
            //调用上面的二维数组进行初始化图片,直接通关
            x = 3;
            y = 3;
            initImage();
        }
    }


    //判断data数组中的数据是否跟win数组中相同
    //如果全部相同,返回true,否则返回false
    public boolean victory(){
    
    
        for (int i = 0; i < newArr.length; i++) {
    
    
            for (int j = 0; j < newArr[i].length; j++) {
    
    
                if(newArr[i][j] != win[i][j]){
    
    
                    //只要有一个数据不一样则返回false
                    return false;
                }
            }
        }
        //循环结束表示数组遍历比较完毕,完全一样则返回true
        return true;
    };

    @Override
    public void actionPerformed(ActionEvent e) {
    
    
        //获取当前被点击的条目对象
        Object obj = e.getSource();
        //判断
        if(obj == reLoginItem){
    
    
            System.out.println("重新登录");
            //关闭当前的游戏界面
            this.setVisible(false);
            //打开登录界面
            new LoginJFrame();
        }else if(obj == closeItem){
    
    
            System.out.println("关闭游戏");
            System.exit(0);
        }else if(obj == accountItem){
    
    
            System.out.println("公众号");

            //创建一个弹框对象
            JDialog jDialog = new JDialog();
            //创建一个管理图片的容器对象JLabel
            JLabel jLabel = new JLabel(new ImageIcon("image\\about.png"));
            //设置位置和宽高
            jLabel.setBounds(0,0,258,258);
            //把图片添加到弹框当中
            jDialog.getContentPane().add(jLabel);
            //给弹框设置大小
            jDialog.setSize(344, 344);
            //让弹框置顶
            jDialog.setAlwaysOnTop(true);
            //让弹框居中
            jDialog.setLocationRelativeTo(null);
            //弹框不关闭则无法操作下面的界面
            jDialog.setModal(true);
            //让弹框显示出来
            jDialog.setVisible(true);
        }else if(obj == girlItem){
    
    
            System.out.println("美女图片");
            //随机获取图片
            int num = r.nextInt(1, 14);
            path =  "image\\girl\\girl"+ num + "\\";

            //初始化步数
            step = 0;
            //初始化数据
            initData();
            //初始化图片
            initImage();
            //刷新
            this.repaint();

        }else if(obj == animalItem){
    
    
            System.out.println("动物图片");
            int num = r.nextInt(1, 9);
            path =  "image\\animal\\animal"+ num + "\\";
            //初始化步数
            step = 0;
            //初始化数据
            initData();
            //初始化图片
            initImage();
            //刷新
            this.repaint();

        }else if(obj == sportItem){
    
    
            System.out.println("运动图片");
            int num = r.nextInt(1, 11);
            path =  "image\\sport\\sport"+ num + "\\";
            //初始化步数
            step = 0;
            //初始化数据
            initData();
            //初始化图片
            initImage();
            //刷新
            this.repaint();
        }
    }
}

以下が主なプログラムです

import ui.GameFrame;
import ui.LoginJFrame;
import ui.RegisterJFrame;

public class App {
    
    
    public static void main(String[] args) {
    
    

        //表示程序的启动入口

        //如果我们想要开启一个界面,就创建谁的对象就可以了
        new GameFrame();

        //new LoginJFrame();

        //new RegisterJFrame();

        //
    }
}

おすすめ

転載: blog.csdn.net/m0_64041302/article/details/128689399