One function to rule them all - javascript

I am coding an application which displays balls obeying certain laws of physics.
So I have a Ball Object, and a path prototype. This path prototype calculates the coordinates of the ball at any given moment and draws it, that goes kinda like this :
Ball.prototype.path = function(v) {
modifying the ball coordinates...
ctx.arc(....);
(other canvas stuff)}
If I want to display an animation of the ball, I do this:
var ball1 = new Ball(...);
var ball2...
function loop () {
ctx.beginPath(); // The balls won't show up if I begin and close the Path in path(), I don't know why...
ball1.path();
ball2...
ctx.closePath();
};
setInterval(loop, 0.0015);
But I want to add a button which adds and displays balls. So I'm looking for a function which executes these commands to any ball added.
It's a little bit tricky, because it has to:
Create and name a new variable.
Execute path() according to that name.
All of that, in the same loop function so I can make a setInterval later.
EDIT: FIXED
#Frederik #Doorknob I've used a BallArray:
var BallArray = new Array(); i=0; function AddBallonClick() { i++; BalleArray.push(i) }; function loop() { for (var i=0;i<BalleArray.length;i++) { ctx.beginPath(); var ball = new Ball(0, 0, 40); ball.path(); ctx.closePath(); }; }; setInterval(loop, dt);
But I want to name the new variables ball_i, ie: ball_1, ball_2..., and I don't know how to. The script doesn't seem to be working even when I add the ball just once, so that's a problem too...
EDIT 2: FIXED
Also, I want to set an initial speed to every new ball, typically I do this:
ball.v = new V(...);
But now that I have an array, I added this to the loop, but it doesn't work...:
balles[i].v = new V(...)
EDIT 3:
I have another problem, whenever I click the button, a ball is not added and drawn, but instead the animation "replays". It seems that javascript can't draw balls at the same time with my kind of code:
function loop()
{
for(var i = 0; i < balls.length; i++) {
ctx.beginPath();
balls[i].path();
ctx.closePath();
}
};
setInterval(loop, dt);
EDIT: ALL FIXED
I've solved the last problem you just have to put the ctx.clearRect(0, 0, width, height) in the loop function but before the for(var i=0...).
Thanks to all of you :) !

As mentioned in the comments, the answer is arrays. You don't seem to quite understand them, so here's a quick overview. An array is sort of a list of objects. In your case, you probably want a list of Balls. You can initialize it like this:
var balls = []; // [] is about the same as new Array(), but more concise
To add a new ball to it, you can use push, passing it the ball:
balls.push(new Ball(/* ... */));
(You could, of course, pass it an already-existing ball, too:)
var ball = /* obtain ball from elsewhere */;
balls.push(ball);
You do seem to understand how to loop through the arrays, but not how to get the values as you loop through it. To loop through the array, you use a for loop:
for(var i = 0; i < balls.length; i++) {
// ...
}
Obviously, i will be an integer from 0 to balls.length. We can't do much with the number on its own, though; what we really want is the ball at that index in the array. You can do this by indexing the array. That would look like this:
var ball = balls[i];
Now ball contains the ball at position i in the balls array, and you can do whatever you want with it from there. In your case, you probably want to call path on it:
// If you've stored it into a variable as above:
ball.path();
// Or more concisely without having to store it into a variable:
balls[i].path();
With arrays, there is no need for variables named, e.g., ball_1, ball_2, etc. Instead, you just have an array, balls, and index it, e.g., balls[0], balls[1], etc.

Related

draw function not looping as expected

