As in the title, im trying to create a grid of objects in P5 Spot(x, y, size), with a 4 pixel space between them and center it on the canvas without using translate, heres what i've got:
gridSize = 7;
spotSize = 60;
spots = [];
for (var y = height / 2 - ((gridSize * spotSize + gridSize * 4) / 2); y < (height / 2 - ((gridSize * spotSize + gridSize * 4) / 2)) + (gridSize * spotSize + gridSize * 4); y += spotSize + 4) {
for (var x = width / 2 - ((gridSize * spotSize + gridSize * 4) / 2); x < (width / 2 - ((gridSize * spotSize + gridSize * 4) / 2)) + (gridSize * spotSize + gridSize * 4); x += spotSize + 4) {
spots.push(new Spot(x, y, spotSize));
}
}
Problem is that my grid looks off, why is it not centered? Probably a really simple and stupid mistake but i cant find it. Any help appreciated.
My Spot object just draws an ellipse at the given x and y. Entire code at http://codepen.io/felipe_mare/pen/GWyMOL
-SOLVED-
spots.push(new Spot(x + spotSize/2, y + spotSize/2, spotSize));
Wasn't taking into account the fact that the ellipse is drawn from the center, so i have to add the radius of the circle spotSize/2
Related
In Javascript i have a Uint8Array() RGBA of image, here is console.log of this :
Here is image with rgba array as a string in HTML:
Is that possible to manipulate this array, to ex. change colors?
Thanks for help!
Here's a JS algorithm of plotting a pixel to this kind of array:
function changeColor(x, y, c)
{
colorArray[(x * 4) + (y * (imageWidth * 4))] = c.r;
colorArray[(x * 4) + (y * (imageWidth * 4)) + 1] = c.g;
colorArray[(x * 4) + (y * (imageWidth * 4)) + 2] = c.b;
colorArray[(x * 4) + (y * (imageWidth * 4)) + 3] = c.a;
}
where x and y are the coordinates of the pixel you want to change, imageWidth being the width of the image this array produces, c being the colour you want to change the pixel to, and colorArray is the array itself.
I'm currently working on a Pinball game using the HTML5 Canvas and JavaScript. Right now I'm getting a hard time with the pixel by pixel collision, which is fundamental because of the flippers.
Right now my Bounding Box Collision seems to be working
checkCollision(element) {
if (this.checkCollisionBoundingBox(element)) {
console.log("colision with the element bounding box");
if (this.checkCollisionPixelByPixel(element)) {
return true;
} else {
return false;
}
} else {
return false;
}
}
checkCollisionBoundingBox(element) {
if (this.pos.x < element.pos.x + element.width && this.pos.x + this.width > element.pos.x && this.pos.y < element.pos.y + element.height && this.pos.y + this.height > element.pos.y) {
return true;
} else {
return false;
}
}
I've tried several ways of implementing the pixel by pixel one but for some reason it does not work perfectly (on walls, on images, on sprites etc). I'll leave them here:
checkCollisionPixelByPixel(element) {
var x_left = Math.floor(Math.max(this.pos.x, element.pos.x));
var x_right = Math.floor(Math.min(this.pos.x + this.width, element.pos.x + element.width));
var y_top = Math.floor(Math.max(this.pos.y, element.pos.y));
var y_bottom = Math.floor(Math.min(this.pos.y + this.height, element.pos.y + element.height));
for (var y = y_top; y < y_bottom; y++) {
for (var x = x_left; x < x_right; x++) {
var x_0 = Math.round(x - this.pos.x);
var y_0 = Math.round(y - this.pos.y);
var n_pix = y_0 * (this.width * this.total) + (this.width * (this.actual-1)) + x_0; //n pixel to check
var pix_op = this.imgData.data[4 * n_pix + 3]; //opacity (R G B A)
var element_x_0 = Math.round(x - element.pos.x);
var element_y_0 = Math.round(y - element.pos.y);
var element_n_pix = element_y_0 * (element.width * element.total) + (element.width * (element.actual-1)) + element_x_0; //n pixel to check
var element_pix_op = element.imgData.data[4 * element_n_pix + 3]; //opacity (R G B A)
console.log(element_pix_op);
if (pix_op == 255 && element_pix_op == 255) {
console.log("Colision pixel by pixel");
/*Debug*/
/*console.log("This -> (R:" + this.imgData.data[4 * n_pix] + ", G:" + this.imgData.data[4 * n_pix + 1] + ", B:" + this.imgData.data[4 * n_pix + 2] + ", A:" + pix_op + ")");
console.log("Element -> (R:" + element.imgData.data[4 * element_n_pix] + ", G:" + element.imgData.data[4 * element_n_pix + 1] + ", B:" + element.imgData.data[4 * element_n_pix + 2] + ", A:" + element_pix_op + ")");
console.log("Collision -> (x:" + x + ", y:" + y +")");
console.log("This(Local) -> (x:" + x_0 + ", y:" + y_0+")");
console.log("Element(Local) -> (x:" + element_x_0 + ", y:" + element_y_0+")");*/
/*ball vector*/
var vector = {
x: (x_0 - Math.floor(this.imgData.width / 2)),
y: -(y_0 - Math.floor(this.imgData.height / 2))
};
//console.log("ball vector -> ("+vector.x+", "+vector.y+") , Angulo: "+ Math.atan(vector.y/vector.x)* 180/Math.PI);
// THIS WAS THE FIRST TRY, IT DIDN'T WORK WHEN THE BALL WAS GOING NORTHEAST AND COLLIDED WITH A WALL. DIDN'T WORK AT ALL WITH SPRITES
//this.angle = (Math.atan2(vector.y, vector.x) - Math.PI) * (180 / Math.PI);
// THIS WAS THE SECOND ATTEMPT, WORKS WORSE THAN THE FIRST ONE :/
//normal vector
var normal = {
x: (x_0 - (this.imgData.width / 2)),
y: -(y_0 - (this.imgData.height / 2))
};
//Normalizar o vetor
var norm = Math.sqrt(normal.x * normal.x + normal.y * normal.y);
if (norm != 0) {
normal.x = normal.x / norm;
normal.y = normal.y / norm;
}
var n_rad = Math.atan2(normal.y, normal.x);
var n_deg = (n_rad + Math.PI) * 180 / Math.PI;
console.log("Vetor Normal -> (" + normal.x + ", " + normal.y + ") , Angulo: " + n_deg);
//Vetor Velocidade
var velocity = {
x: Math.cos((this.angle * Math.PI / 180) - Math.PI),
y: Math.sin((this.angle * Math.PI / 180) - Math.PI)
};
console.log("Vetor Velocidade -> (" + velocity.x + ", " + velocity.y + ") , Angulo: " + this.angle);
//Vetor Reflexao
var ndotv = normal.x * velocity.x + normal.y * velocity.y;
var reflection = {
x: -2 * ndotv * normal.x + velocity.x,
y: -2 * ndotv * normal.y + velocity.y
};
var r_rad = Math.atan2(reflection.y, reflection.x);
var r_deg = (r_rad + Math.PI) * 180 / Math.PI;
console.log("Vetor Reflexao -> (" + reflection.x + ", " + reflection.y + ") , Angulo: " + r_deg);
this.angle = r_deg;
return true;
}
}
}
return false;
}
}
The ball class
class Ball extends Element {
constructor(img, pos, width, height, n, sound, angle, speed) {
super(img, pos, width, height, n, sound);
this.angle = angle; //direction [0:360[
this.speed = speed;
}
move(ctx, cw, ch) {
var rads = this.angle * Math.PI / 180
var vx = Math.cos(rads) * this.speed / 60;
var vy = Math.sin(rads) * this.speed / 60;
this.pos.x += vx;
this.pos.y -= vy;
ctx.clearRect(0, 0, cw, ch);
this.draw(ctx, 1);
}
}
Assuming a "flipper" is composed of 2 arcs and 2 lines it would be much faster to do collision detection mathematically rather than by the much slower pixel-test method. Then you just need 4 math collision tests.
Even if your flippers are a bit more complicated than arcs+lines, the math hit tests would be "good enough" -- meaning in your fast-moving game, the user cannot visually notice the approximate math results vs the pixel-perfect results and the difference between the 2 types of tests will not affect gameplay at all. But the pixel-test version will take magnitudes more time and resources to accomplish. ;-)
First two circle-vs-circle collision tests:
function CirclesColliding(c1,c2){
var dx=c2.x-c1.x;
var dy=c2.y-c1.y;
var rSum=c1.r+c2.r;
return(dx*dx+dy*dy<=rSum*rSum);
}
Then two circle-vs-line-segment collision tests:
// [x0,y0] to [x1,y1] define a line segment
// [cx,cy] is circle centerpoint, cr is circle radius
function isCircleSegmentColliding(x0,y0,x1,y1,cx,cy,cr){
// calc delta distance: source point to line start
var dx=cx-x0;
var dy=cy-y0;
// calc delta distance: line start to end
var dxx=x1-x0;
var dyy=y1-y0;
// Calc position on line normalized between 0.00 & 1.00
// == dot product divided by delta line distances squared
var t=(dx*dxx+dy*dyy)/(dxx*dxx+dyy*dyy);
// calc nearest pt on line
var x=x0+dxx*t;
var y=y0+dyy*t;
// clamp results to being on the segment
if(t<0){x=x0;y=y0;}
if(t>1){x=x1;y=y1;}
return( (cx-x)*(cx-x)+(cy-y)*(cy-y) < cr*cr );
}
I'm looking for a function to arrange some elements around a circle.
result should be something like :
Here's some code that should help you:
var numElements = 4,
angle = 0
step = (2*Math.PI) / numElements;
for(var i = 0; i < numElements.length; i++) {
var x = container_width/2 + radius * Math.cos(angle);
var y = container_height/2 + radius * Math.sin(angle);
angle += step;
}
It is not complete but should give you a good start.
Update: Here's something that actually works:
var radius = 200; // radius of the circle
var fields = $('.field'),
container = $('#container'),
width = container.width(),
height = container.height(),
angle = 0,
step = (2*Math.PI) / fields.length;
fields.each(function() {
var x = Math.round(width/2 + radius * Math.cos(angle) - $(this).width()/2),
y = Math.round(height/2 + radius * Math.sin(angle) - $(this).height()/2);
$(this).css({
left: x + 'px',
top: y + 'px'
});
angle += step;
});
Demo: http://jsfiddle.net/ThiefMaster/LPh33/
Here's an improved version where you can change the element count.
For an element around a centre at (x, y), distance r, the element's centre should be positioned at:
(x + r cos(2kπ/n), y + r sin(2kπ/n))
where n is the number of elements, and k is the "number" of the element you're currently positioning (between 1 and n inclusive).
I've combined ThiefMaster's fiddle with the jQuery pointAt plugin:
Demo: http://jsfiddle.net/BananaAcid/nytN6/
the code is somewhat like above.
might be interesting to some of you.
Arrange Elements In Circle (Javascript)
function arrangeElementsInCircle (elements, x, y, r) {
for (var i = 0; i < elements.length; i++) {
elements[i].scaleX = 1 / elements.length
elements[i].scaleY = 1 / elements.length
elements[i].x = (x + r * Math.cos((2 * Math.PI) * i/elements.length))
elements[i].y = (y + r * Math.sin((2 * Math.PI) * i/store.length))
}
}
Where x,y is point co-ordinates and elements is array of elements to be placed and r is radius.
Javascript only version of thiefmaster's answer
function distributeFields(deg){
deg = deg || 0;
var radius = 200;
var fields = document.querySelectorAll('.field'), //using queryselector instead of $ to select items
container = document.querySelector('#container'),
width = container.offsetWidth, //offsetWidth gives the width of the container
height = container.offsetHeight,
angle = deg || Math.PI * 3.5,
step = (2 * Math.PI) / fields.length;
console.log(width, height)
//using forEach loop on a NodeList instead of a Jquery .each,
//so we can now use "field" as an iterator instead of $(this)
fields.forEach((field)=>{
var x = Math.round(width / 2 + radius * Math.cos(angle) - field.offsetWidth/2);
var y = Math.round(height / 2 + radius * Math.sin(angle) - field.offsetHeight/2);
console.log(x, y)
field.style.left = x + 'px'; //adding inline style to the document (field)
field.style.top= y + 'px';
angle += step;
})
}
distributeFields();
I'm working my way through some online examples and trying to get my head around how a HTML5 game may be put together.
I've got my head around the basics, such as the game loop, and separating the update and render logic.
I'm at the stage where I can generate a map from a tileset, and render a character who can walk around and have its sprite animate as it moves.
What I've struggling with is the way that the viewport follows the character as he moves. It's mostly working, the camera will follow - however, it has a very strange effect on the map that is generated.
The code so far is pretty long, but I've created a JSFiddle of the current way it works, and it illustrates the problems I'm having. The character can be moved with the arrow keys.
jsFiddle of the current issue
jsFiddle of the code
Incidentally, I've noticed if the character walks into the top left corner, it seems to be OK.
The code that is generating the map is:
tileSize = game.currentMap.tileset.tileWidth;
if (player.x >= (game.viewport.x / 2)) r = Math.floor(player.x - ((map.width * map.tileset.tileWidth) / 2))
for (r = Math.floor(game.viewport.x / map.tileset.tileWidth); r < Math.floor(game.viewport.x / map.tileset.tileWidth) + canvas.width / map.tileset.tileWidth + 1; r++) {
for (c = Math.floor(game.viewport.y / map.tileset.tileHeight); c < Math.floor(game.viewport.y / map.tileset.tileHeight) + canvas.height / map.tileset.tileHeight + 1; c++) {
var tile = ground[r][c];
var tileRow = (tile / game.currentMap.tileset.tilesInImgRow) | 0; // Bitwise OR operation
var tileCol = (tile % game.currentMap.tileset.tilesInImgRow) | 0;
ctx.drawImage(
game.currentMap.tileset.image, (tileCol * tileSize), (tileRow * tileSize),
tileSize,
tileSize, (c * tileSize) - game.viewport.x, (r * tileSize) - game.viewport.y,
tileSize,
tileSize);
tile = layer1[r][c];
tileRow = (tile / game.currentMap.tileset.tilesInImgRow) | 0;
tileCol = (tile % game.currentMap.tileset.tilesInImgRow) | 0;
ctx.drawImage(
game.currentMap.tileset.image, (tileCol * tileSize), (tileRow * tileSize),
tileSize,
tileSize, (c * tileSize) - game.viewport.x, (r * tileSize) - game.viewport.y,
tileSize,
tileSize);
}
}
However, as I'm studying the code from other resources - I'm a bit stuck as to whether the issue is being caused by this function or the function which generates and updates the viewport.
Any suggestions?
Update
The code that adjusts the viewport so that if the character is near the edge of the map, it no longer remains in the center of the viewport:
if (game.viewport.x <= 0) dX = player.x;
else if (game.viewport.x >= game.currentMap.tileset.tileWidth * game.currentMap.width - canvas.width) dX = player.x - game.viewport.x;
else dX = Math.round(canvas.width / 2 - player.width / 2);
if (game.viewport.y <= 0) dY = player.y;
else if (game.viewport.y >= game.currentMap.tileset.tileHeight * game.currentMap.height - canvas.height) dY = player.y - game.viewport.y;
else dY = Math.round(canvas.height / 2 - player.height / 2);
And the code that updates the viewport when the character is moved:
if (player.x + player.width / 2 < canvas.width / 2) {
game.viewport.x = 0;
} else if (player.x + canvas.width / 2 + player.width / 2 >= map.tileset.tileWidth * map.width) {
game.viewport.x = map.tileset.tileWidth * map.width - canvas.width;
} else {
game.viewport.x = Math.floor(player.x - (canvas.width / 2 - player.width / 2));
}
if (player.y + player.height / 2 < canvas.height / 2) {
game.viewport.y = 0;
} else if (player.y + canvas.height / 2 + player.height / 2 >= map.tileset.tileHeight * map.height) {
game.viewport.y = map.tileset.tileHeight * map.height - canvas.height;
} else {
game.viewport.y = Math.floor(player.y - (canvas.height / 2 - player.height / 2));
}
The viewport is initially set using the following:
var game = {
images: 0,
imagesLoaded: 0,
backgroundColor: '#000',
viewport: {
x: Math.floor(player.x - (canvas.width / 2 - playerSpriteSize / 2)),
y: Math.floor(player.y - (canvas.height / 2 - playerSpriteSize / 2))
},
currentMap: map,
fps: 0,
lastfps: 0,
fpsTimer: 0
}
Well, after playing with the code for a few days and many cups of coffee, I've managed to solve my own problem which I thought I would share for anyone else having similar issues.
The code to update the viewport was fine, the problem was the initial values assigned to the viewport and the way that the map x and y tiles were being iterated over.
In the game variable, I changed the viewport to be set to:
viewport: {
x: Math.floor(player.x - (canvas.width / 2)),
y: Math.floor(player.y - (canvas.height / 2))
},
And then in the drawMap function:
for (r = 0; r < map.rowTileCount; r++) {
for (c = 0; c < map.colTileCount; c++) {
This way, we are always generating the full map (which may be unnecessarily using extra resources, but we can always come back and look at this again later) and then we draw just a clipped part of this to the canvas which is exactly what I wanted.
http://jsfiddle.net/zdMSx/6/
How can I calculate the missing number, so that when I add it to a certain variable the result will be equal to or greater than the other variable.
Promoter: 10
Detractor: 2
Total: 12
Average: 66.67
Target: 75
You need ??? Promoters to reach your Target.
I want to find how many Promoters I need if the Average is less than the Target. How can I calculate the missing number so that when I add it to the Promoter the results of the Average is equal to or greater than the Target.
Thank You!
function missingNum() {
var xPromoter = '';
var x = 10;
var y = 2;
var target = 75;
var z = x + y;
var v = ((x - y) / z) * 100;
average = Math.round(v * 100) / 100;
if (average<target) //how to increment x so that average => target
document.write("Promoter:" + "\n" + x + "<br>");
document.write("Detractor:" + "\n" + y + "<br>" );
document.write("Total:" + "\n" + z + "<br>" );
document.write("Average:" + "\n" + average + "<br>" );
document.write("Target:" + "\n" + target + "<br>" );
document.write("You need " + "\n" + xPromoter + "Promoters to reach your Target." );
}
This is a simple problem of algebra.
Average = ((Promoters - Detractors) / Total) * 100
Let fp be the final number of promotors you're looking for
Let d be the current number of Detractors
Let t be your target Average
The formula using these variable to find your target starts as such:
((fp - d) / (fp + d)) * 100 = t
Divide by 100
(fp - d) / (fp + d) = t / 100
Multiply both sides by (fp + d)
fp - d = (t / 100) * (fp + d)
Distribute t / 100
fp - d = (t / 100) * fp + (t / 100) * d
Add d to both sides
fp = (t / 100) * fp + (t / 100) * d + d
Subtract (t / 100) * x from both sides
fp - (t / 100) * fp = (t / 100) * d + d
Simplify the lefthand side
(1 - t / 100) * fp = (t / 100) * d + d
You want to have only fp on the lefthand side, so divide both sides by (1 - t / 100)
fp = ((t / 100) * d + d) / (1 - t / 100)
This is the formula you will use to find the total number of promotors you need
Now just plug in the numbers for t and d and solve for x
fp = ((75 / 100) * 2 + 2) / (1 - 75 / 100)
Writing this all out so you can see the final result:
fp = (.75 * 2 + 2) / (1 - .75)
fp = (1.5 + 2) / .25
fp = 3.5 / .25
fp = 14
Using the variables in your program
var fp = ((target / 100) * y + y) / (1 - target / 100);
xPromoter = fp - x;
Forget looping and do some math! Doing some algebra, the formula for needed promoters, given the target and current number of detractors and promoters, is:
promotersNeeded = (detractors * (target + 100) / (100 - target)) - promoters
So in your example,
promotersNeeded = 2 * (75 + 100) / (100 - 75) - 10
which comes out to 4.
You could always make a simple function.. however a calculation might be better..
function calculateNeededPromoters (promo, detrac) {
var percentage = 0.00;
var currentPromo = promo;
while (percentage < 75) {
currentPromo++;
var total = currentPromo + detrac;
var v = ((currentPromo - detrac) / total) * 100;
percentage = Math.round(v * 100) / 100;
}
return currentPromo;
}