skip to Main Content

I’m currently creating a game in native JavaScript, HTML, and a bit of CSS. I have 2 blocks named Sprites and whenever they go toward the left or right edge of the canvas, they don’t stop. Question: How can I use native JS to stop the sprites from moving off the canvas?

Note: All answers to this solution are in other languages or use libraries, so this question has not been answered.

const canvas = document.querySelector('#canvas');
const ctx = canvas.getContext("2d");

canvas.width = 1024;
canvas.height = 576;

function logMessage(msg) {
    document.querySelector('#myMessage').textContent += msg + '. ';
}

//This creates a background
ctx.fillRect(0, 0, canvas.width, canvas.height);

//Cahnging this value changes how fast the sprites fall
const gravity = 0.7;

class Sprite {
    //The curly braces makes it easier to pass values back and forth
    constructor({position, velocity}) {
        this.position = position;
        this.velocity = velocity;
        this.height = 150;
        this.lastKey;
    }

    draw() {
        ctx.fillStyle = 'red';
        //Because this is inside of the class "Sprite",
        //We can use "this." to give us the position/velocity/whatever that is being used in this instance
        ctx.fillRect(this.position.x, this.position.y, 50, this.height);
    }

    update() {
        this.draw();
        this.position.x += this.velocity.x;
        this.position.y += this.velocity.y;

        //Stops the sprite from moving off the edge of the canvas
        if (this.position.x <= 0 || this.position.x + 50 >= canvas.width) {
            logMessage("Hi")
            this.velocity.x == 0;
        }

        //Stops the velocity in the y direction if the sprite hits the bottom
        if (this.position.y + this.height + this.velocity.y >= canvas.height) {
            this.velocity.y = 0;
        } else {
            this.velocity.y += gravity;
        }
    }
}

const Player = new Sprite({
    position: {
        x: 0,
        y: 0
    },
    velocity: {
        x: 0,
        y: 0
    }
})

const Enemy = new Sprite({
    position: {
        x: 400,
        y: 100
    },
    velocity: {
        x: 0,
        y: 0
    }
})

const keys = {
    w: {
        pressed: false
    },
    a: {
        pressed: false
    },
    d: {
        pressed: false
    },
    ArrowUp: {
        pressed: false
    },
    ArrowLeft: {
        pressed: false
    },
    ArrowRight: {
        pressed: false
    }
}

function animate() {
    //This makes the animate method run over and over
    window.requestAnimationFrame(animate);
    //The two below fill in the background so that when the sprites move, the background stays the same
    ctx.fillStyle = 'black';
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    Player.update();
    Enemy.update();

    //Player movement
    Player.velocity.x = 0; //This stops the sprite from continuously moving

    if (keys.a.pressed == true && Player.lastKey == "a") {
        Player.velocity.x = -5; //Changing this value will change how fast the sprite moves left
    } else if (keys.d.pressed == true && Player.lastKey == "d") {
        Player.velocity.x = 5;
    }

    //Enemy movement
    Enemy.velocity.x = 0;

    if (keys.ArrowLeft.pressed == true && Enemy.lastKey == "ArrowLeft") {
        Enemy.velocity.x = -5;
    } else if (keys.ArrowRight.pressed == true && Enemy.lastKey == "ArrowRight") {
        Enemy.velocity.x = 5;
    }
}

animate();

//Event listeners that move the player when certain keys are pressed
window.addEventListener('keydown', (event) => {
    switch (event.key) {
        //Player movement
        case 'w':
            Player.velocity.y = -20;
        break;

        case 'a':
            keys.a.pressed = true;
            //This keeps track of our last pressed key so our sprite does what was pressed most recently
            Player.lastKey = "a";
        break

        case 'd':
            keys.d.pressed = true;
            Player.lastKey = "d";
        break;
    }

    //Enemy movement
    switch (event.key) {
        case 'ArrowUp':
            Enemy.velocity.y = -20;
        break;

        case 'ArrowRight':
            keys.ArrowRight.pressed = true;
            Enemy.lastKey = 'ArrowRight'
        break

        case 'ArrowLeft':
            keys.ArrowLeft.pressed = true;
            Enemy.lastKey = 'ArrowLeft'
        break;
    }    
})

