写在前面
在小型足球机器人的比赛中,因为足球机器人并不会像人一样那么智能,所以球出界是非常正常的事情。球出界之后就是进行罚球,因此罚球脚本就显得非常重要了,在所有的发球场景中,角球又是最容易进球的一个场景,因此我将分析旋转角球的task层和skill层实现。
自然语言描述
如上图所示,中场会在上半边的场内绕某点做公转的同时进行自转的操作,前锋会过去拿球,前锋拿到球过一段时间之后会将高尔夫球挑射到对面去,在前锋挑球之后,中场会根据最小二乘法拟合出来的直线去接球,接到球之后会直接射门。
Task层实现
我是基于有限状态机实现的,先放一下我的Lua程序:
gPlayTable.CreatePlay{
firstState = "GetBall",
--该场景已转换为比赛脚本
--转圆圈甩人
["GetBall"] = {
switch = function()--2.5张/秒
if Cbuf_cnt(CIsGetBall("Kicker"),450) then -- 改变这个时间可以控制跑位的时间,一定要在8s之内
return "delay"
end
end,
Receiver = task.ReceiverTask("runcircle"),
Tier = task.TierTask("def"),
Kicker = task.KickerTask("GetBall_Front"),--拿球加上传
Goalie = task.GoalieTask("goalie")
},
["delay"] = {
switch = function()
if CIsBallKick("Kicker") then -- 延迟时间由测试得出
return "PassBall"
end
end,
Receiver = task.ReceiverTask("runcircle"),
Kicker = task.KickerTask("cpkick_c1"),--拿球加上传球
Tier = task.TierTask("def"),
Goalie = task.GoalieTask("goalie")
},
["PassBall"] = {
switch = function()
if CIsGetBall("Receiver") then --CIsGetBall("Kicker")
return "Shoot"
end
end,
Receiver = task.ReceiverTask("GoRecPos_Front"),--接球的朝向为射门点
Kicker = task.GotoPos("Kicker",150,0,0),
Tier = task.TierTask("def"),
Goalie = task.GoalieTask("goalie")
},
["Shoot"] = {
switch = function()
if CIsBallKick("Receiver") then
return "finish"
end
end,
Receiver = task.Shoot("Receiver"),--该射门为拿球转向
Kicker = task.GotoPos("Kicker",150,0,0),
Tier = task.TierTask("def"),
Goalie = task.GoalieTask("goalie")
},
name = "connerkick_3"
}
整个Task层就是按照拿球->延时->传球->射门的顺序进行,在这个场景的动作中,守门员和后卫都在执行防守任务,因此不做特殊分析,主要来看前锋和中场的动作。
在Lua层动作的调用中,采用Kicker = task.KickerTask("GetBall_Front")的格式来调用,等号左边是需要控制的足球机器人的角色名称,右边task.KickerTask()是固定的,括号里面放的是C++底层写的动作的名字,这样一个语句的意思就是让Kicker去执行GetBall_Front动作。
言归正传,中场执行的是转圈和射门的动作,所以他调用的脚本就是runcircle和GoRecPos_Front,前锋执行的是拿球和挑球的动作,因此他调用的脚本就是GetBall_Front和cpkick_c1。
Skill层实现
接下来看中场转圈动作分析,我们确实一个中场转圈的中点,然后让中场绕该点做半径一定的旋转即完成了公转,那么如何实现自转呢?
这个也是很简单啦,开始时以对面球门中点和中场运动起始点连线为初始0度,随着循环调用的进行,每次都给中场一个增加的角度,从而实现了中场的自传,具体程序实现看下面:
#include "utils\maths.h"
//用户注意;接口需要如下声明
extern "C"_declspec(dllexport) PlayerTask player_plan(const WorldModel* model, int robot_id);
PlayerTask player_plan(const WorldModel* model, int robot_id){
PlayerTask task;
const point2f& ball = model->get_ball_pos(); //球的坐标
const point2f& runner = model->get_our_player_pos(robot_id); //球员的坐标
const point2f& goal = FieldPoint::Goal_Center_Point;//我方球门的左边
const point2f& opp_goal = -FieldPoint::Goal_Center_Point;//对方球门的坐标
const float& dir = model->get_our_player_dir(robot_id);//获取我方球员的朝向
int convert = ball.y > 0 ? -1 : 1;
/****************************/
//下面三个参数由测试得出
point2f pos(210, 105);
pos.y *= convert;
float circleR = 18;//中场公转的半径
float DetAngle = 0.9;//中场公转的变化角度
float dir_angle = 0.7;//中场自传的变化角度
/****************************/
task.target_pos = pos + Maths::vector2polar(circleR, (runner - pos).angle() + convert*DetAngle);//实现改变中场的公转位置
task.orientate = dir + convert*dir_angle;//实现中场自转角度的改变
task.flag = 1;
return task;
}
其中pos(210, 105)就是中场公转的中点,convert参数是为了区别左右半场。
评论(4)
您还未登录,请登录后发表或查看评论