如何获取在画布上移动的图像的X和Y坐标

4
当我在画布上移动player.hero的图像时,我希望有一个变量来保存英雄当前的x和y位置,这样我就可以让僵尸图像向英雄当前的位置移动。谢谢,如果我的代码到目前为止很糟糕,请建议修改,谢谢。
(function() {

    var canvas, context, width, height, speed = 8;    
    var interval_id;
    var zombies = [];
    var bullets = [];

    var moveLeft = false;
    var moveRight = false;
    var moveUp = false;
    var moveDown = false;

    var player = {
        x : 0,
        y : 0,
        width : 35,
        height : 60,
        hero : new Image(),
    };

    for (var i = 0; i < 10; i += 1){
        var zombie = {
            x : 10,
            y : 10,
            undead : new Image(),
            targetToGox : 0,
            targetToGoy : 0,
         };
         zombies.push(zombie);
    }
    var mouse = {
        x : 0,
        y : 0,
    }

    document.addEventListener('DOMContentLoaded', init, false);

    function init() {    
        canvas = document.querySelector('canvas');
        context = canvas.getContext('2d');
        width = canvas.width;
        height = canvas.height;
        player.x = width / 2 - 18;
        player.y = height / 2 - 30;
        player.hero.src = 'hero.png';
        zombie.undead.src = 'zombie.png';
        //context.drawImage(player.hero, player.x, player.y);  
        window.addEventListener("keydown", activate,false);
        window.addEventListener("keyup",deactivate,false);
        //window.addEventListener("mouseover", drawImagePointingAt, false);
        interval_player = window.setInterval(drawPlayer, 33);
    }

    function drawPlayer() {
        context.clearRect(0 ,0 ,width, height);
        context.drawImage(player.hero,player.x, player.y);
        //******** need zombie to go to position of player.hero******///
        context.drawImage(zombie.undead (somthing for x and y coordinats of player.hero);
        // stops player moveing beyond the bounds of the canvas
        if (player.x + player.width >= width) {
            moveRight = false
        }
        if (player.y + player.height >= height) {
            moveDown = false
        }
        if (player.x  <= 0) {
            moveLeft = false
        }
        if (player.y <= 0) {
            moveUp = false
        }     
        if (moveRight) {
            player.x  += speed;
        }
        if (moveUp) {
            player.y -= speed;
        }
        if (moveDown) {
            player.y += speed;
        }
        if (moveLeft){
            player.x -= speed;
        }

    function activate(event) {
        var keyCode = event.keyCode;
            if (keyCode === 87){
                moveUp = true;
            }
            else if (keyCode === 68){
                moveRight = true;
            }
            else if (keyCode === 83){
                moveDown = true;
            }
            else if (keyCode === 65){
                moveLeft = true;
            }

    }
    function deactivate(event) {
        var keyCode = event.keyCode;
            if (keyCode === 87){
                moveUp = false;}
            else if (keyCode === 68){
                moveRight = false;}
            else if (keyCode === 83){
                moveDown = false;}
            else if (keyCode === 65){
                moveLeft = false;}
    }
    function getRandomNumber(min, max) {
        return Math.round(Math.random() * (max - min)) + min;
    }
    function stop() {
        clearInterval(interval_player);

    }
})();

也许可以包含一个可工作的JSFiddle或Plunker吗? - Tim Harker
我正在处理这个问题,只是试图弄清楚如何将这两张图片放入jsFiddle中。 - NickCortes
1
你试过fabricjs.com吗?这是一个很棒的HTML画布框架,可以让你更轻松地使用画布。 - Tim Harker
只是猜测,我假设你做了类似这样的事情:... x: Math.random(),然而 random 返回一个大于等于 0 且小于 1 的浮点数。你可以尝试这样做:... x: Math.floor(Math.random() * canvas.width),以获得一个介于 0 和 canvas.width 之间的数字。 - Marc Rohloff
2个回答

2
这是一段相当长的文本,讲述了为什么我认为你重新构建代码会比获取我在屏幕上移动的图像的X和Y坐标更好。本文末尾附有一个脚本,试图展示如何实现此操作。
你问到你的代码质量如何。对于一个新手程序员来说,你的代码并不糟糕,但你正在陷入一些经典的陷阱,这在代码库变得更大时会很痛苦。
例如,为每个可能的方向保留变量,以便在按键后移动玩家(这些变量在不同的函数中被操纵和使用)。问题在于,当你决定改变该系统的任何方面时,它将崩溃。
相反,考虑拥有代表玩家的对象,其中包含自己的内部逻辑,通过改变自己的坐标来“移动”。
我无法强调这个观点的重要性——优秀的黑客总是给自己留有工作的余地。例如,你不应该让Player直接操作游戏绘图例程。这是一个非常普遍的概念,在软件工程中甚至有一些词语用于描述这种组织原则的不同方面(比如封装松耦合迪米特法则)。它非常重要。
这也带我谈到了另一个问题:全局变量。
这是一个更难的问题,因为所有程序员最终都会发现如果他们过于批评它(特别是在做低级别的工作时),他们会变成伪君子。然而,最好将你所拥有的任何全局变量整合起来,并可能创建函数作为其与“外部世界”交互的接口。
在你的游戏中,这意味着像moveLeft这样的移动进入一个“游戏循环”,该循环仅检查所有“对象”的坐标,清除屏幕并适当地绘制这些对象。
另一个重要的想法是,“重复”功能应该共享一个方法。在你的情况下,这将意味着PlayerZombie都成为某个更抽象的类别的实例,我将其命名为GameObject。这使您可以为GameObject编写所有主要函数,并在PlayerZombie内部“继承”它们的功能(有许多其他, 也许更好 的方法可以在没有原型或继承的情况下完成这一点)。
考虑到这一切,我试图花20分钟设计出一些东西,希望能为您提供一些工作的依据。如果这与您想要的完全无关,至少您可以在您的带有可能毫无意义的互联网言论的记录中再加上一次。

我的代码的“继承”采用了非常简单的Javascript风格,尽管事实上在JS中有不少于十种“新的和改进的”方法来共享代码之间的实现细节,每种方法都具有很大的多样性,其遵循原型或面向对象编程原则的深度也不同。

我无法涵盖Stamps、jSL甚至Javascript现在臭名昭著的新“class”关键字,所以我建议您阅读相关资料,或许自己能够将它们利用起来,但现在我仍然坚持基础知识。

const ZOMBIE_COUNT = 10

function GameState() {
    this.player  = null;
    this.enemies = []
}
var Game = new GameState() // our global game state

// An abstract 'game object' or character
function GameObject({x, y, image}) {
    this.x = x
    this.y = y
    this.image = image
}
GameObject.prototype.draw = function() {
    Game.ctx.fillStyle = this.color
    Game.ctx.fillRect(this.x, this.y, 10, 10)
}
GameObject.prototype.moveLeft =  function(n) { if(this.x > 0) this.x -= n }
GameObject.prototype.moveRight = function(n) { if(this.x < Game.width) this.x += n }
GameObject.prototype.moveDown =  function(n) { if(this.y < Game.height) this.y += n}
GameObject.prototype.moveUp =    function(n) { if(this.y > 0) this.y -= n }

function Player({x, y, width}) {
    GameObject.call(this, {x, y}) // setup x, y & image
    this.color = 'red'
}
Player.prototype = Object.create(GameObject.prototype, {})

function Zombie({x, y, target}) {
    GameObject.call(this, {x, y}) // setup x, y & image
    this.target = target // target contains the player
    this.color = 'blue'
}
Zombie.prototype = Object.create(GameObject.prototype, {})
Zombie.prototype.moveToPlayer = function() {
    let {x, y} = Game.player
    // very basic 'movement' logic
    if (this.x < x) {
        this.moveRight(1/4)
    } else if (this.x > x) {
        this.moveLeft(1/4)
    }

    if (this.y > y) {
        this.moveUp(1/4)
    } else if (this.y < y) {
        this.moveDown(1/4)
    }
}


function init() {
    var canvas = document.getElementById('canvas')
    if (canvas.getContext) {
        var ctx = canvas.getContext('2d')
    } else {
        console.log("No canvas")
        return -1
    }

    let {width, height} = canvas

    // Setup our game object
    Game.width = width
    Game.height = height
    Game.ctx = ctx
    // Create our player in the middle
    Game.player = new Player({x: width / 2, y: height / 2})

    // Create our enemies
    for(let i = 0; i < ZOMBIE_COUNT; i++) {
        Game.enemies.push(new Zombie({x: Math.random() * width  | 0, // truncate our value
                                      y: Math.random() * height | 0}))
    }

    game_loop()
}

function game_loop() {
    window.requestAnimationFrame(game_loop)
    Game.ctx.fillStyle = 'white'
    Game.ctx.fillRect(0, 0, Game.width, Game.height);
    Game.player.draw()

    Game.enemies.map(enemy => {
        enemy.moveToPlayer()
        enemy.draw()
    })
}

function process_key(ev) {
    let speed = 3
    let key = ev.keyCode
    if (key === 68)
        Game.player.moveRight(speed)
    if (key === 87)
        Game.player.moveUp(speed)
    if (key === 65)
        Game.player.moveLeft(speed)
    if (key === 83)
        Game.player.moveDown(speed)
}

window.addEventListener('keydown', process_key, false);

init()
canvas { border: 3px solid #333; }
<canvas id="canvas" width="400" height="400"></canvas>


非常感谢您提供如此详细的解释,我一定会遵循您给我的所有建议。我会阅读您给我的建议,以改善我对如何进行这项工作的理解。再次感谢您,非常棒的帖子。 - NickCortes
我已经阅读了您的建议,但恐怕它超出了我的当前技能水平。我正在努力让您的代码与我的两个图像配合使用。您的片段正是我想要的。我尝试了我能想到的一切来操纵您的代码以适应我的需求。 - NickCortes
我知道我的代码不太好,但至少我能够理解它。你能修改我的代码使僵尸跟随英雄吗?这样我就能更好地理解了,即使我没有采用最优化的方式。我之后可以在这些技能基础上进行构建。 - NickCortes
@NickCortes 现在僵尸会跟随英雄了。 - zetavolt
@NickCortes 那太棒了! - zetavolt
你能看到我的问题吗?当我围着僵尸转圈时,它们会重叠在一起,我该如何解决这个问题? - NickCortes

1

我假设你指的是这一行?

//******** need zombie to go to position of player.hero******///
context.drawImage(zombie.undead (somthing for x and y coordinats of player.hero);

我会把代码改成这样:

我会把代码改成这样:

function init() {    
  ...
  interval_player = window.setInterval(updateGame, 33);
}

function updateGame() {
    context.clearRect(0 ,0 ,width, height);
    updatePlayer();
    for (let zombie of zombies) {
       updateZombie(zombie);
}

function updatePlayer() {
    // stops player moveing beyond the bounds of the canvas
    if (player.x + player.width >= width) {
        moveRight = false
    }
    if (player.y + player.height >= height) {
        moveDown = false
    }
    if (player.x  <= 0) {
        moveLeft = false
    }
    if (player.y <= 0) {
        moveUp = false
    }     
    if (moveRight) {
        player.x  += speed;
    }
    if (moveUp) {
        player.y -= speed;
    }
    if (moveDown) {
        player.y += speed;
    }
    if (moveLeft){
        player.x -= speed;
    }
    context.drawImage(player.x, player.y);
}

function updateZombie(zombie) {
  // Move zombie closer to player
  if (zombie.x > player.x)
     zombie.x -= zombieSpeed;
  else if (zombie.x < player.x)
     zombie.x += zombie.Speed;

  if (zombie.y > player.y)
     zombie.y -= zombieSpeed;
  else if (zombie.y < player.y)
     zombie.y += zombie.Speed;

  context.drawImage(zombie.undead, zombie.x, zombie.y);
}

这里是一行代码:

zombie.undead.src = 'zombie.png';

只会改变最后创建的僵尸。你真的需要移动它:

for (var i = 0; i < 10; i += 1) {
    var zombie = {
        x : 10,
        y : 10,
        undead : new Image(),
        targetToGox : 0,
        targetToGoy : 0,
     };
     zombie.undead.src = 'zombie.png'; 
     zombies.push(zombie);
}

不,我只是假设你不希望僵尸以不同的速度移动,否则游戏将无法玩耍,但我没有重写完整的示例,你可以在定义玩家速度的地方添加它。 - Marc Rohloff
谢谢,我最终弄明白了。现在僵尸的速度是玩家速度的一半...顺便说一句,非常感谢你帮助了我。 - NickCortes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接