Can you declare <canvas> methods within a template in javascript? - javascript

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

Related

Problem with Khan Academy Challenge - Hovering over a button is inconsistent

I'm working my way through the Khan Academy advanced JS and I've encountered what I feel should be a simple problem, but I've just gotten entirely stuck on it. I'm working on a memory tile game, trying to add some extra features.
I'm trying to change the stroke colour of an object when the mouse is over it - I've included this as a prototype in each 'tile' object and this check is performed during the draw function - so every frame.
The stroke colour does change as I wanted it to, but stops working once I 'flip up' a couple of the tiles and they flip back down, the hover effect stops working. It now works only when a single tile is flipped up.
I can't figure out why the 'face up' variable in the tile object would be affecting the hover check - I feel I'm missing something obvious but for the life of me can't see it.
I've looked at some similar projects and their button highlighting, they seem to do exactly the same thing as me.
The Tile Object -
var Tile = function(x, y, face) {
this.x = x;
this.y = y;
this.size = 70;
this.face = face;
this.isFaceUp = false;
this.isMatch = false;
};
Tile.prototype.draw = function() {
this.hover();
fill(214, 247, 202);
strokeWeight(2);
rect(this.x, this.y, this.size, this.size, 10);
if (this.isFaceUp) {
image(this.face, this.x, this.y, this.size, this.size);
} else {
image(getImage("avatars/leaf-green"), this.x, this.y, this.size, this.size);
}
};
//Mouse Hover code - only works one time for some reason. After flipping back this stops working for some reason.
Tile.prototype.hover = function() {
stroke(0, 0, 0);
if (this.isMouseInside()){
stroke(7, 122, 44);
}
};
Tile.prototype.isUnderMouse = function(x, y) {
return x >= this.x && x <= this.x + this.size &&
y >= this.y && y <= this.y + this.size;
};
//check if mouse cursor is inside the tile
Tile.prototype.isMouseInside = function() {
return mouseX > this.x &&
mouseX < (this.x + this.size) &&
mouseY > this.y &&
mouseY < (this.y + this.size) &&
this.isFaceUp === false;
};
The Mouse Clicked Function - prewritten by KA
mouseClicked = function() {
for (var i = 0; i < tiles.length; i++) {
var tile = tiles[i];
if (tile.isUnderMouse(mouseX, mouseY)) {
if (flippedTiles.length < 2 && !tile.isFaceUp) {
tile.isFaceUp = true;
flippedTiles.push(tile);
if (flippedTiles.length === 2) {
numTries++;
if (flippedTiles[0].face === flippedTiles[1].face) {
flippedTiles[0].isMatch = true;
flippedTiles[1].isMatch = true;
flippedTiles.length = 0;
numMatches++;
}
delayStartFC = frameCount;
}
}
loop();
}
}
};
The Draw function - prewritten by KA
draw = function() {
background(255, 255, 255);
if (delayStartFC && (frameCount - delayStartFC) > 30) {
for (var i = 0; i < tiles.length; i++) {
var tile = tiles[i];
if (!tile.isMatch) {
tile.isFaceUp = false;
}
}
flippedTiles = [];
delayStartFC = null;
noLoop();
}
for (var i = 0; i < tiles.length; i++) {
tiles[i].draw();
}
if (numMatches === tiles.length/2) {
fill(0, 0, 0);
textSize(20);
text("You found them all in " + numTries + " tries!", 20, 375);
}
};
noLoop();
It drove me nuts trying to work out a function for mouse over then it hit me, my solution to change the stroke weight on rollover. in the Tile.prototype.draw function add:
if (this.isUnderMouse(mouseX, mouseY)){
strokeWeight(5);
}
else {
strokeWeight(2);
}

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

Canvas collision

