Screeps:Arena チュートリアルだけやりました(デモ版ではなくちゃんと買ったのにまだそれしかしてない)

先日早期アクセスでリリースされたこのようなゲームがあるとのことで早速飛びついた news.denfaminicogamer.jp

多少趣が違うScreeps:Worldという前作(?)があるようだけど、軽くググっただけではWorldについての日本語記事も見つけられなかったので、Arenaの方のチュートリアル完了までのことを少し日本語で書いておきます。(だいたいで書くので、正確なことはゲーム内のリファレンスとかを頑張って読んでください)

ゲーム概要

生産施設、エネルギー資源、ロボ(クリープ)なんかが配置された空間があり、

  • 生産施設はクリープを生産
  • 作業クリープはエネルギーを収穫
  • 攻撃クリープは敵が近くに居たら攻撃、射程外なら接近する

プレイヤーは上記のようなことをさせるプログラムを読み込ませて、敵プレイヤーと結果で争います。

試合の目的は、今のところ「フラグを取る」「敵の生産施設を破壊する」「点数を稼ぐ」の3種類あるようで、それぞれ入り口が別なので、目的に応じたプログラムを組むことになります。

前述の記事で「リアルタイムストラテジー」と表現されていますが、プレイヤーは「AIの行動をプログラミングしたソースコードを読み込ませる」「結果を見る」をやるだけで、リアルタイムで操作することはないです。なのでRTSではないんじゃないかなー、と。最初に記事を読んだときは「ムム!左から敵が!!戦力を左に集中!!カタカタカタカタ…ッターン!!」みたいな感じかと思ったんですけど違うようです。

※これ書いた後に一回試しに対戦してみっかと思ったんだけど、対戦相手探し中のまま何分も待たされてるので「アップロード済みのソースコード同士で非同期に対戦が開始される」わけではないっぽい

チュートリアル解説

チュートリアル2~9は画面下部の「Sample Code」にクリアに必要なコードが用意されている(1は本文中にある)ので、それをコピペすれば9まではクリアするだけならできます。

注意点としては、1面でスクリプトの置き場所が「C:\Users[username]\ScreepsArena\tutorial-loop_and_import」となっているのを、例えばDドライブなどに変更したとしても、2面を開くと一旦「C:\Users[username]\ScreepsArena\tutorial-simple_move」にデフォルトのファイルが作られるので、Dドライブに先回りしてファイルを作って「なんで動かないんだ?」となる可能性があるんじゃないかと思います(しばらくそれで悩んだ)

1.Loop and Import
メインループ

試合中、1ターンごとにメイン関数から loop() 関数を呼ばれます。この loop() の中で処理がループするわけではないので、たとえばこの関数の先頭で変数の初期化などを行う場合、毎ターンそれを実行することになるはずです。「1ターン目だけ実行したい」場合は getTicks() == 1 で判定するなどの工夫が必要になります。

また、関数の外にグローバル変数を宣言し、ターンを跨いで値を持ち越すことは可能です。

インポートの書き方

import { getTicks } from '/game/utils';

何がインポートできるかなどは後のチュートリアルでも出てくるのでとりあえずサンプルコードコピペでいいと思います。

2.Simple move
クリープやフラッグ、その他オブジェクトの取得

getObjectsByPrototype() で、オブジェクトタイプを指定することで、フィールド内のそのタイプのオブジェクトの配列が取得できます。1個の場合でもちゃんと配列になります。

味方のクリープ+敵のクリープ、味方のフラッグ+敵のフラッグなど、敵味方ごちゃまぜで取得されるので、味方のオブジェクトに絞り込みたい場合はチュートリアル3で出る方法を参照してください。

クリープの移動

creep.moveTo(target); targetのオブジェクトに向けて1歩移動します(最短経路かどうか怪しい動きをする時がある気がする)。多分座標指定とかもできると思うけど調べてないのでリファレンス見てください。

