在SelectPlay.lua中决定了要运行的脚本之后,脚本的实际执行转到Play.lua中在运行。下面就脚本运行过程进行简单介绍。
一些预备操作¶
首先,创建一个元组metaadd
,并定义table
的加法操作。
注:此处关于setmetatabla
的使用以及元组这个数据类型可以参考https://www.runoob.com/lua/lua-metatables.html
metaadd = {}
function metaadd.__add(a, b)
for _, value in ipairs(b) do
table.insert(a, value)
end
return a
end
gPlay = setmetatable({}, metaadd)
接着,对一系列变量初始化。
--gRefPlayTable, gTestPlayTable, gNorPlayTable参见config中的table
gPlay = gPlay + gRefPlayTable + gTestPlayTable + gNorPlayTable
gPlayTable = {}
gTimeCounter = 0 --计算运行时间用于判断是否超时
gCurrentState = "" --当前状态机
gLastState = "" --上一轮的状态机
gLastPlay = "" --上一轮的脚本,用于SelectPlay中
gCurrentPlay = "" --当前的脚本,用于SelectPlay中
gRealState = "" --真实的状态机
gLastCycle = 0 --上一轮的循环数
gLastRefMsg = "" --上一轮的裁判盒信息
gActiveRole = {} --场上active的球员
gIsOurIndirectKickExit = false --是敌方directkick
然后是一些工具函数
--分割字符串,可以理解为python中的类似操作
function split(inputstr, sep)
if sep == nil then
sep = "%s"
end
local t={}
for str in string.gmatch(inputstr, "([^"..sep.."]+)") do
table.insert(t, str)
end
return t
end
--计算一个table的长度
function table_length(t)
local length=0
for k, v in pairs(t) do
length=length+1
end
return length
end
CreatePlay¶
gPlayTable.CreatePlay(spec)
中的spec
是一个类似于表的数据类型,此处的CreatePlay
即具体脚本中的
gPlayTable.CreatePlay{
firstState = "start",
["start"] = {
switch = function ()
if ... then
return ...
end
end,
Leader = task.goCmuRush(START_POS,math.pi,_,flag.allow_dss),
...
match = "[L][AS][MDBCF][EH]",
},
...
name = "Ref_KickOff_th",
applicable = {
exp = "a",
a = true
},
attribute = "attack",
timeout = 99999
}
spec
中的组成部分合规:
assert(spec.firstState ~= nil)
assert(type(spec.name) == "string")
assert(spec.applicable ~= nil)
assert(spec.attribute ~= nil)
assert(type(spec.timeout) == "number")
new_spec
并初始化,等待加入gPlayTable
new_spec = {}
new_spec.firstState = spec.firstState
new_spec.switch = spec.switch
new_spec.name = spec.name
new_spec.applicable = spec.applicable
new_spec.attribute = spec.attribute
new_spec.timeout = spec.timeout
new_spec
进行一些必要的处理
for attr, attr_table in pairs(spec) do
if attr~="applicable" and type(attr_table) == "table" then --此时attr_table即为状态机
assert(attr_table.match ~= nil) --保证球员匹配不为nil
new_attr_table = {}
for rolename, roletask in pairs(attr_table) do
if type(rolename) ~= "function" and rolename ~= "switch" and string.find(rolename,"_") then --若球员名称是用_分隔的,依次把task按顺序分配给各个球员角色
roles = split(rolename,"_")
assert(table_length(roles) == table_length(roletask)) --球员数与task数相等
for i=1,table_length(roles) do
new_attr_table[roles[i]] = roletask[i]
end
else --否则直接添加球员-任务
new_attr_table[rolename] = roletask
end
end
new_spec[attr] = new_attr_table
end
end
new_spec
加入gPlayTable
RunPlay¶
这里我们先跳过IsRoleActive
和DoRolePosMatch
,先来看一看在CreatePlay之后执行的RunPlay操作
function RunPlay(name)
在SelectPlay.lua
的末尾执行,此处的name
即为脚本名
首先,确保脚本在gPlayTable
中存在(即CreatePlay
过),否则输出错误信息
--在仿真界面上输出debug信息,指明运行的脚本
debugEngine:gui_debug_msg(CGeoPoint:new_local(-50, -param.pitchWidth/2-20),gCurrentPlay)
local curPlay = gPlayTable[name]
local curState
local isStateSwitched = false
--进行状态跳转,这里有if和else是因为有的脚本把switch写在状态里面,如上面的例子脚本;有的脚本把switch写在curPlay的第一层。
if curPlay.switch ~= nil then
curState = curPlay:switch()
else
if gCurrentState ~= "exit" and gCurrentState ~= "finish" then
curState = curPlay[gCurrentState]:switch()
end
end
--设定状态机
if curState ~= nil then
gLastState = gCurrentState
gCurrentState = curState
isStateSwitched = true
world:SPlayFSMSwitchClearAll(true)
end
DoRolePosMatch
的介绍
DoRolePosMatch(curPlay, false, isStateSwitched)
然后分配task
--Play中任务返回规则,即task.lua中的返回值,如
--return {mexe, mpos, kick.auto(role), idir, pre.middle, ikp, cp.toPlayer(role), flag.nothing}
--1 ---> task, 2 --> matchpos, 3--->kick, 4 --->dir, 5 --->pre, 6 --->kp, 7--->cp, 8--->flag
kickStatus:clearAll()--清空踢球状态
for rolename, task in pairs(curPlay[gRealState]) do
if (type(task) == "function" and rolename ~= "match" and (gRoleNum[rolename] ~= nil or type(rolename)=="function")) then
--若task是任务函数、且rolenname是球员角色名、且这名角色在gRoleNum中有车号分配(此处分配在DoRolePosMatch中执行)或rolename是一个会返回球员名的函数
task = task(gRoleNum[rolename])
end
if (type(task) == "table" and rolename ~= "match" and (gRoleNum[rolename] ~= nil or type(rolename)=="function")) then
--若task是一个Table(即上面注释中说的return{...}),且rolenname是球员角色名、且这名角色在gRoleNum中有车号分配(此处分配在DoRolePosMatch中执行)或rolename是一个会返回球员名的函数
if task[1] == nil then --若task[1]是nil,则给task赋值上一轮状态的task
task = curPlay[gLastState][rolename]
end
--确定执行任务的车号,gRoleNum的细节详见RoleMatch.lua
local roleNum
if type(rolename)=="string" then
roleNum = gRoleNum[rolename]
elseif type(rolename)=="function" then
roleNum = gRoleNum[rolename()]
end
--若匹配了场上的某号车
if roleNum ~= -1 then
--若返回的task的表中3号元素(即kick的状态)不为nil
if task[3] ~= nil and task[3](roleNum) ~= kick.none() then
local mkick = task[3](roleNum)--kick设定,平射or挑射
local mdir = task[4](roleNum)--kick方向
local mpre = task[5](roleNum)--kick精确度
local mkp = task[6](roleNum)--平射力度
local mcp = task[7](roleNum)--挑射力度
local mflag = task[8]--flag
local isDirOk = world:KickDirArrived(vision:Cycle(), mdir, mpre, roleNum)
--若可以踢球了,或强制踢球,则setkick
if isDirOk or bit:_and(mflag, flag.force_kick) ~= 0 then
if mkick == kick.flat() then
kickStatus:setKick(roleNum, mkp)
elseif mkick == kick.chip() then
kickStatus:setChipKick(roleNum, mcp)
end
end
end
--print一些debug信息
if(type(rolename)=="string") then
debugEngine:gui_debug_msg(vision:OurPlayer(roleNum):Pos(), string.sub(rolename, 1, 1))
end
task[1](roleNum)--让roleNum号车执行task[1]中的task
end
end
end
DoRolePosMatch¶
紧接上面函数,我们介绍一下Play中的角色匹配问题
DoRolePosMatch()
函数在RunPlay中进行角色匹配任务,首先进行一些状态机的设定,防止在"exit"或"finish"
if gCurrentState == "exit" or gCurrentState == "finish" then
gRealState = gLastState
else
gRealState = gCurrentState
end
gActiveRole = {}
for rolename, task in pairs(curPlay[gRealState]) do
if(type(task) == "function" and rolename ~= "match" and rolename~="switch") then
task = task()
end
if(type(task) == "table" and rolename ~= "match") then
table.insert(gActiveRole, rolename) --把角色加入gActiveRole
if task.name == "continue" and gCurrentState ~= "exit" and gCurrentState ~= "finish" then
curPlay[gRealState][rolename] = {}
for i,v in ipairs(curPlay[gLastState][rolename]) do
table.insert(curPlay[gRealState][rolename], v)
end
curPlay[gRealState][rolename].name = "continue"
end
if type(rolename) == "function" then
gRolePos[rolename()] = curPlay[gRealState][rolename][2]()
else
gRolePos[rolename] = curPlay[gRealState][rolename][2]()
end
end
end
其它¶
Play.lua中还有一些次要函数,仅在此作简单介绍
-
ResetPlay(name):重新设置play,用于SelectPlay中
-
IsRoleActive(rolename):rolename在gActiveRole中
-
NeedExit(name): 状态进入"exit"or"finish",或timeout了,需要退出脚本,用于SelectPlay