文件存放位置

  • rocos/ZBin 文件夹下的 CLienMedusa 两个可执行文件是 OpenSLL 可视化客户端的前端与后端

  • rocos/ZBin/lua_scripts 文件夹下的 Config.lua 文件是配置软件运行的重要文件

  • rocos/ZBin/lua_scripts 文件夹下的 play 文件夹存放的 play 代码,其中分为三个子文件夹 NorRefTest 存放自动、裁判和测试代码

  • rocos/ZBin/lua_scripts/worldmodel 文件夹下存放我们书写 play 代码可以借助获取场上信息库函数

  • rocos/ZBin/lua_scripts/utils 文件夹下存放我们书写 play 代码可以借助的工具函数

  • rocos/ZBin/lua_scripts/skill 文件夹下存放小车执行状态时的不同的 sill 技能代码

Play 层的由来

  • 1997 年 CMU STP 框架是 Skill-Tactic-Play 三层
  • 2012年 ZJU 改版为 Skill-State-Play-Selector 四层
    • 方便更快的更改策略
    • 快速相应裁判盒节奏
    • play 之间相互独立

Config.lua文件

测试一个自己写的 play 代码需要对 Config.lua 进行下列修改:

  1. 将第 1 行 IS_TEST_MODE 设置为 true
  2. 将第 13 行 gTestPlay 改为要测试的代码文件名
  3. 视情况而定大概指明 gRoleFixNum 用来固定某个序号的车担任某个任务
  4. 将测试的代码文件放入 rocos/ZBin/lua_scripts/play 中的对应文件夹并在 Config.lua 文件中加入存放路径,客户端将跟踪到 rocos/ZBin/lua_scripts/play 中的三个文件夹寻找对应的代码文件

存储如果是多层子文件夹分类嵌套那么就需要都写上直到执行某个代码文件

MyTestRun.lua 代码文件书写

文件结构

  • 一个文件就是一个用 lua 书写的脚本代码 play 层

  • 开头写明需要用到的常量,或者需要用到记录位置信息等表

  • 文件中每个中括号代表一个状态 state,帮助小车确定不同情况下需要执行的相应动作,一个 play 层包含多个 state 互相跳转实现小车不同环境下实现不同的动作

state 书写

状态跳转 switch

switch=function() 函数帮助根据不同的信息确定是否需要跳转到其他 state

角色 role 与 task

Config.lua 文件中 gRoleFixNum 指明的车号就是在对应某个角色 role,只有指明的车号才能去执行任务 task,即 role 是与某个小车对应的,任务也是对应的小车执行的

  • task function 对应 skill.lua,再对应 skill.cpp
  • task function 内部代码只执行一遍,因为本身就是在一张表中所以表内角色任务的赋值就是一次!

但是 switch function 是表内的函数所以会执行多次,注意区分表的赋值和函数返还的区别

角色匹配 match

除去守门员应该是固定车号的,在赛场上某个任务不应该是由指定车号完成的。比如更多需要的是采取“就近原则”来完成从而实现策略的动态高效,这就需要借助 match 实现角色匹配

角色 role 与 task 指明动作后 match 写 role 的简写即可(角色对应可以去 RoleMatch.lua 文件中查看)匹配规则有三种对应三种括号:

  1. {} 当前状态维持之前的匹配车号,即当前状态不重新进行角色匹配

    第一次执行状态时会自动匹配一个车号,所以不用担心第一次 {} 没有匹配车号的情况

  2. [] 当前状态内部实时进行角色匹配

  3. () 每次状态切换执行一次角色匹配,即进入状态时进行一次角色匹配并在状态内部始终维持这个角色

一个括号内可以存放多个角色即进行多个角色匹配,若是多个括号则是执行完第一个括号内的角色匹配再执行第二个括号内的角色匹配

bufcnt() 函数

不同状态跳转根据 if-else 语句直接跳转过于生硬,有时可能出现小车未执行完上一个状态就转而执行下一个状态,动作并不准确。我们可以借助 bufcnt() 函数来延迟状态的跳转从而使小车动作更加流畅

函数第一个根据判断语句的 bool 值确定是否执行,第二个参数指定当返还为 true 时延迟多少帧数执行后面的代码

例如我们想实现小车到达指定位置附近 10 cm时延迟 10 帧执行状态跳转就可以写为如下

1
2
3
4
5
6
7
8
9
["run1"]={ 
switch=function()
if bufcnt(player.toTargetDist('Kicker')<10,10) then
return "run2"
end
end,
Kicker=task.goCmuRush(testPos[1],0),
match=""
},

其中判断语句借助 player.toTargetDist('Kicker') 获取前锋到指定点的距离,同样我们还可以借助 enemy.luaball.lua 或者 pos.lua 获取其他的信息用来状态判断

课后作业

题目

  • 没有球时让小车处于 (0,0)
  • 球在进攻侧 x>0 时靠近某个进攻位置
  • 球在防守侧 x<0 时靠近某个防守位置
  • 利用 bufcnt() 使程序运行更加稳定

分析

  1. 借助一个 check() 函数判断小球在场的合法性
  2. 设立三个 state 分别为 outsideforwardbackword
  3. 状态跳转注意判断条件考虑齐全,跳转到 forwardbackword 首先是必须小球在场合法然后才是判断到达攻防侧侧,否则直接考虑第二个那么小球在场外是小车到达 (0,0) 会紧接着又跑到攻防侧从而进入死循环
  4. 小球几乎跑全场所以 bufcnt 设置帧数大一些尽可能避免小车出现漂移
  5. 匹配前锋进行攻防不能进入禁区

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
local testPos={ 	--confirm the positions of three state
CGeoPoint:new_local(0,0), -- position of outside
CGeoPoint:new_local(3400,0), -- position of forward
CGeoPoint:new_local(-3400,0), -- position of backward
}

function check() -- help to check if a ball in yard
if (ball.posY()>3000 or ball.posY()<-3000 or ball.posX()>4500 or ball.posX()<-4500) then
return true;
else
return false;
end
end

gPlayTable.CreatePlay{

firstState="outside", --first put the ball at outside of yard

["outside"]={ --the state of middle
switch=function()
if bufcnt(check()==false and ball.posX()<0,10) then -- get the ball position to help judge machine next position
return "backward"
elseif bufcnt(check()==false and ball.posX()>0,10) then
return "forward"
end
end,
Kicker=task.goCmuRush(testPos[1],0),
match="{K}"
},

["forward"]={ -- the state of forward
switch=function()
if bufcnt(check()==true,10) then
return "outside"
elseif bufcnt(check()==false and ball.posX()<0,10) then
return "backward"
end
end,
Kicker=task.goCmuRush(testPos[2],0),
match="{K}"
},

["backward"]={ -- the state of backward
switch=function()
if bufcnt(check()==true,10) then
return "outside"
elseif bufcnt(check()==false and ball.posX()>0,10) then
return "forward"
end
end,
Kicker=task.goCmuRush(testPos[3],0),
match="{K}"
},

name="MyTestRun",
applicable={
exp="a",
a=true
},
attribute = "attack",
timeout = 99999
}