window.addEventListener('keyup', (event) => {
    //Player keys
    switch (event.key) {
        case 'w':
            keys.w.pressed = false;
        break;    

        case 'a':
            keys.a.pressed = false;
        break

        case 'd':
            keys.d.pressed = false;
        break;
    }

    //Enemy keys
    switch (event.key) {
        case 'ArrowUp':
            keys.ArrowUp.pressed = false;
        break;    

        case 'ArrowLeft':
            keys.ArrowLeft.pressed = false;
        break

        case 'ArrowRight':
            keys.ArrowRight.pressed = false;
        break;
    }
})
#myConsole {
    background-color: black;
    color: white;
    min-height: 100px;
}
<!DOCTYPE html>
<html lang="en">

<html>
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE-edge">
        <meta name="viewport", content="width=device-width, initial-scale=1.0">
        <meta name="author" content="Christian Davis">
        <link rel="stylesheet" href="styles.css">

        <title>Fighting Game</title>
    </head>

    <body>
        <canvas id="canvas"></canvas>
        
        <p id="myConsole">&gt;&nbsp;<span id="myMessage"></span></p>

        <script src="app.js"></script>
    </body>
</html>

2

Answers


  1. Chosen as BEST ANSWER

    After reading @Rory McCrossan's solution, I came up with my own that keeps the sprites inside the bounds of the canvas completely.

    const canvas = document.querySelector('#canvas');
    const ctx = canvas.getContext("2d");
    
    canvas.width = 1024;
    canvas.height = 576;
    
    function logMessage(msg) {
        document.querySelector('#myMessage').textContent += msg + '. ';
    }
    
    //This creates a background
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    
    //Changing this value changes how fast the sprites fall
    const gravity = 0.7;
    
    class Sprite {
        //The curly braces makes it easier to pass values back and forth
        constructor({position, velocity}) {
            this.position = position;
            this.velocity = velocity;
            this.height = 150;
            this.lastKey;
        }
    
        draw() {
            ctx.fillStyle = 'red';
            //Because this is inside of the class "Sprite",
            //We can use "this." to give us the position/velocity/whatever that is being used in this instance
            ctx.fillRect(this.position.x, this.position.y, 50, this.height);
        }
    
        update() {
            this.draw();
            //This keeps the Sprites from going off the edge of the canvas
            this.position.x = Math.max(0, Math.min(canvas.width - 50, this.position.x + this.velocity.x));
            //This stops the sprite from going above the canvas
            this.position.y = Math.max(0, Math.min(canvas.height, this.position.y + this.velocity.y));
    
            //Stops the velocity in the y direction if the sprite hits the bottom
            if (this.position.y + this.height + this.velocity.y >= canvas.height) {
                this.velocity.y = 0;
            } else {
                this.velocity.y += gravity;
            }
        }
    }
    
    const Player = new Sprite({
        position: {
            x: 0,
            y: 0
        },
        velocity: {
            x: 0,
            y: 0
        }
    })
    
    const Enemy = new Sprite({
        position: {
            x: 400,
            y: 100
        },
        velocity: {
            x: 0,
            y: 0
        }
    })
    
    const keys = {
        w: {
            pressed: false
        },
        a: {
            pressed: false
        },
        d: {
            pressed: false
        },
        ArrowUp: {
            pressed: false
        },
        ArrowLeft: {
            pressed: false
        },
        ArrowRight: {
            pressed: false
        }
    }
    
    function animate() {
        //This makes the animate method run over and over
        window.requestAnimationFrame(animate);
        //The two below fill in the background so that when the sprites move, the background stays the same
        ctx.fillStyle = 'black';
        ctx.fillRect(0, 0, canvas.width, canvas.height);
        Player.update();
        Enemy.update();
    
        //Player movement
        Player.velocity.x = 0; //This stops the sprite from continuously moving
    
        if (keys.a.pressed == true && Player.lastKey == "a") {
            Player.velocity.x = -5; //Changing this value will change how fast the sprite moves left
        } else if (keys.d.pressed == true && Player.lastKey == "d") {
            Player.velocity.x = 5;
        }
    
        //Enemy movement
        Enemy.velocity.x = 0;
    
        if (keys.ArrowLeft.pressed == true && Enemy.lastKey == "ArrowLeft") {
            Enemy.velocity.x = -5;
        } else if (keys.ArrowRight.pressed == true && Enemy.lastKey == "ArrowRight") {
            Enemy.velocity.x = 5;
        }
    }
    
    animate();
    
    //Event listeners that move the player when certain keys are pressed
    window.addEventListener('keydown', (event) => {
        switch (event.key) {
            //Player movement
            case 'w':
                Player.velocity.y = -20;
            break;
    
            case 'a':
                keys.a.pressed = true;
                //This keeps track of our last pressed key so our sprite does what was pressed most recently
                Player.lastKey = "a";
            break
    
            case 'd':
                keys.d.pressed = true;
                Player.lastKey = "d";
            break;
        }
    
        //Enemy movement
        switch (event.key) {
            case 'ArrowUp':
                Enemy.velocity.y = -20;
            break;
    
            case 'ArrowRight':
                keys.ArrowRight.pressed = true;
                Enemy.lastKey = 'ArrowRight'
            break
    
            case 'ArrowLeft':
                keys.ArrowLeft.pressed = true;
                Enemy.lastKey = 'ArrowLeft'
            break;
        }    
    })
    
    window.addEventListener('keyup', (event) => {
        //Player keys
        switch (event.key) {
            case 'w':
                keys.w.pressed = false;
            break;    
    
            case 'a':
                keys.a.pressed = false;
            break
    
            case 'd':
                keys.d.pressed = false;
            break;
        }
    
        //Enemy keys
        switch (event.key) {
            case 'ArrowUp':
                keys.ArrowUp.pressed = false;
            break;    
    
            case 'ArrowLeft':
                keys.ArrowLeft.pressed = false;
            break
    
            case 'ArrowRight':
                keys.ArrowRight.pressed = false;
            break;
        }
    })
    #myConsole {
        background-color: black;
        color: white;
        min-height: 100px;
    }
    <!DOCTYPE html>
    <html lang="en">
    
    <html>
        <head>
            <meta charset="UTF-8">
            <meta http-equiv="X-UA-Compatible" content="IE-edge">
            <meta name="viewport", content="width=device-width, initial-scale=1.0">
            <meta name="author" content="Christian Davis">
            <link rel="stylesheet" href="styles.css">
    
            <title>Fighting Game</title>
        </head>
    
        <body>
            <canvas id="canvas"></canvas>
            
            <p id="myConsole">&gt;&nbsp;<span id="myMessage"></span></p>
    
            <script src="app.js"></script>
        </body>
    </html>


  2. The issue is because your logic to determine the extents of the canvas was flawed.

    You can simplify the logic by locking the bounds of position.x using Math.min() and Math.max(). I would suggest adding a width property to your sprites to avoid having to use the magic number.

    this.position.x = Math.max(0, Math.min(canvas.width - 50, this.position.x + this.velocity.x));
    

    Here’s a working example, note that I commented out the other sprite to make the effect more clear.

    const canvas = document.querySelector('#canvas');
    const ctx = canvas.getContext("2d");
    
    canvas.width = 1024;
    canvas.height = 576;
    
    //This creates a background
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    
    //Cahnging this value changes how fast the sprites fall
    const gravity = 0.7;
    
    class Sprite {
      //The curly braces makes it easier to pass values back and forth
      constructor({
        position,
        velocity
      }) {
        this.position = position;
        this.velocity = velocity;
        this.height = 150;
        this.lastKey;
      }
    
      draw() {
        ctx.fillStyle = 'red';
        ctx.fillRect(this.position.x, this.position.y, 50, this.height);
      }
    
      update() {
        this.draw();
        this.position.x = Math.max(0, Math.min(canvas.width - 50, this.position.x + this.velocity.x));
        this.position.y += this.velocity.y;
    
        //Stops the velocity in the y direction if the sprite hits the bottom
        if (this.position.y + this.height + this.velocity.y >= canvas.height) {
          this.velocity.y = 0;
        } else {
          this.velocity.y += gravity;
        }
      }
    }
    
    const Player = new Sprite({
      position: {
        x: 0,
        y: 0
      },
      velocity: {
        x: 0,
        y: 0
      }
    })
    
    const Enemy = new Sprite({
      position: {
        x: 400,
        y: 100
      },
      velocity: {
        x: 0,
        y: 0
      }
    })
    
    const keys = {
      w: {
        pressed: false
      },
      a: {
        pressed: false
      },
      d: {
        pressed: false
      },
      ArrowUp: {
        pressed: false
      },
      ArrowLeft: {
        pressed: false
      },
      ArrowRight: {
        pressed: false
      }
    }
    
    function animate() {
      //This makes the animate method run over and over
      window.requestAnimationFrame(animate);
      //The two below fill in the background so that when the sprites move, the background stays the same
      ctx.fillStyle = 'black';
      ctx.fillRect(0, 0, canvas.width, canvas.height);
      //Player.update();
      Enemy.update();
    
      //Player movement
      Player.velocity.x = 0; //This stops the sprite from continuously moving
    
      if (keys.a.pressed == true && Player.lastKey == "a") {
        Player.velocity.x = -5; //Changing this value will change how fast the sprite moves left
      } else if (keys.d.pressed == true && Player.lastKey == "d") {
        Player.velocity.x = 5;
      }
    
      //Enemy movement
      Enemy.velocity.x = 0;
    
      if (keys.ArrowLeft.pressed == true && Enemy.lastKey == "ArrowLeft") {
        Enemy.velocity.x = -5;
      } else if (keys.ArrowRight.pressed == true && Enemy.lastKey == "ArrowRight") {
        Enemy.velocity.x = 5;
      }
    }
    
    animate();
    
    //Event listeners that move the player when certain keys are pressed
    window.addEventListener('keydown', (event) => {
      switch (event.key) {
        //Player movement
        case 'w':
          Player.velocity.y = -20;
          break;
    
        case 'a':
          keys.a.pressed = true;
          //This keeps track of our last pressed key so our sprite does what was pressed most recently
          Player.lastKey = "a";
          break
    
        case 'd':
          keys.d.pressed = true;
          Player.lastKey = "d";
          break;
      }
    
      //Enemy movement
      switch (event.key) {
        case 'ArrowUp':
          Enemy.velocity.y = -20;
          break;
    
        case 'ArrowRight':
          keys.ArrowRight.pressed = true;
          Enemy.lastKey = 'ArrowRight'
          break
    
        case 'ArrowLeft':
          keys.ArrowLeft.pressed = true;
          Enemy.lastKey = 'ArrowLeft'
          break;
      }
    })
    
    window.addEventListener('keyup', (event) => {
      //Player keys
      switch (event.key) {
        case 'w':
          keys.w.pressed = false;
          break;
    
        case 'a':
          keys.a.pressed = false;
          break
    
        case 'd':
          keys.d.pressed = false;
          break;
      }
    
      //Enemy keys
      switch (event.key) {
        case 'ArrowUp':
          keys.ArrowUp.pressed = false;
          break;
    
        case 'ArrowLeft':
          keys.ArrowLeft.pressed = false;
          break
    
        case 'ArrowRight':
          keys.ArrowRight.pressed = false;
          break;
      }
    })
    #myConsole {
      background-color: black;
      color: white;
      min-height: 100px;
    }
    <canvas id="canvas"></canvas>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search