C#服务端的微信小游戏——多人在线角色扮演(四)

知识共享许可协议 版权声明:署名,允许他人基于本文进行创作,且必须基于与原先许可协议相同的许可协议分发本文 (Creative Commons

C#服务端的微信小游戏——多人在线角色扮演(四)

时间、空间和物质,这就是宇宙的全部,让世界在你的服务器上运转起来吧。
——茂叔

GameServer的第一个工作。

在上一篇里,服务端的GameServer启动后,并没有进行任何实际的工作,只是通知GameMonitor它已经启动了。这个相当于我们上班打卡,然后在座位上补瞌睡……现在,让我们看看有什么活需要干吧。

首先,我们知道GameServer的功能主要有三个:

  1. 让游戏跑起来
  2. 接受和处理客户端发过来的请求
  3. 把客户需要的信息发回客户端

现在我们先处理让游戏跑起来的工作。后面两项工作将来配合客户端开发来一起完成。
所以,我们要有个Game类对吧。
然后这个Game类要可以创建一个空间,把控时间,调动空间里的物质。
好的,在VS方案里面创建一个新的项目,类型为Class Library(.NET Framework),命名为GameLib,然后把自动生成的Class1更名为Game

另外在新建一个用以保存全局信息的类G,在G里面定义一个静态成员GlobeTime,这就是我们在游戏里面的时间了。游戏世界的任何动作都应该以这个时间为尺度。
对于世界上所有的内容,时间,都是平等的,不仅仅是在游戏里。

在游戏类创建的时候,将时间归零,一切从头开始……这个只有游戏里面可以做到。

接下来创建一个新的类GameWorld 这就是我们游戏世界的空间
因为我们是做的2D游戏,所以空间是由若干地图构成的,因此还需要一个GameMap类。
另外再创建一个GameObject这个是空间里的内容

有了全局的时间,Game里面还必须有个GameWorld,而GameWorld里面有一堆GameMap,然后GameMap里面又有一堆GameObject,所以,这几个类应该是这样的:

public class G
    {
        public static uint GlobeTime;
    }
public class Game
    {
        private readonly GameWorld gWorld;
        public Game()
        {
            G.GlobeTime = 0;
            gWorld = new GameWorld();
        }
    }
class GameWorld
    {
        private readonly List<GameMap> gMaps;
        public GameWorld()
        {
            gMaps = new List<GameMap>();
        }
    }
class GameMap
    {
        private readonly List<GameObject> gObjects;
        public GameMap()
        {
            gObjects = new List<GameObject>();
        }
    }
class GameObject
    {
        public GameObject()
        {
        }
    }

当游戏服务器GameServer启动的时候,我们需要让游戏跑起来,那就需要访问Game类,所以要为GameMonitor项目添加引用GameLib,右键选取GameMonitor,选择添加->引用->‘工程’,然后给GameLib打上勾勾,确定。再到GameServer里面添加一行using GameLib;就可以了。

扫描二维码关注公众号,回复: 6497942 查看本文章

GameServer增加一个成员private readonly Game gGame,这个就是我们的游戏了。

GameServerStartUp方法里面,添加gGame.Run(),这样游戏就跑起来了。VS会提醒gGame没有Run这个方法,没关系,选中Run然后alt+enter,选择自动生成一个就可以了。

GameServer.cs里面:

 public void StartUp()
        {
            if (Status == 0)
            {
                gGame.Run();
                Status = 1;
                LOG("GameServer启动成功");
            }
            else
            {
                LOG("GameServer当前处于启动状态,请勿重复启动");
            }                
        }
        public void ShutDown()
        {
            if (Status == 1)
            {
                gGame.Stop();
                Status = 0;
                LOG("GameServer停机成功");
            }
            else
            {
                LOG("GameServer当前处于停机状态,无需再次停机");
            }
        }

Game.cs 里面:

        public void Run()
        {
            
        }

        public void Stop()
        {
            
        }

接下来,我们为游戏添加一个跑的线程,因为跑嘛,就需要一直跑,所以需要一个单独的线程来做反复的工作,这样一来,我们还需要一个游戏状态标志。以便游戏可以从外部停止跑。
为了监控这一切,我们还需要把GameServerLOG方法传递给Game
所以,Game变成这样:

public class Game
    {
        private readonly GameWorld gWorld;
        private Thread RunThread;
        private bool IsRunning;
        private Action<string> LOG;
        public Game(Action<string> LOGFUN)
        {
            G.GlobeTime = 0;
            gWorld = new GameWorld();
            RunThread = null;
            IsRunning = false;
            LOG = LOGFUN;
            LOG("Game类创建成功");
        }

        public void Run()
        {
            
            IsRunning = true;
            if (RunThread == null)
            {
                RunThread = new Thread(() =>
                {
                    while (IsRunning)
                    {
                        LOG("Game 正在运行");
                        string[] buff = new string[1024000];
                        for (int i = 0; i < 1024000; i++)
                        {
                            buff[i] = "test data "+i;
                        }
                    }
                    LOG("Game 即将停止");
                });
            }
            RunThread.Start();
        }

        public void Stop()
        {
            IsRunning = false;
            while (RunThread != null && RunThread.ThreadState != ThreadState.Stopped)
            {
                LOG("Game 正在停止,请稍候");
            }
            RunThread = null;
            GC.Collect();//简单回收一下,为下次启动腾出资源。
            LOG("Game 成功停止");
        }
    }

然后GameServer变成这样:

class GameServer
    {
        private readonly MainForm Monitor;
        private readonly Game gGame;
        public int Status;
        public GameServer(MainForm monitor)
        {
            Status = 0;
            Monitor = monitor;
            LOG("GameServer创建成功");
            gGame = new Game(LOG);
        }

        private void LOG(string msg)
        {
            try
            {
                Monitor.Invoke(Monitor.Delegate_AddLog, new object[] { msg });
            }
            catch{ }
        }
        public void StartUp()
        {
            if (Status == 0)
            {
                gGame.Run();
                Status = 1;
                LOG("GameServer启动成功");
            }
            else
            {
                LOG("GameServer当前处于启动状态,请勿重复启动");
            }                
        }
        public void ShutDown()
        {
            if (Status == 1)
            {
                gGame.Stop();
                Status = 0;
                LOG("GameServer停机成功");
            }
            else
            {
                LOG("GameServer当前处于停机状态,无需再次停机");
            }
        }

    }

我在run里面加了一段占用内容资源的代码:

string[] buff = new string[1024000];
for (int i = 0; i < 1024000; i++)
{
    buff[i] = "test data "+i;
}

可以在调试的时候通过内存监控面板直观的看见GC.Collect()的必要性。

让世界跑起来

接下来,把GameWorld也调整一下,加上LOG引用,并在构造函数里面传入GameLOG
现在,为GameWorld类添加一个关键的方法——心跳(HeartBeat),用它来完成世界的运转。
GameWorld创建一个List<GameMap> gMaps用来存放这个世界的空间(地图)。
以此类推,为GameMap创建一个List<GameObject> gObjects用来存放这个地图的内容。

最后,在GameRun的"死循环"里面让GameWorld开始心跳。

Game

……

        public Game(Action<string> LOGFUN)
        {
            G.GlobeTime = 0;
            gWorld = new GameWorld(LOGFUN);
            RunThread = null;
            IsRunning = false;
            LOG = LOGFUN;
            LOG("Game类创建成功");
        }
……
        public void Run()
        {
            
            IsRunning = true;
            if (RunThread == null)
            {
                RunThread = new Thread(() =>
                {
                    while (IsRunning)
                    {
                        if (G.GlobeTime % 1000 == 0)
                        {
                            gWorld.HeartBeat();                            
                        }
                        _ = G.GlobeTime > 10000000 ? G.GlobeTime = 0 : G.GlobeTime++;

                    }
                    LOG("Game 即将停止");
                });
            }
            RunThread.Start();
        }

心跳的频率以每1000次GlobeTime为单位,如果以后觉得太慢或者太快,再自己调整。总之,这个世界最小的时间单位就是1000次的GlobeTime了,任何东西都别想穿越~~

GameWorld

    class GameWorld
    {
        private readonly List<GameMap> gMaps;
        private Action<string> LOG;
        public GameWorld(Action<string> LOGFUN)
        {
            gMaps = new List<GameMap>();
            LOG = LOGFUN;
            LOG("GameWorld类创建成功");
        }

        internal void HeartBeat()
        {
            LOG("GameWorld正在心跳 " + G.GlobeTime);
        }
    }

调试一下,感觉世界心跳的脉搏。
调试效果
好了,世界开始有了心跳,但是每次心跳该干点什么呢?我们下一篇来详细讨论。

上一篇:C#服务端的微信小游戏——多人在线角色扮演(三)
下一篇:C#服务端的微信小游戏——多人在线角色扮演(五)

请用微信扫描查看游戏效果演示

演示入口

猜你喜欢

转载自blog.csdn.net/foomow/article/details/92080828