I am a new in javascript and trying to find out how to make a collision with ball and plank which will stop the game and alert player with something like "You lost". But I only want red balls to hit the plank and blue to pass on without touching. Here is code that I am working on. (I dont mind if you could help to do collision only with both balls)
var spawnRate = 100;
var spawnRateOfDescent = 2;
var lastSpawn = -10;
var objects = [];
var startTime = Date.now();
function spawnRandomObject() {
var t;
if (Math.random() < 0.50) {
t = "red";
} else {
t = "blue";
}
var object = {
type: t,
x: Math.random() * (canvas.width - 30) + 15,
y: 0
}
objects.push(object);
}
function animate() {
var time = Date.now();
if (time > (lastSpawn + spawnRate)) {
lastSpawn = time;
spawnRandomObject();
}
for (var i = 0; i < objects.length; i++) {
var object = objects[i];
object.y += spawnRateOfDescent;
ctx.beginPath();
ctx.arc(object.x, object.y, 8, 0, Math.PI * 2);
ctx.closePath();
ctx.fillStyle = object.type;
ctx.fill();
}
}
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
var paddleHeight = 10;
var paddleWidth = 60;
var paddleY = 480
var paddleX = (canvas.width-paddleWidth)/2;
var rightPressed = false;
var leftPressed = false;
document.addEventListener("keydown", keyDownHandler, false);
document.addEventListener("keyup", keyUpHandler, false);
function keyDownHandler(e) {
if(e.keyCode == 39) {
rightPressed = true;
}
else if(e.keyCode == 37) {
leftPressed = true;
}
}
function keyUpHandler(e) {
if(e.keyCode == 39) {
rightPressed = false;
}
else if(e.keyCode == 37) {
leftPressed = false;
}
}
function drawPaddle() {
ctx.beginPath();
ctx.rect(paddleX, paddleY, paddleWidth, paddleHeight);
ctx.fillStyle = "#0095DD";
ctx.fill();
ctx.closePath();
}
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawPaddle();
animate();
if(rightPressed && paddleX < canvas.width-paddleWidth) {
paddleX += 3;
}
else if(leftPressed && paddleX > 0) {
paddleX -= 3;
}
}
setInterval(draw, 10);
Thanks!
If you have an object like this:
let ball = { type: 'red', x: 10, y: 10, width: 10, height: 10 };
You might want to consider adding a method to this to check if it overlaps any other rectangle:
ball.overlapsBall = function( otherBall ){
return !(
otherBall.x + otherBall.width < this.x
&& otherBall.y + otherBall.height < this.y
&& otherBall.y > this.y + this.height
&& otherBall.x > this.x + this.height
);
}
You do this by checking if it does not overlap, which is only true if one box is entirely outside of the other (have a read through the if statement and try to visualise it, its actually rather simple)
In your draw function you could now add a loop to see if any overlap occurs:
var overlap = objects.filter(function( ball ) { return paddle.overlapsBall( ball ) });
You could even place an if statement to check it's type! (The filter will take you entire array of balls and check the overlaps, and remove anything from the array that does not return true. Now you can use overlaps.forEach(function( ball ){ /* ... */}); to do something with all the balls that overlapped your paddle.)
One last thing, if you are planning on doing this with many objects you might want to consider using a simple class like this for every paddle or ball you make:
class Object2D {
constructor(x = 0, y = 0;, width = 1, height = 1){
this.x = x;
this.y = x;
this.width = width;
this.height = height;
}
overlaps( otherObject ){
!( otherObject.x + otherObject.width < this.x && otherObject.y + otherObject.height < this.y && otherObject.y > this.y + this.height && otherObject.x > this.x + this.height );
}
}
This allows you to this simple expression to create a new object that automatically has a method to check for overlaps with similar objects:
var paddle = new Object2D(0,0,20,10);
var ball = new Object2D(5,5,10,10);
paddle.overlaps( ball ); // true!
On top of that, you are ensured that any Object2D contains the values you will need for your calculations. You can check if this object is if the right type using paddle instanceof Object2D (which is true).
Note Please note, as #Janje so continuously points out in the comments below, that we are doing a rectangle overlap here and it might create some 'false positives' for all the pieces of rectangle that aren't the circle. This is good enough for most cases, but you can find the math for other overlaps and collisions easily ith a quick google search.
Update: Simple Implementation
See below for a very simple example of how overlaps work in action:
var paddle = { x: 50, y: 50, width: 60, height: 20 };
var box = { x: 5, y: 20, width: 20, height: 20 };
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
document.body.appendChild( canvas );
canvas.width = 300;
canvas.height = 300;
function overlaps( a, b ){
return !!( a.x + a.width > b.x && a.x < b.x + b.width
&& a.y + a.height > b.y && a.y < b.y + b.height );
}
function animate(){
ctx.clearRect( 0, 0, canvas.width, canvas.height );
ctx.fillStyle = overlaps( paddle, box ) ? "red" : "black";
ctx.fillRect( paddle.x, paddle.y, paddle.width, paddle.height );
ctx.fillRect( box.x, box.y, box.width, box.height );
window.requestAnimationFrame( animate );
}
canvas.addEventListener('mousemove', function(event){
paddle.x = event.clientX - paddle.width / 2;
paddle.y = event.clientY - paddle.height / 2;
})
animate();

