投稿記事

領地運営の記事 (6)

satofumi 2023/01/03 20:00

NPC をコーディングして領地運営するゲーム開発(エラー処理)

通しプレイしながら機能実装しています。
今回は、プレイ中に起きたエラーの処理について書きます。あと、城塞で敵を防衛するのに使う「矢」を作るまでをプレイしていきます。

goto 使ったことによるエラーの対処

NPC を操作する Lua スクリプトで continue を使う代わりに goto を使ったわけですが、Unity アセットの MoonSharp でエラーが起きてしまいました。

↓エラーになった記述
`
while true do

  ::continue::
  coroutine.yield()

  -- 建築予定地がなければ自宅に移動させる。
  local sites = world:buildings(Buildings.PlannedConstructionSite)
  if #sites <= 0 then
     character:goHome()
     goto continue
  end

調べたり試行錯誤した結果、このように書くことでエラーが起きなくなりました。

↓エラーにならない記述

::continue::
while true do

  coroutine.yield()

多分、goto で local sites = の行が2回評価されることでエラーが起きた気もするのですが、私が Lua に詳しくなくて謎です。
とりあえずエラーにならない記述をサンプルとして掲載しようと思います。
# 異なる変数を渡したことによるエラー時のメッセージ表示
ゲーム側が用意した Lua スクリプトのメソッドを呼び出してエラーになったとき、今までは下記のように「どこでエラーが起きた」かだけ表示されていました。

↓表示されるエラーメッセージ

LuaScripts/character.lua:(84,3-35): attempt to index a nil value
`

このエラーこの場合の意味は「そのメソッドはありません」です。
ただ、この character.lua はプレイヤーは閲覧できないし、どのメソッドからの呼び出しなのかもわからないのでデバッグ不可能な状況でした。
なので、エラーまでの呼出履歴を表示するようにしました。

