十字型空间站 · 电磁炮 / 激光 / 导弹
by ApexFalcon66699 lines21.3 KB
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>十字型空间站 · 电磁炮 / 激光 / 导弹</title>
<style>
* { margin:0; padding:0; box-sizing:border-box; }
html, body { width:100%; height:100%; overflow:hidden; }
body {
background: radial-gradient(circle at 50% 45%, #0c1228 0%, #03050c 100%);
font-family: system-ui, -apple-system, sans-serif;
-webkit-user-select:none; user-select:none;
}
/* ===== 视角容器 ===== */
.viewport {
position: fixed; inset: 0;
overflow: hidden;
cursor: grab;
touch-action: none;
}
.viewport.grabbing { cursor: grabbing; }
.scene {
position: absolute;
left: 50%; top: 50%;
width: 1200px; height: 1200px;
margin-left: -600px; margin-top: -600px;
transform-origin: 50% 50%;
will-change: transform;
}
/* ===== 星空 ===== */
.star-layer { position:absolute; inset:0; }
.star { position:absolute; background:#fff; border-radius:50%; animation: twinkle 3s infinite ease-in-out; }
@keyframes twinkle { 0%,100% { opacity:.15; } 50% { opacity:.95; } }
/* ===== 空间站 ===== */
.station {
position: absolute;
left: 380px; top: 380px;
width: 440px; height: 440px;
animation: float 7s ease-in-out infinite;
}
@keyframes float {
0%,100% { transform: translateY(0); }
50% { transform: translateY(-10px); }
}
.mod {
position:absolute;
background:#fff;
border-radius:3px;
box-shadow: 0 3px 14px rgba(0,0,0,.45), inset 0 -2px 5px rgba(150,170,200,.25);
}
.core { width:96px; height:24px; left:172px; top:208px; }
.arm-left { width:80px; height:24px; left:92px; top:208px; }
.arm-right { width:80px; height:24px; left:268px; top:208px; }
.arm-up { width:24px; height:80px; left:208px; top:128px; }
.arm-down { width:24px; height:80px; left:208px; top:232px; }
.panel {
position:absolute;
width:26px; height:72px;
background: repeating-linear-gradient(0deg, #1d4d86 0px, #1d4d86 8px, #2b6cb5 8px, #2b6cb5 10px);
border:2px solid #4a90d9;
border-radius:2px;
box-shadow: 0 0 16px rgba(74,144,217,.35);
}
.panel-lt { left:79px; top:136px; }
.panel-lb { left:79px; top:232px; }
.panel-rt { left:335px; top:136px; }
.panel-rb { left:335px; top:232px; }
.win {
position:absolute;
width:8px; height:4px;
background:#2d4d6e;
border-radius:1px;
box-shadow: 0 0 4px rgba(120,190,255,.6);
}
.port { position:absolute; background:#b9c6d4; border:1px solid #8496aa; border-radius:2px; }
.port-v { width:10px; height:12px; }
.ship {
position:absolute;
width:30px; height:14px;
background:#e8eef5;
border:2px solid #98aabd;
border-radius:3px;
}
/* ===== 底部推进器 ===== */
.thruster { position:absolute; left:206px; top:324px; width:28px; height:14px; }
.thruster .bell {
width:100%; height:100%;
background: linear-gradient(180deg,#eef3f9 0%,#b7c4d3 55%,#8a9bb0 100%);
clip-path: polygon(0 0, 100% 0, 82% 100%, 18% 100%);
}
.flame { position:absolute; left:50%; top:13px; width:60px; height:80px; transform:translateX(-50%); pointer-events:none; }
.flame-outer {
position:absolute; left:50%; top:0;
width:30px; height:62px;
transform:translateX(-50%); transform-origin:50% 0;
background: radial-gradient(ellipse at 50% 12%, #ffd166 0%, #ff9a1f 35%, #ff5f00 68%, rgba(255,90,0,0) 100%);
clip-path: polygon(50% 100%, 0 0, 100% 0);
filter: blur(.6px);
animation: flameFlicker .16s infinite alternate ease-in-out;
}
.flame-core {
position:absolute; left:50%; top:0;
width:13px; height:34px;
transform:translateX(-50%); transform-origin:50% 0;
background: radial-gradient(ellipse at 50% 15%, #ffffff 0%, #fff4b8 45%, #ffc247 100%);
clip-path: polygon(50% 100%, 0 0, 100% 0);
animation: flameFlicker .13s infinite alternate-reverse ease-in-out;
}
.flame-glow {
position:absolute; left:50%; top:-6px;
width:80px; height:80px;
transform:translateX(-50%);
background: radial-gradient(circle, rgba(255,150,40,.42) 0%, rgba(255,120,0,0) 68%);
border-radius:50%;
animation: glowPulse .5s infinite alternate ease-in-out;
}
@keyframes flameFlicker {
from { transform: translateX(-50%) scale(.88,.78); opacity:.82; }
to { transform: translateX(-50%) scale(1.06,1.15); opacity:1; }
}
@keyframes glowPulse {
from { opacity:.55; transform: translateX(-50%) scale(.9); }
to { opacity:.95; transform: translateX(-50%) scale(1.1); }
}
/* ===== 上方电磁炮 ===== */
.rail-turret { position:absolute; left:220px; top:122px; width:0; height:0; z-index:12; }
.rail-base {
position:absolute; width:30px; height:30px;
left:-15px; top:-15px;
border-radius:5px;
background: linear-gradient(180deg,#ffffff,#b9c6d4);
border:2px solid #8a9bb0;
box-shadow: 0 0 14px rgba(120,200,255,.6);
}
.rail-barrel {
position:absolute; left:0; top:-5px;
width:54px; height:10px;
transform-origin:0 50%;
background: linear-gradient(180deg,#f4f9ff 0%,#b8c6d6 55%,#7f8ea3 100%);
border:1px solid #6a7e92;
border-radius:0 3px 3px 0;
box-shadow: 0 0 8px rgba(120,200,255,.4);
transition: transform .09s linear;
}
/* ===== 核心舱激光炮 ===== */
.laser-turret { position:absolute; left:220px; top:220px; width:0; height:0; z-index:13; }
.laser-base {
position:absolute; width:24px; height:24px;
left:-12px; top:-12px;
border-radius:50%;
background: radial-gradient(circle at 35% 30%, #ffffff, #9fc4e8 70%, #6a8aaa);
border:2px solid #5f7fa0;
box-shadow: 0 0 14px rgba(90,190,255,.7);
}
.laser-barrel {
position:absolute; left:0; top:-3px;
width:30px; height:6px;
transform-origin:0 50%;
background: linear-gradient(90deg,#e8f8ff 0%,#6fc8ff 60%,#3aa6e8 100%);
border:1px solid #2f9ae0;
border-radius:0 3px 3px 0;
box-shadow: 0 0 10px rgba(80,190,255,.85);
transition: transform .09s linear;
}
/* ===== 左右导弹巢 ===== */
.missile-pod {
position:absolute;
width:18px; height:28px;
top:206px;
border-radius:4px;
background:
radial-gradient(circle at 30% 22%, #2d4d6e 2px, transparent 2.4px),
radial-gradient(circle at 70% 22%, #2d4d6e 2px, transparent 2.4px),
radial-gradient(circle at 30% 50%, #2d4d6e 2px, transparent 2.4px),
radial-gradient(circle at 70% 50%, #2d4d6e 2px, transparent 2.4px),
radial-gradient(circle at 30% 78%, #2d4d6e 2px, transparent 2.4px),
radial-gradient(circle at 70% 78%, #2d4d6e 2px, transparent 2.4px),
linear-gradient(180deg,#ffffff,#b9c6d4);
border:2px solid #8a9bb0;
box-shadow: 0 0 10px rgba(255,190,120,.35);
z-index:12;
}
.missile-pod.left { left:81px; }
.missile-pod.right { left:341px; }
/* ===== 弹体通用 ===== */
.projectile { position:absolute; left:0; top:0; pointer-events:none; }
/* 电磁炮弹丸 */
.rail-slug {
width:34px; height:6px;
margin:-3px 0 0 -17px;
border-radius:3px;
background: linear-gradient(90deg, rgba(120,220,255,0) 0%, #7fdcff 45%, #ffffff 80%, #ffffff 100%);
box-shadow: 0 0 12px #7fdcff, 0 0 26px rgba(127,220,255,.6);
}
/* 导弹 */
.missile {
width:16px; height:6px;
margin:-3px 0 0 -8px;
border-radius:0 3px 3px 0;
background: linear-gradient(90deg,#ffb347 0%,#e8eef5 55%,#ffffff 100%);
box-shadow: 0 0 6px rgba(255,150,50,.75);
}
.missile::before {
content:"";
position:absolute;
right:100%; top:50%;
width:24px; height:6px;
margin-top:-3px;
background: linear-gradient(90deg, rgba(255,120,0,0) 0%, #ff8c1a 55%, #ffd166 100%);
border-radius:3px 0 0 3px;
transform-origin:100% 50%;
filter: blur(1px);
animation: missileFlame .1s infinite alternate;
}
@keyframes missileFlame {
from { transform: scaleX(.75); opacity:.8; }
to { transform: scaleX(1.15); opacity:1; }
}
/* 激光束 */
.laser-beam {
position:absolute; left:0; top:0;
height:4px;
margin-top:-2px;
transform-origin:0 50%;
border-radius:2px;
background: linear-gradient(90deg, #ffffff 0%, #d8f4ff 18%, #6fc8ff 55%, rgba(79,184,255,0) 100%);
box-shadow: 0 0 14px #4fb8ff, 0 0 34px rgba(79,184,255,.75);
animation: laserFade .34s ease-out forwards;
pointer-events:none;
}
.laser-beam::before {
content:"";
position:absolute;
left:0; top:50%;
width:100%; height:14px;
margin-top:-7px;
border-radius:7px;
background: linear-gradient(90deg, rgba(180,240,255,.55) 0%, rgba(120,210,255,.35) 30%, rgba(79,184,255,0) 100%);
filter: blur(2px);
}
@keyframes laserFade {
0% { opacity:1; }
65% { opacity:1; }
100% { opacity:0; }
}
/* 炮口闪光 */
.muzzle-flash {
position:absolute; left:0; top:0;
width:48px; height:48px;
margin:-24px 0 0 -24px;
border-radius:50%;
background: radial-gradient(circle, rgba(255,255,255,.95) 0%, rgba(140,225,255,.65) 38%, rgba(60,180,255,0) 70%);
animation: flashPop .2s ease-out forwards;
pointer-events:none;
}
@keyframes flashPop {
from { opacity:1; transform: scale(.6); }
to { opacity:0; transform: scale(1.3); }
}
/* ===== UI ===== */
.reset-btn {
position:fixed; top:16px; right:16px; z-index:50;
padding:8px 14px;
font-size:12px; letter-spacing:1px;
color:#cfe6ff;
background: rgba(20,40,70,.65);
border:1px solid rgba(100,170,255,.45);
border-radius:8px;
-webkit-backdrop-filter: blur(6px);
backdrop-filter: blur(6px);
cursor:pointer;
-webkit-tap-highlight-color: transparent;
}
.reset-btn:active { background: rgba(40,80,130,.85); }
.hint {
position:fixed; left:50%; bottom:18px;
transform:translateX(-50%);
color: rgba(180,210,240,.55);
font-size:12px; letter-spacing:1.5px;
white-space:nowrap;
pointer-events:none;
z-index:20;
}
</style>
</head>
<body>
<div class="viewport" id="viewport">
<div class="scene" id="scene">
<div class="star-layer" id="starLayer"></div>
<div class="station" id="station">
<!-- 太阳能帆板 -->
<div class="panel panel-lt"></div>
<div class="panel panel-lb"></div>
<div class="panel panel-rt"></div>
<div class="panel panel-rb"></div>
<!-- 纵向臂 -->
<div class="mod arm-up">
<div class="win" style="left:8px; top:26px;"></div>
<div class="win" style="left:8px; top:48px;"></div>
</div>
<div class="mod arm-down">
<div class="win" style="left:8px; top:26px;"></div>
<div class="win" style="left:8px; top:48px;"></div>
</div>
<!-- 横向臂 -->
<div class="mod arm-left">
<div class="win" style="left:28px; top:10px;"></div>
<div class="win" style="left:44px; top:10px;"></div>
</div>
<div class="mod arm-right">
<div class="win" style="left:28px; top:10px;"></div>
<div class="win" style="left:44px; top:10px;"></div>
</div>
<!-- 核心舱 -->
<div class="mod core">
<div class="win" style="left:14px; top:10px;"></div>
<div class="win" style="left:78px; top:10px;"></div>
</div>
<!-- 顶部对接口 -->
<div class="port port-v" style="left:215px; top:116px;"></div>
<!-- 停靠飞船 -->
<div class="ship" style="left:50px; top:213px;"></div>
<!-- 底部推进器 -->
<div class="thruster">
<div class="bell"></div>
<div class="flame">
<div class="flame-glow"></div>
<div class="flame-outer"></div>
<div class="flame-core"></div>
</div>
</div>
<!-- 上方电磁炮 -->
<div class="rail-turret">
<div class="rail-base"></div>
<div class="rail-barrel" id="railBarrel"></div>
</div>
<!-- 核心舱激光炮 -->
<div class="laser-turret">
<div class="laser-base"></div>
<div class="laser-barrel" id="laserBarrel"></div>
</div>
<!-- 左右导弹巢 -->
<div class="missile-pod left" id="podLeft"></div>
<div class="missile-pod right" id="podRight"></div>
</div>
</div>
</div>
<button class="reset-btn" id="resetBtn">重置视角</button>
<div class="hint">单指拖动 · 双指缩放 · 点击开火</div>
<script>
(function () {
'use strict';
const viewport = document.getElementById('viewport');
const scene = document.getElementById('scene');
const station = document.getElementById('station');
const starLayer = document.getElementById('starLayer');
const resetBtn = document.getElementById('resetBtn');
const railBarrel = document.getElementById('railBarrel');
const laserBarrel = document.getElementById('laserBarrel');
/* ---------- 星空 ---------- */
const frag = document.createDocumentFragment();
for (let i = 0; i < 200; i++) {
const s = document.createElement('div');
s.className = 'star';
const size = (Math.random() * 2 + 1).toFixed(1);
s.style.width = size + 'px';
s.style.height = size + 'px';
s.style.left = (Math.random() * 100).toFixed(2) + '%';
s.style.top = (Math.random() * 100).toFixed(2) + '%';
s.style.animationDelay = (Math.random() * 3).toFixed(2) + 's';
s.style.animationDuration = (2 + Math.random() * 3).toFixed(2) + 's';
frag.appendChild(s);
}
starLayer.appendChild(frag);
/* ---------- 视角状态 ---------- */
const MIN_SCALE = 0.35;
const MAX_SCALE = 3;
let scale = 1, tx = 0, ty = 0;
function apply() {
scene.style.transform = `translate(${tx}px, ${ty}px) scale(${scale})`;
}
(function initScale() {
const r = viewport.getBoundingClientRect();
const fit = Math.min(r.width / 560, r.height / 560);
scale = Math.max(MIN_SCALE, Math.min(1, fit));
apply();
})();
/* ---------- 指针手势:拖动 / 双指缩放 ---------- */
const pointers = new Map();
let gestureMulti = false;
let startX = 0, startY = 0, startT = 0;
let pinchDist = 0, pinchCX = 0, pinchCY = 0;
viewport.addEventListener('pointerdown', function (e) {
e.preventDefault();
try { viewport.setPointerCapture(e.pointerId); } catch (err) {}
pointers.set(e.pointerId, { x: e.clientX, y: e.clientY });
viewport.classList.add('grabbing');
if (pointers.size === 1) {
gestureMulti = false;
startX = e.clientX;
startY = e.clientY;
startT = performance.now();
} else if (pointers.size === 2) {
gestureMulti = true;
const pts = [...pointers.values()];
pinchDist = Math.hypot(pts[1].x - pts[0].x, pts[1].y - pts[0].y);
pinchCX = (pts[0].x + pts[1].x) / 2;
pinchCY = (pts[0].y + pts[1].y) / 2;
}
}, { passive: false });
viewport.addEventListener('pointermove', function (e) {
const p = pointers.get(e.pointerId);
if (!p) return;
const dx = e.clientX - p.x;
const dy = e.clientY - p.y;
p.x = e.clientX;
p.y = e.clientY;
if (pointers.size === 1) {
tx += dx; ty += dy;
apply();
return;
}
const pts = [...pointers.values()];
const a = pts[0], b = pts[1];
const dist = Math.hypot(b.x - a.x, b.y - a.y);
const c2x = (a.x + b.x) / 2;
const c2y = (a.y + b.y) / 2;
if (pinchDist > 0 && dist > 0) {
tx += c2x - pinchCX;
ty += c2y - pinchCY;
const rect = viewport.getBoundingClientRect();
const cX = rect.width / 2;
const cY = rect.height / 2;
const mx = c2x - rect.left;
const my = c2y - rect.top;
let ns = scale * (dist / pinchDist);
ns = Math.max(MIN_SCALE, Math.min(MAX_SCALE, ns));
if (ns !== scale) {
const k = ns / scale;
const ux = mx - cX;
const uy = my - cY;
tx = ux * (1 - k) + k * tx;
ty = uy * (1 - k) + k * ty;
scale = ns;
}
apply();
}
pinchDist = dist;
pinchCX = c2x;
pinchCY = c2y;
}, { passive: false });
function endPointer(e) {
if (!pointers.has(e.pointerId)) return;
const wasSingle = (pointers.size === 1);
pointers.delete(e.pointerId);
if (pointers.size === 0) viewport.classList.remove('grabbing');
if (wasSingle && !gestureMulti) {
const dt = performance.now() - startT;
const dist = Math.hypot(e.clientX - startX, e.clientY - startY);
if (dist < 12 && dt < 400) {
fireAll(e.clientX, e.clientY);
}
}
if (pointers.size === 0) {
gestureMulti = false;
pinchDist = 0;
} else if (pointers.size === 1) {
pinchDist = 0;
}
}
viewport.addEventListener('pointerup', endPointer);
viewport.addEventListener('pointercancel', endPointer);
/* ---------- 读取空间站浮动偏移 ---------- */
function getStationOffset() {
const t = getComputedStyle(station).transform;
if (t && t !== 'none') {
const m = t.match(/matrix\(([^)]+)\)/);
if (m) {
const v = m[1].split(',').map(Number);
return { x: v[4], y: v[5] };
}
}
return { x: 0, y: 0 };
}
/* ---------- 坐标转换:屏幕 → 场景 ---------- */
function screenToScene(clientX, clientY) {
const rect = viewport.getBoundingClientRect();
const cX = rect.width / 2;
const cY = rect.height / 2;
const mx = clientX - rect.left;
const my = clientY - rect.top;
return {
x: (mx - cX - tx) / scale,
y: (my - cY - ty) / scale
};
}
/* ---------- 三武器同时开火 ---------- */
const projectiles = [];
function fireAll(clientX, clientY) {
const target = screenToScene(clientX, clientY);
const off = getStationOffset();
const O = { x: 380 + off.x, y: 380 + off.y };
// 电磁炮:上方纵臂顶端
fireRailgun(O.x + 220, O.y + 122, target.x, target.y);
// 激光:核心舱中央
fireLaser(O.x + 220, O.y + 220, target.x, target.y);
// 导弹:左右横臂末端各一发
fireMissile(O.x + 90, O.y + 220, target.x, target.y);
fireMissile(O.x + 350, O.y + 220, target.x, target.y);
}
/* 电磁炮:高速穿甲弹,带蓝色拖尾 */
function fireRailgun(bx, by, tx, ty) {
const angle = Math.atan2(ty - by, tx - bx);
railBarrel.style.transform = `rotate(${angle}rad)`;
const len = 56;
const mx = bx + Math.cos(angle) * len;
const my = by + Math.sin(angle) * len;
const el = document.createElement('div');
el.className = 'projectile rail-slug';
scene.appendChild(el);
projectiles.push({
el: el, x: mx, y: my, angle: angle,
vx: Math.cos(angle) * 2200,
vy: Math.sin(angle) * 2200,
life: 0, maxLife: 1.4
});
spawnFlash(mx, my, 'rail');
}
/* 激光:瞬发光束 */
function fireLaser(bx, by, tx, ty) {
const angle = Math.atan2(ty - by, tx - bx);
laserBarrel.style.transform = `rotate(${angle}rad)`;
const len0 = 30;
const mx = bx + Math.cos(angle) * len0;
const my = by + Math.sin(angle) * len0;
const dist = Math.hypot(tx - mx, ty - my) + 320;
const beam = document.createElement('div');
beam.className = 'laser-beam';
beam.style.width = dist + 'px';
beam.style.transform = `translate(${mx}px, ${my}px) rotate(${angle}rad)`;
scene.appendChild(beam);
setTimeout(function () { beam.remove(); }, 380);
spawnFlash(mx, my, 'laser');
}
/* 导弹:中速飞行,带橙色尾焰 */
function fireMissile(bx, by, tx, ty) {
const angle = Math.atan2(ty - by, tx - bx);
const el = document.createElement('div');
el.className = 'projectile missile';
el.style.transform = `translate(${bx}px, ${by}px) rotate(${angle}rad)`;
scene.appendChild(el);
projectiles.push({
el: el, x: bx, y: by, angle: angle,
vx: Math.cos(angle) * 680,
vy: Math.sin(angle) * 680,
life: 0, maxLife: 2.4
});
spawnFlash(bx, by, 'missile');
}
function spawnFlash(x, y, type) {
const el = document.createElement('div');
el.className = 'muzzle-flash';
el.style.transform = `translate(${x}px, ${y}px)`;
if (type === 'missile') {
el.style.background = 'radial-gradient(circle, rgba(255,240,200,.95) 0%, rgba(255,160,60,.7) 35%, rgba(255,120,0,0) 70%)';
} else if (type === 'rail') {
el.style.background = 'radial-gradient(circle, rgba(255,255,255,.95) 0%, rgba(140,230,255,.7) 35%, rgba(80,190,255,0) 70%)';
}
scene.appendChild(el);
setTimeout(function () { el.remove(); }, 240);
}
/* ---------- 弹丸动画循环 ---------- */
let lastT = performance.now();
function loop(now) {
const dt = Math.min((now - lastT) / 1000, 0.05);
lastT = now;
for (let i = projectiles.length - 1; i >= 0; i--) {
const p = projectiles[i];
p.x += p.vx * dt;
p.y += p.vy * dt;
p.life += dt;
if (p.life > p.maxLife ||
p.x < -800 || p.x > 2000 ||
p.y < -800 || p.y > 2000) {
p.el.remove();
projectiles.splice(i, 1);
continue;
}
p.el.style.transform = `translate(${p.x}px, ${p.y}px) rotate(${p.angle}rad)`;
}
requestAnimationFrame(loop);
}
requestAnimationFrame(loop);
/* ---------- 重置视角 ---------- */
resetBtn.addEventListener('click', function () {
const r = viewport.getBoundingClientRect();
const fit = Math.min(r.width / 560, r.height / 560);
const targetScale = Math.max(MIN_SCALE, Math.min(1, fit));
scene.style.transition = 'transform .3s ease-out';
scale = targetScale;
tx = 0;
ty = 0;
apply();
setTimeout(function () { scene.style.transition = ''; }, 320);
});
})();
</script>
</body>
</html>Game Source: 十字型空间站 · 电磁炮 / 激光 / 导弹
Creator: ApexFalcon66
Libraries: none
Complexity: complex (699 lines, 21.3 KB)
The full source code is displayed above on this page.
Remix Instructions
To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: game-apexfalcon66-mvdgkifc" to link back to the original. Then publish at arcadelab.ai/publish.