Java-黄金矿工(一)

编辑器:IDEA
语言:Java
主要技术:Socket网络通讯

效果

https://www.bilibili.com/video/BV1Um4y187FV/?spm_id_from=333.999.0.0

主要思想

①服务端与客户端通讯,互相发送各个状态,以达到相对同步;
②共3个界面:服务端、客户端、游戏界面;
③倒计时结束,根据双方的金钱进行判断输赢。

实现

1.准备图片

(里面的hook.png、stone.png和loginBackground.png没有用到,主要是钩子还得跟着线甩,后采取了使用粉色小球代替钩子,相对较容易实现;stone石头和loginBackground登录背景图未添加入程序)
imgs

(1)背景图

下面两张图为主要背景,是用ps相对画了一下。
up
down

(2)金块

共3种金块,分别为小金块、中等金块和大金块,金块是在4399上截的图。
smallGold
middleGold
bigGold

(3)矿工

共3种状态,分别为手在上(手在上时,钩子来回摇摆,等待下钩)、手在下(手在下时,钩子释放,去抓取金块)、手上下动(表示正在往上拉,钩子回收中),这个也是在4399上截的图。
minerState1
minerState2
minerState3

(4)钩子

即粉色小球。
hookBall

2.服务端与客户端搭建

服务端:
服务端
客户端:
客户端

(1)新建Server类

继承JFrame。

public class Server extends JFrame{
     public static void main(String[] args) {
         //点击运行,从此处开始运行
     }
}

(2)添加基本元素

①多行文本框:“textArea”,主要用来显示一些重要信息,比如“开启服务”、“输赢情况”等等;
②滚动条:“scrollPane”,多行文本框的上下滚动条;
③文本:“labelPort ”→“端口号:”;
④文本输入框:“textPort ”→输入开启的端口号;
端口号
⑤按钮:“btOpen”→“开启服务”,确定好端口号,点击此按钮,代表服务端开启服务,等待来自客户端的连接;
⑥按钮:“btBegin”→“开始游戏”,客户端连接成功后,点击此按钮,可进入游戏界面。

    public static JTextArea textArea = new JTextArea(5,10);//多行文本框,可以显示信息
    private static JScrollPane scrollPane;//这个是给textArea添加滚动条
    private static JLabel labelPort = new JLabel("端口号:");
    private static JTextField textPort = new JTextField("8088",10);
    private static JButton btOpen = new JButton("开启服务");
    public static JButton btBegin = new JButton("开始游戏");

(3)新建launch方法

主要写入各个元素的属性(文本格式、按钮点击事件等等)。

点击查看代码
void launch(){
        this.setVisible(true);//设置窗口可见
        this.setSize(500,500);//窗口的大小,宽500*高500像素
        this.setLocationRelativeTo(null);//窗口位置:屏幕居中
        this.setTitle("黄金矿工联机版(server)");//窗口的标题
        this.setResizable(false);//设置窗口不可以更改大小
        setDefaultCloseOperation(EXIT_ON_CLOSE);//设置窗口的关闭事件,即点击窗口右上角的×,就可以关闭程序
        setLayout(null);//设置窗口的布局

        //textArea.setOpaque(false);//透明
        textArea.setForeground(Color.black);//设置textArea的前景色,即里面字体的颜色
        textArea.setFont(new Font("幼圆",Font.BOLD,15));//设置textArea的字体
        textArea.setEnabled(false);//只读,不可以更改textArea里面的内容
        textArea.setBounds(0,0,500,200);//textArea的坐标和大小
        scrollPane = new JScrollPane(textArea);
        scrollPane.createHorizontalScrollBar();//竖的滚动条
        scrollPane.setBounds(0,0,500,200);

        labelPort.setFont(new Font("幼圆",Font.PLAIN,18));
        labelPort.setBounds(145,250,100,20);//500/2=250 250-(100+100+10)/2)=145

        textPort.setFont(new Font("幼圆",Font.PLAIN,18));
        textPort.setBounds(238,250,100,20);//162+66+10=238

        //开启服务按钮
        btOpen.setFont(new Font("幼圆",Font.PLAIN,15));
        btOpen.setBounds(200,300,100,30);//500/2=250 250-100/2=200
        btOpen.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                //TODO:点击“btOpen”按钮后运行这里
            }
        });

        //开始游戏按钮
        btBegin.setFont(new Font("幼圆",Font.PLAIN,15));
        btBegin.setBounds(200,350,100,30);//按钮的位置和大小
        btBegin.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                //TODO:点击“btBegin”按钮后运行这里
            }
        });

        this.add(scrollPane);//把textArea添加到窗口中
        this.add(labelPort);
        this.add(textPort);
        this.add(btOpen);
        this.add(btBegin);
    }

(4)新建公共类publicMeans

里面主要放置一些公共变量。

public class publicMeans {
    public static String ip = null;
    public static int port = 0;
    public static boolean isServer = false;//记录此程序是服务端还是客户端,服务端:true;客户端:false
    public static boolean isConn = false;//记录是否连接成功
    public static boolean isEnd = false;//表示游戏是否结束
}

