Game Of AutoTest2游戏自动化测试的技术选型
Posted utmhikari
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Game Of AutoTest2游戏自动化测试的技术选型相关的知识,希望对你有一定的参考价值。
要做游戏自动化测试,首先需要了解游戏自动化技术。因此,本文详细讲解下游戏自动化测试领域可能用到的一些技术以及对应的场景,为自动化测试落地的技术选型提供参考。
游戏自动化测试的测试对象是游戏本身。对于游戏这个概念,可以有以下几种:
- 客户端:我们实际玩到的游戏本体,比如说PC程序,手机App,Web-App。
- 服务器:在多人网络游戏中,用以模拟游戏世界的本体。
- 编辑器:包含未烘焙到OS平台的游戏程序以及资源。
我们在技术层面上所要做到的,就是通过某些方式访问这些程序运行环境产生的内容,改变游戏呈现以及玩家行为,操作玩家或游戏程序本身,达到我们的测试目的。在笔者的工作经验当中,主要做的是UE
安卓客户端的自动化,应用场景主要在业务功能测试方面,因此本文会对客户端自动化做稍微详尽的解析。其他自动化的方案和叙述,如果其中描述有所纰漏,恳请指正。
客户端自动化
针对游戏客户端的自动化,在游戏测试里是最为广泛应用的,不仅是因为客户端是一个游戏必须有的成分,而更加因为我们在手工测试游戏的时候,实际是拿着客户端来测的。因此,客户端自动化会最贴合游戏功能测试的需求。
要实现客户端自动化,有以下的方法:
- 脚本自动化:通过游戏脚本代码驱动游戏运行。
- 适用于业务逻辑主要采用可热更的脚本语言(如
lua
、js
)编写的游戏。 - 游戏对外暴露一个接口,对这个接口发送脚本代码,游戏内部即可自动执行这段脚本代码。
- UI自动化:通过UI操作驱动游戏运行,分为UI查询、UI操作两部分。
- UI查询可以通过图像识别/OCR识别推断控件所在坐标,也可以通过从游戏运行环境中导出UI控件树,获取每个控件的属性和在视口里的坐标数据。
- 由控件树定位控件的方案,相较于图像识别的方案,稳定性会更高,但也需要一定的定位策略。通常来讲,控件之间的层级关系不会有太多的变化,而控件的名称会微调,因此可以通过关键字+层级关系的条件去精准筛选控件。
- 图像识别方案不稳定在于几点:图像识别技术本身的不确定性,以及UI风格迭代导致图像识别的基线数据失效。
- UI操作可以通过
adb
之类的方式直接模拟屏幕操作;也可以在游戏中运行一个UI自动化模块,测试人员可以向这个模块请求对某个UI发起点击/按下/释放的操作,而后这个模块内部执行UI实例OnClick/OnPress/OnRelease
的委托。
- 指令自动化:在游戏逻辑中预置一些玩家操作接口以及参数,通过调用指令实现玩家操控。
- 游戏内部暴露给外部指令调用接口,外部发送指令和参数,然后游戏内部做
dispatch
,去执行对应封装好的指令。 - 对于手工测试需求而言是标配,对于自动化测试需求而言,只需要额外支持外部程序调用这些内容。
客户端自动化的开源实现也有不少,其中有两个比较广泛应用:Airtest
和GAutomator
。
Airtest
:一个完整的游戏自动化测试方案,参考这个文档
GAutomator
:针对Unity
、UE
的UI自动化测试框架,源码在这里
一般来讲,开源的自动化方案基本上是UI自动化相关,而脚本自动化和指令自动化,则需要依据不同游戏项目的实现去自定义相关逻辑。
客户端自动化适用于以下的场景:
- 功能测试:代替人工测试工作。
- 玩法冒烟测试:适用于各个玩法的基础功能冒烟。
- 可以应用于强UI的玩法,比如商业化、活动、社交、装备一类。
- 可以应用于流程类的玩法,比如副本、任务一类。
- 功能遍历测试:适用于操作不复杂但重复量较大,繁琐耗时的功能测试任务。
- 依据不同玩法的测试点特性而定,每个玩法可能都有测试点是遍历性质的。
- 可以应用于地图/大世界相关的跑测,比如对场景物件、寻路、空气墙、切线的效果检查。
- 可以应用于玩家状态机、
Buff
或是技能效果的遍历测试。
- 专项测试:自动执行整个专项测试流程,或是作为流程的一部分,提供对测试有用的信息。
- 客户端性能测试:跑测游戏客户端,并在其中收集游戏以及设备的性能数据,用于做性能分析。
- 客户端适配测试:跑测不同机型的客户端,覆盖一些简单的场景跟操作,看性能或者表现上是否一致。
- UI专项测试:这里指UI状态互斥、UI层级关系这一类非强业务相关的专项测试点。
- 玩法业务专项:跑测游戏客户端,收集特定游戏玩法数据,用于玩家状态检查、玩法效果评估、数据一致性检查等测试点。需要按各个玩法的特性去自定义自动跑测逻辑。
- 玩法环境构造:调用多台客户端机器,自动游玩一个玩法,模拟多人场景。
- 用真实客户端构造场景,通常会消耗许多线上机器资源。因此在资源紧缺或是线上运行环境不稳定的情况下,不建议采取此方案。
不论是功能还是专项测试场景,在自动化行为的实现上,脚本、UI、指令三种自动化方式都可以相互结合。这是因为,从游戏测试用例过程的角度来看,每一个步骤其实都是一个玩家操作,而玩家操作的实现,可以是发一段脚本,也可以是点某个UI,更可以是执行一个指令。
相对于其它自动化方式,脚本自动化的实现方案是最优先的,这是因为能够支持热执行客户端脚本的话,理论上可以操纵整个客户端的运行,从而能够满足更多自动化行为的需求。因此如果有条件,比如说测试侧有项目的源代码权限,那么自动化行为的实现,就优先采用脚本编写的方案,而对于一些需要复杂脚本实现的行为,才用UI操作代替。如果没有源码条件,也可以考虑在客户端源码里抽出一个单独的project
,包装项目代码API
类型信息以及一系列自动化指令,做成一个测试侧也可以单独维护的自动化底层模块。
服务器自动化
针对服务器的自动化测试是非常关键的一部分。比如我们经常提到的服务器压测,其实本质上是一种自动化驱动的逻辑。从整个游戏产品的质量保证而言,服务器自动化同样是不可或缺的。
要实现服务器自动化,一般是两种方法:
- 协议交互:通过
C/S
协议交互的方式驱动游戏逻辑进行
- 一般是通过一个机器人客户端连到服务器,作为一个玩家的身份发送登录协议登入游戏,然后再发送其他
C/S
协议,从而驱动玩家做某些行为,或者是达到某些状态。
- 内部指令调用:服务器预编写一些测试接口,通过调用这些接口驱动服务器逻辑。
- 可以是通过机器人客户端连到服务器,发送登录协议登入游戏,但之后不直接通过
C/S
协议,而是通过某些外部调用,或者是这个机器人发送某个特定的指令,驱动服务器逻辑,从而让机器人参与到某些测试活动当中。
具体的技术实现,实际得依据服务器本身的技术栈以及协议交互的技术栈而定,并没有一套确定的方案。
服务器自动化适用于以下场景:
- 服务器代码单元测试
- 用以在程序层面测试服务器的基础代码逻辑,通常会配合
CI
流水线使用。 - 也可以配合代码覆盖率测试工具一起使用,用以观测测试用例/代码实现的完整性。
- 实现上,可以调用某个接口指令,一次性批量或有序地执行服务器业务模块的单元测试代码。
- 压力测试
- 驱动多个机器人登录服务器,模拟玩家操作。
- 需配合服务器性能监控工具,观测服务链路性能表征,分析性能瓶颈。
- 功能测试环境构造
- 实现原理和压力测试类似,通常会涉及多个机器人玩家在游戏中漫游。
- 相比于用多个真实客户端设备构造多人玩法环境的方法,通过机器人构造更加方便,不浪费设备资源。
- 可以应用于匹配、登录、世界
Boss
等涉及海量玩家参与的玩法模块。
- 服务器协议安全测试
- 同样可以通过机器人的方式驱动,但测试策略需参考协议测试需求。
- 通常的测试目标是,绕过客户端做游戏机制限定之外的请求,预期无法通过服务器业务逻辑的校验。
编辑器自动化
编辑器代表着游戏的开发环境,其中的资源会直接影响到游戏的构建结果,因此针对编辑器及资产的自动化测试也是重要的一部分。以UE
为例,其中内置了许多自动化测试的机制:
编辑器自动化适用于以下的场景:
- 编辑器与开发者工具的功能测试
- 引擎核心功能测试
- 游戏资产静态测试
- 测试对象可以是美术、UI甚至是特定玩法的资产,主要测试的场景是对资产配置的合规性和可用性检查。
- 许多游戏客户端本体和策划
excel
配置可能是分离的,由于在本文范畴中,excel
配置不直接影响游戏构建结果,因此关于excel
配置的合规性检查暂不纳入本文叙述范围。
- 关卡配置检查
- 打开特定关卡/子关卡,静态检查关卡内场景物件配置的合规性。
- 启动
PIE
,在游玩期间进行功能测试。
- 如果偏重于系统玩法级的功能测试,并且客户端构建频繁交付,实际用客户端自动化方案会更贴合产品测试的需求。
自动化周边技术
客户端/服务器/编辑器自动化技术,是游戏自动化实现的最底层逻辑。在此基础上,还可以加码一些周边技术包装,从而优化自动化测试的效果。
录制回放
通常在微服务测试中,会通过录制的方式记录真实访问的流量,作为测试用例,然后在测试环境随时回放,从而起到观测真实流量反映的作用。
在游戏自动化中,也存在录制回放的操作,服务器跟客户端都能做。以客户端为例,录制回放主要是在UI测试中应用,技术实现上大致有两种方法:
- 图像识别
- 录制:根据屏幕操作坐标,记录操作时间,以及坐标附近的特征图。
- 回放:根据特征图,识别特征图在屏幕中的位置,再根据操作时间,有序地模拟屏幕操作。
- 输入事件
- 录制:引擎底层记录玩家操作的输入事件,以及操作时间。
- 回放:根据输入事件,推断操作控件与操作方式,再根据操作时间,有序地调用控件对应的操作。
客户端UI测试的录制回放技术,可以用到的场景有:
- UI功能测试:测试游戏UI的可用性,监控不同版本UI呈现的变化。
- UI耗时测试:测试特定玩家UI操作下,界面加载的性能损耗。
- 录制的产物,可用于快速生成UI自动化测试用例代码。
行为树
行为树是AI的一种实现,主要通过行为抽象+流程控制的方式,实现AI机器人的自动决策。
在自动化测试领域,通常会提到AI自动化,而行为树就是AI自动化的一种典型案例。但这里需要强调的是,AI驱动并不是最底层的自动化技术,以运行在游戏客户端的行为树为例,每个行为本身的实现,还是得走UI自动化、脚本自动化的套路。AI驱动的,只是业务逻辑,不是游戏本身。
行为树决策的逻辑是人为制定的,因此引入行为树,会很适合一些操作复杂,耗时长,但比较有规律的测试场景,让这些场景实现自动化的成本大大降低。比如说:
- PVE副本、任务:这类型的玩法通常分为很多环,每一环需要根据副本阶段指引信息、任务类型信息等采取行为决策,因此比较适合用行为树表征。
- PVP、竞技玩法:这类型的玩法通常有特定的游玩规则/目标,因此也适合行为树去做决策。
针对自动化测试本身而言,行为树也可以帮助我们快速确定基础的自动化逻辑结构,生成基础代码,提高自动化研发效率。但也需要注意,行为树不能作为代码编程的完整代替方案,否则一旦行为复杂度较高,行为树本身就难以维护,详情可以参考这篇文章。
行为树方案,建议是配套可视化编程工具提效开发和调试工作,否则研发效率并不一定优于纯粹的代码编写。
机器学习
机器学习是一种更为复杂的AI实现案例,有几个特点:
- 有一定的目标,实质是通过不断的运算,来达到特定的目标,通过自学习的方式形成更多的经验。
- 决策的过程是黑盒的,经验论,无法追溯其中的思维链路。
- 学习方式上有监督(人工数据介入)或是无监督均可,但无论如何都需要处理大量的数据作为决策依据。
因此机器学习的方案,可以考虑在几个自动化测试的场景应用:
- 行为复杂度较高,不容易被行为树表征的的PVP、竞技玩法。
- 训练竞技AI,收集数据,用以评价玩法数值配置的合理性,或是做漫游探索测试,尝试发现问题。
- 需要识别客户端显现异常图像的场景。
- 结合客户端自动化方案执行,可用于监测捏脸/时装等丰富美术场景的显示异常,也可以用于场景穿模、适配闪屏等场景的监控。
「网易官方」极客战记(codecombat)攻略-游戏开发2-硬币游戏第 5 步:平衡-game-of-coins-step-5-balance
(点击图片进入关卡)
千里之堤,溃于蚁穴。
简介
在 Pac-man 风格的街机游戏系列的最后一关,我们将平衡游戏。
游戏逻辑准备就绪。 在之前的关卡中,我们使用高英雄速度等非均衡游戏参数而是简化游戏测试的早期阶段。
然而,游戏的最终版本应该是平衡的。 如果太容易,玩家会感到无聊。 如果太难了,那么他们会很沮丧,很快放弃。
为了简化平衡过程,我们已将所有游戏参数移至代码顶部 所以你可以在一个地方看到他们,并迅速做出改变。
目前游戏参数设置为噩梦难度。 尽量让它更容易。 别担心,没有 "ideal" 的参数,也没有正确的答案。
你决定参数应该是多少以及游戏有多难。 尝试使其具有挑战性,但有趣!
使用键盘或鼠标进行播放器控制:
- W -> 向上
- S -> 向下
- A -> 左边
- D -> 右边
- 鼠标点击 -> 移动到该点
默认代码
# 游戏测试人员不仅应该发现错误,还要解决平衡问题。
# 游戏参数
# 游戏参数定义游戏难度。
# 改变游戏参数来平衡游戏。
# 强化时间需要多长时间(秒)。
POWER_DURATION = 2 # ?
# 硬币,蘑菇和食人魔的得分值。
COIN_SCORE = 1 # ?
MUSHROOM_SCORE = 5 # ?
DEFEATED_SCORE = 10 # ?
# 每场比赛时间流逝得分减少多少。
TIME_SCORE = 1 # ?
# 玩家和食人魔的速度。
HERO_SPEED = 17 # ?
OGRE_SPEED = 15 # ?
# 食人魔产生延迟(秒)。
SPAWN_DELAY = 4 # ?
# 关卡布局
# 障碍和可收集的物品。
game.spawnXY("forest", 16, 16)
game.spawnXY("forest", 32, 16)
game.spawnXY("forest", 48, 16)
game.spawnXY("forest", 64, 16)
game.spawnXY("forest", 16, 32)
game.spawnXY("forest", 32, 32)
game.spawnXY("forest", 48, 32)
game.spawnXY("forest", 64, 32)
game.spawnXY("forest", 16, 48)
game.spawnXY("forest", 32, 48)
game.spawnXY("forest", 48, 48)
game.spawnXY("forest", 64, 48)
game.spawnXY("bronze-coin", 16, 8)
game.spawnXY("bronze-coin", 24, 8)
game.spawnXY("bronze-coin", 32, 8)
game.spawnXY("bronze-coin", 48, 8)
game.spawnXY("bronze-coin", 56, 8)
game.spawnXY("bronze-coin", 64, 8)
game.spawnXY("bronze-coin", 72, 8)
game.spawnXY("bronze-coin", 8, 16)
game.spawnXY("bronze-coin", 24, 16)
game.spawnXY("bronze-coin", 40, 16)
game.spawnXY("bronze-coin", 56, 16)
game.spawnXY("bronze-coin", 72, 16)
game.spawnXY("bronze-coin", 8, 24)
game.spawnXY("bronze-coin", 16, 24)
game.spawnXY("bronze-coin", 24, 24)
game.spawnXY("bronze-coin", 32, 24)
game.spawnXY("bronze-coin", 40, 24)
game.spawnXY("bronze-coin", 48, 24)
game.spawnXY("bronze-coin", 56, 24)
game.spawnXY("bronze-coin", 64, 24)
game.spawnXY("bronze-coin", 72, 24)
game.spawnXY("bronze-coin", 24, 32)
game.spawnXY("bronze-coin", 56, 32)
game.spawnXY("bronze-coin", 8, 40)
game.spawnXY("bronze-coin", 16, 40)
game.spawnXY("bronze-coin", 24, 40)
game.spawnXY("bronze-coin", 32, 40)
game.spawnXY("bronze-coin", 40, 40)
game.spawnXY("bronze-coin", 48, 40)
game.spawnXY("bronze-coin", 56, 40)
game.spawnXY("bronze-coin", 64, 40)
game.spawnXY("bronze-coin", 72, 40)
game.spawnXY("bronze-coin", 8, 48)
game.spawnXY("bronze-coin", 24, 48)
game.spawnXY("bronze-coin", 40, 48)
game.spawnXY("bronze-coin", 56, 48)
game.spawnXY("bronze-coin", 72, 48)
game.spawnXY("bronze-coin", 8, 56)
game.spawnXY("bronze-coin", 16, 56)
game.spawnXY("bronze-coin", 24, 56)
game.spawnXY("bronze-coin", 32, 56)
game.spawnXY("bronze-coin", 48, 56)
game.spawnXY("bronze-coin", 56, 56)
game.spawnXY("bronze-coin", 64, 56)
game.spawnXY("bronze-coin", 72, 56)
game.spawnXY("mushroom", 40, 8)
game.spawnXY("mushroom", 8, 32)
game.spawnXY("mushroom", 72, 32)
game.spawnXY("mushroom", 40, 56)
# 游戏设置
# 目标,UI和游戏全局变量
game.score = 1000
game.powerDuration = POWER_DURATION
game.powerTime = 0
game.powerEndTime = 0
ui.track(game, "time")
ui.track(game, "score")
ui.track(game, "powerTime")
game.addCollectGoal()
game.addSurviveGoal();
# 英雄设置
# 玩家,其参数和事件处理程序。
player = game.spawnPlayerXY("knight", 8, 8)
player.maxSpeed = HERO_SPEED
def powerPlayerUp():
player.scale = 2
player.attackDamage = 100
game.powerEndTime = game.time + game.powerDuration
def powerPlayerDown():
player.scale = 1
player.attackDamage = 1
def onCollect(event):
player = event.target
item = event.other
if item.type == "bronze-coin":
game.score += COIN_SCORE
if item.type == "mushroom":
game.score += MUSHROOM_SCORE
powerPlayerUp()
def onCollide(event):
player = event.target
other = event.other
if other.type == "scout" and player.scale == 2:
other.defeat()
player.on("collect", onCollect)
player.on("collide", onCollide)
# 敌人
# 敌方发生器,敌人事件和参数。
generator = game.spawnXY("generator", 41, 31)
generator.spawnType = "scout"
generator.spawnDelay = SPAWN_DELAY;
def onSpawn(event):
unit = event.target
unit.maxSpeed = OGRE_SPEED
unit.attackDamage = player.maxHealth
while True:
if player.scale == 2:
unit.behavior = "RunsAway"
else:
unit.behavior = "AttacksNearest"
def onDefeat(event):
game.score += DEFEATED_SCORE
game.setActionFor("scout", "spawn", onSpawn)
game.setActionFor("scout", "defeat", onDefeat)
# 游戏循环
# 主要的游戏循环具有基于时间的功能。
def checkTimeScore():
game.score -= TIME_SCORE
if game.score < 0:
game.score = 0
def checkPowerTimer():
game.powerTime = game.powerEndTime - game.time
if game.powerTime <= 0:
game.powerTime = 0
if player.scale == 2:
powerPlayerDown()
def checkTimers():
checkTimeScore()
checkPowerTimer()
while True:
checkTimers()
# 赢得游戏胜利!
概览
尝试逐个更改参数,而不是每次迭代更改所有参数。 当你同时改变几个参数时,很难理解游戏参数的影响。
但是有时游戏参数具有协同作用,只有在将它们一起更改时才能看到结果。 没有正确的答案。 只需测试各种参数并分析结果。
硬币游戏第 5 步:平衡 解法
# 游戏测试人员不仅应该发现错误,还要解决平衡问题。
# 游戏参数
# 游戏参数定义游戏难度。
# 改变游戏参数来平衡游戏。
# 强化时间需要多长时间(秒)。
POWER_DURATION = 4 # ?
# 硬币,蘑菇和食人魔的得分值。
COIN_SCORE = 1 # ?
MUSHROOM_SCORE = 5 # ?
DEFEATED_SCORE = 10 # ?
# 每场比赛时间流逝得分减少多少。
TIME_SCORE = 0.5 # ?
# 玩家和食人魔的速度。
HERO_SPEED = 22 # ?
OGRE_SPEED = 12 # ?
# 食人魔产生延迟(秒)。
SPAWN_DELAY = 6 # ?
# 关卡布局
# 障碍和可收集的物品。
game.spawnXY("forest", 16, 16)
game.spawnXY("forest", 32, 16)
game.spawnXY("forest", 48, 16)
game.spawnXY("forest", 64, 16)
game.spawnXY("forest", 16, 32)
game.spawnXY("forest", 32, 32)
game.spawnXY("forest", 48, 32)
game.spawnXY("forest", 64, 32)
game.spawnXY("forest", 16, 48)
game.spawnXY("forest", 32, 48)
game.spawnXY("forest", 48, 48)
game.spawnXY("forest", 64, 48)
game.spawnXY("bronze-coin", 16, 8)
game.spawnXY("bronze-coin", 24, 8)
game.spawnXY("bronze-coin", 32, 8)
game.spawnXY("bronze-coin", 48, 8)
game.spawnXY("bronze-coin", 56, 8)
game.spawnXY("bronze-coin", 64, 8)
game.spawnXY("bronze-coin", 72, 8)
game.spawnXY("bronze-coin", 8, 16)
game.spawnXY("bronze-coin", 24, 16)
game.spawnXY("bronze-coin", 40, 16)
game.spawnXY("bronze-coin", 56, 16)
game.spawnXY("bronze-coin", 72, 16)
game.spawnXY("bronze-coin", 8, 24)
game.spawnXY("bronze-coin", 16, 24)
game.spawnXY("bronze-coin", 24, 24)
game.spawnXY("bronze-coin", 32, 24)
game.spawnXY("bronze-coin", 40, 24)
game.spawnXY("bronze-coin", 48, 24)
game.spawnXY("bronze-coin", 56, 24)
game.spawnXY("bronze-coin", 64, 24)
game.spawnXY("bronze-coin", 72, 24)
game.spawnXY("bronze-coin", 24, 32)
game.spawnXY("bronze-coin", 56, 32)
game.spawnXY("bronze-coin", 8, 40)
game.spawnXY("bronze-coin", 16, 40)
game.spawnXY("bronze-coin", 24, 40)
game.spawnXY("bronze-coin", 32, 40)
game.spawnXY("bronze-coin", 40, 40)
game.spawnXY("bronze-coin", 48, 40)
game.spawnXY("bronze-coin", 56, 40)
game.spawnXY("bronze-coin", 64, 40)
game.spawnXY("bronze-coin", 72, 40)
game.spawnXY("bronze-coin", 8, 48)
game.spawnXY("bronze-coin", 24, 48)
game.spawnXY("bronze-coin", 40, 48)
game.spawnXY("bronze-coin", 56, 48)
game.spawnXY("bronze-coin", 72, 48)
game.spawnXY("bronze-coin", 8, 56)
game.spawnXY("bronze-coin", 16, 56)
game.spawnXY("bronze-coin", 24, 56)
game.spawnXY("bronze-coin", 32, 56)
game.spawnXY("bronze-coin", 48, 56)
game.spawnXY("bronze-coin", 56, 56)
game.spawnXY("bronze-coin", 64, 56)
game.spawnXY("bronze-coin", 72, 56)
game.spawnXY("mushroom", 40, 8)
game.spawnXY("mushroom", 8, 32)
game.spawnXY("mushroom", 72, 32)
game.spawnXY("mushroom", 40, 56)
# 游戏设置
# 目标,UI和游戏全局变量
game.score = 1000
game.powerDuration = POWER_DURATION
game.powerTime = 0
game.powerEndTime = 0
ui.track(game, "time")
ui.track(game, "score")
ui.track(game, "powerTime")
game.addCollectGoal()
game.addSurviveGoal();
# 英雄设置
# 玩家,其参数和事件处理程序。
player = game.spawnPlayerXY("knight", 8, 8)
player.maxSpeed = HERO_SPEED
def powerPlayerUp():
player.scale = 2
player.attackDamage = 100
game.powerEndTime = game.time + game.powerDuration
def powerPlayerDown():
player.scale = 1
player.attackDamage = 1
def onCollect(event):
player = event.target
item = event.other
if item.type == "bronze-coin":
game.score += COIN_SCORE
if item.type == "mushroom":
game.score += MUSHROOM_SCORE
powerPlayerUp()
def onCollide(event):
player = event.target
other = event.other
if other.type == "scout" and player.scale == 2:
other.defeat()
player.on("collect", onCollect)
player.on("collide", onCollide)
# 敌人
# 敌方发生器,敌人事件和参数。
generator = game.spawnXY("generator", 41, 31)
generator.spawnType = "scout"
generator.spawnDelay = SPAWN_DELAY;
def onSpawn(event):
unit = event.target
unit.maxSpeed = OGRE_SPEED
unit.attackDamage = player.maxHealth
while True:
if player.scale == 2:
unit.behavior = "RunsAway"
else:
unit.behavior = "AttacksNearest"
def onDefeat(event):
game.score += DEFEATED_SCORE
game.setActionFor("scout", "spawn", onSpawn)
game.setActionFor("scout", "defeat", onDefeat)
# 游戏循环
# 主要的游戏循环具有基于时间的功能。
def checkTimeScore():
game.score -= TIME_SCORE
if game.score < 0:
game.score = 0
def checkPowerTimer():
game.powerTime = game.powerEndTime - game.time
if game.powerTime <= 0:
game.powerTime = 0
if player.scale == 2:
powerPlayerDown()
def checkTimers():
checkTimeScore()
checkPowerTimer()
while True:
checkTimers()
# 赢得游戏胜利!
本攻略发于极客战记官方教学栏目,原文地址为:
https://codecombat.163.com/news/jikezhanji-yingbiyouxipingheng
极客战记——学编程,用玩的!
以上是关于Game Of AutoTest2游戏自动化测试的技术选型的主要内容,如果未能解决你的问题,请参考以下文章
「网易官方」极客战记(codecombat)攻略-游戏开发2-硬币游戏第 5 步:平衡-game-of-coins-step-5-balance
基于图像识别的AI自动化测试框架:GAME AI SDK平台解析
[USACO08FEB]连线游戏Game of Lines
洛谷 P2665 [USACO08FEB]连线游戏Game of Lines
Game of Cards Gym - 102822G
ZOJ 3964 Yet Another Game of Stones Nim游戏变种