Script project adalah class C++ di assets/scripts/ yang diturunkan dari
Component. Editor meng-compile tiap .cpp menjadi shared library dan
menjalankannya saat Play. Header SDK:
| Header | Isi |
|---|---|
core_engine/core/Component.hpp |
Component, ScriptProperties (include ini saja) |
core_engine/script/GameObject.hpp |
Vec3, Transform, GameObject, GameObjectRef, ObjectRef<T> |
core_engine/script/SceneObjects.hpp |
AudioGameObject, VideoPlayerGameObject, LightGameObject, CameraGameObject, UiGameObject, UiTextGameObject, UiImageGameObject, UiButtonGameObject, TextGameObject, DialogGameObject |
Versi API: ilmeee::script::kApiVersion = 5. Script yang di-compile untuk
versi lain ditolak, cukup tekan Recompile.
// assets/scripts/spherecontroller.hpp
#pragma once
#include <core_engine/core/Component.hpp>
class spherecontroller : public Component {
public:
float speed = 3.0f;
Vec3 tempat{0, 1, 0};
GameObjectRef target;
ObjectRef<AudioGameObject> musik;
void DescribeProperties(ScriptProperties &properties) override;
void Start() override;
void Update(float deltaTime) override;
};// assets/scripts/spherecontroller.cpp
#include "spherecontroller.hpp"
void spherecontroller::DescribeProperties(ScriptProperties &properties) {
properties.Field("Kecepatan", speed);
properties.Field("Tempat", tempat);
properties.Field("Tujuan", target);
properties.Field("Musik", musik);
}
void spherecontroller::Start() {
gameObject.SetPosition(tempat);
if (musik.IsValid()) musik.Get().Play();
Log("Mulai di " + gameObject.Name());
}
void spherecontroller::Update(float deltaTime) {
if (IsKeyDown("W")) gameObject.Translate({0, 0, -speed * deltaTime});
}| Anggota | Keterangan |
|---|---|
gameObject |
Object yang memasang script ini |
Awake(), Start() |
Dipanggil sekali saat Play dimulai |
Update(float deltaTime) |
Tiap frame selama Play (bukan saat Pause) |
OnEnable(), OnDisable() |
Saat script aktif / berhenti |
DescribeProperties(ScriptProperties&) |
Daftar field yang tampil di Inspector |
Log(msg) |
Console > Output, awalan [Script] |
LogWarning(msg) |
Sama, dengan [WARNING] |
LogError(msg) |
Dengan [ERROR], dan membuka Console di editor |
IsKeyDown(key) |
Selama tombol ditahan. "W", "A", "S", "D", "Space", "E", "Enter", "Interact" |
IsKeyPressed(key) |
Hanya di frame pertama tombol ditekan (menu, dialog, interaksi) |
"E" dan "Interact" sama: tombol E di keyboard, atau Button UI dengan
Action Interact (untuk Android). WASD ikut joystick UI.
Fungsi member yang mengembalikan Coroutine bisa berhenti sementara dengan
co_await, lalu lanjut di frame berikutnya / setelah waktu tertentu (mirip
IEnumerator + yield return di Unity, memakai coroutine C++20).
class Lampu : public Component {
public:
LightGameObject lampu;
Coroutine Kedip() {
for (int i = 0; i < 3; ++i) {
lampu.SetIntensity(0); co_await WaitForSeconds(0.5f);
lampu.SetIntensity(5); co_await WaitForSeconds(0.5f);
}
Log("selesai kedip");
}
Coroutine Intro() {
Log("tunggu 2 detik...");
co_await WaitForSeconds(2);
co_await Kedip(); // tunggu coroutine lain selesai
co_await WaitUntil([this] { return IsKeyPressed("E"); });
Log("E ditekan!");
}
void Start() override {
lampu = gameObject.As<LightGameObject>();
StartCoroutine(Intro());
}
};| API | Keterangan |
|---|---|
StartCoroutine(Fungsi()) |
Jalan langsung sampai co_await pertama. Mengembalikan CoroutineId (0 kalau sudah selesai) |
StopCoroutine(id) / StopAllCoroutines() |
Hentikan. Aman dipanggil dari dalam coroutine (termasuk untuk dirinya sendiri) |
IsCoroutineRunning(id) |
true selama masih berjalan |
co_await WaitForSeconds(detik) |
Jeda dengan waktu game (deltaTime yang sama dengan Update) |
co_await WaitForNextFrame() |
Lanjut di frame berikutnya |
co_await WaitUntil(fungsi) / WaitWhile(fungsi) |
Dicek tiap frame; lanjut saat true / saat false |
co_await FungsiLain() |
Jalankan coroutine lain sampai selesai, lalu lanjut |
co_return; |
Selesai lebih awal |
Aturan:
- Coroutine maju sekali per frame, setelah
Update. Coroutine yang dibuat di frame ini (termasuk dariStart) baru dilanjutkan frame berikutnya. - Semua coroutine dihapus saat Play di-Stop (bersama component-nya).
- Pakai fungsi member (aman memakai
this). Jangan jadikan lambda yang meng-capture sebagai coroutine: capture-nya hilang setelahStartCoroutine. - Exception di dalam coroutine menghentikan script itu (pesan error muncul di
Console), sama seperti exception di
Update.
| Tipe C++ | Tampilan Inspector |
|---|---|
float |
DragFloat |
bool |
Checkbox |
std::string |
InputText (panjang bebas, maks. 65535 byte saat disimpan) |
Vec3 |
DragFloat3 (X, Y, Z) |
GameObjectRef |
Pilihan object / drag dari Hierarchy |
ObjectRef<T> |
Sama seperti GameObjectRef, tapi Get() langsung bertipe T |
std::vector<T> |
List: tiap item pakai widget tipe T, tombol X (hapus), + Add (salin item terakhir), Clear |
T di std::vector<T> boleh semua tipe di atas kecuali std::vector lagi
(list di dalam list tidak didukung). Maksimal 4096 item per list.
Nama field harus unik. Nilai default float/Vec3 harus finite. Nilai dari
Inspector tersimpan di .ilmeeescene (Vec3 sejak format v1.10, string dan
std::vector sejak v1.14).
class Spawner : public Component {
public:
std::string judul = "Gelombang 1";
std::vector<std::string> dialog{"Halo", "Siap?"};
std::vector<Vec3> titik;
std::vector<ObjectRef<AudioGameObject>> suara;
void DescribeProperties(ScriptProperties &p) override {
p.Field("Judul", judul);
p.Field("Dialog", dialog);
p.Field("Titik Spawn", titik);
p.Field("Suara", suara);
}
void Start() override {
for (const auto &baris : dialog) Log(judul + ": " + baris);
for (auto &s : suara) if (s.IsValid()) s.Get().Play();
}
};Tipe lain (mis. int) gagal compile dengan pesan constraint not satisfied
pada Field. Untuk menambah tipe baru di engine: tambah nilai di akhir
ScriptValueType, spesialisasi ilmeee::script::FieldType<T>, lalu
ReadScalar/WriteScalar (NativeScripts.cpp), widget Inspector, dan format
scene — std::vector<T> untuk tipe baru itu otomatis ikut jalan.
Transform tidak bisa jadi satu field. Kalau perlu, pakai tiga field
Vec3 (posisi, rotasi, skala), lalu gabungkan:
Vec3 pos, rot, skala{1, 1, 1};
// ...
gameObject.SetTransform({pos, rot, skala});Berlaku juga untuk semua object bertipe di bawah.
| Fungsi | Keterangan |
|---|---|
IsValid() |
Object masih ada di scene |
Id(), Name() |
Identitas dan nama di Hierarchy |
GetTransform() / SetTransform(Transform) |
position, rotation (euler, derajat), scale |
GetPosition() / SetPosition(Vec3) |
Posisi saja |
Translate(Vec3) |
Geser relatif dari posisi sekarang |
Find(name) |
Cari object lain berdasarkan nama |
Is(ObjectKind) |
Light, Camera, Audio, Video, Ui (elemen Canvas), atau Dialog? |
As<T>() |
Pandangan bertipe, misalnya As<AudioGameObject>() |
Transform bersifat local, yaitu relatif ke parent. Untuk object di root, nilainya sama dengan world.
Vec3 mendukung +, -, dan * float.
Cara mendapatkannya:
auto audio = target.Get().As<AudioGameObject>(); // dari GameObjectRef
auto video = gameObject.Find("Video Screen").As<VideoPlayerGameObject>();
ObjectRef<LightGameObject> lampu; // langsung sebagai fieldIsValid() bernilai false kalau object tidak punya komponen tersebut, jadi
selalu cek dulu. Semua setter mengembalikan bool: false berarti nilainya
ditolak (bukan angka finite, di luar rentang, atau komponennya tidak ada).
Untuk object Audio Source atau object apa pun yang punya komponen Audio Source.
| Fungsi | Keterangan |
|---|---|
Play() |
Mulai dari awal. Kalau dipanggil di Start(), menggantikan Play On Start |
Stop() |
Berhenti. Di Start(), mencegah Play On Start |
IsPlaying() |
Sedang terdengar |
Get/SetVolume(float) |
≥ 0, langsung berlaku |
Get/SetClip(path) |
Path relatif project, berlaku di Play() berikutnya |
Get/SetLoop(bool) |
Berlaku di Play() berikutnya |
Get/SetSpatial(bool) |
3D (ikut posisi kamera game) atau 2D |
Get/SetMinDistance, Get/SetMaxDistance |
Jarak redaman 3D |
IsEnabled() / SetEnabled(bool) |
Komponen nonaktif tidak bisa di-Play |
| Fungsi | Keterangan |
|---|---|
Play() |
Mulai dari awal. Di Start(), menggantikan Play On Start |
Stop() |
Berhenti dan layar kosong |
IsPlaying() |
Sedang berjalan (false setelah selesai kalau tidak loop) |
Get/SetClip(path) |
Mengganti clip saat berjalan akan memuat ulang video |
Get/SetLoop(bool) |
Sama, memuat ulang |
Get/SetVolume(float) |
Volume suara video |
Get/SetUnlit(bool) |
Terang penuh, atau ikut pencahayaan |
IsEnabled() / SetEnabled(bool) |
Pause/Seek per object belum ada. Pause global ikut tombol Pause editor.
| Fungsi | Keterangan |
|---|---|
Get/SetColor(Vec3) |
RGB 0..1 |
Get/SetIntensity(float) |
≥ 0 |
Get/SetRange(float) |
Point / Spot |
Get/SetSpotAngle(float) |
Derajat, 0..179 |
GetType() / SetType(LightGameObject::Type) |
Directional, Point, Spot |
| Fungsi | Keterangan |
|---|---|
IsOrthographic() / SetOrthographic(bool) |
Perspective atau Orthographic |
Get/SetFov(float) |
Derajat, 1..179 |
Get/SetOrthoSize(float) |
Setengah tinggi view orthographic |
Get/SetNear, Get/SetFar |
Clip plane |
Elemen Canvas (Text, Image, Button, Joystick). Dapatkan dengan field
ObjectRef<UiGameObject> (dropdown Inspector juga menampilkan elemen UI di
bagian UI), atau gameObject.Find("Text 1").As<UiGameObject>().
| Fungsi | Keterangan |
|---|---|
Get/SetText(text) |
Teks Text/Button |
Get/SetImage(path) |
Gambar elemen Image atau sprite Button, path relatif project |
Get/SetAction(action) |
Action Button |
IsVisible() / SetVisible(bool), Show(), Hide() |
Elemen tersembunyi tidak digambar dan tidak bisa diklik |
GetPosition() / SetPosition(Vec3) / Translate(Vec3) |
x, y dalam pixel referensi dari anchor (z diabaikan) |
Get/SetSize(Vec3) |
x = lebar, y = tinggi |
Get/SetFontSize(float) |
1..512 |
Get/SetColor(Vec3), Get/SetAlpha(float) |
Warna teks / tint Image / knob joystick |
Get/SetBackground(Vec3), Get/SetBackgroundAlpha(float) |
Warna latar |
WasClicked() |
Diklik / di-tap sejak Update() sebelumnya. Berlaku untuk semua jenis elemen, bukan hanya Button |
Semua perubahan UI dari script (dan dari Dialog) dikembalikan saat Stop.
Centang Visible di Inspector elemen UI untuk kondisi awalnya; di editor
elemen tersembunyi tampil samar dan diberi tanda [hidden] di Hierarchy.
Teks Text sekarang otomatis turun baris (word wrap) sesuai lebar elemen.
Pilih elemen Text, Image, atau Button di Canvas, lalu Inspector > Add
Component untuk menempelkan script C++ yang sudah dikompilasi. gameObject
di script itu menunjuk langsung ke elemen UI-nya; Awake, Start, Update,
dan enable/disable dijalankan seperti script pada objek 3D. Binding dan field
Inspector disimpan di scene serta ikut Build Linux/Android.
UiTextGameObject, UiImageGameObject, dan UiButtonGameObject mewarisi API
UiGameObject, tetapi IsValid() juga memeriksa jenis elemen yang benar.
Gunakan ObjectRef<...> untuk target dan drag elemen Hierarchy atau pilih
dropdown di Inspector. WasClicked() diperiksa di Update(), bukan Start().
// Tempelkan script ini pada Button UI. Pilih label dunia pada field title.
#include <core_engine/core/Component.hpp>
class ChangeTitle : public Component {
public:
ObjectRef<TextGameObject> title;
void DescribeProperties(ScriptProperties &p) override {
p.Field("title", title);
}
void Start() override {
gameObject.As<UiButtonGameObject>().SetText("Bicara");
}
void Update(float) override {
if (gameObject.As<UiButtonGameObject>().WasClicked() && title.IsValid())
title.Get().SetText("Halo, selamat datang!");
}
};Hierarchy: klik kanan Scene > Create > Text (3D), atau Create Child > Text (3D) pada objek karakter. Ini bukan Canvas: posisi/rotasi/skala memakai unit dunia dan parent transform. Glyph berupa bidang XY menghadap lokal +Z, memakai depth test sehingga tertutup benda di depannya. Atur Content, Font size, Color, Alignment dan Visible di Inspector. Teks beberapa baris didukung dengan newline. Undo/Redo, save/reopen, dan ekspor didukung.
Script: gameObject.As<TextGameObject>(),
gameObject.Find("Text (3D)").As<TextGameObject>(), atau
ObjectRef<TextGameObject>. API-nya: Get/SetText, Get/SetFontSize (tinggi
baris dalam unit dunia, 0.001..100), Get/SetColor, Get/SetAlpha,
Get/SetAlignment(TextGameObject::Alignment::Left/Center/Right),
IsVisible, SetVisible, Show, Hide, dan transform GameObject biasa.
Perubahan Text saat Play dikembalikan saat Stop.
Font bawaan MiSans Latin ikut paket game (fallback embedded bila tidak ada). Ini text surface tanpa ekstrusi, bukan mesh huruf tebal, rich text, atau billboard otomatis. Konten render dibatasi 16 KiB per objek.
Object yang punya komponen Dialog (lihat di bawah).
| Fungsi | Keterangan |
|---|---|
StartDialog() |
Buka dialog dari baris pertama. false kalau dialog lain sedang terbuka, tidak ada baris, atau komponen nonaktif |
Next() |
Selesaikan baris yang sedang diketik, atau lanjut ke baris berikutnya (menutup di akhir) |
StopDialog() |
Tutup dialog object ini |
IsTalking() |
Dialog object ini sedang terbuka |
GetLineIndex() |
Baris ke berapa (mulai 0), -1 kalau tertutup |
GetSpeaker(), GetLine() |
Nama dan teks lengkap baris sekarang |
Get/SetScript(text) |
Isi dialog (format sama dengan Inspector), dipakai di StartDialog() berikutnya. Dialog File di Inspector lebih diutamakan |
IsEnabled() / SetEnabled(bool) |
|
Get/SetRange(float) |
Jarak interaksi |
Get/SetSpeed(float) |
Huruf per detik, 0 = langsung tampil |
Tambahkan lewat Add Component > Dialog pada karakter (object apa pun). Tidak perlu script sama sekali.
| Field | Keterangan |
|---|---|
| Trigger | Interact (dekati lalu tekan E / Enter / Button Interact), On Start (langsung saat Play), Script Only (hanya StartDialog()) |
| Range | Jarak ke player. Player = object pertama yang punya Movement Script; kalau tidak ada, kamera game |
| Dialog Box | Elemen UI yang muncul selama dialog (panel) |
| Text | Elemen UI yang menerima baris dialog (efek mengetik) |
| Speaker Name | Opsional, menerima nama pembicara; disembunyikan untuk narasi |
| Prompt | Opsional, petunjuk ("[E] Talk") yang muncul saat player dalam jangkauan |
| Lines / Dialog File | Isi dialog langsung di Inspector, atau file teks di project (drag dari Explorer) |
| Typing Speed | Huruf per detik, 0 = langsung |
| Repeatable | Bisa diajak bicara lagi setelah selesai |
| Freeze Player | Movement (WASD/joystick) diabaikan selama dialog; kamera tetap bisa digerakkan |
Tombol Create Dialog UI membuat elemen Box / Text / Name yang belum ada
di Canvas, plus Talk Button (Button dengan Action Interact) sebagai
Prompt, dan langsung menghubungkannya. Banyak karakter boleh
memakai elemen UI yang sama. Saat Play dimulai semua elemen itu
disembunyikan, lalu ditampilkan saat dialog dibuka.
Format dialog, satu baris per kalimat:
# komentar dan baris kosong diabaikan
Sakura: Halo! Kamu murid baru ya?
Iya, salam kenal.
: Angin sore berhembus pelan.
Sakura: Ayo ke kelas.\nNanti telat!
Nama: teks→ pembicaraNama(maks. 40 byte sebelum:, jadiJam 10:30tetap dianggap teks).- Tanpa nama → pembicara = nama object pemilik komponen.
: teks→ narasi, Speaker Name disembunyikan.\n→ pindah baris di dalam satu kalimat.
Ada tiga cara memulai dialog dengan Trigger Interact, semuanya hanya saat
player dalam Range:
- Tombol E atau Enter di keyboard.
- Button UI dengan Action
Interact(pilih dari dropdown Built-in actions di Inspector Button). Ini yang dipakai di HP. - Paling rapi: pilih Button
Interactitu sebagai Prompt di komponen Dialog. Button-nya otomatis hanya muncul saat player dekat karakter, dan bisa diklik / di-tap / diganti tombol E.
Button bisa memakai sprite sendiri: drag gambar dari Explorer ke field
Sprite di Inspector, ke baris Button di Hierarchy, atau ke Button di
canvas Scene. Sprite menggantikan warna Background, teks tetap digambar di
atasnya (kosongkan Text kalau gambarnya sudah bertulisan), dan sprite jadi
lebih gelap saat ditekan. Script: UiGameObject::SetImage(path).
Saat dialog terbuka: E / Enter, atau klik/tap Dialog Box / Text, akan menyelesaikan ketikan, lalu lanjut ke baris berikutnya. Hanya satu dialog yang terbuka dalam satu waktu.
// Naikkan volume musik saat Space ditekan, dan lampu berkedip.
void Demo::Update(float dt) {
waktu += dt;
auto bgm = musik.Get();
if (IsKeyDown("Space") && bgm.IsValid())
bgm.SetVolume(std::min(1.0f, bgm.GetVolume() + dt));
if (auto l = lampu.Get(); l.IsValid())
l.SetIntensity(1.0f + 0.5f * std::sin(waktu * 4.0f));
}// Putar video sekali, lalu log saat selesai.
void Intro::Start() {
layar = gameObject.Find("Video Screen").As<VideoPlayerGameObject>();
if (!layar.IsValid()) { LogError("Video Screen tidak ditemukan"); return; }
layar.SetLoop(false);
layar.Play();
}
void Intro::Update(float) {
if (!selesai && layar.IsValid() && !layar.IsPlaying()) {
selesai = true;
Log("Intro selesai");
}
}IsPlaying() sudah true sejak Play() dipanggil (atau sejak awal untuk
Play On Start), walaupun clip baru dibuka di frame berikutnya. Jadi pola di
atas aman. Kalau clip gagal dibuka, nilainya false dan error-nya muncul di
Console.
// Ganti potret karakter sesuai pembicara, dan beri hadiah setelah dialog.
class Potret : public Component {
public:
ObjectRef<DialogGameObject> npc;
ObjectRef<UiGameObject> wajah; // elemen Image
bool selesai = false;
void DescribeProperties(ScriptProperties &p) override {
p.Field("NPC", npc);
p.Field("Wajah", wajah);
}
void Update(float) override {
auto d = npc.Get();
auto w = wajah.Get();
if (!d.IsValid() || !w.IsValid()) return;
w.SetVisible(d.IsTalking());
if (d.IsTalking())
w.SetImage(d.GetSpeaker() == "Sakura" ? "assets/ui/sakura.png"
: "assets/ui/player.png");
if (!selesai && wasTalking && !d.IsTalking()) {
selesai = true; // dialog baru saja ditutup
Log("Dapat hadiah!");
}
wasTalking = d.IsTalking();
}
private:
bool wasTalking = false;
};// Tombol UI biasa (Text pun bisa) dan tombol keyboard satu kali tekan.
void Menu::Update(float) {
if (tombolMulai.Get().WasClicked()) tombolMulai.Get().Hide();
if (IsKeyPressed("Enter")) Log("Enter ditekan sekali");
}Nama class script tidak boleh berupa keyword C++ atau nama SDK:
Component, Vec3, Transform, GameObject, GameObjectRef,
ObjectRef, AudioGameObject, VideoPlayerGameObject, LightGameObject,
CameraGameObject, UiGameObject, DialogGameObject, ScriptProperties.
Alias pendek (Vec3, AudioGameObject, ...) hanya ada saat compile script
(-DILMEEE_SCRIPT_BUILD). Di kode engine, pakai ilmeee::script::....
Server MCP editor punya tool list_ui_elements, set_ui_element,
get_dialog, set_dialog (tambah/ubah komponen Dialog, createUi: true
untuk membuat UI-nya) dan start_dialog (saat Play, untuk testing). Elemen UI
dirujuk dengan namanya, mis. {"index": 7, "lines": "Sakura: Halo!", "createUi": true} lalu save_scene.
Spring bone (physics rambut/dada/rok, model PMX): get_spring_bones dan
set_spring_bones. {"index": 12, "auto": true} mendeteksi bone dari nama
dan menambah collider kepala/dada/paha; field lain (enabled, wind,
chains, colliders) menimpa. Jalan saat Play, lalu save_scene.
set_animation memasang clip (.ilmeeeanim/.vmd) atau controller ke model
PMX, mis. {"index": 12, "clip": "assets/animations/PhysicsDemo.ilmeeeanim"}.
Authoring berjalan di editor native. Urutan umum: list_scene_objects →
get_skeleton → generate_animation atau create_animation →
get_animation / sample_animation → edit_animation → set_animation →
save_scene. Pakai index karakter dari hasil list_scene_objects; index
bisa berubah setelah struktur scene diedit.
| Tool | Parameter utama | Hasil |
|---|---|---|
get_skeleton |
index; opsional group, contains, offset, limit |
Bone, parent, bind position, grup, role humanoid, dan jenis generator |
get_animation |
path; opsional index, includeKeys, bone, offset, limit |
Metadata, jumlah track/key, kecocokan rig, dan keyframe bila diminta |
sample_animation |
path, time; opsional index, bone, loop |
Transform lokal; dengan index juga posisi bone di ruang model |
create_animation |
path; opsional index, name, duration, fps, ops, overwrite |
Clip .ilmeeeanim baru, default 3 detik / 30 FPS |
generate_animation |
index, path, type; opsional name, duration, intensity, ops, overwrite |
Clip prosedural sesuai role rig |
edit_animation |
path, ops; opsional index, output, name, overwrite |
Clip hasil edit atau salinan baru |
path dan output relatif terhadap project aktif. Akses keluar project,
termasuk lewat symlink, ditolak. Output harus .ilmeeeanim; untuk mengedit
VMD, isi output agar sumber .vmd tetap utuh. create_animation,
generate_animation, dan salinan output menolak file yang sudah ada kecuali
overwrite: true; edit_animation tanpa output memperbarui clip asal.
Seluruh batch divalidasi sebelum file disimpan; kegagalan tidak mengubah
clip asal. Edit file langsung tersimpan dan berada di luar Undo/Redo scene.
Tool yang menulis clip dan set_animation memerlukan Play dihentikan.
Bone menerima nama asli, nama alternatif, atau @role, mis. @head,
@upper_body, @arm_l, @elbow_r. Untuk validasi rig dan penggunaan role,
sertakan index. Role yang tersedia ditampilkan oleh get_skeleton; pemetaan
nama mencakup PMX, Mixamo, VRM, dan Biped, sementara playback mengikuti
skeleton yang didukung importer engine. Sumbu karakter: +Z depan, +X kiri
karakter, +Y atas. Rotasi input/output adalah Euler lokal [x,y,z] dalam
derajat; translasi adalah offset lokal dalam satuan model, scale default [1,1,1].
Pada file .ilmeeeanim, rotasi tetap disimpan sebagai quaternion XYZW.
get_skeleton memfilter halaman bone, sedangkan roles/groups selalu
merangkum seluruh rig. contains mencocokkan substring nama asli/alternatif
secara case-sensitive. get_animation selalu menampilkan ringkasan seluruh
track; bone memfilter halaman keys ketika includeKeys: true.
Keduanya memakai offset default 0 dan limit default 200, maksimal 1000,
dengan nextOffset: null pada halaman terakhir. Kecocokan playback dihitung
dengan nama track asli secara persis; nama alternatif dan role diubah menjadi
nama asli ketika membuat key. sample_animation memakai interpolasi clip
dan FK tanpa simulasi spring bone/IK; waktu di-clamp ke durasi atau dibungkus
jika loop: true. Ini inspeksi numerik pose, bukan render GIF/PNG.
Jenis generator: idle, breathe, wave, nod, shake_head, sway,
bow, talk, happy_bounce, look_around. Durasi default menyesuaikan
gerakan; nilai eksplisit 0.2–60 detik, intensity 0–3 (default 1).
Pose lengan santai dikoreksi berdasarkan bind position agar T-pose/A-pose
memakai sudut dasar yang sesuai. Generator menambahkan track hanya untuk
role yang ada pada rig.
Contoh argumen generate_animation (ganti index sesuai karakter):
{"index":12,"path":"assets/animations/Wave.ilmeeeanim","type":"wave","duration":2.4,"intensity":1}Kemudian edit_animation untuk mengatur kepala dan memperlambat gerakan:
{
"index":12,
"path":"assets/animations/Wave.ilmeeeanim",
"ops":[
{"op":"set_key","bone":"@head","time":0.7,"rotation":[0,-12,5],"ease":"smooth"},
{"op":"scale_time","factor":1.25}
]
}Operasi ops (maksimal 2000 per request):
op |
Field | Perilaku |
|---|---|---|
set_key |
bone, time; opsional rotation, translation, scale, ease |
Tambah/ganti key, pertahankan nilai transform yang tidak diberikan dari pose pada waktu itu; durasi bertambah bila perlu |
delete_key |
bone, time |
Hapus key di waktu itu; error jika tidak ada |
clear_track |
bone |
Hapus seluruh track bone |
add_rotation |
bone, rotation; opsional from, to |
Tambah rotasi pada key yang sudah ada dalam rentang inklusif, dengan offset quaternion dikalikan dari kiri |
mirror |
opsional from: "left"/"right", bone |
Salin track ke pasangan sisi; default kiri ke kanan; refleksi YZ, target diganti; bone memilih satu track |
scale_time |
factor (0.01–100) |
Kalikan seluruh waktu key dan durasi |
shift |
by; opsional bone |
Geser waktu key, clamp ke nol, key terakhir menang bila bertumpuk; durasi diperpanjang bila perlu |
set_duration |
duration; opsional fps |
Ganti metadata; durasi tidak boleh memotong key yang masih ada |
set_key.time 0–600 detik, set_duration.duration >0–600 detik, FPS 1–240.
Validasi akhir membatasi clip maksimal 3600 detik / 100000 key / 4096 track.
ease: linear, smooth (default), ease_in, ease_out. Kurva Bezier
melekat pada key tujuan segmen. set_key tanpa ease menggunakan smooth.
Untuk memasang hasil, panggil set_animation dengan
{"index":12,"clip":"assets/animations/Wave.ilmeeeanim"}, lalu save_scene.
Pemberian clip mengaktifkan animasi dan mengosongkan controller lama kecuali
enabled/controller juga diberikan secara eksplisit; controller yang diisi
tetap mendapat prioritas. Kecepatan playback speed 0–4.
Verifikasi tanpa GPU: build dan jalankan TestAnimationTooling serta
TestMcpServer; tes tooling mencakup generator, edit atomik, konversi VMD,
pagination, sampling FK, batas project/symlink, dan round trip HTTP.