# オブジェクトとクリーチャー

> Nodaro の SDK を使って、TypeScript からオブジェクトとクリーチャーを作成し、メイン画像とバリエーションを生成して、アニメーション化し、クリーチャーに話をさせます。

Source: https://nodaro.ai/ja/docs/developers/sdk/objects-and-creatures

**`client.objects`** は、小道具、商品、乗り物向けに、オブジェクト／小道具スタジオでできることをすべてスクリプトから行います。**`client.creatures`** は、動物やクリーチャー向けに同じことを行います。どちらも、アイテムの作成と編集、メイン画像候補の生成、その承認、バリエーションとモーションクリップの追加、そして以降のプロンプトでアイテムの一貫性を保つ説明の作成を行います。これらのメソッドは、[オブジェクト](https://nodaro.ai/docs/developers/api/objects)と[クリーチャー](https://nodaro.ai/docs/developers/api/creatures)の REST API を呼び出します。エディターでの見方については、[オブジェクトと小道具](https://nodaro.ai/docs/guides/objects)と[動物とクリーチャー](https://nodaro.ai/docs/guides/creatures)を参照してください。

## メソッド
この 2 つのリソースは、同じメソッドを持ちます。

| メソッド | 内容 |
| --- | --- |
| [`objects.list(params?)`](#objectslistparams)、[`creatures.list(params?)`](#creatureslistparams) | アイテムを一覧表示します |
| [`objects.listArchived(params?)`](#objectslistarchivedparams) | アーカイブしたアイテムを一覧表示します |
| [`objects.get(id)`](#objectsgetid) | 1 つのアイテムを、進行中のジョブとともに読み取ります |
| [`objects.create(input)`](#objectscreateinput)、[`creatures.create(input)`](#creaturescreateinput) | アイテムを作成します |
| [`objects.update(id, input)`](#objectsupdateid-input)、[`creatures.update(id, input)`](#creaturesupdateid-input) | アイテムを変更します |
| [`objects.delete(id)` と `restore(id)`](#objectsdeleteid-and-restoreid) | アイテムをアーカイブするか、元に戻します |
| [`objects.permanentDelete(id)`](#objectspermanentdeleteid) | アーカイブしたアイテムと、そのファイルを完全に削除します |
| [`objects.generate(input)`](#objectsgenerateinput)、[`creatures.generate(input)`](#creaturesgenerateinput) | メイン画像の候補を生成します |
| [`objects.generateAsset(input)`](#objectsgenerateassetinput)、[`creatures.generateAsset(input)`](#creaturesgenerateassetinput) | バリエーションを生成します |
| [`objects.generateMotion(input)`](#objectsgeneratemotioninput)、[`creatures.generateMotion(input)`](#creaturesgeneratemotioninput) | メイン画像をアニメーション化してクリップにします |
| [`objects.approveMainImage(id, candidateJobId, expectedUpdatedAt?)`](#objectsapprovemainimageid-candidatejobid-expectedupdatedat) | 候補をメイン画像にします |
| [`objects.recaption(id)`](#objectsrecaptionid) | 説明を作成し直します |

## client.objects
オブジェクトは、メイン画像を `sourceImageUrl` に持ち、`angles`、`materials`、`variations`、`motionClips` の 4 つのコレクション（いずれも `{ name, url }` のリストです）、`boards`、`referencePhotos`、`canonicalDescription`、`styleLock` を持ちます。`category` は、`furniture`、`vehicle`、`weapon`、`food`、`clothing`、`electronics`、`nature`、`tool`、`animal`、`other` のいずれかです。

`Object` という名前は、JavaScript のグローバルと同じです。両方が必要な場合は、別名でインポートしてください。`import type { Object as NodaroObject } from "@nodaro/sdk"` のようにします。

### objects.list(params?)
自分のオブジェクトを一覧表示します。デフォルトでは、有効なオブジェクトだけを返します。ページングは任意です。`limit` を指定しない場合は一覧全体を、`limit`（最大 500）を指定すると 1 ページ分と `nextCursor` を取得します。

```ts
list(params?: { archived?: boolean; projectId?: string; limit?: number; cursor?: string }): Promise<{
objects: Object[]
nextCursor?: string | null
}>
```

<TypeTable
type={{
archived: { type: 'boolean', default: 'false', description: "true にすると、代わりにアーカイブしたオブジェクトを一覧表示します。" },
projectId: { type: 'string', description: "このプロジェクトのオブジェクトだけにします。" },
limit: { type: 'number', description: "ページのサイズで、最大 500 です。省略すると一覧全体になります。" },
cursor: { type: 'string', description: "前のページの nextCursor です。" },
}}
/>

```ts
const { objects } = await client.objects.list()
const page = await client.objects.list({ limit: 100 })
```

### objects.listArchived(params?)
自分のアーカイブしたオブジェクトを一覧表示します。`list({ archived: true })` のショートカットです。`creatures.listArchived()` も同じように動作します。

```ts
listArchived(params?: { projectId?: string; limit?: number; cursor?: string }): Promise<{ objects: Object[]; nextCursor?: string | null }>
```

<TypeTable
type={{
projectId: { type: 'string', description: "このプロジェクトのオブジェクトだけにします。" },
limit: { type: 'number', description: "ページのサイズで、最大 500 です。" },
cursor: { type: 'string', description: "前のページの nextCursor です。" },
}}
/>

```ts
const { objects: archived } = await client.objects.listArchived()
```

### objects.get(id)
1 つのオブジェクトを、`pendingJobs`（まだ生成中のバリエーション）とともに読み取ります。アーカイブしたオブジェクトは返され**ません**。`NotFoundError` がスローされます。`creatures.get()` も同じように動作します。

```ts
get(id: string): Promise<ObjectDetail>
```

<TypeTable
type={{
id: { type: 'string', required: true, description: "オブジェクトの ID です。" },
}}
/>

```ts
const object = await client.objects.get(objectId)
console.log(object.sourceImageUrl, object.materials)
```

### objects.create(input)
オブジェクトを作成します。`name` と `nodeId` は必須です。キャンバスのノードがないスクリプトでは、`nodeId` に `"mcp-managed"` を渡せます。

```ts
create(input: CreateObjectInput): Promise<{ id: string }>
```

<TypeTable
type={{
nodeId: { type: 'string', required: true, description: "オブジェクトが属するキャンバスのノードです。ノードがない場合は mcp-managed です。" },
name: { type: 'string', required: true, description: "名前です。" },
description: { type: 'string', description: "自由記述の説明です。" },
category: { type: 'ObjectCategory', description: "furniture、vehicle、weapon、food、clothing、electronics、nature、tool、animal、other のいずれかです。" },
style: { type: 'string', description: "ビジュアルスタイルで、realistic などです。" },
projectId: { type: 'string', description: "所属させるプロジェクトです。" },
workflowId: { type: 'string', description: "元になったワークフローです。" },
sourceImageUrl: { type: 'string', description: "メイン画像の URL です。" },
imageProvider: { type: 'string | null', description: "メイン画像の画像モデルです。" },
referencePhotos: { type: 'ObjectReferencePhoto[]', description: "リファレンス写真で、それぞれ { url, kind } です。kind は front、side、detail、context、moodBoard、other のいずれかです。" },
canonicalDescription: { type: 'string', description: "プロンプトで使われる説明です。" },
styleLock: { type: 'boolean', description: "バリエーションを、承認済みのスタイルに保ちます。" },
}}
/>

```ts
const { id: objectId } = await client.objects.create({
nodeId: "mcp-managed",
name: "Antique Lantern",
description: "Weathered brass lantern with hand-engraved filigree",
category: "tool",
style: "realistic",
})
```

### objects.update(id, input)
オブジェクトを変更します。送信したフィールドだけが書き込まれます。バリエーションのコレクションは、この呼び出しの対象ではありません。作業中に、生成ジョブがコレクションへ追加していくためです。

```ts
update(id: string, input: UpdateObjectInput): Promise<{ id: string; updatedAt: string }>
```

<TypeTable
type={{
id: { type: 'string', required: true, description: "オブジェクトの ID です。" },
name: { type: 'string', description: "名前です。" },
description: { type: 'string', description: "説明です。" },
category: { type: 'ObjectCategory', description: "カテゴリーです。" },
style: { type: 'string', description: "ビジュアルスタイルです。" },
sourceImageUrl: { type: 'string', description: "メイン画像の URL です。" },
imageProvider: { type: 'string | null', description: "メイン画像の画像モデルです。" },
referencePhotos: { type: 'ObjectReferencePhoto[]', description: "リファレンス写真です。" },
canonicalDescription: { type: 'string', description: "プロンプトで使われる説明です。" },
styleLock: { type: 'boolean', description: "バリエーションを、承認済みのスタイルに保ちます。" },
boards: { type: 'Array<{ name, url, type?, sourceImages? }>', description: "リファレンスボードです。" },
selectedAssetByVariant: { type: 'Record<string, string>', description: "バリエーションごとに選ばれているテイクです。" },
expectedUpdatedAt: { type: 'string', description: "自分が読み取った updatedAt の値です。それ以降にオブジェクトが変更されていた場合、更新は 409 concurrent_modification で失敗します。" },
}}
/>

```ts
await client.objects.update(objectId, {
canonicalDescription: "A weathered brass lantern with engraved filigree and a glass chimney",
expectedUpdatedAt: object.updatedAt,
})
```

`409 concurrent_modification` は、通常の `NodaroError` として返されます。オブジェクトを読み取り直し、マージしてから、もう一度試してください。

### objects.delete(id) と restore(id)
`delete()` はオブジェクトをアーカイブします。アーカイブ済みのオブジェクトに対して繰り返しても、何も変わりません。`restore()` は元に戻します。大文字と小文字を区別せずに、有効なオブジェクトの名前と一致する場合、サーバーは `(restored)` を付けて、使った名前を返します。

```ts
delete(id: string): Promise<{ success: true; archived: true }>
restore(id: string): Promise<{ id: string; name: string }>
```

<TypeTable
type={{
id: { type: 'string', required: true, description: "オブジェクトの ID です。" },
}}
/>

```ts
await client.objects.delete(objectId)
const { name } = await client.objects.restore(objectId)
```

### objects.permanentDelete(id)
アーカイブしたオブジェクトと、それが参照する保存済みのすべてのファイルを削除します。アーカイブしたオブジェクトにしか使えません。有効なオブジェクトでは `400 not_archived` で失敗します。先に `delete()` でアーカイブしてください。`creatures.permanentDelete()` も同じように動作します。

```ts
permanentDelete(id: string): Promise<{ success: true; permanent: true }>
```

<TypeTable
type={{
id: { type: 'string', required: true, description: "アーカイブしたオブジェクトの ID です。" },
}}
/>

```ts
await client.objects.delete(objectId)
await client.objects.permanentDelete(objectId)
```

Nodaro の MCP ツールには、この操作は用意されていません。そのため、AI アシスタントがオブジェクトを削除することはできません。

### objects.generate(input)
メイン画像の候補を生成します（`POST /v1/generate-object`）。`count` が 1 より大きい場合、どのジョブも開始する前に、すべてのジョブ分を確保します。そのため、途中で失敗すると、バッチ全体がロールバックされます。

```ts
generate(input: GenerateObjectInput): Promise<{ jobIds: string[]; jobId?: string }>
```

<TypeTable
type={{
name: { type: 'string', required: true, description: "オブジェクトの名前です。" },
description: { type: 'string', description: "オブジェクトの見た目です。" },
userPrompt: { type: 'string', description: "追加の指示です。" },
category: { type: 'ObjectCategory', description: "カテゴリーです。" },
style: { type: '"realistic" | "anime" | "3d-pixar" | "illustration"', description: "ビジュアルスタイルです。" },
sourceImageUrl: { type: 'string', description: "元にする写真です。" },
provider: { type: 'string', description: "画像モデルの ID です。" },
count: { type: 'number', description: "生成する候補の数です。" },
seedPromptHint: { type: 'string', description: "プロンプトに追加するピッカーの選択内容です。たとえば、素材（Material）ピッカーの antique brass です。" },
attachToObjectId: { type: 'string', description: "単一の結果の書き込み先となるオブジェクトです。" },
attachName: { type: 'string', description: "アタッチする結果の名前です。" },
expectedUpdatedAt: { type: 'string', description: "自分が読み取った updatedAt の値です。新しい変更を上書きしないためのものです。" },
}}
/>

```ts
const { jobIds } = await client.objects.generate({ name: "Antique Lantern", count: 4 })
for (const jobId of jobIds) {
// poll each candidate with client.jobs.getStatus(jobId)
}
```

`jobIds` は常に存在し、候補ごとに 1 つの ID を持ちます。`jobId` は、候補が 1 つの場合の古いエイリアスです。`jobIds` を使ってください。`attachToObjectId` を指定して候補が 1 つの場合、ジョブが完了すると、その結果がメイン画像になります。それ以外の場合は、`approveMainImage()` で 1 つを選んでください。

### objects.generateAsset(input)
1 つのバリエーションを生成します（`POST /v1/generate-object-asset`）。`attachToObjectId`、`attachToColumn`、`attachName` を指定すると、ジョブが完了したときに、結果がそのコレクションに追加されます。

```ts
generateAsset(input: GenerateObjectAssetInput): Promise<{ jobId: string }>
```

<TypeTable
type={{
assetType: { type: '"angles" | "materials" | "variations" | "motion" | "custom"', required: true, description: "バリエーションの種類です。" },
variant: { type: 'string', required: true, description: "バリエーションで、gold や three-quarter などです。" },
name: { type: 'string', required: true, description: "オブジェクトの名前です。" },
description: { type: 'string', description: "そのバリエーションのプロンプトです。省略し、かつ結果をアタッチする場合は、言語モデルが、オブジェクトの説明とバリエーション名から作成します。" },
userPrompt: { type: 'string', description: "追加の指示です。" },
sourceImageUrl: { type: 'string', description: "元にする画像で、通常はメイン画像です。" },
provider: { type: 'string', description: "画像モデルの ID です。" },
seedPromptHint: { type: 'string', description: "プロンプトに追加するピッカーの選択内容です。" },
attachToObjectId: { type: 'string', description: "結果を追加する先のオブジェクトです。" },
attachToColumn: { type: 'string', description: "コレクションです。angles、materials、variations、motion_clips、sheets、detail_closeups のいずれかです。custom では必須です。" },
attachName: { type: 'string', description: "新しいエントリーの名前です。" },
}}
/>

```ts
const { jobId } = await client.objects.generateAsset({
name: "Antique Lantern",
assetType: "materials",
variant: "gold",
attachToObjectId: objectId,
attachToColumn: "materials",
attachName: "gold",
})
```

`angles`、`materials`、`variations`、`motion` では、コレクションはアセットの種類から決まります。`custom` のバリエーションでは、`attachToColumn` が必要です。

### objects.generateMotion(input)
オブジェクトの画像を、[**動画生成**](https://nodaro.ai/docs/nodes/video/generate-video)（Generate Video）の画像から動画へのモードで、クリップにアニメーション化します（`POST /v1/generate-object-motion`）。クリップは常に `motionClips` に入ります。デフォルトは商品撮影向けで、モデルは `kling-turbo`、フレームは `1:1` です。

```ts
generateMotion(input: GenerateObjectMotionInput): Promise<{ jobId: string }>
```

<TypeTable
type={{
motionPrompt: { type: 'string', required: true, description: "オブジェクトの動き方、またはカメラがオブジェクトの周りをどう動くかです。" },
sourceImageUrl: { type: 'string', required: true, description: "アニメーション化する画像です。フォールバックはないため、メイン画像を渡してください。" },
name: { type: 'string', required: true, description: "オブジェクトの名前です。" },
provider: { type: 'string', default: '"kling-turbo"', description: "動画モデルの ID です。" },
aspectRatio: { type: '"1:1" | "3:4" | "16:9" | "9:16" | "4:3"', default: '"1:1"', description: "クリップのアスペクト比です。オブジェクトでは、カタログ撮影用に 4:3 が加わります。" },
duration: { type: 'number', description: "クリップの長さ（秒）です。" },
refineFromVideoUrl: { type: 'string', description: "画像から始める代わりに、新しいプロンプトで作り直す、既存のクリップです。動画から動画へのモードです。" },
category: { type: 'string', description: "カテゴリーです。" },
style: { type: '"realistic" | "anime" | "3d-pixar" | "illustration"', description: "ビジュアルスタイルです。" },
canonicalDescription: { type: 'string', description: "オブジェクトの説明です。" },
seedPromptHint: { type: 'string', description: "プロンプトに追加するピッカーの選択内容です。" },
attachToObjectId: { type: 'string', description: "クリップを追加する先のオブジェクトです。" },
attachName: { type: 'string', description: "新しいクリップの名前です。" },
}}
/>

```ts
const { jobId } = await client.objects.generateMotion({
name: "Antique Lantern",
motionPrompt: "Slow 360-degree rotation, soft golden rim light",
sourceImageUrl: object.sourceImageUrl!,
attachToObjectId: objectId,
attachName: "rotate-360",
})
```

### objects.approveMainImage(id, candidateJobId, expectedUpdatedAt?)
`generate()` からの完了した候補を、オブジェクトのメイン画像にします。続けて、ビジョンモデルがオブジェクトの説明を作成し、このメソッドは両方を返します。

```ts
approveMainImage(id: string, candidateJobId: string, expectedUpdatedAt?: string): Promise<{
sourceImageUrl: string
canonicalDescription: string | null
}>
```

<TypeTable
type={{
id: { type: 'string', required: true, description: "オブジェクトの ID です。" },
candidateJobId: { type: 'string', required: true, description: "完了した候補のジョブ ID です。" },
expectedUpdatedAt: { type: 'string', description: "自分が読み取った updatedAt の値です。それ以降にオブジェクトが変更されていた場合、この呼び出しは 409 concurrent_modification で失敗します。" },
}}
/>

```ts
const { sourceImageUrl, canonicalDescription } = await client.objects.approveMainImage(objectId, jobIds[0])
```

説明を作成できなかった場合、`canonicalDescription` は `null` になります。メイン画像は設定されたままなので、もう一度試すには `recaption()` を呼び出してください。

### objects.recaption(id)
現在のメイン画像から、オブジェクトの説明を作成し直します。この呼び出しは繰り返しても安全で、同時実行制御用のトークンは必要ありません。

```ts
recaption(id: string): Promise<{ canonicalDescription: string }>
```

<TypeTable
type={{
id: { type: 'string', required: true, description: "オブジェクトの ID です。" },
}}
/>

```ts
const { canonicalDescription } = await client.objects.recaption(objectId)
```

オブジェクトにメイン画像がない場合は `400 main_image_required` で、ビジョンモデルが失敗した場合は 502 で失敗します。

## client.creatures
クリーチャーは、動物や空想上の生き物です。オブジェクトと同じように動作しますが、4 つの違いがあります。

- **`species`** は、`dragon` や `wolf` などの自由記述のタイプで、メイン画像のプロンプトの主題になります。`category` も自由記述です。
- **`poses`** が `materials` の代わりになるため、コレクションは `angles`、`poses`、`variations`、`motionClips` です。バリエーションの種類は `angles`、`poses`、`variations`、`custom` です。
- **`boards`** は、最大 24 枚の名前付きの**クリーチャーボード**（Creature Board）を保持します。クリーチャーボードは、[**画像生成**](https://nodaro.ai/docs/nodes/image/generate-image)（Generate Image）の**クリーチャーボード**プリセットで作る、情報量の多いリファレンスシートです。このリストは自分で管理するもので、`create()` と `update()` は、リスト全体を置き換えます。
- **`voice`** を設定すると、そのクリーチャーは話せるようになります。キャラクターのボイスと同じ形で、`{ voiceId, voiceName, traits, voiceType?, previewUrl?, ttsProvider? }` です。取り除くには `voice: null` を渡します。

`creatures.listArchived()`、`get()`、`delete()`、`restore()`、`permanentDelete()`、`approveMainImage()`、`recaption()` は、同じ引数を取り、上のオブジェクトのメソッドと同じように動作します。

### creatures.list(params?)
```ts
list(params?: { archived?: boolean; projectId?: string; limit?: number; cursor?: string }): Promise<{
creatures: Creature[]
nextCursor?: string | null
}>
```

<TypeTable
type={{
archived: { type: 'boolean', default: 'false', description: "true にすると、代わりにアーカイブしたクリーチャーを一覧表示します。" },
projectId: { type: 'string', description: "このプロジェクトのクリーチャーだけにします。" },
limit: { type: 'number', description: "ページのサイズで、最大 500 です。省略すると一覧全体になります。" },
cursor: { type: 'string', description: "前のページの nextCursor です。" },
}}
/>

```ts
const { creatures } = await client.creatures.list()
```

### creatures.create(input)
```ts
create(input: CreateCreatureInput): Promise<{ id: string }>
```

<TypeTable
type={{
nodeId: { type: 'string', required: true, description: "クリーチャーが属するキャンバスのノードです。ノードがない場合は mcp-managed です。" },
name: { type: 'string', required: true, description: "名前です。" },
species: { type: 'string', description: "クリーチャーの種類で、dragon や wolf などです。" },
description: { type: 'string', description: "自由記述の説明です。" },
category: { type: 'string', description: "自由記述のカテゴリーです。" },
style: { type: 'string', description: "ビジュアルスタイルです。" },
projectId: { type: 'string', description: "所属させるプロジェクトです。" },
sourceImageUrl: { type: 'string', description: "メイン画像の URL です。" },
referencePhotos: { type: 'CreatureReferencePhoto[]', description: "リファレンス写真で、それぞれ { url, kind } です。kind は front、side、detail、context、moodBoard、other のいずれかです。" },
canonicalDescription: { type: 'string', description: "プロンプトで使われる説明です。" },
styleLock: { type: 'boolean', description: "バリエーションを、承認済みのスタイルに保ちます。" },
voice: { type: 'CreatureVoice | null', description: "クリーチャーのボイスです。" },
}}
/>

```ts
const { id: creatureId } = await client.creatures.create({
nodeId: "mcp-managed",
name: "Biscuit",
species: "ginger cat",
style: "realistic",
})
```

### creatures.update(id, input)
`nodeId` を除く `create()` のフィールドに加え、`boards`、`selectedAssetByVariant`、`expectedUpdatedAt` を取ります。送信したフィールドだけが書き込まれます。

```ts
update(id: string, input: UpdateCreatureInput): Promise<{ id: string; updatedAt: string }>
```

<TypeTable
type={{
id: { type: 'string', required: true, description: "クリーチャーの ID です。" },
boards: { type: 'Array<{ name, url, type?, sourceImages? }>', description: "クリーチャーボードで、最大 24 枚です。リスト全体が置き換わります。" },
voice: { type: 'CreatureVoice | null', description: "ボイスです。null にすると解除されます。" },
expectedUpdatedAt: { type: 'string', description: "自分が読み取った updatedAt の値です。新しい変更を上書きしないためのものです。" },
}}
/>

```ts
await client.creatures.update(creatureId, {
voice: { voiceId: chosenVoiceId, voiceName: "Aria", traits: "smug, unhurried" },
})
```

### creatures.generate(input)
メイン画像の候補を生成します。`objects.generate()` のフィールドに加えて `species` を取り、`{ jobIds }` を返します。

```ts
generate(input: GenerateCreatureInput): Promise<{ jobIds: string[]; jobId?: string }>
```

<TypeTable
type={{
name: { type: 'string', required: true, description: "クリーチャーの名前です。" },
species: { type: 'string', description: "クリーチャーの種類です。" },
description: { type: 'string', description: "クリーチャーの見た目です。" },
count: { type: 'number', description: "生成する候補の数です。" },
provider: { type: 'string', description: "画像モデルの ID です。" },
attachToCreatureId: { type: 'string', description: "単一の結果の書き込み先となるクリーチャーです。" },
}}
/>

```ts
const { jobIds } = await client.creatures.generate({ name: "Biscuit", species: "ginger cat", count: 4 })
```

### creatures.generateAsset(input)
1 つのバリエーションを生成します。`attachToCreatureId` がある点を除き、`objects.generateAsset()` と同じように動作します。

```ts
generateAsset(input: GenerateCreatureAssetInput): Promise<{ jobId: string }>
```

<TypeTable
type={{
assetType: { type: '"angles" | "poses" | "variations" | "custom"', required: true, description: "バリエーションの種類です。" },
variant: { type: 'string', required: true, description: "バリエーションで、walking や sitting などです。" },
name: { type: 'string', required: true, description: "クリーチャーの名前です。" },
attachToCreatureId: { type: 'string', description: "結果を追加する先のクリーチャーです。" },
attachToColumn: { type: 'string', description: "angles、poses、variations、motion_clips、sheets、detail_closeups のいずれかです。custom では必須です。" },
attachName: { type: 'string', description: "新しいエントリーの名前です。" },
}}
/>

```ts
await client.creatures.generateAsset({
name: "Biscuit",
assetType: "poses",
variant: "sitting",
attachToCreatureId: creatureId,
attachToColumn: "poses",
attachName: "sitting",
})
```

### creatures.generateMotion(input)
クリーチャーの画像をクリップにアニメーション化します。同じデフォルト（`kling-turbo` と `1:1`）で `objects.generateMotion()` と同じように動作し、クリップを `motionClips` に追加します。

```ts
generateMotion(input: GenerateCreatureMotionInput): Promise<{ jobId: string }>
```

<TypeTable
type={{
motionPrompt: { type: 'string', required: true, description: "クリーチャーが何をするかです。" },
sourceImageUrl: { type: 'string', required: true, description: "アニメーション化する画像です。" },
name: { type: 'string', required: true, description: "クリーチャーの名前です。" },
provider: { type: 'string', default: '"kling-turbo"', description: "動画モデルの ID です。" },
aspectRatio: { type: '"1:1" | "3:4" | "16:9" | "9:16" | "4:3"', default: '"1:1"', description: "クリップのアスペクト比です。" },
attachToCreatureId: { type: 'string', description: "クリップを追加する先のクリーチャーです。" },
attachName: { type: 'string', description: "新しいクリップの名前です。" },
}}
/>

```ts
await client.creatures.generateMotion({
name: "Biscuit",
motionPrompt: "The cat stretches, then yawns",
sourceImageUrl: creature.sourceImageUrl!,
attachToCreatureId: creatureId,
})
```

## クリーチャーに話をさせる
音声に、クリーチャー専用のメソッドは必要ありません。クリーチャーのボイスでセリフをレンダリングし、それをクリーチャーの画像にリップシンクします。

```ts
const creature = await client.creatures.get(creatureId)

// 1. Speak the line in the creature's voice
const speech = await client.nodes.runAndWait("text-to-speech", {
text: "I knocked the vase off the shelf. I regret nothing.",
voice: creature.voice!.voiceId,
provider: creature.voice!.ttsProvider,
voiceType: creature.voice!.voiceType,
})

// 2. Lip-sync the audio onto the creature's main image
const clip = await client.nodes.runAndWait("lip-sync", {
imageUrl: creature.sourceImageUrl!,
audioUrl: speech.audioUrl,
provider: "kling-avatar",
})
console.log(clip.videoUrl)
```

[**リップシンク**](https://nodaro.ai/docs/nodes/video/lip-sync)（Lip Sync）ノードは、既存の動画を吹き替えることもできます。`videoUrl` と、動画に対応したモデルを渡します。`volcengine-lipsync` は、吹き替えに使えるモデルの中でいちばん料金が安く、複数の話者を扱える唯一のモデルです。

```ts
const dub = await client.nodes.runAndWait("lip-sync", {
videoUrl: "https://example.com/scene.mp4",
audioUrl: "https://example.com/new-vocal.mp3",
provider: "volcengine-lipsync",
mode: "basic",          // for complex scenes
openScenedet: true,     // several speakers: scene and speaker detection
audioDurationSec: 42,   // sets the per-second price; without it, you pay for 5 minutes
})
```

クリーチャーをリファレンスとしてショットに組み込むには、`@nodaro/shared` の `toConnectedReference({ kind: "creature", id, name, url, description })` でリファレンスを作ります。画像生成が、クリーチャーの体のつくり、模様、色を保つ一文を追加します。[リファレンス](https://nodaro.ai/docs/developers/sdk/nodes#references)を参照してください。

## Frequently asked questions

### Nodaro のオブジェクトとクリーチャーの違いは何ですか？

オブジェクトは、小道具、商品、乗り物で、アングル、素材、バリエーションの画像を持ちます。クリーチャーは、動物や空想上の生き物で、アングル、ポーズ、バリエーションの画像に加えて、種族、リファレンスボード、任意のボイスを持ちます。

### オブジェクトのメイン画像の候補を生成するには、どうすればよいですか？

name と count を指定して client.objects.generate を呼び出します。常に jobIds が返され、候補ごとに 1 つの ID が入ります。完了したら、client.objects.approveMainImage で 1 つを承認します。

### SDK でオブジェクトを完全に削除できますか？

できます。2 段階の手順です。まず delete() でアーカイブし、次に permanentDelete() を呼び出します。これは、オブジェクトと、それが参照するすべてのファイルを削除します。有効なオブジェクトに対しては、400 not_archived で拒否されます。

### クリーチャーに話をさせるには、どうすればよいですか？

テキストから音声（Text to Speech）ノードとクリーチャーのボイスで音声をレンダリングし、次に、クリーチャーのメイン画像とその音声で、リップシンク（Lip Sync）ノードを実行します。
