GameApi.getGameObjectData(objId) / getUnitData(unitId)

| Chrono Divide | 6 Reads

GameApi 暴露了兩個用於在執行期間檢查物件的輔助方法:

getGameObjectData(objId: number): GameObjectData | undefined;
getUnitData(unitId: number): UnitData | undefined;

這些方法的回傳內容

GameObjectData 提供任何遊戲內物件的一般資訊。主要欄位包括規則定義、地圖座標、基礎尺寸,以及選擇性地包含生命值或擁有者資訊:

export interface GameObjectData {
    id: number;
    type: ObjectType;
    name: string;
    rules: ObjectRules;
    tile: Tile;
    worldPosition: Vector3;
    tileElevation: number;
    foundation: Size;
    hitPoints?: number;
    maxHitPoints?: number;
    owner?: string;
}

UnitData 在此基礎上擴充,提供單位特有的屬性,例如武器、工廠狀態、集結點、砲塔朝向等:

export interface UnitData extends GameObjectData {
    rules: TechnoRules;
    owner: string;
    sight: number;
    veteranLevel: VeteranLevel;
    guardMode: boolean;
    purchaseValue: number;
    primaryWeapon?: WeaponData;
    secondaryWeapon?: WeaponData;
    deathWeapon?: WeaponData;
    attackState?: AttackState;
    hitPoints: number;
    maxHitPoints: number;
    direction?: number;
    onBridge?: boolean;
    zone?: ZoneType;
    buildStatus?: BuildStatus;
    factory?: FactoryData;
    rallyPoint?: Tile;
    isPoweredOn?: boolean;
    hasWrenchRepair?: boolean;
    garrisonUnitCount?: number;
    garrisonUnitsMax?: number;
    turretFacing?: number;
    turretNo?: number;
    isIdle?: boolean;
    canMove?: boolean;
    velocity?: Vector3;
    stance?: StanceType;
    harvestedOre?: number;
    harvestedGems?: number;
    passengerSlotCount?: number;
    passengerSlotMax?: number;
    ammo?: number;
    isWarpedOut: boolean;
    mindControlledBy?: number;
    tntTimer?: number;
}

編譯後的程式碼顯示:

getGameObjectData 根據 ID 擷取世界物件,並回傳其欄位:

...getGameObjectData(t){
  if(Mp(this,Lp,"f").getWorld().hasObjectId(t)){
    let e=Mp(this,Lp,"f").getObjectById(t);
    return {
      id: e.id,
      type: e.type,
      name: e.name,
      rules: e.rules,
      tile: e.tile,
      tileElevation: e.tileElevation,
      worldPosition: e.position.worldPosition.clone(),
      foundation: e.getFoundation(),
      hitPoints: e.healthTrait?.getHitPoints(),
      maxHitPoints: e.healthTrait?.maxHitPoints,
      owner: e.isTechno() ? e.owner.name : void 0
    }
  }
}

getUnitData 在此基礎上進行擴充,若該物件不是單位或建築則會丟出錯誤:

getUnitData(t){
  var i = this.getGameObjectData(t);
  if(i){
    let e = Mp(this,Lp,"f").getObjectById(t);
    if(!e.isTechno())
      throw new Error(`Game object with id ${t} is not a Techno type`);
    return {
      ...i,
      owner: e.owner.name,
      sight: e.sight,
      veteranLevel: e.veteranLevel,
      guardMode: e.guardMode,
      purchaseValue: e.purchaseValue,
      primaryWeapon: e.primaryWeapon ? Mp(this,jp,"m",Up).call(this, e.primaryWeapon) : void 0,
      secondaryWeapon: e.secondaryWeapon ? Mp(this,jp,"m",Up).call(this, e.secondaryWeapon) : void 0,
      deathWeapon: e.armedTrait?.deathWeapon ? Mp(this,jp,"m",Up).call(this, e.armedTrait.deathWeapon) : void 0,
      attackState: e.attackTrait?.attackState,
      direction: e.direction,
      onBridge: e.isInfantry() || e.isVehicle() ? e.onBridge : void 0,
      zone: e.isUnit() ? e.zone : void 0,
      buildStatus: e.isBuilding() ? e.buildStatus : void 0,
      factory: e.isBuilding() && e.factoryTrait ? {
        deliveringUnit: e.factoryTrait.deliveringUnit?.id,
        status: e.factoryTrait.status
      } : void 0,
      rallyPoint: e.isBuilding() ? e.rallyTrait?.getRallyPoint() : void 0,
      isPoweredOn: e.isBuilding() && e.poweredTrait ? e.poweredTrait.isPoweredOn : void 0,
      hasWrenchRepair: e.isBuilding() && e.autoRepairTrait.isDisabled,
      turretFacing: e.isBuilding() || e.isVehicle ? e.turretTrait?.facing : void 0,
      ...
    }
  }
}

使用範例

下方的 bot 在遊戲開始時會回報其第一個單位的基本資訊。
getGameObjectData 提供一般資訊,getUnitData 提供單位的詳細資料。

import { cdapi, Bot, GameApi } from "@chronodivide/game-api";

class ObjectInfoBot extends Bot {
    onGameStart(game: GameApi) {
        const units = game.getVisibleUnits(this.name, "self");
        if (!units.length) {
            console.log("No units visible!");
            return;
        }
        const id = units[0];

        const obj = game.getGameObjectData(id);
        const unit = game.getUnitData(id);

        if (obj) {
            console.log(`Object ${id}: ${obj.name} at (${obj.tile.rx}, ${obj.tile.ry})`);
        }
        if (unit) {
            console.log(
                `HP ${unit.hitPoints}/${unit.maxHitPoints}, sight ${unit.sight}, ` +
                `veteran=${unit.veteranLevel}`
            );
            if (unit.primaryWeapon) {
                console.log(`Primary weapon: ${unit.primaryWeapon.name}`);
            }
        }
    }
}

async function main() {
    await cdapi.init(process.env.MIX_DIR!);
    await cdapi.createGame({
        agents: [new ObjectInfoBot("Scout", "Americans")],
        mapName: "mp03t4.map",
        shortGame: true,
        online: false,
    });
}
main().catch(console.error);

這段程式碼展示了如何取得單位 ID,並將其轉換為 GameObjectDataUnitData,再輸出位置、生命值、武器等關鍵欄位。


這些方法在整個專案中被廣泛使用,例如任務系統會使用它們來更新重心位置:

const movableUnitTiles = this.unitIds
    .map((unitId) => gameApi.getUnitData(unitId))
    .filter((unit) => unit?.canMove)
    .map((unit) => unit?.tile)
    .filter((tile) => !!tile) as Tile[];

意識模組則會先將可見單位 ID 轉換為物件後,再更新四叉樹:

const hostileUnits = hostileUnitIds
    .map((id) => game.getGameObjectData(id))
    .filter(
        (gameObjectData: GameObjectData | undefined): gameObjectData is GameObjectData =>
            gameObjectData !== undefined,
    );

透過將數字 ID 轉換為結構化資料,Bot 可以取得規則定義、當前生命值、武器等眾多屬性,使其能夠理解世界狀態並做出決策。

 

→返回《@chronodivide/game-api 使用教學與完整 API 對照表》

This article was last edited at