- 一. 游戏内教程汉化
- 1. 循环和导入(Loop and Import)
- 2. 简单移动(Simple move)
- 3. 首次攻击(First Attack)
- 4. 爬虫的身体部分(Creeps Bodies)
- 5. 存储和转移 (Store and Transfer)
- 6. 地形 (Terrain)
- 7. 生产爬虫(Spawn Creeps)
- 8. 收割能源(Harvest Energy)
- 9. 建设(Construction)
- 10. 最终测试(Final Test)
Screeps Arena 是一款纯编程的RTS游戏(即时战略游戏),通过编写JavaScript(或其他语言)代码,控制从采集资源、生产单位、建造、移动、攻击的各种操作和应变逻辑,来让自己的战略运行起来,和其他的代码成果进行对局。
一. 游戏内教程汉化
1. 循环和导入(Loop and Import)
欢迎来到本教程,您将学习如何通过编码发挥爬虫竞技场 (Screeps Arena) 的基础知识。
你通过在本地文件夹中编写代码来玩游戏,游戏内的编辑器不好使,所有我们推荐你们使用一些其他的代码编辑器,例如vscode来编写代码。
循环:
当您点击PLAY按钮时,这些文件被提交给服务器。如果你的代码中没有语法错误,游戏就会开始,你就可以观看了。你不能在游戏期间更改提交的代码,比赛几秒钟后就结束了之后,你可以用任何速度观看。
每一个回合(或者我们称之为tick),游戏都会运行你的主函数 loop()
。该函数在main.mjs
中被定义:
export function loop() {
// Your code goes here
}
这个函数中的所有内容都将被反复执行,直到游戏结束。
导入:
有一个特殊的函数getTicks()
,您可以调用它来确定当前的刻度是多少。为了调用它,你必须将它从/game/utils
模块导入到你的代码中:
import { getTicks } from 'game/utils';
export function loop() {
console.log('Current tick:', getTicks());
}
有许多方法和对象可以通过这种方式导入。请参阅文档了解。console.log()
输出游戏控制台面板中的任何值,以便您可以调试和检查正在运行的代码。你不需要导入这个函数,它是全局可用的。
让我们开始:
在教程的第一步,我们没有任何游戏对象,游戏地图完全是空的。完成这一步所要的就是打印循环的一些信息。只需要将上面的代码复制粘贴到你的main.mjs文件中,然后点击PLAY按钮运行。
2. 简单移动(Simple move)
每个爬虫可以做各种动作,显然它可以移动。
creep.moveTo(target);
这个命令会让你的爬虫向目标移动一步,如果你在每次循环迭代中都执行它,你的爬虫就会一直移动到目的地。(在调用此函数时立即执行,但只能在循环函数完成之后执行移动。)
从代码访问你的爬虫,有一个特殊的函数叫做getObjectsByPrototype( )
,它返回一个数组,其中包含所有具有给定原型或类的游戏对象。你可以像下面这样选择游戏中的所有爬虫:
import { getObjectsByPrototype } from 'game/utils';
import { Creep } from 'game/prototypes';
export function loop() {
let creeps = getObjectsByPrototype(Creep);
}
在本教程的步骤中,我们有一个Flag对象,它也有它的原型名称。
最终目标:使用移动的方法让你的爬虫踩到旗子上。
代码如下:
import { getObjectsByPrototype } from 'game/utils';
import { Creep, Flag } from 'game/prototypes';
export function loop() {
let creeps = getObjectsByPrototype(Creep);
let flags = getObjectsByPrototype(Flag);
creeps[0].moveTo(flags[0]);
}
3. 首次攻击(First Attack)
Screeps Arena是一款多人PvP游戏,你的爬虫将会和对手的爬虫战斗,所有让我们来简单学习一下攻击。
如何判断哪些是你的爬虫,使用JavaScript标准的Array.filter( )
或Array.find( )
方法返回给定条件的元素数组。
filter( )
方法返回提供的数组中满足所提供测试函数的所有元素的数组。
const words = ['spray', 'elite', 'exuberant', 'destruction', 'present'];
const result = words.filter((word) => word.length > 6);
console.log(result);
// Expected output: Array ["exuberant", "destruction", "present"]
find()
方法返回提供的数组中满足所提供测试函数的第一个元素。
const array1 = [5, 12, 8, 130, 44];
const found = array1.find((element) => element > 10);
console.log(found);
// Expected output: 12
在下面的例子中,可以基于爬虫的属性来查找指定爬虫。
import { getObjectsByPrototype } from 'game/utils';
import { Creep } from 'game/prototypes';
export function loop() {
var myCreep = getObjectsByPrototype(Creep).find(creep => creep.my);
var enemyCreep = getObjectsByPrototype(Creep).find(creep => !creep.my);
}
现在我们可以使用attack
方法攻击敌人了,当它无法到达目标时,他将返回ERR_NOT_IN_RANGE
的错误信息,所以你需要先使用moveTo
:
if(myCreep.attack(enemyCreep) == ERR_NOT_IN_RANGE) {
myCreep.moveTo(enemyCreep);
}
本节最终目标:消灭敌方爬虫。
import { getObjectsByPrototype } from 'game/utils';
import { Creep } from 'game/prototypes';
import { ERR_NOT_IN_RANGE } from 'game/constants';
export function loop() {
let myCreep = getObjectsByPrototype(Creep).find(creep => creep.my);
let enemyCreep = getObjectsByPrototype(Creep).find(creep => !creep.my);
if(myCreep.attack(enemyCreep) === ERR_NOT_IN_RANGE) {
myCreep.moveTo(enemyCreep);
}
}
4. 爬虫的身体部分(Creeps Bodies)
你可以调用爬虫的很多函数来完成某个行为,但是并非所有函数都可以用于某个爬虫。爬虫可以调用什么函数取决于它的身体部位。你可以检查他的body属性,它是一个数组,其中数组每个元素代表某种类型的一个主体部分:
MOVE
:使爬虫移动。
ATTACK
:允许它近战范围内攻击。
RANGED_ATTACK
:允许它攻击3格外的目标。
HEAL
:允许治疗它自己或另一个爬虫。
WORK
:可以建造建筑或收集能量。
CARRY
:增加爬虫携带资源的能力,身上可以携带更多的资源。
TOUGH
:没有任何效果。
if(creep.body.some(bodyPart => bodyPart.type == ATTACK)) {
// this creep has ATTACK body parts
}
Array..some()
是 JavaScript 中用于数组的方法之一,它用于检查数组中是否至少有一个元素满足指定的条件。这个方法会遍历数组的每个元素,直到找到一个满足条件的元素,然后立即返回 true。如果没有找到满足条件的元素,则返回 false。
array.some(callback(element[, index[, array]])[, thisArg])
参数说明:
callback:一个用于测试每个元素的函数,它可以接受三个参数:
element:当前正在处理的数组元素。
index(可选):当前正在处理的元素的索引。
array(可选):调用 some 方法的数组本身。
thisArg(可选):可选参数,函数执行时的 this 值。
每一个身体部位都会使爬虫的攻击力提高100点。当爬虫受到伤害时,他的身体部分也会受到伤害,如果该部分的数值被攻击清零了,它就会失去该部分的功能。同一类型的身体部位越多,这种类型的效果就越强。
现在,本教程步骤中我们有3种不同的部位:ATTACK
、RANGED_ATTACK
和HEAL
的爬虫。只有协调好对手的行动,你才能打败他们。
为了做到这一点,使用与前一个教程步骤相同的方法,但根据他们的身体部位区分他们的行动,伤害发送者应该使用不同的方法(attack
和rangedAttack
)进行伤害,治疗者应该使用heal
方法治疗你受伤的爬虫。
import { getObjectsByPrototype } from 'game/utils';
import { Creep } from 'game/prototypes';
import { ERR_NOT_IN_RANGE, ATTACK, RANGED_ATTACK, HEAL } from 'game/constants';
export function loop() {
let myCreeps = getObjectsByPrototype(Creep).filter(creep => creep.my);
let enemyCreep = getObjectsByPrototype(Creep).find(creep => !creep.my);
for(let creep of myCreeps) {
if(creep.body.some(bodyPart => bodyPart.type == ATTACK)) {
if(creep.attack(enemyCreep) == ERR_NOT_IN_RANGE) {
creep.moveTo(enemyCreep);
}
}
if(creep.body.some(bodyPart => bodyPart.type == RANGED_ATTACK)) {
if(creep.rangedAttack(enemyCreep) == ERR_NOT_IN_RANGE) {
creep.moveTo(enemyCreep);
}
}
if(creep.body.some(bodyPart => bodyPart.type == HEAL)) {
let myDamagedCreeps = myCreeps.filter(i => i.hits < i.hitsMax);
if(myDamagedCreeps.length > 0) {
if(creep.heal(myDamagedCreeps[0]) == ERR_NOT_IN_RANGE) {
creep.moveTo(myDamagedCreeps[0]);
}
}
}
}
}
for...of
语句执行一个循环,该循环处理来自可迭代对象的值序列。可迭代对象包括内置对象的实例,例如 Array、String、TypedArray、Map、Set、NodeList(以及其他 DOM 集合),还包括 arguments 对象、由生成器函数生成的生成器,以及用户定义的可迭代对象。
const array1 = ['a', 'b', 'c'];
for (const element of array1) {
console.log(element);
}
// Expected output: "a"
// Expected output: "b"
// Expected output: "c"
5. 存储和转移 (Store and Transfer)
在不同的区域,有不同的可用资源。但是有一种常见的资源类型RESOURCE_ENERGY
,用来建造建筑、生产爬虫等各种活动。
塔(Tower
)可以造成50点的范围攻击伤害,尽管它的效率随着距离的增加而降低。在本教程的步骤中,我们将使用能量来给塔充能,以击败敌人。
tower.attack(target);
塔每次射击消耗10个能量单位。为了给它装载能量,你需要使用一个将能量传递给它的爬虫。
creep.transfer(tower, RESOURCE_ENERGY);
从哪里获得能量?附近有一个容器(Container),它的store有一些能量。你可以移动爬虫去容器中回收能量:
creep.withdraw(container, RESOURCE_ENERGY);
当塔内有足够能量时,他会攻击并摧毁目标。
如果你使用我们的示例代码,请注意我们现在是如何以不同的方式导入内容的(不是特别定义他们,而是从/game
模块导入整个命名空间。)用哪种导入的写法,根据自己的喜好来:
import { prototypes, utils, constants } from 'game';
export function loop() {
const tower = utils.getObjectsByPrototype(prototypes.StructureTower)[0];
if(tower.store[constants.RESOURCE_ENERGY] < 10) {
let myCreep = utils.getObjectsByPrototype(prototypes.Creep).find(creep => creep.my);
if(myCreep.store[constants.RESOURCE_ENERGY] == 0) {
let container = utils.getObjectsByPrototype(prototypes.StructureContainer)[0];
myCreep.withdraw(container, constants.RESOURCE_ENERGY);
} else {
myCreep.transfer(tower, constants.RESOURCE_ENERGY);
}
} else {
let target = utils.getObjectsByPrototype(prototypes.Creep).find(creep => !creep.my);
tower.attack(target);
}
}
6. 地形 (Terrain)
游戏中5种地形:平原地形、天然的坚不可摧的墙壁、建造的可摧毁的墙、沼泽、道路。
沼泽和道路上移动与爬虫的重量相关联。每个爬虫的身体部位,除了MOVE
,都会增加了他的重量。(一个空的CARRY
身体部位不会增加重量),但如果爬虫体内有资源将会增加它的身体重量。每个MOVE
部位能增加爬虫的移动速度。如果的MOVE
身体部位和所有其他身体部位数量数量相同时,你的爬虫将勉勉强强能够在平原地形上移动。否则小于的话,他会感到疲劳,动弹不得。
但是有相同身体部位的爬虫在沼泽上,它会变得更加疲劳,并且每5 tick才只能移动一次。为了让你每次tick都能移动,你的爬虫将需要5倍以上的MOVE
部件。
相反,道路减少了你的爬虫对MOVE
部件的需求。例如,如果你的爬虫有10个负重的身体部位,你在道路上移动就只需要5个MOVE
身体部件就可以使这个爬虫每次循环移动。
注意,当你需要使用寻路算法从一个数组中找到一个位置最近的对象时,可以使用findClosestByPath
函数。
let flags = getObjectsByPrototype(Flag);
let closestFlag = creep.findClosestByPath(flags);
在本节教程中,你只需要将每个爬虫移动到相应的旗子。代码如下:
import { getObjectsByPrototype } from 'game/utils';
import { Creep, Flag } from 'game/prototypes';
export function loop() {
let creeps = getObjectsByPrototype(Creep).filter(i => i.my);
let flags = getObjectsByPrototype(Flag);
for(let creep of creeps) {
let flag = creep.findClosestByPath(flags);
creep.moveTo(flag);
}
}
7. 生产爬虫(Spawn Creeps)
当你在一个有Spawn
结构的竞技场上玩游戏时,你可以使用spawnCreep
方法创建新的爬虫。您需要将新爬虫的主体定义为包含其主体部分的数组。
你想要刷出的爬虫越大,它消耗的能量就越多,具体数值如下:
MOVE
:50能量
ATTACK
:80能量
RANGED_ATTACK
:150能量
HEAL
:250能量
WORK
:100能量
CARRY
:50能量
TOUGH
:10能量
spawnCreep
方法返回一个稍后使用的Creep
实例。
let creep = mySpawn.spawnCreep([MOVE, ATTACK]).object;
你甚至可以在loop
函数之外定义变量,以便在tick之间使用它们:
let creep;
export function loop() {
if(!creep) {
creep = mySpawn.spawnCreep([MOVE, ATTACK]).object;
}
}
请注意,您可以为这个爬虫对象分配任何属性,如果你想要给爬虫分布一些目标、角色或其他数据,这可能会很有用:
creep.target = flag;
在本教程步骤中,你有一个刷出和两个旗帜。目标:刷出两个爬虫,并将每个爬虫移动到每个旗帜上。
import { getObjectsByPrototype } from 'game/utils';
import { Creep, Flag, StructureSpawn } from 'game/prototypes';
import { MOVE } from 'game/constants';
let creep1, creep2;
export function loop() {
let mySpawn = getObjectsByPrototype(StructureSpawn)[0];
let flags = getObjectsByPrototype(Flag);
if(!creep1) {
creep1 = mySpawn.spawnCreep([MOVE]).object;
} else {
creep1.moveTo(flags[0]);
if(!creep2) {
creep2 = mySpawn.spawnCreep([MOVE]).object;
} else {
creep2.moveTo(flags[1]);
}
}
}
8. 收割能源(Harvest Energy)
能源可以以不同的形式获得,它可以存储在容器中,但是也可以从Sources
中收获,如果你的爬虫有work身体部位,那么它就可以使用使用harvest
方法从Source
提取一些能量。
creep.harvest(source);
你的爬虫必须靠近能源才能执行这个动作。它拥有的WORK
身体部位越多,每一次从能源处提取的能量就越多。能源中的能量储备是无限的,一段时间后会重新刷满。
本节目标:收割和转移1000能量到Spawn
import { prototypes, utils, constants } from 'game';
export function loop() {
let creep = utils.getObjectsByPrototype(prototypes.Creep).find(i => i.my);
let source = utils.getObjectsByPrototype(prototypes.Source)[0];
let spawn = utils.getObjectsByPrototype(prototypes.StructureSpawn).find(i => i.my);
if(creep.store.getFreeCapacity(constants.RESOURCE_ENERGY)) {
if(creep.harvest(source) == constants.ERR_NOT_IN_RANGE) {
creep.moveTo(source);
}
} else {
if(creep.transfer(spawn, constants.RESOURCE_ENERGY) == constants.ERR_NOT_IN_RANGE) {
creep.moveTo(spawn);
}
}
}
9. 建设(Construction)
你不仅可以生成爬虫,还可以建造建筑!只需要一点能量和一个工人就能完成工作。
首先,使用createConstructionSite
函数放置一个名为StructureTower
在某个地方,你需要指定建筑的坐标和原型。
import { createConstructionSite } from 'game/utils';
import { StructureTower } from 'game/prototypes';
export function loop() {
let constructionSite = createConstructionSite({x: 50, y: 55}, StructureTower).object;
}
你带着能量的爬虫必须接近建筑工地建造他:
creep.build(constructionSite);
每一次成功的建造行动都会消耗你的爬虫体内存储的一些能量,所以你的爬虫需要在你的能量供应处和建筑工地之间来回穿梭。
本节目标:建造一座塔。
import { prototypes, utils } from 'game';
import { RESOURCE_ENERGY, ERR_NOT_IN_RANGE } from 'game/constants';
export function loop() {
const creep = utils.getObjectsByPrototype(prototypes.Creep).find(i => i.my);
if(!creep.store[RESOURCE_ENERGY]) {
const container = utils.findClosestByPath(creep, utils.getObjectsByPrototype(prototypes.StructureContainer));
if(creep.withdraw(container, RESOURCE_ENERGY) == ERR_NOT_IN_RANGE) {
creep.moveTo(container);
}
} else {
const constructionSite = utils.getObjectsByPrototype(prototypes.ConstructionSite).find(i => i.my);
if(!constructionSite) {
utils.createConstructionSite(50,55, prototypes.StructureTower);
} else {
if(creep.build(constructionSite) == ERR_NOT_IN_RANGE) {
creep.moveTo(constructionSite);
}
}
}
}
10. 最终测试(Final Test)
现在你已经学会了如何控制爬虫,攻击敌人,使用能量,产生新的爬虫,建造新的结构。看起来你已经为最终测试做好了准备!
在本教程步骤中,你有一个Spawn
和一个Source
。有一群敌人的爬虫,你要产生更多的爬虫,并击败他们。这次我们甚至不会给你提供示例代码! 当你完成本教程,你可以移动到真正的PvP竞技场,并开始在多人模式下与其他人战斗。祝你好运!
目标:在攻击中幸存下来,杀死所有的敌人并完成教程。
本节就不写示例代码了,每个人都有每个人自己的写法,正好做一个小小测试。
游戏官方已经提供了比较全面的vscode代码补全功能了,你只要用vscode打开对应模式的文件夹(注意不要单打开文件,而应该打开文件夹!)就能使用代码补全来编写代码了。然后在第一行加上// @ts-nocheck
就不会报错啦。
以后打算再汉化一下官方的API文档,敬请期待,希望能帮到大家!