Okay, so in code.org's game lab, they offer a pre-made function called "line();" which should draw a line stemming from a given x1 y1, and x2 y2 coordinate. When I attempted to create a for-loop that would allow me to draw 3 line segments on the same y pixel, the for-loop simply ran forever inside the draw loop, (but not correctly, it just moved the x2 and y2 to the right infinitely), so I moved it outside of the draw loop and now I'm getting no output at all...
var spaceXPos = 70;
var spaceXPos2 = spaceXPos + 25;
for (var i = 0; i < 4; i++){
stroke(rgb(255, 255, 255));
strokeWeight(2);
line(spaceXPos, 200, spaceXPos2, 200);
spaceXPos = spaceXPos + 20;
}
function draw() {
background("wheat");
}
This is for an independent project, I'm using code.org because it offers an easy-to-use screen for animations and drawing, and I'm also taking an AP CompSci class through it as well.
Here's a link to this code in the Game Lab
https://studio.code.org/projects/gamelab/_w391nq3oEo62S3WOOpWg6NrMrMHNvo8n20gVMDu5hg
When your line() command is outside of the draw loop, as in the sample code you posted, the following happens, in order:
Setup (once): Your setup code (everything outside of your draw loop) runs first. This includes your for loop, which ends up calling line() four times.
Draw (every 33ms): Your draw() function calls background("wheat"), painting over the output of your setup code. This happens many times each second.
So you see no output (except for the background color) because your draw() function is painting over the lines your for loop drew.
If, as you describe above, you move the for loop into draw() so that your code looks like this:
var spaceXPos = 70;
var spaceXPos2 = spaceXPos + 25;
function draw() {
background("wheat");
for (var i = 0; i < 4; i++){
stroke(rgb(255, 255, 255));
strokeWeight(2);
line(spaceXPos, 200, spaceXPos2, 200);
spaceXPos = spaceXPos + 20;
}
}
...then the following happens:
Setup (once): spaceXPos is set to 70 and spaceXPos2 is set to 95.
Draw (first time): The background is drawn, and then your lines are drawn. Every time you draw a line you are adding 20 to spaceXPos, but spaceXPos2 is not changing, so the four lines drawn in the first draw call are:
line(70, 200, 95, 200)
line(90, 200, 95, 200)
line(110, 200, 95, 200)
line(130, 200, 95, 200)
Draw (second time): The background is drawn again, and then your lines are drawn again. However, the value of spaceXPos didn't get reset, so it keeps moving to the right. Notice that the x2 value 95 hasn't changed, so one end of the line is staying in the same place.
line(150, 200, 95, 200)
line(170, 200, 95, 200)
line(190, 200, 95, 200)
line(210, 200, 95, 200)
Draw (and so on): One end of the line keeps moving to the right until it's off the screen, and no more changes are visible, so you end up with something like this:
If your goal is to draw three line segments in a row, you'll need to make sure both the x1 and x2 values change for each call, and make sure they start in the same place on every draw call. Here's one way to do that:
var START_X = 70;
var FIXED_Y = 200;
var WIDTH = 20;
var GAP = 5;
function draw() {
background("wheat");
stroke(rgb(255, 255, 255));
strokeWeight(2);
for (var i = 0; i < 3; i++){
line(
(WIDTH + GAP) * i + START_X,
FIXED_Y,
(WIDTH + GAP) * i + START_X + WIDTH,
FIXED_Y
);
}
}
Related
I have the below code rendering a set of trees, The problem with this code is it includes noLoop. if noLoop is removed, the trees keep pulsing and generating new trees. I need to move this tree function to another long code, which is actually a game with a lot of objects in it. The noLoop in this function stop everything else in my draw function. and I wasn't able to restructure the code to make part of it in the setup.
The code below :
function setup() {
createCanvas(windowWidth, windowHeight);
background(200);
}
function draw() {
noLoop(); // NoLoop must be in draw in order for the code tho work
branchIteration(width / 2, height / 4);
}
function branch(len, firstTime = false, x, y) {
translate(x, y);
if (firstTime) {
translate(width / 2, height / 2);
firstTime = true;
}
angleMode(DEGREES);
push();
if (len > 10) {
strokeWeight(map(len, 10, 100, 1, 15));
stroke(70, 40, 20);
line(0, 0, 0, -len);
translate(0, -len);
rotate(random(-20, -30));
console.log(random(-20, -30));
branch(len * random(0.7, 0.9), 0, 0 - len);
rotate(random(50, 60));
branch(len * random(0.7, 0.9), 0, 0 - len);
} else {
var r = 80 + random(-20, 20);
var g = 120 + random(-20, 20);
var b = 40 + random(-20, 20);
fill(r, g, b, 150);
noStroke();
ellipse(0, 0, 10);
beginShape();
for (var i = 135; i > 40; i--) {
var rad = 15;
var x = rad * cos(i);
var y = rad * sin(-i) + 20;
vertex(x, y);
}
endShape(CLOSE);
}
pop();
}
function branchIteration(xPos, yPos) {
for (var j = 0; j < 10; j++) {
push();
var xPosOffset = -2000 + j * 350;
translate(xPos + xPosOffset, yPos);
branch(60, true);
pop();
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.5.0/p5.js"></script>
Link to try the code https://editor.p5js.org/josefalk/sketches/taCAyFI4X
I want noLoop removed from draw.
Others advised me to place the function in setup. I cannot load this function in the setup, Because I'm doing a transformation in the destination code draw function and I want trees to move with other objects. as below:
Draw ();
push(); translate(-cameraPosX * 0.2, 0);
function mountain
function clouds
some other functions
tree code function here // which I cannot add
pop(); // transform end status.
I want noLoop removed
I don't want to use conditional statements. e.g if (!branchesDrawn) {branchIteration(width / 2, height / 4); branchesDrawn = true; }
because it disappears quickly because other items render on top of it.
I tried also importing values from the setup function, but It did not work.
I need:
re-structure the code without noLoop and the program loading the same set of tree every frame. When you re-load the page new trees will be generated.
In the code, you see a lone logging random value to the console : console.log (random (-20, -30)) . The console is logging a lot of random value lines then it stop. even it has noLoop in it. But if you place the same random console logging in setup, You will only get one line random value. why the random value generating few more number in draw but it generate only one number in setup? I'm missing some knowledge here.
Trees are random, so every time you generate one it will look different.
One option is to include a randomSeed(42) at the beginning of your draw function, so everytime trees are generated they will look the same. (you can put any number, 42 is just an example)
function draw() {
randomSeed(42);
branchIteration(width / 2, height / 4);
}
However trees are expensive to generate (specially if your recursion goes very deep), so I'd instead generate them once, then use saveCanvas() to save the current canvas as an image and then use that image in the draw function.
If redrawing your background at every frame drops your fps there are other techniques to only update a desired part of the image and leave the rest of the background static.
Take a look at the library https://osteele.github.io/p5.libs/p5.layers/ that includes a lot of useful tools for drawing at different layers, so you can leave some layers static and only update other layers, making your program performance increase.
Finally, I'd recommend you start using classes, so your code gets more organized. Make a class Tree and put it in a different file, so your main file keeps as simple and clean as possible. That's important as you will start adding more and more things, and keeping things encapsulated in classes is a very good idea.
I'm making a tower defense game using JavaScript and p5.js library. I have 2 images a base and a gun. The gun is drawn on top of the base to make it appear as a single unit. I need my gun to point towards the enemy(which follows a path). I need help to make the gun rotate on a point(not centre), keeping in mind that there can be several of these towers. I have tried translating(to change centre) and rotating but this doesn't work for many objects and obviously can't keep track of many objects.
I haven't seen any other question regarding this matter either, is there a better solution/alternative to what I'm trying to accomplish?
Required Code
Some Important Variables
var isFireTowerPressed = false; // checks if tower is placed
var FireTowerPos = []; // location of every tower => [] - 2d array
Preloaded Stuff
function preload() {
backgroundImg = loadImage("http://127.0.0.1:8080/img/extra/map1.png");
[...]
firetowerbaseImg = loadImage("http://127.0.0.1:8080/img/towers/firetowerbase.png");
firetowerturretImg = loadImage("http://127.0.0.1:8080/img/towers/firetowergun.png");
}
Draw Every Frame
function draw()
{
background(60, 238, 161);
[...]
if (isFireTowerPressed == true) //checks if I have pressed the button to place the tower
{
image(firetowerbaseImg, mouseX - 28, mouseY - 28);
// show range circle
noFill();
stroke(0,0,0);
strokeWeight(1);
circle(mouseX, mouseY, 300);
}
for (var i = 0; i < FireTowerPos.length; i++)
{
image(firetowerbaseImg, FireTowerPos[i][0], FireTowerPos[i][1]);
image(firetowerturretImg, FireTowerPos[i][0], FireTowerPos[i][1]-20);
}
}
Mouse Click Event
function mouseClicked()
{
if (isFireTowerPressed==true && mouseX+28 <= 750) // place-able area
{
FireTowerPos.push([mouseX-28, mouseY-28]);
isFireTowerPressed = false;
}
}
The picture shows the 2 pictures I'm using(base & gun). I need to be able to rotate the gun towards the enemy
Any help is appreciated, thank you
Translating and rotating works for several objects if you also use the push() and pop() methods, from the p5 library, which allow you to store the current settings.
To rotate towards a given point the function atan2(y2 - y1, x2 - x1) is the commonly used method.
To illustrate, I wrote this quick snippet in which the guns will rotate towards the mouse only if the mouse is within range of the gun.
let gunSprite
function preload(){
gunSprite = loadImage("https://i.imgur.com/ayvg9J2.jpg", ()=>{
gunSprite.resize(20, 40)
})
}
let guns = [
{ x: 160, y: 80, angle: 0, range: 100 },
{ x: 300, y: 200, angle: 0, range: 150 },
{ x: 100, y: 240, angle: 0, range: 120 }
]
function setup(){
createCanvas(600, 400)
noFill()
}
function draw(){
background(200)
for(let gun of guns)
drawGun(gun)
}
function drawGun(gun){
const isWithinRange = dist(mouseX, mouseY, gun.x, gun.y) < gun.range
if(isWithinRange)
gun.angle = atan2(mouseY - gun.y, mouseX - gun.x) + radians(90)
push()
translate(gun.x, gun.y)
rect(-25, -20, 50, 40) // Draw the gun base
ellipse(0, 0, gun.range*2) // display the gun range
rotate(gun.angle)
image(gunSprite, -10, -40) // Set the offset of the gun sprite and draw the gun
pop()
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.1.9/p5.min.js"></script>
I am doing a small assignment that requires the following:
I have to create a while-loop that draws 30 lines on-screen. Line number 7 and line number 23 should be colored red.
Because I am still trying to figure things out, I try to color the 2 nearest lines from stroke(0); to stroke(255);, but no matter what I try, I cannot get the 2 lines to change color
In this example I tried nesting a while loop, which so far doesn't work. I also tried removing the nested while loop and add an "if" statement with the same variables (x == 40 && x == 60) but still nothings happens. What can I do to fix this?
var x = 20;
var stap = 20;
var stop = 600;
function setup () {
createCanvas(700, 700);
}
function draw () {
stroke(0);
while(x < stop) {
line(x, 60, x, 80);
x += stap;
while (x == 40 && x == 60) {
stroke(255);
}
}
}
You're very close, you just don't need that inner while loop!
For completeness, here's a p5 solution:
var x = 20;
var stap = 20;
var stop = 600;
function setup() {
createCanvas(700, 700);
}
function draw() {
while (x < stop) {
if (x === 20 || x === 40) {
stroke(255, 0, 0);
} else {
stroke(0);
}
line(x, 60, x, 80);
x += stap;
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.2/p5.js"></script>
You said you'd like to get the nearest two to display, those lines are at x position 20 and 40, rather than 40 and 60!
You are setting the stroke color in your if-statement after drawing the line. Make sure to first set the colors, then draw whatever you are going to draw.
The syntax for a while loop is as follows:
while (condition) {
statements to execute while the condition is true
}
In your case:
while (line_number <= 30) {
y = step_size * line_number;
// Determine which color to use
if (line_number == 7 || line_number == 23) {
// Set color to RED
line_color = color(127, 0, 0);
} else {
// Set alternate color
line_color = color(127, 127, 127);
}
// Set the color to draw the line
stroke(line_color);
// Draw a horizontal line
line(0, y, width, y);
// Go to the next line
line_number++;
}
So, basically, I have created a canvas on which I draw points. The coordinates of those points are obtained via a JSON obtained from a url. Here's the function that draws the points:
function drawDATA() {
getJSON('http://theossrv2.epfl.ch/aiida_assignment2/api/points/',
function (err, data) {
if (err !== null) {
alert('Something went wrong: ' + err);
} else {
for (var i = 0; i < data.circles.length; i++) {
c.beginPath();
c.arc(data.circles[i].x, data.circles[i].y, 5, 0, 2 * Math.PI);
c.strokeStyle = 'red';
c.stroke();
c.fillStyle = 'red';
c.fill();
}
}
});
}
where c is the context of my canvas.
Now, in the HTML code, I have a button that calls this function when it is pressed:
<input id="Refresh" type="button" value="Refresh" onclick="drawDATA();" />
As you can probably imagine, each time I press the button, new points are added to the canvas (while the points that were previously drawn stay). For example, once I open my HTML file and press the button twice, I have 6 points on my canvas (each time I click on the button, three points appear).
My problem is that I want the previous point to fade out in the following manner:
The first time I press the button, the first 3 points (call them A,B,C) appear normally.
The second time I press the button, three new points appear (D,E,F) and the opacity of A,B,C is set to 75%
The third time I press the button, G,H,I appear, the opacity of A,B,C is set to 50% and the opacity of D,E,F is set to 75%
... and so on (so, after 5 clicks, the points A,B,C must completely disappear).
I tried to approach this problem by creating a CSS file that allows me to fadeOut some elements of my canvas, but it was far from the expected result (so I don't think it is necessary for me to show it).
I hope I was clear enough. Thank you for your help.
The simplest way would be to paint your whole canvas with an opaque layer before applying your new points:
c.beginPath();
c.fillStyle = 'rgba(255, 255, 255, 0.25)';
c.fillRect(0, 0, c.canvas.width, c.canvas.height);
c.closePath();
// draw new points here
Here's a live example
Queue Array
If instead of painting the entire canvas with an opaque layer
you could create a queue array of sets of points that also have each a alpha property.
function drawSET( set ) {
for (var i = 0; i < set.circles.length; i++) {
var circle = set.circles[i];
c.beginPath();
c.arc(circle.x, circle.y, 5, 0, 2 * Math.PI);
c.strokeStyle = 'rgba(255, 0, 0,'+ set.alpha +')';
c.stroke();
c.fillStyle = 'rgba(255, 0, 0,'+ set.alpha +')';
c.fill();
c.closePath();
}
}
var queue = [];
var alphaStep = 0.2;
function drawDATA() {
getJSON('http://theossrv2.epfl.ch/aiida_assignment2/api/points/', function (err, data) {
if (err) return console.log('Something went wrong: ' + err);
queue.push( {alpha: 1, circles: data.circles} ); // Append
if (queue.length > 1/alphaStep) queue.splice(0,1); // Remove first
c.clearRect(0, 0, c.canvas.width, c.canvas.height); // clear canvas
for (var i=0; i<queue.length; i++) {
var set = queue[i];
drawSET( set ); // Draw as is
set.alpha -= alphaStep; // And lower alpha for next iteration
}
});
}
Here's a live example - controlling each set opacity
Re-render scene using alpha to fade.
You need to create an array holding the points. Each point has an alpha value that you decrease each time you draw it. When that alpha value reaches zero you remove the point from the array.
When you get new points you add them to the array of points setting their alpha to one and then render all the points.
Each time you draw the circles you must clear the canvas and draw all the circles. If you have other content on the canvas you must either redraw that content or save it as a separate canvas.
const points = []; // array to hold points
// clear canvas and redraw all points
function drawPoints(ctx, points){
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
for (var i = 0; i < points.length; i ++) {
const point = points[i];
ctx.beginPath();
ctx.globalAlpha = point.alpha;
ctx.fillStyle = "red";
ctx.arc(point.x, point.y, 5, 0, 2 * Math.PI);
ctx.fill();
// decrease alpha. Will fade through 0.75, 0.50, 0.25
point.alpha -= 0.25;
if (point.alpha <= 0) { // remove point
points.splice(i--,1);
}
}
ctx.globalAlpha = 1; // restore the alpha
}
function drawDATA() {
getJSON('http://theossrv2.epfl.ch/aiida_assignment2/api/points/',
function (err, data) { // I am ignoring error
// add new points to points array
points.push(...data.circles.map(circle => {
return {x : circle.x, y : circle.y, alpha : 1};
}));
// Use animation frame to ensure its presented correctly.
requestAnimationFrame(()=> {
drawPoints(c, points); // draw points
});
}
});
}
You could memorize the dots you drew in one iteration and redraw them with a color closer to the background color in the next iteration. You cannot retrieve previously drawn shapes in your canvas so it should be the best way to do it.
use a fillStyle of rgba.
Keep the alpha values in your data.circles array, then decrease them as desired, and use it
c.fillStyle = "rgba(255,0,0,"+data.circles[i].alpha+")";
I am new to coding and Javascript and I'm a little lost. I am trying to create multiple lines that animate on refresh. something like this...
http://jsfiddle.net/79zcp/6/
if (min < max) {
context.beginPath();
if (direction) {
context.moveTo(topMinX, topMinY);
topMinX = topMinX + 2;
context.lineTo(topMinX, topMaxY);
} else {
context.moveTo(topMinX, topMinY);
topMinY = topMinY + 2;
context.lineTo(topMaxX, topMinY);
}
context.lineWidth = 4;
context.stroke();
}
}
but with multiple lines going down the y axis, spaced about 20 pixels apart.
My teacher suggested making the multiple lines with an array but i'm totally lost.
I've forked the Fiddle to create a new one: http://jsfiddle.net/UcrUj/3.Note that I've changed the functions such that it only works for vertical lines (the direction variable is now obsolete).
You should us an array as the x-coordinates. Here, the array is
linesX = [20, 40, 60, 80]
which specifies 4 lines on the x-coordinates x = 20, x = 40, x = 60, and x = 80.
Now, you run a for loop going through each of the x-coordinates, drawing each line individually. Finally the increment is added at the end, topMinY += 2, which to clarify is shorthand for topMinY = topMinY + 2.