4

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

    }
})();
Johannes
  • 64,305
  • 18
  • 73
  • 130
NickCortes
  • 111
  • 14
  • Perhaps include a working JSFiddle or Plunker? – Tim Harker Mar 11 '17 at 22:06
  • Im working on that now just trying to fugure out how to et the 2 images into the jsFiddle – NickCortes Mar 11 '17 at 22:49
  • 1
    Have you tried fabricjs.com... Great HTML canvas framework. Makes working with the canvas easier. – Tim Harker Mar 11 '17 at 22:59
  • Just guessing, I assume you did something like: `... x: Math.random()`, however random returns a floating point number >= 0 and < 1. You could try something like: `... x: Math.floor(Math.random() * canvas.width)` to get a number between 0 and canvas.width – Marc Rohloff Mar 13 '17 at 02:40

2 Answers2

2

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>
Community
  • 1
  • 1
zetavolt
  • 2,989
  • 1
  • 23
  • 33
  • Thanks for such a detailed explication and I definitely follow up on the all the good advice you gave me. ill read about the suggestions you game me to improve my understand of hos to go about this.. thanks again great post. – NickCortes Mar 12 '17 at 02:11
  • I have done a lot of reading on your suggestion but i'm afraid its a bit to far above my current skill level. I'm struggling to get your code to work with my two images. Your snippet is exactly what I'm going for. I've tried everything I could think of to manipulate your code to fit mine. – NickCortes Mar 12 '17 at 15:31
  • I know my code isn't great but at least I can follow it. Could you amend my code to make zombie follow hero..? I would then be able to understand it a lot better even though I'm not doing it the most optimal way.. I can build on those skills latter. – NickCortes Mar 12 '17 at 15:35
  • @NickCortes The zombies do follow the hero now. – zetavolt Mar 12 '17 at 22:59
  • @NickCortes That is awesome! – zetavolt Mar 13 '17 at 19:18
  • Can you see my problem when i circle zombies they overlap each other how can i fix this. – NickCortes Mar 13 '17 at 21:41
1

I assume you mean this line?

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

I would change the code to something like:

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

This line here:

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

will only change the last zombie created. You really need to move that:

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
  • 1,332
  • 7
  • 8
  • No, I just assumed you wouldn't want zombies to go at a different speed or the game would be unplayable but I didn't rewrite the complete example, You would add it where you define the player speed. – Marc Rohloff Mar 12 '17 at 22:13
  • yup thanks i figure that out eventually I now have the zombie at half the player speed...Thanks by the wat you really helped me out. – NickCortes Mar 12 '17 at 23:22