(5)创建线程类ServerThread

主要用来新建接收和发送信息的相关变量(is,isr,br,info,os,pw),然后接收并解析来自客户端的信息。

点击查看代码
public class ServerThread extends Thread{//extends Thread创建服务端线程
    private Socket socket = null;
    //接收来自客户端信息的相关变量
    public InputStream is = null;
    public InputStreamReader isr = null;
    public static BufferedReader br = null;
    public static String info = null;
    //向客户端发送信息的相关变量
    public OutputStream os = null;
    public PrintWriter pw = null;

    @Override
    public void run() {//线程开启后,自动执行run
        try{
            socket = Server.serverSocket.accept();//等待客户端连接,若没有连接,就会一直在这一行代码等着,若连接了,就会下一步
            publicMeans.isServer = true;//表示这个窗口是服务端
            publicMeans.isConn = true;//这里表示客户端连接了,所以把isConn置为true
            Server.textArea.append("连接成功。\r\n");//把这句话添加到textArea中

            os = socket.getOutputStream();
            pw = new PrintWriter(os);//这两个是向客户端发送信息的相关变量

            is = socket.getInputStream();
            isr = new InputStreamReader(is);
            br = new BufferedReader(isr);//这三个事接收客户端信息的变量
            while(true){//这里使用死循环,是因为要一直监视客户端有没有发过来信息
                while((info = br.readLine()) != null){
                    //br.readLine()就是读取客户端发过来的信息,br.readLine()读取一行,然后使用while((info = br.readLine()) != null)读取发过来的所有信息
                    //TODO:解析客户端发过来的信息
                }
            }
        }catch (Exception e){}
    }

(6)在Server类中建立服务端

    private static Socket socket;
    public static ServerSocket serverSocket;
    private static ServerThread thread;
    public static void BuildServer(){//建立服务端
        try{
            serverSocket = new ServerSocket(8088);//创建服务端,8088是端口号,可以更改,这里的端口号要跟客户端的端口号一致
            textArea.append("服务端开启成功,等待客户端连接......\r\n");//把这句话添加到textArea中
            btOpen.setEnabled(false);//设置开启服务按钮为不可以点击的状态,因为已经开启服务了
            thread = new ServerThread();//创建一个线程→第(4)步
            thread.start();//线程开启,会自动执行ServerThread中的run方法
        }catch (Exception e){e.printStackTrace();}
    }

(7)调用建立服务端方法

在launch方法中的btOpen按钮的点击事件中引用建立服务端的方法(BuildServer)。

btOpen.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if (!textPort.getText().isEmpty()){//如果textPort不为空,才可以去建立
                    publicMeans.port = Integer.parseInt(textPort.getText().trim());
                    BuildServer();//把建立服务端移到了这里
                    //只有点击开启服务按钮,才会去建立服务端
                }
            }
        });

(8)在Server类中建立发送信息的方法

    //服务端发送信息时引用
    public static void Send(String message){
        thread.pw.write(message + "\r\n");//这句就是把message发送给客户端
        thread.pw.flush();
    }

(9)在main方法中调用launch方法

第(1)步的main。

public static void main(String[] args){//这个main是程序入口,就是点击运行,程序从这里开始跑
        Server server = new Server();
        server.launch();//调用launch方法
    }

(10)新建Client类

继承JFrame(创建客户端的方法与创建服务端大体相似)。

public class Client extends JFrame {
    public static void main(String[] args) {
         //点击运行,从此处开始运行
     }
}

(11)添加基本元素

①多行文本框:“textArea”,主要用来显示一些重要信息,比如“连接成功”、“输赢情况”等等;
②滚动条:“scrollPane”,多行文本框的上下滚动条;
③文本:“labelIP”→“IP:”;
④文本输入框:“textIP”→输入服务端IP;
⑤文本:“labelPort ”→“端口号:”;
⑥文本输入框:“textPort ”→输入开启的端口号;
⑦按钮:“btConn”→“连接”,客户端连接服务端。

    public static JTextArea textArea = new JTextArea(5,10);
    private static JScrollPane scrollPane;
    private static JLabel labelIP = new JLabel("IP:");
    private static JTextField textIP = new JTextField("127.0.0.1",10);
    private static JLabel labelPort = new JLabel("端口号:");
    private static JTextField textPort = new JTextField("8088",10);
    public static JButton btConn = new JButton("连接");

(12)新建launch方法

主要写入各个元素的属性(文本格式、按钮点击事件等等)。

