How do I change a variable while using a function? - javascript

I'm coding on the p5.js Website Editor
So I'm trying to make a lot of rectangles that move when I press a specific key.
To make that I thought of making a function where I would put everything related to the rectangles moving, so that I don't have to rewrite a code to make them move every time. I want all of them to move the same way.
This is what I tried
function wall(x, y, sx, sy){
rect(x, y, sx, sy);
if(keyIsDown(65)){
return x+1;
}
}
wall(300, 300, 20, 30);
and just got a rectangle in the right coordinates but not moving when I press the "a" key

When you want to move things in p5.js, you need to use the draw() function. The draw() function runs the code inside of it repeatedly multiple times a second. You can use that to create an illusion of movement by updating the location of your object by a small amount every frame and then re-rendering the object.
I don't see you using the draw() function, so I'm guessing this is the first part of your problem.
The second part I see is that you are referring to "a lot of rectangles", but you are only making one.
Consider the code below for a quick solution (copy-paste it into the p5.js editor to see it in action).
let wallX = 200;
let wallY = 100;
let wallSpeed = 2;
let wallWidth = 20;
let wallHeight = 30;
function setup() {
createCanvas(400, 400);
}
function draw() {
background(220);
if (keyIsDown(LEFT_ARROW)) {
wallX = wallX - wallSpeed;
} else if (keyIsDown(RIGHT_ARROW)) {
wallX = wallX + wallSpeed;
}
if (keyIsDown(UP_ARROW)) {
wallY = wallY - wallSpeed;
} else if (keyIsDown(DOWN_ARROW)) {
wallY = wallY + wallSpeed;
}
wall(wallX, wallY, wallWidth, wallHeight);
}
function wall(x, y, sx, sy) {
for (let i = 0; i < 5; i++){
rect(x, y + sy * i, sx, sy);
}
}
In this code snippet, I am defining some global variables (generally not a good practice, but for the purposes of a p5.js sketch this is fine). The important ones are the wallX and the wallY variables. These variables are defined outside of the draw() function, so they will not be reset every time the loop runs.
Inside the loop, there are key handlers defined (I used the arrow keys for clarity). If the arrow keys are pressed, the position of the wall will change by the wallSpeed value per frame.
Also, note the background(220) call - this redraws the grey background at the beginning of the loop. If you remove that, the results of the previous renders will be visible, chances are that you don't want that.
Finally, the wall() function. You can see that the key press handling is not done inside of it - it needs the state information to come from the outside. All this function does is use a loop to draw five rectangles in a stack, at the position it is given. When the position changes, it redraws the wall in a different place on the canvas. The rectangles all refer to the root x and y values, so they will all move together as a unit.
Another way would be to create the class to represent a Wall and make instances of that class and call methods on those instances, but that could be something to revisit once you gain more experience with p5.js.
Hope this helps!

Related

Having trouble trying to make an circle move on a canvas element, while programming with JS compiled Kotlin

I am building a very simple web-based 'breakout', and I have run into trouble when trying to make the ball move on the canvas element. I do have the game up and running in javascript. Thing is, I am now trying to port it to Kotlin(javascript compiled). Even after doing the adaptations I think were necessary, the ball won't move.
I am also having trouble with types(thats why you will see those "*1.0" lying around), since I've not been able to convert from int to double, but I wouldn't say that's the issue here. Also, the IDE is showing a remark I am not familiar with. I talk about that on the section about error messages, but the message is as follows: "Wrapped into a reference object to be modified when captured in a closure".
I am not sure where exactly the problem lies, but so far I have tried:
-Other signatures for my 'setInterval' call
-Reformating the program so that my code is within main(), as to get rid of the return and 'val canvas = initializeCanvas()' call.
import org.w3c.dom.*
import kotlin.browser.*
import kotlin.math.*
val canvas = initializeCanvas()
fun initializeCanvas(): HTMLCanvasElement {
val canvas = document.createElement("canvas") as HTMLCanvasElement
val ctx = canvas.getContext("2d") as CanvasRenderingContext2D
ctx.canvas.width = 480
ctx.canvas.height = 320
document.body!!.appendChild(canvas)
var x = canvas.width/2
var y = canvas.height-30
val dx = 2
val dy = -2
fun drawBall(){
ctx.beginPath()
ctx.arc(x*1.0, y*1.0, 10.0, 0.0, PI*2)
ctx.fillStyle = "#000000"
ctx.fill()
ctx.closePath()
}
fun draw(){
ctx.clearRect(0.0, 0.0, canvas.width*1.0, canvas.height*1.0)
drawBall()
x += dx
y += dy
}
window.setInterval(draw(),10)
return canvas
}
The expected output would be that of the ball moving towards the top-right of the canvas, and then vanishing into the wall, since I have not yet implemented collisions.
The current output, as I stated, is of a static ball.
As for error messages, there are none. But an "IDE Note" has been bothering me. on the code I provided, inside the draw function, I increment x and y. When I hover over them, intellij says that they are "Wrapped into a reference object to be modified when captured in a closure". I have never seen such warning before and a web search has not been fruitful.
Wrapped into a reference object to be modified when captured in a
closure
That merely means that you have to wrap the method you want to execute - draw - inside a closure. Without it will just invoked once.
So simply change
window.setInterval(draw(), 10)
to
window.setInterval( { draw() }, 10)