How to find out which part of the circle was clicked, using HTML5 canvas?

I am trying to create Simon game using HTML5 canvas and vanilla JavaScript. I am confused about the coordinate system in the arc() method. I have divided the circle into 4 quadrants and would like to alert the number of the quadrant clicked. But, I am not sure how to find out which part of the circle was clicked. https://jsfiddle.net/xawpLdys/1/
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
var x = canvas.width / 2;
var y = canvas.height / 2;
var pads = [];
var angle = 2 * Math.PI / 4;
var color = ["green","red","blue","yellow"];
var Pads = function(x, y, radius, start, end) {
this.x = x;
this.y = y;
this.radius = radius;
this.start = start;
this.end = end;
};
function drawSimon(radius) {
for (var i = 0; i < 4; i++) {
context.beginPath();
context.moveTo(x, y);
context.arc(x, y, radius, i*angle, (i+1)*angle, false);
context.lineWidth = radius;
context.fillStyle = color[i];
context.fill();
context.lineWidth = 2;
context.strokeStyle = '#444';
context.stroke();
var pad = new Pads(x, y, radius, i*angle, (i+1)*angle);
pads.push(pad);
}
}
drawSimon(150);
$('#myCanvas').click(function (e) {
/*for (var i = 0; i < pads.length; i++) {
if (//condition matches) {
alert (i);
}
}*/
});
Try This
This example just translates the clicked e.pageX and e.pageY to normal quadrant system. And after some condition, you can determine which part has been clicked.
$('#myCanvas').click(function (e) {
var nx,ny;
nx=-(x- e.pageX);
ny=y- e.pageY;
if (nx>0 && ny>0){
alert('Yellow');
}else if (nx<0 && ny>0){
alert('Blue');
}else if (nx>0 && ny<0){
alert('Green');
}else if (nx<0 && ny<0){
alert('Red');
}
});
Here is the fiddle https://jsfiddle.net/xawpLdys/3/
UPDATE
John S was right, (It counts clicks that are outside the circle). To prevent the clicks outside the circle from considering, we need to just find the distance from the center of the circle and the clicked point. Then compare the distance with the circle's radius to see it is inside radius.
The updated code :
$('#myCanvas').click(function (e) {
var nx,ny;
nx=-(x- e.pageX);
ny=y- e.pageY;
var dx = Math.abs(Math.abs(x)-Math.abs(e.pageX));
var dy = Math.abs(Math.abs(y)-Math.abs(e.pageY));
var distance_clicked = Math.sqrt((dx*dx)+(dy*dy));
if(distance_clicked <= radius){
if (nx>0 && ny>0){
alert('Yellow');
}else if (nx<0 && ny>0){
alert('Blue');
}else if (nx>0 && ny<0){
alert('Green');
}else if (nx<0 && ny<0){
alert('Red');
}
}
});
Here is the updated fiddle https://jsfiddle.net/xawpLdys/8/
It still have the limitations of dividing the circle more than 4 slices.
The accepted answer seems a bit limited. It counts clicks that are outside the circle. That could be fixed fairly easily, but it would still be limited to four sections.
To determine if a point is in a sector:
First check if it is within the circle. The Pythagorean theorem comes into play here. Square the x and y values, if their sum is less than or equal to the radius squared, the point is in the circle.
If the point is in the circle, then check if its angle is between the start and end angles for the sector. You can get the point's angle using the arc tangent function from trigonometry.
Try this jsfiddle.
Here are the types that help make this work:
var Circle = function(center, radius) {
this.center = center;
this.radius = radius;
this._radiusSquared = Math.pow(this.radius, 2);
}
$.extend(Circle.prototype, {
containsPoint: function(point) {
var relPoint = this.pointToRelPoint(point);
return Math.pow(relPoint.x, 2) + Math.pow(relPoint.y, 2)
<= this._radiusSquared;
},
getAngleForPoint: function(point) {
var relPoint = this.pointToRelPoint(point);
return Math.atan2(-relPoint.y, -relPoint.x) + Math.PI;
},
pointToRelPoint: function(point) {
return { x: point.x - this.center.x, y: point.y - this.center.y }
}
});
var CircleSector = function(startAngle, endAngle) {
this.startAngle = startAngle;
this.endAngle = endAngle;
};
$.extend(CircleSector.prototype, {
containsAngle: function(angle) {
return (angle >= this.startAngle) && (angle < this.endAngle);
},
containsPoint: function(circle, point) {
return circle.containsPoint(point)
&& this.containsAngle(circle.getAngleForPoint(point));
}
});

