3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

toioを動かす with ChatGPT

Posted at

本記事について

ChatGPT APIを利用してコードを生成し、それを動的に実行することでtoioを動かした記事

chatgptで動的にコード実行する参考にはなるかもしれない。

成果物1 toioを1台動かす。Simulation

output.gif

プロンプト

cube1: patrol random 3 points once
生成されたコードを表示(折りたたみ)
.lua
function routine()
  cubeCommand:ShowMessage('Starting patrol')
  
  -- Define 3 random points
  local points = {}
  for i=1,3 do
    local x = math.random(100, 400)
    local y = math.random(100, 400)
    table.insert(points, {x=x, y=y})
  end
  
  -- Move to each point and wait for 1 second
  for i, point in ipairs(points) do
    coroutine1 = startCoroutine(cubeCommand:Navi2TargetCoroutine('cube1', point.x, point.y))
    coroutine.yield(coroutine1)
    coroutine.yield(CS.UnityEngine.WaitForSeconds(1))
  end
  
  cubeCommand:ShowMessage('Patrol finished')
end

 

成果物2 toioを2台で相撲してみる。Real

output.gif

相撲をしてください。

配置はNavi, 突撃するときはMoveを1s使ってください。

勝敗はどちらかが勝つように決めてください。
負けた方は四隅のどこかに飛ばされてください。

## Cube1
技: フェイント
少し前に進み、すぐ後ろに下がる

## Cube2
技: spin
cube2がspinすると半径50以内にいる相手は吹き飛んで負けます。

## 相撲の流れ

1. スタートポジションにつく。 cube1がtop側 y300、cube2がbottom側でy200
2. 対峙する (cube1はdeg -90、cube2はdeg 90) Use Rotate2DegCoroutine
3. 0.5秒待つ
4. 「はっけよーい」のメッセージ
5. 0.5秒待つ
6. のこった!のメッセージ
7. それぞれのCubeが正面に向かって突撃する Use 「cubeCommand:Move」両方ともspeed 70(not minus)
8. 0.2秒待つ
9. 後ろに下がる
10. 0.2秒待つ
11. それぞれ技を出してください。
12. 0.2秒待つ
13. どちらかが負けて吹き飛ばされてください。
14. 0.1秒待つ
15. 負けた方がspinしてください。
16. 勝敗宣言をしてください

突撃にはMoveを使用してください。突撃でNavi2Targetは使用しないでください。
生成されたコードを表示(折りたたみ)
.lua
function routine()
  -- スタートポジションにつく
  coroutine1 = startCoroutine(cubeCommand:Navi2TargetCoroutine('cube1', 250, 300))
  coroutine2 = startCoroutine(cubeCommand:Navi2TargetCoroutine('cube2', 250, 200))
  coroutine.yield(coroutine1)
  coroutine.yield(coroutine2)
  coroutine.yield(CS.UnityEngine.WaitForSeconds(0.5))

  -- はっけよーい
  cubeCommand:ShowMessage('はっけよーい')
  coroutine.yield(CS.UnityEngine.WaitForSeconds(0.5))

  -- のこった!
  cubeCommand:ShowMessage('のこった!')
  coroutine.yield(CS.UnityEngine.WaitForSeconds(0.5))

  -- 突撃
  coroutine1 = startCoroutine(cubeCommand:Move('cube1', 100, 100, 1000))
  coroutine2 = startCoroutine(cubeCommand:Move('cube2', -100, -100, 1000))
  coroutine.yield(coroutine1)
  coroutine.yield(coroutine2)

  -- 技を出す
  coroutine1 = startCoroutine(faint, 'cube1')
  coroutine2 = startCoroutine(spin, 'cube2')
  coroutine.yield(coroutine1)
  coroutine.yield(coroutine2)

  -- 勝敗宣言
  if cubeCommand:GetCubePosY('cube1') > cubeCommand:GetCubePosY('cube2') then
    cubeCommand:ShowMessage('cube1の勝ち!')
    coroutine.yield(startCoroutine(cubeCommand:Navi2TargetCoroutine('cube2', 50, 50)))
  else
    cubeCommand:ShowMessage('cube2の勝ち!')
    coroutine.yield(startCoroutine(cubeCommand:Navi2TargetCoroutine('cube1', 450, 450)))
  end
end

function faint(id)
  -- フェイント
  coroutine.yield(CS.UnityEngine.WaitForSeconds(0.1))
  coroutine1 = startCoroutine(cubeCommand:Move(id, 100, 100, 500))
  coroutine.yield(coroutine1)
  coroutine.yield(CS.UnityEngine.WaitForSeconds(0.1))
  coroutine2 = startCoroutine(cubeCommand:Move(id, -100, -100, 500))
  coroutine.yield(coroutine2)
end

function spin(id)
  -- spin
  coroutine.yield(CS.UnityEngine.WaitForSeconds(0.1))
  coroutine1 = startCoroutine(cubeCommand:Move(id, 60, -60, 200))
  coroutine.yield(coroutine1)
  coroutine.yield(CS.UnityEngine.WaitForSeconds(0.1))
  coroutine2 = startCoroutine(cubeCommand:Move(id, -60, 60, 200))
  coroutine.yield(coroutine2)
  coroutine.yield(CS.UnityEngine.WaitForSeconds(0.1))
  coroutine3 = startCoroutine(cubeCommand:Move(id, 60, -60, 200))
  coroutine.yield(coroutine3)

  -- 判定
  if math.sqrt((cubeCommand:GetCubePosX('cube1') - cubeCommand:GetCubePosX('cube2'))^2 + (cubeCommand:GetCubePosY('cube1') - cubeCommand:GetCubePosY('cube2'))^2) < 50 then
    cubeCommand:ShowMessage(id .. 'の勝ち!')
    if id == 'cube1' then
      coroutine.yield(startCoroutine(cubeCommand:Navi2TargetCoroutine('cube2', 50, 50)))
    else
      coroutine.yield(startCoroutine(cubeCommand:Navi2TargetCoroutine('cube1', 450, 450)))
    end
  end
end

 

リポジトリ

クライアント

サーバー

仕組み

スクリーンショット 2023-04-30 18.58.55.png

unity側のロジックは以下

サーバーのロジックは以下みればだいたい書いてある。

知見

コード以外のものを含めてくる問題
=> 「```」で囲まれたコードブロックとってくることで対処

chatgptが生成するluaのバージョンが低く、廃止されているメソッドを生成してしまう。(math.atan2)
=> 幸い置換(math.atan2 => math.atan)でなんとかなった。

while trueで無限ループしたり、実行するとフリーズするやばいコードを作ってくる時がある。
=> while true禁止を指示しても使うため防ぐのなかなか厳しい。

同期的に呼ぶ関数を非同期に呼ぼうとする。
=> 非同期な呼び方をしても大丈夫なように同期メソッドを作る。

非同期な並列処理も自動生成できるけど精度かなり落ちる
=> 複数台動かすといった処理の生成が向いてない。

chatgptが生成するコードは単語のイメージに強く依存する。
image.png

toioは専用のマットの上だと自分の位置の絶対座標が取得できる。
で、その絶対座標は左上原点のため「topに行くほどYの値が小さくなる」

「top」という単語には正のイメージがあるらしく、
そのままこの座標を使うと、「go left top」でleft bottomに進むコードが
生成されがちになる。

そのため、左下原点の位置をもらうのが有効

.cs
// NOTE: 左上原点の座標系だとchatgptの精度落ちるため左下原点の座標系での点をもらう。
double ReversePosY(double y)
{
    return 500 - y;
}

関連

xLua の使い方

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?