使用 JS 创建轨道武器精灵 canvas 创建动画 errors/glitches

Creating orbiting weapon sprite with JS canvas creates animation errors/glitches

我正在尝试制作一款玩家拥有可装备武器的游戏。截至目前,我已将此武器设置为弓的图像,并且我希望该武器在面向鼠标的同时在玩家周围移动。这类似于buildroyale.io,其中玩家随着他的武器旋转以面对鼠标。

截至目前,在@Justin 的帮助下,我已经让弓在屏幕上(稍微)旋转了。它仅在按需要按下左键时显示,但不会按预期旋转。这是一个展示它如何移动的剪辑:clip

这是我使用的代码:

    class EventHandler {

    equip_weapon() {

        if (holding) {

            player.weapon = player.bag.slot_1;

            canv.globalAlpha = 1;
            player.weapon.equip();
            player.weapon.update();
        
        }

    }

}

class Weapon {

    image_path;

    constructor(image_path) {

        this.x = player.x + 30;
        this.y = player.y + 30;
        this.width = 120;
        this.height = 120;
        this.angle = 0;
        this.distance = 50;
        this.image = image_path;

    }

    equip() {

        this.angle = Math.atan2(mouse.y - this.y, mouse.x - this.x)
        canv.save();
        canv.translate(this.x, this.y);
        canv.rotate(this.angle);
        canv.drawImage(this.image, this.distance, -this.height/2, this.width, this.height);
        canv.restore();  

    }

    update() {

        this.x = player.x + player.width / 2;
        this.y = player.y + player.height / 2;

    }

}

bow = new Weapon(bow_image);
player.bag.slot_1 = bow;

aim_bounds = document.documentElement.getBoundingClientRect();

class 玩家 {

constructor() {

    this.name = null;
    this.speed = 5;
    this.skin = player_sheet;
    this.can_move = true;
    this.is_moving = false;
    this.width = 68;
    this.height = 68;
    this.scale = 1;
    this.x = 566;
    this.y = 316;
    this.direction = facing.down;
    this.frame = 0;
    this.shadow_offset = 25;
    this.frame_rate = 10;
    this.health = 100;
    this.clip_amount = 10;
    this.weapon = null;

    // Player inventory
    this.bag = {

        slot_1 : null,
        slot_2 : null,
        slot_3 : null,
        slot_4 : null,
        slot_5 : null,
        offhand : null,
        armor : null

    }

    this.is_menu_open = false;

    console.log("Player constructed!");

}

update() {

    // Animation updates
    if (game.tick % this.frame_rate == 0 && this.is_moving && !this.is_menu_open) {

        this.frame += 68;

        if (this.frame >= 272) { this.frame = 0; }

    } else if (!this.is_moving) { this.frame = 0; }

    // Movement updates
    if (this.can_move) {

        if (controller.up) { this.direction = facing.up; this.y -= this.speed; this.is_moving = true; }

        else if (controller.down) { this.direction = facing.down; this.y += this.speed; this.is_moving = true; }

        else if (controller.left) { this.direction = facing.left; this.x -= this.speed; this.is_moving = true; }

        else if (controller.right) { this.direction = facing.right; this.x += this.speed; this.is_moving = true; }

        if (!controller.up && !controller.down && !controller.left && !controller.right) { this.is_moving = false; }
        
    }
    
    // Checks
    if (this.is_menu_open) { this.can_move = false; } else { this.can_move = true; }

    document.getElementById("health_bar").value = this.health;

    if (this.is_menu_open) { menu.style.display = "block"; } else { menu.style.display = "none"; }

}

animate() {

    //  Player shadow
    canv.drawImage(player_shadow, this.x, this.y + this.shadow_offset);

    // Player
    canv.globalAlpha = 1;
    canv.drawImage(player_sheet, (sprite.x + this.frame), (sprite.y * this.direction), 
                                 sprite.width, sprite.height, 
                                 this.x, this.y, 
                                 this.width, this.height);

}
}

这是我当前代码的下载,以备您需要更多调试时使用:game.zip

我处理这个问题的方式是确保我的图像首先是正确的。在我的例子中,我的弓图像看起来像这样(忽略质量)。只要看到它面向右即可。