How to get a circle to move when you click on it in javascript?

I'm making a whack-a-mole game based on a 3x3 grid for one of my classes but i'm having trouble getting the mole(an ellipse made using p5.js) to move to a different grid square on click. I want it to automatically move after 7 seconds which i achieved using function sleep(milliseconds), but if the player clicks the mole before the 7 seconds is up then I want it to switch grid squares right then. This is my code for mousePressed and moveCircle.
function mousePressed() {
var distance = int(dist(mouseX, mouseY, moleX, moleY));
if(distance <= circleDiameter/2 ) {
moveCircle();
score += 1;
}
document.getElementById('playerScore').innerHTML=score;
}
function moveCircle(){
var randomX = [110, 260, 410];
var randomY = [95, 245, 395];
var moleX = random(randomX);
var moleY = random(randomY);
}
Your sleep() function is not how you should handle timing in P5.js, or in JavaScript in general.
Your sleep() function causes your program to become completely unresponsive, which is bad for a lot of reasons. One problem is that your event code will not trigger, which is the problem you're seeing.
Instead, you need to use the millis() function or the frameCount variable to handle your timing without blocking your whole program. Here's a small example:
function setup() {
createCanvas(200,200);
background(32);
}
function draw(){
// 60 frames is 1 second
if(frameCount % 60 == 0){
ellipse(random(width), random(height), 25, 25);
}
}
This code uses the frameCount variable along with the % modulus operator to check whether 1 second has elapsed. In other words, this program draws a circle every second.
You need to do something similar to move your mole, which will allow your event code to be fired even while the mole is "waiting".
int(dist(mouseX, mouseY, moleX, moleY))
This int keyword does not make sense. If you want it to be a whole number, use the Math.round() function (or just round() in p5js).
Try console.loging the distance variable.

Canvas Rendering Optical Illusion in JS Game (Briefly duplicated graphics) - Fix?

