实验二 doPreprocess()(kick 和 dash)
1、实验目的:
- 了解 doPreprocess 的工作原理
会对 doPreprocess 进行简单的修改
2、实验设备
硬件环境:PC
软件环境:Linux3、实验内容
1. doPreprocess 的工作原理:
doPreprocess 函数负责在动作链之前对球队的各类特殊情况进行处理,它如果产生了动作就返回true,否则返回false,由后面的动作链来决定执行的动作。在 doPreprocess 中有一系列的判断来决定每个周期的动作。下面对 doPreprocess 进行简要的分析.
doPreprocess()是一个决策函数,在策略上使用的是下面这个简单的策略:// check tackle expires 检查截球有效性 // check self position accuracy 检查自身位置的有效性 // ball search 球的搜索 // check queued intention 检查队列的目的 // check simultaneous kick 检查即时的行动
我们现在先看一下球可踢时的代码:
在调用SamplePlayer::doForceKick()函数中:
bool
SamplePlayer::doForceKick()
{
const WorldModel & wm = this->world(); //获得世界模型
if ( wm.gameMode().type() == GameMode::PlayOn //比赛模式
&& ! wm.self().goalie() //不是守门员
&& wm.self().isKickable() // 球可踢
&& wm.existKickableOpponent() ) // 对手也可以进行踢球
{
Vector2D goal_pos( ServerParam::i().pitchHalfLength(), 0.0 ); // 对方球门中心
if ( wm.self().pos().x > 36.0
&& wm.self().pos().absY() > 10.0 )
{
goal_pos.x = 45.0;
}
Body_KickOneStep( goal_pos, // 踢球的函数
ServerParam::i().ballSpeedMax() // 速度
).execute( this ); // 将自身对象传过去,因为动作要调用自身对象的函数 有兴趣可以看一下里边。
this->setNeckAction( new Neck_ScanField() );
return true; // 进行了动作
}
return false; // 返回false 由动作链处理其他的内容
}
这一小段函数决定了当球在球员 Agent 的可踢范围之内时应当做的动作,这里是一个简单的根据一些情况进行踢球的方法。该程序段的条理是很清晰的。
总结一下,调用踢球的函数就是Body_KickOneStep(位置,速度).excute(this)
2.对 doPreprocess 进行简单的修改:
现在我们对 doPreprocess 进行简单的修改,让它在球可踢的时候进行带球的动作。带球就是 kick 和 dash 动作序列的结合。
带球的函数为 Body_Dribble()
它接收4个参数,第一个参数为带球要到达的目标点,第二个参数为带球的力量参数。第三个参数是提踢球后有几个dash命令。
Body_Dribble(Vector2D(0,0),
1.0,
ServerParam::i().maxDashPower(),
2
).execute(this);
调用的execute函数的返回值是一个 bool 类型。 标志着是否进行了带球动作。 知道了如何调用 dribble,我们将其加入doPreprocess函数的前边:
#include <rcsc/action/body_dribble.h>
// 注意在文件中加入需要的头文件
if ( wm.self().isKickable() ){
Vector2D goal_pos( ServerParam::i().pitchHalfLength() - 5, 0.0 );
Body_Dribble( goal_pos,
1.0,
ServerParam::i().maxDashPower(),
2
).execute(this);
return true;
}
这样,在球可踢的时候,球员 Agent 将把球带向球门,即一直向球门前快速带球。
我们再次对 doPreprocess 函数进行修改,这次是让球员 Agent 将球踢向各个不同的地方。这将调用 Body_KickOneStep()的 execute()函数来完成。下面简要说明一下Body_KickOneStep()函数的使用方法:这个函数有两个参数,第一个参数是目标点的坐标,第二个参数是球到达目标点是的速度,返回是否成功执行了动作。可以使用下面的形式来调用:
Body_KickOneStep( goal_pos,
ServerParam::i().ballSpeedMax()
).execute( this );
函数在其内部将会决定踢球时所用力量的大小,并且会判断是否能够将球踢到该点, 并作出相应的调整。比如,将球以最大的速度踢向(0,0):
if ( wm.self().isKickable() &&
Body_KickOneStep( Vector2D(0,0),
ServerParam::i().ballSpeedMax()
).execute(this))
return true;
根据上述操作,完成以下踢球操作:
- 将球踢向对方的球门。
- 将球踢向距离自己最近的队友。
- 尝试不同的踢球点。
以下是一些可能用到的函数:
- 得到对方球门的坐标.
Vector2D goal_pos( ServerParam::i().pitchHalfLength(), 0.0 );
- 得到距离自己最近的队员
PlayerObject* nearteammate = wm.teammatesFromSelf()[0];
得到一个对象的坐标
Vector2D pos = nearteammate->pos();
更多的函数请查找教材,或者查看源程序的 WorldModel.cpp, PlayerObject.cpp等文件。
根据上述操作,完成以下带球的操作:
- 用不同的带球速度进行带球。
- 将球向对方的球门方向带。
- 尝试不同的踢球位置以及速度。
根据上述内容,完成以下综合联系:
带球与踢球的结合:- 让 agent 一直向对方球门的方向带球,在进入对方禁区后以最大力量踢向球门。涉及到的具体函数请查看教材。