场景脚本编写教程——“跑道竞速”任务
任务要求
目的:要求机器人从起点出发,沿跑道行进到终点;
任务限时:180秒
成功条件:在任务限时内行进到终点;
失败条件:
    1. 在任务限时内未行进行到终点;
    2. 机器人从跑道上掉落;
    3. 机器人未完整走完个跑道(中途从高处跑道掉到低处跑道平继续行进行到终点);
    4. 未从起始点出发走到终点;
评分方式:采用时间奖励分方式(任务限时-任务用时)
构建场景
进入“构建场景”,启动场景编辑器,使用“模型”>“场景”类中的“跑道_1”到“跑道_21”按下图或自由设计构建场景。
构建要求
无论什么样式的场景都需要包括以下内容:
    1. 添加一个“模型”>“自然”>“地图_7”作为地面,在其平面上构建跑道;
    2. 按图示或自由设计一条跑道;
    3. 跑道上有一个“起始点”在其上放置一个触发方块;
    4. 跑道上有一个终点,放置一个触发方块;
    5. 在跑道的必经“关键点”上放置触发方块;
拼接跑道
按照“构建场景用户册”中“修改模型位置”章节关于“可拼接模型”的说明,构建“跑道竞速场景”。
典型的跑道竞速场景如下图:
放置“起始点”
此任务中要求机器人从跑道上开始,因此“起始点”要放置在跑道上,有2个方法实现:
●    将场景中原有起始点移动到需要的位置和高度,需要调整X、Y、Z轴,注意要求“起始点”比跑道面略高;
●    将原有“起始点”删除,重新在需要的位置添加一个“起始点”,添加时“起始点”自动放置到跑道上,且比跑道略高,只需调整X、Y轴到需要的位置。
添加触发方块
添加起点触发方块,在“起始点”上添加一个触发方块;
添加终点触发方块,在跑道终点上添加一个触发方块;
添加若干(至少5个)关键点触发器,在跑道上添加触发方块
图标 | 名称 | 缩放 | 旋转 | 作用 |
---|---|---|---|---|
起始点触发方块 | 4,1,4 | 按需要 | 检测机器人是否从起始点出发 | 终点触发方块 | 4,1,4 | 按需要 | 检测机器人是否到达终点 | 路径关键点触发方块 | 4,1,4 | 按跑道需要 | 检测机器人是否经过此处,横置在跑道上 |
按下表设置相关模型的属性。
图标 | 名称 | 属性 | 新值 | 作用 |
---|---|---|---|---|
相机限制 | 设定半径 | 100 | 放置整个跑道的中心位置,半径根据需要设定 | |
任务配置 | 一次任务限时(单位:秒) | 180(默认值) | 关注此值,设置任务限时 |
编写规则
单击“功能”>“编写脚本”,弹出“脚本逻辑”窗口,开始编写此任务的规则。
模型名称变量
在场景稍复杂,且可能会反复修改的场景中,为事件中使用的模型名称定义变量是比较好的做法。在修改场景、更新模型等之后,模型的名称发生变化后,在脚本中修改对应的名称变量,即可使原来的脚本正常工作,而不必找到脚本中所有使用此名称的位置逐个修改;
在规则文件的最后添加一个表,记录场景中的所有物体的名称等。此段代码要完整的添加到脚本文件最后。
Rule = { MissionState = "Running",-- 任务状态, Running:进行中 Failure:任务失败 RobotName = "起始点", -- 场景中"起始点"名称 TrigStart = "触发方块", -- 起始点触发方块名称 TrigEnd = "触发方块_15", -- 终点触发方块名称 Ground = "大地形", -- 地面模型名称 KeyPoint = { -- 所有关键点触发器名称,及是否接触列表 -- 1.名称 2.是否接触 {"触发方块_2", false}, -- 注意格式, {"触发方块_3", false}, {"触发方块_4", false}, {"触发方块_5", false}, {"触发方块_6", false}, {"触发方块_7", false}, {"触发方块_8", false}, {"触发方块_9", false}, {"触发方块_10", false}, {"触发方块_11", false}, {"触发方块_12", false}, {"触发方块_13", false}, {"触发方块_14", false}, }, BeginStart = false, -- 记录机器人是否从起始点出发 }
仿真复位时处理
在Game.GameReset () 函数中添加以下代码,这里主要作用是隐藏触发器和读取任务限时,在调试阶段,如要显示触发器,可以将隐藏触发器代码每行用“- -”注释;
Game.GameReset = function(self) SDK.SetVisible(Rule.TrigStart, false) -- 设置起始区触发方块不可见 SDK.SetVisible(Rule.TrigEnd, false) -- 设置终点区触发方块不可见 for k,v in pairs(Rule.KeyPoint) do -- 设置所有关键点触发方块不可见 SDK.SetVisible(v[1], false) end 游戏信息.任务限时 = IRQ_MissionConfig.任务限时 -- 读取"一次任务限时" end
仿真开始时处理
在Game.GameInit()函数中添加以下代码,使得在开始仿真时显示一条消息;
Game. GameInit = function(self) ShowMsg("跑道竞速任务开始!") -- 任务开始显示任务信息 end
“游戏信息”表添加记录
在“游戏信息”表中加入一个变量,记录任务是否完成;
游戏信息 = { 总耗时 = 0, -- 任务总时间 任务完成 = 0, -- 完成为1, 未从起始点开始,未成功,未完整走完等都为0 }
计算分数方法
在Game.ComputeResult() 函数中加入以下代码,用于计算分数。“游戏信息.任务限时”从场景的“任务配置”中读取,单位为“秒”,“游戏信息.任务时间”在仿真时由系统写入,单位为“毫秒”,算分时注意单位,Round()函数为四舍五入函数。
Game.ComputeResult = function(self) -- 任务完成时, 采用成功的算分方法 if 游戏信息.任务完成 == 1 then return 0, Round(游戏信息.任务限时 - 游戏信息.任务时间/1000,2) -- 否则采用失败的算法方法 else return 1,0 end end
运行时处理结构
在Game.Update = function(self, fdelta)函数中加入以下结构代码
Game.Update=function(self,fDelta) -- 任务运行中处理 if Rule.MissionState == "Running" then -- 接下来会添加任务进行时处理代码 -- 任务失败时处理 elseif Rule.MissionState == "Failure" then -- 接下来会添加任务失败时处理代码 end end
在“任务运行中处理”部分中添加代码,检测机器人是否从起始点出发,当机器人整体离开起始点的触发器时认为是从起始点出发;
Game.Update=function(self,fDelta) -- 任务运行中处理 if Rule.MissionState == "Running" then -- 检测机器人是否从起始点出发 -- 当标记为 false 时一直检测"机器人是否整体离开起始点触发器" if Rule.BeginStart == false and Event.IsAllOut(Rule.RobotName, Rule.TrigStart) then Rule.BeginStart = true -- 离开后标记为 true end -- 任务失败时处理 elseif Rule.MissionState == "Failure" then -- 接下来会添加任务失败时处理代码 end end
继续在“任务运行中处理”部分添加代码,检测机器人是否经过所有关键点。机器人每经过(接触到时)一个关键点触发器,将这个触发器的标记为true,由于有多个触发器,可以用遍历(类似循环)方法来对每个进行检测。
Game.Update=function(self,fDelta) -- 任务运行中处理 if Rule.MissionState == "Running" then --(检测从起始点出发) ...... -- 检测机器人是否经过所有关键点 for k,v in pairs(Rule.KeyPoint) do -- k 为记录序号, 此处无用 -- v[1] 为触发器名称, 用于接触事件检测 -- v[2] 为检测结果 -- 没检测到(false)时始终检测, 检测到之后(修改为true)不再检测 if v[2] == false and Event.IsTouch(Rule.RobotName, v[1]) then v[2] = true -- 检测到接触时标记为 true end end -- 任务失败时处理 elseif Rule.MissionState == "Failure" then -- 接下来会添加任务失败时处理代码 end end
续在“任务运行中处理”部分添加代码,以处理当机器人到达终点时的情况。首先判断机器人是否从起始点出发,如果是从起始点出发时,再判断机器人是否经过了所有触发器。
Game.Update=function(self,fDelta) -- 任务运行中处理 if Rule.MissionState == "Running" then --(检测从起始点出发) --(检测机器人是否经过所有关键点) ...... -- 机器人到达终点后处理 if Event.IsTouch(Rule.RobotName, Rule.TrigEnd) then -- 判断机器人是否从起始点出发 if Rule.BeginStart == false then -- 未从起始点出发时, 显示错误消息, 将任务状态转到失败 ShowMsg("机器人未从起始点出发,任务失败!\n5 秒钟后结束任务,请复位后重新开始。", "red") Rule.MissionState = "Failure" -- 判断机器人是否经过所有关键点 else -- 先假设经过了所有关键点 local isTrigAll = true -- 无序遍历所有关键点触发器的记录 for k,v in pairs(Rule.KeyPoint) do -- k为记录序号, 此处无用 -- v[1] 为触发器名称, 此处无用 -- v[2] 为机器人是否经过此处 if v[2] == false then -- 有一个标记为 false时, 即表示未经过所有关键点, 不用再继续检测了,结束遍历 isTrigAll = false break end end -- 判断是否经过所有关键点 -- 经过所有关键点 if isTrigAll == true then 游戏信息.任务完成 = 1 -- 在 "游戏信息" 标记任务已完成 Execution.OverGame() -- 结束仿真 else -- 未经过所有关键点, 显示错误消息, 将任务状态转到失败 ShowMsg("机器人虽然到达终点,但未完整走完整个跑道,任务失败!\n5 秒钟后结束任务,请复位后重新开始。", "red") Rule.MissionState = "Failure" end end end -- 任务失败时处理 elseif Rule.MissionState == "Failure" then -- 接下来会添加任务失败时处理代码 end end
继续在“任务运行中处理”部分添加代码,检测机器人是否掉落到地面;
Game.Update=function(self,fDelta) -- 任务运行中处理 if Rule.MissionState == "Running" then --(检测从起始点出发) --(检测机器人是否经过所有关键点) --(机器人到达终点后处理) ...... -- 检测机器人是否掉落到地面 if Event.IsCollide(Rule.RobotName, Rule.Ground) then -- 未经过所有关键点, 显示错误消息, 将任务状态转到失败 ShowMsg("机器人掉落到地面,任务失败!\n5 秒钟后结束任务,请复位后重新开始。", "red") Rule.MissionState = "Failure" end -- 任务失败时处理 elseif Rule.MissionState == "Failure" then -- 接下来会添加任务失败时处理代码 end end
任务失败时处理
继续在“任务失败时处理”部分添加代码,使得失败后,显示过失败提示后5秒后结束仿真;
Game.Update=function(self,fDelta) -- 任务运行中处理 if Rule.MissionState == "Running" then ...... -- 任务失败时处理 elseif Rule.MissionState == "Failure" then -- 失败后一直在此执行, 不再做任何检测 -- 经过 5 秒钟后结束仿真 if Event.IsContinue("失败", 5.0*1000, true) then Execution.OverGame() end end end
调试
“跑道竞速”任务的规则要求比较多,在规则编写完之后,每个成功和失败的情况都要测试到,调试规则有以下几个方法可以参考。
●    机器人掉落到地面:编写使机器人一直前进的控制程序,模拟这种情况;
●    机器人未从起始点出发:在仿真开始之前,单击仿真控制栏的“手动设置方位”按钮,将机器人向前移动,使其离开起始点触发器,再开始仿真;
●    机器人是否经过所有关键点:可以编辑场景,将其中的一个或多个触发器的高度改变,使机器人正常经过时,不能接触到此触发器;
●    任务超时:修改程序,使机器人不动,仿真开始后,等待“任务配置”中的“一次任务限时”时间;
完善
在不影响机器人运动的前提下,跑道周围或远或近放置一些建筑、树木等模型,增加场景的非富性;
可以放置“天空”模型,修改场景的天空样式和环境光;