I am trying to make a function inside my class, where by pressing the ArrowUp key, it will become true. However, it just says: "Cannot read property 'key' of undefined?
class Boks{
constructor(x,y,w,h,r,k){
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.r = r;
this.k = k;
}
visSnake(){
rect(this.x,this.y,this.w,this.h)
}
moveSnake(){
this.x = this.x + speed;
}
keyb(event){
if(event.key === "ArrowUp"){
return true;
}
}
}
My guess is that I have to define some sort of DOM event key in the constructor or parameter to the constructor, but i dont know how?
In p5.js you've just to define the function keyPressed() which is automatically called, if an button is pressed. You don't need to define any DOM event, p5.js does the job for you, you've just to "use" the existing callbacks, which are well documented.
I recommend to read p5.js - Get Started.
If you want to evaluate if the UP key is pressed, then you can evaluate the state of the built in variable keyIsPressed and keyCode in draw:
function draw() {
if (keyIsPressed && keyCode === UP_ARROW) {
// [...]
}
// [...]
}
See the short example:
let x=0, y=0, w=20, h=20;
function setup(){
createCanvas(window.innerWidth-20,window.innerHeight-20)
x = width/2;
y = height/2;
frameRate(10);
}
function draw(){
if (keyIsPressed && keyCode === LEFT_ARROW){
if (x > 0) {
x -= w;
}
}
if (keyIsPressed && keyCode === RIGHT_ARROW){
if (x < width-w) {
x += w;
}
}
if (keyIsPressed && keyCode === UP_ARROW) {
if (y > 0) {
y -= h;
}
}
if (keyIsPressed && keyCode === DOWN_ARROW){
if (y < height-h) {
y += h;
}
}
background(0);
stroke(255);
fill(255);
rect(x, y, w, h);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.9.0/p5.js"></script>
Related
I want to move an ellipse but I am stuck because my this.x value isn't changing... print("hit this.y = 30"); is working just fine but this--; is not working. Is anything blocking it? Thank you in advance for your time!
let redBubble = 0;
let x = false;
let y = 0;
let d = 0;
function setup() {
createCanvas(400, 400);
redBubble = new Bubble(300, 100, 10);
}
function draw() {
background(56, 23, 56);
redBubble.show();
redBubble.toRight();
redBubble.toLeft();
redBubble.example();
};
class Bubble {
constructor(x, y, d) {
this.x = x;
this.y = y;
this.d = d;
}
show() {
fill(255);
ellipse(this.x, this.y, this.d * 2);
}
toRight() {
if (this.x < 350) {
this.x = this.x + 1;
} else if (this.x === 350) {
this.y = this.y - 1;
}
}
toLeft() {
if (this.y <= 30) {
x = true;
} else if (this.y === 30) {
x = false;
}
}
example() {
if (x) {
this.x--; // not working
print("hit this.y = 30"); // working
}
}
}
Your code is running, but it sounds like it's not doing what you want it to.
All of your functions show(), toRight(), toLeft(), example(), are running every single draw. So every single time, toRight() is incrementing this.x by 1 (unless this.x is exactly 350). So when example() decrements it by 1, they are in effect canceling each other out and not changing this.x, when x is true and this.x is 349.
If you wanted the net effect to be this.x to decrease, you could replace this.x--; with this.x = this.x - 2;. But this is not a great solution.
Using states
It looks like you want to do different things at different stages of your animation, i.e. go right, then go up, then go left. As you've discovered, it can be pretty tricky to coordinate all of these different changes when all of your functions are running every cycle.
Instead, it's a lot easier to conditionally run only the functions you want, depending on what stage of your animation you're in. For this you can use an internal variable to store what it should be doing (I'll use this.direction for this example). Then in your draw function, you'd use if/then statements, like this:
function draw() {
background(56, 23, 56);
redBubble.show();
if (redBubble.direction === 'right') {
redBubble.goRight();
} else if (redBubble.direction === 'up') {
redBubble.goUp();
} else if (redBubble.direction === 'left') {
redBubble.goLeft();
}
};
And then in your individual functions, you'd change direction based on some condition. For example:
toRight() {
this.x = this.x + 1;
if (this.x === 350) {
this.direction = 'up';
}
}
Here's a working snippet of the above technique:
let redBubble;
function setup() {
createCanvas(400, 400);
redBubble = new Bubble(300, 100, 10);
}
function draw() {
background(56, 23, 56);
redBubble.show();
if (redBubble.direction === 'right') {
redBubble.goRight();
} else if (redBubble.direction === 'up') {
redBubble.goUp();
} else if (redBubble.direction === 'left') {
redBubble.goLeft();
} else if (redBubble.direction === 'down') {
redBubble.goDown();
}
};
class Bubble {
constructor(x, y, d) {
this.x = x;
this.y = y;
this.d = d;
this.direction = 'right';
}
show() {
fill(255);
ellipse(this.x, this.y, this.d * 2);
}
goRight() {
this.x = this.x + 1;
if (this.x === 350) {
this.direction = 'up';
}
}
goLeft() {
this.x = this.x - 1;
if (this.x === 30) {
this.direction = 'down';
}
}
goUp() {
this.y = this.y - 1;
if (this.y === 30) {
this.direction = 'left';
}
}
goDown() {
this.y = this.y + 1;
if (this.y === 300) {
this.direction = 'right';
}
}
}
<script src="https://unpkg.com/p5#0.6.1/lib/p5.min.js"></script>
Want to move an image across the screen. At the moment the image renders but does not translate the page when the specific keys are pressed. Can't see to get it to work - any suggestions?? I included the entire block of code as I believe it makes more sense.
var player;
var img;
function preload() {
img = loadImage("charcter.png");
}
function setup() {
background(255, 0, 0, 0.4);
createCanvas(1000, 600);
player = new Astro(0,0);
}
function draw() {
// clear();
player.show();
player.update();
}
function keyPressed() {
if (keyCode === UP_ARROW) {
player.dir(0, -10);
} else if (keyCode === DOWN_ARROW) {
player.dir(0, 10);
} else if (keyCode === RIGHT_ARROW) {
player.dir(10, 0);
} else if (keyCode === LEFT_ARROW) {
player.dir(-10, 0);
}
}
function Astro(x, y) {
this.x = x;
this.y = y;
this.xSpeed = 0;
this.ySpeed = 0;
this.img = img;
this.show = function() {
image(img, this.x, this.y);
};
this.update = function() {
this.x += this.xSpeed;
this.y += this.ySpeed;
if (this.x > width || this.x < 0) {
this.xSpeed = this.xSpeed * -1;
}
if (this.y > height || this.y < 0) {
this.ySpeed = this.ySpeed * -1;
}
};
this.dir = function(x, y) {
this.xspeed = x;
this.yspeed = y;
}
}
You need to take a step back and debug your program. For example, are you sure the keyPressed() function is firing? Are you sure the if statements are working the way you expected? Are you sure the player.dir() function is being called?
Use console.log() statements, or use the JavaScript debugger, to answer all of the above. Figure out exactly which line of code is behaving differently from what you expected, and then isolate that problem in a MCVE. (For example, don't include image loading code if the problem is not directly related to loading images. Use a basic rectangle instead.) Good luck.
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);
}
})();
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>
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);
}
I built centipede in javascript from a Khan Academy tutorial. Then I figured out how to put it into a web browser. however, the key presses are not working. I have tried to change the keyCode values, and change some of the function definitions to "void" but nothing has worked. The app uses processing .js to work. Here is the section of js dealing with the keys:
var Player = function(x,y,size,speed){
this.x = x;
this.y = y;
this.size = size;
this.speed = speed;
this.update = function(){
if(keys[LEFT]){
this.x -= this.speed;
}
if(keys[RIGHT]){
this.x += this.speed;
}
if(keys[DOWN]){
this.y += this.speed;
}
if(keys[UP]){
this.y -= this.speed;
}
if(keys[76] && bulletDelay < 0){
var b = new Bullet(this.x, this.y, 5,5);
bullets.push(b);
bulletDelay = 40;
}
if (this.x < 0){
this.x = 0;
}
if (this.x > width){
this.x = width;
}
if (this.y > 800){
this.y = 800;
}
//This adjusts the max height the player can move up on y-axis. Adjust to make more like Atari version
if (this.y < 100) {
this.y = 100;
}
noStroke();
fill(0,255,0);
ellipse(this.x, this.y, this.size, this.size);
};
};
I sorted it out. The keyPress and keyRelease functions needed to be changed from
var keyPressed = function(){
to
void keyPressed() . . .
similar for the keyReleased function
The syntax
if(keys[LEFT])
should be
if(keyCode == LEFT)
See the Processing.js keyCode reference and key
EDIT 1: For special keys (arrow, space), you should use keyCode instead of key.
Not entirely sure I posed the question in the best way but here goes...
I have been playing around with the HTML5 canvas API and have got as far as drawing a shape in the canvas and getting it to move around with the arrow keys.
I then tried to move my various variables and functions to a template so I could spawn multiple shapes (that would eventually be controlled by different keys).
This is what I have:
function player(x, y, z, colour, speed){
this.lx = x;
this.ly = y;
this.speed = 10;
this.playerSize = z;
this.colour = colour;
}
playerOne = new player(100, 100, 10, "#F0F");
function persona(z, colour){
zone.fillStyle = colour;
offset = 0 - (z / 2);
zone.fillRect(offset, offset, z, z);
}
function move(x, y){
playerOne.lx = playerOne.lx + x;
playerOne.ly = playerOne.ly + y;
zone.clearRect(0, 0, 500, 500);
zone.save();
zone.translate(playerOne.lx, playerOne.ly);
persona(playerOne.playerSize, playerOne.colour);
zone.restore();
}
window.onkeydown = function() {
var direction = this.event.keyCode;
var s = playerOne.speed;
// Arrow Keys
if( direction == 38 && playerOne.ly >= 10){ // Up
move(0,-s);
}
if( direction == 40 && playerOne.ly <= 490){ // Down
move(0,s);
}
if( direction == 37 && playerOne.lx >= 10){ // Left
move(-s,0);
}
if( direction == 39 && playerOne.lx <= 490){ // Right
move(s,0);
}
};
window.onload = function() {
zone = document.getElementById('canvas').getContext('2d');
zone.save();
zone.translate(playerOne.lx, playerOne.ly);
persona(playerOne.playerSize, playerOne.colour);
zone.restore();
};
So what I tried to do was move the persona function into the player template like this:
function player(x, y, z, colour, speed){
this.lx = x;
this.ly = y;
this.speed = 10;
function persona(){
zone.fillStyle = colour;
var offset = 0 - (z / 2);
zone.fillRect(offset, offset, z, z);
}
}
And then where before it said
persona(playerOne.playerSize, playerOne.colour);
it now just says
playerOne.persona();
But this is just totally flaking out and not working and I can't figure out why.
I'm probably going about it all the wrong way and I think the problem is that I'm trying to manipulate the canvas.context (call zone in my script) from within a object/template.
Perhaps its nothing to do with that at all and I an just not declaring my persona functions properly in the context of the template.
Documentation for the canvas API is very thin on the ground and any hint in the right direction will be very much appreciated.
First, you need the "zone" variable to be global, so declare it in the global scope to be able to access it from anywhere.
But I suggest you to use a framework to make animations really easier, like CakeJS or RaphaelJS.
Been hacking away at this for a few more minutes and got the following which is basically what I was trying to achieve from the start. I went in a slightly different direction with this but I would still like any feedback anyone may want to give.
function Player(x, y, z, colour, speed){
this.lx = x;
this.ly = y;
this.speed = speed;
this.it = false;
this.playerSize = z;
this.colour = colour;
this.move = move;
this.draw = persona;
}
function move(dx, dy){
this.lx = this.lx + (dx * this.speed);
this.ly = this.ly + (dy * this.speed);
}
function persona(){
zone.fillStyle = this.colour;
var offset = 0 - (this.playerSize / 2);
zone.fillRect(offset, offset, this.playerSize, this.playerSize);
}
playerOne = new Player(400,400, 10, "#F0F", 10);
playerTwo = new Player(100,100, 10, "#0F0", 10);
function drawPlayers(){
zone.clearRect(0, 0, 500, 500);
zone.save();
zone.translate(playerOne.lx, playerOne.ly);
playerOne.draw();
zone.restore();
zone.save();
zone.translate(playerTwo.lx, playerTwo.ly);
playerTwo.draw();
zone.restore();
}
window.onkeydown = function() {
var direction = this.event.keyCode;
// Arrows
if( direction == 38 && playerOne.ly >= 10){ // Up
playerOne.move(0,-1);
}
if( direction == 40 && playerOne.ly <= 490){ // Down
playerOne.move(0,1);
}
if( direction == 37 && playerOne.lx >= 10){ // Left
playerOne.move(-1,0);
}
if( direction == 39 && playerOne.lx <= 490){ // Right
playerOne.move(1,0);
}
// WASD
if( direction == 87 && playerTwo.ly >= 10){ // Up
playerTwo.move(0,-1);
}
if( direction == 83 && playerTwo.ly <= 490){ // Down
playerTwo.move(0,1);
}
if( direction == 65 && playerTwo.lx >= 10){ // Left
playerTwo.move(-1,0);
}
if( direction == 68 && playerTwo.lx <= 490){ // Right
playerTwo.move(1,0);
}
drawPlayers();
};
window.onload = function() {
zone = document.getElementById('canvas').getContext('2d');
drawPlayers();
};
Just commenting that your Player.draw method should be able to handle positioning your sprites on the canvas. This code behaves the same way as your solution, but hopefully is clearer and more compact.
function Player(x, y, z, colour, speed){
this.lx = x;
this.ly = y;
this.speed = speed;
this.it = false;
this.playerSize = z;
this.colour = colour;
}
Player.prototype = {
move: function(dx, dy){
this.lx = this.lx + (dx * this.speed);
this.ly = this.ly + (dy * this.speed);
},
draw: function(){
var size = this.playerSize,
offset = size / 2;
zone.fillStyle = this.colour;
zone.fillRect(playerTwo.lx - offset, playerTwo.ly - offset, size, size);
}
};
playerOne = new Player(400,400, 10, "#F0F", 10);
playerTwo = new Player(100,100, 10, "#0F0", 10);
function drawPlayers(){
zone.clearRect(0, 0, 500, 500);
playerOne.draw();
playerTwo.draw();
}