I'm trying to make a game where bullets are shot at a target's last location. The bullets should miss the target unless the target is not moving. Each bullet should continuously move in a single direction using the most recently calculated angle.
for (var z=0; z < this.bullets.length; z++){
var by = enemy1.y - posY;
var bx = enemy1.x - posX;
var fangle = Math.atan2 (by, bx);
velocitiesx[z] = Math.cos(fangle) * 1;
velocitiesy[z] = Math.sin(fangle) * 1;
bullets[z].x += velocitiesx[z] ;
bullets[z].y += velocitiesy[z] ;
}
Here is my problem:
When the target is not moving, the bullets correctly hit the target. However, when the shooter is moving, but the target is still, all of the bullets miss -- but those bullets should all hit the still, non moving target.
I think what is happening is the program keeps calculating the angle for the newest bullet and using that calculated on the older bullets, causing them to change direction. I am not sure how I could make it so that each new bullet follows the most recently calculated angle and keeps moving in that direction.
changed to (still same problem):
function fire(){
counter++;
if (37 in keys && counter >= firerate ) {
var by = enemy1.y - posY;
var bx = enemy1.x - posX;
var fangle = Math.atan2 (by, bx);
velxf = Math.cos(fangle) * 1;
velyf = Math.sin(fangle) * 1;
bullets[bullets.length] = new Box({
x: posX ,
y: posY ,
width: 4,
height: 4,
color: '#7FFF00',
});
counter = 0;
}
}
function movebullets(){
for (var z=0; z < this.bullets.length; z++){
bullets[z].x += velxf ;
bullets[z].y += velyf ;
}
}
calculated angle and velxf and velyf on fire....and made velxf and velyf properties of box as suggested by kokodoko
bullets[bullets.length] = new Box({
x: posX ,
y: posY ,
width: 4,
height: 4,
color: '#7FFF00',
velxb: velxf,
velyb: velyf,
});
function movebullets(){
for (var z=0; z < this.bullets.length; z++){
bullets[z].x += bullets[z].velxb ;
bullets[z].y += bullets[z].velyb ;
}
}
Related
I have a problem with JS canvas ctx.fill() filling outside of my polygonal shape.
Here's how my code works :
ctx.beginPath()
// Here are for loops that draws a the closed shape using
ctx.stroke();
ctx.fill();
Here are the for loops:
var sx1, sy1, ex1, ey1, sx2, sy2, ex2, ey2;
for(var i = 0; i < n; i += Math.floor(n/steps)){
var radius = Math.exp(-2*i/n)*rmax+rmin;
radius += frequencyData[i]/255*(n-i + 200)/n*50;
var angle = -Math.PI/2 - i/n*2*Math.PI;
var x = radius*Math.cos(angle) + w/2+rmin/2;
var y = radius*Math.sin(angle) + (h-110)/2+rmin/2 + analyser_offset;
if (i == 0) {
gctx.moveTo(x,y);
sx1 = x;
sy1 = y;
}else if (i == n-1){
ex1 = x;
ey1 = y;
}else{
gctx.lineTo(x,y);
}
spd += frequencyData[i];
}
for(var i = 0; i < n; i += Math.floor(n/steps)){
var radius = Math.exp(-2*i/n)*rmax+rmin;
radius -= frequencyData[i]/255*(n-i + 200)/n*50;
var angle = -Math.PI/2 - i/n*2*Math.PI;
var x = radius*Math.cos(angle) + w/2+rmin/2;
var y = radius*Math.sin(angle) + (h-110)/2+rmin/2 + analyser_offset;
if (i == 0) {
gctx.moveTo(x,y);
}else if (i == 20){
sx2 = x;
sy2 = y;
}else if (i == n-1){
ex2 = x;
ey2 = y;
} else {
gctx.lineTo(x,y);
}
}
gctx.moveTo(sx1, sy1);
gctx.lineTo(sx2, sy2);
gctx.moveTo(ex1, ey1);
gctx.lineTo(ex2, ey2);
So the first for loop draws the outter side of the shape, the second for loop draws the inner side. And then the sx1, sy1, ex1, ey1, sx2, sy2, ex2, ey2 variables are here to ensure that in the last 4 lines, it closes the shape (by adding vertical line between the outter and inner lines). Maybe this problem happens because I draw the lines in an unusual order? (like drawing a rectangle by starting with 2 horizontal lines and then adding 2 vertical ones)
Here's what I get after the fill() :
And this is what I would like to have:
So could you guide me on how I'm supposed to achieve this?
Ok I fixed it by making the second loop go in the reverse order like this: for (var i = n-1; i >= 0; i -= Math.floor(n/steps)) so it draws the polygon in a more usual order and that works! I don't even need to close it using the last 4 lines which was what I wanted so that's great!
Edited : Thanks to all for valuable time and effort. Finally I made this )) JSfiddle
I was just playing with canvas and made this. Fiddle link here.
... some code here ...
var cords = [];
for(var i = 50; i <= width; i += 100) {
for(var j = 50; j <= height; j += 100) {
cords.push({ cor: i+','+j});
}
}
console.log(cords);
var offset = 15,
speed = 0.01,
angle = 0.01;
cords.forEach(function(e1) {
e1.base = parseInt(Math.random()*25);
e1.rgb = 'rgb('+parseInt(Math.random()*255)+','+parseInt(Math.random()*255)+','+parseInt(Math.random()*255)+')';
});
setInterval(function() {
cords.forEach(function(e1) {
e1.base = parseInt(Math.random()*25);
e1.rgb = 'rgb('+parseInt(Math.random()*255)+','+parseInt(Math.random()*255)+','+parseInt(Math.random()*255)+')';
});
},5000);
function render() {
ctx.clearRect(0,0,width,height);
cords.forEach(function(e1) {
//console.log(e1);
ctx.fillStyle = e1.rgb;
ctx.beginPath();
var r = e1.base + Math.abs(Math.sin(angle)) * offset;
var v = e1.cor.split(',');
ctx.arc(v[0],v[1],r,0,Math.PI * 2, false);
ctx.fill();
});
angle += speed;
requestAnimationFrame(render);
}
render();
Was wondering if -
Coordinates can be made random, now they are fixed as you can see. After 5000 mil, balls will show up in various random cords but even at their fullest they won't touch each other.
Every ball has same speed for changing size, I want that to be different too. Meaning, After 5000 mil, they show up with different animation speeds as well.
Also any suggestion on improving code and making it better/quicker/lighter is much appreciated. Thank you !
TL;DR - See it running here.
Making the coordinates random:
This requires you to add some random displacement to the x and y coordinates. So I added a random value to the coordinates. But then a displacement of less than 1 is not noticeable. So you'd need to magnify that random number by a multiplier. That's where the randomizationFactor comes in. I have set it to 100 since that is the value by which you shift the coordinates in each iteration. So that gives a truly random look to the animation.
Making Speed Random:
This one took me a while to figure out, but the ideal way is to push a value of speed into the array of coordinates. This let's you ensure that for the duration of animation, the speed will remain constant and that gives you a smoother feel. But again multiplying the radius r with a value between 0 and 1 reduces the speed significantly for some of the circles. So I have added a multiplier to 3 to compensate slightly for that.
Ideally I'd put a 2, as the average value of Math.random() is 0.5, so a multiplier of 2 would be adequate to compensate for that. But a little experimentation showed that the multiplier of 3 was much better. You can choose the value as per your preference.
Your logic of generating the coordinates changes as follows:
for(var i = 50; i <= width;i += 100) {
for(var j = 51; j <= height;j += 100) {
var x = i + (Math.random() - 0.5)*randomizationFactor;
var y = j + (Math.random() - 0.5)*randomizationFactor;
cords.push({ cor: x+','+y, speed: Math.random()});
}
}
Your logic of enlarging the circles changes as follows:
function render() {
ctx.clearRect(0,0,width,height);
cords.forEach(function(e1) {
//console.log(e1);
ctx.fillStyle = e1.rgb;
ctx.beginPath();
var r = e1.base + Math.abs(Math.sin(angle)) * offset * e1.speed * 3;
var v = e1.cor.split(',');
ctx.arc(v[0],v[1],r,0,Math.PI * 2, false);
ctx.fill();
});
angle += speed ;
requestAnimationFrame(render);
}
Suggestion: Update the coordinates with color
I'd probably also update the location of circles every 5 seconds along with the colors. It's pretty simple to do as well. Here I've just created a function resetCoordinates that runs every 5 seconds along with the setBaseRgb function.
var cords = [];
function resetCoordinates() {
cords = [];
for(var i = 50; i <= width;i += 100) {
for(var j = 51; j <= height;j += 100) {
var x = i + (Math.random() - 0.5)*randomizationFactor;
var y = j + (Math.random() - 0.5)*randomizationFactor;
cords.push({ cor: x+','+y, speed: Math.random()});
}
}
}
UPDATE I did some fixes in your code that can make your animation more dynamic. Totally rewritten sample.
(sorry for variable name changing, imo now better)
Built in Math.random not really random, and becomes obvious when you meet animations. Try to use this random-js lib.
var randEngine = Random.engines.mt19937().autoSeed();
var rand = function(from, to){
return Random.integer(from, to)(randEngine)
}
Internal base properties to each circle would be better(more dynamic).
var circles = [];
// better to save coords as object neither as string
for(var i = 50; i <= width; i += 100)
for(var j = 50; j <= height; j += 100)
circles.push({
coords: {x:i,y:j}
});
We can adjust animation with new bouncing property.
var offset = 15,
speed = 0.005,
angle = 0.01,
bouncing = 25;
This is how setBaseRgb function may look like
function setBaseRgb(el){
el.base = rand(-bouncing, bouncing);
el.speed = rand(5, 10) * speed;
el.angle = 0;
el.rgb = 'rgb('+rand(0, 255)+','+rand(0, 255)+','+rand(0, 255)+')';
}
All your animations had fixed setInterval timeout. Better with random timeout.
cords.forEach(function(el){
// random timeout for each circle
setInterval(setBaseRgb.bind(null,el), rand(3000, 5000));
})
You forgot to add your base to your circle position
function render() {
ctx.clearRect(0,0,width,height);
circles.forEach(function(el) {
ctx.fillStyle = el.rgb;
ctx.beginPath();
var r = bouncing + el.base + Math.abs(Math.sin(el.angle)) * offset;
var coords = el.coords;
ctx.arc(
coords.x + el.base,
coords.y + el.base,
r, 0, Math.PI * 2, false
);
ctx.fill();
el.angle += el.speed;
});
requestAnimationFrame(render);
}
render();
Effect 1 JSFiddle
Adding this
if(el.angle > 1)
el.angle=0;
Results bubling effect
Effect 2 JSFiddle
Playing with formulas results this
Effect 3 JSFiddle
I am currently trying to detect a mouse click on two grids of boxes simultaneously. One grid is easy, and I've just been using:
var gridPosX = Math.floor(mouseClickX/BoxWidth);
var gridPosY = Math.floor(mouseClickY/BoxHeight);
Now I also want to detect a mouse click on a secondary grid of boxes, located at the corners of the first grid of boxes. This could be achieved in a similar way to the first grid. The problem comes in because I want to detect a click on either the first grid, or the second one, at the same time. What is the best way to differentiate a click on the first grid verses a click on the second grid? I've tried to remove the Math.floor and used the greater than and less than operators (> <) to see if the click was closer to one grid spot than the other, but I've had no luck with that so far.
This is an image example of the grid. The black being the main one, the red being the second one
var WIDTH = 1280, HEIGHT = 1280;
var canvas, context;
var grid = [];
var grid2 = [];
var gridWidth = 10, gridHeight = 10;
var boxWidth = WIDTH/gridWidth, boxHeight = HEIGHT/gridHeight;
function main(){
canvas = document.createElement("canvas");
canvas.width = WIDTH;
canvas.height = HEIGHT;
context = canvas.getContext("2d");
document.body.appendChild(canvas);
canvas.onmousedown = function(e){
if(e.which == 1){
var gridPosX = Math.floor(e.offsetX/boxWidth);
var gridPosY = Math.floor(e.offsetY/boxHeight);
grid[gridPosX][gridPosY] = 0;
}
}
init();
setInterval(draw, 30);
}
function init(){
for(var x = 0; x < gridWidth; x++){
grid[x] = [];
grid2[x] = [];
for(var y = 0; y < gridHeight; y++){
grid[x][y] = 1;
grid2[x][y] = 1;
}
}
}
function draw(){
for(var x = 0; x < gridWidth; x++){
for(var y = 0; y < gridHeight; y++){
if(grid[x][y] == 1){
context.fillStyle = 'gray';
context.fillRect(x*boxWidth, y*boxHeight, boxWidth, boxHeight);
context.strokeRect(x*boxWidth, y*boxHeight, boxWidth, boxHeight);
}
}
}
for(var x = 0; x < gridWidth; x++){
for(var y = 0; y < gridHeight; y++){
if(grid2[x][y] == 1){
context.fillStyle = 'red';
context.fillRect((x*boxWidth)+(boxWidth)-(boxWidth/4), (y*boxHeight)+(boxHeight)-(boxHeight/4), boxWidth/2, boxHeight/2);
context.strokeRect((x*boxWidth)+(boxWidth)-(boxWidth/4), (y*boxHeight)+(boxHeight)-(boxHeight/4), boxWidth/2, boxHeight/2);
}
}
}
}
main();
Since the red grids show on top of the gray ones, I think you can first decide whether a mouse event is on a red grid or not. If not, then it must be on gray grids.
Based on the calculations below to check if a red grid is clicked:
var xRedIndex = Math.floor((e.offsetX - 3 / 4 * boxWidth) / (boxWidth / 2));
var yRedIndex = Math.floor((e.offsetY - 3 / 4 * boxHeight) / (boxHeight / 2));
if (xRedIndex % 2 === 0 && yRedIndex % 2 === 0) {
console.log("red");
console.log("Red grid x: " + (xRedIndex / 2));
console.log("Red grid y: " + (yRedIndex / 2));
} else {
console.log("gray");
var gridPosX = Math.floor(e.offsetX / boxWidth);
var gridPosY = Math.floor(e.offsetY / boxHeight);
grid[gridPosX][gridPosY] = 0;
}
Basically, you first subtract the initial gray area in the first column/row from the offsetX/Y, then see if the rest of the offsetX/Y contains an odd or even number of boxSize/2 (side length of red grid). An even number means the click is on red grids, otherwise it falls on the uncovered gray area.
Working fiddle: https://jsfiddle.net/mwxzgth6/1/
I am trying to plot coordinates around a square programatically here it is hard coded to show what i am after.
http://jsfiddle.net/zwkny/
// amount of chairs
var aoc = 4;
// table width
var tw = 200;
// table height
var th = 200;
// chair width height
var cwh = 60 / 2;
var space = tw * 4 / aoc;
var left = [-30,100,-30,-160];
var top = [-160,-30,100,-30];
// straing point
var sp = 12;
for(var i=0; i<aoc; i++){
var x = cwh + space * i / aoc;
console.log(x);
//var y = space / 2 - cwh * i;
$("#center").append("<div class='chair' style='left:"+left[i]+"px;top:"+top[i]+"px;'>"+i+"</div>");
}
Maths is definately not my strong point just thought i would post up here see if anyone can help point me in the right direction i keep going and update if i get it???
I need it this way to represent people the small circles standing around the large square but there will be random amounts of people and they all need to be at equal distances.
I posted the same post about a circle object yesterday and now i am on squares i just cant get my head around the maths, any help.
Sore that this has been voted down just thought i would update with a post putting all these together
http://devsforrest.com/116/plot-positions-around-shapes-with-javascript
Hope it helps someone else
var x,y;
// amount of chairs
var totalChairs = 12;
// square size
var squareSize = 200;
var chairSize = 20;
for(var i=0; i<totalChairs; i++){
var angle = 2*Math.PI * i/totalChairs;
if (angle > Math.PI/4 && angle <= Math.PI* 3/4){
x = (squareSize/2) / Math.tan(angle);
y = -squareSize/2;
} else if (angle > Math.PI* 3/4 && angle <= Math.PI* 5/4){
x = -squareSize/2;
y = (squareSize/2) * Math.tan(angle);
} else if (angle > Math.PI* 5/4 && angle <= Math.PI* 7/4){
x = -(squareSize/2) / Math.tan(angle);
y = -squareSize/2 + squareSize;
} else {
x = -squareSize/2 + squareSize;
y = -(squareSize/2) * Math.tan(angle);
}
x -= chairSize/2;
y -= chairSize/2;
$("#center").append("<div class='chair' style='left:"+x+"px;top:"+y+"px;'></div>");
}
Demo
I have a HTML5 canvas that generates a bouncing box every time you click on it. The box array stores the x-value, y-value, x-velocity, and y-velocity of each box created. The box will travel in a random direction at first and will bounce of the sides of the canvas but if it hits a corner the box dissappears instead of bouncing back. EDIT: I answered my own question noticing that the soundY and soundX functions were causing the problem.
var box = new Array();
var width = window.innerWidth;
var height = window.innerHeight;
var field = document.getElementById('canvas');
field.width = width;
field.height = height;
field.ctx = field.getContext('2d');
field.ctx.strokeStyle = 'rgba(255,255,255,1)';
setInterval('redraw()', 200);
addEventListener('click', createBox, false);
function createBox(e) { // this box will always fail collision detection at the upper-left corner
box.push(100); // x-value is normally mouse position
box.push(100); // y-value is normally mouse position
box.push(-5); // x-speed is normally random
box.push(-5); // y-speed is normally random
}
function redraw() {
field.ctx.clearRect(0,0,width,height);
for(var i = 0; i < box.length; i+=4) {
if(box[i] < 0) { box[i+2] *= -1; soundY(box[i+1]); } // parameter of soundY is less than 0
else if(box[i] > width) { box[i+2] *= -1; soundY(box[i+1]); } // which is invalid and causes this to break
if(box[i+1] < 0) { box[i+3] *= -1; soundX(box[i]); }
else if(box[i+1] > height) { box[i+3] *= -1; soundX(box[i]); }
box[i] += box[i+2];
box[i+1] += box[i+3];
field.ctx.strokeRect(box[i], box[i+1], 4, 4);
}
}
function soundX(num) {
// play a sound file based on a number between 0 and width
}
function soundY(num) {
// play a sound file based on a number between 0 and height
}
The only way I could recreate the problem was by generating the box in one of the corners so that with the right x and y velocity the box was initially created outside the bounds of the canvas. When that happens, the inversion of the velocity isn't enough to bring the item back in bounds and so on the next frame the velocity is inverted again (and so on).
I think this might solve your problem:
var boxes = [];
var boxSize = 4;
var width = window.innerWidth;
var height = window.innerHeight;
var field = document.getElementById('canvas');
function redraw() {
field.ctx.clearRect(0, 0, width, height);
var box;
for (var i = 0; i < boxes.length; i++) {
box = boxes[i];
field.ctx.strokeRect(box.x, box.y, boxSize, boxSize);
if (box.x < 0) {
box.x = 0;
box.dx *= -1;
} else if (box.x > width - boxSize) {
box.x = width - boxSize;
box.dx *= -1;
}
if (box.y < 0) {
box.y = 0;
box.dy *= -1;
} else if (box.y > height - boxSize) {
box.y = height - boxSize;
box.dy *= -1;
}
box.x += box.dx;
box.y += box.dy;
}
}
field.width = width;
field.height = height;
field.ctx = field.getContext('2d');
field.ctx.strokeStyle = 'rgba(0,0,0,1)';
setInterval(redraw, 200);
addEventListener('click', createBox, false);
function createBox(e) {
boxes.push({
x: e.clientX - 10,
y: e.clientY - 10, // arbitrary offset to place the new box under the mouse
dx: Math.floor(Math.random() * 8 - boxSize),
dy: Math.floor(Math.random() * 8 - boxSize)
});
}
I fixed a few errors in your code and made some changes to make it a bit more readable (I hope). Most importantly, I extended your collision detection so that it resets the coordinates of the box to the bounds of your canvas should the velocity take it outside.
Created a jsfiddle which might be handy if further discussion is needed.
It was additional code (see edit) that I left out assuming it was unrelated to the issue, but removing the code solved the problem as it appears this use-case would cause an invalid input in this part of the code.