<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="./style.css">
<title>Document</title>
</head>
<body>
<canvas id="canvas"></canvas>
<script src="./script.js/astar.js"></script>
</body>
</html>
#canvas {
display: block;
margin: 0 auto;
}
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
canvas.width = 1024;//32*96;
canvas.height = 600;//32*60;
let tileSize = 32;
let imageNumTiles = 2;
let x_offs = 0;// координаты смещения карты
let y_offs = 0;
const objectShip = new Image();
const backgraund = new Image();
objectShip.src = 'https://i.postimg.cc/VLz6kQt2/object-Ship.png';
backgraund.src = 'https://i.postimg.cc/bJLQsNsF/backgraund.png';
// инициализация массива с непроходимыми клетками заливаем все проходимым и прописываем только непроходимые
const points = {
0: [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,43,56,75,88],
1: [24,43,56,75,88], 2: [11,24,43,56,75,88],
3: [11,24,43,56,75,88], 4: [11,24,43,56,75,88], 5: [11,24,43,56,75,88],
6: [11,24,43,56,75,88], 7: [11,24,43,56,75,88], 8: [11,24,43,56,75,88],
9: [11,24,43,56,75,88], 10: [11,24,43,56,75,88], 11: [11,24,43,56,75,88],
12: [11,24,43,56,75,88], 13: [11,24,43,56,75,88], 14: [11,24,43,56,75,88],
15: [11,24,43,56,75,88], 16: [11,24,43,56,75,88], 17: [11,24,43,56,75,88],
18: [11,24,43,56,75,88], 19: [11,24,43,56,75,88], 20: [11,24,43,56,75,88],
21: [11,24,43,56,75,88], 22: [11,24,43,56,75,88], 23: [11,24,43,56,75,88],
24: [11,24,43,56,75,88], 25: [11,24,43,56,75,88], 26: [11,24,43,56,75,88],
27: [11,24,43,56,75,88], 28: [11,24,43,56,75,88], 29: [11,24,43,56,75,88],
30: [11,24,43,56,75,88], 31: [11,24,43,56,75,88], 32: [11,24,43,56,75,88],
33: [11,24,43,56,75,88], 34: [11,24,43,56,75,88], 36: [11,24,43,56,75,88],
37: [11,24,43,56,75,88], 38: [11,24,43,56,75,88], 39: [11,24,43,56,75,88],
40: [11,24,43,56,75,88], 42: [11,24,43,56,75,88], 43: [11,24,43,56,75,88],
44: [11,24,43,56,75,88], 45: [11,24,43,56,75,88], 46: [11,24,43,56,75,88],
47: [11,24,43,56,75,88], 48: [11,24,43,56,75,88], 49: [11,24,43,56,75,88],
50: [11,24,43,56,75,88], 51: [11,24,43,56,75,88], 52: [11,24,43,56,75,88],
53: [11,24,43,56,75,88], 54: [11,24,43,56,75,88], 55: [11,24,43,56,75,88],
56: [11,24,43,56,75,88], 57: [11,24,43,56,75,88], 58: [11,24,43,56,75,88],
59: [11,24,43,56,75,88]
};
const a = Array(60)
.fill()
.map((_, y) => {
const row = Array(96).fill(0);
if (points[y] !== undefined) {
points[y].forEach(p => row[p] = 1);
}
return row;
});
// стартовое и финишное положение игрока
let sx=16,sy=10,ex=16,ey=10;
// =============================================================================================
let way = []; //массив просчитанного пути
let wx = 0,wy = 0;
let oex = -1,oey = -1,clicknum = 1;
// клетки на карте с шагом в 32 пикселя
const step = {
grid(n) {
return n / 32
}
};
// проверка на старт с непроходимым обьектом под кораблем ========================
function BadStartEnd()
{
if (a[sy][sx]==1) return 1;
if (a[ey][ex]==1) return 1;
return 0;
}
// вычисления дистанции пути с учетом диагонали или горизонталь/вертикаль ==============================
function Dist(x1,x2,y1,y2)
{
let dx = Math.abs(x1-x2);
let dy = Math.abs(y1-y2);
if (dx>dy)
{
return dy*14+(dx-dy)*10;
}
else
{
return dx*14+(dy-dx)*10;
}
}
function Calc() {
if (BadStartEnd()===1) {
return;
}
for (let i=0;i<60;i++)
{
for (let j=0;j<96;j++)
{
if (a[i][j]>1) {a[i][j]=0;}
}
}
a[sy][sx] = Dist(sx,ex,sy,ey)*100+10;
var b = true,i = 0;
while ((b)&&(i<300))
{
b = AlgStep();
}
RecalcA();
b = true,i = 0;
while ((b)&&(i<300))
{
b = AlgStepWay();
}
if (clicknum == 1)
{
if ((oex==ex)&&(oey==ey))
clicknum = 2;
}
if (clicknum === 2)
{
//sx = ex;
//sy = ey;
}
oex = ex;
oey = ey;
}
function AlgStep()
{
minv=3072, xminv=-1, yminv = -1;
for (let i=0;i<60;i++)
{
for (let j=0;j<96;j++)
{
if ( (a[i][j] % 10 == 0) && (Math.floor(a[i][j]/10) % 10 == 1) )
{//поиск минимального значения среди открытых вершин не препятствий
let temp = Math.floor(a[i][j]/100);
if (temp<minv)
{
yminv = i;
xminv = j;
minv = temp;
}
}
}
}
if (minv === 3072)
{
return false;
}
let af0=Math.floor(a[yminv][xminv]/100);
let ah0 = Dist(xminv,ex,yminv,ey);
let ag0 = af0 - ah0;
//2. open/close для этой точки ищем соседей не препятствие у которых пересчитаваем массив
for (let i=-1;i<=1;i++)
{
for (let j=-1;j<=1;j++)
{
if ((xminv+j>=0)&&(xminv+j<96)&&(yminv+i>=0)&&(yminv+i<60)&&(i*i+j*j>0))
{
let temp = a[yminv+i][xminv+j];
if ( (Math.floor(temp/10) % 10 < 2) && (temp % 10 == 0) )
{
let incr = 10;
if (i*i+j*j==2)
{
incr = 14;
}
let agtmp = ag0 + incr;
let aftmp = agtmp + Dist(xminv+j,ex,yminv+i,ey);
if ( (temp==0)||(temp>aftmp*100) )
{
a[yminv+i][xminv+j] = aftmp*100+10+0;
}
}
}
}
}
a[yminv][xminv]+=10;
if ((yminv==ey)&&(xminv==ex))
{
return false;
}
return true;
}
function RecalcA()
{
for (let i=0;i<60;i++)
{
for (let j=0;j<96;j++)
{
if (a[i][j]==1)
{
a[i][j]=1;
}
else if (Math.floor(a[i][j]/10)%10<2)
{
a[i][j]=0;
}
else
{
a[i][j]=a[i][j]-100*Dist(ex,j,ey,i)+10000;
}
}
}
//Путешествуем с финиша, находим клетку где минимальное значение но с учетом равенства по соседям
way = []; //обнуление массива
let tmp = {};
wx=ex;
wy=ey;
tmp.x = wx;
tmp.y = wy;
way.push(tmp);
}
function AlgStepWay()
{
let d = 0,itmp = 0, jtmp = 0, dtmp = 0;
for (let i=-1;i<=1;i++) for (let j=-1;j<=1;j++) if (i*i+j*j>0)
{
if (i*i+j*j==1) d=10;
if (i*i+j*j==2) d=14;
if ((a[wy+i][wx+j]>100)&&(a[wy+i][wx+j]+d*100==a[wy][wx])&&(dtmp<d))
{
itmp = i;
jtmp = j;
dtmp = d;
}
}
wx+=jtmp;
wy+=itmp;
let tmp = {};
tmp.x = wx;
tmp.y = wy;
way.push(tmp);
if ((wx==sx)&&(wy==sy))
return false;
else
return true;
}
canvas.onmousedown = function(e) {
let rect = this.getBoundingClientRect();
x = e.clientX - rect.left ;
y = e.clientY - rect.top ;
if (clicknum===1)
{
ex = Math.floor(step.grid(x))+x_offs;
ey = Math.floor(step.grid(y))+y_offs;
Calc();
}
};
// сдвиг карты кнопками
document.body.onkeydown = function(e) {
if (e.key=='ArrowLeft') x_offs-=3;
if (e.code=='ArrowRight') x_offs+=3;
if (e.code=='ArrowUp') y_offs-=3;
if (e.code=='ArrowDown') y_offs+=3;
if (x_offs<0) x_offs = 0;
if (y_offs<0) y_offs = 0;
if (x_offs>80) x_offs = 80;
if (y_offs>50) y_offs = 50;
};
// отрисовка объектов на канвасе ======================================================================================
function render() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.strokeStyle = "rgb(255, 255, 0)";
//1024/32=32, 640/32=20
for (let i = 0; i < a.length; i++) {
for (let j = 0; j < a[i].length; j++) {
let tile = a[i][j]%10;
let tileRow = (tile / imageNumTiles) | 0; // Операция "побитовое ИЛИ"
let tileCol = (tile % imageNumTiles) | 0;
ctx.drawImage(backgraund, tileCol * tileSize, tileRow * tileSize, tileSize, tileSize, j * tileSize - x_offs*32 , i * tileSize - y_offs*32 , tileSize, tileSize);
}
}
// если путь просчитан, то обрисовать желтым===================================================================
if ((way.length>0)&&(ex===oex)&&(ey===oey))
{
for (let i=0;i<way.length;i++) {
ctx.beginPath();
ctx.arc((way[i].x-x_offs)*32+16,(way[i].y-y_offs)*32+16,7,0,Math.PI*2)
ctx.stroke();
ctx.fillStyle = "rgba(255, 255, 255, 0.3)";
ctx.arc((way[i].x-x_offs)*32+16,(way[i].y-y_offs)*32+16,5,0,Math.PI*2)
ctx.fill();
}
}
//if (clicknum === 1) {
ctx.drawImage(objectShip, (sx-x_offs)*32, (sy-y_offs)*32, 32, 32);
//}
//else {ctx.drawImage(objectShip, (delWay.x-x_offs)*32, (delWay.y-y_offs)*32, 32, 32);}
};
// обновление объектов на канвасе ======================================================================================
//Получить угол между двумя точками
function getAngle(dx, dy, dx1, dy1) {
return Math.atan2(dy - dy1, dx - dx1) + Math.PI;
}
const update = function() {
if (way.length == 0) {
clicknum = 1;
}
if(clicknum == 2 && way.length > 0){
let stepLen = 0.1;//ширина шага за итеарция, чем меньше тем дольше будет идти
let stepCur = stepLen;
while(way.length > 0 && stepCur > 0){
let nx = way[way.length-1].x;//следующая точка куда идти
let ny = way[way.length-1].y;
let dist = Math.hypot(sx-nx,sy-ny);//Получить расстояние между игроком и следующей точкой
if(dist <= stepCur){
stepCur -=dist;
way.pop()
sx = nx;
sy = ny;
}
else{
let angle = getAngle(sx,sy,nx,ny);
sx+= stepCur * Math.cos(angle);
sy+= stepCur * Math.sin(angle);
break;
}
}
x_offs = Math.max(0,sx-16);
y_offs = Math.max(0,sy-10);
if (x_offs>80) x_offs = 80;
if (y_offs>50) y_offs = 50;
}
};
// функция игрового цикла, замкнутая петля в канвас ============================================================
let game = function() {
render();
update();
requestAnimationFrame(game);
};
game();
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.