チュートリアル4で出てきますが、クリープは「MOVE」や「ATTACK」など、可能な行動を示すパーツを装備しています。この章では触れていませんが「MOVE」がないと移動できません。

3.First attack
味方クリープの抽出

var myCreep = getObjectsByPrototype(Creep).find(creep => creep.my);

まあ、 find はJSの普通の関数ですね。他の条件で絞り込みたい場合は他の条件で絞ると良いと思います。

クリープの攻撃

「ATTACK」を持っているクリープは隣接した敵を攻撃することができる

if(myCreep.attack(enemyCreep) == ERR_NOT_IN_RANGE) {
    myCreep.moveTo(enemyCreep);
}

「攻撃できたら行動終了、攻撃が届かないなら移動」という感じで、このゲーム用の書き方かなと思います。

attackとmoveを同じターンのうちに両方行っても特にエラーにはならないようです(attackを無限ループしたら勝ちじゃんと思うけど、多分1つのオブジェクトは同じ行動は1ターン1回しかできない)

4.Creeps bodies
クリープのパーツの種類

まあ名前を見ればわかると思いますが、以下の種類のパーツがあり、装備しているパーツに応じた行動が可能です。 - MOVE:移動ができる - ATTACK:隣接したクリープを攻撃できる - RANGED_ATTACK:3マス離れたクリープを攻撃できる - HEAL:自分自身か他のクリープを回復できる - WORK:エネルギー源からの収穫、建設、建築物の解体 ができる - CARRY:運搬ができる(エネルギーの収穫をさせたい場合、WORK+CARRYにしないと多分持てない) - TOUGH : HPが多い

たとえばチュートリアル3では「MOVE」「ATTACK」を持ったクリープが用意されていました。

これらは敵の攻撃などで個別に破壊されることがあります。「MOVE」だけ破壊されると固定砲台のような状態でその場に残りますし、「ATTACK」だけ破壊されると移動だけできる状態で生き残ります。どの順で攻撃・破壊されるかは調べてないです。

5.Store and transfer
タワー

エネルギーが10あると攻撃できる。チュートリアルでの出番がこれだけだったから調べてない。他はリファレンス参照

エネルギーの受け渡し
  • transfer:クリープから他のものに運搬中のエネルギーを渡す。CARRYを持つクリープ同士での受け渡しも可能
  • withdraw: 他のものからエネルギーを取得する。リファレンスを見るとクリープからも取れそうなんだけどうまくいかなかった。
6.Terrain
地形

超えられないブロックがあるので、 findClosestByPath() を使うことで「到達可能な最短経路があるオブジェクト」を見つけることができる。 findClosestByRange() だと「到達不可能でも最も距離が近いオブジェクト」を見つける。

7.Spawn Creeps
クリープの生産

スポーンが設置されている場合、スポーンに指示することでクリープが生産できる。

前述のクリープのパーツを複数指定(同じ属性を複数指定することも可能っぽい)して生産できる。パーツごとに必要なエネルギー量が違う。

1つのスポーンで1ターンのうちに1体しかクリープを生産できない(2体生産しようとすると1体目もうまく生産されず詰むと思う)ことと、生産されたクリープはそのターン中は何もできないことに注意。

8.Harvest energy
エネルギーの収穫

WORKとCARRYを持ったクリープは、harvest() でエネルギー源からエネルギーを収穫して保持することができる。

9.Construction
建築

WORKを持ったクリープはタワーを建築できる(エネルギーが要るので実質的にはCARRYも必要)。

createConstructionSite() で建築場所を設置して build() で建築。 createConstructionSite() したターンは build() できないかも(未確認)

10.Final test
  • クリープの生産
  • エネルギーの収穫と運搬
  • 敵の攻撃

と、チュートリアルでやったことを組み合わせることになる。 開始時点では、敵は壁に囲まれたエリアにいて攻撃できないが、壁を破壊して出てきてくれるので、その後で倒せばOK。 (こちらも壁の破壊に協力すれば早く出てきてくれると思うが、壁の攻撃の指定などが面倒で試してない)