Sebelumnya kita sudah belajar bikin Geometry sendiri dan atur-atur nilainya lewat kode. Sekarang kita bikin panel biar nilai-nilai itu bisa diutak-atik langsung dari layar.
Setiap project kreatif itu wajib gampang di-utak-atik. Bukan cuma sama kita yang ngoding, tapi juga sama orang lain yang terlibat kaya designer atau bahkan klien. Mereka harus bisa ganti sebanyak mungkin nilai: warna, kecepatan, jumlah, ukuran, dan lain-lain, biar ketemu kombinasi paling pas.
Bayangin kalau tiap mau ganti warna kubus kita harus buka code editor, cari 0xff0000, ganti, simpan, tunggu reload. Sepuluh kali percobaan aja udah males duluan. Padahal seringnya justru dari coba-coba iseng begitulah kita nemu hasil bagus yang gak kepikiran sebelumnya.
Jadi, kita butuh Debug UI, panel kecil di pojok layar yang isinya slider dan tombol buat ngatur nilai-nilai tadi secara langsung.
Pilihan Library
Kita sebenarnya bisa bikin Debug UI sendiri pakai HTML / CSS / JS biasa, tapi ngapain capek-capek kalau udah banyak yang bikinin:
Semuanya bisa ngelakuin hal yang kita mau, tapi di sini kita pakai lil-gui karena populer, masih diurus sama pembuatnya, dan gampang dipakai.
Sedikit cerita biar gak bingung: dulu semua latihan Three.js Journey pakai dat.GUI. Sempat lama gak di-update sampai NPM ngasih peringatan vulnerability tiap kali di-install. Sekarang peringatannya udah beres, cuma keburu banyak yang pindah ke lil-gui yang cara pakainya mirip banget tapi fiturnya lebih enak. Jadi kalau di video-video berikutnya kamu masih lihat dat.GUI, cuekin aja, pakai lil-gui dengan cara yang sama persis.
Oh iya, GUI itu singkatan dari Graphical User Interface.
Memanggil lil-gui
Untungnya lil-gui udah kita install dari awal di 01 Memulai Three JS > 1. Install Vite, Three JS, dan lil-gui, jadi gak perlu install lagi. Kalau ternyata belum ada, tinggal jalanin ini di terminal (matiin dulu servernya, atau buka jendela terminal baru di folder yang sama), terus nyalain lagi servernya setelah selesai:
pnpm add lil-guiSekarang tinggal kita import di script.js:
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
import gsap from 'gsap'
import GUI from 'lil-gui'Terus kita bikin objek GUI-nya di variabel gui, taruh paling atas aja setelah semua import:
/**
* Debug
*/
const gui = new GUI()Ada juga gaya penulisan lain yang mungkin kamu temuin:
import * as dat from 'lil-gui'
// ...
const gui = new dat.GUI()Dua-duanya hasilnya sama persis, tinggal pilih yang kamu suka.
Sekarang coba jalanin, di pojok kanan atas bakal muncul panel kosong. Itu wadahnya, sekarang kita isi.
Contoh Debug UI yang niat banget bisa dilihat di portfolio-nya Bruno Simon, panelnya baru nongol kalau URL-nya ditambahin #debug. Di situ gravitasi, warna, kecepatan, sampai posisi elemen bisa diutak-atik. Bikinnya makan waktu lama, tapi tanpa itu game-nya bakal kerasa gak seimbang.
Jenis-jenis Tweak
Tweak itu istilah buat satu baris pengaturan di dalam panel. Ada beberapa jenis:
- Range, buat angka yang punya nilai minimum dan maksimum. Bentuknya slider.
- Color, buat warna dengan berbagai format. Bentuknya kotak color picker.
- Text, buat teks biasa.
- Checkbox, buat boolean (
trueataufalse). Bentuknya kotak centang. - Select, buat milih satu dari beberapa pilihan. Bentuknya dropdown.
- Button, buat mencet tombol yang menjalankan fungsi.
Kita bahas yang paling sering dipakai satu-satu.
1. Range
Hampir semua tweak ditambahkan pakai gui.add(...). Parameter pertama itu objeknya, parameter kedua itu nama properti dari objek tersebut yang mau kita utak-atik.
Satu hal yang wajib diinget: gui.add(...) harus ditulis setelah objek dan properti itu dibuat. Kalau ditulis duluan, artinya kita nyuruh lil-gui ngatur sesuatu yang belum ada wujudnya.
❌ salah, mesh belum lahir pas dipanggil
gui.add(mesh.position, 'y')
const mesh = new THREE.Mesh(geometry, material)
scene.add(mesh)✅ benar, panggil setelah mesh dibuat
const mesh = new THREE.Mesh(geometry, material)
scene.add(mesh)
gui.add(mesh.position, 'y')Coba jalanin, sekarang muncul satu kolom input di panel. Ubah angkanya, dan kubusnya langsung naik-turun ngikutin, persis kaya waktu kita atur position manual di 03 Transform Object > `position` Memindahkan Object, bedanya sekarang gak perlu ngedit kode.
Cuma bentuknya masih kolom angka, belum slider. Biar jadi slider, kita tentuin nilai minimum, nilai maksimum, dan tingkat ketelitiannya (step) lewat parameter berikutnya:
gui.add(mesh.position, 'y', - 3, 3, 0.01)Jadi bacanya: objeknya mesh.position, propertinya y, minimalnya -3, maksimalnya 3, dan loncatannya 0.01. Sekarang bentuknya udah slider yang enak digeser-geser.
Masalahnya, parameter berderet gitu susah dibaca. Angka -3, 3, 0.01 itu yang mana minimum yang mana step? Harus hafal urutannya. Makanya ada cara kedua pakai method min(...), max(...), dan step(...) yang dirantai langsung setelah add(...):
gui.add(mesh.position, 'y').min(- 3).max(3).step(0.01)Dan kalau kepanjangan ke samping, tinggal dipecah jadi beberapa baris:
gui
.add(mesh.position, 'y')
.min(- 3)
.max(3)
.step(0.01)Sekarang jelas banget mana yang minimum, mana yang maksimum. Ini yang bakal kita pakai seterusnya.
Terakhir, label di panel masih ketulis y doang, kurang informatif. Kita ganti pakai method name(...):
gui
.add(mesh.position, 'y')
.min(- 3)
.max(3)
.step(0.01)
.name('elevation')2. Tweak untuk yang Bukan Properti
Ini penting banget dan sering bikin kejeblos: lil-gui cuma bisa ngutak-atik properti dari sebuah objek. Dia gak bisa ngutak-atik variabel yang berdiri sendiri.
❌ salah, mau diisi apa parameter keduanya? Gak ada jawabannya
let myVariable = 1337
gui.add(myVariable, '???')Kenapa gak bisa? Karena gui.add() butuh dua hal: wadahnya dan nama isinya. Kalau kita cuma kasih variabel telanjang, lil-gui gak punya pegangan buat ngubah nilainya nanti.
✅ benar, akalin dengan bikin objek yang tugasnya emang cuma jadi wadah
const myObject = {
myVariable: 1337
}
gui.add(myObject, 'myVariable')Lagi-lagi ngoding itu segalanya tentang ngakalin. Trik ini bakal kepakai terus di 4. Colors, 5. Function / Button, dan Ngutak-atik Geometry, jadi diinget baik-baik. Untuk sekarang, hapus dulu contoh ini karena cuma buat penjelasan.
3. Checkbox
lil-gui itu pinter, dia otomatis ngedeteksi jenis propertinya terus milih tampilan yang cocok. Contoh bagusnya properti visible milik Object3D, yaitu class induk dari semua object di ThreeJS. Isinya boolean, kalau diisi false objeknya bakal disembunyiin:
gui.add(mesh, 'visible')Kita gak bilang apa-apa soal checkbox, tapi yang muncul di panel ya kotak centang, karena lil-gui lihat isinya true/false.
Hal yang sama bisa kita lakuin ke properti wireframe milik material, yang udah kita kenalan di 07 Geometry > 2. Wireframe:
gui.add(material, 'wireframe')4. Colors
Nah, warna ini agak ribet. Kita mau ngutak-atik properti color milik material.
Pertama, kita gak bisa pakai add(...), harus pakai addColor(...). Alasannya: properti color itu bukan angka, bukan teks, bukan boolean, tapi sebuah objek hasil dari class Color bawaan ThreeJS. Di dalamnya ada properti r, g, dan b yang dipakai lil-gui buat nampilin color picker:
gui.addColor(material, 'color')Sekarang muncul kotak warna di panel, dan warnanya bisa kita ganti sesuka hati.
Tapi ada jebakan besar di sini. Coba ambil nilai warna yang ketulis di panel itu, terus tempel ke kode kita. Hasilnya? Warnanya beda.
Kenapa? Karena ThreeJS diam-diam ngelakuin color management biar hasil renderingnya lebih optimal. Akibatnya, nilai warna yang ditampilkan di panel itu bukan nilai yang dipakai ThreeJS di dalam. Bahasan color management ada di bab lain, tapi sekarang kita tetap butuh nilai warna yang benar.
Ada dua cara buat ngatasinnya.
Cara 1: Ambil warna yang sudah diolah ThreeJS
Cara pertama, kita ambil warna versi dalamnya ThreeJS pakai method getHexString() tiap kali nilai tweak-nya berubah.
Buat tau kapan nilainya berubah, kita pakai method onChange():
gui
.addColor(material, 'color')
.onChange(() =>
{
console.log('value has changed')
})Terus kita akses instance Color-nya. Bisa lewat material.color seperti biasa, atau lebih singkat lagi lewat parameter yang dikirim ke fungsinya:
gui
.addColor(material, 'color')
.onChange((value) =>
{
console.log(material.color)
console.log(value)
})Dua-duanya nunjuk ke instance Color yang sama persis. Sekarang tinggal panggil getHexString()-nya:
gui
.addColor(material, 'color')
.onChange((value) =>
{
console.log(value.getHexString())
})Nilai yang keluar di Console itulah warna yang aman kita tempel ke kode.
Masalahnya, cara ini mengharuskan kita buka Console terus-terusan. Ribet banget, apalagi kalau yang ngutak-atik itu designer atau klien yang boro-boro tau apa itu Console.
Cara 2: Simpan warna sebelum disentuh ThreeJS
Cara kedua ini yang lebih enak: kita urusin warnanya sebelum ThreeJS sempat ngubah-ngubah.
Pertama, kita simpan warnanya di luar ThreeJS. Ingat trik objek wadah di 2. Tweak untuk yang Bukan Properti? Nah, sekarang kepakai. Bikin satu objek kosong khusus buat nampung nilai-nilai debug. Saya biasanya kasih nama debugObject (ada juga yang suka nyebut global atau parameters), taruh persis setelah bikin GUI:
const gui = new GUI()
const debugObject = {}Terus kita tambahin properti color ke debugObject, dan ini ditulis sebelum bikin kubus biar bisa langsung dikirim ke materialnya:
debugObject.color = '#3a6ea6'
const geometry = new THREE.BoxGeometry(1, 1, 1, 2, 2, 2)
const material = new THREE.MeshBasicMaterial({ color: debugObject.color, wireframe: false })Untungnya di sini: warna awal cuma ditulis di satu tempat. Mau ganti warna default? Ubah satu baris itu doang.
Terus tweak-nya kita alihin, yang tadinya nunjuk ke material.color sekarang nunjuk ke debugObject.color:
gui
.addColor(debugObject, 'color')Tapi kalau berhenti di situ doang, warnanya gak bakal berubah sama sekali waktu digeser, soalnya kita cuma ngubah angka di dalam debugObject, materialnya gak dikasih tau apa-apa. Jadi kita sambung pakai onChange() dan kasih tau materialnya pakai method set():
gui
.addColor(debugObject, 'color')
.onChange(() =>
{
material.color.set(debugObject.color)
})Saya lebih suka cara ini, karena gak ada yang perlu buka Console. Tapi terserah kamu, dua-duanya sah.
Terakhir, kita balikin warnanya jadi #a778d8:
debugObject.color = '#a778d8'5. Function / Button
Kadang kita gak mau ngatur nilai, kita cuma mau mencet tombol terus sesuatu terjadi. Contohnya sekarang: kita mau kubusnya muter satu putaran penuh tiap kali tombol di panel dipencet.
Caranya sama kaya yang lain, kita kirim sebuah properti yang isinya fungsi. Dan lagi-lagi, fungsi telanjang gak bisa:
❌ salah, sama kaya kasus variabel tadi, gak ada wadahnya
const myFunction = () => {
console.log('do something')
}
gui.add(myFunction, '???')✅ benar, tempelin sebagai properti di debugObject, animasinya pakai GSAP yang sudah kita pelajari di bab Animasi
debugObject.spin = () =>
{
gsap.to(mesh.rotation, { duration: 1, y: mesh.rotation.y + Math.PI * 2 })
}
gui.add(debugObject, 'spin')Math.PI * 2 itu satu putaran penuh dalam radian alias 360 derajat, dan kita tambahin ke rotasi yang sekarang (mesh.rotation.y + ...), bukan diisi angka mati. Kalau diisi angka mati, pencetan kedua gak bakal ada efeknya karena posisinya udah di situ.
Sekarang di panel muncul tombol spin, dan tiap dipencet kubusnya muter satu putaran dengan animasi mulus.
Ngutak-atik Geometry
Sekarang yang lebih menantang: kita mau ngatur jumlah subdivision geometry-nya langsung dari panel, yang di 07 Geometry > 1. Segments alias Subdivisions masih kita ganti manual di kode.
Biar kelihatan segitiganya, kita nyalain dulu wireframe-nya:
const material = new THREE.MeshBasicMaterial({ color: '#9c7fe3', wireframe: true })Kalau kita lihat dokumentasi BoxGeometry, parameter yang ngatur subdivision itu namanya widthSegments, heightSegments, dan depthSegments. Ya udah, tinggal kita tambahin tweak-nya kan?
gui
.add(geometry, 'widthSegments')
.min(1)
.max(20)
.step(1)Error. Kenapa? Karena widthSegments itu bukan properti dari geometry. Dia cuma parameter yang kita kirim sekali doang waktu BoxGeometry dibuat, dipakai buat ngerakit bentuknya, terus udah, gak disimpan buat diubah-ubah lagi.
Jadi ada dua masalah yang harus kita selesaikan: (1) nilainya harus punya wadah, dan (2) geometry-nya harus dirakit ulang dari nol tiap nilainya berubah.
Masalah pertama kita selesaikan dengan debugObject lagi:
debugObject.subdivision = 2
gui
.add(debugObject, 'subdivision')
.min(1)
.max(20)
.step(1)Namanya kita bikin subdivision aja (bukan widthSegments) biar satu nilai ini bisa kita pakai buat ketiga-tiganya sekaligus.
Masalah kedua: tiap nilainya berubah, kita hancurin geometry lama dan bikin yang baru. Awalnya kita dengerin onChange dulu buat mastiin jalan:
gui
.add(debugObject, 'subdivision')
.min(1)
.max(20)
.step(1)
.onChange(() =>
{
console.log('subdivision changed')
})Tapi ini pilihan yang buruk. Ngerakit geometry itu kerjaan berat buat CPU, sedangkan onChange kepanggil tiap kali slider-nya gerak sedikiiit aja. Bayangin orang geser slider dari 1 ke 20 sambil ditarik pelan, bisa ratusan kali geometry dibangun ulang dalam sedetik. Laptopnya bisa ngambek.
Solusinya pakai onFinishChange, yang cuma jalan pas kita udah lepas slider-nya:
❌ berat, dipanggil tiap gerakan slider
.onChange(() =>
{
// bikin ulang geometry
})✅ enteng, dipanggil sekali pas selesai digeser
.onFinishChange(() =>
{
// bikin ulang geometry
})Sekarang kita ganti console.log()-nya dengan bikin geometry baru, terus kita tempelin ke properti geometry milik mesh:
gui
.add(debugObject, 'subdivision')
.min(1)
.max(20)
.step(1)
.onFinishChange(() =>
{
mesh.geometry = new THREE.BoxGeometry(
1, 1, 1,
debugObject.subdivision, debugObject.subdivision, debugObject.subdivision
)
})Jalan! Tapi kita baru aja bikin kesalahan kecil yang efeknya gak kelihatan: geometry-geometry lama masih nyangkut di memori GPU. Kita cuma nyuruh mesh berhenti make yang lama, tapi barangnya gak dibuang, cuma ditinggal. Geser slider 50 kali, numpuk 50 geometry gak kepake di GPU. Ini namanya memory leak.
Perbaikannya gampang, panggil method dispose() ke geometry lama sebelum bikin yang baru:
gui
.add(debugObject, 'subdivision')
.min(1)
.max(20)
.step(1)
.onFinishChange(() =>
{
mesh.geometry.dispose()
mesh.geometry = new THREE.BoxGeometry(
1, 1, 1,
debugObject.subdivision, debugObject.subdivision, debugObject.subdivision
)
})Urutannya penting: buang dulu, baru bikin baru. Kalau kebalik, mesh.geometry udah keburu nunjuk ke yang baru, terus yang kita buang malah geometry yang baru dibikin.
Folders
Coba bayangin project yang udah gede, tweak-nya bisa puluhan. Panelnya jadi kaya gulungan kain, panjang ke bawah dan bingung nyari yang mana.
Solusinya: kelompokin ke dalam folder pakai method addFolder(). Panggil sebelum bikin tweak-nya, terus simpan di sebuah variabel:
const cubeTweaks = gui.addFolder('Awesome cube')Terus semua tweak yang tadinya nempel di gui, sekarang nempel di cubeTweaks:
const cubeTweaks = gui.addFolder('Awesome cube')
cubeTweaks
.add(mesh.position, 'y')
.min(- 3)
.max(3)
.step(0.01)
.name('elevation')
cubeTweaks
.add(mesh, 'visible')
cubeTweaks
.add(material, 'wireframe')
cubeTweaks
.addColor(debugObject, 'color')
.onChange(() =>
{
material.color.set(debugObject.color)
})
cubeTweaks
.add(debugObject, 'spin')
cubeTweaks
.add(debugObject, 'subdivision')
.min(1)
.max(20)
.step(1)
.onFinishChange(() =>
{
mesh.geometry.dispose()
mesh.geometry = new THREE.BoxGeometry(
1, 1, 1,
debugObject.subdivision, debugObject.subdivision, debugObject.subdivision
)
})Kalau mau foldernya ketutup dari awal, panggil method close():
const cubeTweaks = gui.addFolder('Awesome cube')
cubeTweaks.close()Folder juga bisa disarangin di dalam folder lain, jadi gak ada alasan buat punya Debug UI yang berantakan. Kali ini gak kita pakai, jadi close()-nya kita komen dulu biar foldernya kebuka:
// cubeTweaks.close()Pengaturan Panelnya Sendiri
lil-gui itu lentur, panelnya sendiri juga bisa kita atur.
1. Width
Lebar panelnya diatur dengan ngirim objek berisi properti width waktu bikin GUI-nya:
const gui = new GUI({
width: 300
})2. Title
Judul di atas panel diatur pakai properti title:
const gui = new GUI({
width: 300,
title: 'Nice debug UI'
})3. Close Folders
Kalau mau semua folder ketutup dari awal, pakai closeFolders:
const gui = new GUI({
width: 300,
title: 'Nice debug UI',
closeFolders: true
})Kita balikin ke false dulu:
const gui = new GUI({
width: 300,
title: 'Nice debug UI',
closeFolders: false
})4. Close
Panelnya sendiri bisa dilipat dari awal pakai method close():
const gui = new GUI({
width: 300,
title: 'Nice debug UI',
closeFolders: false
})
gui.close()Komen dulu:
// gui.close()5. Hide
Kalau `close()` cuma ngelipat, hide() bener-bener ngilangin panelnya:
const gui = new GUI({
width: 300,
title: 'Nice debug UI',
closeFolders: false
})
// gui.close()
gui.hide()6. Toggling
Tapi kalau udah disembunyiin, munculinnya gimana? Terserah kamu mau bikin caranya kaya apa, tapi yang paling gampang: dengerin tombol keyboard pakai keydown, mirip cara kita masang “kuping” buat dengerin perubahan ukuran layar. Misalnya tombol h buat sembunyi/muncul:
window.addEventListener('keydown', (event) =>
{
if(event.key == 'h')
gui.show(gui._hidden)
})Cara kerjanya: gui._hidden itu properti berisi true kalau panelnya lagi sembunyi. Jadi gui.show(gui._hidden) artinya “tampilkan panelnya sesuai kebalikan keadaan sekarang”. Lagi sembunyi (_hidden = true) → show(true) → muncul. Lagi muncul (_hidden = false) → show(false) → sembunyi. Satu baris, dua fungsi.
Terakhir, komen hide()-nya biar panelnya kelihatan dari awal:
// gui.hide()7. Dan Lain-lain
Masih banyak yang bisa diatur di dokumentasi lil-gui kalau kamu penasaran, kaya styling, posisi panel, bikin tweak yang otomatis ikut ter-update kalau nilainya berubah dari kode, jenis tweak lain kaya Select, dan sebagainya.
Code Lengkap
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
import gsap from 'gsap'
import GUI from 'lil-gui'
/**
* Debug
*/
const gui = new GUI({
width: 300,
title: 'Nice debug UI',
closeFolders: false
})
// gui.close()
// gui.hide()
window.addEventListener('keydown', (event) =>
{
if(event.key == 'h')
gui.show(gui._hidden)
})
const debugObject = {}
/**
* Base
*/
// Canvas
const canvas = document.querySelector('canvas.webgl')
// Scene
const scene = new THREE.Scene()
// Sizes
const sizes = {
width: window.innerWidth,
height: window.innerHeight
}
/**
* Object
*/
debugObject.color = '#a778d8'
const geometry = new THREE.BoxGeometry(1, 1, 1, 2, 2, 2)
const material = new THREE.MeshBasicMaterial({ color: debugObject.color, wireframe: true })
const mesh = new THREE.Mesh(geometry, material)
scene.add(mesh)
const cubeTweaks = gui.addFolder('Awesome cube')
cubeTweaks
.add(mesh.position, 'y')
.min(- 3)
.max(3)
.step(0.01)
.name('elevation')
cubeTweaks
.add(mesh, 'visible')
cubeTweaks
.add(material, 'wireframe')
cubeTweaks
.addColor(debugObject, 'color')
.onChange(() =>
{
material.color.set(debugObject.color)
})
debugObject.spin = () =>
{
gsap.to(mesh.rotation, { duration: 1, y: mesh.rotation.y + Math.PI * 2 })
}
cubeTweaks
.add(debugObject, 'spin')
debugObject.subdivision = 2
cubeTweaks
.add(debugObject, 'subdivision')
.min(1)
.max(20)
.step(1)
.onFinishChange(() =>
{
mesh.geometry.dispose()
mesh.geometry = new THREE.BoxGeometry(
1, 1, 1,
debugObject.subdivision, debugObject.subdivision, debugObject.subdivision
)
})
/**
* Camera
*/
const camera = new THREE.PerspectiveCamera(75, sizes.width / sizes.height, 0.1, 100)
camera.position.z = 3
scene.add(camera)
// Controls
const controls = new OrbitControls(camera, canvas)
controls.enableDamping = true
/**
* Renderer
*/
const renderer = new THREE.WebGLRenderer({
canvas: canvas
})
renderer.setSize(sizes.width, sizes.height)
/**
* Animate
*/
const tick = () =>
{
// Update controls
controls.update()
// Render
renderer.render(scene, camera)
// Call tick again on the next frame
window.requestAnimationFrame(tick)
}
tick()Ringkasan
- Debug UI itu panel buat ngutak-atik nilai langsung dari layar, biar kita, designer, atau klien gak perlu bolak-balik ngedit kode cuma buat nyari warna atau kecepatan yang pas.
- Kita pakai
lil-gui, penerusnyadat.GUIyang masih diurus dan fiturnya lebih enak. Di project kita dia udah keinstall sejak 01 Memulai Three JS > 1. Install Vite, Three JS, dan lil-gui. gui.add(objek, 'namaProperti')itu rumus dasarnya, dan wajib ditulis setelah objeknya dibuat.- lil-gui cuma bisa ngutak-atik properti dari sebuah objek, bukan variabel atau fungsi yang berdiri sendiri. Akalin dengan bikin objek wadah, biasanya dinamain
debugObject. - Pakai
.min().max().step()yang dirantai, jangan angka berderet di dalamadd(...), biar kebaca mana yang mana. Labelnya diganti pakai.name(). - lil-gui otomatis milih bentuk tampilan sesuai jenis propertinya, boolean jadi checkbox, angka jadi slider, dan seterusnya.
- Warna wajib pakai
addColor(...), bukanadd(...), karenacoloritu objekColor, bukan angka atau teks. - Nilai warna di panel ≠ nilai warna yang dipakai ThreeJS, gara-gara color management. Dua solusinya ada di Cara 1: Ambil warna yang sudah diolah ThreeJS dan Cara 2: Simpan warna sebelum disentuh ThreeJS.
- Tombol dibuat dengan naruh fungsi sebagai properti di
debugObject, contohnyadebugObject.spinyang isinya animasi GSAPmesh.rotation.y + Math.PI * 2. widthSegmentsdkk bukan properti geometry, cuma parameter sekali pakai. Jadi buat ngubahnya, geometry-nya harus dirakit ulang dari nol.- Pakai
onFinishChangebuat kerjaan berat, bukanonChange.onChangejalan tiap slider bergerak sedikit,onFinishChangecuma jalan pas slider dilepas. - Buang geometry lama pakai
dispose()sebelum bikin yang baru, kalau nggak numpuk di memori GPU dan jadi memory leak. - Rapikan panel pakai
gui.addFolder('nama'), folder bisa disarangin, dan bisa ditutup dari awal pakai.close(). - Panelnya sendiri bisa diatur lewat objek pengaturan:
width,title,closeFolders, plus methodclose(),hide(), danshow(). - Sembunyiin/munculin panel pakai satu baris
gui.show(gui._hidden)yang dipasang di eventkeydown. - Tambahin tweak sambil jalan, jangan ditunda sampai akhir project. Kalau ditunda, ujung-ujungnya gak ada tweak sama sekali dan kita kehilangan banyak kesempatan nemu hasil bagus dari coba-coba.
Bab selanjutnya kita akan belajar Textures, biar objek kita gak polos warna doang tapi punya permukaan yang beneran kelihatan kaya kayu, batu, atau besi.