chore(prototype): 添加 2D 线缆物理交互 HTML 原型
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 349229758cc74e1c8a52bebdf232de2a
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 2.8 KiB |
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d19f495b03da4884b9be4e6e602777b5
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,535 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<script src="./support.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<x-dc>
|
||||
<helmet>
|
||||
<style>
|
||||
html,body{margin:0;padding:0;height:100%;overflow:hidden;background:#F4F3F0}
|
||||
a{color:#3D63DD;text-decoration:none}a:hover{color:#2B4BB5}
|
||||
</style>
|
||||
</helmet>
|
||||
<div style="position:fixed;inset:0;background:#F4F3F0;font-family:system-ui,-apple-system,sans-serif;overflow:hidden">
|
||||
<canvas ref="{{ canvasRef }}" style="position:absolute;inset:0;width:100%;height:100%;touch-action:none;display:block"></canvas>
|
||||
<div style="position:absolute;top:22px;left:28px;right:28px;display:flex;align-items:center;justify-content:space-between;gap:16px;pointer-events:none">
|
||||
<div style="display:flex;flex-direction:column;gap:3px">
|
||||
<div style="font-size:15px;font-weight:650;color:#26262A;letter-spacing:-0.01em">线缆物理</div>
|
||||
<div style="font-size:11.5px;color:#9A978F;font-variant-numeric:tabular-nums">Verlet rope · 拉出 / 回收 / 插接</div>
|
||||
</div>
|
||||
<div style="display:flex;gap:4px;padding:4px;background:#E9E7E2;border-radius:10px;pointer-events:auto">
|
||||
<sc-for list="{{ materials }}" as="m" hint-placeholder-count="3">
|
||||
<button onClick="{{ m.onPick }}" style="border:none;cursor:pointer;padding:7px 14px;border-radius:7px;font-size:12.5px;font-weight:600;font-family:inherit;background:{{ m.bg }};color:{{ m.fg }};transition:background .15s,color .15s">{{ m.label }}</button>
|
||||
</sc-for>
|
||||
</div>
|
||||
</div>
|
||||
<div style="position:absolute;top:74px;right:28px;width:206px;padding:12px 14px;background:rgba(244,243,240,.92);border:1px solid rgba(38,38,42,.08);box-shadow:0 10px 28px rgba(40,38,32,.10);border-radius:8px;display:flex;flex-direction:column;gap:10px;pointer-events:auto">
|
||||
<div style="display:flex;align-items:center;justify-content:space-between;gap:10px;font-size:12px;font-weight:650;color:#26262A">
|
||||
<span>重力</span>
|
||||
<span style="font-variant-numeric:tabular-nums;color:#77746C">{{ gravityScaleText }}</span>
|
||||
</div>
|
||||
<input type="range" min="0.6" max="2.2" step="0.02" value="{{ gravityScale }}" onInput="{{ onGravityInput }}" style="width:100%;accent-color:#4B72E0">
|
||||
<div style="display:flex;align-items:center;justify-content:space-between;gap:10px;font-size:12px;font-weight:650;color:#26262A">
|
||||
<span>阻尼</span>
|
||||
<span style="font-variant-numeric:tabular-nums;color:#77746C">{{ motionDampingText }}</span>
|
||||
</div>
|
||||
<input type="range" min="0.86" max="0.99" step="0.005" value="{{ motionDamping }}" onInput="{{ onDampingInput }}" style="width:100%;accent-color:#4B72E0">
|
||||
</div>
|
||||
<div style="position:absolute;bottom:22px;left:0;right:0;display:flex;justify-content:center;pointer-events:none">
|
||||
<div style="padding:8px 16px;border-radius:99px;background:{{ statusBg }};color:{{ statusFg }};font-size:12.5px;font-weight:550;white-space:nowrap;flex:none;transition:background .25s,color .25s">{{ status }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-dc>
|
||||
<script type="text/x-dc" data-dc-script data-props="{"maxStretch":{"editor":"range","default":1.5,"min":1.5,"max":4,"step":0.1,"tsType":"number","section":"物理","description":"最大拉出长度(相对初始长度的倍数)"},"retractSpeed":{"editor":"range","default":0.09,"min":0.02,"max":0.3,"step":0.01,"tsType":"number","section":"物理","description":"松手后回收速度"},"gravityScale":{"editor":"range","default":1.28,"min":0.6,"max":2.2,"step":0.02,"tsType":"number","section":"重量","description":"线缆和插头的重力倍率"},"motionDamping":{"editor":"range","default":0.94,"min":0.86,"max":0.99,"step":0.005,"tsType":"number","section":"重量","description":"数值越低越沉,越不飘"}}">
|
||||
class Component extends DCLogic {
|
||||
state = { material: 'rubber', plugged: false, dragging: false, gravityScale: 1.28, motionDamping: 0.94 };
|
||||
|
||||
presets = {
|
||||
rubber: { label: '橡胶粗缆', g: 2600, damp: 0.960, w: 17, iter: 30, smooth: 0.34, Lmin: 92, retract: 0.085,
|
||||
body: '#4B72E0', dark: '#2F4CA8', hi: 'rgba(255,255,255,.38)', kind: 'rubber' },
|
||||
braided: { label: '编织线', g: 2000, damp: 0.972, w: 12, iter: 24, smooth: 0.20, Lmin: 92, retract: 0.10,
|
||||
body: '#5C6070', dark: '#3B3E49', hi: '#A9AFBE', kind: 'braid' },
|
||||
pixel: { label: '像素风', g: 1900, damp: 0.974, w: 15, iter: 24, smooth: 0.22, Lmin: 92, retract: 0.11,
|
||||
body: '#3D5BD9', dark: '#222E66', mid: '#6E8CF5', hi: '#B9C8FF', kind: 'pixel' }
|
||||
};
|
||||
|
||||
N = 42; plugLen = 58; dockOff = 41; exitGuardLen = 12; extraFeedLen = 300;
|
||||
|
||||
componentDidMount() {
|
||||
this.canvas = this.canvasRef.current;
|
||||
this.ctx = this.canvas.getContext('2d');
|
||||
this.fit = () => {
|
||||
const dpr = window.devicePixelRatio || 1;
|
||||
this.W = this.canvas.clientWidth; this.H = this.canvas.clientHeight;
|
||||
this.canvas.width = this.W * dpr; this.canvas.height = this.H * dpr;
|
||||
this.ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
||||
this.anchor = { x: this.W * 0.5, y: Math.min(218, this.H * 0.32) };
|
||||
this.LminScale = Math.min(1, Math.max(0.4, (this.H - this.anchor.y - 90) / 300));
|
||||
this.hole = { x: this.W - 86, y: Math.max(260, this.H * 0.44) };
|
||||
};
|
||||
this.fit();
|
||||
window.addEventListener('resize', this.fit);
|
||||
// init rope hanging down
|
||||
const P = [];
|
||||
for (let i = 0; i < this.N; i++) {
|
||||
const t = i / (this.N - 1);
|
||||
P.push({ x: this.anchor.x + t * 2, y: this.anchor.y + t * this.presets.rubber.Lmin, px: 0, py: 0 });
|
||||
P[i].px = P[i].x; P[i].py = P[i].y;
|
||||
}
|
||||
this.pts = P;
|
||||
this.L = this.presets.rubber.Lmin;
|
||||
this.pointer = null; this.reelSpin = 0; this.plugAng = Math.PI / 2; this.plugDir = { x: 0, y: 1 };
|
||||
const c = this.canvas;
|
||||
c.addEventListener('pointerdown', this.onDown);
|
||||
c.addEventListener('pointermove', this.onMove);
|
||||
c.addEventListener('pointerup', this.onUp);
|
||||
c.addEventListener('pointercancel', this.onUp);
|
||||
this.raf = requestAnimationFrame(this.loop);
|
||||
}
|
||||
componentWillUnmount() {
|
||||
cancelAnimationFrame(this.raf);
|
||||
window.removeEventListener('resize', this.fit);
|
||||
}
|
||||
|
||||
pos(e) { const r = this.canvas.getBoundingClientRect(); return { x: e.clientX - r.left, y: e.clientY - r.top }; }
|
||||
plugCenter() {
|
||||
const P = this.pts, a = P[this.N - 1];
|
||||
const dir = this.plugDir || { x: Math.cos(this.plugAng ?? Math.PI / 2), y: Math.sin(this.plugAng ?? Math.PI / 2) };
|
||||
const ux = dir.x, uy = dir.y;
|
||||
return { x: a.x + ux * this.plugLen * 0.5, y: a.y + uy * this.plugLen * 0.5, ux, uy };
|
||||
}
|
||||
onDown = (e) => {
|
||||
const p = this.pos(e), pc = this.plugCenter();
|
||||
if (Math.hypot(p.x - pc.x, p.y - pc.y) < 52) {
|
||||
this.canvas.setPointerCapture(e.pointerId);
|
||||
this.pointer = p; this.dragT = { x: this.pts[this.N - 1].x, y: this.pts[this.N - 1].y };
|
||||
this.setState({ dragging: true });
|
||||
}
|
||||
};
|
||||
onMove = (e) => {
|
||||
const p = this.pos(e);
|
||||
if (this.state.dragging) { this.pointer = p; }
|
||||
else {
|
||||
const pc = this.plugCenter();
|
||||
this.canvas.style.cursor = Math.hypot(p.x - pc.x, p.y - pc.y) < 52 ? 'grab' : 'default';
|
||||
}
|
||||
};
|
||||
onUp = () => {
|
||||
if (!this.state.dragging) return;
|
||||
const end = this.pts[this.N - 1];
|
||||
const dock = { x: this.hole.x - this.dockOff, y: this.hole.y };
|
||||
const near = Math.hypot(end.x - dock.x, end.y - dock.y) < 52;
|
||||
this.pointer = null;
|
||||
this.setState({ dragging: false, plugged: near });
|
||||
};
|
||||
|
||||
loop = () => {
|
||||
this.raf = requestAnimationFrame(this.loop);
|
||||
const m = this.presets[this.state.material];
|
||||
const gravityScale = this.state.gravityScale ?? this.props.gravityScale ?? 1.28;
|
||||
const motionDamping = this.state.motionDamping ?? this.props.motionDamping ?? 0.94;
|
||||
const Lmin = m.Lmin * (this.LminScale || 1);
|
||||
const Lmax = Math.max(Lmin * (this.props.maxStretch ?? 2.8), Lmin + this.extraFeedLen * (this.LminScale || 1));
|
||||
const retract = this.props.retractSpeed ?? m.retract;
|
||||
const P = this.pts, N = this.N, dt = 1 / 60;
|
||||
const dock = { x: this.hole.x - this.dockOff, y: this.hole.y };
|
||||
|
||||
// --- drag target (heavy lerp) + unplug check
|
||||
if (this.state.dragging && this.pointer) {
|
||||
this.dragT.x += (this.pointer.x - this.dragT.x) * 0.38;
|
||||
this.dragT.y += (this.pointer.y - this.dragT.y) * 0.38;
|
||||
if (this.dragT.y > this.H - 18) this.dragT.y = this.H - 18;
|
||||
// clamp to CURRENT rope length — the pin can never overstretch the rope (kills taut-drag jitter);
|
||||
// length feeds out below, and the target follows it
|
||||
const dx = this.dragT.x - this.anchor.x, dy = this.dragT.y - this.anchor.y;
|
||||
const d = Math.hypot(dx, dy), maxR = Math.min(Lmax, this.L) * 0.99;
|
||||
if (d > maxR) { this.dragT.x = this.anchor.x + dx / d * maxR; this.dragT.y = this.anchor.y + dy / d * maxR; }
|
||||
if (this.state.plugged && Math.hypot(this.pointer.x - dock.x, this.pointer.y - dock.y) > 74) this.setState({ plugged: false });
|
||||
}
|
||||
|
||||
// --- length feed / retract (driven by raw pointer, not the clamped target)
|
||||
const endT = this.state.dragging && this.pointer ? this.pointer : (this.state.plugged ? dock : P[N - 1]);
|
||||
const needed = Math.hypot(endT.x - this.anchor.x, endT.y - this.anchor.y) * 1.04 + 20;
|
||||
let Lt;
|
||||
if (this.state.dragging || this.state.plugged) Lt = Math.min(Math.max(needed, Lmin), Lmax);
|
||||
else Lt = Lmin;
|
||||
const rate = Lt > this.L ? 0.45 : (this.state.dragging || this.state.plugged ? 0.06 : retract);
|
||||
const prevL = this.L;
|
||||
this.L += (Lt - this.L) * rate;
|
||||
this.reelSpin += (this.L - prevL) * 0.016;
|
||||
|
||||
// --- verlet integrate
|
||||
const seg = this.L / (N - 1);
|
||||
const exitGuardSegments = this.getExitGuardSegments(seg);
|
||||
const pinnedEnd = this.state.dragging || this.state.plugged;
|
||||
for (let i = exitGuardSegments + 1; i < N; i++) {
|
||||
const p = P[i];
|
||||
const damp = Math.min(m.damp, motionDamping, (!pinnedEnd && i >= N - 3) ? 0.84 : 1);
|
||||
const vx = (p.x - p.px) * damp, vy = (p.y - p.py) * damp;
|
||||
p.px = p.x; p.py = p.y;
|
||||
p.x += vx; p.y += vy + m.g * gravityScale * dt * dt;
|
||||
}
|
||||
this.applyExitGuard(seg, exitGuardSegments);
|
||||
if (this.state.dragging) { P[N - 1].x = this.dragT.x; P[N - 1].y = this.dragT.y; }
|
||||
else if (this.state.plugged) {
|
||||
P[N - 1].x += (dock.x - P[N - 1].x) * 0.5; P[N - 1].y += (dock.y - P[N - 1].y) * 0.5;
|
||||
}
|
||||
|
||||
// --- constraints
|
||||
for (let k = 0; k < m.iter; k++) {
|
||||
for (let i = 0; i < N - 1; i++) {
|
||||
const a = P[i], b = P[i + 1];
|
||||
const dx = b.x - a.x, dy = b.y - a.y;
|
||||
const d = Math.hypot(dx, dy) || 1e-6;
|
||||
const diff = (d - seg) / d;
|
||||
const pinA = i <= exitGuardSegments;
|
||||
const pinB = (i + 1 <= exitGuardSegments) || ((i === N - 2) && pinnedEnd);
|
||||
const wa = pinA ? 0 : (pinB ? 1 : 0.5), wb = pinB ? 0 : (pinA ? 1 : 0.5);
|
||||
a.x += dx * diff * wa; a.y += dy * diff * wa;
|
||||
b.x -= dx * diff * wb; b.y -= dy * diff * wb;
|
||||
}
|
||||
if (this.state.plugged) {
|
||||
// orient plug horizontally into socket
|
||||
const e2 = P[N - 2];
|
||||
e2.x += (dock.x - seg - e2.x) * 0.4; e2.y += (dock.y - e2.y) * 0.4;
|
||||
}
|
||||
// bending stiffness
|
||||
if (m.smooth > 0 && k % 3 === 0) {
|
||||
for (let i = exitGuardSegments + 1; i < N - 1; i++) {
|
||||
if (i === N - 2 && pinnedEnd) continue;
|
||||
const p = P[i];
|
||||
const mx = (P[i - 1].x + P[i + 1].x) / 2, my = (P[i - 1].y + P[i + 1].y) / 2;
|
||||
p.x += (mx - p.x) * m.smooth * 0.5; p.y += (my - p.y) * m.smooth * 0.5;
|
||||
}
|
||||
}
|
||||
this.stabilizePlugEnd(seg, pinnedEnd);
|
||||
this.applyExitGuard(seg, exitGuardSegments);
|
||||
}
|
||||
// floor (pinned dragged end is exempt — no pin-vs-floor fight)
|
||||
const floor = this.H - 14;
|
||||
for (let i = exitGuardSegments + 1; i < N; i++) {
|
||||
if (i === N - 1 && this.state.dragging) continue;
|
||||
if (P[i].y > floor) {
|
||||
P[i].y = floor;
|
||||
P[i].py = P[i].y;
|
||||
P[i].px += (P[i].x - P[i].px) * 0.45;
|
||||
}
|
||||
}
|
||||
|
||||
this.draw(m, Lmin, Lmax);
|
||||
};
|
||||
|
||||
getExitGuardSegments(seg) {
|
||||
return Math.max(1, Math.min(3, Math.round(this.exitGuardLen / Math.max(1, seg))));
|
||||
}
|
||||
|
||||
applyExitGuard(seg, guardSegments) {
|
||||
const P = this.pts, a = this.anchor;
|
||||
for (let i = 0; i <= guardSegments && i < this.N; i++) {
|
||||
const p = P[i];
|
||||
p.x = a.x;
|
||||
p.y = a.y + seg * i;
|
||||
p.px = p.x;
|
||||
p.py = p.y;
|
||||
}
|
||||
}
|
||||
|
||||
stabilizePlugEnd(seg, pinnedEnd) {
|
||||
if (pinnedEnd || this.N < 6) return;
|
||||
const P = this.pts, N = this.N;
|
||||
const tail = P[N - 1], neck = P[N - 2], back = P[N - 5];
|
||||
const dx = neck.x - back.x, dy = neck.y - back.y;
|
||||
const d = Math.hypot(dx, dy) || 1;
|
||||
const tx = dx / d, ty = dy / d;
|
||||
const desiredX = neck.x + tx * seg;
|
||||
const desiredY = neck.y + ty * seg;
|
||||
const pull = 0.025;
|
||||
const ox = (desiredX - tail.x) * pull;
|
||||
const oy = (desiredY - tail.y) * pull;
|
||||
tail.x += ox; tail.y += oy;
|
||||
tail.px += ox; tail.py += oy;
|
||||
}
|
||||
|
||||
drawCablePath(path, m, shadow, cap = 'round') {
|
||||
const ctx = this.ctx;
|
||||
ctx.lineCap = cap; ctx.lineJoin = 'round';
|
||||
if (shadow !== false) {
|
||||
ctx.save(); ctx.translate(0, 7);
|
||||
ctx.strokeStyle = 'rgba(40,38,32,.10)'; ctx.lineWidth = m.w + 2; ctx.stroke(path); ctx.restore();
|
||||
}
|
||||
if (m.kind === 'pixel') {
|
||||
// flat stepped bands — no gradients, hard edges
|
||||
ctx.strokeStyle = m.dark; ctx.lineWidth = m.w + 4; ctx.stroke(path);
|
||||
ctx.strokeStyle = m.body; ctx.lineWidth = m.w; ctx.stroke(path);
|
||||
ctx.save(); ctx.translate(0, -m.w * 0.14);
|
||||
ctx.strokeStyle = m.mid; ctx.lineWidth = m.w * 0.55; ctx.stroke(path); ctx.restore();
|
||||
ctx.save(); ctx.translate(0, -m.w * 0.3);
|
||||
ctx.strokeStyle = m.hi; ctx.lineWidth = m.w * 0.22; ctx.stroke(path); ctx.restore();
|
||||
} else if (m.kind === 'braid') {
|
||||
ctx.strokeStyle = m.dark; ctx.lineWidth = m.w + 2.5; ctx.stroke(path);
|
||||
ctx.strokeStyle = m.body; ctx.lineWidth = m.w; ctx.stroke(path);
|
||||
ctx.save();
|
||||
ctx.strokeStyle = m.hi; ctx.lineWidth = m.w * 0.62;
|
||||
ctx.setLineDash([m.w * 0.9, m.w * 1.1]); ctx.stroke(path);
|
||||
ctx.strokeStyle = 'rgba(0,0,0,.22)'; ctx.lineWidth = m.w * 0.62;
|
||||
ctx.setLineDash([m.w * 0.9, m.w * 1.1]); ctx.lineDashOffset = m.w; ctx.stroke(path);
|
||||
ctx.restore();
|
||||
} else {
|
||||
ctx.strokeStyle = m.dark; ctx.lineWidth = m.w + 2.5; ctx.stroke(path);
|
||||
ctx.strokeStyle = m.body; ctx.lineWidth = m.w; ctx.stroke(path);
|
||||
ctx.save(); ctx.translate(0, -m.w * 0.22);
|
||||
ctx.strokeStyle = m.hi; ctx.lineWidth = m.w * 0.3; ctx.stroke(path); ctx.restore();
|
||||
}
|
||||
}
|
||||
|
||||
draw(m, Lmin, Lmax) {
|
||||
this.clearCanvas();
|
||||
this.drawReel(m, Lmin, Lmax);
|
||||
this.drawExitStub(m);
|
||||
if (!this.state.plugged) this.drawSocket(m);
|
||||
this.drawMainCable(m);
|
||||
this.drawFrontCableOverlay(m);
|
||||
this.drawCablePlug(m);
|
||||
if (this.state.plugged) this.drawSocket(m);
|
||||
}
|
||||
|
||||
clearCanvas() {
|
||||
this.ctx.clearRect(0, 0, this.W, this.H);
|
||||
}
|
||||
|
||||
drawReel(m, Lmin, Lmax) {
|
||||
const ctx = this.ctx, a = this.anchor;
|
||||
const reelY = a.y - 128;
|
||||
const frac = (this.L - Lmin) / (Lmax - Lmin);
|
||||
|
||||
ctx.fillStyle = '#E8E6E1';
|
||||
ctx.beginPath(); ctx.arc(a.x, reelY, 108, 0, 7); ctx.fill();
|
||||
ctx.strokeStyle = '#DBD8D2'; ctx.lineWidth = 1.5;
|
||||
ctx.beginPath(); ctx.arc(a.x, reelY, 74, 0, 7); ctx.stroke();
|
||||
|
||||
this.drawReelSpinMarker(reelY);
|
||||
|
||||
ctx.strokeStyle = m.body; ctx.lineWidth = 5; ctx.lineCap = 'round';
|
||||
ctx.beginPath();
|
||||
ctx.arc(a.x, reelY, 92, -Math.PI / 2, -Math.PI / 2 + Math.max(0.001, frac) * Math.PI * 2);
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
drawReelSpinMarker(reelY) {
|
||||
const ctx = this.ctx, a = this.anchor;
|
||||
ctx.save(); ctx.translate(a.x, reelY); ctx.rotate(this.reelSpin);
|
||||
ctx.fillStyle = '#CFCCC5';
|
||||
for (let i = 0; i < 3; i++) {
|
||||
ctx.rotate(Math.PI * 2 / 3);
|
||||
ctx.beginPath(); ctx.arc(74, 0, 5, 0, 7); ctx.fill();
|
||||
}
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
drawExitStub(m) {
|
||||
const ctx = this.ctx, a = this.anchor;
|
||||
ctx.fillStyle = '#E8E6E1';
|
||||
ctx.beginPath(); ctx.roundRect(a.x - 20, a.y - 46, 40, 44, 8); ctx.fill();
|
||||
ctx.fillStyle = 'rgba(40,38,32,.28)';
|
||||
ctx.beginPath(); ctx.ellipse(a.x, a.y - 4, Math.min(16, m.w * 0.5 + 5), 4.5, 0, 0, 7); ctx.fill();
|
||||
}
|
||||
|
||||
drawMainCable(m) {
|
||||
const ctx = this.ctx;
|
||||
ctx.save();
|
||||
ctx.clip(this.buildExitStubMask(), 'evenodd');
|
||||
this.drawCablePath(this.buildMainCablePath(), m);
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
buildMainCablePath() {
|
||||
const a = this.anchor, P = this.pts, path = new Path2D();
|
||||
path.moveTo(a.x, a.y - 5);
|
||||
path.lineTo(P[0].x, P[0].y);
|
||||
this.appendCableCurve(path, 1, this.N - 1);
|
||||
return path;
|
||||
}
|
||||
|
||||
buildExitStubMask() {
|
||||
const a = this.anchor, clip = new Path2D();
|
||||
clip.rect(0, 0, this.W, this.H);
|
||||
clip.roundRect(a.x - 20, a.y - 46, 40, 44, 8);
|
||||
return clip;
|
||||
}
|
||||
|
||||
drawFrontCableOverlay(m) {
|
||||
const frontStartIndex = this.findFrontCableStartIndex();
|
||||
if (frontStartIndex >= this.N - 1) return;
|
||||
this.drawCablePath(this.buildFrontCablePath(frontStartIndex), m, false, 'butt');
|
||||
}
|
||||
|
||||
buildFrontCablePath(startIndex) {
|
||||
const P = this.pts, N = this.N;
|
||||
const path = new Path2D();
|
||||
path.moveTo(P[startIndex].x, P[startIndex].y);
|
||||
this.appendCableCurve(path, startIndex + 1, N - 1);
|
||||
return path;
|
||||
}
|
||||
|
||||
findFrontCableStartIndex() {
|
||||
const P = this.pts, N = this.N;
|
||||
let bottomIndex = 1;
|
||||
for (let i = 2; i < N - 1; i++) {
|
||||
if (P[i].y > P[bottomIndex].y) bottomIndex = i;
|
||||
}
|
||||
return bottomIndex;
|
||||
}
|
||||
|
||||
appendCableCurve(path, controlStart, endIndex) {
|
||||
const P = this.pts;
|
||||
for (let i = controlStart; i < endIndex; i++) {
|
||||
const mx = (P[i].x + P[i + 1].x) / 2, my = (P[i].y + P[i + 1].y) / 2;
|
||||
path.quadraticCurveTo(P[i].x, P[i].y, mx, my);
|
||||
}
|
||||
path.lineTo(P[endIndex].x, P[endIndex].y);
|
||||
}
|
||||
|
||||
drawCablePlug(m) {
|
||||
const P = this.pts, e = P[this.N - 1];
|
||||
this.updatePlugAngle();
|
||||
this.drawPlug(e.x, e.y, this.plugAng, m);
|
||||
}
|
||||
|
||||
updatePlugAngle() {
|
||||
const dir = this.getPlugTargetDirection();
|
||||
let ta = Math.atan2(dir.y, dir.x);
|
||||
const da = Math.atan2(Math.sin(ta - this.plugAng), Math.cos(ta - this.plugAng));
|
||||
if (!this.state.plugged && Math.abs(da) < 0.006) return;
|
||||
let step = da * (this.state.plugged ? 0.4 : (this.state.dragging ? 0.18 : 0.12));
|
||||
const MAXW = this.state.plugged ? 0.11 : (this.state.dragging ? 0.075 : 0.045);
|
||||
if (step > MAXW) step = MAXW; else if (step < -MAXW) step = -MAXW;
|
||||
this.plugAng += step;
|
||||
this.plugDir.x = Math.cos(this.plugAng);
|
||||
this.plugDir.y = Math.sin(this.plugAng);
|
||||
}
|
||||
|
||||
getPlugTargetDirection() {
|
||||
if (this.state.plugged) return { x: 1, y: 0 };
|
||||
|
||||
const P = this.pts, N = this.N, e = P[N - 1];
|
||||
const prev = this.plugDir || { x: Math.cos(this.plugAng), y: Math.sin(this.plugAng) };
|
||||
const seg = Math.max(1, this.L / Math.max(1, N - 1));
|
||||
let sx = 0, sy = 0, total = 0;
|
||||
|
||||
for (let i = N - 2; i >= Math.max(0, N - 8); i--) {
|
||||
const dx = e.x - P[i].x, dy = e.y - P[i].y;
|
||||
const d = Math.hypot(dx, dy);
|
||||
if (d < seg * 0.35) continue;
|
||||
const w = Math.min(1.75, d / seg);
|
||||
sx += dx / d * w;
|
||||
sy += dy / d * w;
|
||||
total += w;
|
||||
}
|
||||
|
||||
const span = P[Math.max(0, N - 7)];
|
||||
const spanLen = Math.hypot(e.x - span.x, e.y - span.y);
|
||||
if (total <= 0.001 || spanLen < seg * 1.2) return prev;
|
||||
|
||||
let dx = sx / total, dy = sy / total;
|
||||
const d = Math.hypot(dx, dy) || 1;
|
||||
dx /= d; dy /= d;
|
||||
|
||||
const dot = dx * prev.x + dy * prev.y;
|
||||
if (dot < -0.2 && spanLen < seg * 2.8) return prev;
|
||||
return { x: dx, y: dy };
|
||||
}
|
||||
|
||||
drawSocket(m) {
|
||||
const ctx = this.ctx, P = this.pts, N = this.N, h = this.hole;
|
||||
const e = P[N - 1];
|
||||
const nearPlug = Math.hypot(e.x - (h.x - this.dockOff), e.y - h.y);
|
||||
ctx.fillStyle = '#E8E6E1';
|
||||
ctx.beginPath(); ctx.roundRect(h.x - 30, h.y - 74, 110, 148, 14); ctx.fill();
|
||||
ctx.fillStyle = '#2B2B2E';
|
||||
ctx.beginPath(); ctx.arc(h.x, h.y, 19, 0, 7); ctx.fill();
|
||||
ctx.fillStyle = '#0E0E10';
|
||||
ctx.beginPath(); ctx.arc(h.x, h.y, 10, 0, 7); ctx.fill();
|
||||
if (!this.state.plugged && this.state.dragging && nearPlug < 110) {
|
||||
ctx.strokeStyle = m.body; ctx.lineWidth = 3; ctx.globalAlpha = 0.4 + 0.5 * (1 - Math.min(1, nearPlug / 110));
|
||||
ctx.beginPath(); ctx.arc(h.x, h.y, 30, 0, 7); ctx.stroke(); ctx.globalAlpha = 1;
|
||||
}
|
||||
ctx.fillStyle = this.state.plugged ? '#3FA96B' : '#C9C6BF';
|
||||
ctx.beginPath(); ctx.arc(h.x + 26, h.y - 52, 4.5, 0, 7); ctx.fill();
|
||||
}
|
||||
|
||||
drawPlug(x, y, ang, m) {
|
||||
const ctx = this.ctx;
|
||||
ctx.save(); ctx.translate(x, y); ctx.rotate(ang);
|
||||
ctx.lineCap = 'butt';
|
||||
// strain relief (cable-colored taper)
|
||||
ctx.fillStyle = m.dark;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(-14, -m.w * 0.5 - 1); ctx.lineTo(6, -m.w * 0.72 - 2);
|
||||
ctx.lineTo(6, m.w * 0.72 + 2); ctx.lineTo(-14, m.w * 0.5 + 1); ctx.closePath(); ctx.fill();
|
||||
// barrel
|
||||
const g = ctx.createLinearGradient(0, -13, 0, 13);
|
||||
g.addColorStop(0, '#B9BCC2'); g.addColorStop(0.28, '#F2F3F5'); g.addColorStop(0.55, '#8E9298'); g.addColorStop(1, '#5A5D63');
|
||||
ctx.fillStyle = g;
|
||||
ctx.beginPath(); ctx.roundRect(6, -13, 26, 26, 3); ctx.fill();
|
||||
// accent rings
|
||||
ctx.fillStyle = '#C2358F'; ctx.fillRect(32, -12, 5, 24);
|
||||
ctx.fillStyle = '#28B7A8'; ctx.fillRect(37, -12, 4, 24);
|
||||
// collar
|
||||
const g2 = ctx.createLinearGradient(0, -10, 0, 10);
|
||||
g2.addColorStop(0, '#D8DADE'); g2.addColorStop(0.5, '#F5F6F7'); g2.addColorStop(1, '#75787E');
|
||||
ctx.fillStyle = g2;
|
||||
ctx.beginPath(); ctx.roundRect(41, -10, 6, 20, 1.5); ctx.fill();
|
||||
// gold shaft
|
||||
const g3 = ctx.createLinearGradient(0, -7, 0, 7);
|
||||
g3.addColorStop(0, '#8F6B12'); g3.addColorStop(0.3, '#F4D876'); g3.addColorStop(0.6, '#D9AE33'); g3.addColorStop(1, '#7A5A0E');
|
||||
ctx.fillStyle = g3;
|
||||
ctx.fillRect(47, -6.5, 20, 13);
|
||||
// notch rings
|
||||
ctx.fillStyle = '#1C1710';
|
||||
ctx.fillRect(52, -6.5, 2.2, 13); ctx.fillRect(58, -6.5, 2.2, 13);
|
||||
// tip
|
||||
ctx.fillStyle = g3;
|
||||
ctx.beginPath(); ctx.moveTo(67, -6.5); ctx.lineTo(72.5, -3); ctx.lineTo(72.5, 3); ctx.lineTo(67, 6.5); ctx.closePath(); ctx.fill();
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
canvasRef = React.createRef();
|
||||
|
||||
onGravityInput = (e) => {
|
||||
this.setState({ gravityScale: Number(e.target.value) });
|
||||
};
|
||||
|
||||
onDampingInput = (e) => {
|
||||
this.setState({ motionDamping: Number(e.target.value) });
|
||||
};
|
||||
|
||||
renderVals() {
|
||||
const active = this.state.material;
|
||||
const materials = Object.keys(this.presets).map((id) => ({
|
||||
id, label: this.presets[id].label,
|
||||
bg: id === active ? '#26262A' : 'transparent',
|
||||
fg: id === active ? '#FFFFFF' : '#6B6963',
|
||||
onPick: () => this.setState({ material: id })
|
||||
}));
|
||||
const plugged = this.state.plugged;
|
||||
return {
|
||||
canvasRef: this.canvasRef,
|
||||
materials,
|
||||
gravityScale: this.state.gravityScale,
|
||||
gravityScaleText: this.state.gravityScale.toFixed(2),
|
||||
motionDamping: this.state.motionDamping,
|
||||
motionDampingText: this.state.motionDamping.toFixed(3),
|
||||
onGravityInput: this.onGravityInput,
|
||||
onDampingInput: this.onDampingInput,
|
||||
status: plugged ? '已连接 — 拖拽插头可拔出' : (this.state.dragging ? '拉扯会放出更多线,松手自动回收' : '拖拽插头试试 — 插进右侧插孔'),
|
||||
statusBg: plugged ? '#26402F' : '#E9E7E2',
|
||||
statusFg: plugged ? '#8FE0AE' : '#77746C'
|
||||
};
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 94b761c7a93d45c5a2030fbb5bef8c23
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 573faa4818a14f48bc33412272a6d125
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9ba977124f9744b1bef1acca513e7b4e
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,115 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 89c3eedbb60c406a9e8544e36295c34a
|
||||
TextureImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 12
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 0
|
||||
sRGBTexture: 1
|
||||
linearTexture: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
mipMapsPreserveCoverage: 0
|
||||
alphaTestReferenceValue: 0.5
|
||||
mipMapFadeDistanceStart: 1
|
||||
mipMapFadeDistanceEnd: 3
|
||||
bumpmap:
|
||||
convertToNormalMap: 0
|
||||
externalNormalMap: 0
|
||||
heightScale: 0.25
|
||||
normalMapFilter: 0
|
||||
flipGreenChannel: 0
|
||||
isReadable: 0
|
||||
streamingMipmaps: 0
|
||||
streamingMipmapsPriority: 0
|
||||
vTOnly: 0
|
||||
ignoreMipmapLimit: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
seamlessCubemap: 0
|
||||
textureFormat: 1
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
serializedVersion: 2
|
||||
filterMode: 1
|
||||
aniso: 1
|
||||
mipBias: 0
|
||||
wrapU: 1
|
||||
wrapV: 1
|
||||
wrapW: 1
|
||||
nPOTScale: 0
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 1
|
||||
spriteExtrude: 1
|
||||
spriteMeshType: 1
|
||||
alignment: 0
|
||||
spritePivot: {x: 0.5, y: 0.5}
|
||||
spritePixelsToUnits: 100
|
||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||
spriteGenerateFallbackPhysicsShape: 1
|
||||
alphaUsage: 1
|
||||
alphaIsTransparency: 1
|
||||
spriteTessellationDetail: -1
|
||||
textureType: 8
|
||||
textureShape: 1
|
||||
singleChannelComponent: 0
|
||||
flipbookRows: 1
|
||||
flipbookColumns: 1
|
||||
maxTextureSizeSet: 0
|
||||
compressionQualitySet: 0
|
||||
textureFormatSet: 0
|
||||
ignorePngGamma: 0
|
||||
applyGammaDecoding: 0
|
||||
swizzle: 50462976
|
||||
cookieLightType: 0
|
||||
platformSettings:
|
||||
- serializedVersion: 3
|
||||
buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: Standalone
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
physicsShape: []
|
||||
bones: []
|
||||
spriteID: 5e97eb03825dee720800000000000000
|
||||
internalID: 0
|
||||
vertices: []
|
||||
indices:
|
||||
edges: []
|
||||
weights: []
|
||||
secondaryTextures: []
|
||||
nameFileIdTable: {}
|
||||
spritePackingTag:
|
||||
pSDRemoveMatte: 0
|
||||
pSDShowRemoveMatteOption: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,115 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dd51a060e6484d1ca3fb7a28c65a48fc
|
||||
TextureImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 12
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 0
|
||||
sRGBTexture: 1
|
||||
linearTexture: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
mipMapsPreserveCoverage: 0
|
||||
alphaTestReferenceValue: 0.5
|
||||
mipMapFadeDistanceStart: 1
|
||||
mipMapFadeDistanceEnd: 3
|
||||
bumpmap:
|
||||
convertToNormalMap: 0
|
||||
externalNormalMap: 0
|
||||
heightScale: 0.25
|
||||
normalMapFilter: 0
|
||||
flipGreenChannel: 0
|
||||
isReadable: 0
|
||||
streamingMipmaps: 0
|
||||
streamingMipmapsPriority: 0
|
||||
vTOnly: 0
|
||||
ignoreMipmapLimit: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
seamlessCubemap: 0
|
||||
textureFormat: 1
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
serializedVersion: 2
|
||||
filterMode: 1
|
||||
aniso: 1
|
||||
mipBias: 0
|
||||
wrapU: 1
|
||||
wrapV: 1
|
||||
wrapW: 1
|
||||
nPOTScale: 0
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 1
|
||||
spriteExtrude: 1
|
||||
spriteMeshType: 1
|
||||
alignment: 0
|
||||
spritePivot: {x: 0.5, y: 0.5}
|
||||
spritePixelsToUnits: 100
|
||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||
spriteGenerateFallbackPhysicsShape: 1
|
||||
alphaUsage: 1
|
||||
alphaIsTransparency: 1
|
||||
spriteTessellationDetail: -1
|
||||
textureType: 8
|
||||
textureShape: 1
|
||||
singleChannelComponent: 0
|
||||
flipbookRows: 1
|
||||
flipbookColumns: 1
|
||||
maxTextureSizeSet: 0
|
||||
compressionQualitySet: 0
|
||||
textureFormatSet: 0
|
||||
ignorePngGamma: 0
|
||||
applyGammaDecoding: 0
|
||||
swizzle: 50462976
|
||||
cookieLightType: 0
|
||||
platformSettings:
|
||||
- serializedVersion: 3
|
||||
buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: Standalone
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
physicsShape: []
|
||||
bones: []
|
||||
spriteID: 5e97eb03825dee720800000000000000
|
||||
internalID: 0
|
||||
vertices: []
|
||||
indices:
|
||||
edges: []
|
||||
weights: []
|
||||
secondaryTextures: []
|
||||
nameFileIdTable: {}
|
||||
spritePackingTag:
|
||||
pSDRemoveMatte: 0
|
||||
pSDShowRemoveMatteOption: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,115 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 71e7e75a2ef44bf3bcdc63103638c639
|
||||
TextureImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 12
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 0
|
||||
sRGBTexture: 1
|
||||
linearTexture: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
mipMapsPreserveCoverage: 0
|
||||
alphaTestReferenceValue: 0.5
|
||||
mipMapFadeDistanceStart: 1
|
||||
mipMapFadeDistanceEnd: 3
|
||||
bumpmap:
|
||||
convertToNormalMap: 0
|
||||
externalNormalMap: 0
|
||||
heightScale: 0.25
|
||||
normalMapFilter: 0
|
||||
flipGreenChannel: 0
|
||||
isReadable: 0
|
||||
streamingMipmaps: 0
|
||||
streamingMipmapsPriority: 0
|
||||
vTOnly: 0
|
||||
ignoreMipmapLimit: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
seamlessCubemap: 0
|
||||
textureFormat: 1
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
serializedVersion: 2
|
||||
filterMode: 1
|
||||
aniso: 1
|
||||
mipBias: 0
|
||||
wrapU: 1
|
||||
wrapV: 1
|
||||
wrapW: 1
|
||||
nPOTScale: 0
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 1
|
||||
spriteExtrude: 1
|
||||
spriteMeshType: 1
|
||||
alignment: 0
|
||||
spritePivot: {x: 0.5, y: 0.5}
|
||||
spritePixelsToUnits: 100
|
||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||
spriteGenerateFallbackPhysicsShape: 1
|
||||
alphaUsage: 1
|
||||
alphaIsTransparency: 1
|
||||
spriteTessellationDetail: -1
|
||||
textureType: 8
|
||||
textureShape: 1
|
||||
singleChannelComponent: 0
|
||||
flipbookRows: 1
|
||||
flipbookColumns: 1
|
||||
maxTextureSizeSet: 0
|
||||
compressionQualitySet: 0
|
||||
textureFormatSet: 0
|
||||
ignorePngGamma: 0
|
||||
applyGammaDecoding: 0
|
||||
swizzle: 50462976
|
||||
cookieLightType: 0
|
||||
platformSettings:
|
||||
- serializedVersion: 3
|
||||
buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: Standalone
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
physicsShape: []
|
||||
bones: []
|
||||
spriteID: 5e97eb03825dee720800000000000000
|
||||
internalID: 0
|
||||
vertices: []
|
||||
indices:
|
||||
edges: []
|
||||
weights: []
|
||||
secondaryTextures: []
|
||||
nameFileIdTable: {}
|
||||
spritePackingTag:
|
||||
pSDRemoveMatte: 0
|
||||
pSDShowRemoveMatteOption: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,115 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e916d6782b9b46ca921a46a74a370cca
|
||||
TextureImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 12
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 0
|
||||
sRGBTexture: 1
|
||||
linearTexture: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
mipMapsPreserveCoverage: 0
|
||||
alphaTestReferenceValue: 0.5
|
||||
mipMapFadeDistanceStart: 1
|
||||
mipMapFadeDistanceEnd: 3
|
||||
bumpmap:
|
||||
convertToNormalMap: 0
|
||||
externalNormalMap: 0
|
||||
heightScale: 0.25
|
||||
normalMapFilter: 0
|
||||
flipGreenChannel: 0
|
||||
isReadable: 0
|
||||
streamingMipmaps: 0
|
||||
streamingMipmapsPriority: 0
|
||||
vTOnly: 0
|
||||
ignoreMipmapLimit: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
seamlessCubemap: 0
|
||||
textureFormat: 1
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
serializedVersion: 2
|
||||
filterMode: 1
|
||||
aniso: 1
|
||||
mipBias: 0
|
||||
wrapU: 1
|
||||
wrapV: 1
|
||||
wrapW: 1
|
||||
nPOTScale: 0
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 1
|
||||
spriteExtrude: 1
|
||||
spriteMeshType: 1
|
||||
alignment: 0
|
||||
spritePivot: {x: 0.5, y: 0.5}
|
||||
spritePixelsToUnits: 100
|
||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||
spriteGenerateFallbackPhysicsShape: 1
|
||||
alphaUsage: 1
|
||||
alphaIsTransparency: 1
|
||||
spriteTessellationDetail: -1
|
||||
textureType: 8
|
||||
textureShape: 1
|
||||
singleChannelComponent: 0
|
||||
flipbookRows: 1
|
||||
flipbookColumns: 1
|
||||
maxTextureSizeSet: 0
|
||||
compressionQualitySet: 0
|
||||
textureFormatSet: 0
|
||||
ignorePngGamma: 0
|
||||
applyGammaDecoding: 0
|
||||
swizzle: 50462976
|
||||
cookieLightType: 0
|
||||
platformSettings:
|
||||
- serializedVersion: 3
|
||||
buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: Standalone
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
physicsShape: []
|
||||
bones: []
|
||||
spriteID: 5e97eb03825dee720800000000000000
|
||||
internalID: 0
|
||||
vertices: []
|
||||
indices:
|
||||
edges: []
|
||||
weights: []
|
||||
secondaryTextures: []
|
||||
nameFileIdTable: {}
|
||||
spritePackingTag:
|
||||
pSDRemoveMatte: 0
|
||||
pSDShowRemoveMatteOption: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,115 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 27ec977643184f14b75e84b0859513c5
|
||||
TextureImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 12
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 0
|
||||
sRGBTexture: 1
|
||||
linearTexture: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
mipMapsPreserveCoverage: 0
|
||||
alphaTestReferenceValue: 0.5
|
||||
mipMapFadeDistanceStart: 1
|
||||
mipMapFadeDistanceEnd: 3
|
||||
bumpmap:
|
||||
convertToNormalMap: 0
|
||||
externalNormalMap: 0
|
||||
heightScale: 0.25
|
||||
normalMapFilter: 0
|
||||
flipGreenChannel: 0
|
||||
isReadable: 0
|
||||
streamingMipmaps: 0
|
||||
streamingMipmapsPriority: 0
|
||||
vTOnly: 0
|
||||
ignoreMipmapLimit: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
seamlessCubemap: 0
|
||||
textureFormat: 1
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
serializedVersion: 2
|
||||
filterMode: 1
|
||||
aniso: 1
|
||||
mipBias: 0
|
||||
wrapU: 1
|
||||
wrapV: 1
|
||||
wrapW: 1
|
||||
nPOTScale: 0
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 1
|
||||
spriteExtrude: 1
|
||||
spriteMeshType: 1
|
||||
alignment: 0
|
||||
spritePivot: {x: 0.5, y: 0.5}
|
||||
spritePixelsToUnits: 100
|
||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||
spriteGenerateFallbackPhysicsShape: 1
|
||||
alphaUsage: 1
|
||||
alphaIsTransparency: 1
|
||||
spriteTessellationDetail: -1
|
||||
textureType: 8
|
||||
textureShape: 1
|
||||
singleChannelComponent: 0
|
||||
flipbookRows: 1
|
||||
flipbookColumns: 1
|
||||
maxTextureSizeSet: 0
|
||||
compressionQualitySet: 0
|
||||
textureFormatSet: 0
|
||||
ignorePngGamma: 0
|
||||
applyGammaDecoding: 0
|
||||
swizzle: 50462976
|
||||
cookieLightType: 0
|
||||
platformSettings:
|
||||
- serializedVersion: 3
|
||||
buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: Standalone
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
physicsShape: []
|
||||
bones: []
|
||||
spriteID: 5e97eb03825dee720800000000000000
|
||||
internalID: 0
|
||||
vertices: []
|
||||
indices:
|
||||
edges: []
|
||||
weights: []
|
||||
secondaryTextures: []
|
||||
nameFileIdTable: {}
|
||||
spritePackingTag:
|
||||
pSDRemoveMatte: 0
|
||||
pSDShowRemoveMatteOption: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,115 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 59c494bd6d6d465687bb3121cdac50c9
|
||||
TextureImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 12
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 0
|
||||
sRGBTexture: 1
|
||||
linearTexture: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
mipMapsPreserveCoverage: 0
|
||||
alphaTestReferenceValue: 0.5
|
||||
mipMapFadeDistanceStart: 1
|
||||
mipMapFadeDistanceEnd: 3
|
||||
bumpmap:
|
||||
convertToNormalMap: 0
|
||||
externalNormalMap: 0
|
||||
heightScale: 0.25
|
||||
normalMapFilter: 0
|
||||
flipGreenChannel: 0
|
||||
isReadable: 0
|
||||
streamingMipmaps: 0
|
||||
streamingMipmapsPriority: 0
|
||||
vTOnly: 0
|
||||
ignoreMipmapLimit: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
seamlessCubemap: 0
|
||||
textureFormat: 1
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
serializedVersion: 2
|
||||
filterMode: 1
|
||||
aniso: 1
|
||||
mipBias: 0
|
||||
wrapU: 1
|
||||
wrapV: 1
|
||||
wrapW: 1
|
||||
nPOTScale: 0
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 1
|
||||
spriteExtrude: 1
|
||||
spriteMeshType: 1
|
||||
alignment: 0
|
||||
spritePivot: {x: 0.5, y: 0.5}
|
||||
spritePixelsToUnits: 100
|
||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||
spriteGenerateFallbackPhysicsShape: 1
|
||||
alphaUsage: 1
|
||||
alphaIsTransparency: 1
|
||||
spriteTessellationDetail: -1
|
||||
textureType: 8
|
||||
textureShape: 1
|
||||
singleChannelComponent: 0
|
||||
flipbookRows: 1
|
||||
flipbookColumns: 1
|
||||
maxTextureSizeSet: 0
|
||||
compressionQualitySet: 0
|
||||
textureFormatSet: 0
|
||||
ignorePngGamma: 0
|
||||
applyGammaDecoding: 0
|
||||
swizzle: 50462976
|
||||
cookieLightType: 0
|
||||
platformSettings:
|
||||
- serializedVersion: 3
|
||||
buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: Standalone
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
physicsShape: []
|
||||
bones: []
|
||||
spriteID: 5e97eb03825dee720800000000000000
|
||||
internalID: 0
|
||||
vertices: []
|
||||
indices:
|
||||
edges: []
|
||||
weights: []
|
||||
secondaryTextures: []
|
||||
nameFileIdTable: {}
|
||||
spritePackingTag:
|
||||
pSDRemoveMatte: 0
|
||||
pSDShowRemoveMatteOption: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,115 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9086b29c556b42b592a5bacb60054247
|
||||
TextureImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 12
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 0
|
||||
sRGBTexture: 1
|
||||
linearTexture: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
mipMapsPreserveCoverage: 0
|
||||
alphaTestReferenceValue: 0.5
|
||||
mipMapFadeDistanceStart: 1
|
||||
mipMapFadeDistanceEnd: 3
|
||||
bumpmap:
|
||||
convertToNormalMap: 0
|
||||
externalNormalMap: 0
|
||||
heightScale: 0.25
|
||||
normalMapFilter: 0
|
||||
flipGreenChannel: 0
|
||||
isReadable: 0
|
||||
streamingMipmaps: 0
|
||||
streamingMipmapsPriority: 0
|
||||
vTOnly: 0
|
||||
ignoreMipmapLimit: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
seamlessCubemap: 0
|
||||
textureFormat: 1
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
serializedVersion: 2
|
||||
filterMode: 1
|
||||
aniso: 1
|
||||
mipBias: 0
|
||||
wrapU: 1
|
||||
wrapV: 1
|
||||
wrapW: 1
|
||||
nPOTScale: 0
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 1
|
||||
spriteExtrude: 1
|
||||
spriteMeshType: 1
|
||||
alignment: 0
|
||||
spritePivot: {x: 0.5, y: 0.5}
|
||||
spritePixelsToUnits: 100
|
||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||
spriteGenerateFallbackPhysicsShape: 1
|
||||
alphaUsage: 1
|
||||
alphaIsTransparency: 1
|
||||
spriteTessellationDetail: -1
|
||||
textureType: 8
|
||||
textureShape: 1
|
||||
singleChannelComponent: 0
|
||||
flipbookRows: 1
|
||||
flipbookColumns: 1
|
||||
maxTextureSizeSet: 0
|
||||
compressionQualitySet: 0
|
||||
textureFormatSet: 0
|
||||
ignorePngGamma: 0
|
||||
applyGammaDecoding: 0
|
||||
swizzle: 50462976
|
||||
cookieLightType: 0
|
||||
platformSettings:
|
||||
- serializedVersion: 3
|
||||
buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: Standalone
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
physicsShape: []
|
||||
bones: []
|
||||
spriteID: 5e97eb03825dee720800000000000000
|
||||
internalID: 0
|
||||
vertices: []
|
||||
indices:
|
||||
edges: []
|
||||
weights: []
|
||||
secondaryTextures: []
|
||||
nameFileIdTable: {}
|
||||
spritePackingTag:
|
||||
pSDRemoveMatte: 0
|
||||
pSDShowRemoveMatteOption: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,115 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2bcf39c49a364bd9b4edb964f62cb9ab
|
||||
TextureImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 12
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 0
|
||||
sRGBTexture: 1
|
||||
linearTexture: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
mipMapsPreserveCoverage: 0
|
||||
alphaTestReferenceValue: 0.5
|
||||
mipMapFadeDistanceStart: 1
|
||||
mipMapFadeDistanceEnd: 3
|
||||
bumpmap:
|
||||
convertToNormalMap: 0
|
||||
externalNormalMap: 0
|
||||
heightScale: 0.25
|
||||
normalMapFilter: 0
|
||||
flipGreenChannel: 0
|
||||
isReadable: 0
|
||||
streamingMipmaps: 0
|
||||
streamingMipmapsPriority: 0
|
||||
vTOnly: 0
|
||||
ignoreMipmapLimit: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
seamlessCubemap: 0
|
||||
textureFormat: 1
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
serializedVersion: 2
|
||||
filterMode: 1
|
||||
aniso: 1
|
||||
mipBias: 0
|
||||
wrapU: 1
|
||||
wrapV: 1
|
||||
wrapW: 1
|
||||
nPOTScale: 0
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 1
|
||||
spriteExtrude: 1
|
||||
spriteMeshType: 1
|
||||
alignment: 0
|
||||
spritePivot: {x: 0.5, y: 0.5}
|
||||
spritePixelsToUnits: 100
|
||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||
spriteGenerateFallbackPhysicsShape: 1
|
||||
alphaUsage: 1
|
||||
alphaIsTransparency: 1
|
||||
spriteTessellationDetail: -1
|
||||
textureType: 8
|
||||
textureShape: 1
|
||||
singleChannelComponent: 0
|
||||
flipbookRows: 1
|
||||
flipbookColumns: 1
|
||||
maxTextureSizeSet: 0
|
||||
compressionQualitySet: 0
|
||||
textureFormatSet: 0
|
||||
ignorePngGamma: 0
|
||||
applyGammaDecoding: 0
|
||||
swizzle: 50462976
|
||||
cookieLightType: 0
|
||||
platformSettings:
|
||||
- serializedVersion: 3
|
||||
buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: Standalone
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
physicsShape: []
|
||||
bones: []
|
||||
spriteID: 5e97eb03825dee720800000000000000
|
||||
internalID: 0
|
||||
vertices: []
|
||||
indices:
|
||||
edges: []
|
||||
weights: []
|
||||
secondaryTextures: []
|
||||
nameFileIdTable: {}
|
||||
spritePackingTag:
|
||||
pSDRemoveMatte: 0
|
||||
pSDShowRemoveMatteOption: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user