Netty简单网络游戏(五子棋实现)- 第一版

由于是第一次尝试,所以初级版特别简单。 只具备简单的两人对战

棋盘的绘制

棋盘的绘制是采用java swing来的。 因为比较方便,我前端比较菜
这里就直接贴代码了

 @Override
        public void paint(Graphics g) {
            Graphics2D g2 = (Graphics2D) g;
            //设置画笔颜色
            g2.drawImage(image, 0, 0, 1600, 900, null);
            g2.setColor(Color.CYAN); //填充规定区域
            g2.fill3DRect(50, 50, 700, 700, true);
            g2.setColor(Color.black);
            // 绘制下棋区域
            for (int i = 1; i < 16; i++) {
                g2.drawLine(50, 50 * i, 750, 50 * i);
            }
            for (int i = 1; i < 16; i++) {
                g2.drawLine(50 * i, 50, 50 * i, 750);
            } //画外框 //设置画笔宽度
            g2.setStroke(new BasicStroke(5));
            g2.drawLine(25, 25, 775, 25);
            g2.drawLine(25, 775, 775, 775);
            g2.drawLine(25, 25, 25, 775);
            g2.drawLine(775, 25, 775, 775); //画四个黑色圆
            for (int x = 0; x < 15; x++) {
                for (int y = 0; y < 15; y++) {
                    GoBangModelMsg[][] qipan = coordinate;
                    if (qipan[x][y] != null) {
                        GoBangModelMsg bangModel = qipan[x][y];
                        g2.setColor(bangModel.getUser().getColor());
                        g2.fillOval((x + 1) * 50 - 10, (y + 1) * 50 - 10, 20, 20);
                    }
                }
            }
            g2.setColor(new Color(176, 166, 100, 254));
            //设置画笔颜色
            g2.fillRect(26, 26, 749, 23);
            g2.fillRect(26, 49, 23, 725);
            g2.fillRect(49, 752, 725, 23);
            g2.fillRect(752, 49, 23, 703);
        }

要落子,那么肯定要实现鼠标点击事件。 所以你的类可以继承 MouseListener 。 这个类是用来定义一系列鼠标事件的。 我这里主要实现了鼠标点击事件

 int x = e.getX();
        int y = e.getY();
        GoBangModelMsg goBangModelMsg = new GoBangModelMsg();
        goBangModelMsg.setX(x);
        goBangModelMsg.setY(y);
        goBangModelMsg.setUser(getUser());
        //发送消息
        Client instance = Client.getInstance();
        instance.getFuture()
                .channel().writeAndFlush(goBangModelMsg);

这里的 GoBangModelMsg 是我自己定义的消息对象。 每次鼠标点击。 把鼠标点击对应的 坐标以及提前定义好的用户传给服务端。 这名要说明一下。 因为下棋需要知道是谁下,下在哪里所以才有了这个对象的定义

消息的编写

消息实体类

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class GoBangModelMsg implements Serializable {

    /**
     * 用户
     */
    private User user;

    /**
     * 坐标x
     */
    private Integer x;

    /**
     * 坐标y
     */
    private Integer y;
}

用户类

public class User implements Serializable {

    /**
     * 用户编号
     */
    private String userId;

    /**
     * 颜色
     */
    private Color color;
}

color主要是用来区分落子的。不然都是一样的颜色,容易搞混。 另外需要编写解码器和编码器。 编码器是客户端发送消息,进行编码,服务端解码的过程,反之就是服务端解码,客户端编码
编码器,这里实现的是 MessageToByteEncoder

public class GoBangEncoder extends MessageToByteEncoder<GoBangModelMsg> {


    @Override
    protected void encode(ChannelHandlerContext channelHandlerContext, GoBangModelMsg goBangModelMsg, ByteBuf buf) throws Exception {
        byte[] bytes = BytesUtil.objectToBytes(goBangModelMsg);
        buf.writeInt(bytes.length);
        buf.writeBytes(bytes);
    }
}

这里比较简单。 就是将消息体进行编码,这里用的Hessian2, 具体实现看下面。 主要功能是序列化和反序列化

 public static byte[] objectToBytes(Object object) throws IOException {
        Hessian2Output output = null;
        //序列化所用
        try {
            ByteArrayOutputStream os = new ByteArrayOutputStream();
            output = new Hessian2Output(os);
            //写入object对象中
            output.writeObject(object);
            output.flush();
            //写入序列化后的数组
            byte[] bytes = os.toByteArray();
            return bytes;
        }finally {
            if(output != null){
                output.close();
            }
        }
    }

    public static void main(String[] args) throws IOException {
        System.out.println(75 / 50);
    }

    public static Object bytesToObject(byte[] bytes) throws IOException {
        //序列化所用
        Hessian2Input input = null;
        try {
            ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
            input = new Hessian2Input(bis);
            return input.readObject();
        }finally {
            if(input != null){
                input.close();
            }
        }


    }

解码器,解码器继承的是ByteToMessageDecoder

 @Override
    protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf in, List<Object> list) throws Exception {
        if(in.readableBytes() < 8) return;
        in.markReaderIndex();

        int length = in.readInt();
        int readableBytes = in.readableBytes();
        log.info("length {},readable {}",length,readableBytes);
        if(readableBytes < length) {
            in.resetReaderIndex();
            return;
        }
        byte[] bytes = new byte[readableBytes];
        in.readBytes(bytes);
        Object object = BytesUtil.bytesToObject(bytes);
        list.add(object);
    }

服务端

这里主要针对是服务端nettyHandler

 private static ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);


    @Override
    public void channelRead(ChannelHandlerContext ctx, final Object msg) throws Exception {
        if(msg instanceof GoBangModelMsg){
            log.info("当前五子棋的下子坐标为 {}", JSONUtil.toJsonStr(msg));
            //开始通知对应的客户端
            channelGroup.forEach(channel -> {
                log.info("开始通知所有连接过来的通道");
                channel.writeAndFlush(msg);
            });
        }
    }

    @Override
    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        super.channelRegistered(ctx);
        log.info(ctx.name() + "注册进来了");
        ChannelId id = ctx.channel().id();
        //加入channel组
        channelGroup.add(ctx.channel());
        channelGroup.find(id);
    }

    @Override
    public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
        super.channelUnregistered(ctx);
    }

channelGroup 将会缓存所有连进来的客户端

客户端

   @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        GoBangModelMsg m = (GoBangModelMsg) msg;
        GoBangFrame frame = GoBangFrame.getInstance();
        int x = (m.getX() - 25) / 50;
        int y = (m.getY() - 50) / 50;
        GoBangModelMsg[][] coordinate = GoBangFrame.coordinate;
        coordinate[x][y] = m;
        GoBangFrame.QiPanJPanel canvas = frame.getCanvas();
        //重新绘制棋子
        canvas.repaint();
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
       super.channelActive(ctx);
    }


    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        cause.printStackTrace();
        ctx.close();
    }

这里所有的逻辑是这样的

客户端1 服务端 客户端2 棋盘2 棋盘1 注册 注册 落子 发送消息(客户端1下子) 发送消息 (客户端1下子) 重新绘制棋盘 重新绘制棋盘 客户端1 服务端 客户端2 棋盘2 棋盘1

代码地址

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值