Saya mencoba membuat game kanvas 2d di javascript dan nodejs. Dalam permainan pemain adalah kapal yang dapat menavigasi jalannya di laut. Objek kapal adalah sprite sheet dengan 16 posisi kapal (22,5 derajat untuk setiap frame) mulai dari 0 sampai 360 derajat.

Untuk menavigasi kapal saya menggunakan NippleJS, itu memberi saya derajat 0-360 (misalnya: 328.9051138238949).

Sekarang, saya membutuhkan algoritma yang memeriksa sprite mana dari 16 yang harus ditampilkan setiap saat.

Kapal harus menggerakkan 1 sprite kiri atau kanan di setiap derajat pemeriksaan dan harus jalan terbaik (cara terbaik berarti jika derajat kapal 0 dan derajat dari Nipplejs adalah 335 kapal harus menavigasi ke kanan bukan ke kiri ..).

Saya mulai memikirkannya dan saya mengerti bahwa saya perlu menampilkan bingkai gambar jika derajatnya antara -(22,5/2) dan (22,5/2) tetapi saya mendapat banyak masalah ketika saya lebih dari 360 dan lebih banyak hal..

Untuk memahami satu sama lain mari kita sebut derajat kapal ship.degree dan derajat dari Nipplejs kita sebut nipplejs.degree, dan sprite kapal akan diwakili oleh ship.sprite[0-16] (0 = arah kanan).

Tolong bantu.

circle 360 degrees

0
Rafael 29 Oktober 2019, 09:51

1 menjawab

Jawaban Terbaik

Untuk membelokkan sudut terkecil, fungsi di bawah ini akan mengubah sudut dengan jumlah langkah angleMoveSteps per 360 derajat. Misalnya 90 langkah akan melangkah dalam langkah 4 derajat.

Ini akan berhenti melangkah jika perbedaan sudut kurang dari langkah ini.

const angleSteps = 16;
const angleMoveSteps = 90;

function getNextAngle(newDirection, currentDirection) {
    // normalize both directions to 360 
    newDirection %= 360;
    currentDirection %= 360;

    // if new direction is less than current move it ahead by 360
    newDirection = newDirection < currentDirection ? newDirection + 360 : newDirection;

    // get the difference (will always be positive)
    const dif = newDirection - currentDirection;

    // if the difference is greater than 180 and less than 360 - step
    // turn CCW
    if (dif > 360 / 2 && dif < 360 - (360 / angleMoveSteps)) {
         return currentDirection - (360 / angleMoveSteps);
    }

    // if the difference is greater than step and less than 180
    // turn CW
    if (dif > (360 / angleMoveSteps) && dif < 360 / 2){
        return currentDirection + (360 / angleMoveSteps);
    }

    // do nothing if within step angle of target
     return currentDirection
}

Untuk mengonversi dari deg ke indeks gambar, fungsi berikut akan melakukannya. Lihat demo untuk versi ringkas.

function getStepAngle(dir) {  // dir in degrees

    // normalise the angle
    dir = (dir % 360 + 360) % 360;

    // scale to angleSteps 0 to 16
    dir /= 360 / angleSteps;

    // offset by half a step
    dir += 0.5;

    // floor the result to get integer
    dir = Math.floor(dir);

    // Get remainder to ensure value between 0 and 16 not including 16
    return dir % angleSteps;
}

Demo

Demo menunjukkan dua fungsi yang digunakan. Klik pada tombol untuk menetapkan arah target baru dan arah saat ini akan bergerak ke arah baru, menunjukkan sudut bergerak (hijau) dan indeks gambar (biru) saat bergerak.

const angleSteps = 16;
const angleMoveSteps = 90;
var currentDir = 0;
var shipDir = 0;
var targetAngle = 0;


