JavaScript Canvas -- Is it possible to draw depending upon Math.random? - javascript

I am creating a very basic sprite for a game (it is drawn in Canvas/Context/LineTo). I want its expression to change randomly based on two different draw methods. This is my attempt at this:
drawFace = function () {
if (Math.random() < 0.05) {
Player.drawhappyface(context);
}
else if (Math.random() < 0.1) {
Player.drawsadface(context);
}
}
drawFace();
I can confirm that the drawhappyface and drawsadface draw methods work independent of this function (respectively drawing a smile and a frown). But using this function and its logic, they're simply never drawn (the player entirely lacks a face). So, have I written this wrong? I'm inspired by the following simulation which has constantly animating expressions also using Math.random: http://www.blobsallad.se/
If I instead write the function like this, then absolutely nothing is drawn on the canvas (all other sprites, etc. also not drawn):
drawFace = function (context) {
if (Math.random() < 0.05) {
Player.drawhappyface(context);
}
else if (Math.random() < 0.1) {
Player.drawsadface(context);
}
}
drawFace();

The problem might be that you call Math.random() in both if statements and then they will have different values. Call Math.random() only once and save the value for every time you need/call drawFace Try like so:
drawFace = function () {
var randomNumber = Math.random();
console.log(randomNumber);
if (randomNumber < 0.05) {
console.log('randomNumber < 0.05');
}
else if (randomNumber < 0.1) {
console.log('randomNumber < 0.1');
}
}
drawFace();
if you just want to draw happy or sad face you can do even simpler if else
drawFace = function () {
var randomNumber = Math.random();
console.log(randomNumber);
if (randomNumber < 0.5) {
console.log('draw happy face');
} else {
console.log('draw sad face');
}
}
drawFace();

Related

Function using beginShape() slows down frame rate at higher resolutions

As stated in the title, rendering slows down significantly at higher resolutions. I'm wondering if this is caused by beginShape() as well as why and how to get around it. Other functions that do not use beginShape() do not affect the frame rate negatively. Link to p5 editor here: https://editor.p5js.org/anton.ermkv/sketches/mSkLrkPJ9
Code below:
function w(v) {if (v == null) return width;return width * v;}
function h(v) {if (v == null) return height;return height * v;}
let zoff = 0;
let irregCircs = []
let numCircs;
function setup() {
createCanvas(windowWidth, windowHeight);
exType = chooseExpandType()
pixelDensity(1)
setIrregCircles(exType)
}
function draw() {
translate(width/2,height/2)
background(255,50);
drawIrregCircles()
console.log(frameRate())
}
function chooseExpandType() {
expandType = 'ellipses'
return expandType
}
function irregExpand(radius,noiseVal) {
beginShape();
for (let a = 0; a <= TWO_PI; a += radians(6)) {
let xoff = map(cos(a), -1, 1, noiseVal/3, noiseVal);
let yoff = map(sin(a), -1, 1, noiseVal/3, noiseVal);
let diff = map(noise(xoff, yoff, zoff), 0, 1, .65, 1.35);
let x = radius * diff * cos(a);
let y = radius * diff * sin(a);
vertex(x,y)
}
endShape(CLOSE);
zoff += 0.0001;
}
function setIrregCircles(expandType) {
numCircs = 4;
for (let i = 0; i < numCircs; i++) {
radius = map(i,0,numCircs,w(.03),w(.65))
noiseVal = random(1,3)
circ = new IrregCircle(radius,noiseVal,expandType,numCircs);
irregCircs.push(circ);
}
}
function drawIrregCircles() {
for (let i = 0; i < irregCircs.length; i++){
irregCircs[i].run();
}
}
class IrregCircle{
constructor(_radius,_noiseVal,_expandType,_numC) {
this.radius = _radius;
this.noiseVal = _noiseVal;
this.expandType = _expandType;
this.numC = _numC;
}
run() {
this.update()
this.checkEdges()
this.show()
}
update() {
this.radius += w(.0015)
}
checkEdges() {
if (this.radius > w(.73)) {
this.radius = w(.01)
}
}
show() {
noFill()
if (this.expandType === 'ellipses'){
push()
rotate(frameCount / 60)
stroke(35,20)
strokeWeight(w(.002))
irregExpand(this.radius,this.noiseVal)
irregExpand(this.radius*1.15,this.noiseVal*1.35)
irregExpand(this.radius*1.3,this.noiseVal*1.7)
irregExpand(this.radius*1.45,this.noiseVal*2)
irregExpand(this.radius*1.6,this.noiseVal*2.3)
irregExpand(this.radius*1.75,this.noiseVal*2.8)
pop()
}
}
}
Thanks in advance to anyone having a look.
the fact that you are printing the frameRate at each frame slows down your program significantly. You can replace:
console.log(frameRate())
by:
if(frameCount % 60 == 0)
console.log(frameRate())
to only print it every 60 frame.
I don't know if it solves your problem but on my side, it seems that it get rid of most of the freezing problem.
As you draw a lot of similar shapes, you should also try to compute an array of the points you need at the beginning and then reuse it at each frame and for each similar shape by scaling it by the right factor (Your code ran a lot faster when I removed the noise to draw circles only so I think what slows your code is the computation inside the BeginShape() block and not the BeginShape() itself).

How to make an image appear cyclically on each click with p5js

I am new to the amazing world of creative coding and p5js. I want to create a website for my design studio with a p5js effect that I can't find the solution for. I searched everywhere and I can't find anyone with the same problem as me, that's why I'm posting my very first message here. Here's the idea: in a canvas, I would like that with each mouse click, a different image can appear. Currently, my code allows to display images randomly but I would like to be able to set a cyclic order of appearance : work0.png, work1.png, work2.png... and it starts again.
If someone has seen this problem somewhere or could explain it to me I would be very grateful. Thanks !
let works = []
function preload() {
for (let i = 0; i < 5; i++) {
works[i] = loadImage("work" + i + ".png")
}
}
function setup() {
canvas = createCanvas(windowWidth, windowHeight);
canvas.position(0, 0);
canvas.style('z-index', '1');
}
function windowResized() {
resizeCanvas(windowWidth, windowHeight);
canvas.position(0, 0);
canvas.style('z-index', '1');
}
function draw() {
cursor(CROSS);
}
function mouseClicked() {
imageMode(CENTER);
let r = floor(random(0, 6));
image(works[r], mouseX, mouseY, 500, 600);
}
instead of using random, you can use a counter
let works = []
function preload() {
for (let i = 0; i < 5; i++) {
works[i] = loadImage("work" + i + ".png")
}
}
function setup() {
canvas = createCanvas(windowWidth, windowHeight);
canvas.position(0, 0);
canvas.style('z-index', '1');
}
function windowResized() {
resizeCanvas(windowWidth, windowHeight);
canvas.position(0, 0);
canvas.style('z-index', '1');
}
function draw() {
cursor(CROSS);
}
var counter = 0
function mouseClicked() {
imageMode(CENTER);
counter++ //add one to the counter
image(works[counter%5], mouseX, mouseY, 500, 600); // the % symbols is modulo, which is a fancy word for remainder when divided by that number, so it would cycle from 0 to 4
}
So the counter starts at zero, and every time you click we increase the counter by one. to stop it from increasing constantly, we can use the modulo operator %(in programming this isn't the percent sign). examples: 2%5 is 2, 16%5 is 1, 25%5 is 0.
this essentially makes the value cycle from 0 to 4. the counter is constantly increasing, but the remainder when divided by five will always cycle.
also btw p5.js is a javascript library, and you are loading in code made by other people, which means without the p5.js library the code doesn't run in this code snippet on StackOverflow

Is there a way I can make leaves fall more organically?

So, at the moment I'm doing that, but the way my leaves are falling is weird, they're sticking to the same pattern as if they were still all stuck together, I know I could make them a class and give them random magnitude so each one of them would follow it's own path with its own velocity, but I am new to code and don't quite know how to do it, would there be a simpler way??? Thank you in advance (also it'd be nice to know if there is a way to add some time between the sprouting and the falling of the leaves).
var leaves = [];
var count = 0;
function setup() {
createCanvas(windowWidth, windowHeight);
var a = createVector (width / 2, height);
var b = createVector (width / 2, height*3/4);
tree[0] = root;
}
function mousePressed() {
count++;
if (count % 3 === 0) {
for (var i = 0; i < tree.length; i++) {
if (!tree[i].finished) {
var leaf = tree[i].end.copy();
leaves.push(leaf);
}
}
function draw() {
background(51);
for (var i = 0; i < leaves.length; i++) {
fill(255, 204, 100);
noStroke();
ellipse(leaves[i].x, leaves[i].y, 2.7, 5);
leaves[i].x += random (0, 1.7);
leaves[i].y += random (0, 1.5);
}
}

Target element of array for collision detection

UPDATE: I wanted to let you know that I'd solved this problem, if not necessarily answered my own question. Rather than trying to target the element of the push array, I did this:
In
var ground=[], water=[], enemies=[], environment=[];
I added
tokens=[];
to the end.
Then I wrote a new function, updateTokens:
function updateTokens() {
for (var i = 0; i < tokens.length; i++) {
tokens[i].update();
tokens[i].draw();
if (player.minDist(tokens[i]) <= player.width - platformWidth / 2) {
gameOver();
}
}
if (tokens[0] && tokens[0].x < - platformWidth) {
tokens.splice(0, 1);
}
}
(The 'gameOver();' function is just for testing purposes).
Then I added 'spawnTokensSprites();' to the spawnSprites(); function.
Next, I wrote a new function to spawnTokensSprites:
function spawnTokensSprites() {
if (score > 0 && rand(0, 20) === 0 && platformHeight < 3 {
if (Math.random() > 0.5) {
tokens.push(new Sprite(
canvas.width + platformWidth % player.speed,
platformBase - platformHeight * platformSpacer - platformWidth,
'tokens'
));
}
}
}
Then I added 'updateTokens();' to the animate function, and finally, I added 'tokens=[]' to the 'startGame()' function.
So there it is. Not the answer to the original question, but a working solution to the problem I had. I hope this can help someone else, which is why I updated my post.
---------------------------------------------------------
I'm well out of my depth here. I've been following this tutorial to make an HTML5 canvas game. It's great, and I've learned loads, but I'm totally stuck.
I have a vector for sprites
function Sprite(x, y, type) {
this.x = x;
this.y = y;
this.width = platformWidth;
this.height = platformWidth;
this.type = type;
Vector.call(this, x, y, 0, 0);
this.update = function () {
this.dx = -player.speed;
this.advance();
};
this.draw = function () {
ctx.save();
ctx.translate(0.5, 0.5);
ctx.drawImage(assetLoader.imgs[this.type], this.x, this.y);
ctx.restore();
};
}
Sprite.prototype = Object.create(Vector.prototype);
from which all sprites inherit. To generate 'token' sprites, I use this function:
function spawnEnvironmentSprites() {
if (score > 0 && rand(0, 20) === 0 && platformHeight < 3) {
if (Math.random() > 0.5) {
environment.push(new Sprite(
canvas.width + platformWidth % player.speed,
platformBase - platformHeight * 2 * platformSpacer - platformWidth,
'tokens'
));
}
else if (platformLength > 2) {
environment.push(new Sprite(
canvas.width + platformWidth % player.speed,
platformBase - platformHeight * 1.5 * platformSpacer - platformWidth,
'tokens'
));
}
}
}
Which draws the sprites to the screen. What I cannot do is single these type of sprites out from the array for collision detection. I've tried to use an 'if' statement from the 'updateEnemies' function:
if (player.minDist(enemies[i]) <= player.width - platformWidth/2) {
gameOver();
}
}
within the 'updateEnvironment' function, where I substitute 'enemies' for 'environment', but because I don't know how to specify 'tokens' from 'environment', using this line results in no environment sprites being drawn to the canvas, including platforms. Earlier today, I tried including 'tokens' in 'enemySprites', which worked in so much as when the player collided, the gameOver() function ran, but I couldn't then figure out how to give 'tokens' different behaviour (ie. Score increase rather than gameOver). I should mention that the sprites are called this way because of the assetLoader function I've used.
So long story short, how can I collide with 'tokens' and have everything else behave as it should?
Thanks in advance.
EDIT: Sorry, I forgot to mention that the array for environment is declared in a variable that looks like this:
var ground = [], water = [], enemies = [], environment = [];
Thanks.
UPDATE: Ok, I still haven't sorted this out but I've made a change that I think will make things a bit easier for you to help me with(!)
I've removed 'tokens' from the 'spawnEnvironmentSprites()' function and added a condition to 'spawnEnemySprites' that pushes 'tokens'. This means they now have the same behaviour as my other 'enemies' i.e.. gameOver() is run upon collision detection. My question now is, how do I target 'tokens' in this line of code so that I can give that collision different behaviour? I'd like the 'token' sprite to disappear and the score to increase, but I don't know how to target just the 'tokens' part of the array. The line that deals with the collisions is:
if (player.minDist(enemies[i]) <= player.width - platformWidth/2) {
gameOver();
}
}
Can you help? Thanks in advance.

problems with translating a p5js sketch into processing

I translate a p5js scetch to processing because I want to generate a mov file. The scetch is based on a tutorial from daniel shiffman on kadenze but I´ve got a problem now with a boolean output. How can I code this piece of p5js code in processing?
function draw() {
for( var i = 0; i < particles.length; i++) {
if(particles[i].isDead()) {
//code
}
}
}
function Particles() {
this.isDead = function() {
var distance = p5.Vector.sub(attractor.pos, this.pos);
var d = distance.mag();
if(d < 5) {
return true;
} else {
return false;
}
}
}
First I tried it with void, but void hasn´t got an output. Then I tried something like this with boolean but it also doesn´t work.
void setup() {
//code
}
void draw () {
for (int i = 0; i < particles.length; i++) {
if(particles[i].isDead()) {
//code
}
}
}
Class Particle {
Particle() {
//code
}
boolean isDead() {
PVector distance = PVector.sub(a.location, location);
float d = distance.mag();
if(d < 5) {
return true;
} else {
return false;
}
}
}
It will be great if somebody can help me.
regards mattias
The first problem is that you aren't ever actually doing anything if isDead() returns true. You need to actually put code inside that if statement, or maybe move that if statement to somewhere that makes logical sense.
For example, you could modify your display() function inside your Mover class to draw in green when the Mover is dead:
void display() {
if (isDead()) {
fill(0, 255, 0);
} else {
fill(255, 0, 0);
}
stroke(0);
strokeWeight(2);
ellipse(location.x, location.y, 10, 10);
}
That's just an example, and what you actually do depends on what you want to happen when a Mover is dead.
But even if you make that change, you'll notice that a Mover only dies when it reaches the middle of the gray circle. That's because of this if statement inside your isDead() function:
PVector distance = PVector.sub(a.location, location);
float d = distance.mag();
if (d < 5) {
return true;
} else {
return false;
}
You're measuring the distance between the center of the gray circle and the center of each little red circle. But you're only returning true if the d < 5. The problem with that is the gray circle has a diameter of 50, so if you want the little red circles to die when they enter the gray circle, you have to factor that diameter into your calculations. Try using if(d < 30) instead. I got 30 by dividing the diameter 50 by 2 and then adding 5 for the small circle size. You might have to play with it to get the effect you're looking for.
By the way, you might be interested in the dist() function, which returns the distance between two points. More info can be found in the reference.

Categories