在绘图函数中,我使用 translate() 来定位图像,并使用 drawImage(img, x, y, w, h) 内的 xy 来绘制沿顶部居中的图像canvas 的边缘。 x(在本例中设置为 50)位置本质上是图像旋转的半径,y 只是将我的弓箭在 canvas 的 y 轴上居中。

使用Math.atan2()我可以旋转图像

this.angle = Math.atan2(mouse.y - this.y, mouse.x - this.x)

在这个片段中,我有两行被注释掉了。如果你取消注释它们,你会更好地看到发生了什么。

const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
canvas.width = 600;
canvas.height = 600;

let mouse = {x: 10, y: 10}
let canvasBounds = canvas.getBoundingClientRect();

canvas.addEventListener('mousemove', e => {
  mouse.x = e.x - canvasBounds.x
  mouse.y = e.y - canvasBounds.y
})

let bow = new Image();
bow.src = "https://lh3.googleusercontent.com/g5Sr3HmGZgWx07sRQMvgvtxZ-ErhWNT0_asFdhLIlw-EQMTuUq3BV3YY8d5rrIrZBiJ-Uo2l836Qlmr8dmaCi-dcCCqN6veS6xnE8jSrmdtRtZKnmF5FQ5aTxuVBgB28n6ICoxSlpA=w2400";

class Weapon {
  constructor() {
    this.x = 200;
    this.y = 200;
    this.w = 60;
    this.h = 60;
    this.angle = 0
  }
  draw() {
    this.angle = Math.atan2(mouse.y - this.y, mouse.x - this.x)
    ctx.save();
    ctx.translate(this.x, this.y);
    ctx.rotate(this.angle);
    //ctx.fillStyle = 'lightgrey';
    //ctx.fillRect(0, 0, canvas.width, canvas.height);
    ctx.drawImage(bow, 50, -this.h/2, this.w, this.h)
    ctx.restore();  
  }
}

let bowArrow = new Weapon();

function animate() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  bowArrow.draw();
  requestAnimationFrame(animate)
}
animate();
<canvas id="canvas"></canvas>

更新:

这是我在你的文件中所做的更改,它对我来说效果很好。

engine.controller.js 更改 mousemove 并添加侦听器以调整大小。我也很确定有一种方法可以摆脱 getMouse 函数,因为下面的侦听器会为您获取鼠标坐标。

canvas.addEventListener("mousemove", function (event) {
  mouse_position = event
  mouse.x = event.x - canvas_bounds.x;
  mouse.y = event.y - canvas_bounds.y;
})

window.addEventListener('resize', () => {
  canvas_bounds = canvas.getBoundingClientRect();
})

engine.eventhandler.js

class Weapon {

    image_path;

    constructor(image_path) {

        this.x = player.x + player.width/2;
        this.y = player.y + player.height/2;
        this.w = 60;
        this.h = 60;
        this.angle = 0;
        this.image = image_path;

    }

    equip() {
        this.x = player.x + player.width/2;
        this.y = player.y + player.height/2;
        this.angle = Math.atan2(mouse.y - this.y, mouse.x - this.x)
        canv.save();
        canv.translate(this.x, this.y);
        canv.rotate(this.angle);
        canv.drawImage(this.image, 50, -this.h/2, this.w, this.h);
        canv.restore();  

    }

}

engine.main.js

function setup() {

    game = new Game;
    player = new Player;
    controller = new Controller;
    event_handler = new EventHandler;
    canvas.width = 1200;
    canvas.height = 700;
    canvas_bounds = canvas.getBoundingClientRect();
    
    //  REMOVE LATER
    bow = new Weapon(bow_image);
    player.bag.slot_1 = bow;

    document.getElementById("bag").style.display = "block";

}

engine.setup.js

// Weapon stuff
var weapon_equiped = false;
var canvas = document.getElementById("canvas");
let mouse = {

    x : 10,
    y : 10

}
let canvas_bound;
var mouse_position, holding;
var rect, mouse_x, mouse_y;

...and
//you have a space between canvas. height in both fillRect
canv.fillRect(0, 0, canvas.width, canvas. height);

我想这就是我改变的全部。很确定您可以使用 mouse_x 或 mouse.x,但您的代码中可能不需要两者。