KNOWLEDGE MODULE 03

资源采集与房间经济

先掌握 Store 容量语义,再覆盖 Container、掉落资源、Tombstone、Ruin、Storage、Link、Terminal 与 Mineral,让资源读取、保全、储存和运输形成清楚链路。

专题内进度1 / 10查看完整学习顺序 →

Screeps 基础工程

Screeps Store API 怎么判断容量:getUsedCapacity、getFreeCapacity、getCapacity 与 null 陷阱

系统解释 Screeps Store 的已用容量、剩余容量和总容量:区分通用 Store 与受限 Store,正确处理 resource 参数、0 与 null、Lab 双容量、Tombstone/Ruin 只读资源,以及安全的 withdraw/transfer 数量计算。

难度
基础
适用阶段
Store 与 Container 基础
模块位置
1 / 10
前置知识
已完成新手路线即可开始

KNOWLEDGE GRAPH

把这篇文章连接到 API、错误码和工具

这里由现有 API Reference、Object Hub 与知识库顺序自动生成,用来继续排查和学习;它不替代正文中的具体前置条件与返回值说明。

RELATED API

相关 API 与对象入口

Store 实践 HubHub
POSSIBLE ERRORS

相关错误码

ERR_NOT_ENOUGH_RESOURCES-6ERR_FULL-8
USEFUL TOOLS

相关诊断与规划工具

运输吞吐量规划器Tool房间运行诊断Tool
NEXT TOPIC

继续这个知识模块

当前属于“资源采集与房间经济”第 1 / 10 篇。

下一篇:Creep.withdraw() 怎么从 Container 安全取出 Energy
VERIFICATION文档已核对 · 语法已检查 · Console待测试 · 主循环待验证查看验证详情
官方文档
已核对
JavaScript 语法
已检查
Screeps Console
待测试
真实主循环
待验证
最后核对
2026年8月6日
本文目录20 个主要章节
  1. 常见错误

几乎所有 Screeps 资源代码都会遇到 Store

creep.store
container.store
spawn.store
lab.store
terminal.store

最常见的错误并不是不会读取 Energy,而是把三种不同问题混在一起:

现在有多少资源?
最多能装多少?
还能再装多少?

它们分别对应:

store.getUsedCapacity(resource)
store.getCapacity(resource)
store.getFreeCapacity(resource)

本文重点不是背 API 名称,而是理解:

  • 通用 Store 与受限 Store 的行为不同;
  • resource 参数是否省略会改变返回值含义;
  • 0 表示合法但当前没有,null 表示该容量查询不适用或资源无效;
  • 对 Storage 传入某个资源,剩余容量仍然是共享空间;
  • Spawn、Extension、Tower、Lab、Power Spawn、Nuker 等结构必须按资源查询;
  • Tombstone 与 Ruin 可以读取已用资源,但没有可写入容量;
  • 动作数量应使用来源库存、目标空间和 Creep 空间的最小值。

快速答案

读取某种资源当前数量:

const energy = object.store.getUsedCapacity(
  RESOURCE_ENERGY
);

读取通用 Store 的总已用容量:

const totalUsed = object.store.getUsedCapacity();

读取目标还能接收多少 Energy:

const freeEnergy = object.store.getFreeCapacity(
  RESOURCE_ENERGY
);

读取该 Store 对 Energy 的容量:

const energyCapacity = object.store.getCapacity(
  RESOURCE_ENERGY
);

生产代码不要把返回值直接用于真假判断:

if (store.getFreeCapacity(RESOURCE_ENERGY)) {
  // 0 和 null 都会进入 false
}

应显式区分:

const free = store.getFreeCapacity(
  RESOURCE_ENERGY
);

if (free === null) {
  return {
    status: 'resource-not-supported'
  };
}

if (free === 0) {
  return {
    status: 'store-full'
  };
}

Store 有两种基本类型

官方文档把 Store 分为两类。

通用 Store

可以在同一总容量里保存多种资源,例如:

  • Creep;
  • Container;
  • Storage;
  • Terminal;
  • Factory。

假设 Storage 总容量为 1,000,000,当前有:

Energy 600,000
H 20,000
Power 5,000

总已用容量是:

625,000

剩余共享容量是:

375,000

因此:

storage.store.getFreeCapacity(
  RESOURCE_ENERGY
);

