Factory function to create 작은 튀어오르는 보상 상자 in your world.
export function createAsset(THREE, options = {}) {
const bodyColor = options.body_color || '#C07A46';
const lidColor = options.lid_color || '#6A4A34';
const indicatorColor = options.indicator_color || '#B9F26B';
const coreGlow = Math.max(0, Math.min(1.5, options.core_glow ?? 0.45));
const highlightLevel = Math.max(0, Math.min(1.5, options.highlight_level ?? 0.8));
const poseTilt = Math.max(-0.18, Math.min(0.18, options.pose_tilt ?? 0.04));
const group = new THREE.Group();
group.name = 'small-bouncing-reward-crate';
group.rotation.z = poseTilt;
group.userData = {
assetId: 'small-bouncing-reward-crate',
gameplay: { role: 'reward', affordance: 'pickup_hint' }
};
const bodyMat = new THREE.MeshStandardMaterial({ color: new THREE.Color(bodyColor), roughness: 0.78, metalness: 0.02 });
const lidMat = new THREE.MeshStandardMaterial({ color: new THREE.Color(lidColor), roughness: 0.82, metalness: 0.02 });
const baseMat = new THREE.MeshStandardMaterial({ color: new THREE.Color('#4A4A4A'), roughness: 0.72, metalness: 0.08 });
const rimMat = new THREE.MeshStandardMaterial({ color: new THREE.Color(indicatorColor), roughness: 0.45, metalness: 0.05, emissive: new THREE.Color(indicatorColor), emissiveIntensity: 0.12 * highlightLevel });
const coreMat = new THREE.MeshStandardMaterial({ color: new THREE.Color('#FAE29A'), roughness: 0.38, metalness: 0.04, emissive: new THREE.Color(indicatorColor), emissiveIntensity: coreGlow, transparent: true, opacity: 0.95 });
function markPart(object, partId, label) {
object.userData = object.userData || {};
object.userData.partId = partId;
object.userData.selection = { id: partId, kind: 'asset', label };
return object;
}
const base = markPart(new THREE.Mesh(new THREE.BoxGeometry(1.7, 0.18, 1.7), baseMat), 'crate-base', '받침판');
base.name = 'crate-base';
base.position.set(0, 0.09, 0);
group.add(base);
const body = markPart(new THREE.Group(), 'crate-body', '본체');
body.name = 'crate-body';
group.add(body);
const bodyBlock = new THREE.Mesh(new THREE.BoxGeometry(1.35, 1.05, 1.35), bodyMat);
bodyBlock.name = 'crate-body-block';
bodyBlock.position.set(0, 0.72, 0);
body.add(bodyBlock);
const rimTop = new THREE.Mesh(new THREE.BoxGeometry(1.48, 0.07, 0.08), rimMat);
rimTop.name = 'crate-body-rim-top';
rimTop.position.set(0, 1.22, 0.72);
body.add(rimTop);
const rimBottom = new THREE.Mesh(new THREE.BoxGeometry(1.48, 0.06, 0.08), rimMat);
rimBottom.name = 'crate-body-rim-bottom';
rimBottom.position.set(0, 0.32, 0.72);
body.add(rimBottom);
const lid = markPart(new THREE.Group(), 'crate-lid', '뚜껑');
lid.name = 'crate-lid';
group.add(lid);
const lidBlock = new THREE.Mesh(new THREE.BoxGeometry(1.52, 0.32, 1.52), lidMat);
lidBlock.name = 'crate-lid-block';
lidBlock.position.set(0, 1.42, 0);
lid.add(lidBlock);
const latch = new THREE.Mesh(new THREE.BoxGeometry(0.2, 0.16, 0.1), lidMat);
latch.name = 'crate-lid-latch';
latch.position.set(0, 1.22, 0.78);
lid.add(latch);
const core = markPart(new THREE.Mesh(new THREE.BoxGeometry(0.42, 0.42, 0.08), coreMat), 'crate-core', '중앙 마크');
core.name = 'crate-core';
core.position.set(0, 0.82, 0.705);
group.add(core);
return group;
}