NickCortes NickCortes - 9 months ago 49
Javascript Question

How do I get the X and Y coordinates of an image that I move around the canvas

As I move the image of player.hero around the canvas I would like a variable that holds the current x and y pos of the hero. So I can make zombie image move towards current position of hero. Thanks and if my code so far is terrible please suggest amendments thanks.

(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);

}
})();

Answer Source

This is a pretty long wall of text about why I think it would be better that you restructured your code instead of "... getting the X and Y coordinates of an image that I move around the screen".

The end of this post contains a script that tries to show how you might go about doing that.

You asked about your code's quality. Your code is not terrible for a new programmer, but you are falling into some classic traps will be painful as your codebase gets larger.

An example of this might be keeping variables for each of the possible directions your player should move after a keypress (which is manipulated & used in separate functions). The problem with this is that when you decide to change any aspect of this system, it's going to crumble.

Instead, consider having an object representing the player which contains it's own internal logic for 'moving' by changing it's own coordinates.

I cannot emphasize this idea enough - Excellent hackers always give themselves a room to work. You shouldn't ever (for example), make the Player directly manipulate the game drawing routines. This is such a pervasive concept that there are actually words in software engineering for different facets of this organizational principle (words like 'Encapsulation' or 'Loose coupling' or 'Law of Demeter'). It's that important.

This leads me to another point: Global variables.

This is a tougher one because it's one where all programmers eventually make hypocrites of themselves if they are too critical of it (especially if they are doing low-level work). Still, it's best to consolidate whatever global variables you do have, and perhaps make functions that serve as their interface to the 'outside world'.

In your game, this would mean moving like moveLeft into a "game loop" that simply checks all of the 'objects' coordinates, clearing the screen & drawing those objects appropriately.

Another important idea here is that 'duplicate' functionality should share a single method. In your case, this would entail that both Player and Zombie become instances of some more abstract category which I'll name GameObject. This allows you to write all your major functions once for GameObject and 'inherit' their functionality inside of the Player and Zombie (there are many other, perhaps even better, ways to accomplish this without prototypes or inheritance at all)

In consideration of all of this, I tried to spend 20 minutes whipping up something that can hopefully give you something to work from. If this is totally unrelated to what you were going for, at the very least you can notch another round of possibly pointless internet pontification under your belt.

My code's "inheritance" is done in a very plain Javascript style, even in spite of the fact there are no less than a dozen 'new and improved' ways to share implementation details between code in JS, each with great variety in their depth of adherence to the principles of either prototypical or object oriented programming.

I cannot hope to cover Stamps, jSL, even Javascript's now infamous new 'class' keyword, so I would advise you read up about these and perhaps put them to profitable use yourself, but for now I'm sticking with the basics.

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>