C#服务端的微信小游戏——多人在线角色扮演(四)
时间、空间和物质,这就是宇宙的全部,让世界在你的服务器上运转起来吧。
——茂叔
GameServer的第一个工作。
在上一篇里,服务端的GameServer
启动后,并没有进行任何实际的工作,只是通知GameMonitor
它已经启动了。这个相当于我们上班打卡,然后在座位上补瞌睡……现在,让我们看看有什么活需要干吧。
首先,我们知道GameServer
的功能主要有三个:
- 让游戏跑起来
- 接受和处理客户端发过来的请求
- 把客户需要的信息发回客户端
现在我们先处理让游戏跑起来
的工作。后面两项工作将来配合客户端开发来一起完成。
所以,我们要有个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;
就可以了。
给GameServer
增加一个成员private readonly Game gGame
,这个就是我们的游戏了。
在GameServer
的StartUp
方法里面,添加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()
{
}
接下来,我们为游戏添加一个跑的线程,因为跑嘛,就需要一直跑,所以需要一个单独的线程来做反复的工作,这样一来,我们还需要一个游戏状态标志。以便游戏可以从外部停止跑。
为了监控这一切,我们还需要把GameServer
的LOG
方法传递给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
引用,并在构造函数里面传入Game
的LOG
。
现在,为GameWorld
类添加一个关键的方法——心跳(HeartBeat
),用它来完成世界的运转。
为GameWorld
创建一个List<GameMap> gMaps
用来存放这个世界的空间(地图)。
以此类推,为GameMap
创建一个List<GameObject> gObjects
用来存放这个地图的内容。
最后,在Game
的Run
的"死循环"里面让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#服务端的微信小游戏——多人在线角色扮演(五)
请用微信扫描查看游戏效果演示