Skip to content

Instantly share code, notes, and snippets.

@EncodeTheCode
Created June 27, 2026 01:54
Show Gist options
  • Select an option

  • Save EncodeTheCode/0b29444a709e393f1db82bcdccc08bd1 to your computer and use it in GitHub Desktop.

Select an option

Save EncodeTheCode/0b29444a709e393f1db82bcdccc08bd1 to your computer and use it in GitHub Desktop.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Tank Physics System</title>
<style>
body { margin:0; overflow:hidden; background:#111; }
canvas { display:block; }
</style>
</head>
<body>
<canvas id="game"></canvas>
<script>
const canvas = document.getElementById("game");
const ctx = canvas.getContext("2d");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
/* =========================
INPUT
========================= */
const keys = {};
const mouse = { x: 0, y: 0 };
window.addEventListener("keydown", e => keys[e.key.toLowerCase()] = true);
window.addEventListener("keyup", e => keys[e.key.toLowerCase()] = false);
window.addEventListener("mousemove", e => {
mouse.x = e.clientX;
mouse.y = e.clientY;
});
/* =========================
ANGLE LERP (important)
========================= */
function lerpAngle(a, b, t) {
const diff = Math.atan2(Math.sin(b - a), Math.cos(b - a));
return a + diff * t;
}
/* =========================
SPRITE LAYER
========================= */
class SpriteLayer {
constructor(src, offsetX = 0, offsetY = 0) {
this.img = new Image();
this.img.src = src;
this.offsetX = offsetX;
this.offsetY = offsetY;
this.rotation = 0;
this.scale = 1;
this.isTurret = false;
}
draw(ctx, x, y, parentRotation = 0) {
if (!this.img.complete) return;
ctx.save();
ctx.translate(x, y);
ctx.rotate(parentRotation + this.rotation);
ctx.drawImage(
this.img,
this.offsetX - this.img.width / 2,
this.offsetY - this.img.height / 2,
this.img.width * this.scale,
this.img.height * this.scale
);
ctx.restore();
}
}
/* =========================
GAME OBJECT (TANK)
========================= */
class GameObject {
constructor(x, y, playerId = 1) {
this.x = x;
this.y = y;
this.vx = 0;
this.vy = 0;
// Movement tuning
this.acceleration = 0.5;
this.maxSpeed = 6;
this.friction = 0.61;
this.gravity = 0.0;
// Rotation tuning (IMPORTANT)
this.rotation = 0;
this.turnSpeed = 0.01; // body turning speed
this.velocityRotationThreshold = 0.001;
this.health = 100;
this.playerId = playerId;
this.layers = [];
this.turretFollowMouse = false;
}
addLayer(layer) {
this.layers.push(layer);
return layer;
}
update() {
/* =========================
PLAYER 1 CONTROLS (WASD)
========================= */
if (this.playerId === 1) {
if (keys["w"]) this.vy -= this.acceleration;
if (keys["s"]) this.vy += this.acceleration;
if (keys["a"]) this.vx -= this.acceleration;
if (keys["d"]) this.vx += this.acceleration;
// clamp speed
this.vx = Math.max(-this.maxSpeed, Math.min(this.maxSpeed, this.vx));
this.vy = Math.max(-this.maxSpeed, Math.min(this.maxSpeed, this.vy));
/* =========================
TURRET AIM (MOUSE)
========================= */
if (this.turretFollowMouse) {
const dx = mouse.x - this.x;
const dy = mouse.y - this.y;
const angle = Math.atan2(dy, dx);
const turret = this.layers.find(l => l.isTurret);
if (turret) turret.rotation = angle;
}
}
/* =========================
PHYSICS
========================= */
this.vx *= this.friction;
this.vy *= this.friction;
this.vy += this.gravity;
this.x += this.vx;
this.y += this.vy;
/* =========================
BODY ROTATION TOWARD MOVEMENT
========================= */
const speed = Math.hypot(this.vx, this.vy);
if (speed > this.velocityRotationThreshold) {
const targetAngle = Math.atan2(this.vy, this.vx);
this.rotation = lerpAngle(
this.rotation,
targetAngle,
this.turnSpeed
);
}
}
draw(ctx) {
for (let layer of this.layers) {
layer.draw(ctx, this.x, this.y, this.rotation);
}
}
}
/* =========================
GAME SETUP
========================= */
const objects = [];
/* Tank */
const tank = new GameObject(400, 300, 1);
// body
const body = new SpriteLayer("https://i.imgur.com/4YfKX5c.png");
tank.addLayer(body);
// turret
const turret = new SpriteLayer("https://i.imgur.com/5cX1S0m.png");
turret.isTurret = true;
tank.addLayer(turret);
// enable mouse turret
tank.turretFollowMouse = true;
objects.push(tank);
/* =========================
GAME LOOP
========================= */
function loop() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (let obj of objects) obj.update();
for (let obj of objects) obj.draw(ctx);
requestAnimationFrame(loop);
}
loop();
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment