I wrote a javascript code to drop in ball multiple times when clicked on canvas. It is an experiment.
Here is the code:
HTML
<br style="clear: both" />
<canvas id="myCanvas1" width="134px" height="331px" onclick="draw(0)"></canvas>
<canvas id="myCanvas2" width="134px" height="331px" onclick="draw(1)"></canvas>
<canvas id="myCanvas3" width="134px" height="331px" onclick="draw(2)"></canvas>
JAVASCRIPT
var balls = [[], [], []],
canvases = document.getElementsByTagName('canvas'),
context = [],
interval,
boxWidth = 150,
ballRadius = 10,
canvasHeight = 235;
for (var i = 0; i < canvases.length; i++) {
context.push(canvases[i].getContext('2d'));
}
function draw() {
var movement = false;
for (var i = 0; i < 3; i++) {
context[i].clearRect(0, 0, boxWidth, canvasHeight);
for (var j = 0; j < balls[i].length; j++) {
if (balls[i][j].y < balls[i][j].yStop) {
balls[i][j].y += 4;
movement = true;
}
context[i].beginPath();
context[i].fillStyle = "red";
context[i].arc(balls[i][j].x, balls[i][j].y, ballRadius, 0, Math.PI * 2, true);
context[i].closePath();
context[i].fill();
}
}
if (!movement) {
clearInterval(interval);
interval = null;
}
}
function newBall(n) {
console.log('new ball', n);
var last = balls[n][balls[n].length - 1],
ball = {x: ballRadius, y: ballRadius, yStop: canvasHeight - ballRadius};
if (last) {
if (last.x < boxWidth - ballRadius * 3) {
ball.x = last.x + ballRadius * 2;
ball.yStop = last.yStop;
} else {
ball.yStop = last.yStop - ballRadius * 2;
}
}
balls[n].push(ball);
if (!interval) {
interval = setInterval(draw, 10);
}
}
But balls aren't dropping in. Please tell me that where am I wrong so that I can fix it...
balls is [] when the loop starts. So balls[0] is undefined, and thus has no property length.
Your code is always looping from 0 to 3. However, at first, there is no ball around. When you try to reach balls[0], balls[1] and balls[2], you get undefined error.
What you have to do is to change the loop to:
for (var i = 0; i < balls.length; i++)
or if you do not want to change the loop, you can initialize 3 balls at the start:
balls = [ball1, ball2, ball3];
where ball1, ball2 and ball3 are defined as how your ball data type is.
EDIT:
As I understand, you have some number of contexts, and for each context, you want to have a list of balls so that you can draw them.
Then:
balls = []
for (var i = 0; i < canvases.length; i++) {
context.push(canvases[i].getContext('2d'));
balls.push([]);
}
and use the remaining code same.
Related
This is the code
let x = 10;
let y = 0;
let bottomy = 100;
let Speed = 1
function setup() {
createCanvas(windowWidth,600);
}
function draw() {
background(0)
strokeWeight(3)
stroke(255)
for (i = 0; i < width; i += 20) {
water()
line(i,y,i,bottomy)
}
bottomy = bottomy + Speed;
if (bottomy > height) {
bottomy = 100
}
frameRate(1)
}
function water(){
bottomy = random(0,600)
//noLoop()
}
I want to randomise each y2 line coordinate in the for loop. But then have the y2 line coordinate to increment by 1. To create a rain effect.
I can't put the random variable in setup and then call it in the for loop because it won't affect each line in the for loop and I can't put the for loop in setup because I need the line to be drawn.
I've also tried creating a function that loops once and then calling it in draw but it ends up stopping all the code in the draw function.
I've seen examples where they generate like an infinite amount of random lines. But I would like to keep the x position of each line the same if possible. If it's not possible to do this with a for loop and I have to draw each line individually that's fine I was just wondering if this is possible to efficiently do this with a for loop.
I think what you are looking for is individual variables for each line.
Probably a classic:
from-several-variables-to-arrays
from-several-arrays-to-classes
situation. (those were made in Java's processing, but the concept can be easily adapted)
Anyway, i think this is what you tried to make with your code, but it does not work as intended, since you only have one bottomY var for all lines.
let x = 10;
let y = 0;
let bottomY = 100;
let spd = 1; //by convention capitalized names are for classes
function setup() {
createCanvas(windowWidth, 600);
}
function draw() {
background(0);
strokeWeight(3);
stroke(255);
for (i = 0; i < width; i += 20) {
if (bottomY > height) {
bottomY = random(600);
}
line(i, y, i, bottomY);
}
bottomY += spd;
}
What you want is several lines that each has individual x and bottomY
So you could use two arrays for that:
let x = [];
let y = 0;
let bottomY = [];
//why not have individual speeds as well...
let spd = [];
function setup() {
createCanvas(windowWidth, 600);
for (i = 0; i < width; i += 20) {
x.push(i);
bottomY.push(random(height));
spd.push(random(0.6, 2));
}
strokeWeight(3);
stroke(255);
}
function draw() {
background(0);
for (let i = 0; i < x.length; i++) {
line(x[i], y, x[i], bottomY[i]);
if (bottomY[i] < height) {
bottomY[i] += spd[i];
} else {
bottomY[i] = random(height);
}
}
}
And finally a simple class implementation:
let rain = [];
function setup() {
createCanvas(windowWidth, 600);
for (i = 0; i < width; i += 20) {
rain.push(new Drop(i, 0));
}
}
function draw() {
background(0);
for (const d of rain) {
d.run();
}
}
class Drop {
constructor(x, y) {
this.x = x;
this.y = y;
this.btY = random(height);
this.spd = random(0.6, 2);
}
run() {
strokeWeight(3);
stroke(255);
line(this.x, this.y, this.x, this.btY);
if (this.btY < height) {
this.btY += this.spd;
} else {
this.btY = random(height);
}
}
} //class
Did it make sense?
I am writing very basic code hear (just started learning) using Javascript & canvas. I try to do some random drawing (rectangles have 50% chance to appear on certain spots). Once this happens I want browser to wait one second, clear the screen and instantly draw it again (visually one drawing should be instantly replaced by another).
When I do so, it seems like canvas does not want to update, it only updates once when main loop ends, you can find code below:
<script>
let canvas = document.querySelector("canvas");
let context = canvas.getContext("2d");
function wait(ms) {
var start = new Date().getTime();
var end = start;
while (end < start + ms) {
end = new Date().getTime();
}
}
for (let k = 0; k < 3; k++) {
context.clearRect(0, 0, canvas.width, canvas.height);
console.log("First checkpoint");
for (let i = 0; i < 5; i++) {
for (j = 0; j < 5; j++) {
let width_custom = 60;
let height_custom = 60;
let gap = 20;
let x = 100 + (width_custom + gap) * i;
let y = 100 + (height_custom + gap) * j;
context.beginPath();
context.rect(x, y, width_custom, height_custom);
context.stroke();
if (Math.random() > 0.5) {
context.beginPath();
context.rect(x + 8, y + 8, width_custom - 16, height_custom - 16);
context.stroke();
}
}
}
wait(1000);
console.log("Second checkpoint");
}
Is there any way to force canvas to refresh during the loop?
Best,
Mat
Your wait function goes against the asynchronous nature of javascript. You are blocking the program in a way so things don't get refreshed. You should be using setTimeout for this kind of things (or setInterval or even better requestAnimationFrame).
I have prepared quite confusing but fun example how to loop asynchronously and wait. I'm sure other people will suggest to use Promise instead. Or await and async. It's all good.
let canvas = document.querySelector("canvas");
let context = canvas.getContext("2d");
my_loop(3)
function my_loop(k) {
if (k > 0) {
one_round(1000, function() {
my_loop(k - 1)
})
}
}
function one_round(delay, foo_then) {
context.clearRect(0, 0, canvas.width, canvas.height);
for (let i = 0; i < 5; i++) {
for (j = 0; j < 5; j++) {
let width_custom = 60;
let height_custom = 60;
let gap = 20;
let x = 100 + (width_custom + gap) * i;
let y = 100 + (height_custom + gap) * j;
context.beginPath();
context.rect(x, y, width_custom, height_custom);
context.stroke();
if (Math.random() > 0.5) {
context.beginPath();
context.rect(x + 8, y + 8, width_custom - 16, height_custom - 16);
context.stroke();
}
}
}
setTimeout(foo_then, delay);
}
<canvas></canvas>
I am trying to create a small grid for connect four game using a four loop. I have printed circles for X and Y axis but I have only been able to print 1 row successfully, I am trying to print this seven times across the canvas but the for loop I have created does not seem to work.
var x = 30;
var y = 30;
function setup(){
createCanvas(300,300);
background(0);
for(i=0; i<=6; i++){
for(i=0; i<=6; i++){
x+=30;
circle(x, y, 20);
for(i=0; i<=6; i++){
y+=30;
circle(x, y, 20);
}
}
}
}
setup();
I am trying to achieve this:
Change your loop structure - iterate 7 times and increase y at the end of each iteration, and iterate within this loop where you render the circle, and increase x:
for (let i = 0; i < 6; i++) {
x = 30;
for (let j = 0; j < 7; j++) {
circle(x, y, 20);
x += 30;
}
y += 30;
}
You do have three loops that use i, and actually all loops will work on the same number, therefore the inner loop will loop 6times, than all three loops end. As your aim is to loop over x and y, just use them:
for(let x = 1; x < 6; x++) { // always declare variables with let!
for(let y = 1; y < 6; y++) {
circle(x * 30, y * 30, 20); // just keep as many varoables as necessary, the position can easily be derived from the index
}
}
Maybe this is what you need:
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
let cw = (canvas.width = 300),
cx = cw / 2;
let ch = (canvas.height = 300),
cy = ch / 2;
//the circles radius
let ar = 30;
//the red and yellow clees index
let red = [10, 23, 30, 31, 37, 40];
let gold = [16, 17, 24, 32, 38, 39];
let n = 0;// a counter
let cellw = cw / 7;// the width of a cell
//the loop:
for (let y = cellw / 2; y < ch; y += cellw) {
for (let x = cellw / 2; x < cw; x += cellw) {
ctx.beginPath();
ctx.arc(x, y, ar / 2, 0, 2 * Math.PI);
//set the color of the circles
for (let i = 0; i < red.length; i++) {
if (n == red[i]) {
ctx.fillStyle = "red";
break;
} else if (n == gold[i]) {
ctx.fillStyle = "gold";
break;
} else {
ctx.fillStyle = "white";
}
}
ctx.fill();
n++;
}
}
body {
background-color: #222;
overflow: hidden;
}
canvas {
background-color: #000;
display: block;
position:absolute;
margin: auto;
top:0;bottom:0;left:0;right:0
}
<canvas id="canvas"></canvas>
Yep, there's a problem in the for loop.
You just need to 2 loops for that.
for (let row = 0; row <= 6; row++) {
for (let column = 0; column <= 6; column++) {
circle(row * 30, column * 30, 20)
}
}
I'm still getting used to objects, but no matter what I do I can't get my objects to reverse their x or y. It doesn't even register that any of them have hit a canvas wall, they just go off screen.
Here's my code:
"use strict";
//variables
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
var randomX = 0; //in for loop use Math.random()
var randomY = 0; //in for loop use Math.random()
var ballRadius = 10;
var ballX = canvas.width/2;
var ballY = canvas.height-30;
var ballMove = [];
var ballObject = [];
//declares the objects x and y's
for(var i = 0; i < 10; i++){
ballObject[i] = { x: 0, y: 0 };
ballMove[i] = { x:1, y:-1 };
randomX = (Math.random() * (canvas.width-ballRadius)) +1;
randomY = (Math.random() * (canvas.height-ballRadius)) +1;
ballObject[i].x = randomX;
ballObject[i].y = randomY;
}
function draw()
{
ctx.clearRect(0,0,canvas.width, canvas.height);
//didn't actually need this for loop but was experimenting
for(var i = 0; i<10; i++){
drawBall(i);
}
moveBall();
//this method should check the x and y of the balls.
bounceBall();
}
// basic ball drawing function
function drawBall(i)
{
ctx.beginPath();
ctx.arc(ballObject[i].x, ballObject[i].y, ballRadius, 0, Math.PI*2);
ctx.fillStyle = "blue";
ctx.fill();
ctx.closePath();
}
//extremely basic move function (just adds to x and y)
function moveBall()
{
for(var i = 0; i < 10; i++){
ballObject[i].x += ballMove[i].x;
ballObject[i].y += ballMove[i].y;
}
}
//WHY IT NO WORK
//for whatever reason this function doesn't reverse the x and y
function bounceBall()
{
for(var i = 0; i < 10; i++)
{
if (ballObject[i].x == canvas.width-ballRadius || ballObject[i].x == 0+ballRadius){
alert("i've hit a wall");
ballMove[i].x = -ballMove[i].x;
}
if (ballObject[i].y == canvas.height-ballRadius || ballObject[i].y == 0+ballRadius){
alert("i've hit a wall");
ballMove[i].y = -ballMove[i].y;
}
}
}
setInterval(draw, 10);
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Gamedev Canvas Workshop</title>
<style>
* { padding: 0; margin: 15; }
canvas { background: #eee; display: block; margin: 0 auto; }
</style>
</head>
<body>
<canvas id="myCanvas" width="480" height="320"></canvas>
<script src="ballPop.js">
</script>
</body>
</html>
I've kinda made a mess of my code trying to use objects and make them bounce.
I made a brick break game after following some tut online and the bounce worked fine - however that same concept doesn't seem to work with objects.
This likely has to do with the fact that your ball coordinates are floating point values. For example:
> (Math.random() * (200 - 3)) + 1
179.1025927176495
When you do your checks in bounceBall you may get better results by checking if the ball would exceed the boundary
function bounceBall()
{
for(var i = 0; i < 10; i++)
{
if (ballObject[i].x >= canvas.width-ballRadius || ballObject[i].x <= 0+ballRadius) {
alert("i've hit a wall");
ballMove[i].x = -ballMove[i].x;
}
if (ballObject[i].y >= canvas.height-ballRadius || ballObject[i].y <= 0+ballRadius){
alert("i've hit a wall");
ballMove[i].y = -ballMove[i].y;
}
}
}
Note: == changed to >= and <=
I am making a gravity simulator to get the feel for physics based coding and I have made an idea here. But I have a problem, after some point after bouncing, the particle (square) gets stuck bouncing to the same point. Does anyone know why?
Heres a link to the jsfiddle: https://jsfiddle.net/jjndeokk/6/
var c = document.getElementById("canvas");
var ctx = c.getContext("2d");
var gravity, objectDensity, force;
gravity = 10.8;
function Object(mass, x, y, w, h, acc, hacc) {
this.m = mass;
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.a = acc;
this.ha = hacc;
};
var particle = [];
var rows = [1];
for (let i = 0, len = rows.length; i < len; i++) {
particle.push(new Object(10, i * 30, 10, 20, 20, 0, 0));
};
function draw() {
ctx.clearRect(0, 0, c.width, c.height)
for (let i = 0, len = particle.length; i < len; i++) {
ctx.fillRect(particle[i].x, particle[i].y, particle[i].w, particle[i].h)
particle[i].a += gravity;
particle[i].ha = 3;
particle[i].x += particle[i].ha;
if (particle[i].y + particle[i].h + particle[i].a > c.height) {
particle[i].y = c.height - particle[i].h;
} else {
particle[i].y += particle[i].a;
}
}
}
function update() {
for (let i = 0, len = particle.length; i < len; i++) {
if (particle[i].a >= 0) {
if (particle[i].y + particle[i].h >= c.height) {
particle[i].a *= -1;
}
}
}
draw();
}
setInterval(update, 60);
The main reason your bounce gets stuck is that you are applying gravity to the dot even when it is on the ground. After that, you reverse its velocity and it flies back up into the air.
You need to check whether it's on the ground and not apply gravity if it is:
if (isAboveFloor(particle)) {
particle.a += gravity;
}
Once that's fixed, what you'll actually find is that the bounce goes back and forth between its initial height and the ground, and this is to be expected - it's conservation of momentum.
In order to make the bounce more realistic, you need to introduce a "coefficient of restitution" that is less than 1:
if (particle.y + particle.h >= c.height) {
particle.a *= -cRest; // cRest is between 0 and 1
}
Once that's done, you get a pretty nice simulation: https://jsfiddle.net/jjndeokk/17/
I've also made the following modifications:
Used .forEach so that the code isn't completely littered with [i]s
Made the gravity and velocity calculations take time into account
Renamed particle.a and particle.ha to particle.vy and particle.vx because those properties were measuring velocity, not acceleration.
Moved all of the calculations into the update() function so you don't have most of them in the draw() function.
var c = document.getElementById("canvas");
var ctx = c.getContext("2d");
var gravity, objectDensity, force;
gravity = 240; // pixels / second / second
var cRest = 0.6;
var interval = 60;
var secondsPerInterval = interval / 1000;
function Object(mass, x, y, w, h, vxi, vyi) {
this.m = mass;
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.vx = vxi;
this.vy = vyi;
};
var particles = [];
var rows = [1];
for (let i = 0, len = rows.length; i < len; i++) {
particles.push(new Object(10, i * 30, 10, 20, 20, 40, 0));
};
function draw() {
ctx.clearRect(0, 0, c.width, c.height);
particles.forEach(function(particle) {
ctx.fillRect(particle.x, particle.y, particle.w, particle.h);
})
}
function isAboveFloor(particle) {
return Math.abs(particle.y + particle.h - c.height) > 1;
}
function update() {
particles.forEach(function(particle) {
if (particle.vy < 0 || isAboveFloor(particle)) {
particle.x += particle.vx * secondsPerInterval;
particle.y = Math.min(particle.y + particle.vy * secondsPerInterval, c.height - particle.h);
// if still above floor, accelerate
if(isAboveFloor(particle)){
particle.vy += gravity * secondsPerInterval;
}
}
if (particle.vy >= 0 && particle.y + particle.h >= c.height) {
particle.vy *= -cRest;
}
console.log(particle);
});
draw();
}
setInterval(update, interval);
<canvas id="canvas" height="600" width="800"></canvas>