独立渲染引擎就绪引擎就绪
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Transformable Whegs - IFR Principle Animation</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;700;800&family=Rajdhani:wght@500;600;700&display=swap');
:root {
--bg-color: #050914;
--grid-color: rgba(0, 240, 255, 0.05);
--primary-cyan: #00f0ff;
--primary-cyan-dim: rgba(0, 240, 255, 0.3);
--accent-magenta: #ff2a5f;
--accent-green: #00ff66;
--structural-gray: #475569;
--text-main: #e2e8f0;
--hud-bg: rgba(5, 9, 20, 0.8);
}
body {
margin: 0;
padding: 0;
background-color: var(--bg-color);
color: var(--text-main);
font-family: 'Rajdhani', sans-serif;
overflow: hidden;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background-image:
linear-gradient(var(--grid-color) 1px, transparent 1px),
linear-gradient(90deg, var(--grid-color) 1px, transparent 1px);
background-size: 40px 40px;
background-position: center center;
}
#animation-container {
position: relative;
width: 100vw;
max-width: 1400px;
aspect-ratio: 16/9;
max-height: 100vh;
box-shadow: 0 0 100px rgba(0, 240, 255, 0.05) inset;
border: 1px solid rgba(0, 240, 255, 0.1);
overflow: hidden;
}
svg {
width: 100%;
height: 100%;
display: block;
}
/* HUD Overlay Styles */
.hud-panel {
position: absolute;
background: var(--hud-bg);
border: 1px solid var(--primary-cyan-dim);
padding: 15px 20px;
backdrop-filter: blur(4px);
border-left: 3px solid var(--primary-cyan);
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5);
pointer-events: none;
}
.hud-top-left { top: 30px; left: 30px; }
.hud-bottom-right { bottom: 30px; right: 30px; border-left: none; border-right: 3px solid var(--accent-magenta); text-align: right; }
.hud-bottom-left { bottom: 30px; left: 30px; }
.hud-title {
font-family: 'JetBrains Mono', monospace;
font-size: 0.8rem;
color: var(--primary-cyan);
letter-spacing: 2px;
text-transform: uppercase;
margin-bottom: 5px;
}
.hud-value {
font-family: 'JetBrains Mono', monospace;
font-size: 2rem;
font-weight: 800;
color: #fff;
text-shadow: 0 0 10px var(--primary-cyan-dim);
}
.hud-value.alert {
color: var(--accent-magenta);
text-shadow: 0 0 15px rgba(255, 42, 95, 0.5);
}
.hud-value.success {
color: var(--accent-green);
text-shadow: 0 0 15px rgba(0, 255, 102, 0.5);
}
.hud-unit {
font-size: 1rem;
color: var(--structural-gray);
margin-left: 5px;
}
.hud-label {
font-size: 0.9rem;
color: #94a3b8;
margin-top: 5px;
}
.triz-box {
position: absolute;
top: 30px;
right: 30px;
width: 320px;
background: rgba(0, 255, 102, 0.05);
border: 1px solid rgba(0, 255, 102, 0.2);
border-top: 3px solid var(--accent-green);
padding: 15px;
backdrop-filter: blur(4px);
}
.triz-header {
font-family: 'JetBrains Mono', monospace;
color: var(--accent-green);
font-size: 0.85rem;
font-weight: bold;
margin-bottom: 8px;
display: flex;
align-items: center;
gap: 8px;
}
.triz-header::before {
content: '';
display: block;
width: 8px;
height: 8px;
background: var(--accent-green);
box-shadow: 0 0 8px var(--accent-green);
}
.triz-desc {
font-size: 0.95rem;
line-height: 1.4;
color: #cbd5e1;
}
.highlight-text {
color: #fff;
font-weight: 600;
text-shadow: 0 0 5px rgba(255,255,255,0.3);
}
/* SVG Element Styles */
.glowing-cyan { filter: drop-shadow(0 0 8px var(--primary-cyan)); }
.glowing-magenta { filter: drop-shadow(0 0 12px var(--accent-magenta)); }
.glowing-green { filter: drop-shadow(0 0 8px var(--accent-green)); }
.fade-overlay {
position: absolute;
top: 0; left: 0; width: 100%; height: 100%;
background: var(--bg-color);
pointer-events: none;
transition: opacity 0.5s ease;
opacity: 0;
z-index: 100;
}
</style>
</head>
<body>
<div id="animation-container">
<svg id="canvas" viewBox="0 0 1600 900" preserveAspectRatio="xMidYMid slice">
<defs>
<!-- Filters for Glow Effects -->
<filter id="glow-cyan" x="-20%" y="-20%" width="140%" height="140%">
<feGaussianBlur stdDeviation="4" result="blur" />
<feMerge>
<feMergeNode in="blur" />
<feMergeNode in="SourceGraphic" />
</feMerge>
</filter>
<filter id="glow-magenta" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur stdDeviation="6" result="blur" />
<feMerge>
<feMergeNode in="blur" />
<feMergeNode in="SourceGraphic" />
</feMerge>
</filter>
<!-- Patterns -->
<pattern id="stair-texture" x="0" y="0" width="20" height="20" patternUnits="userSpaceOnUse">
<path d="M 20 0 L 0 20" stroke="#1e293b" stroke-width="1" fill="none"/>
</pattern>
<linearGradient id="ground-grad" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" stop-color="#0f172a" />
<stop offset="100%" stop-color="#050914" />
</linearGradient>
<linearGradient id="force-grad" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" stop-color="rgba(255, 42, 95, 0)" />
<stop offset="100%" stop-color="rgba(255, 42, 95, 1)" />
</linearGradient>
</defs>
<!-- Environment -->
<g id="environment">
<!-- Grid Background Lines (Technical Vibe) -->
<path d="M 0 600 L 1600 600" stroke="rgba(0,240,255,0.2)" stroke-width="1" stroke-dasharray="4 4" />
<path d="M 0 350 L 1600 350" stroke="rgba(0,240,255,0.2)" stroke-width="1" stroke-dasharray="4 4" />
<!-- Flat Ground -->
<rect x="0" y="600" width="800" height="300" fill="url(#ground-grad)" />
<path d="M 0 600 L 800 600" stroke="#334155" stroke-width="4" />
<!-- Stair Obstacle -->
<rect x="800" y="350" width="800" height="550" fill="url(#ground-grad)" />
<rect x="800" y="350" width="800" height="550" fill="url(#stair-texture)" />
<path d="M 800 600 L 800 350 L 1600 350" stroke="#00f0ff" stroke-width="4" filter="url(#glow-cyan)" />
<!-- Stair Corner Highlight (Target) -->
<circle cx="800" cy="350" r="6" fill="#00f0ff" filter="url(#glow-cyan)" />
<circle cx="800" cy="350" r="12" fill="none" stroke="#00f0ff" stroke-width="1" class="pulse-ring" opacity="0.5"/>
</g>
<!-- Dynamic Vectors & Forces -->
<g id="force-vectors" opacity="0">
<!-- Resistance Force Arrow -->
<path d="M 880 500 L 810 500" stroke="url(#force-grad)" stroke-width="8" marker-end="url(#arrowhead)" />
<polygon points="800,500 815,492 815,508" fill="#ff2a5f" filter="url(#glow-magenta)" />
<text x="890" y="505" fill="#ff2a5f" font-family="'JetBrains Mono'" font-size="16" filter="url(#glow-magenta)">OBSTACLE RESISTANCE</text>
</g>
<!-- Main Wheel Assembly -->
<g id="wheel-assembly" transform="translate(300, 480)">
<!-- Outer Rim Blocks (Treads/Whegs) -->
<g id="rim-blocks"></g>
<!-- Linkages connecting Inner to Rim -->
<g id="linkages"></g>
<!-- Outer Hub (Housing) -->
<circle cx="0" cy="0" r="50" fill="#0f172a" stroke="#475569" stroke-width="3" />
<circle cx="0" cy="0" r="50" fill="none" stroke="#00f0ff" stroke-width="1" stroke-dasharray="2 6" opacity="0.5"/>
<!-- Inner Hub (Motor Driven) -->
<g id="inner-hub">
<circle cx="0" cy="0" r="30" fill="#1e293b" stroke="#00f0ff" stroke-width="2" />
<polygon points="0,-15 13,7.5 -13,7.5" fill="#00f0ff" opacity="0.3" /> <!-- Direction Indicator -->
<circle cx="0" cy="0" r="8" fill="#00f0ff" filter="url(#glow-cyan)" />
</g>
<!-- Spring Pin Mechanism (Torque Sensor) -->
<g id="locking-pin" transform="translate(0, -40)">
<rect x="-6" y="-10" width="12" height="20" fill="#334155" rx="2" />
<path id="pin-core" d="M -3 -8 L 3 -8 L 3 8 L -3 8 Z" fill="#00ff66" filter="url(#glow-green)" />
</g>
<!-- Measurement Overlays (Dynamic) -->
<g id="wheel-measurements" opacity="0">
<path id="radius-line" d="M 0 0 L 0 -120" stroke="#00f0ff" stroke-width="1" stroke-dasharray="4 4" />
<path id="extension-line" d="M 0 -120 L 0 -160" stroke="#00ff66" stroke-width="2" />
<text x="10" y="-140" fill="#00ff66" font-family="'JetBrains Mono'" font-size="14" font-weight="bold">+40mm EXTENSION</text>
</g>
</g>
</svg>
<!-- HUD Elements -->
<div class="hud-panel hud-top-left">
<div class="hud-title">System Status</div>
<div class="hud-value" id="hud-mode">WHEEL MODE</div>
<div class="hud-label" id="hud-desc">High-speed flat rolling. High efficiency.</div>
</div>
<div class="hud-panel hud-bottom-left">
<div class="hud-title">Drive Torque</div>
<div class="hud-value" id="hud-torque">2.1<span class="hud-unit">N·m</span></div>
<div style="width: 200px; height: 4px; background: #1e293b; margin-top: 10px; position: relative;">
<div id="torque-bar" style="width: 25%; height: 100%; background: var(--primary-cyan); transition: width 0.1s, background 0.3s;"></div>
<div style="position: absolute; left: 80%; top: -8px; width: 2px; height: 20px; background: var(--accent-magenta);"></div>
<div style="position: absolute; left: 80%; top: 15px; font-family: 'JetBrains Mono'; font-size: 10px; color: var(--accent-magenta);">8 N·m THRESHOLD</div>
</div>
</div>
<div class="hud-panel hud-bottom-right">
<div class="hud-title">Traction Extension</div>
<div class="hud-value" id="hud-extension">+0<span class="hud-unit">mm</span></div>
<div class="hud-label">Adaptive Wheg Radius</div>
</div>
<div class="triz-box">
<div class="triz-header">TRIZ IFR ACTIVE</div>
<div class="triz-desc">
<span class="highlight-text">Ideal Final Result:</span> The system adapts itself without complex external controls.<br><br>
By utilizing the <span class="highlight-text">obstacle's resistance</span> as the trigger energy, the wheel instantly transforms into legged mode (Whegs) only when required, eliminating the contradiction between flat-ground efficiency and obstacle-climbing capability.
</div>
</div>
<div id="fade" class="fade-overlay"></div>
</div>
<script>
document.addEventListener("DOMContentLoaded", () => {
// --- SVG Elements & Constants ---
const rimGroup = document.getElementById('rim-blocks');
const linkGroup = document.getElementById('linkages');
const wheelAssembly = document.getElementById('wheel-assembly');
const innerHub = document.getElementById('inner-hub');
const pinCore = document.getElementById('pin-core');
const forceVectors = document.getElementById('force-vectors');
const wheelMeasurements = document.getElementById('wheel-measurements');
// HUD Elements
const hudMode = document.getElementById('hud-mode');
const hudDesc = document.getElementById('hud-desc');
const hudTorque = document.getElementById('hud-torque');
const torqueBar = document.getElementById('torque-bar');
const hudExtension = document.getElementById('hud-extension');
const fadeLayer = document.getElementById('fade');
const NUM_SEGMENTS = 6;
const BASE_RADIUS = 120; // Closed wheel radius
const MAX_EXTENSION = 40; // +40mm requirement
const INNER_HUB_RADIUS = 25;
const LINK_ANCHOR_RADIUS = 85; // Point on rim block where linkage attaches
// --- Geometry Generation ---
// Create the 6 Rim Blocks (Whegs) and Linkages
const segments = [];
for(let i=0; i<NUM_SEGMENTS; i++) {
const angleDeg = i * (360 / NUM_SEGMENTS);
// 1. Create Rim Block Path
const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
// Draw a thick structural arc that tapers into a hook
// Center is (0,0), drawing pointing "up" (angle 0), then we rotate the group
const arcSpan = 52; // degrees
const halfSpan = arcSpan / 2;
const rOuter = BASE_RADIUS;
const rInner = BASE_RADIUS - 18;
// Math for arc coordinates
const rad = Math.PI / 180;
const x1o = Math.sin(-halfSpan * rad) * rOuter; const y1o = -Math.cos(-halfSpan * rad) * rOuter;
const x2o = Math.sin(halfSpan * rad) * rOuter; const y2o = -Math.cos(halfSpan * rad) * rOuter;
const x1i = Math.sin(-halfSpan * rad) * rInner; const y1i = -Math.cos(-halfSpan * rad) * rInner;
const x2i = Math.sin(halfSpan * rad) * rInner; const y2i = -Math.cos(halfSpan * rad) * rInner;
// Shape: Arc, then angle down to form hook tip, back inner arc, then close
const d = `
M ${x1o} ${y1o}
A ${rOuter} ${rOuter} 0 0 1 ${x2o} ${y2o}
L ${x2o + 15} ${y2o + 10}
L ${x2i + 5} ${y2i + 5}
A ${rInner} ${rInner} 0 0 0 ${x1i} ${y1i}
Z`;
path.setAttribute("d", d);
path.setAttribute("fill", "#1e293b");
path.setAttribute("stroke", "#00f0ff");
path.setAttribute("stroke-width", "2");
const rimG = document.createElementNS("http://www.w3.org/2000/svg", "g");
rimG.appendChild(path);
// Add pivot point visual on rim
const rimPivot = document.createElementNS("http://www.w3.org/2000/svg", "circle");
rimPivot.setAttribute("cx", "0");
rimPivot.setAttribute("cy", `-${LINK_ANCHOR_RADIUS}`);
rimPivot.setAttribute("r", "4");
rimPivot.setAttribute("fill", "#fff");
rimG.appendChild(rimPivot);
rimGroup.appendChild(rimG);
// 2. Create Linkage Line
const link = document.createElementNS("http://www.w3.org/2000/svg", "line");
link.setAttribute("stroke", "#94a3b8");
link.setAttribute("stroke-width", "4");
link.setAttribute("stroke-linecap", "round");
linkGroup.appendChild(link);
segments.push({
rimGroup: rimG,
linkLine: link,
baseAngle: angleDeg
});
}
// --- Kinematics Engine ---
// Calculates positions based on deployment progress (0 to 1)
function updateKinematics(deployProgress) {
// Inner hub rotates relative to outer base to push linkages
const relativeAngle = deployProgress * 45; // max 45 deg relative rotation
innerHub.setAttribute("transform", `rotate(${relativeAngle})`);
// Calculate leg extension and local rotation (to expose the hook)
const currentExtension = deployProgress * MAX_EXTENSION;
const currentRadiusOffset = currentExtension;
const hookPronouncementAngle = deployProgress * 25; // Rotate leg to point hook down
segments.forEach((seg, i) => {
// 1. Position Rim Block
// It moves outward radially, and rotates locally to expose the hook
const totalAngle = seg.baseAngle;
// Translate out, then rotate locally, then apply base angle rotation
// Note: SVG transforms are applied right-to-left.
seg.rimGroup.setAttribute("transform", `
rotate(${totalAngle})
translate(0, -${currentRadiusOffset})
rotate(${hookPronouncementAngle})
`);
// 2. Position Linkage
// Pivot A: on the inner hub, rotated by relativeAngle
const rad = Math.PI / 180;
const angleA = (seg.baseAngle + relativeAngle) * rad;
const ax = Math.sin(angleA) * INNER_HUB_RADIUS;
const ay = -Math.cos(angleA) * INNER_HUB_RADIUS;
// Pivot B: on the rim block.
// We must calculate its absolute position taking into account the rim's local translation and rotation
const angleB_base = seg.baseAngle * rad;
// Position of rim origin before local rotation
const rx = Math.sin(angleB_base) * currentRadiusOffset;
const ry = -Math.cos(angleB_base) * currentRadiusOffset;
// The pivot B is at (0, -LINK_ANCHOR_RADIUS) in the rim's local un-rotated space.
// We apply the local rotation and translation.
const localPivotDist = LINK_ANCHOR_RADIUS;
const totalAngleB = (seg.baseAngle + hookPronouncementAngle) * rad;
// Center of the rim group after translation
const centerRimX = Math.sin(angleB_base) * currentRadiusOffset;
const centerRimY = -Math.cos(angleB_base) * currentRadiusOffset;
// Absolute position of B
const bx = centerRimX + Math.sin(totalAngleB) * localPivotDist;
const by = centerRimY - Math.cos(totalAngleB) * localPivotDist;
seg.linkLine.setAttribute("x1", ax);
seg.linkLine.setAttribute("y1", ay);
seg.linkLine.setAttribute("x2", bx);
seg.linkLine.setAttribute("y2", by);
});
// Update measurements overlay
const measureLine = document.getElementById('extension-line');
measureLine.setAttribute("d", `M 0 -120 L 0 -${120 + currentExtension}`);
if(deployProgress > 0) {
wheelMeasurements.setAttribute("opacity", deployProgress);
} else {
wheelMeasurements.setAttribute("opacity", 0);
}
}
// --- Animation State Machine ---
let startTime = null;
const CYCLE_DURATION = 9000; // Total loop time in ms
function easeInOutCubic(x) {
return x < 0.5 ? 4 * x * x * x : 1 - Math.pow(-2 * x + 2, 3) / 2;
}
function easeOutBack(x) {
const c1 = 1.70158;
const c3 = c1 + 1;
return 1 + c3 * Math.pow(x - 1, 3) + c1 * Math.pow(x - 1, 2);
}
function updateFrame(timestamp) {
if (!startTime) startTime = timestamp;
let elapsed = (timestamp - startTime) % CYCLE_DURATION;
// Animation State Variables
let wheelX = 0;
let wheelY = 480; // Flat ground Y = 600 - base_radius(120)
let wheelRot = 0;
let deployLevel = 0;
// HUD Variables
let torque = 2.1;
let mode = "WHEEL MODE";
let modeColor = "var(--primary-cyan)";
let isAlert = false;
let isSuccess = false;
// Phase 1: Flat Rolling (0 - 2000ms)
if (elapsed < 2000) {
let p = elapsed / 2000;
wheelX = 100 + p * 580; // Move from 100 to 680 (hitting stair at 800 - 120 = 680)
wheelRot = p * 360 * 1.5; // Rotate 1.5 times
torque = 2.0 + Math.random() * 0.4; // Normal fluctuation
}
// Phase 2: Impact & Resistance Builds (2000 - 2400ms)
else if (elapsed < 2400) {
let p = (elapsed - 2000) / 400;
wheelX = 680;
wheelRot = 360 * 1.5 + p * 10; // Slight rotation as it pushes
// Torque spikes past 8Nm
torque = 2.4 + p * 7.5; // reaches ~9.9
isAlert = true;
if (p > 0.8) {
forceVectors.setAttribute("opacity", (p - 0.8) * 5); // Fade in vector
pinCore.setAttribute("fill", "#ff2a5f"); // Pin turns red (broken)
pinCore.setAttribute("filter", "url(#glow-magenta)");
} else {
forceVectors.setAttribute("opacity", 0);
pinCore.setAttribute("fill", "#00ff66");
pinCore.setAttribute("filter", "url(#glow-green)");
}
mode = "RESISTANCE DETECTED";
modeColor = "var(--accent-magenta)";
}
// Phase 3: Deployment (2400 - 3200ms)
else if (elapsed < 3200) {
let p = (elapsed - 2400) / 800;
let easedP = easeOutBack(Math.min(p * 1.2, 1)); // Snappy deploy
wheelX = 680;
wheelRot = 360 * 1.5 + 10 + p * 30; // Motor continues turning to drive deploy
deployLevel = easedP;
torque = 8.5 + Math.random(); // High torque maintained during deploy
forceVectors.setAttribute("opacity", 1 - p); // Fade out vector
pinCore.setAttribute("fill", "#ff2a5f");
mode = "WHEGS DEPLOYING";
modeColor = "var(--accent-magenta)";
isAlert = true;
}
// Phase 4: Climbing the Step (3200 - 5500ms)
else if (elapsed < 5500) {
let p = (elapsed - 3200) / 2300;
let easedP = easeInOutCubic(p);
// Kinematics: The wheel center pivots over the stair edge
// Start: X=680, Y=480. Target: X=850, Y=230 (350 stair height - 120 radius)
wheelX = 680 + easedP * 170;
wheelY = 480 - easedP * 250;
// Rotation continues as it climbs
wheelRot = 360 * 1.5 + 40 + p * 180;
deployLevel = 1;
torque = 6.0 - p * 3; // Torque drops as it crests
mode = "CLIMBING MODE (WHEGS)";
modeColor = "var(--accent-green)";
isSuccess = true;
pinCore.setAttribute("fill", "#334155"); // Pin idle
pinCore.setAttribute("filter", "none");
}
// Phase 5: Retraction (5500 - 6500ms)
else if (elapsed < 6500) {
let p = (elapsed - 5500) / 1000;
let easedP = easeInOutCubic(p);
wheelX = 850 + p * 150; // Move forward on top
wheelY = 230; // Top level Y
wheelRot = 360 * 1.5 + 220 + p * 90;
deployLevel = 1 - easedP; // Retract
torque = 3.0 - p * 0.9;
mode = "AUTO-RETRACTING";
modeColor = "var(--primary-cyan)";
pinCore.setAttribute("fill", "#00ff66"); // Pin locks back
pinCore.setAttribute("filter", "url(#glow-green)");
}
// Phase 6: Roll Away on top (6500 - 8000ms)
else if (elapsed < 8000) {
let p = (elapsed - 6500) / 1500;
wheelX = 1000 + p * 400; // Roll off screen
wheelY = 230;
wheelRot = 360 * 1.5 + 310 + p * 270;
deployLevel = 0;
torque = 2.0 + Math.random() * 0.4;
mode = "WHEEL MODE";
modeColor = "var(--primary-cyan)";
}
// Phase 7: Fade out and reset (8000 - 9000ms)
else {
let p = (elapsed - 8000) / 1000;
wheelX = 1400;
wheelY = 230;
deployLevel = 0;
torque = 0;
if (p < 0.5) {
fadeLayer.style.opacity = p * 2; // Fade to black
} else {
// Reset position behind black screen
wheelX = 100;
wheelY = 480;
wheelRot = 0;
fadeLayer.style.opacity = 2 - (p * 2); // Fade back in
}
mode = "SYSTEM RESET";
}
// Apply calculated transforms
wheelAssembly.setAttribute("transform", `translate(${wheelX}, ${wheelY}) rotate(${wheelRot})`);
updateKinematics(deployLevel);
// Update HUD DOM
hudTorque.innerHTML = `${torque.toFixed(1)}<span class="hud-unit">N·m</span>`;
hudTorque.className = `hud-value ${isAlert ? 'alert' : ''}`;
let torquePercent = Math.min((torque / 10) * 100, 100);
torqueBar.style.width = `${torquePercent}%`;
torqueBar.style.background = torque > 8 ? 'var(--accent-magenta)' : 'var(--primary-cyan)';
hudMode.textContent = mode;
hudMode.style.color = modeColor;
const extValue = (deployLevel * MAX_EXTENSION).toFixed(1);
hudExtension.innerHTML = `+${extValue}<span class="hud-unit">mm</span>`;
hudExtension.className = `hud-value ${deployLevel > 0.9 ? 'success' : ''}`;
if(deployLevel === 0) hudDesc.textContent = "High-speed flat rolling. High efficiency.";
else if(deployLevel > 0 && deployLevel < 1 && isAlert) hudDesc.textContent = "Torque > 8N·m. Mechanical lock disengaged.";
else if(deployLevel === 1) hudDesc.textContent = "Hooking mechanism active. Overcoming obstacle.";
requestAnimationFrame(updateFrame);
}
// Initialization
updateKinematics(0); // Set initial closed state
requestAnimationFrame(updateFrame); // Start Auto-Play Loop
});
</script>
</body>
</html>
积分规则:第一轮对话扣减8分,后续每轮扣6分
等待动画代码生成...