I'm a beginner on here, so apologies in advance for naivety. I've made a simple image on Brackets using Javascript, trying to generate circles with random x and y values, and random colours. There are no issues showing when I open the browser console in Developer Tools, and when I save and refresh, it works. But I was expecting the refresh to happen on a loop through the draw function. Any clues as to where I've gone wrong?
Thanks so much
var r_x
var r_y
var r_width
var r_height
var x
var y
var z
function setup()
{
r_x = random()*500;
r_y = random()*500;
r_width = random()*200;
r_height = r_width;
x = random(1,255);
y= random(1,255);
z= random(1,255);
createCanvas(512,512);
background(255);
}
function draw()
{
ellipse(r_x, r_y, r_width, r_height);
fill(x, y, z);
}
Brackets.io is just your text editor (or IDE if you want to be technical) - so we can remove that from the equation. The next thing that baffles me is that something has to explicitly call your draw() method as well as the setup() method -
I'm thinking that you're working in some sort of library created to simplify working with the Canvas API because in the setup() method you're calling createCanvas(xcord,ycord) and that doesn't exist on it's own. If you want to rabbit hole on that task check out this medium article, it walks you thru all the requirements for creating a canvas element and then drawing on that canvas
Your also confirming that you're drawing at least 1 circle on browser refresh so i think all you need to focus on is 1)initiating your code on load and 2)a loop, and we'll just accept there is magic running in the background that will handle everything else.
At the bottom of the file you're working in add this:
// when the page loads call drawCircles(),
// i changed the name to be more descriptive and i'm passing in the number of circles i want to draw,
// the Boolean pertains to event bubbling
window.addEventListener("load", drawCircles(73), false);
In your drawCircles() method you're going to need to add the loop:
// im using a basic for loop that requires 3 things:
// initialization, condition, evaluation
// also adding a parameter that will let you determine how many circles you want to draw
function drawCircles(numCircles) {
for (let i = 0; i < numCircles; i++) {
ellipse(r_x, r_y, r_width, r_height);
fill(x, y, z);
}
}
here's a link to a codepen that i was tinkering with a while back that does a lot of the same things you are
I hope that helps - good luck on your new learning venture, it's well worth the climb!
Thank you so much for your help! What you say makes sense - I basically deleted the equivalent amount of code from a little training exercise downloaded through coursera, thinking that I could then essentially use it as an empty sandpit to play in. But there's clearly far more going on under the hood!
Thanks again!

How do I save/store the output of the random function?

