深入理解博客园仿真足球竞赛平台的仿真周期
在上一篇文章中,我们讲解了如何使用如何利用CSharp球队模板编写自己的足球队,为了编写出更加智能的球队,我们需要进一步了解平台的仿真周期。
首先,我们还是来看这个图中被黄色标记的“将更新后的场上信息发送给球队*”的部分。
平台发送过来的场上信息在封装在了CnblogsDotNetSDK.Data.WorldModel中。
CnblogsDotNetSDK.Data.WorldModel中包含了10个CnblogsDotNetSDK.Data.Agent(前5个代表自己的球员信息,后5个代表对方的)信息和1个CnblogsDotNetSDK.Data.Ball的信息。
由于CnblogsDotNetSDK.Data名字空间中定义的数据是用来与平台进行数据交互的,所以你无法直接在其中加入自己定义的一些属性。
所以在每个仿真周期的开始阶段,我们会调用一个函数将只包含最基本信息的数据类型转化为自定义的数据类型。
在我们的CSharp球队模板中
我们在Main主函数中,每个仿真周期都会调用SetWorldModelFromServer这个函数。
//从服务器获得本周期的场上比赛信息 CnblogsDotNetSDK.Data.WorldModel wmData = Communicator.Instance.GetWorldModelFromServer(); //将从服务器接收到的全场信息转化为自己的信息结构 myTeam.SetWorldModelFromServer(wmData);
我们来详细看看SetWorldModelFromServer函数的实现
/// <summary> /// 将从服务器中获得的场上信息转化为自己定义的数据结构 /// </summary> /// <param name="wmData">从服务器中获得的场上信息</param> /// <remarks>每个周期开始的时候调用</remarks> public void SetWorldModelFromServer(CnblogsDotNetSDK.Data.WorldModel wmData) { //设置我方球员的信息 for (int i = 0; i < RuleSettings.Instance.AgentNum; i++) { _wm.OwnAgents[i].Pos = wmData.agents[i].Pos; _wm.OwnAgents[i].Dir = wmData.agents[i].Dir; } //设置对方球员的信息 for (int i = 0; i < RuleSettings.Instance.AgentNum; i++) { _wm.OpsAgents[i].Pos = wmData.agents[i + RuleSettings.Instance.AgentNum].Pos; _wm.OpsAgents[i].Dir = wmData.agents[i + RuleSettings.Instance.AgentNum].Dir; } //设置足球的信息 _wm.Ball.Pos = wmData.ball.Pos; _wm.Ball.Vel = wmData.ball.Vel; }
在这个函数中,我们将CnblogsDotNetSDK.Data.WorldModel中的数据转移到了自己的重新定义了DemoTeamCSharp.WorldModel,DemoTeamCSharp.Agent和DemoTeamCSharp.WorldModel。这样做可以为我们提供更多的方便,比如我们在自己定义的DemoTeamCSharp.WorldModel区分了我方的机器人(OwnAgents)和对方的机器人(OpsAgents),这样就方便了我们区分敌我的情况。
特别注意,每个仿真周期中我们从平台得到的消息都是经过平台自身处理后的,也就是说,无论你的球队实际是在哪边运行(左边或者右边),都是给的你在左边的信息。所以说,在实际编写的代码的时候,始终把自己当成是左边的队伍来考虑即可。
下面我们来简单分析一下我们可爱的球员,拿左边的蓝色球员来说
我们看到的球员大小,就是他可以踢球的范围。如何足球在这个范围内,那么球员在本周期是可以踢足球的(可以使用Kick命令)
这个球员中的红色箭头代表该踢球的身体朝向。球员的身体的朝向就是我们的球员的奔跑方向。这也就是说,球员的奔跑的时候,只能以当前自己的身体朝向前进。
为了更好地理解仿真周期,大家可以观看以下这段视频演示。
视频中我们实现了以下3个功能:
- 让球员朝向足球
Vector2f tempPos = _wm.Ball.Pos - _wm.OwnAgents[4].Pos; _commands[4].CType = CommandType.Turn; _commands[4].Parameter1 = tempPos.GetDirection();
- 让球员跑向足球
Vector2f tempPos = _wm.Ball.Pos - _wm.OwnAgents[4].Pos; if (Math.Abs(tempPos.GetDirection() - _wm.OwnAgents[4].Dir) < 2) { _commands[4].CType = CommandType.Dash; _commands[4].Parameter1 = RuleSettings.Instance.MaxDashVel; } else { _commands[4].CType = CommandType.Turn; _commands[4].Parameter1 = tempPos.GetDirection(); }
- 让球员去踢足球
Vector2f tempPos = _wm.Ball.Pos - _wm.OwnAgents[4].Pos; if (tempPos.GetLength() < RuleSettings.Instance.MaxKickBallDistance) { _commands[4].CType = CommandType.Kick; _commands[4].Parameter1 = 1.0; _commands[4].Parameter2 = 0; } else if (Math.Abs(tempPos.GetDirection() - _wm.OwnAgents[4].Dir) < 2) { _commands[4].CType = CommandType.Dash; _commands[4].Parameter1 = RuleSettings.Instance.MaxDashVel; } else { _commands[4].CType = CommandType.Turn; _commands[4].Parameter1 = tempPos.GetDirection(); }
在下一篇文章中,我们将详细了解一下平台的物理模型以及球员命令的分析。