I tried to rewrite a vanilla JavaScript Pong game in addition with PixiJS, mainly because I had resizing issues to get the canvas responsive.
The resizing works fine with PixiJS, but all the objects started to have trailing paths: Fiddle1
I figured that's because added new instances on update instead of just updating the position, so I tried to rework it from scratch again. The movement of the player works so far but I can't manage to get the ball working...I am kinda lost and willed to learn. Help is much appreciated ...Thanks a lot!
Fiddle2
import 'pixi.js';
// define gamne variables
const appWidth = window.innerWidth;
const appHeight = window.innerHeight;
const paddleWidth = 150;
const paddleHeight = 30;
const ballSize = 15;
const halfBall = ballSize / 2;
const appWidthHalf = appWidth / 2;
const appHeightHalf = appHeight / 2;
const paddleWidthHalf = paddleWidth / 2;
const pongColor = 0x57dfbf;
const bgColor = 0x282625;
const computerPositionX = appWidthHalf - paddleWidthHalf;
const computerPositionY = 50;
const playerPositionX = computerPositionX;
const playerPositionY = appHeight - computerPositionY - paddleHeight;
const ballPositionX = appWidthHalf - halfBall;
const ballPositionY = appHeightHalf - halfBall;
const playerSpeed = 4;
const computerSpeed = 4;
const ballSpeed = 3;
// Setup the ticker and the root stage PIXI.Container.
const app = new PIXI.Application(appWidth, appHeight, {
antialias: false,
backgroundColor: bgColor,
transparent: false,
resolution: 1,
});
// append app to body
document.body.appendChild(app.view);
// create graphic elements
const player = new PIXI.Graphics();
const computer = new PIXI.Graphics();
const ball = new PIXI.Graphics();
// Player
player
.beginFill(pongColor)
.drawRect(playerPositionX, playerPositionY, paddleWidth, paddleHeight)
.endFill();
// Computer
computer
.beginFill(pongColor)
.drawRect(computerPositionX, computerPositionY, paddleWidth, paddleHeight)
.endFill();
// Ball
ball
.beginFill(pongColor)
.drawRect(ballPositionX, ballPositionY, ballSize, ballSize)
.endFill();
// Player Movement
player.update = function () {
for (const key in keysDown) {
const value = Number(key);
if (value === 37) {
player.move(-playerSpeed, 0);
} else if (value === 39) {
player.move(playerSpeed, 0);
} else {
player.move(0, 0);
}
}
};
player.move = function (x, y) {
this.x += x;
this.y += y;
this.x_speed = x;
this.y_speed = y;
if (this.x < -appWidthHalf + paddleWidthHalf) {
this.x = -appWidthHalf + paddleWidthHalf;
this.x_speed = 0;
} else if (this.x + this.width - paddleWidthHalf > appWidthHalf) {
this.x = appWidthHalf - this.width + paddleWidthHalf;
this.x_speed = 0;
}
};
// computer Movement
// eslint-disable-next-line
computer.update = function(ball) {
const x_pos = ball.x;
let diff = -(computer.x + paddleWidthHalf - x_pos);
if (diff < 0 && diff < -computerSpeed) {
diff = -ballSize;
} else if (diff > 0 && diff > computerSpeed) {
diff = ballSize;
}
computer.position.set(diff, 0);
if (computer.x < 0) {
computer.x = 0;
} else if (computer.x + paddleWidthHalf > appWidth) {
computer.x = appWidth - paddleWidthHalf;
}
};
// Ball Movement
ball.update = function (paddle1, paddle2) {
this.x += this.x_speed;
this.y += this.y_speed;
const top_x = this.x - ballSize;
const top_y = this.y - ballSize;
const bottom_x = this.x + ballSize;
const bottom_y = this.y + ballSize;
if (this.x - ballSize < 0) {
this.x = ballSize;
this.x_speed = -this.x_speed;
} else if (this.x + ballSize > appWidth) {
this.x = appWidth - ballSize;
this.x_speed = -this.x_speed;
}
if (this.y < 0 || this.y > appHeight) {
this.x_speed = 0;
this.y_speed = ballSpeed;
this.x = appWidthHalf;
this.y = appHeightHalf;
}
if (top_y > appHeightHalf) {
if (
top_y < paddle1.y + paddle1.height &&
bottom_y > paddle1.y &&
top_x < paddle1.x + paddle1.width &&
bottom_x > paddle1.x
) {
this.y_speed = -ballSpeed;
this.x_speed += paddle1.x_speed / 2;
this.y += this.y_speed;
}
} else if (
top_y < paddle2.y + paddle2.height &&
bottom_y > paddle2.y &&
top_x < paddle2.x + paddle2.width &&
bottom_x > paddle2.x
) {
this.y_speed = ballSpeed;
this.x_speed += paddle2.x_speed / 2;
this.y += this.y_speed;
}
};
// controls
const keysDown = {};
window.addEventListener('keydown', (event) => {
keysDown[event.keyCode] = true;
});
window.addEventListener('keyup', (event) => {
delete keysDown[event.keyCode];
});
// update function
function update() {
player.update();
computer.update(ball);
ball.update(player, computer);
}
// append childs to app
app.stage.addChild(player);
app.stage.addChild(computer);
app.stage.addChild(ball);
// game loop
app.ticker.add(update);
Fiddle 1 definitely had issue, as you were not changing the ball.x and y there, but instead changing the graphics object, in fiddle2 it seemed, you were doing
good job with it. You just have some logical errors in the code, regarding setting the x- and y-coordinates and the ball movement.
So if you add console.log(this.x, this.y) inside the ball.update function in fiddle2, you will notice the NaN values.
To see the ball moving, if you set the initial values with:
ball.x_speed = 0;
ball.y_speed = 0;
And then
paddle.x_speed
paddle.y_speed
To
paddle.x
paddle.y
You see the ball moving at least (though collision logic doesn't work), (modified example in the fiddle below)
https://jsfiddle.net/4L6zbd01/
Related
I'm working on a project where I simulate physics with balls.
Here is the link to the p5 editor of the project.
My problem is the following, when I add a lot of ball (like 200), balls are stacking but some of them will eventually collapse and I don't know why.
Can somebody explain why it does this and how to solve the problem ?
Thanks.
Here is the code of the sketch.
document.oncontextmenu = function () {
return false;
}
let isFlushing = false;
let isBallDiameterRandom = false;
let displayInfos = true;
let displayWeight = false;
let clickOnce = false;
let FRAME_RATE = 60;
let SPEED_FLUSH = 3;
let Y_GROUND;
let lastFR;
let balls = [];
function setup() {
frameRate(FRAME_RATE);
createCanvas(window.innerWidth, window.innerHeight);
Y_GROUND = height / 20 * 19;
lastFR = FRAME_RATE;
}
function draw() {
background(255);
if (isFlushing) {
for (let i = 0; i < SPEED_FLUSH; i++) {
balls.pop();
}
if (balls.length === 0) {
isFlushing = false;
}
}
balls.forEach(ball => {
ball.collide();
ball.move();
ball.display(displayWeight);
ball.checkCollisions();
});
if (mouseIsPressed) {
let ballDiameter;
if (isBallDiameterRandom) {
ballDiameter = random(15, 101);
} else {
ballDiameter = 25;
}
if (canAddBall(mouseX, mouseY, ballDiameter)) {
isFlushing = false;
let newBall = new Ball(mouseX, mouseY, ballDiameter, balls);
if (mouseButton === LEFT && !clickOnce) {
balls.push(newBall);
clickOnce = true;
}
if (mouseButton === RIGHT) {
balls.push(newBall);
}
}
}
drawGround();
if (displayInfos) {
displayShortcuts();
displayFrameRate();
displayBallCount();
}
}
function mouseReleased() {
if (mouseButton === LEFT) {
clickOnce = false;
}
}
function keyPressed() {
if (keyCode === 32) {//SPACE
displayInfos = !displayInfos;
}
if (keyCode === 70) {//F
isFlushing = true;
}
if (keyCode === 71) {//G
isBallDiameterRandom = !isBallDiameterRandom;
}
if (keyCode === 72) {//H
displayWeight = !displayWeight;
}
}
function canAddBall(x, y, d) {
let isInScreen =
y + d / 2 < Y_GROUND &&
y - d / 2 > 0 &&
x + d / 2 < width &&
x - d / 2 > 0;
let isInAnotherBall = false;
for (let i = 0; i < balls.length; i++) {
let d = dist(x, y, balls[i].position.x, balls[i].position.y);
if (d < balls[i].w) {
isInAnotherBall = true;
break;
}
}
return isInScreen && !isInAnotherBall;
}
function drawGround() {
strokeWeight(0);
fill('rgba(200,200,200, 0.25)');
rect(0, height / 10 * 9, width, height / 10);
}
function displayFrameRate() {
if (frameCount % 30 === 0) {
lastFR = round(frameRate());
}
textSize(50);
fill(255, 0, 0);
let lastFRWidth = textWidth(lastFR);
text(lastFR, width - lastFRWidth - 25, 50);
textSize(10);
text('fps', width - 20, 50);
}
function displayBallCount() {
textSize(50);
fill(255, 0, 0);
text(balls.length, 10, 50);
let twBalls = textWidth(balls.length);
textSize(10);
text('balls', 15 + twBalls, 50);
}
function displayShortcuts() {
let hStart = 30;
let steps = 15;
let maxTW = 0;
let controlTexts = [
'LEFT CLICK : add 1 ball',
'RIGHT CLICK : add 1 ball continuously',
'SPACE : display infos',
'F : flush balls',
'G : set random ball diameter (' + isBallDiameterRandom + ')',
'H : display weight of balls (' + displayWeight + ')'
];
textSize(11);
fill(0);
for (let i = 0; i < controlTexts.length; i++) {
let currentTW = textWidth(controlTexts[i]);
if (currentTW > maxTW) {
maxTW = currentTW;
}
}
for (let i = 0; i < controlTexts.length; i++) {
text(controlTexts[i], width / 2 - maxTW / 2 + 5, hStart);
hStart += steps;
}
fill(200, 200, 200, 100);
rect(width / 2 - maxTW / 2,
hStart - (controlTexts.length + 1) * steps,
maxTW + steps,
(controlTexts.length + 1) * steps - steps / 2
);
}
Here is the code of the Ball class.
class Ball {
constructor(x, y, w, e) {
this.id = e.length;
this.w = w;
this.e = e;
this.progressiveWidth = 0;
this.rgb = [
floor(random(0, 256)),
floor(random(0, 256)),
floor(random(0, 256))
];
this.mass = w;
this.position = createVector(x + random(-1, 1), y);
this.velocity = createVector(0, 0);
this.acceleration = createVector(0, 0);
this.gravity = 0.2;
this.friction = 0.5;
}
collide() {
for (let i = this.id + 1; i < this.e.length; i++) {
let dx = this.e[i].position.x - this.position.x;
let dy = this.e[i].position.y - this.position.y;
let distance = sqrt(dx * dx + dy * dy);
let minDist = this.e[i].w / 2 + this.w / 2;
if (distance < minDist) {
let angle = atan2(dy, dx);
let targetX = this.position.x + cos(angle) * minDist;
let targetY = this.position.y + sin(angle) * minDist;
this.acceleration.set(
targetX - this.e[i].position.x,
targetY - this.e[i].position.y
);
this.velocity.sub(this.acceleration);
this.e[i].velocity.add(this.acceleration);
//TODO : Effets bizarre quand on empile les boules (chevauchement)
this.velocity.mult(this.friction);
}
}
}
move() {
this.velocity.add(createVector(0, this.gravity));
this.position.add(this.velocity);
}
display(displayMass) {
if (this.progressiveWidth < this.w) {
this.progressiveWidth += this.w / 10;
}
stroke(0);
strokeWeight(2);
fill(this.rgb[0], this.rgb[1], this.rgb[2], 100);
ellipse(this.position.x, this.position.y, this.progressiveWidth);
if (displayMass) {
strokeWeight(1);
textSize(10);
let tempTW = textWidth(int(this.w));
text(int(this.w), this.position.x - tempTW / 2, this.position.y + 4);
}
}
checkCollisions() {
if (this.position.x > width - this.w / 2) {
this.velocity.x *= -this.friction;
this.position.x = width - this.w / 2;
} else if (this.position.x < this.w / 2) {
this.velocity.x *= -this.friction;
this.position.x = this.w / 2;
}
if (this.position.y > Y_GROUND - this.w / 2) {
this.velocity.x -= this.velocity.x / 100;
this.velocity.y *= -this.friction;
this.position.y = Y_GROUND - this.w / 2;
} else if (this.position.y < this.w / 2) {
this.velocity.y *= -this.friction;
this.position.y = this.w / 2;
}
}
}
I see this overlapping happen when the sum of ball masses gets bigger than the elasticity of the balls. At least it seems so. I made a copy with a smaller pool so it doesn't take so much time to reproduce the problem.
In the following example, with 6 balls (a mass of 150 units) pressing on the base row, we see that the 13 balls in the base row overlap. The base row has a width of ca. 300 pixels, which is only enough space for 12 balls of diameter 25. I think this is showing the limitation of the model: the balls are displayed circular but indeed have an amount of elasticity that they should display deformed instead. It's hard to say how this can be fixed without implementing drawing complicated shapes. Maybe less friction?
BTW: great physics engine you built there :-)
Meanwhile I was able to make another screenshot with even fewer balls. The weight of three of them (eq. 75 units) is sufficient to create overlapping in the base row.
I doubled the size of the balls and changed the pool dimensions as to detedt that there is a more serious error in the engine. I see that the balls are pressed so heavily under pressure that they have not enough space for their "volume" (area). Either they have to implode or it's elastic counter force must have greater impact of the whole scene. If you pay close attention to the pendulum movements made by the balls at the bottom, which have the least space, you will see that they are very violent, but apparently have no chance of reaching the outside.
Could it be that your evaluation order
balls.forEach(ball => {
ball.collide();
ball.move();
ball.display(displayWeight);
ball.checkCollisions();
});
is not able to propagate the collisions in a realistic way?
I am trying to make a pong canvas game responsive on resize, i am nor sure what i am missing and what would be the best way to have it responsive while still not being to process heavy. i tried to delete and reinject the canvas on resize what is a bad idea i think, the responsiveness works on load so far... thanks a lot for the insights, much appreciated
http://jsfiddle.net/ut0hsvd4/1/
var animate = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function(callback) {
window.setTimeout(callback, 1000 / 60)
};
var canvas = document.createElement("canvas");
var width = document.getElementById('pong').offsetWidth;
var height = document.getElementById('pong').offsetHeight;
canvas.width = width;
canvas.height = height;
var context = canvas.getContext('2d');
var player = new Player();
var computer = new Computer();
var ball = new Ball(width / 2, height / 2);
var keysDown = {};
var render = function() {
context.fillStyle = "red";
context.fillRect(0, 0, width, height);
player.render();
computer.render();
ball.render();
};
var update = function() {
player.update();
computer.update(ball);
ball.update(player.paddle, computer.paddle);
};
var step = function() {
update();
render();
animate(step);
};
function Paddle(x, y, width, height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.x_speed = 0;
this.y_speed = 0;
}
Paddle.prototype.render = function() {
context.fillStyle = "#0000FF";
context.fillRect(this.x, this.y, this.width, this.height);
};
Paddle.prototype.move = function(x, y) {
this.x += x;
this.y += y;
this.x_speed = x;
this.y_speed = y;
if (this.x < 0) {
this.x = 0;
this.x_speed = 0;
} else if (this.x + this.width > width) {
this.x = width - this.width;
this.x_speed = 0;
}
};
function Computer() {
this.paddle = new Paddle(width / 2 - 25, 10, 50, 10);
}
Computer.prototype.render = function() {
this.paddle.render();
};
Computer.prototype.update = function(ball) {
var x_pos = ball.x;
var diff = -((this.paddle.x + (this.paddle.width / 2)) - x_pos);
if (diff < 0 && diff < -4) {
diff = -5;
} else if (diff > 0 && diff > 4) {
diff = 5;
}
this.paddle.move(diff, 0);
if (this.paddle.x < 0) {
this.paddle.x = 0;
} else if (this.paddle.x + this.paddle.width > width) {
this.paddle.x = width - this.paddle.width;
}
};
function Player() {
this.paddle = new Paddle(width / 2 - 25, height - 20, 50, 10);
}
Player.prototype.render = function() {
this.paddle.render();
};
Player.prototype.update = function() {
for (var key in keysDown) {
var value = Number(key);
if (value == 37) {
this.paddle.move(-4, 0);
} else if (value == 39) {
this.paddle.move(4, 0);
} else {
this.paddle.move(0, 0);
}
}
};
function Ball(x, y) {
this.x = x;
this.y = y;
this.x_speed = 0;
this.y_speed = 3;
}
Ball.prototype.render = function() {
context.beginPath();
context.arc(this.x, this.y, 5, 2 * Math.PI, false);
context.fillStyle = "#000000";
context.fill();
};
Ball.prototype.update = function(paddle1, paddle2) {
this.x += this.x_speed;
this.y += this.y_speed;
var top_x = this.x - 5;
var top_y = this.y - 5;
var bottom_x = this.x + 5;
var bottom_y = this.y + 5;
if (this.x - 5 < 0) {
this.x = 5;
this.x_speed = -this.x_speed;
} else if (this.x + 5 > width) {
this.x = width - 5;
this.x_speed = -this.x_speed;
}
if (this.y < 0 || this.y > height) {
this.x_speed = 0;
this.y_speed = 3;
this.x = width / 2;
this.y = height / 2;
}
if (top_y > height / 2) {
if (top_y < (paddle1.y + paddle1.height) && bottom_y > paddle1.y && top_x < (paddle1.x + paddle1.width) && bottom_x > paddle1.x) {
this.y_speed = -3;
this.x_speed += (paddle1.x_speed / 2);
this.y += this.y_speed;
}
} else {
if (top_y < (paddle2.y + paddle2.height) && bottom_y > paddle2.y && top_x < (paddle2.x + paddle2.width) && bottom_x > paddle2.x) {
this.y_speed = 3;
this.x_speed += (paddle2.x_speed / 2);
this.y += this.y_speed;
}
}
};
document.getElementsByClassName('js-pong')[0].appendChild(canvas);
animate(step);
window.addEventListener("keydown", function(event) {
keysDown[event.keyCode] = true;
});
window.addEventListener("keyup", function(event) {
delete keysDown[event.keyCode];
});
var resizer = function() {
document.getElementsByTagName('canvas').remove();
document.getElementsByClassName('js-pong')[0].appendChild(canvas);
animate(step);
}
window.addEventListener('resize', resizer);
Dont use the Resize event for animations.
(if you use requestAnimationFrame)
Do not add a resize to the resize event. The resize event does not fire in sync with the display, and it can also fire many time quicker than the display rate. This can make the resize and game feel sluggish while dragging at the window size controls.
Smooth and efficient resizing
For the smoothest resize simply check the canvas size inside the main loop. If the canvas does not match the containing element size then resize it. This ensures you only resize when needed and always resize in sync with the display (if you use requestAnimationFrame)
Eg
// get a reference to the element so you don't query the DOM each time
const pongEl = document.getElementById("pong");
// then in the main loop
function mainLoop() {
// do first thing inside the render loop
if(canvas.width !== pongEl.offsetWidth || canvas.height !== pongEl.offsetHeight ){
canvas.width = pongEl.offsetWidth;
canvas.height = pongEl.offsetHeight;
// note the above clears the canvas.
}
// game code as normal
requestAnimationFrame(mainLoop);
}
You have to detect when the resize happens with JavaScript:
window.onresize = function(event) {
...
};
and add a css class or properties to make the canvas width 100%. Also you have to give it a min-width to make sure no body can resize it to a really uncomfortable width.
This piece of code needs to run again after a resize:
var canvas = document.createElement("canvas");
var width = document.getElementById('pong').offsetWidth;
var height = document.getElementById('pong').offsetHeight;
canvas.width = width;
canvas.height = height;
var context = canvas.getContext('2d');
I found this amazing plugin which simulates the physics of fabric: http://codepen.io/anon/pen/mxjKC
document.getElementById('close').onmousedown = function(e) {
e.preventDefault();
document.getElementById('info').style.display = 'none';
return false;
};
// settings
var physics_accuracy = 3,
mouse_influence = 20,
mouse_cut = 5,
gravity = 1200,
cloth_height = 30,
cloth_width = 50,
start_y = 20,
spacing = 7,
tear_distance = 60;
window.requestAnimFrame =
window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function (callback) {
window.setTimeout(callback, 1000 / 60);
};
var canvas,
ctx,
cloth,
boundsx,
boundsy,
mouse = {
down: false,
button: 1,
x: 0,
y: 0,
px: 0,
py: 0
};
var Point = function (x, y) {
this.x = x;
this.y = y;
this.px = x;
this.py = y;
this.vx = 0;
this.vy = 0;
this.pin_x = null;
this.pin_y = null;
this.constraints = [];
};
Point.prototype.update = function (delta) {
if (mouse.down) {
var diff_x = this.x - mouse.x,
diff_y = this.y - mouse.y,
dist = Math.sqrt(diff_x * diff_x + diff_y * diff_y);
if (mouse.button == 1) {
if (dist < mouse_influence) {
this.px = this.x - (mouse.x - mouse.px) * 1.8;
this.py = this.y - (mouse.y - mouse.py) * 1.8;
}
} else if (dist < mouse_cut) this.constraints = [];
}
this.add_force(0, gravity);
delta *= delta;
nx = this.x + ((this.x - this.px) * .99) + ((this.vx / 2) * delta);
ny = this.y + ((this.y - this.py) * .99) + ((this.vy / 2) * delta);
this.px = this.x;
this.py = this.y;
this.x = nx;
this.y = ny;
this.vy = this.vx = 0
};
Point.prototype.draw = function () {
if (!this.constraints.length) return;
var i = this.constraints.length;
while (i--) this.constraints[i].draw();
};
Point.prototype.resolve_constraints = function () {
if (this.pin_x != null && this.pin_y != null) {
this.x = this.pin_x;
this.y = this.pin_y;
return;
}
var i = this.constraints.length;
while (i--) this.constraints[i].resolve();
this.x > boundsx ? this.x = 2 * boundsx - this.x : 1 > this.x && (this.x = 2 - this.x);
this.y < 1 ? this.y = 2 - this.y : this.y > boundsy && (this.y = 2 * boundsy - this.y);
};
Point.prototype.attach = function (point) {
this.constraints.push(
new Constraint(this, point)
);
};
Point.prototype.remove_constraint = function (constraint) {
this.constraints.splice(this.constraints.indexOf(constraint), 1);
};
Point.prototype.add_force = function (x, y) {
this.vx += x;
this.vy += y;
};
Point.prototype.pin = function (pinx, piny) {
this.pin_x = pinx;
this.pin_y = piny;
};
var Constraint = function (p1, p2) {
this.p1 = p1;
this.p2 = p2;
this.length = spacing;
};
Constraint.prototype.resolve = function () {
var diff_x = this.p1.x - this.p2.x,
diff_y = this.p1.y - this.p2.y,
dist = Math.sqrt(diff_x * diff_x + diff_y * diff_y),
diff = (this.length - dist) / dist;
if (dist > tear_distance) this.p1.remove_constraint(this);
var px = diff_x * diff * 0.5;
var py = diff_y * diff * 0.5;
this.p1.x += px;
this.p1.y += py;
this.p2.x -= px;
this.p2.y -= py;
};
Constraint.prototype.draw = function () {
ctx.moveTo(this.p1.x, this.p1.y);
ctx.lineTo(this.p2.x, this.p2.y);
};
var Cloth = function () {
this.points = [];
var start_x = canvas.width / 3 - cloth_width * spacing / 2;
for (var y = 0; y <= cloth_height; y++) {
for (var x = 0; x <= cloth_width; x++) {
var p = new Point(start_x + x * spacing, start_y + y * spacing);
x != 0 && p.attach(this.points[this.points.length - 1]);
y == 0 && p.pin(p.x, p.y);
y != 0 && p.attach(this.points[x + (y - 1) * (cloth_width + 1)])
this.points.push(p);
}
}
};
Cloth.prototype.update = function () {
var i = physics_accuracy;
while (i--) {
var p = this.points.length;
while (p--) this.points[p].resolve_constraints();
}
i = this.points.length;
while (i--) this.points[i].update(.016);
};
Cloth.prototype.draw = function () {
ctx.beginPath();
var i = cloth.points.length;
while (i--) cloth.points[i].draw();
ctx.stroke();
};
function update() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
cloth.update();
cloth.draw();
requestAnimFrame(update);
}
function start() {
canvas.onmousedown = function (e) {
mouse.button = e.which;
mouse.px = mouse.x;
mouse.py = mouse.y;
var rect = canvas.getBoundingClientRect();
mouse.x = e.clientX - rect.left,
mouse.y = e.clientY - rect.top,
mouse.down = true;
e.preventDefault();
};
canvas.onmouseup = function (e) {
mouse.down = false;
e.preventDefault();
};
canvas.onmousemove = function (e) {
mouse.px = mouse.x;
mouse.py = mouse.y;
var rect = canvas.getBoundingClientRect();
mouse.x = e.clientX - rect.left,
mouse.y = e.clientY - rect.top,
e.preventDefault();
};
canvas.oncontextmenu = function (e) {
e.preventDefault();
};
boundsx = canvas.width - 1;
boundsy = canvas.height - 1;
ctx.strokeStyle = 'red';
cloth = new Cloth();
update();
}
window.onload = function () {
canvas = document.getElementById('c');
ctx = canvas.getContext('2d');
canvas.width = 1120;
canvas.height = 700;
canvas.style.width = '560px';
canvas.style.height = '350px';
ctx.scale(2,2);
start();
};
<div id=info>
<input type="button" value="close" id="close"></input>
<canvas id="c"></canvas>
</div>
I would love to know how to use it for an image instead of the net but I don't know where to start at all.
What is the theory behind that - where could I place the image?
You can not put an image on that mesh because the resulting squares are not 2d. This answer gives a little detail as to why.
To do what you want you need to use webGL. It is a straightforward conversion, the cloth is a mesh (vertices and polygons connecting the vertices) where each vertex will hold a texture coordinate and you let the sim run as is. There should be plenty of examples of how to map a image onto a mesh in stackoverflow. You might consider using three.js if you are new to 3D and coding.
Here is an example using three.js and a verlet cloth with texture mapped onto it.
I was wondering if it was possible to have mouseover events with multiple squares on a canvas
this is my code right now: http://jsfiddle.net/2j3u9f7m/
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
var Enemy = function (x, y, velx, vely) {
this.x = x;
this.y = y;
this.velx = 0;
this.vely = 0;
}
Enemy.prototype.update = function () {
var tx = 650 - this.x;
var ty = 250 - this.y;
var dist = Math.sqrt(tx * tx + ty * ty);
this.velx = (tx / dist);
this.vely = (ty / dist);
if (dist > 0) {
this.x += this.velx;
this.y += this.vely;
}
};
Enemy.prototype.render = function () {
context.fillStyle = '#000000';
context.beginPath();
context.rect(this.x, this.y, 25, 25);
context.fill();
context.closePath();
};
var enemies = [];
for (var i = 0; i < 10; i++) {
// random numbers from 0 (inclusive) to 100 (exclusive) for example:
var randomX = Math.random() * 896;
var randomY = Math.random() * 1303;
console.log(randomX);
console.log(randomY);
if (randomX > 100 && randomX < 1200) {
if (randomX % 2 == 0) {
randomX = 140;
} else {
randomX = 1281;
}
}
if (randomY > 100 && randomY < 75) {
if (randomY % 2 == 0) {
randomY = 15;
} else {
randomY = 560;
}
}
var enemy = new Enemy(randomX, randomY, 0, 0);
enemies.push(enemy);
}
for (var i = 0; i < 15; i++) {
// random numbers from 0 (inclusive) to 100 (exclusive) for example:
var randomX = Math.random() * 200;
var randomY = Math.random() * 403;
console.log(randomX);
console.log(randomY);
if (randomX > 100 && randomX < 1200) {
if (randomX % 2 == 0) {
randomX = 140;
} else {
randomX = 1281;
}
}
if (randomY > 100 && randomY < 75) {
if (randomY % 2 == 0) {
randomY = 15;
} else {
randomY = 560;
}
}
var enemy = new Enemy(randomX, randomY, 0, 0);
enemies.push(enemy);
}
function render() {
context.clearRect(0, 0, canvas.width, canvas.height);
for (var i = 0; i < enemies.length; i++) {
var one = enemies[i];
one.update();
one.render();
}
requestAnimationFrame(render);
}
render();
What I want to do is to have a mouseover event for each square; is there a way to do this without using a library?
You can extend your Enemy object doing a region check like this:
Enemy.prototype.isOnEnemy = function(x, y) {
return (x >= this.x && x < this.x + 25 && // 25 = width
y >= this.y && y < this.y + 25); // 25 = height
};
If the provided (x,y) position is inside the rectangle (here assuming width and height of 25) it will return true.
Then add a mousemove event listener to the canvas. Inside adjust the mouse position, then feed the muse position to each enemy object to check:
context.canvas.onmousemove = function(e) {
var rect = this.getBoundingClientRect(), // correct mouse position
x = e.clientX - rect.left,
y = e.clientY - rect.top,
i = 0;
for(; i < enemies.length; i++) { // check each enemy
if (enemies[i].isOnEnemy(x, y)) { // is inside?
console.log("AAAARG...", i); // some action...
}
}
};
Modified fiddle (see console for hits).
I have a problem with my canvas game. I try to make it jump but I have some troubles with it. Everything is working fine but if I touch an object from the bottom, it throw me up to the object.
Problem may be with LastColison. Can someone help me ? GIF Image link.
function Block(x, y) {
var size = 80
GameObject.call(this, x*size, y*size, size)
this.img = document.getElementById("block")
this.class = "block"
}
// Dedi vlastnosti z GameObject
Block.prototype = Object.create(GameObject.prototype)
Block.prototype.draw = function() {
ctx.fillStyle = "green"
ctx.drawImage(this.img,this.x,this.y,this.size,this.size)
}
function Player(x, y) {
var size = 120
this.dx = Math.random() * 50 - 25
this.dy = Math.random() * 50 - 25
GameObject.call(this, x*size, y*size, size)
// this.player = document.getElementById("player")
this.img = [document.getElementById("player"),document.getElementById("ball_r"),document.getElementById("ball_l"),document.getElementById("ball_j")]
this.player = this.img[0]
this.class = "player"
}
// Dedi vlastnosti z GameObject
Player.prototype = Object.create(GameObject.prototype)
Player.prototype.move = function() {
var x = this.x
var y = this.y
//ak dam this.y = y -5 môžem pohnuť aj so stlačenou sipkou dole
this.player = this.img[0]
// Posun
if ( keys[37] )
{
if(level == 0){
x -= 4;
}
if (level == 1) {
x -= 8;
}
this.player = this.img[2];
this.y = y;
}
if ( keys[39])
{
if (level == 0) {
x += 4;
}
if (level == 1) {
x += 8;
}
this.player = this.img[1];
}
if ( keys[38] )
{ this.player = this.img[3], this.dy = -10; }
// ak nedam else if mozem ouzzivat naraz viac tlacidiel takze upravit potom
// Test novej pozicie
var collision = false
for (i in scene) {
var obj = scene[i]
if (obj.class == "cloud") { continue; }
if (obj.class == "ladder") { continue; }
if (obj.class == "touched") { continue; }
if (obj.class == "dirt") { this.x = x; this.y = y }
if (obj.class == "block") { this.x = x; this.y = y }
if (obj.class == "enemy") { this.x = x; this.y = y}
var test = x +30 >= obj.x + obj.size || x + this.size - 40<= obj.x /* kolide right*/|| y >= obj.y + obj.size /*kolizia up*/|| y + 40 + this.size <= obj.y /*kolizia bottom*/
if (!test) {
collision = true;
var touch = 0;
if (obj.class == "enemy") {
touch = 1;
if (touch == 1) {
health -= 20; console.log(health);
this.x = x - 250;
if (klik % 2 == 0){
var hit = new Audio('snd/Hit_Hurt15.wav')
hit.play()
}
}
if (health == 0) { health = 0; console.log("GAMEOVER");scene = []}
}
if (obj.class == "coin") {
score += 10; obj.class = "touched";
if (klik % 2 == 0) {
var hrahra = new Audio('snd/pickcoin.wav')
hrahra.play()
}
}
else { touch = 0; }
if (obj.class == "touched") {}
break;
}
}
if (score >= 200 && score <= 200) {
if (klik % 2 == 0) {
var levelup = new Audio('snd/Powerup9.wav')
levelup.loop = false;
levelup.play()
}
level = 1;
health = 100;
score += 1;
}
// Ladder
// if(collision){score += 1,scene.pop() }
// Posun bez kolizie
if (!collision) {
this.x = x
this.y = y + this.dy
this.dy += 0.3;
}
**else {
if (obj.class == this.LastColl) {
this.dy = 0;
this.y = obj.y -160
}
this.dy = 0;
this.LastColl = obj.class
}**
}
Player.prototype.draw = function() {
ctx.fillStyle = "blue"
ctx.beginPath()
ctx.drawImage(this.player,this.x, this.y, 110,160)
ctx.shadowColor = "rgba( 0, 0, 0, 0.3 )";
ctx.shadowOffsetX = -10;
ctx.shadowOffsetY = 0
ctx.shadowBlur = 3;
ctx.drawImage(this.player,this.x,this.y,110,160)
ctx.closePath()
ctx.fill()
}
I can't currently access the GIF you provided. But from what i can gather these lines are your problem:
if (!collision) {
this.x = x
this.y = y + this.dy
this.dy += 0.3;
}
**else {
if (obj.class == this.LastColl) {
this.dy = 0;
this.y = obj.y -160
}
This line - this.y = obj.y -160
Looks like you're telling it to move up -160 pixels on the canvas.
Does this answer your question?
Just a note too - I'd recommend using semicolons at the end of each statement. Don't sometimes use them and other times don't - it will cause problems for you and is bad practice :)
I don't know much about canvas right now, but I noticed that you don't have any semicolons to end your statements...
example:
var x = this.x;
etc.
Another thing that I noticed... The score checks for both greater than or equal to and less than or equal to 200...
if (score >= 200 && score <= 200)
{
...
}