↓修正したエラーメッセージ
`
LuaScripts/character.lua:(84,3-35): attempt to index a nil value
an argument was passed that is not of type Building
stack traceback:

[clr]: in function 'Character:canWork'
E:\Document\ScriptedLand\_CustomStages\World_01\Test\lord.lua:(13,6-48): in function '<E:\Document\ScriptedLand\_CustomStages\World_01\Test\lord.lua:4>'

こうすることで、character:canWork() の呼び出しないでエラーが起きてて、それはこの canWork() に渡した引数の型が Building でないよ、というのがわかるようになった、はずです。
# 工房で矢を作ってるあたりの動画
で、機能追加しつつ、いろいろ実装して少し賑やかになった様子の動画がこれです。

<player src="2732836" size="100%" />

よいと思います。
# まとめと今後の予定
ちょっとずつですけど機能追加や改修をしています。引き続きがんばります。

satofumi 2022/12/13 20:00

NPC をコーディングして領地運営するゲーム開発(ビルダーの実装)

NPC をコーディングして領地経営するゲーム、必須機能は実装し終えたので通しプレイをしながら機能実装してます。

ビルダー(建築する人)の追加

前回までで「布告」を担当するスクリプトに「伐採小屋」と「石切場」を建築しろ、というスクリプトを記述して石切場のデータを追加したので、今回はその続きで「ビルダー」を実装していきます。

↓布告を処理するスクリプト(edict.lua)

return function()
   -- 伐採小屋を建築させる
   edict:build(Buildings.LumberjackHut, Grid(1, 3))
   
   -- 石切場を建築させる
   edict:build(Buildings.StonePit, Grid(6, 3))
end

今回は、この布告スクリプト(edict.lua) の edict:build() によって作られる建築予定地に対して、資材を運搬して建築を行うビルダーのスクリプトを記述します。
edict:build() では「ここにこの建物を建てることにする!」という布告を出しただけなので、資材の運搬と建築は住民が行う必要があります。

で、その住民の行動を Lua スクリプトで記述してみたものがこれです。

↓builder.lua

require 'stage_util'

return function()
   -- 城塞に移動して木槌を装備する。
   local keep = world:keep()
   character:move(keep)
   character:get(Items.WoodenHammer)
   character:equip()

   while true do
      ::continue::
      coroutine.yield()

      -- 疲れたら休憩させる。
      restWhenTired(keep)

      -- 建築予定地がなければ自宅に移動させる。
      local sites = world:buildings(Buildings.PlannedConstructionSite)
      if #sites <= 0 then
         character:goHome()
         goto continue
      end

      -- 最初の建築予定地を作業対象にする。
      local site = sites[1]

      -- 建築のオーダーを開始するために不足しているアイテム名を取得して運搬させる。
      local buildOrder = site:orderData(Items.BuildingMaterials)
      local itemNames = buildOrder:requestedItemNames()

      if #itemNames > 0 then
         local itemName = itemNames[1]

         -- 城塞にある資材を利用する。
         if keep:itemCount(itemName) > 0 then
            character:move(keep)
            character:get(itemName)
            character:move(site)
            character:put()
         end
      else
         -- 建築を開始する。
         character:move(site)
         character:work()
      end
   end
end

えっと「これをプレイヤーに書かせていいの? プログラマでない人にはつらくない?」というのは、毎回思ってます。悩ましいです。

そして、この builder.lua スクリプトを住民1人に割り振るように布告スクリプト(edict.lua)を修正すると、こんな感じになります。

↓edict.lua

return function()
   edict:build(Buildings.LumberjackHut, Grid(1, 3))
   edict:build(Buildings.StonePit, Grid(6, 3))

   -- キャラクターを作成して builder.lua を登録する。
   local builder = Character:new(edict)
   builder:setScript('builder')
end

建築する様子

これらのスクリプトで、ビルダーが実際に建築を行う様子の動画はこんな感じです。

↓ビルダーが建築する様子

小さくて見えにくいですが、建築予定地に石材を運んで、それから建築を行う様子がわかります。
また、伐採小屋の建築が終わると、その周囲の森林タイルが「育成林」になって木材が入手できるようにもなるのもわかります。

よいと思います。

まとめと今後の予定

今回プレイしながら何が楽しいのか改めて考えていました。とりあえず「指示通りにキャラクターが動いて仕事するのを眺める楽しさ」があるとは思っています。

また、いったん動作するようになった後に「木材が不足しがちだから木こりの人数を2人にしてみよう」みたいな改善を楽しめるようにもしたくて、これらの楽しみを引き出せる仕組みは今後もプレイしながら考えていきます。

次は、木材や石材を採取させたり、食料生産をさせるスクリプトをこのフィールドに投入してみようと思います。がんばります。

satofumi 2022/12/06 20:00

NPC をコーディングして領地運営するゲーム開発(岩場と石切場の追加)

前回から、ゲームの通しプレイをしながら不足要素を実装することにしました。
そして、建物の建築に必要な「石材」を入手する方法がなかったので追加します。

岩場の地面タイルの追加

追加して描画してみました。山脈のわきにある1のタイルが岩場タイルです。


違和感がすごいと思ったので色合いだけ変更してみました。

まだ違和感が強いです。
多分、草地のタイルに岩場の描画だけ重ね合わせるのが良いのかも? またいつか考えて修正します。

採石所の建物タイルの追加

これもパラメータを設定して画像登録で追加しました。


この建物パネルの建築コストが仮実装なままなのに気付いてしまいましたが、それもまたいつか修正します。

まとめと今後の予定

とりあえず、無事に追加できたのでよかったです。この建物の動作確認はまた改めて行います。

satofumi 2022/11/29 20:00

NPC をコーディングして領地運営するゲーム開発(エディタでのマップ作成)

今回は、タイルエディタでマップを作るあたりに着手します。

Tiled でマップ作成する


Tiled はタイル画像を用意して、ちょっと設定すればちゃんと動くので大変助かります。
後は、データの保存形式を CSV にしておいて、それを Python スクリプトで好みのフォーマットに変更したらオッケーです。

カスタムステージとしてゲームで表示する

そして、いくつかのエラーを修正しつつゲーム内で表示したものがこれです。


とりあえず描画できていて良い感じです。

まとめと今後の予定

ここから、この中くらいのサイズのマップでゲームクリアを目標実際ににプレイしながら、不具合の修正や未実装の機能を追加していきます。

satofumi 2022/09/06 20:00

NPC をコーディングして領地運営するゲーム開発(どういうゲームなのか)

今回は、どういうゲームを作っていて、それがどう楽しいか(予定)についての記事です。

ゲームの流れ

Lua 言語でコーディングをして NPC の行動を自動化するゲームなので、当然のようにコーディングを行います。そして、そのコーディングの結果であるキャラクターの動きを見ながら、期待する動作になるまでコーディング内容を修正していきます。

流れをスクリーンショットで説明すると

チュートリアルでは未完成のコードが用意されているのでそれを動かしてみて、

どういう記述が不足しているか理解した上でコーディングして、

期待する動作になるまで API やサンプルコードを読み直してコードを修正する。


という流れになります。

どう楽しいのか

簡単に書くと、ゲームにおいて楽しさにつなががる「今はこういう状況だ(現状把握)、こうすれば良いはずだ(計画)、やってみよう!(実行)、できた!(報酬)」のサイクルをコーディングを通して提供しようとしています。

とはいえ、コードが適切に動作するまでに理解することは多いため、人によっては楽しさを感じる前にコーディングを苦痛に思ってしまうかもしれません。そうならないように、適切な難易度上昇のチュートリアルを用意しようと考えています。

ゲームで提供する必要がある機能

コーディングするとキャラクターがどう動くのかは次回の記事にまとめようと思います。
ただ、その仕組みの実装を別にしても

  • 記述したコードに従って動くゲーム画面
  • コーディングするためのエディタ
  • Lua 言語についてのドキュメント
  • 提供する API についてのドキュメント
  • 適切な難易度上昇のチュートリアルステージ
  • 記述したコードにエラーがあったときの適切な対処

などを実装する必要があり、正直大変です。初心者向けのコーディング講座の資料を書くのよりも、もう少し大変なくらいかなと思います。
このゲームを作っていて「遊ぶ人少ないだろうに、こんなに大変なの割に合わないのでは…」と思いかけることはありますが、なるべく考えないようにして開発しています。

今後の予定

次回は Lua 言語で NPC のどういう行動を制御して領地経営するのかという、ゲームのより具体的な内容について記述していきます。がんばります。

« 1 2

月別アーカイブ

限定特典から探す

記事を検索