function getNextAngle(newDirection, currentDirection) {
    const step = 360 / angleMoveSteps
    newDirection %= 360;
    currentDirection %= 360;
    newDirection = newDirection < currentDirection ? newDirection + 360 : newDirection;
    const dif = newDirection - currentDirection;

    if (dif > 360 / 2 && dif < 360 - step) { return currentDirection - step }
    if (dif > step && dif < 360 / 2) { return currentDirection + step }
     return currentDirection
}

function getStepAngle(dir) {
    return Math.floor(((dir % 360 + 360) % 360) / (360 / angleSteps) + 0.5) % angleSteps;
}






/* Demo code from here down */
Math.TAU = Math.PI * 2;
const ctx = canvas.getContext("2d")
const w = canvas.width;
const h = canvas.height;
var seeking = false;
const speed = 100; // milliseconds

update();
canvas.addEventListener("click", event => {
   const bounds = canvas.getBoundingClientRect();
   const x = event.pageX - bounds.left - scrollX;
   const y = event.pageY - bounds.top  - scrollY;
   targetAngle = Math.atan2(y - w / 2, x - h / 2) * 180 / Math.PI;
   if(!seeking){ render() }
});
function render() {
   requestAnimationFrame(update);
   var newDir = getNextAngle(targetAngle, currentDir);
   if(newDir !== currentDir) {
       currentDir = newDir;
       seeking = true;
       setTimeout(render, speed);
   } else {
       currentDir = targetAngle;
        setTimeout(()=>requestAnimationFrame(update), speed);
       seeking = false;
   }
   
}

function update() {
   shipDir = getStepAngle(currentDir);
   clear();
   drawCompase();
   drawTargetAngle(targetAngle);
   drawCurrentAngle(currentDir);
   drawStepAngle(shipDir);

}

function clear() { ctx.clearRect(0,0,w,h) }

