Zinx下MMO游戏具体实现-02玩家模块和世界模块
1.构建项目目录
core:存放算法、核心功能
conf:zinx框架配置文件
pb:存放用来与前端交互的proto文件
apis:存放用户自定义的路由业务,一个msgID对应一个业务
main.go:zinx-server开发的编程流程
2.实现一个Player玩家模块
2.1.在core中创建Player结构体并实现初始化函数
type Player struct {
Pid int32 //玩家ID
Conn ziface.IConnection //当前玩家的链接(与对应客户端通信)
X float32 //平面的x轴坐标
Y float32 //高度
Z float32 //平面的y轴坐标
V float32 //玩家脸朝向的方向
}
// playerID 生成器
var PidGen int32 = 1//用于生产玩家ID计数器
var IdLock sync.Mutex //保护PidGen生成器的互斥锁
//初始化玩家的方法
func NewPlayer(conn ziface.IConnection) *Player{
//分配一个玩家ID
IdLock.Lock()
id := PidGen
PidGen++
IdLock.Unlock()
//创建一个玩家对象
p := &Player{
Pid:id,
Conn:conn,
X: float32(160 + rand.Intn(10)),//随机生成玩家上线所在的x轴坐标
Y:0,
Z: float32(140 + rand.Intn(10)), //随机在140坐标点附近 y轴坐标上线
V:0,//角度为0
}
return p
}
2.2.实现玩家可以和对端客户端发送消息的方法
func (p *Player) SendMsg(msgID uint32, proto_struct proto.Message) error {
//要将proto结构体 转换成 二进制的数据
binary_proto_data, err := proto.Marshal(proto_struct)
if err != nil {
fmt.Println("marshal proto struct error ", err)
return err
}
//再调用zinx原生的connecton.Send(msgID, 二进制数据)
if err := p.Conn.Send(msgID, binary_proto_data); err != nil {
fmt.Println("Player send error!" , err)
return err
}
return nil
}
2.3.实现上线业务
需要实现服务器给客户端发送玩家初始ID和服务器给客户端发送一个玩家的初始化位置信息,这时需要用到proto创建message用来发送数据
//返回给玩家上线的ID信息
message SyncPid{
int32 Pid=1;
}
//返回给上线玩家初始的坐标
message BroadCast{
int32 Pid=1;
int32 Tp=2; //Tp: 1 世界聊天, 2 坐标, 3 动作, 4 移动之后坐标信息更新
oneof Data {
string Content=3;
Position P=4;
int32 ActionData=5;
}
}
//位置信息
message Position{
float X=1;
float Y=2;
float Z=3;
float V=4;
}
将msg.proto生成对应的go文件,实现通信
//服务器给客户端发送玩家初始ID
func (p *Player) ReturnPid() {
//定义个msg:ID 1 proto数据结构
proto_msg := &pb.SyncPid{
Pid:p.Pid,
}
//将这个消息 发送给客户端
p.SendMsg(1, proto_msg)
}
//服务器给客户端发送一个玩家的初始化位置信息
func (p *Player) ReturnPlayerPosition() {
//组建MsgID:200消息
proto_msg := &pb.BroadCast{
Pid:p.Pid,
Tp:2, //2 -坐标信息
Data: &pb.BroadCast_P{
P:&pb.Position{
X:p.X,
Y:p.Y,
Z:p.Z,
V:p.V,
},
},
}
//将这个消息 发送给客户端
p.SendMsg(200, proto_msg)
}
在main中具体实现上述功能
//当前客户端建立链接之后触发Hook函数
func OnConnectionAdd(conn ziface.IConnection) {
fmt.Println("conn Add..")
//创建一个玩家 将链接和玩家模块绑定
p := core.NewPlayer(conn)
//给客户端发送一个msgID:1
p.ReturnPid()
//给客户端发送一个msgID:200
p.ReturnPlayerPosition()
}
func main() {
s := net.NewServer("MMO Game Server")
//注册一些 链接创建/销毁的 Hook钩子函数
s.AddOnConnStart(OnConnectionAdd)
//注册一些路由业务
s.Serve()
}
3.实现一个WorldManager世界模块
3.1.在core中创建WorldManager结构体并实现初始化函数
//当前世界地图的边界参数
const (
AOI_MIN_X int = 85
AOI_MAX_X int = 410
AOI_CNTS_X int = 10
AOI_MIN_Y int = 75
AOI_MAX_Y int = 400
AOI_CNTS_Y int = 20
)
/*
当前的场景的世界管理模块
*/
type WorldManager struct {
//当前全部在线的Player集合
Players map[int32] *Player
//保护Player集合的锁
pLock sync.RWMutex
//AOIManager当前的地图的管理器
AoiMgr *AOIManager
}
//对外提供一个全局世界管理模块指针
var WorldMgrObj *WorldManager
func init() {
//创建一个全局的世界管理对象
WorldMgrObj = NewWorldManager()
}
//初始化方法
func NewWorldManager() *WorldManager {
wm := &WorldManager{
AoiMgr:NewAOIManager(AOI_MIN_X, AOI_MAX_X, AOI_CNTS_X, AOI_MIN_Y, AOI_MAX_Y, AOI_CNTS_Y),
Players:make(map[int32] *Player),
}
return wm
}
3.2实现和玩家有关的方法
//添加一个玩家
func (wm *WorldManager) AddPlayer(player *Player) {
//加入世界管理器中
wm.pLock.Lock()
wm.Players[player.Pid] = player
wm.pLock.Unlock()
//加入到世界地图中
wm.AoiMgr.AddToGridByPos(int(player.Pid), player.X, player.Z)
}
//删除一个玩家
func (wm *WorldManager) RemovePlayerByPid(pid int32) {
//从世界管理删除
wm.pLock.Lock()
//先通过pid 从世界管理器得到player对象
player := wm.Players[pid]
//从世界地图中删除
wm.AoiMgr.RemoteFromGridbyPos(int(pid), player.X, player.Z)
delete(wm.Players, pid)
wm.pLock.Unlock()
}
//通过一个玩家ID得到一个Player对象
func (wm *WorldManager) GetPlayerByPid(pid int32) *Player {
wm.pLock.RLock()
p := wm.Players[pid]
wm.pLock.RUnlock()
return p
}
//获取全部的在线玩家集合
func (wm *WorldManager) GetAllPlayers() []*Player {
wm.pLock.RLock()
defer wm.pLock.RUnlock()
players := make([]*Player, 0)
//将世界管理器的player对象加入到返回的切片中
for _, player := range wm.Players {
players = append(players, player)
}
return players
}
在main中的OnConnectionAdd中把Player添加到WorldMgrObj中
func OnConnectionAdd(conn ziface.IConnection) {
fmt.Println("conn Add..")
//创建一个玩家 将链接和玩家模块绑定
p := core.NewPlayer(conn)
//给客户端发送一个msgID:1
p.ReturnPid()
//给客户端发送一个msgID:200
p.ReturnPlayerPosition()
//上线成功了
//将玩家对象添加到世界管理器中
core.WorldMgrObj.AddPlayer(p)
fmt.Println("----> player ID = ", p.Pid, "Online...", ", Player num = ", len(core.WorldMgrObj.Players))
}