点击查看代码
void launch(){
        this.setVisible(true);
        this.setSize(500,500);
        this.setLocationRelativeTo(null);//窗口位置:居中
        this.setTitle("黄金矿工联机版(client)");
        this.setResizable(false);
        setLayout(null);//设置布局
        setDefaultCloseOperation(EXIT_ON_CLOSE);

        //textArea.setOpaque(false);//透明
        textArea.setForeground(Color.black);
        textArea.setBackground(Color.white);
        textArea.setFont(new Font("幼圆",Font.BOLD,15));
        textArea.setEnabled(false);//只读
        textArea.setBounds(0,0,500,200);
        scrollPane = new JScrollPane(textArea);
        scrollPane.setBounds(0,0,500,200);

        //"IP:"
        labelIP.setFont(new Font("幼圆",Font.PLAIN,15));
        labelIP.setBounds(145,250,100,20);

        //输入IP地址的文本框
        textIP.setFont(new Font("幼圆",Font.PLAIN,15));
        textIP.setBounds(238,250,100,20);

        //“端口:”
        labelPort.setFont(new Font("幼圆",Font.PLAIN,15));
        labelPort.setBounds(145,300,100,20);

        //输入端口号的文本框
        textPort.setFont(new Font("幼圆",Font.PLAIN,15));
        textPort.setBounds(238,300,100,20);

        //连接按钮
        btConn.setFont(new Font("幼圆",Font.PLAIN,15));
        btConn.setBounds(200,350,100,30);
        btConn.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                //TODO:点击“btOpen”按钮后运行这里
                //比如“    123    ”,加了.trim就会得到“123”
                publicMeans.ip = textIP.getText().trim();//获取输入的IP,就是textIP里面的 .trim()的意思是删除文本框里面内容前后的空格
                publicMeans.port = Integer.parseInt(textPort.getText().trim());//因为port是int类型,所以要使用Integer.parseInt把string转为int
            }
        });

        this.add(scrollPane);
        this.add(labelIP);
        this.add(textIP);
        this.add(labelPort);
        this.add(textPort);
        this.add(btConn);
    }

(13)创建线程类ClientThread

主要用来新建接收和发送信息的相关变量(is,isr,br,info,os,pw),然后接收并解析来自服务端的信息。

点击查看代码
public class ClientThread extends Thread{//创建客户端线程
    private Socket socket;
    public ClientThread(Socket socket){this.socket = socket;}
    //接收来自服务端信息的相关变量
    public InputStream is = null;
    public InputStreamReader isr = null;
    public BufferedReader br = null;
    private static String info = null, infoAll = null;
    //向服务端发送信息的相关变量
    public OutputStream os = null;
    public PrintWriter pw = null;
    
    @Override
    public void run() {//线程开始,自动运行
        try{
            socket = new Socket(publicMeans.ip,publicMeans.port);//服务端的ip地址和端口号 这里的ip和port也就是刚才文本框里输入的
            publicMeans.isServer = false;//如果成功进入到这一步,则代表此窗口是客户端窗口
            publicMeans.isConn = true;//连接成功
            Client.textArea.append("连接成功。\r\n");//"\r\n"的意思是换行
            Client.btConn.setEnabled(false);//禁用客户端的连接按钮

            os = socket.getOutputStream();
            pw = new PrintWriter(os);

            is = socket.getInputStream();
            isr = new InputStreamReader(is);
            br = new BufferedReader(isr);//这里就是客户端接收的地方
            while(true){//这里使用死循环,是因为要一直监视服务端有没有发过来信息
                //刚才在服务端里面,开始游戏的时候服务端向客户端发送了“开始游戏”,这里解析
                while((info = br.readLine()) != null){
                    //TODO:解析服务端发过来的内容
                }
            }//跟服务端一个意思
        }catch (Exception e){
            e.printStackTrace();
        }
    }

(14)在Client类中建立客户端

    private static Socket socket;
    private static ClientThread thread;
    public static void BuildClient(){//创建客户端
        try{
            thread = new ClientThread(socket);//跟刚才一样,创建一个客户端线程
            thread.start();
        }catch (Exception e){}
    }

(15)调用建立客户端的方法

在launch方法中的btConn按钮的点击事件中引用建立客户端的方法(BuildClient)。

btConn.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                //比如“    123    ”,加了.trim就会得到“123”
                publicMeans.ip = textIP.getText().trim();//获取输入的IP,就是textIP里面的 .trim()的意思是删除文本框里面内容前后的空格
                publicMeans.port = Integer.parseInt(textPort.getText().trim());//因为port是int类型,所以要使用Integer.parseInt把string转为int
                BuildClient();//点击连接按钮后,去创建客户端
            }
        });

(16)在Client类中建立发送信息的方法

//客户发送信息时引用
    public static void Send(String message){
        thread.pw.write(message + "\r\n");
        thread.pw.flush();
    }

(17)在main方法中调用launch方法

第(10)步的main。

public static void main(String[] args){//这个main是程序入口,就是点击运行,程序从这里开始跑
        Client client = new Client();
        client.launch();
    }

3.游戏界面实现

Java-黄金矿工(二) https://www.cnblogs.com/xbxxx/p/18207183

posted @ 2024-05-22 21:14  小豹小小小  阅读(211)  评论(0编辑  收藏  举报