H5斗地主游戏应用后台教程(一)—— 应用和个体的总体设计

1. 前言

几个月的断断续续开发,终于也将H5斗地主游戏的前后端都基本完成了,也实现了电脑端和手机端的适应。总的来说,从这个项目中学到的挺多,不论是后台业务的开发还是前端功能的实现。从想法的最初萌生到功能一次次的累加和优化以至完成,都经历了精心的设计和尝试。

因此,特地开了这系列博客,希望能记录下设计的大致思路和逻辑。如果你感兴趣的话,那便是极好的。

1. 应用设计

我将后台应用主要分成了以下的三个模块:

TIM截图20190221171729.png

1.1 算法模块

算法模块构成了游戏的规则判断的实现,有点类似 “ 裁判 ” 的角色,它主要提供的功能有:

  • 构造牌并分成三份牌,同时分配给三个玩家;
  • 判断用户打出牌的类型;
  • 比较两个玩家打出牌的牌,并比较它们的大小,以判断是否能管得住上家的牌。

1.2 业务模块

业务模块搭建和玩家客户端(前端)的通信桥梁,负责处理房间管理(创建房间、退出房间),叫牌、出牌以及游戏结束后计分逻辑的具体实现。

1.3 通知模块

在H5中,我们需要及时更新房间内的信息(例如某个玩家加入、游戏开始、玩家出牌等操作),就必须要使用到webSocket协议来支持。因此需要利用通过模块来通知前端这些事件的发生,让玩家的客户端能及时反应这些事件的发生。

2. 个体设计

接下来我们再来针对个体来讨论,并根据游戏中的个体创建所对应的POJO实体类。让我们想象这个游戏有哪些个体?

  • 房间:每个房间都是一个个体,一个房间所包含了三个玩家;
  • 玩家:三个玩家,每个玩家都是一个个体。每个玩家都包含了17张(地主为20张);
  • 牌:每张牌都是一个个体。

接下来我们根据这些个体,来创建对应的实体类。

2.1 房间

房间的属性并不复杂,主要的属性由以下几个。有个特殊的属性stepNum主要是用来判断当前是否是某个玩家的出牌回合,后面会做具体的讲解。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Data
public class Room {

private String id; // 房间号

private String password; // 房间密码

private boolean locked; // 房间是否设置密码,true为设置

private List<Player> playerList; // 当前玩家列表

private RoomStatusEnum status; // 房间的状态(准备/开始)

private int multiple; // 房间底分

private int stepNum; // 每局走的步数,用来控制玩家的出牌回合
}

public enum RoomStatusEnum {
PLAYING("游戏中"),
PREPARING("准备中");
}

2.2 玩家

因为每个玩家都有各自的出牌顺序,所以需要为每个玩家分配一个id号,并可以通过id获得下家出牌的玩家。在该应用的设计中,会将Player玩家对象和User用户对象相关联,目的是将它们的属性区分。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Data
public class Player {

private Integer id; // 玩家在当前房间的座位顺序

private User user; // 玩家的用户对象

private List<Card> cards; // 玩家当前手中的牌

private boolean ready; // 玩家是否准备

private IdentityEnum identity; // 当前局的身份(地主、农民)
}

public enum IdentityEnum {
LANDLORD("地主"),
FARMER("农民");
}

2.3 牌

牌的设计稍微复杂一些, 如下面一张牌所示:

23441533a-0.jpg

我们通过三个标识属性来区分每一张牌:

1
2
3
4
5
6
7
8
9
10
11
12
@Data
public class Card implements Comparable<Card> {

private int id; // 牌的数字ID,用于分牌操作

private CardTypeEnum type; // 牌的类型

private CardNumberEnum number; // 牌的数值

private CardGradeEnum grade; // 牌的等级

}

cardType

对应着牌的花色,如黑桃、红桃、方块、梅花,以及特殊的大小王。枚举类为:

1
2
3
4
5
6
7
8
9
10
public enum CardTypeEnum {

SPADE("黑桃"),
HEART("红桃"),
CLUB("梅花"),
DIAMOND("方块"),

SMALL_JOKER("小王"),
BIG_JOKER("大王");
}

cardNumber

即牌的数字,从1 ~ 15;枚举类的定义为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public enum CardNumberEnum {

ONE(1),
TWO(2),
THREE(3),
FOUR(4),
FIVE(5),
SIX(6),
SEVEN(7),
EIGHT(8),
NINE(9),
TEN(10),

JACK(11), // J
LADY(12), // Q
KING(13), // K

SMALL_JOKER(14), // 小王
BIG_JOKER(15); // 大王
}

cardGrade

即牌的等级,也是从1 ~ 15;枚举类的定义为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public enum CardGradeEnum {

// 3 ~ K
FIRST(1), // 3
SECOND(2), // 4
THIRD(3), // 5
FOURTH(4), // 6
FIFTH(5), // 7
SIXTH(6), // 8
SEVENTH(7), // 9
EIGHTH(8), // 10
NINTH(9), // J
TENTH(10), // Q
ELEVENTH(11), // K

// A 和 2
TWELFTH(12),
THIRTEENTH(13),

// 大小王
FOURTEENTH(14),
FIFTEENTH(15);
}

看到这里你可能会纳闷,这个属性和cardNumber有什么区别。的确,这是一个比较特殊的标识属性,我们都知道,虽然从数学意义上来说,A23小;但是,在斗地主的规则中却比3大。

因此,cardNumber只是用来显示牌的数值;而cardGrade用来衡量牌的等级,可以用于比较牌的大小

Pushy wechat
欢迎订阅我的微信公众号