I'm trying to practice using matter.js to create top down levels Bomberman style.
Right now I want to get my circle, which is controlled by arrow keys to move and bump into static squares but it is just going through them. Did I set it up incorrectly? I have been coding for three months, so I might be quite slow sorry!
var Engine = Matter.Engine,
World = Matter.World,
Bodies = Matter.Bodies;
var engine = Engine.create();
var world = engine.world;
var player;
var rocks = [];
var cols = 7;
var rows = 7;
function setup() {
createCanvas(750, 750);
Engine.run(engine);
player = new Player(300, 300, 25);
var spacing = width / cols;
for (var j = 0; j < rows; j++) {
for (var i = 0; i < cols; i++) {
var r = new Rocks(i * spacing, j * spacing);
rocks.push(r);
}
}
}
function draw() {
background(51);
Engine.update(engine);
for (var i = 0; i < rocks.length; i++) {
rocks[i].show();
}
player.show();
player.move();
}
function Player(x, y, r) {
this.body = Bodies.circle(x, y, r);
this.r = r;
World.add(world, this.body);
this.show = function () {
ellipse(x, y, this.r * 2);
}
this.move = function () {
if (keyIsDown(RIGHT_ARROW))
x += 10;
if (keyIsDown(LEFT_ARROW))
x -= 10;
if (keyIsDown(UP_ARROW))
y -= 10;
if (keyIsDown(DOWN_ARROW))
y += 10;
x = constrain(x, this.r, height - this.r);
y = constrain(y, this.r, width - this.r);
}
}
function Rocks(x, y, w, h, options) {
var options = {
isStatic: true
}
this.body = Bodies.rectangle(x, y, h, w, options);
this.w = w;
this.h = h;
this.size = player.r * 2;
World.add(world, this.body);
this.show = function () {
rect(x, y, this.size, this.size);
}
}
I think the problem is that you player is not drawn in the same position that the physics engine thinks its at.
in your Player function after the initialization of x and y the rest all need to be this.body.position.x and this.body.position.y. Otherwise you're changing where the picture is drawn at but not where the player actually is.
I'm not entirely sure what all you want me to point out besides that but also I think you want to disable gravity with engine.world.gravity.y = 0 and I was trying to fix the constrain function because as I tested it it wasn't working, I wasn't able to fix it but I'd recommend just making static boundary objects for the walls and just don't draw them.
Also matter.js processes the locations of objects from their centers. When drawing the objects you either have to take that into consideration or switch the mode to ellipseMode(CENTER);, 'rectMode(CENTER);` .. etc.
I hope this helps
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'm learning JS in p5.js and I need some help with this thing: I have an array of objects (just some lines that keep moving in random patterns) and I want to keep them inside the borders of another object, in this case, a circle. I was trying to figure it out with the createGraphics function, but it won't work.
The array of lines is called "linhas" (the object class is in another file named "linhas.js", the second background "fundo2", the circle is "circulo".
The code is this:
let linhas = [];
let fundo2;
function setup() {
createCanvas(400, 400);
pixelDensity(1);
fundo2 = createGraphics (400,400);
for (let i = 0; i < 100; i++){
let x = random(0, 400);
let y = random(0, 400);
let z = random(0, 400);
let w = random(0, 400);
linhas[i] = new Linha(x, y, z, w);
}
}
function draw() {
sol();
tracos();
}
function tracos() {
for (let linha of linhas) {
linha.display();
linha.tremer();
}
}
function sol() {
image(fundo2,0,0);
fundo2.noStroke();
fundo2.circle(200,200,250);
}
In case you guys wanna see better my attempt, the link to the web editor of p5.js is this: https://editor.p5js.org/dolivetro/sketches/NZ2FlsPwx
Thanks in advance!
my p5.js project
You basically have three options 1) do the math to calculate the start and end points for each line based on the intersections between each line and the circle that they are to be drawn within; 2) draw your lines to a separate context, copy the content to an Image, and then use the p5.Image.mask function; or 3) draw your lines to a separate context, and then use either the erase() function or blendMode(REMOVE) to clear the portion of that context that is not inside the circle (this would require an additional mask shape as well).
Since you are already drawing to a separate context, here is an example that uses an image mask (i.e. #2):
let linhas = [];
let fundo2;
let img;
let maskCtx;
function setup() {
createCanvas(400, 400);
pixelDensity(1);
fundo2 = createGraphics(400, 400);
maskCtx = createGraphics(400, 400);
maskCtx.noStroke();
maskCtx.circle(200, 200, 250);
img = createImage(width, height);
for (let i = 0; i < 100; i++) {
let x = random(0, 400);
let y = random(0, 400);
let z = random(0, 400);
let w = random(0, 400);
linhas[i] = new Linha(x, y, z, w);
}
}
function draw() {
circulo();
tracos();
}
function tracos() {
for (let linha of linhas) {
linha.display();
linha.tremer();
}
}
function circulo() {
img.copy(fundo2, 0, 0, width, height, 0, 0, width, height);
img.mask(maskCtx);
image(img, 0, 0, width, height);
fundo2.noStroke();
fundo2.circle(200, 200, 250);
}
class Linha {
constructor(x, y, z, w) {
this.x = x;
this.y = y;
this.z = z;
this.w = w;
}
display() {
fundo2.stroke(255, 0, 0, 70);
fundo2.strokeWeight(2);
fundo2.line(this.x, this.y, this.z, this.w);
}
tremer() {
this.x = this.x + random(-1, 1);
this.y = this.y + random(-1, 1);
this.z = this.z + random(-1, 1);
this.w = this.w + random(-1, 1);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>
I have created an element called Particle. Single animation of this element also works. But as soon as I try to run multiple animations, only one animation gets performed. I think the problem is the requestAnimationFrame (this.animate.bind(this))-call, but I don't know how to change it to accept multiple animations at once. Any ideas on how to fix this?
Code:
//gloabl vars
let particels = [];
let numberParticels = 120;
let canvas;
let ctx;
let title;
let mesaureTitle;
let boundRadius;
let animations;
window.onload = function () {
this.init();
for(let i = 0; i < numberParticels; i++){
particels[i].update();
particels[i].draw();
particels[i].animate(0);
}
}
function init(){
canvas = document.getElementById("c");
ctx = canvas.getContext("2d");
title = document.getElementById("title");
mesaureTitle = title.getBoundingClientRect();
bound = {
x: mesaureTitle.x,
y: mesaureTitle.y,
width: mesaureTitle.width,
height: mesaureTitle.height,
};
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
for(let i = 0; i < numberParticels; i++){
let x = Math.floor(Math.random() * window.innerWidth);
let y = Math.floor(Math.random() * window.innerHeight);
let size = Math.floor(Math.random() * 25) + 3;
let weight = Math.floor(Math.random() * 11) + 2;
particels.push(new Particel(x, y, size, weight));
}
}
class Particel {
constructor (x,y,size, weight) {
this.x = x;
this.y = y;
this.size = size;
this.directionX = 0.15332;
this.resetWeight = weight;
this.weight = weight;
this.lastTime = 0;
this.interval = 1000/60;
this.timer = 0;
}
update(){
this.weight += 0.02;
this.y = this.y + this.weight;
this.x += this.directionX;
//check for collision with textField
if (this.x < bound.x + bound.width
&& this.x + this.size > bound.x &&
this.y < bound.y + bound.height &&
this.y + this.size > bound.y) {
this.y -= 3;
this.weight *= -0.3;
}
}
draw(){
if(this.y > canvas.height){
this.y = 0 - this.size;
this.weight = this.resetWeight;
//create random start point
this.x = Math.floor(Math.random() * canvas.width);
}
ctx.fillStyle = "rgb(0, 180, 97)";
ctx.beginPath();
ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
ctx.closePath();
ctx.fill();
}
animate(timeStamp){
const deltaTime = timeStamp - this.lastTime;
this.lastTime = timeStamp;
if(this.timer > this.interval){
ctx.clearRect(0,0, canvas.width, canvas.height);
this.update();
this.draw();
this.timer = 0;
}else {
this.timer += deltaTime;
}
ctx.fillStyle = "rgba(0, 0, 0, 0.3)";
requestAnimationFrame(this.animate.bind(this));
}
}
The major problem is clearing the canvas inside the animate method of each particle. So if you draw multiple particles, each particle update call clears the canvas, overwriting previous particle data, which only leaves the last particle visible.
You could try removing the
ctx.clearRect(0,0, canvas.width, canvas.height);
line from where it is and create a createAnimationFrame call back in init to clear the canvas before amimating particles:
function init() {
// ....
requestAnimationFrame( ()=> ctx.clearRect(0,0, canvas.width, canvas.height));
// existing for loop:
for(let i = 0; i < numberParticels; i++){
// ... .
}
However this creates (one plus the number of particles) requests for an animation frame. A better solution would be to remove requesting animation frames from the Particel class and create a single requestAnimationFrame callback which goes through the particels array and calls a class method to redraw each particle on the canvas with updated position.
Also the code generates an error in strict mode that bound has not been declared. I suggest declaring it globally rather than relying on sloppy mode JavaScript creating it as a window property for you.
I'm currently developing a small game for my capstone project. In the game, the user tries to avoid rectangles of random sizes the move from the right side of the screen to the left at a set speed.
It's built using object-oriented Javascript, and I've assigned it an anonymous function, however, I can't seem to get it to generate a shape and animate it more than the initial time the function is called. The problem can be solved if I create more than one object, but I would like this function to run automatically and generate more than just the first rectangle.
I've tried to call the function with an interval to force it to re-run the function with no results. I also attempted to separate the initialization function to call it with a parameter to generate the number of shapes given to it.
This is the function that generates the shape with the initial call, and determines the color, size, and location as well as draws it on the canvas.
var randomRectangle = function(){
this.init = function() {
this.speed = 4;
this.x = canvas.width-50;
this.y = Math.floor(Math.random()*280) + 40;
this.w = Math.floor(Math.random()*200) + 50;
this.h = Math.floor(Math.random()*150) + 20;
this.col = "#b5e61d";
}
this.move = function(){
this.x -= this.speed;
}
this.draw = function(num){
draw.rectangles(this.x, this.y, this.w, this.h, this.col);
}
};
This is where the object is initialized and the loop generates all objects and animations on the canvas.
randRecs = new randomRectangle();
randRecs.init();
function loop(){
draw.clear();
player.draw();
player.move();
wall1.draw();
wall2.draw();
randRecs.draw();
randRecs.move();
}
var handle = setInterval(loop, 30);
I expected the rectangle to continuously be generated at a new y-coordinate with a new size, then move from the right side of the screen to the left. However, only one rectangle is created and animated.
var list = [];
var canvas = document.querySelector('canvas');
var ctx = canvas.getContext('2d');
var randomRectangle = function() {
this.init = function() {
this.speed = 4;
this.x = canvas.width - 50;
this.y = Math.floor(Math.random() * 280) + 40;
this.w = Math.floor(Math.random() * 200) + 50;
this.h = Math.floor(Math.random() * 150) + 20;
this.col = "#b5e61d";
}
this.move = function() {
this.x -= this.speed;
// restart x position to reuse rectangles
// you can change the y value here to a new random value
// or you can just remove with array.splice
if (this.x < -50) this.x = canvas.width - 50;
}
this.draw = function(num) {
draw.rectangles(this.x, this.y, this.w, this.h, this.col);
}
};
function loop() {
draw.clear();
//player.draw();
//player.move();
//wall1.draw();
//wall2.draw();
// call the methods draw and move for each rectangle on the list
for (var i=0; i<list.length; i++) {
rec = list[i];
rec.draw();
rec.move();
}
}
// spawn any number of new rects in a specific interval
var rectsPerSpawn = 1;
function addRects() {
for (var i=0; i<rectsPerSpawn; i++) {
if (list.length < 100) {
var rec = new randomRectangle();
list.push(rec);
rec.init();
}
}
}
// every half second will spawn a new rect
var spawn = setInterval(addRects, 500);
var draw = {
clear: function () {
ctx.clearRect(0,0,canvas.width,canvas.height);
},
rectangles: function (x, y, w, h, col) {
ctx.fillStyle = col;
ctx.fillRect(x,y,w,h);
}
}
var handle = setInterval(loop, 30);
<canvas></canvas>
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>