I'm creating a side-scrolling space-shooter in javascript. So far everything seems to be working well. However, there is an odd bug in the canvas rendering that I can't quite figure out (and is difficult to describe, so bear with me!)
I have a player that can shoot projectiles by clicking the left mouse button. When the projectile first leaves the player, there appears to be two of them for a brief second, until they eventually merge in to the one projectile. I'm not creating two, so this seems like an optical illusion (this is most evident if you fire a few projectiles in quick succession).
The really odd thing is, when I try and capture a screenshot of this happening, all looks fine. Can anyone figure out what's going on?
Player code including projectiles (full code in fiddle);
var Player = (function () {
// ------------------------------------------------------------------------------------------------
// PLAYER VARIABLES
// ------------------------------------------------------------------------------------------------
var w = 50;
var h = 50;
var x = 0;
var y = 0;
var projectiles = [];
// ------------------------------------------------------------------------------------------------
// BIND EVENTS TO THE GLOBAL CANVAS
// ------------------------------------------------------------------------------------------------
Canvas.bindEvent('mousemove', function (e) {
y = (e.pageY - Canvas.element.getBoundingClientRect().top) - (h / 2);
});
Canvas.bindEvent('click', function () {
createProjectile(50, (y + (h / 2)) - 10);
});
// ------------------------------------------------------------------------------------------------
// FUNCTIONS
// ------------------------------------------------------------------------------------------------
var createProjectile = function (x, y) {
projectiles.push({
x: x,
y: y
})
};
var update = function () {
for (var p = projectiles.length - 1; p >= 0; p--) {
projectiles[p].x += 10;
if (projectiles[p].x > Canvas.element.width)projectiles.splice(p, 1);
}
};
var render = function () {
Canvas.context.fillStyle = 'white';
Canvas.context.fillRect(x, y, w, h);
for (var p = 0; p < projectiles.length; p++) {
Canvas.context.fillStyle = 'red';
Canvas.context.fillRect(projectiles[p].x, projectiles[p].y, 5, 5);
}
};
// ------------------------------------------------------------------------------------------------
// Exposed Variables and Functions
// ------------------------------------------------------------------------------------------------
return {
update: update,
render: render
}
})();
Js Fiddle Demo HERE: https://jsfiddle.net/oqz204bj/
EDIT
Based on #Pimskie's answer, It does indeed seem like an optical illusion - so my question now becomes, how could I reduce this effect? I plan on implementing a feature in the future that allows the player to switch weapons (where some of them would **actually* fire multiple projectiles) but I don't want this effect to remain for fear of confusion.
yes it is an optical illusion. The reason it looks like there multiple squares when first fired is because your eyes are focused on the big static ship square. Once your eye starts to follow the movement path, then it looks more like a fluid square moving instead of a square being redrawn 60 or 30 times per second. hold a piece of paper or your hand up to your screen covering the left half of it. Focus on the piece of paper and fire a few shots. You'll notice that the shots seem to appear multiple, the same as when just fired. It's a matter of your mind seeing 3 different frames as the same one.
requestAnimationFrame depends on the frame rate of your browser and computer. In most cases that's 60fps. 60 to 70fps is the limit of most monitors, and so it doesn't make sense to try and go above that. HOWEVER you can create the illusion of a more fluid movement by having a trailing tracer effect on your projectiles. That would involve having 2 or 3 extra squares created behind each projectile that have less and less opacity.
My best guess it's an optical illusion indeed.
Check this updated fiddle: https://jsfiddle.net/oqz204bj/1/
I removed one requestAnimationFrame and replaced a other with a very slow setInterval, just for demonstration. You can see only one bullet is created.

Canvas won't draw rectangle after for loop in function