storage.store.getFreeCapacity(
  RESOURCE_HYDROGEN
);

都会返回同一个共享剩余空间,而不是分别为每种资源保留独立格子。

受限 Store

只能存放结构允许的少数资源,例如:

  • Spawn 与 Extension 只接收 Energy;
  • Tower 只接收 Energy;
  • Power Spawn 分别保存 Energy 与 Power;
  • Nuker 分别保存 Energy 与 Ghodium;
  • Lab 分别保存 Energy 与一种 Mineral/Compound。

对这类 Store,建议始终传入明确资源:

spawn.store.getFreeCapacity(
  RESOURCE_ENERGY
);

而不是:

spawn.store.getFreeCapacity();

后者通常返回 null,因为“总共享容量”对受限 Store 没有统一含义。

三个方法分别回答什么

方法 回答的问题
getUsedCapacity(resource) 当前已经存了多少
getFreeCapacity(resource) 现在还能再存多少
getCapacity(resource) 对该资源最多能存多少

通用 Store 中:

store.getUsedCapacity();

通常返回所有资源之和。

受限 Store 中,没有资源参数的容量查询可能返回 null。为了代码可移植,应优先使用明确资源参数。

直接属性与 getUsedCapacity()

下面两种读取方式都常见:

const energyA = creep.store[
  RESOURCE_ENERGY
];

const energyB = creep.store.getUsedCapacity(
  RESOURCE_ENERGY
);

公开引擎的 Store 代理会让已知资源键在没有库存时返回 0

container.store[RESOURCE_POWER] === 0

但枚举行为不同。Object.keys(store)for...in 通常只包含当前非零资源。

因此不要通过下面的方法判断某个资源是否合法:

RESOURCE_ENERGY in object.store
Object.keys(object.store).includes(
  RESOURCE_ENERGY
)

资源为零时可能不会出现在枚举结果里。读取确定资源时使用:

object.store.getUsedCapacity(resource)

或直接属性。

0null 必须分开

这是 Store API 最重要的诊断规则。

返回 0

通常表示:

该资源对 Store 合法,但当前数量或剩余空间为 0

例如空 Storage 中:

storage.store.getUsedCapacity(
  RESOURCE_HYDROGEN
) === 0

满 Storage 中:

storage.store.getFreeCapacity(
  RESOURCE_ENERGY
) === 0

返回 null

通常表示:

该查询对这个 Store 类型不适用
或该资源不是这个受限 Store 支持的资源
或结构当前没有有效容量

例如:

spawn.store.getCapacity(
  RESOURCE_POWER
) === null

Tombstone 不能接收资源:

tombstone.store.getFreeCapacity(
  RESOURCE_ENERGY
) === null

不要写:

const free = store.getFreeCapacity(resource) || 0;

这样会把“不支持”错误地转换成“已满”。

建立统一的容量快照

function inspectStoreResource(
  object,
  resourceType
) {
  if (!object?.store) {
    return {
      status: 'store-missing'
    };
  }

  const used = object.store.getUsedCapacity(
    resourceType
  );
  const free = object.store.getFreeCapacity(
    resourceType
  );
  const capacity = object.store.getCapacity(
    resourceType
  );

  return {
    status:
      used === null
      && free === null
      && capacity === null
        ? 'resource-unsupported'
        : 'store-observed',
    resourceType,
    used,
    free,
    capacity
  };
}

调用:

console.log(JSON.stringify(
  inspectStoreResource(
    Game.spawns.Spawn1,
    RESOURCE_ENERGY
  )
));

不要假设:

used + free === capacity

在受限 Store、不可写 Store 或无效资源上,字段可能包含 null。只在三个值都为有限数字时检查等式。

function isConsistentCapacity(snapshot) {
  return Number.isFinite(snapshot.used)
    && Number.isFinite(snapshot.free)
    && Number.isFinite(snapshot.capacity)
    && snapshot.used + snapshot.free
      === snapshot.capacity;
}

通用 Store 的资源参数陷阱

假设 Storage 有:

Energy 700
H 200
总容量 2,000

则:

storage.store.getUsedCapacity() === 900
storage.store.getUsedCapacity(RESOURCE_ENERGY) === 700
storage.store.getUsedCapacity(RESOURCE_HYDROGEN) === 200

但剩余容量是共享的:

storage.store.getFreeCapacity() === 1100
storage.store.getFreeCapacity(RESOURCE_ENERGY) === 1100
storage.store.getFreeCapacity(RESOURCE_HYDROGEN) === 1100

错误理解是:

Energy 还有 1,300 空间
H 还有 1,800 空间

这些数字不能同时成立,因为两种资源竞争同一个 1,100 空间。

Spawn、Extension 与 Tower

这类结构只支持 Energy。

function inspectEnergyOnlyStructure(
  structure
) {
  const used = structure.store.getUsedCapacity(
    RESOURCE_ENERGY
  );
  const free = structure.store.getFreeCapacity(
    RESOURCE_ENERGY
  );
  const capacity = structure.store.getCapacity(
    RESOURCE_ENERGY
  );

  if (
    used === null
    || free === null
    || capacity === null
  ) {
    return {
      status: 'energy-capacity-unavailable'
    };
  }

  return {
    status: free > 0
      ? 'needs-energy'
      : 'full',
    used,
    free,
    capacity
  };
}

对 Spawn 传入 RESOURCE_POWER 应返回 null,而不是 0。这能帮助你发现错误的资源路由。

Lab 为什么必须传资源

Lab 有两个独立容量区域:

Energy 容量
Mineral/Compound 容量

因此:

lab.store.getCapacity(
  RESOURCE_ENERGY
);

lab.store.getCapacity(
  RESOURCE_UTRIUM_HYDRIDE
);

返回不同容量。

没有参数时:

lab.store.getCapacity();
lab.store.getFreeCapacity();

不能可靠表达“总空间”,通常应视为无效查询。

安全的 Lab 快照:

function inspectLabStore(
  lab,
  mineralType
) {
  return {
    energy: inspectStoreResource(
      lab,
      RESOURCE_ENERGY
    ),
    mineral: inspectStoreResource(
      lab,
      mineralType
    )
  };
}

这也适用于 Power Spawn 和 Nuker:分别检查每种受支持资源,不要使用一个总容量值。

Tombstone 与 Ruin 只有库存,没有可写容量

Tombstone 和 Ruin 可以保存多种遗留资源:

const energy = tombstone.store
  .getUsedCapacity(RESOURCE_ENERGY);

const total = tombstone.store
  .getUsedCapacity();

但是它们不是转入目标:

tombstone.store.getCapacity(
  RESOURCE_ENERGY
) === null

tombstone.store.getFreeCapacity(
  RESOURCE_ENERGY
) === null

因此目标筛选不能只检查存在 store

function canReceiveResource(
  object,
  resourceType
) {
  if (!object?.store) {
    return false;
  }

  const free = object.store.getFreeCapacity(
    resourceType
  );

  return Number.isFinite(free)
    && free > 0;
}

非激活结构的特殊状态

公开引擎测试显示,某些非激活的通用 Store 仍能报告当前已用库存,但容量和剩余容量可能返回 null

因此:

object.store.getUsedCapacity(resource)

有数字,不代表它当前可以接收资源。

在进行 transfer() 前还应检查:

structure.isActive()

以及动作返回码。

安全计算 withdraw 数量

一次 withdraw() 的最大安全数量受两个条件限制:

来源有多少
Creep 还能装多少
function calculateWithdrawAmount(
  creep,
  source,
  resourceType,
  requestedAmount = Infinity
) {
  if (!creep?.store || !source?.store) {
    return {
      status: 'store-missing',
      amount: 0
    };
  }

  const available = source.store
    .getUsedCapacity(resourceType);
  const free = creep.store
    .getFreeCapacity(resourceType);

  if (available === null || free === null) {
    return {
      status: 'resource-unsupported',
      amount: 0
    };
  }

  const amount = Math.min(
    available,
    free,
    Number.isFinite(requestedAmount)
      ? Math.max(0, requestedAmount)
      : Infinity
  );

  return {
    status: amount > 0
      ? 'amount-ready'
      : 'nothing-to-withdraw',
    amount,
    available,
    free
  };
}

安全计算 transfer 数量

一次 transfer() 的最大安全数量受两个条件限制:

