Files
miti.sh/html/images/space.svg

434 lines
12 KiB
XML

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg viewBox="-200 -150 400 300" version="1.1" xmlns="http://www.w3.org/2000/svg">
<style>
#frames {
position: absolute;
right: 0px;
}
p {
border: 1px solid black;
box-size: border-box;
}
rect#bg {
fill: gray;
}
.tank rect {
fill: green;
transform: translate(-7.5px, -5px);
}
.tank circle {
fill: white;
}
#crosshair {
opacity: 0.5;
}
circle.bullet {
fill: yellow;
}
</style>
<rect id="bg" x="-200" y="-150" width="400" height="300"/>
<g>
<g class="hitbox">
<g class="tank">
<circle cx="0" cy="0" r="5"/>
<line x1="0" y1="0" x2="0" y2="8" stroke="black"/>
</g>
</g>
<rect id="rect1" x="30" y="30" width="20" height="20"/>
<g id="bullets"></g>
</g>
<foreignObject x="-200" y="-150" width="100%" height="100%">
<div id="frames" xmlns="http://www.w3.org/1999/xhtml">
<span id="fps" xmlns="http://www.w3.org/1999/xhtml">0</span> fps
</div>
<button id="turn-left" xmlns="http://www.w3.org/1999/xhtml">🡐</button>
<button id="move-backward" xmlns="http://www.w3.org/1999/xhtml">🡑</button>
<button id="move-forward" xmlns="http://www.w3.org/1999/xhtml">🡓</button>
<button id="turn-right" xmlns="http://www.w3.org/1999/xhtml">🡒</button>
<button id="rotate-ccw" xmlns="http://www.w3.org/1999/xhtml"></button>
<button id="rotate-cw" xmlns="http://www.w3.org/1999/xhtml"></button>
<button id="fire" xmlns="http://www.w3.org/1999/xhtml">Fire</button>
<pre id="debug" xmlns="http://www.w3.org/1999/xhtml"></pre>
</foreignObject>
<script type="text/javascript">//<![CDATA[
const namespaceURIsvg = 'http://www.w3.org/2000/svg';
const degsRegex = /(-?\d*\.{0,1}\d+)deg/g;
const regex = /(-?\d*\.{0,1}\d+)px/g;
const def = ["0px", "0"];
let bullets = [];
let position = [0, 0]; // meters
let velocity = [0, 0]; // meters per second
let acceleration = [0, 0]; // meters per second per second
let previous, zero, frameCount = 0;
// let friction = 7.5;
let friction = 0;
let rotate = 0;
let rotationSpeed = 0.25;
const maxSpeed = 100;
const rules = document.styleSheets[0].cssRules;
const fps = document.querySelector("#fps");
const info = document.querySelector("#debug");
const tank = document.querySelector(".tank");
const gun = tank.querySelector('line');
const hitbox = document.querySelector(".hitbox");
const bulletsContainer = document.querySelector("#bullets");
const r1 = document.querySelector("#rect1");
const leftTurnButton = document.querySelector("#turn-left");
const rightTurnButton = document.querySelector("#turn-right");
const reverseMoveButton = document.querySelector("#move-backward");
const forwardMoveButton = document.querySelector("#move-forward");
const rotateCWButton = document.querySelector("#rotate-cw");
const rotateCCWButton = document.querySelector("#rotate-ccw");
const fireButton = document.querySelector("#fire");
gun.style.transform = "rotate(0deg)";
function wrapPos(positionX, positionY) {
let x, y;
if (positionY > 150) y = positionY - 300;
else if (positionY < -150) y = positionY + 300;
else y = positionY;
if (positionX > 200) x = positionX - 400;
else if (positionX < -200) x = positionX + 400;
else x = positionX;
return [x, y];
}
function fireBullet(x, y, velocity) {
const degrees = getRotate(gun);
const radians = degrees * Math.PI / 180; // toFixed(15)?
const speed = 200; // meters per second
const vx = -Math.sin(radians);
const vy = Math.cos(radians);
const bulletTimeout = 5000; // miliseconds
const cannonLength = 8;
const el = document.createElementNS(namespaceURIsvg, 'circle');
el.classList.add('bullet');
el.setAttribute('r', 1);
el.setAttribute('cx', 0);
el.setAttribute('cy', 0);
const bullet = {
x: x + vx * cannonLength,
y: y + vy * cannonLength,
vx: vx * speed + velocity[0],
vy: vy * speed + velocity[1],
time: bulletTimeout,
node: bulletsContainer.appendChild(el)
}
bullets.push(bullet);
}
function getTranslate(el) {
let x, y;
if (el.style.transform.length === 0) {
x = 0;
y = 0;
} else {
[[, x], [, y] = def] = [...el.style.transform.matchAll(regex)];
}
return [+x, +y];
}
function getRotate(el) {
let [[, degrees]] = [...el.style.transform.matchAll(degsRegex)];
return +degrees;
}
requestAnimationFrame(firstFrame);
function firstFrame(timestamp) {
zero = timestamp;
previous = timestamp;
animate(timestamp);
}
function animate(timestamp) {
const delta = timestamp - zero;
const elapsed = timestamp - previous;
previous = timestamp;
if (delta >= 1000) {
fps.innerText = frameCount;
info.innerText = `velocity ${velocity}\n`
+ 'bullets\nx\ty\tvx\tvy\n'
+ bullets.map((b, index) => {
return `${b.x.toFixed(2)}\t${b.y.toFixed(2)}\t${b.vx.toFixed(2)}\t${b.vy.toFixed(2)}`;
}).join("\n");
zero = timestamp;
frameCount = 0;
} else {
frameCount++;
}
let [[, degrees]] = [...gun.style.transform.matchAll(degsRegex)];
if (rotate > 0) gun.style.transform = `rotate(${(+degrees + rotationSpeed * elapsed) % 360}deg)`;
else if (rotate < 0) gun.style.transform = `rotate(${(+degrees - rotationSpeed * elapsed) % 360}deg)`;
let [velocityX, velocityY] = velocity;
let [accelerationX, accelerationY] = acceleration;
if (velocityX > 0) accelerationX += -friction;
else if (velocityX < 0) accelerationX += friction;
if (velocityY > 0) accelerationY += -friction;
else if (velocityY < 0) accelerationY += friction;
velocityX = velocityX > 0 && velocityX + accelerationX < 0 ? 0 : velocityX + accelerationX;
velocityY = velocityY > 0 && velocityY + accelerationY < 0 ? 0 : velocityY + accelerationY;
velocity = [velocityX, velocityY];
if (velocity[0] > maxSpeed) velocity[0] = maxSpeed;
else if (velocity[0] < -maxSpeed) velocity[0] = -maxSpeed
if (velocity[1] > maxSpeed) velocity[1] = maxSpeed;
else if (velocity[1] < -maxSpeed) velocity[1] = -maxSpeed
const changeX = 0.001 * elapsed * velocityX;
const changeY = 0.001 * elapsed * velocityY;
bullets.forEach((bullet, index) => {
const deleteCount = 1;
bullets[index].time -= elapsed;
if (bullets[index].time > 0) {
bullets[index].y += 0.001 * elapsed * bullets[index].vy;
bullets[index].x += 0.001 * elapsed * bullets[index].vx;
let [bx, by] = wrapPos(bullets[index].x, bullets[index].y)
bullets[index].x = bx;
bullets[index].y = by;
bullet.node.style.transform = `translate(${bx}px, ${by}px)`;
} else {
bullet.node.remove();
bullets.splice(index, deleteCount);
}
});
let [x, y] = getTranslate(hitbox);
let positionX = changeX + +x;
let positionY = changeY + +y;
if (positionY > 150) positionY += -300;
else if (positionY < -150) positionY += 300;
if (positionX > 200) positionX += -400;
else if (positionX < -200) positionX += 400;
const radius = 5;
const pt = document.querySelector('svg').createSVGPoint();
pt.x = positionX;
pt.y = positionY;
if (r1.isPointInFill(pt))
r1.setAttribute('fill', 'red');
else
r1.setAttribute('fill', 'black');
hitbox.style.transform = `translate(${positionX}px, ${positionY}px)`;
// if (+y < 200)
// if (timestamp < 10000)
requestAnimationFrame(t => animate(t));
}
let force = 1;
let spacePressed = false;
let upPressed = false;
let downPressed = false;
let leftPressed = false;
let rightPressed = false;
let rotateCWPressed = false;
let rotateCCWPressed = false;
document.addEventListener("keydown", function(e) {
switch (e.code) {
case "Space":
if (!spacePressed) {
spacePressed = true;
const [x, y] = getTranslate(hitbox);
fireBullet(x, y, velocity);
}
break;
case "KeyW":
case "ArrowUp":
if (!upPressed) {
upPressed = true;
acceleration[1] += -force;
}
break;
case "KeyS":
case "ArrowDown":
if (!downPressed) {
downPressed = true;
acceleration[1] += force;
}
break;
case "KeyA":
case "ArrowLeft":
if (!leftPressed) {
leftPressed = true;
acceleration[0] += -force;
}
break;
case "KeyD":
case "ArrowRight":
if (!rightPressed) {
rightPressed = true;
acceleration[0] += force;
}
break;
case "KeyQ":
case "Comma":
if (!rotateCCWPressed) {
rotateCCWPressed = true;
rotate += -1;
}
break;
case "KeyE":
case "Period":
if (!rotateCWPressed) {
rotateCWPressed = true;
rotate += 1;
}
break;
}
});
document.addEventListener("keyup", function(e) {
switch (e.code) {
case "Space":
spacePressed = false;
break;
case "KeyW":
case "ArrowUp":
if (upPressed) {
upPressed = false;
acceleration[1] -= -force;
}
break;
case "KeyS":
case "ArrowDown":
if (downPressed) {
downPressed = false;
acceleration[1] -= force;
}
break;
case "KeyA":
case "ArrowLeft":
if (leftPressed) {
leftPressed = false;
acceleration[0] -= -force;
}
break;
case "KeyD":
case "ArrowRight":
if (rightPressed) {
rightPressed = false;
acceleration[0] -= force;
}
break;
case "KeyQ":
case "Comma":
if (rotateCCWPressed) {
rotateCCWPressed = false;
rotate -= -1;
}
break;
case "KeyE":
case "Period":
if (rotateCWPressed) {
rotateCWPressed = false;
rotate -= 1;
}
break;
}
});
leftTurnButton.addEventListener("mousedown", function (e) {
acceleration[0] = -force;
});
leftTurnButton.addEventListener("mouseup", function (e) {
acceleration[0] = 0;
});
rightTurnButton.addEventListener("mousedown", function (e) {
acceleration[0] = force;
});
rightTurnButton.addEventListener("mouseup", function (e) {
acceleration[0] = 0;
});
reverseMoveButton.addEventListener("mousedown", function (e) {
acceleration[1] = -force;
});
reverseMoveButton.addEventListener("mouseup", function (e) {
acceleration[1] = 0;
});
forwardMoveButton.addEventListener("mousedown", function (e) {
acceleration[1] = force;
});
forwardMoveButton.addEventListener("mouseup", function (e) {
acceleration[1] = 0;
});
rotateCWButton.addEventListener("mousedown", function (e) {
rotate = 1;
});
rotateCWButton.addEventListener("mouseup", function (e) {
rotate = 0;
});
rotateCCWButton.addEventListener("mousedown", function (e) {
rotate = -1;
});
rotateCCWButton.addEventListener("mouseup", function (e) {
rotate = 0;
});
fireButton.addEventListener("click", function (e) {
const [x, y] = getTranslate(hitbox);
fireBullet(x, y, velocity);
});
//]]></script>
</svg>