Gravity / Jumping ball in canvas, javascript

My project is to create a game, ball which should be jumping on a keypress, go left and right. My problem is, that the gravity I have implemented into that game brought me a problem. The ball is stuck in the ground, which is a picture on the bottom of my canvas. It is stuck because of wrong collision code, I guess.
Question is, if you can help me with solving this problem and maybe give a a hint how to continue, because I tried to find which collision is making that problem but I found nothing.
Link to that game is here: Game
JSFiddle: Fiddle
Code where the players collision is located /other codes are working as they should so I am not going to put them here, only if you will really need them/
function Player(x, y) {
var size = 70;
GameObject.call(this, x, y, size);
this.img = document.getElementById("lopta");
this.rotation = 0;
this.dx = Math.random() * 50 - 25;
this.dy = Math.random() * 50 - 25;
}
// Dedi vlastnosti z GameObject
Player.prototype = Object.create(GameObject.prototype);
Player.prototype.move = function(dt) {
var x = this.x;
var y = this.y;
var sirka = canvas.width;
var vyska = canvas.height;
var bounce = 0.6;
// Gravitacia
this.dy += 9.8 * dt;
// Posun
if ( keys[37] ) {
this.rotation -= dt;
x-=5;
}
if ( keys[39] ) {
this.rotation += dt;
x+=5;
}
if ( keys[38] ) y-=5;
if ( keys[40] ) y+=5;
// Test novej pozicie
var collision = false;
for (i in scene) {
var obj = scene[i];
var test =
x -35>= obj.x + obj.size ||
x + this.size -35<= obj.x ||
y -35>= obj.y + obj.size ||
this.dy - 35 >= obj.dy + obj.size ||
y + this.size -35 <= obj.y ||
this.dy + this.size -35<= obj.dy;
if (!test) {
collision = true;
break;
}
}
// Posun bez kolizie
if (!collision) {
this.x = x;
this.y = y;
}
// Posun
//this.x += this.dx * dt;
this.y += this.dy * dt;
// podmienky aby lopta nevysla z hracieho pola cize z canvasu
if (this.x + this.size - 35> canvas.width) {
this.x = canvas.width - this.size +35;
}
if (this.x -35 < 0) {
this.x = 35;
}
if (this.y+this.size - 35 > canvas.height) {
this.y = canvas.height - this.size + 35;
this.dy *= -bounce;
if(this.dy * (-bounce) < 4)
this.dy = 0;
}
if (this.y - 35< 0) {
this.y = 35;
};
};
Player.prototype.draw = function() {
ctx.save();
ctx.translate(this.x, this.y);
ctx.rotate(this.rotation);
ctx.translate(-35, -35);
//ctx.scale(this.size,this.size);
ctx.drawImage(this.img, 0, 0, this.size, this.size);
ctx.restore();
};
First, Pardon me for not totally understanding what your asking.
If you're having problems detecting collisions between the ball & brick-rects or the ball & walls, here are some useful utility functions for you:
// Given circle & rect definitions
var circle={x:50,y:50,r:25};
var rect={x:125,y:125,w:50,h:50};
// detect if circle & rect are colliding
function RectCircleColliding(circle,rect){
var distX = Math.abs(circle.x - rect.x-rect.w/2);
var distY = Math.abs(circle.y - rect.y-rect.h/2);
if (distX > (rect.w/2 + circle.r)) { return false; }
if (distY > (rect.h/2 + circle.r)) { return false; }
if (distX <= (rect.w/2)) { return true; }
if (distY <= (rect.h/2)) { return true; }
var dx=distX-rect.w/2;
var dy=distY-rect.h/2;
return (dx*dx+dy*dy<=(circle.r*circle.r));
}
// detect if circle is colliding with canvas sides
function CircleWallColliding(circle){
var cx=circle.x;
var cy=circle.y;
var r=circle.r;
if(cx-r<0 || cx+r>canvas.width || cy-r<0 || cy+r>canvas.height){return(true);}
return(false);
}
Example code and a Demo:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
function reOffset(){
var BB=canvas.getBoundingClientRect();
offsetX=BB.left;
offsetY=BB.top;
}
var offsetX,offsetY;
reOffset();
window.onscroll=function(e){ reOffset(); }
ctx.lineWidth=3;
var isDown=false;
var startX,startY;
var circle={x:50,y:50,r:25};
var rect={x:125,y:125,w:50,h:50};
$("#canvas").mousemove(function(e){handleMouseMove(e);});
draw('green');
function draw(circleFill){
ctx.clearRect(0,0,cw,ch);
ctx.fillStyle='blue';
ctx.fillRect(rect.x,rect.y,rect.w,rect.h);
ctx.beginPath();
ctx.arc(circle.x,circle.y,circle.r,0,Math.PI*2);
ctx.closePath();
ctx.fillStyle=circleFill;
ctx.fill();
ctx.stroke();
}
function handleMouseMove(e){
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);
circle.x=mouseX;
circle.y=mouseY;
var isColliding=RectCircleColliding(circle,rect)||CircleWallColliding(circle);
draw(isColliding?'red':'green');
}
// detect if circle & rect are colliding
function RectCircleColliding(circle,rect){
var distX = Math.abs(circle.x - rect.x-rect.w/2);
var distY = Math.abs(circle.y - rect.y-rect.h/2);
if (distX > (rect.w/2 + circle.r)) { return false; }
if (distY > (rect.h/2 + circle.r)) { return false; }
if (distX <= (rect.w/2)) { return true; }
if (distY <= (rect.h/2)) { return true; }
var dx=distX-rect.w/2;
var dy=distY-rect.h/2;
return (dx*dx+dy*dy<=(circle.r*circle.r));
}
// detect if circle is colliding with canvas sides
function CircleWallColliding(circle){
var cx=circle.x;
var cy=circle.y;
var r=circle.r;
if(cx-r<0 || cx+r>canvas.width || cy-r<0 || cy+r>canvas.height){return(true);}
return(false);
}
body{ background-color: ivory; }
#canvas{border:1px solid red;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<h4>Move ball with mouse.<br>Ball turns red if colling with rect or wall.</h4>
<canvas id="canvas" width=300 height=300></canvas>
A few things I'm noticing. First, you separated your colliding movement logic from your bouncing logic, so the ball can essentially fall straight through objects. From what I can tell, objects only block manual movement. Also, said manual movement ignores dt, so the speed of it can vary with framerate.
Anyway, a breakpoint set on the line collision = true; reveals the problem: One of your scene's objects does not have a size set, which causes obj.x + obj.size and obj.y + obj.size to return NaN which is like infinity. You have several such, actually: It's the grass object.
So here are your choices: Either don't make the grass a part of the scene (It shouldn't participate in collision anyway), and instead change the rendering logic to use a separate object array from the collision logic (as would make sense), or set size = 0; for the grass.

Categories

Resources