I'm trying to create a news ticker that renders text in little square "pixels". I say "pixels" because they look like pixels but the actual squares being displayed are bigger than just 1px.
So far I can get all the letters rendered from an object that I built which contains the pixel coordinates for each rectangle. Code can be seen here:
https://jsfiddle.net/9u3ez6gu/2/
The letters render correctly, but after my for loop (see code sample below) I'm trying to create a lime colored space between each letter. This lime space never gets rendered no matter what I do. Here is the for loop and the code I'm using to render the space. Does anyone know why canvas will not let me draw the lime colored rectangle?
for (i = 0; i <= inv[letter].length; i++) {
var x = inv[letter][i][1] * full;
var y = inv[letter][i][0] * full;
context.beginPath();
context.rect(x, y, unit, unit);
context.fillStyle = 'black';
context.closePath();
context.fill();
}// End for loop
//Add a gap between letters
var gapx = full * 5;
context.beginPath();
context.rect(gapx, 0, full, full);
context.fillStyle = 'lime';
context.closePath();
context.fill();
}// End function
Your for loop goes one iteration too far:
for (i = 0; i <= inv[letter].length; i++) {
Should be <, not <=. Keep your developer console open!
Also, i should be declared with var, either at the top of the function or in the for loop header itself. In this case it (probably) doesn't matter, but it's good to get in the habit. If you don't declare the variable, it'll be global. If another of your functions also fails to declare another i, they'll be the same thing, and weird bugs can result.
If you put
"use strict";
at the top of your <script> blocks or the very top of each function, the parser (in modern browsers) will flag assignments to implicit global variables as errors.

How do js animations work?

Im trying to understand how to make a javascript animation run smoothly and I've been reading some answers around here and I found something I don't understand.
Here is the link to the question Smooth javascript animation
In the answer with most votes it says "which is why generally it's a good idea to base position/frame on the amount of time that has elapsed since the start of the animation (using new Date().getTime()) rather than moving/changing a fixed amount each frame."
Can anyone show me a very very simple example that uses the method from this answer, and explain how you then control the speed of the animation?
Overview
Generally with animations, you have three components
Update
Draw
Timer
These are run within a loop, called the animation loop. A typical animation loop might look like the following (I will explain all of the functions in detail below):
function animate() {
update(); // Executes all game logic and updates your world
draw(); // Draws all of the animated elements onto your drawing context
timer(); // Controls the timing of when animate will be called again
};
animate(); // Start animating
The animation loop is the main flow controller of what goes on inside your animation. Basically the code inside the animation loop is called over and over again. Each execution of the animate function constitutes a frame. During a frame, your world is updated and redrawn on the screen. The frequency with which the animate function runs is called the frame rate, and is controlled by the timer.
You will also need a reference to the drawing context, which will be used to hold the elements you wish to animate, also known as sprites:
// Create a variable with a reference to our drawing context
// Note this is not the same as using a canvas element
var canvas = document.getElementById("canvas");
Update
Update is responsible for updating the status of each item you wish to animate, once per frame. To take a simplistic example, say you have an array containing three cars, each with an x and y position and a velocity. On each frame, you want to update the position of the car to reflect the distance it should travel based on its velocity.
Our cars array, and the code used to generate a car might look like this:
// A method to create new cars
var Car = new Car(x, y, vx, vy) {
this.className = "car"; // The CSS class name we want to give the car
this.x = x || 0; // The x position of the car
this.y = y || 0; // The y position of the car
this.vx = vx || 0; // the x component of the car's velocity
this.vy = vy || 0 // the y component of the car's velocity
};
// A function that can be called to update the position of the car on each frame
Car.prototype.drive = function () {
this.x += this.vx;
this.y += this.vy;
// Return an html string that represents our car sprite, with correct x and y
// positions
return "<div class='"
+ this.className
+ "' style='left:"
+ this.x
+ "px; top:"
+ this.y
+ "px;'></div>";
};
// Create a variable to hold our cars
var cars = [
new Car(10, 10, 5, 3),
new Car(50, 22, 1, 0),
new Car(9, 33, 20, 10)
];
When we call update, we will want to call the drive method of each car in order to move the car sprites around the screen. This drive method will return an html string that represents the sprite, including it's current position. We will want to append this string to a variable that can be used to set the inner HTML of the canvas div:
// An empty string that will be used to contain the innerHTML for our canvas
var htmlStr = "";
// Update the position of each car
function update() {
// Clear the canvas content
htmlStr = "";
for (var i = 0, len = cars.length; i < len; i++) {
// Add the car sprite to the html string to be rendered
htmlStr += cars[i].drive();
}
};
Draw
When the update function is done outputting our sprites, we will need to actually draw the elements on the canvas. In order to do this, we use the innerHTML method of the canvas variable, passing it in htmlStr, which contains markup used to represent all of the sprites. This will ask the browser to parse the text and spit DOM elements out on the screen:
function draw() {
// Parse the text containing our sprites and render them on the DOM tree
canvas.innerHTML = htmlStr;
};
You could argue that there are better ways to do this, and there probably are. However, in the interest of keeping it simple, I kept it simple. This is also not the most performant method, as the DOM API is very slow. If you look at the system resource usage of this application, most of it will be hogged by innerHTML calls. If you want a faster context for drawing, you should use HTML 5 canvas.
Timer
Finally, you need some way of calling animate over and over again. You could do this a couple of ways. First, there is good old setTimeout:
setTimeout(animate, 0);
This instructs the browsers to call animate after a delay of 0ms. In practice, animate will never execute after a delay of 0ms. The minimum resolution of setTimeout in most browsers is around 15ms, but even that is not guaranteed, due to the way the UI thread works in javascript.
A better solution is to use requestAnimationFrame, which basically tells the browser you are doing animation. The browser will do a bunch of nice optimizations for you. However, this is not fully supported across browsers. You should use this solution for a cross-browser requestAnimationFrame polyfill:
http://paulirish.com/2011/requestanimationframe-for-smart-animating/
Then you can use:
requestAnimFrame(animate);
In the end your finished program will look something like:
// shim layer with setTimeout fallback
window.requestAnimFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(/* function */ callback, /* DOMElement */ element){
window.setTimeout(callback, 1000 / 60);
};
})();
// Create a variable with a reference to our drawing context
// Note this is not the same as using a canvas element
var canvas = document.getElementById("canvas");
// A method to create new cars
var Car = new Car(x, y, vx, vy) {
this.className = "car"; // The CSS class name we want to give the car
this.x = x || 0; // The x position of the car
this.y = y || 0; // The y position of the car
this.vx = vx || 0; // the x component of the car's velocity
this.vy = vy || 0 // the y component of the car's velocity
};
// A function that can be called to update the position of the car on each frame
Car.prototype.drive = function () {
this.x += this.vx;
this.y += this.vy;
// Return an html string that represents our car sprite, with correct x and y positions
return "<div class='"
+ this.className
+ "' style='left:"
+ this.x
+ "px; top:"
+ this.y
+ "px;'></div>";
};
// Create a variable to hold our cars
var cars = [
new Car(10, 10, 5, 3),
new Car(50, 22, 1, 0),
new Car(9, 33, 20, 10)
];
// An empty string that will be used to contain the innerHTML for our canvas
var htmlStr = "";
// Update the position of each car
function update() {
// Clear the canvas content
htmlStr = "";
for (var i = 0, len = cars.length; i < len; i++) {
// Add the car sprite to the html string to be rendered
htmlStr += cars[i].drive();
}
};
function draw() {
// Parse the text containing our sprites and render them on the DOM tree
canvas.innerHTML = htmlStr;
};
function animate() {
update(); // Executes all game logic and updates your world
draw(); // Draws all of the animated elements onto your drawing context
requestAnimFrame(animate); // Controls the timing of when animate will be called again
};
animate(); // Start animating
Conclusion
There is a lot of room for optimization, tweaking, abstraction, and a whole lot of other goodness that I didn't go into here. There are a ton of ways to implement update and draw, and all have there pros and cons.
However, every animation you will ever write will use some sort of animation loop and they all have this basic architecture. Hopefully, this illustrates the basics of an animation in javascript.
The basic idea is the following:
You want to move a DIV from 0,0 to 100,0 in 1 second.
You'd like it to have 50 frames per second (using a setTimeout(fun, 20))
However, since the callback is not guaranteed to run in exactly 20ms, your callback needs to figure out when it was actually run. For example, let's say your animation started at time X but your first animation callback wasn't called until X+5ms. You need to calculate the position that the div should be at 25ms of the animation, your animation callback can't assume 50 even steps.
Here's a very simple code sample. I don't usually code with globals but this is the easiest way to show the technique.
http://jsfiddle.net/MWWm6/2/
var duration = 1000; // in ms
var startTime; // in ms
var startX = 0;
var endX = 500;
// 0 means try to get as many frames as possible
// > 1: Remember that this is not guaranteed to run as often as requested
var refreshInterval = 0;
var div = document.getElementById('anim');
function updatePosition() {
var now = (new Date()).getTime();
var msSinceStart = now - startTime;
var percentageOfProgress = msSinceStart / duration;
var newX = (endX - startX) * percentageOfProgress;
div.style.left = Math.min(newX, endX) + "px";
if (window.console) {
console.log('Animation Frame - percentageOfProgress: ' + percentageOfProgress + ' newX = ' + newX);
}
if (newX < endX) {
scheduleRepaint();
}
}
function scheduleRepaint() {
setTimeout(updatePosition, refreshInterval);
}
div.onclick = function() {
startTime = (new Date()).getTime();
scheduleRepaint();
}
This post describes the best way to do animations: http://paulirish.com/2011/requestanimationframe-for-smart-animating/
That will run at 60fps, or as fast as possible.
Once you know that, then you can decide how long you want your animation to take and how far the object is moving, then work out how far it has to move each frame.
Of course, for smooth animations, you should use CSS Transitions where ever possible – take a look at http://css3.bradshawenterprises.com.
The quick answer is that setTimeout doesn't guarantee that the callback will be executed n number of milliseconds after you call it. It just guarantees that it will be executed no sooner than n milliseconds from that time. John Resig covers some of this in this piece.
For this reason, you need to check what time your animation callback has actually executed, not what time you've scheduled it with setTimeout.
I've written a blogpost which demonstrates the concept of "time based" animation using canvas and a sprite sheet.
The example animates a sprite and takes into account the elapsed time to make sure it remains consistent in different framerates. You could easily apply this into moving an element around a page by changing the code slightly. The concepts related to timing and animation itself remain pretty much the same.
http://codeutopia.net/blog/2009/08/21/using-canvas-to-do-bitmap-sprite-animation-in-javascript/
(I always feel so spammy leaving answers that basically just link to my blog, even if the link is actually entirely relevant :D )
Look at it this way: You've got some object you want to move. Let's say you're giving it 5 seconds to move from A to B, and you want it to be done at 30fps. That means you've got to update the object's position 150 times, and have at most 1/30 = 0.0333 seconds per update.
If you're using the "time since last adjustment", and your code takes 0.05 seconds to execute, then you're not going to make the 30fps. However, if you're basing the animation off when it started, then it doesn't matter how long your code takes - when your update code fires, it'll calculate and set the object's position should be at that stage of the animal, regardless of how many frames have actually been displayed.

Categories

Resources