I'm new to JavaScript and I'm trying to code a somewhat simplified version of the breakout game. It is not done yet, and pretty messy regarding the coding, but it works, almost. My collision detector has some trouble detecting the ball. It does what it is supposed to for the most part, but from time to time the ball just goes straight through, and I cannot figure out how to remake that part.
The code looks like this
function collisionDetection(){
for(var c=0; c<brickColumnCount; c++){
for(var r=0; r<brickRowCount; r++){
var b=bricks[c][r];
//collisionDetection
if(b.status == 1){
if(x+15 > b.x && x+15 < b.x+brickWidth && y-10 > b.y && y-10 < b.y+brickHight){
dy = -dy;
b.status = 0;
score++;
color = getRandomColor();
}
}
}
}
}
I made the paddle as long as the canvas for testing - I have not yet made something to get the ball to change the angle, only speed. And I haven't defined a maxSpeed yet which I will do later on, but first I want my collision to work properly.
Do you have an idea why this is not working?
I noticed that at 200 points the collision will not work at all. Might be the speed(Which is insanely high, yes)?
The full code can be found here
Thanks
If the x and y values are the top left corners of your ball and brick elements, then the if statement should probably be:
if (x + 15 > b.x && x < b.x + brickWidth && y + 15 > b.y && y < b.y + brickHight)
For proper collision detection you'll have to implement some side detection as well. I.e. if the ball hits the side of the brick, dx must be reversed and not dy.
Related
I'm making pong with HTML canvas and JavaScript, and I am having trouble with the movement of the ball. If you position the paddles in a way that they hit the ball, and then you just leave them there, the ball will just continue in an infinite loop bouncing between the paddles, and the top and bottom of the screen. How can I implement a random number to make the ball move in the opposite direction but slightly differently? If you need any more code than this, let me know. Thx
// Ball to paddles collision detection
function ballToPaddlesCollisionDetection(a,b) {
if(a.X < b.X + b.width && a.X + a.width > b.X && a.Y < b.Y + b.height && a.Y + a.height > b.Y) {
ballDX = -ballDX;
ballDY = - ballDY;
}
I'm currently making an HTML5 game, and I'm trying to draw various things onto the canvas. My game is basically just where you move around in an infinite area, but I can't figure out how to optimize my code to draw bushes onto the screen. It works properly, but It lags a lot and I know there's ways to optimize it. Here's my current code:
for(var x=offset[0];x<(offset[0]+canvas.width)+300;x++) {
for(var y=offset[1];y<(offset[1]+canvas.height)+300;y++) {
if(x % 85 == 0 && y % 85 == 0 && noise.simplex2(x, y) == 0) {
ctx.drawImage(treeimage, ((x-offset[0])*-1)+canvas.width, ((y-offset[1])*-1)+canvas.height);
}
}
}
treeimage is defined as so:
var treeimage = new Image(); treeimage.src = 'images/mapobjects/tree2.png';
offset[] is an array with the values being the offset of the objects relative to the player (So when the player moves left, it goes up) horizontally and vertically respectively. I use simplex noise to generate the bushes because I like them to be in small clumps. The problem that makes the FPS so low is that at the resolution of my screen, I'm running 2 modulo functions 2137104 per frame, and that gets even worse at higher resolutions. I tried to make it faster by looping through every tile of my game instead of every pixel(each tile is 85x85, so incrementing y and x by 85 instead of 1) and then adding the player offset % 85, but I had issues with that jumping around because the offset % 85 didn't go to 0 right when it jumped to the next tile, and I tried and tried to get that working in many different ways, but this is the only way I could get it to work. This is how it looks, and everything works fine besides the code being super slow.
Is there something I was missing when I was trying to optimize it, or is there a completely different way that would fix it as well. I've never really had to optimize code, so this is a new thing for me. I can tell all the lag is coming from this code because without it and just incrementing by 85 it works perfectly fine. Thank you!
7225 pointless operations per image
Conditions slow code down. When ever possible you should try to avoid them.
eg the line...
if(x % 85 == 0 && y % 85 == 0 && noise.simplex2(x, y) == 0) {
... means that you are evaluating the if statement 85 * 85 (7225) times for every less than one tree this is a massive amount of unneeded overhead.
Remove those 7224 useless iterations.
Avoid indexing arrays when possible by storing repeated array lookups in a variable.
Simplify your math. eg ((x-offset[0])*-1)+canvas.width can be simplified to canvas.width - x + offset[0].
Offload as much as you can to the GPU. By default all position calculations are via the transform done on the GPU so that above math can be done once before the loop.
General rule for performance, reduce the amount of code inside a loop by moving what you can to outside the loop.
The snippet below implements the above points.
As you have not provided details as to the ranges of offset and canvas size the code below could be further optimized
var x, y;
const STEP = 85;
const offsetX = offset[0];
const offsetY = offset[1];
const startX = Math.floor(offsetX / STEP) * STEP;
const startY = Math.floor(offsetY / STEP) * STEP;
const endX = startX + canvas.width;
const endY = startY + canvas.height;
ctx.setTransform(1, 0, 0, 1, canvas.width - offsetX, canvas.height - offsetY);
for (x = startX; x < endX; x += STEP) {
for (y = startY; y < endY; y += STEP) {
if (noise.simplex2(x, y) == 0) {
ctx.drawImage(treeimage, x, y);
}
}
}
// reset transform
ctx.setTransform(1, 0, 0, 1, 0, 0);
Consider a quad tree
The call to simplex2 I suspect will be very slow. All the implementations I have seen are done very poorly. As the result of simplex is constant for any coordinate it should only be done once per coordinate before the game starts (outside the code in production).
As you want an infinite (like) playfield (infinite is impossible) the RAM requirement way too large. There is not much I can suggest, well apart from... Drop the infinite and set a practical limit to the playfield size which will allow you to create a quad tree map that will make it fly.
Many many years ago, as computers weren't as fast as today and you had to do some hefty mathematical operations like computing the sine or cosine - or even the modulo - there was just one option:
instead of calculating it everytime you need it, calculate it once and store it in a huge look-up table. Looking up a value is of course way faster than computation.
So in your case I'd recommend generating two arrays for the modulo of x and y
let xModulo = [];
let yModulo = [];
for (let a = 0; a < canvas.width; a++) {
xModulo.push(a % 85);
}
for (let a = 0; a < canvas.height; a++) {
yModulo.push(a % 85);
}
and in your render loop look up the values from the arrays like:
if (xModulo[x] == 0 && yModulo[y] == 0 && noise.simplex2(x, y) == 0) {
ctx.drawImage(treeimage, ((x - offset[0]) * -1) + canvas.width, ((y - offset[1]) * -1) + canvas.height);
}
That should give a noticeable performance boost. Depending on your needs you might need to change canvas.width / canvas.height to some higher value.
You might even consider generating a look-up table for the simplex noise.
I have this game where I'm planning to make it into some kind of shooter (please also state if that's too hard considering the level I'm currently at) and I'm at the point of making things collide, like the player with point rectangles (should make orbs instead soon).
I've looked at different examples like
Collision Detection with javascript on the html canvas element without using jquery
and
http://jlongster.com/Making-Sprite-based-Games-with-Canvas
but it seems like I didn't understand the code well enough. Here's what I have:
if( (rX + (rX + 20)) >= x && rX <= (x + 20) && (rY + (rY + 20)) >= y && rY <= (y + 20)){
poeng++;
genererRandom();
}
Didn't work, so tried this one:
if (x >= rX || (rX+19) < (x+49) ||
y >= rY || (rY+19) < (y+49)) {
poeng++;
genererRandom();
}
x is the x position of the sprite I'm using (which is 60x60 large)
y is the y of x
rX is a randomly generated number and the x position of the point rectangle (again, think I should make it into orbs soon)
rY is the y of rX
Also, just ask if you want to see more code, although it is mostly not mine, I was given an unfinished game that I could make something out of.
Given a 2d axis aligned rectangle of width=w and height=h with its origin of x and y being in the top left corner, the check whether a point r with position rx and ry lies in the rectangle would be:
if (rx >= x && rx <= x+w && ry >= y && ry <= ry+h)
{
// we're inside the rectangle!
}
In simple terms this means:
check if the point's x lies between the rectangle's x and x+w
check if the point's y lies between the rectangle's y and y+h
In maths you could write it this way (doesn't work in JS though):
x <= rX <= x+w ∧ y <= rY <= y+h
The statements above imply that the coordinate system has the origin in the top left corner and the values increase towards bottom right.
Currently i am working on bouncing a ball of a wall, in classic 2D-geometry.
The wall can be hit on both vertical and horizontal sides, and the result of the reflection depends on which side the ball hits.
I have tried some different solutions, but they just make me more confused.
How can i determine whether the ball is hitting a vertical or horizontal side of the wall?
PseudoCode for the overview:
iterate through each wall
if (collision between ball and wall)
determine if vertical/horizontal hit
calculate new velocity for ball
I use this code for collision detection and it works like a charm:
source: Circle-Rectangle collision detection (intersection)
var isCollision = function (_projectile) {
if(direction != undefined){
var circleDistance = {};
circleDistance.x = Math.abs(_projectile.getCenter().x - centerX);
circleDistance.y = Math.abs(_projectile.getCenter().y - centerY);
if (circleDistance.x > (width/2 + _projectile.getRadius())) { return false; }
if (circleDistance.y > (height/2 + _projectile.getRadius())) { return false; }
if (circleDistance.x <= (width/2)) { return true; }
if (circleDistance.y <= (height/2)) { return true; }
var cornerDistance_sq = square(circleDistance.x - width/2) + square(circleDistance.y - height/2);
return (cornerDistance_sq <= (square(_projectile.getRadius())));
}
return false;
};
var square = function(_value){
return _value * _value;
};
Thank you!
update after question update
Asuming the ball has a direction/velocity vector dx,dy and has a radius of r (ball.x, ball.y are the ball positions of the ball center) do sth like this (have used similar in a billiard game, it is basic geometry and physics):
if (ball.x+ball.dx+r > wallV.x) ball.dx = -ball.dx // ball bounces off the
vertical wall
if ( ball.y+ball.dy+r > wallH.y ) ball.dy = -ball.dy // ball bounces off horizontal wall
do similar for opposite walls if needed, since velocity vector changes, the new ball position after bouncing (or anyway) will be ball.x += ball.dx; ball.y += ball.dy;
Bonus if you add some friction factor (meaning the magnitudes of dx dy eventually fade away to zero, the ball eventually stops after following a path)
To solve properly a collision, you can't just test on X => if collision solve and return THEN test on y => if collision solve on y.
You have to check which axe (x OR y) collides first, and there's also the case when the ball hits two walls at the same time.
So you can't just reason with space : you have to deal with time, and compute an ETA -Estimated Time Arrival- for all walls, based on the ball speed (assuming walls are still).
pseudo code :
minETA = a too big number;
colliderList = [];
for (each wall in walls) {
thisWallETA = computeETA(ball, wall);
if (thisWallETA > minETA) continue;
if (thisWallETA < minETA) {
minETA = thisWallETA;
colliderList.length = 0;
colliderList.push(wall);
continue;
}
if (thisWallETA == minETA) {
colliderList.push(wall);
continue;
}
}
Then when the colliderList has only one item => solve on corresponding axe. If it has two items => solve on both axes.
Then increase the current time by minETA, and try again until the currentTime + minETA you found is > to thisCycleTime + thisCycleDt.
If you're interested, i might clarify some things up.
Good luck !
I am starting on a physics/particle simulator and I am having some trouble with collision detection:
http://mmhudson.com/physics.html
Im not so much looking for a code solution, but someone to explain the issue to me conceptually.
The way it works is I check to see if the particle is going to be inside/intersect with the object when it is next moved. If it is, the gravity multiplier is reversed so its direction is reversed.
The equation for movement I use is:
Next location = current speed + rate of gravity + current location
Where speed is the gravity multiplier
Hopefully someone has seen an issue like this before or is willing to check out the source of my page.
Any help at all is greatly appreciated
No conceptual explanation, but a bunch of random observations:
I'd recommend adding canvas width/height variables and comparing them against the particle position. Right now, your particles keep falling even if they drop off the canvas. Something like:
if( particles[i][1] > height )
particles.splice(i,1);
newPY < objects[k][2] + objects[k][3] + radius
is really weird. What are you getting from this? Adding the width and height of the objects and the particle radius? If you remove this part, particles will bounce off objects as long as they have "momentum".
As for momentum, I assume you want to figure out how to stop the particles from falling through the objects. Given current code, I'd do this: add a fifth variable to the particle, defaulting to the height of the canvas. Then, once you find out that you have an impact, save the impact position to the particle and after the loop, check if the particle is below that point. If so, reset it to that point. Dirty fix, but hey, it works. I've added the complete loop below that worked for me. To stop the objects from being wiped out by the clearRect method, maybe consider redrawing them.
You are checking for particles on top of the objects only, but I assume the "falling through" aspect is part of the bug you asked about, so not too important at the moment:
particles[i][1] < objects[k][1] + objects[k][3] + radius
Could be however, if you decided to play around and reduce gravity, so that particles would instead gain momentum and bounce against objects above.
As for your objCheck variable, you confuse the width for y in the last && part. It should be:
mY < objects[i][1] + objects[i][3] + radius
instead of
mY < objects[i][2] + objects[i][3] + radius
Right now, your objCheck is not working.
Also
for(var i=0; i < particles.length; i++) {
var clrRadius = 2*radius;
canvas.clearRect(particles[i][0]-radius, particles[i][1]-radius, clrRadius, clrRadius);
}
is better than
for(var i=0; i < particles.length; i++){
var clrRadius = radius + 4;
canvas.clearRect(particles[i][0]-(clrRadius/2), particles[i][1]-(clrRadius/2), clrRadius, clrRadius);
}
edit: Seems you changed the code, since I last checked it, so the above might no longer be relevant!
edit2: added particle stopping fix. Here's the complete gravity loop:
for(var i=0; i<particles.length; i++){
var clrRadius = 2*radius;
canvas.clearRect(particles[i][0]-radius, particles[i][1]-radius, clrRadius, clrRadius);
}
for(var i=0; i < particles.length; i++){
var newPY = particles[i][1] += particles[i][2] + particles[i][3];
for(var k=0; k<objects.length; k++){
if(
//particle
particles[i][0] > objects[k][0] - radius &&
particles[i][0] < objects[k][0] + objects[k][2] + radius &&
particles[i][1] > objects[k][1] - radius &&
particles[i][1] < objects[k][1] + objects[k][3] + radius //&&
){
//reverse gravity
particles[i][2] = particles[i][2] * -1;
particles[i][5] = objects[k][1] - radius;
}
}
particles[i][2] += particles[i][3]*weight;
particles[i][1] += particles[i][2];
if( particles[i][1] > particles[i][5] )
particles[i][1] = particles[i][5];
if( particles[i][1] > height )
particles.splice(i,1);
}
for(var i=0; i <particles.length; i++){
canvas.fillStyle = "#000";
canvas.beginPath();
canvas.arc(particles[i][0], particles[i][1], radius, 0, Math.PI*2, true);
canvas.closePath();
canvas.fill();
}
I wouldn't use a gravity multiplier.
Each object should look something like this:
var circle = {
x: 0, // x position
y: 0, // y position
dx: 0, // x velocity
dy: 0 // y velocity
}
To update the particle, multiply velocity (dx, dy) by some time interval and add this to the current position.
Every cycle, add some change in velocity as a result of gravity.
If you detect a collision change the velocity so the circles bounce off each other. An example of this would be:
// In a collision, simply reverse the direction of movement
// so the circles move away from each other.
function onCollision(circleA, circleB) {
circleA.dx *= -1;
circleA.dy *= -1;
circleB.dx *= -1;
circleB.dy *= -1;
}