Merge branch 'master' of http://82.157.175.59:3000/GenarrativeAI/Genarrative
Some checks failed
CI / verify (push) Has been cancelled

This commit is contained in:
2026-05-09 18:24:41 +08:00
18 changed files with 1460 additions and 118 deletions

View File

@@ -140,6 +140,8 @@ const MATCH3D_ITEM_SPAWN_STAGGER_MS = 4;
const MATCH3D_ITEM_SPAWN_STACK_CLEARANCE = 0.14;
const MATCH3D_ITEM_SPAWN_STACK_RADIUS_PADDING = 0.08;
const MATCH3D_ITEM_SPAWN_ANIMATION_MS = 260;
const MATCH3D_ITEM_SPAWN_VISUAL_SCALE_START = 0.18;
const MATCH3D_ITEM_SPAWN_VISUAL_DROP_OFFSET = 0.04;
const MATCH3D_ITEM_EXTREME_VERTICAL_SPEED_LIMIT = 8.6;
const MATCH3D_ITEM_EXTREME_HORIZONTAL_SPEED_LIMIT = 4.4;
const MATCH3D_CENTER_GRAVITY_COEFFICIENT = 0;
@@ -558,6 +560,18 @@ function resolveSpawnAnimationProgress(entry: PhysicsEntry, now: number) {
);
}
export function resolveMatch3DSpawnVisualScale(progress: number) {
const clampedProgress = Math.min(
1,
Math.max(0, Number.isFinite(progress) ? progress : 0),
);
const easedProgress = 1 - Math.pow(1 - clampedProgress, 3);
return (
MATCH3D_ITEM_SPAWN_VISUAL_SCALE_START +
(1 - MATCH3D_ITEM_SPAWN_VISUAL_SCALE_START) * easedProgress
);
}
function applyCenterGravity(entry: PhysicsEntry) {
if (MATCH3D_CENTER_GRAVITY_COEFFICIENT <= 0) {
return;
@@ -1028,7 +1042,7 @@ function createPhysicsEntryFromPendingSpawn(
0.08,
0.08 + (pendingSpawn.item.layer % 4) * 0.02,
);
visual.mesh.scale.setScalar(0.82);
visual.mesh.scale.setScalar(MATCH3D_ITEM_SPAWN_VISUAL_SCALE_START);
runtime.world.addBody(body);
runtime.scene.add(visual.mesh);
@@ -1714,11 +1728,12 @@ export function Match3DPhysicsBoard({
applyStabilityPlanToBody(entry, activeRuntime.stabilityPlan);
constrainBodyInsidePot(entry);
const spawnProgress = resolveSpawnAnimationProgress(entry, now);
const spawnScale = 0.82 + spawnProgress * 0.18;
const spawnScale = resolveMatch3DSpawnVisualScale(spawnProgress);
entry.mesh.scale.setScalar(spawnScale);
entry.mesh.position.set(
entry.body.position.x,
entry.body.position.y - (1 - spawnProgress) * 0.06,
entry.body.position.y -
(1 - spawnProgress) * MATCH3D_ITEM_SPAWN_VISUAL_DROP_OFFSET,
entry.body.position.z,
);
entry.mesh.quaternion.set(

View File

@@ -31,6 +31,7 @@ import {
resolveMatch3DSpawnTimingPlan,
resolveMatch3DStackTargetY,
resolveMatch3DSpawnDelay,
resolveMatch3DSpawnVisualScale,
resolveMatch3DSpawnY,
resolveMatch3DTrayPreviewRotation,
resolveMatch3DTrayPreviewReferenceDimension,
@@ -591,6 +592,18 @@ test('3D 新物体生成高度会避让同位置已有堆叠', () => {
expect(unchangedSpawnY).toBe(plannedSpawnY);
});
test('3D 新物体生成动画只缩放可见模型并最终回到完整尺寸', () => {
const startScale = resolveMatch3DSpawnVisualScale(0);
const middleScale = resolveMatch3DSpawnVisualScale(0.5);
const endScale = resolveMatch3DSpawnVisualScale(1);
expect(startScale).toBeGreaterThan(0);
expect(startScale).toBeLessThan(0.25);
expect(middleScale).toBeGreaterThan(startScale);
expect(middleScale).toBeLessThan(endScale);
expect(endScale).toBe(1);
});
test('积木视觉键不会被统一兜底成红色苹字', () => {
const run = startLocalMatch3DRun(2);
run.items = run.items.slice(0, 2).map((item, index) => ({