由于是第一次尝试,所以初级版特别简单。 只具备简单的两人对战
棋盘的绘制
棋盘的绘制是采用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();
}
这里所有的逻辑是这样的