function angleText(text,x,y,angle,size = 12, col = "#000") {
    const xAX = Math.cos(angle);
    const xAY = Math.sin(angle);
    ctx.fillStyle = col;
    ctx.font = size + "px arial";
    ctx.textAlign = "right";
    ctx.textBaseline = "middle";
    if(xAX < 0) {
        ctx.setTransform(-xAX, -xAY, xAY, -xAX, x, y);
        ctx.textAlign = "left";
    
    } else {
        ctx.setTransform(xAX, xAY, -xAY, xAX, x, y);
        ctx.textAlign = "right";
    }
    ctx.fillText(text,0,0);
}
function drawCompase() {
    var i;
    const rad = h * 0.4, rad1 = h * 0.395, rad2 = h * 0.41;
    ctx.lineWidth = 1;
    ctx.strokeStyle = "#000";
    ctx.beginPath();
    ctx.arc(w / 2, h / 2, rad, 0, Math.TAU);
    ctx.stroke();

    ctx.lineWidth = 2;
    ctx.beginPath();
    for (i = 0; i < 1; i += 1 / angleSteps) {
         const ang = i * Math.TAU;
         ctx.moveTo(Math.cos(ang) * rad1 + w / 2, Math.sin(ang) * rad1 + h / 2);
         ctx.lineTo(Math.cos(ang) * rad2 + w / 2, Math.sin(ang) * rad2 + h / 2);
    }
    ctx.stroke();

    for (i = 0; i < 1; i += 1 / angleSteps) {
         const ang = i * Math.TAU;
         angleText(
             (ang * 180 / Math.PI).toFixed(1).replace(".0",""), 
             Math.cos(ang) * (rad1 - 2) + w / 2, 
             Math.sin(ang) * (rad1 - 2) + h / 2,
             ang
         );
    }
    ctx.setTransform(1,0,0,1,0,0);
}
function drawTargetAngle(angle) { // angle in deg
    const rad = h * 0.30, rad1 = h * 0.1, rad2 = h * 0.34;
    const ang = angle * Math.PI / 180;
    const fromA = ang - Math.PI / (angleSteps * 4);
    
    const toA = ang + Math.PI / (angleSteps * 4);
    ctx.linewidth = 2;
    ctx.strokeStyle = "#F00";
    ctx.beginPath();       
    
    ctx.moveTo(Math.cos(fromA) * rad + w / 2, Math.sin(fromA) * rad + h / 2);
    ctx.lineTo(Math.cos(ang) * rad2 + w / 2, Math.sin(ang) * rad2 + h / 2);
    ctx.lineTo(Math.cos(toA) * rad + w / 2, Math.sin(toA) * rad + h / 2);       
    ctx.stroke();  
    angleText(
         angle.toFixed(1).replace(".0",""), 
         Math.cos(ang) * (rad - 4) + w / 2, 
         Math.sin(ang) * (rad - 4) + h / 2,
         ang,
         12, "#F00"
    );      
    ctx.setTransform(1,0,0,1,0,0);

}
function drawCurrentAngle(angle) { // angle in deg
    const rad = h * 0.14, rad2 = h * 0.17;
    const ang = angle * Math.PI / 180;
    const fromA = ang - Math.PI / (angleSteps * 2);
    
    const toA = ang + Math.PI / (angleSteps * 2);
    ctx.linewidth = 2;
    ctx.strokeStyle = "#0A0";
    ctx.beginPath();       
    
    ctx.moveTo(Math.cos(fromA) * rad + w / 2, Math.sin(fromA) * rad + h / 2);
    ctx.lineTo(Math.cos(ang) * rad2 + w / 2, Math.sin(ang) * rad2 + h / 2);
    ctx.lineTo(Math.cos(toA) * rad + w / 2, Math.sin(toA) * rad + h / 2);       
    ctx.stroke();  
    angleText(
         angle.toFixed(1).replace(".0",""), 
         Math.cos(ang) * (rad - 4) + w / 2, 
         Math.sin(ang) * (rad - 4) + h / 2,
         ang,
         12, "#0A0"
    );      
    ctx.setTransform(1,0,0,1,0,0);

}
function drawStepAngle(angle) { // ang 0 to angleSteps cyclic
    var ang = angle % angleSteps;
    ang *= Math.PI / angleSteps*2;
    const fromA = ang - Math.PI / angleSteps;
    const toA = ang + Math.PI / angleSteps;
    
    const rad = h * 0.4, rad1 = h * 0.35, rad2 = h * 0.44;
    const rad3 = h * 0.34, rad4 = h * 0.45;
    ctx.linewidth = 1;
    ctx.strokeStyle = "#08F";
    ctx.beginPath();   
    ctx.arc(w / 2, h / 2, rad1, fromA, toA);
    ctx.moveTo(w / 2 + Math.cos(fromA) * rad2, h / 2 + Math.sin(fromA) * rad2, 0, Math.TAU);
    ctx.arc(w / 2, h / 2, rad2,  fromA, toA);
    ctx.stroke();
    
    ctx.linewidth = 2;
    ctx.beginPath();       
    
     ctx.moveTo(Math.cos(fromA) * rad3 + w / 2, Math.sin(fromA) * rad3 + h / 2);
     ctx.lineTo(Math.cos(fromA) * rad4 + w / 2, Math.sin(fromA) * rad4 + h / 2);
     ctx.moveTo(Math.cos(toA) * rad3 + w / 2, Math.sin(toA) * rad3 + h / 2);
     ctx.lineTo(Math.cos(toA) * rad4 + w / 2, Math.sin(toA) * rad4 + h / 2);       
     ctx.stroke();     
     
     angleText(
         angle, 
         Math.cos(ang + 0.1) * (rad - 2) + w / 2, 
         Math.sin(ang + 0.1) * (rad - 2) + h / 2,
         ang,
         16, "#08F"
     );     
     
    ctx.setTransform(1,0,0,1,0,0);
}
body { font-family: arial }
canvas {
 position: absolute;
 top: 0px;
 left: 0px;
}
<span> Click to set new target direction</span>
<canvas id="canvas" width="400" height="400"></canvas>
0
Blindman67 29 Oktober 2019, 13:31