魚群シミュレーター
Why don't fish play basketball? Because they're afraid of the net!
<!DOCTYPE html>
<html>
<head>
<title>魚群シミュレーター</title>
<style>
body {
background-color: #0c5460;
color: #fff;
font-size: 1.2rem;
text-align: center;
}
canvas {
border: 1px solid #fff;
background-color: #007bff;
margin: 20px auto;
display: block;
}
</style>
</head>
<body>
<h1>魚群シミュレーター</h1>
<canvas id="canvas"></canvas>
<script>
const canvas = document.getElementById('canvas');
const context = canvas.getContext('2d');
const width = canvas.width = window.innerWidth;
const height = canvas.height = window.innerHeight;
const FISH_SIZE = 5;
const FISH_VELOCITY = 2;
const FISH_COLLISION_RADIUS = 10;
const BORDER_PADDING = 50;
let fishes = [];
function random(min, max) {
return Math.random() * (max - min) + min;
}
function createFish() {
let x = random(BORDER_PADDING, width - BORDER_PADDING);
let y = random(BORDER_PADDING, height - BORDER_PADDING);
let angle = random(0, Math.PI * 2);
let dx = Math.cos(angle) * FISH_VELOCITY;
let dy = Math.sin(angle) * FISH_VELOCITY;
return {x, y, dx, dy};
}
function init() {
for (let i = 0; i < 50; i++) {
fishes.push(createFish());
}
}
function drawFish(fish) {
context.save();
context.translate(fish.x, fish.y);
context.rotate(Math.atan2(fish.dy, fish.dx) + Math.PI);
context.beginPath();
context.moveTo(-FISH_SIZE * 0.5, FISH_SIZE * 0.5);
context.lineTo(FISH_SIZE * 0.5, FISH_SIZE * 0.5 - 2);
context.lineTo(FISH_SIZE * 0.5, FISH_SIZE * 0.5 - 4);
context.lineTo(FISH_SIZE * 0.5 + 4, 0);
context.lineTo(FISH_SIZE * 0.5, -FISH_SIZE * 0.5 + 4);
context.lineTo(FISH_SIZE * 0.5, -FISH_SIZE * 0.5 + 2);
context.lineTo(-FISH_SIZE * 0.5, -FISH_SIZE * 0.5);
context.closePath();
context.fillStyle = '#fff';
context.fill();
context.restore();
}
function updateFish(fish) {
let alignment = {x: 0, y: 0};
let cohesion = {x: 0, y: 0};
let separation = {x: 0, y: 0};
let neighbors = 0;
for (let other of fishes) {
if (other === fish) continue;
let distance = Math.sqrt((fish.x - other.x) ** 2 + (fish.y - other.y) ** 2);
if (distance < FISH_COLLISION_RADIUS) {
separation.x += (fish.x - other.x) / distance;
separation.y += (fish.y - other.y) / distance;
}
if (distance < FISH_COLLISION_RADIUS * 2) {
cohesion.x += other.x;
cohesion.y += other.y;
alignment.x += other.dx;
alignment.y += other.dy;
neighbors++;
}
}
if (neighbors > 0) {
cohesion.x /= neighbors;
cohesion.y /= neighbors;
cohesion.x = (cohesion.x - fish.x) / 100;
cohesion.y = (cohesion.y - fish.y) / 100;
alignment.x /= neighbors;
alignment.y /= neighbors;
alignment.x -= fish.dx;
alignment.y -= fish.dy;
alignment.x /= 8;
alignment.y /= 8;
}
fish.dx += alignment.x + cohesion.x + separation.x;
fish.dy += alignment.y + cohesion.y + separation.y;
fish.dx = Math.min(Math.max(fish.dx, -FISH_VELOCITY), FISH_VELOCITY);
fish.dy = Math.min(Math.max(fish.dy, -FISH_VELOCITY), FISH_VELOCITY);
fish.x += fish.dx;
fish.y += fish.dy;
if (fish.x < BORDER_PADDING) {
fish.dx += FISH_VELOCITY;
} else if (fish.x > width - BORDER_PADDING) {
fish.dx -= FISH_VELOCITY;
}
if (fish.y < BORDER_PADDING) {
fish.dy += FISH_VELOCITY;
} else if (fish.y > height - BORDER_PADDING) {
fish.dy -= FISH_VELOCITY;
}
}
function render() {
context.clearRect(0, 0, width, height);
for (let fish of fishes) {
updateFish(fish);
drawFish(fish);
}
window.requestAnimationFrame(render);
}
init();
render();
</script>
<hr>
<p>Why don't fish play basketball? Because they're afraid of the net!</p>
</body>
</html>