FOUNDATION · MEMORY

How Screeps Memory Saves Creep Roles and Working State

Learn why ordinary variables do not preserve Screeps state, how creep.memory maps to Memory.creeps, what data belongs in Memory, and why object IDs are safer than live game objects.

Verification statusChinese source article: Reviewed in full · Official docs: Checked — Global Objects, Game.getObjectById(), spawnCreep() · API and constants: Checked

VERIFICATION

Evidence and test status

Chinese source article
Reviewed in full
Official docs
Checked — Global Objects, Game.getObjectById(), spawnCreep()
API and constants
Checked
JavaScript syntax
Passed
Offline logic review
Passed — missing Creep, first write, later read, ID recovery
Screeps Console test
Pending
Live multi-tick room test
Pending
Last verified
July 25, 2026

Quick answer

Game describes the current tick, while Memory stores JSON-compatible data for later ticks. A Creep's memory property is a convenient view of Memory.creeps[creep.name]. Use it for small facts such as a role, a working-state flag, a target ID, or a home-room name. Do not save a live Source, Creep, or Structure object in Memory.

Why ordinary variables are not persistent state

The main loop runs again on every tick. A variable created inside that loop is created again when the loop runs again:

module.exports.loop = function () {
  let working = false;

  if (working) {
    console.log('The Creep is working.');
  }
};

This example always starts with working set to false. It is useful as current-execution data, but it does not represent a durable decision. Review how ticks and module.exports.loop work before using cross-tick state.

What the Memory object is

Screeps provides the global Memory object for JSON-compatible information that must be available later:

Memory.exampleValue = 1;
console.log(Memory.exampleValue);

Official documentation explains that Game is rebuilt for the current tick, whereas changes written through Memory are serialized and passed forward. Memory is limited, and reading it has a parsing cost, so this beginner guide stores only small values.

How creep.memory maps to Memory.creeps

const creep = Game.creeps.Harvester1;

if (creep) {
  creep.memory.role = 'harvester';

  console.log(creep.memory.role);
  console.log(Memory.creeps[creep.name].role);
}

Both reads point to the same Creep Memory entry. The role string does not create an official class. It is a label that your own code can use while iterating over Game.creeps:

for (const name in Game.creeps) {
  const creep = Game.creeps[name];

  if (creep.memory.role === 'harvester') {
    console.log(creep.name + ' is assigned to harvesting.');
  }
}

See the beginner role guide for the difference between body abilities and player-defined jobs.

Write role Memory when spawning

StructureSpawn.spawnCreep() accepts an options object that can include initial Memory:

const result = Game.spawns.Spawn1.spawnCreep(
  [WORK, CARRY, MOVE],
  'Harvester1',
  {
    memory: {
      role: 'harvester',
      working: false
    }
  }
);

console.log('spawnCreep() returned ' + result);

This creates the Memory entry as part of the spawn request. The method still needs its normal name, body, energy, and return-code checks. Review the spawnCreep() tutorial before adding role-based replacement logic.

Use a boolean working state

working is a player-defined field that commonly separates an Energy-acquisition phase from an Energy-spending phase:

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

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

if (usedEnergy === 0) {
  creep.memory.working = false;
}

if (freeEnergy === 0) {
  creep.memory.working = true;
}

Your script must then read the flag and call the appropriate API. Writing creep.memory.working = true does not automatically call build(), transfer(), or upgradeController(). The same pattern appears in the Energy-delivery tutorial.

What belongs in Memory

Good beginner valuesWhy
'harvester'Small role string.
true or falseSimple working-state decision.
'E51S44'Room name that can be checked later.
A game object ID stringLets later code recover the current object.
Small arrays or plain objectsRemain JSON-compatible when deliberately bounded.

Functions, circular values, and live game objects do not belong in Memory. Large caches and RawMemory performance work are outside this beginner article.

Save object IDs, not live game objects

A Source object belongs to the current visible game state. Save its ID instead:

const source = creep.pos.findClosestByRange(FIND_SOURCES);

if (source) {
  creep.memory.sourceId = source.id;
}

On a later tick, recover the current object and handle null:

const source = creep.memory.sourceId
  ? Game.getObjectById(creep.memory.sourceId)
  : null;

if (!source) {
  delete creep.memory.sourceId;
}

Game.getObjectById() can return null when the object no longer exists or is not currently visible. The ID is durable data; the returned game object is still current-tick data.

Why dead Creep Memory can remain

Game.creeps and Memory.creeps are related but separate. After a Creep dies, its name disappears from Game.creeps, while the old Memory entry may remain until your code removes it. Do not add automatic deletion before understanding the comparison:

for (const name in Memory.creeps) {
  if (!Game.creeps[name]) {
    console.log(name + ' exists only in Memory.');
  }
}

Complete beginner example

State impact: this script writes one role string and one boolean to the selected Creep's Memory. It does not move the Creep or submit a game action.

module.exports.loop = function () {
  const creep = Game.creeps.Harvester1;

  if (!creep) {
    console.log('Harvester1 was not found.');
    return;
  }

  if (creep.memory.role === undefined) {
    creep.memory.role = 'harvester';
  }

  if (creep.memory.working === undefined) {
    creep.memory.working = false;
  }

  console.log(JSON.stringify({
    name: creep.name,
    role: creep.memory.role,
    working: creep.memory.working
  }));
};

Observe the Memory inspector and several later ticks. A real multi-tick room test has not been performed for this article, so the verification panel remains explicit about that boundary.

Debugging checklist

  • Confirm the Creep exists before reading creep.memory.
  • Use one spelling and capitalization for every role value.
  • Remember that custom fields do nothing until your code reads them.
  • Store JSON-compatible values only.
  • Store a game object's ID, not the object itself.
  • Handle Game.getObjectById() returning null.
  • Expect dead Creep entries to remain until cleanup code removes them.
  • Use the English error-code reference when a later action fails.

Scope and next steps

This guide does not cover RawMemory, segments, parse-cost optimization, global caching, automatic role counts, replacement spawning, or modular role files. Continue with withdrawing Energy from a Container, where Memory can later connect acquisition and delivery states.

Frequently asked questions

Does a normal JavaScript variable survive every Screeps tick?

Do not use a local loop variable as durable game state. Use Memory for information that must be available later.

Is creep.memory different from Memory.creeps?

creep.memory is a convenient alias for the matching Memory.creeps entry.

Can I save a Source object directly?

No. Save source.id, then call Game.getObjectById() on a later tick.

Is working an official field?

No. It is a player-defined boolean whose meaning comes entirely from your own branching logic.

Official documentation

SOURCE AND SCOPE

Review the source, evidence, or next system

This English guide is rewritten for a focused search intent while preserving the technical scope and verification boundaries of its Chinese source. Live-room evidence is claimed only when the verification record says it exists.