Creep 有多少
目标还能装多少
function calculateTransferAmount(
  creep,
  target,
  resourceType,
  requestedAmount = Infinity
) {
  if (!creep?.store || !target?.store) {
    return {
      status: 'store-missing',
      amount: 0
    };
  }

  const carried = creep.store
    .getUsedCapacity(resourceType);
  const free = target.store
    .getFreeCapacity(resourceType);

  if (carried === null || free === null) {
    return {
      status: 'resource-unsupported',
      amount: 0
    };
  }

  const amount = Math.min(
    carried,
    free,
    Number.isFinite(requestedAmount)
      ? Math.max(0, requestedAmount)
      : Infinity
  );

  return {
    status: amount > 0
      ? 'amount-ready'
      : 'nothing-to-transfer',
    amount,
    carried,
    free
  };
}

计算结果仍然只是当前 tick 快照。真实动作应保存返回码,并在后续 tick 验证 Store 变化。

不要把动作接受当成容量已经变化

const before = {
  source: source.store.getUsedCapacity(
    resourceType
  ),
  target: target.store.getUsedCapacity(
    resourceType
  )
};

const result = creep.transfer(
  target,
  resourceType,
  amount
);

result === OK 表示命令被接受,不表示当前脚本读取到的 Store 已立即改变。

保存最小证据:

Memory.storeChecks ??= {};
Memory.storeChecks[creep.name] = {
  resourceType,
  targetId: target.id,
  amount,
  submittedAt: Game.time,
  result,
  before
};

下一 tick 再读取相同对象和资源。

生成可序列化的 Store 摘要

不要把 Store 对象直接写入 Memory。只保存普通数字和资源名:

function summarizeStore(
  object,
  resourceTypes
) {
  if (!object?.store) {
    return {
      status: 'store-missing'
    };
  }

  const resources = {};

  for (const resourceType of resourceTypes) {
    resources[resourceType] = {
      used: object.store.getUsedCapacity(
        resourceType
      ),
      free: object.store.getFreeCapacity(
        resourceType
      ),
      capacity: object.store.getCapacity(
        resourceType
      )
    };
  }

  return {
    status: 'store-summarized',
    objectId: object.id ?? null,
    objectType:
      object.structureType
      ?? object.constructor?.name
      ?? 'unknown',
    totalUsed: object.store.getUsedCapacity(),
    resources
  };
}

常见错误

使用旧的 carrycarryCapacity

现代代码应使用:

creep.store
creep.store.getFreeCapacity()

用真假判断混淆 0null

if (!free) {
  // 无法知道是已满还是不支持
}

对受限 Store 省略资源参数

Spawn、Lab、Power Spawn、Nuker 等结构应明确传资源。

认为通用 Store 每种资源有独立空间

Storage 的 Energy、Mineral、Power 和 Commodity 共用同一个剩余容量。

使用 Object.keys(store) 判断所有资源

枚举通常只显示非零资源。

只检查 object.store 就把它当作 transfer 目标

Tombstone 和 Ruin 有 Store,但不能接收资源。

getUsedCapacity() 有数字理解为结构可用

非激活结构可能保留库存读数,但容量与可写空间无效。

动作返回 OK 后立即读取新容量

动作效果应在后续 tick 验证。

建议的 Store 排查清单

  1. 确认对象存在并具有 store
  2. 判断它是通用 Store、受限 Store,还是只读遗留 Store。
  3. 受限 Store 始终传入 resourceType
  4. 分别记录 usedfreecapacity
  5. 显式区分 0null
  6. 通用 Store 的不同资源共享剩余容量。
  7. Lab、Power Spawn、Nuker 分资源检查。
  8. Tombstone 与 Ruin 只使用 getUsedCapacity()
  9. 计算动作数量时取来源库存与目标空间的最小值。
  10. 保存动作返回码,在后续 tick 验证 Store 差值。

验证状态与适用边界

仓库会对本文 JavaScript 代码块执行语法检查,并通过离线用例验证:

  • 通用 Store 的总已用容量;
  • 共享剩余容量;
  • Energy-only Store 的合法与非法资源;
  • Lab 的 Energy 与 Mineral 独立容量;
  • Tombstone/Ruin 的只读 Store;
  • 0null 分类;
  • withdraw 与 transfer 安全数量;
  • 后续 tick Store 差值状态。

这些测试不能模拟官方 shard 的真实动作结算、Power 效果、结构激活状态变化、并发运输者竞争或实际 CPU 成本。因此 consoleTestedliveTested 保持为 false

相关站内内容

官方与源码资料

返回目录