I'm coding a tree in different seasons (though the original code for the trunk and randomized branches is not my own). For winter, I want to keep the tree randomized, but also have animated snowflakes.
However, each time the function that makes the tree is called, the random numbers are (obviously) different. It there a way to save the output of the function (or just of the random() functions within it) and just draw that output?
The draw() function calls the function that makes the tree, so if it is called multiple times, the tree will rapidly change, since it is randomized each time. The tree function has to repeat itself to draw the tree, so there are a lot of random numbers. Ultimately, I just want to draw a randomized tree, and then repeatedly draw that same tree, so that the draw function can repeatedly run and not cover up the tree. Here are the 2 lines of code with the random numbers I want to save:
rotate(random(-0.05,0.05));
if (random(1.0) < 0.6) {
You want to use the randomSeed() function here. If you call it at the top of your draw() function each time, with the same number, then you will get the same 'random' tree. If you change the number, you will get a different 'random' tree. If you want a new tree (perhaps when the mouse is clicked), then you can change the number. For example:
let rs;
function setup() {
rs = random(0, 1000);
// your code
}
function draw() {
randomSeed(rs);
// your code
}
function mouseClicked {
rs = random(0, 1000);
}
You could do this by storing the values in variables. Here's a simplified example:
var x;
var y;
function setup(){
createCanvas(500, 500);
x = random(width);
y = random(height);
}
function draw(){
background(32);
ellipse(x, y, 25, 25);
}
This is just a simple example, but you could get fancier by using something like a Tree class that stores its own values. But the idea is the same: generate the random value once, store it in a variable, and then use that variable when you draw it.
You could also draw the tree to a buffer once, and then draw that buffer to the screen. The createGraphics() function would come in handy for this approach.

Canvas game framerate drop

Good evening, first i'm not sure i should post this here, since i have litteraly no idea what's wrong here (only clue is it happened after i moved all my variables at the top of my code while trying to "clean up" a little).
but here is the problem: i've created a canvas game and after moving my variables (i think) i started getting major framedrops. Game weighs less than 20Ko, images are super tiny and simple, i have a for loop in the update loop but it never seemed to be a problem (it's not infinite) so in short i do not know what's wrong here
here is a bit of code since links to code "must be accompanied by code" (dont know what's up with that)
for (var i = 0; i<boxes.length; i++){
ctx.rect(boxes[i].x, boxes[i].y, boxes[i].width, boxes[i].height);
var col = coli(player,boxes[i])
};
i've tried different things, like disabling functions (anim and colision), friction and gravity but nothing seems to have any effect, and i dont know that much about the dom except how to look at my own variables so i havent found anything with firebug.
Really hope someone has an idea
You need to add ctx.beginPath() before using ctx.rect, moveTo, lineTo, arc, and any function you need to use ctx.stroke() or ctx.fill() to see.
beginPath tell the canvas 2D context that you wish to start a new shape. If you do not do this you end up adding more and more shapes each update.
From your fiddle
function update() {
ctx.clearRect(0, 0, width, height);
ctx.fillStyle = pat;
//===============================================================
ctx.beginPath() // <=== this will fix your problem. It removes all the
// previous rects
//===============================================================
for (var i = 0; i < boxes.length; i++) {
ctx.rect(boxes[i].x, boxes[i].y, boxes[i].width, boxes[i].height);
// or use ctx.fillRect(...
var col = coli(player, boxes[i])
};
ctx.fill();

adding a margin to a fillRect with JavaScript canvas

I am making a snake game for fun, to practice JavaScript/canvas, and what I have so far is a while loop that says while i is < 100, then add a square and the square is added by using fillRect(10, 10, xCoord, yCoord) and in the while loop I have xCoord = xCoord + 11; so it moves the square to the right with a space in between... here is a link:
http://brycemckenney.com/snake_canvas
And the relevant code:
while(i < 100) {
// fillRect(x, y, width, height)... x is horizontal and y is vertical...
context.fillRect(xCoord, yCoord, sW, sH);
// We want each squere to have one pixel in between them, so we increment by 11, not 10.
xCoord = xCoord + 11;
// When i (0) gets to numOfSquares (3), then make the rest white so that you can't see them...
if (i >= numOfSquares) {
context.fillStyle = "#FFFFFF";
}
//Increment by 1 every loop, so that the squares keep going
i++;
}
I am trying to get the snake to animate, and I have tried many different options. Now I am trying to just add a margin-right the the 4 squares, so it looks like it's moving... is it possible to add margin to those? Here is a snapshot of what I have:
Well, since you're making the game of snake, we know what you want to do and I think it would be more useful if we tried to get you on the right track instead of modifying your existing code.
Lets suppose our logical game grid is 60x60.
So snake-pieces can be anywhere in this grid, having X and Y values between 0 and 59. This means:
A piece of snake in the top-left corner is at [0, 0]
A piece of snake in the top-right corner is at [59, 0]
Let us further suppose that a snake is made up of a number of segments. How about 4 segments to start. This means that we need to keep an array of the 4 positions:
[position1, position2, position3, position4]
We have to pick a front, so lets say that the end of the array is the "front" of the snake. If we pick the top left 4 positions the snake would be:
var mySnake = [[0, 0], [1,0], [2,0], [3,0]]
Which on the board looks like this:
++++OOOOOOOO
OOOOOOOOOOOO
OOOOOOOOOOOO
That means its 4 snake-pieces going from left to right, just like you have so far. The thing is, by saving these locations we've gone and added some persistent state to our program.
Now snake is a funny game, because "moving" the snake really means two things:
Taking the last (tail) piece away
Adding a new piece to the snake
So we should make a function for moving the snake that does both of these. We'll make one based on direction:
// modifies a snake's array
function moveSnake(snake, direction) {
// First we must remove a piece of the snake's tail, which is at the start of the array
// There's a built in command in JavaScript for this called shift()
snake.shift();
// Now we must add the new piece!
// To tell where we need to go we must look at the last location:
var lastLoc = snake[snake.length - 1];
// Just to be a little more clear:
var lastX = lastLoc[0];
var lastY = lastLoc[1];
switch (direction) {
case 'up':
snake.push([lastX, lastY-1]);
break;
case 'down':
snake.push([lastX, lastY+1]);
break;
case 'left':
snake.push([lastX-1, lastY]);
break;
case 'right':
snake.push([lastX+1, lastY]);
break;
}
// redraw after every move!
drawSnake(ctx, mySnake);
}
With this method we could do:
var mySnake = [[0, 0], [1,0], [2,0], [3,0]];
moveSnake(mySnake, 'down');
// mySnake is now [[1,0], [2,0], [3,0], [3,1]];
The snake now looks like this:
O+++OOOOOOOO
OOO+OOOOOOOO
OOOOOOOOOOOO
Now this method is pretty dumb, and in a real game of snake we'd need to add some conditions. Typically:
If the new piece of snake is on top of a piece of food, the tail piece is NOT removed, instead the snake is +1 block longer
If the new piece is outside of out 60x60 grid, the user loses the game
If the new piece is at a location that any of the other snake pieces already exist, the user loses the game
Alas those are not done here
We still need to draw it all, but since we are keeping track of the snake's location that's easy peasy. We can just draw one square for each piece of the snake:
function drawSnake(context, snake) {
// Remember to clear the board!
ctx.clearRect(0, 0, 600, 600);
var length = snake.length;
for (var i = 0; i < length; i++) {
context.fillStyle = 'teal';
// position is its own array and looks like: [x, y]
var position = snake[i];
// our logical snake board is 60x60, but the real canvas is 600x600,
// so we multiply x and y by 10.
// We also use "9" for the width and height so theres a tiny gap
context.fillRect(position[0]*10, position[1]*10, 9, 9);
}
}
See the completed demo here.
http://jsfiddle.net/FsqXE/
Note that it allows arrow keys to control the snake to show off the moveSnake method, but the regular snake game has the movement controlled by a timer, and the user can usually only change the next-possible-direction.
The canvas element is a 2D drawing surface, so it doesn't support (higher level) things like CSS margins.
To do animation, you'll need to continually clear the canvas and redraw the scene, each time slightly adjusting the position of moving objects (so that they appear to move). In your case, this means redrawing the squares, each time with a starting position 4 pixels away from the last time you redrew the frame.
You may want to have a look at the Basic animations page at MDN (part of their canvas tutorial).

Box2d.js with Processing.js

Recently I have been furthering my knowledge of Javascript, by combining the Processing.js and Box2D.js libraries to make some neat browser-based simulations.
With my current idea, I am trying to allow the user to click and drag a shape, and then let it drop once the mouse is released. So far I have been able to figure out how to use the b2MouseJoint object to manipulate a body with mouseX/mouseY coordinates, but it doesn't quite work to the full extent.
All that happens when a shape is clicked is it gets pinned and revolves around what ever mouseX/mouseY point was current at the time of the click.
void mousePressed(){
for(int i = 0; i < circles.size(); i++){
//Get body objects from ArrayList
var obj = circles[i];
// Retrieve shapes from body
var innerShape = obj.GetShapeList();
var rad = innerShape.m_radius;
// Create mouseJoint and add attributes
var mouseJoint = new b2MouseJointDef();
mouseJoint.body1 = world.GetGroundBody();
// Detect body
if(dist(mouseX,mouseY,obj.m_position.x,obj.m_position.y) < rad){
Vec2 p = new b2Vec2(mouseX,mouseY);
mouseJoint.body2 = obj;
mouseJoint.target = p;
mouseJoint.maxForce = 10000.0f * obj.GetMass();
mouseJoint.collideConnected = true;
mouseJoint.dampingRatio = 0;
mouseJoint.frequencyHz = 100;
world.CreateJoint(mouseJoint);
}
}
}
So basically my question is, how can this be written so the Body/Shape follows my mouse's coordinates while I have the mouse held down, instead of just pinning the shape in place.
Cheers
Basically all you have to do is add the code you're using now to set the coordinate in mousePressed to a mouseDragged() method, as well, as this is the event method that gets called while the mouse is moved with one or more buttons depressed:
void mouseDragged()
{
// update obj with mouseX and mouseY in this method
}
You may also want to do a bit more administration by setting up "initial click" mark variables during mousePressed(), updating a set of "offset" variables during mouseDragged() and committing the offsets to the marks in mouseReleased(), so that you can do things like snapping back to the original position, etc.

Categories