Skip to content

Instantly share code, notes, and snippets.

@tosik
Created March 15, 2023 16:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tosik/dfe15fb58d3db34c7ce190ccea49aa94 to your computer and use it in GitHub Desktop.
Save tosik/dfe15fb58d3db34c7ce190ccea49aa94 to your computer and use it in GitHub Desktop.
const canvas = document.getElementById('game');
const ctx = canvas.getContext('2d');
const tileSize = 10;
const numRows = canvas.height / tileSize;
const numCols = canvas.width / tileSize;
const numEnemies = 5; // お好みの敵の数に設定
const rows = 10;
const cols = 10;
const cellSize = 32;
let grid;
function createGrid() {
grid = new Array(rows);
for (let i = 0; i < rows; i++) {
grid[i] = new Array(cols);
for (let j = 0; j < cols; j++) {
// セルのタイプを表す数値を設定します。例えば、0 は空き、1 は壁など
// ここでは、すべてのセルを空き(0)に設定していますが、必要に応じて異なる値を設定できます。
grid[i][j] = 0;
}
}
}
// グリッドの初期化
createGrid();
class Player {
constructor(x, y) {
this.x = x;
this.y = y;
}
move(dx, dy) {
this.x += dx;
this.y += dy;
}
draw() {
ctx.fillStyle = 'blue';
ctx.fillRect(this.x * tileSize, this.y * tileSize, tileSize, tileSize);
}
}
const player = new Player(Math.floor(numCols / 2), Math.floor(numRows / 2));
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
player.draw();
}
function handleKeyPress(e) {
const key = e.key;
let moved = false;
if (key === "ArrowUp") {
moved = tryMovePlayer(0, -1);
} else if (key === "ArrowDown") {
moved = tryMovePlayer(0, 1);
} else if (key === "ArrowLeft") {
moved = tryMovePlayer(-1, 0);
} else if (key === "ArrowRight") {
moved = tryMovePlayer(1, 0);
}
if (moved) {
updateEnemies(grid); // ここで敵を更新
}
}
function tryMovePlayer(dx, dy) {
const newX = player.x + dx;
const newY = player.y + dy;
if (canMoveTo(newX, newY)) {
player.x = newX;
player.y = newY;
return true;
}
return false;
}
class Cell {
constructor(x, y) {
this.x = x;
this.y = y;
this.visited = false;
this.walls = {
top: true,
bottom: true,
left: true,
right: true
};
}
draw() {
ctx.strokeStyle = 'white';
if (this.walls.top) {
ctx.beginPath();
ctx.moveTo(this.x * tileSize, this.y * tileSize);
ctx.lineTo((this.x + 1) * tileSize, this.y * tileSize);
ctx.stroke();
}
if (this.walls.bottom) {
ctx.beginPath();
ctx.moveTo(this.x * tileSize, (this.y + 1) * tileSize);
ctx.lineTo((this.x + 1) * tileSize, (this.y + 1) * tileSize);
ctx.stroke();
}
if (this.walls.left) {
ctx.beginPath();
ctx.moveTo(this.x * tileSize, this.y * tileSize);
ctx.lineTo(this.x * tileSize, (this.y + 1) * tileSize);
ctx.stroke();
}
if (this.walls.right) {
ctx.beginPath();
ctx.moveTo((this.x + 1) * tileSize, this.y * tileSize);
ctx.lineTo((this.x + 1) * tileSize, (this.y + 1) * tileSize);
ctx.stroke();
}
}
}
function createMaze() {
const maze = new Array(numRows);
for (let i = 0; i < numRows; i++) {
maze[i] = new Array(numCols);
for (let j = 0; j < numCols; j++) {
maze[i][j] = new Cell(j, i);
}
}
return maze;
}
function removeWall(cell1, cell2) {
const dx = cell1.x - cell2.x;
const dy = cell1.y - cell2.y;
if (dx === 1) {
cell1.walls.left = false;
cell2.walls.right = false;
} else if (dx === -1) {
cell1.walls.right = false;
cell2.walls.left = false;
}
if (dy === 1) {
cell1.walls.top = false;
cell2.walls.bottom = false;
} else if (dy === -1) {
cell1.walls.bottom = false;
cell2.walls.top = false;
}
}
function generateMaze(maze, currentCell) {
currentCell.visited = true;
while (true) {
const neighbors = [];
const top = maze[currentCell.y - 1]?.[currentCell.x];
const bottom = maze[currentCell.y + 1]?.[currentCell.x];
const left = maze[currentCell.y]?.[currentCell.x - 1];
const right = maze[currentCell.y]?.[currentCell.x + 1];
if (top && !top.visited) {
neighbors.push(top);
}
if (bottom && !bottom.visited) {
neighbors.push(bottom);
}
if (left && !left.visited) {
neighbors.push(left);
}
if (right && !right.visited) {
neighbors.push(right);
}
if (neighbors.length === 0) {
break;
}
const nextCell = neighbors[Math.floor(Math.random() * neighbors.length)];
removeWall(currentCell, nextCell);
generateMaze(maze, nextCell);
}
}
function drawMaze(maze) {
for (let y = 0; y < numRows; y++) {
for (let x = 0; x < numCols; x++) {
if (maze[y][x] === 1) {
ctx.fillStyle = 'black';
ctx.fillRect(x * tileSize, y * tileSize, tileSize, tileSize);
}
}
}
}
let maze = createMaze();
generateMaze(maze, maze[0][0]);
function canMoveTo(x, y) {
if (x < 0 || x >= numCols || y < 0 || y >= numRows) {
return false;
}
return dungeon[y][x] === 0;
}
player.move = function (dx, dy) {
const newX = this.x + dx;
const newY = this.y + dy;
if (canMoveTo(newX, newY)) {
this.x = newX;
this.y = newY;
}
};
class MinHeap {
constructor() {
this.heap = [null];
}
insert(node) {
this.heap.push(node);
let currentIndex = this.heap.length - 1;
while (
currentIndex > 1 &&
this.heap[Math.floor(currentIndex / 2)].f > this.heap[currentIndex].f
) {
[this.heap[Math.floor(currentIndex / 2)], this.heap[currentIndex]] = [
this.heap[currentIndex],
this.heap[Math.floor(currentIndex / 2)],
];
currentIndex = Math.floor(currentIndex / 2);
}
}
remove() {
if (this.heap.length === 1) return null;
if (this.heap.length === 2) return this.heap.pop();
const min = this.heap[1];
this.heap[1] = this.heap.pop();
let currentIndex = 1;
let leftChildIndex = currentIndex * 2;
let rightChildIndex = currentIndex * 2 + 1;
while (
(this.heap[leftChildIndex] &&
this.heap[currentIndex].f > this.heap[leftChildIndex].f) ||
(this.heap[rightChildIndex] &&
this.heap[currentIndex].f > this.heap[rightChildIndex].f)
) {
let smallerChildIndex =
this.heap[rightChildIndex] &&
this.heap[leftChildIndex].f > this.heap[rightChildIndex].f
? rightChildIndex
: leftChildIndex;
[this.heap[currentIndex], this.heap[smallerChildIndex]] = [
this.heap[smallerChildIndex],
this.heap[currentIndex],
];
currentIndex = smallerChildIndex;
leftChildIndex = currentIndex * 2;
rightChildIndex = currentIndex * 2 + 1;
}
return min;
}
}
function coordToString(coord) {
return `${coord.x},${coord.y}`;
}
function heuristic(a, b) {
return Math.abs(a.x - b.x) + Math.abs(a.y - b.y);
}
class PriorityQueue {
constructor() {
this.elements = [];
}
insert(item) {
this.elements.push(item);
this.elements.sort((a, b) => a.f - b.f);
}
pop() {
return this.elements.shift();
}
isEmpty() {
return this.elements.length === 0;
}
}
function getNeighbors(node, grid) {
const neighbors = [];
const { x, y } = node;
if (x > 0) neighbors.push(grid[y][x - 1]);
if (x < grid[0].length - 1) neighbors.push(grid[y][x + 1]);
if (y > 0) neighbors.push(grid[y - 1][x]);
if (y < grid.length - 1) neighbors.push(grid[y + 1][x]);
return neighbors;
}
function aStarSearch(startNode, endNode, grid) {
const openSet = new PriorityQueue();
openSet.insert({ ...startNode, g: 0, f: heuristic(startNode, endNode) });
console.log("Inserted start node:", { ...startNode, g: 0, f: heuristic(startNode, endNode) });
const cameFrom = {};
const gScore = {};
gScore[coordToString(startNode)] = 0;
while (openSet.length > 0) {
const currentNode = openSet.shift();
console.log("Current node:", currentNode);
console.log("Open set:", openSet);
if (coordToString(currentNode) === coordToString(endNode)) {
const path = reconstructPath(cameFrom, current);
console.log("Path found:", path);
return path;
}
const neighbors = getNeighbors(currentNode, grid);
console.log("Neighbors:", neighbors);
for (const neighbor of neighbors) {
const neighborKey = coordToString(neighbor);
const tentativeGScore = gScore[coordToString(current)] + 1;
if (!(neighborKey in gScore) || tentativeGScore < gScore[neighborKey]) {
cameFrom[neighborKey] = current;
gScore[neighborKey] = tentativeGScore;
openSet.insert({
...neighbor,
g: tentativeGScore,
f: tentativeGScore + heuristic(neighbor, endNode),
});
}
}
}
return null;
}
class Enemy {
constructor(x, y) {
this.x = x;
this.y = y;
this.path = [];
}
draw() {
ctx.fillStyle = 'red';
ctx.fillRect(this.x * tileSize, this.y * tileSize, tileSize, tileSize);
}
update(grid, player) {
console.log("Updating enemy position");
const start = { x: this.x, y: this.y };
const goal = { x: player.x, y: player.y };
const path = aStarSearch(start, goal, grid);
if (path && path.length > 1) {
this.x = path[1].x;
this.y = path[1].y;
}
}
}
const ROOM_MIN_SIZE = 3;
const ROOM_MAX_SIZE = 9;
const MAX_ROOMS = 15;
function createDungeon() {
const dungeon = new Array(numRows);
for (let i = 0; i < numRows; i++) {
dungeon[i] = new Array(numCols).fill(1);
}
return dungeon;
}
function createRoom() {
const width = Math.floor(Math.random() * (ROOM_MAX_SIZE - ROOM_MIN_SIZE + 1)) + ROOM_MIN_SIZE;
const height = Math.floor(Math.random() * (ROOM_MAX_SIZE - ROOM_MIN_SIZE + 1)) + ROOM_MIN_SIZE;
const x = Math.floor(Math.random() * (numCols - width - 1)) + 1;
const y = Math.floor(Math.random() * (numRows - height - 1)) + 1;
return { x, y, width, height };
}
function isRoomOverlap(roomA, roomB) {
return (
roomA.x < roomB.x + roomB.width + 1 &&
roomA.x + roomA.width + 1 > roomB.x &&
roomA.y < roomB.y + roomB.height + 1 &&
roomA.y + roomA.height + 1 > roomB.y
);
}
function carveRoom(dungeon, room) {
for (let y = room.y; y < room.y + room.height; y++) {
for (let x = room.x; x < room.x + room.width; x++) {
dungeon[y][x] = 0;
}
}
}
function carveTunnel(dungeon, startPoint, endPoint) {
const [x1, y1] = startPoint;
const [x2, y2] = endPoint;
let x = x1;
let y = y1;
while (x !== x2) {
x += Math.sign(x2 - x);
dungeon[y][x] = 0;
}
while (y !== y2) {
y += Math.sign(y2 - y);
dungeon[y][x] = 0;
}
}
function generateDungeon(dungeon) {
const rooms = [];
for (let i = 0; i < MAX_ROOMS; i++) {
const newRoom = createRoom();
let overlap = false;
for (const room of rooms) {
if (isRoomOverlap(newRoom, room)) {
overlap = true;
break;
}
}
if (!overlap) {
carveRoom(dungeon, newRoom);
rooms.push(newRoom);
}
}
for (let i = 1; i < rooms.length; i++) {
const prevRoomCenter = {
x: Math.floor(rooms[i - 1].x + rooms[i - 1].width / 2),
y: Math.floor(rooms[i - 1].y + rooms[i - 1].height / 2),
};
const currentRoomCenter = {
x: Math.floor(rooms[i].x + rooms[i].width / 2),
y: Math.floor(rooms[i].y + rooms[i].height / 2),
};
if (Math.random() < 0.5) {
carveTunnel(dungeon, [prevRoomCenter.x, prevRoomCenter.y], [currentRoomCenter.x, currentRoomCenter.y]);
} else {
carveTunnel(dungeon, [currentRoomCenter.x, currentRoomCenter.y], [prevRoomCenter.x, prevRoomCenter.y]);
}
}
return rooms;
}
const dungeon = createDungeon();
const rooms = generateDungeon(dungeon);
// 初期プレイヤー位置を最初の部屋の中央に設定
player.x = Math.floor(rooms[0].x + rooms[0].width / 2);
player.y = Math.floor(rooms[0].y + rooms[0].height / 2);
maze = dungeon;
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawMaze(maze);
drawEnemies(); // 敵を描画する関数を追加
player.draw();
}
function spawnEnemies(rooms) {
const enemies = [];
for (let i = 0; i < numEnemies; i++) {
const randomRoomIndex = Math.floor(Math.random() * rooms.length);
const randomRoom = rooms[randomRoomIndex];
const x = Math.floor(randomRoom.x + Math.random() * randomRoom.width);
const y = Math.floor(randomRoom.y + Math.random() * randomRoom.height);
enemies.push(new Enemy(x, y));
}
return enemies;
}
// 敵を生成
let enemies = spawnEnemies(rooms);
function drawEnemies() {
enemies.forEach(enemy => enemy.draw());
}
function updateEnemies(grid) {
enemies.forEach(enemy => enemy.update(grid, player));
}
function moveEnemyTowardPlayer(enemy) {
const dx = Math.sign(player.x - enemy.x);
const dy = Math.sign(player.y - enemy.y);
const newX = enemy.x + dx;
const newY = enemy.y + dy;
if (canMoveTo(newX, newY)) {
enemy.x = newX;
enemy.y = newY;
}
}
function gameLoop() {
draw();
requestAnimationFrame(gameLoop);
}
document.removeEventListener('keydown', handleKeyPress);
document.addEventListener('keydown', (event) => {
handleKeyPress(event);
draw();
});
gameLoop();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment