I'm having trouble with my javascript code. I'm trying to create a moving set of circles where each circle has their own attributes. So far I've managed to input all the needed values into an array, but I can't figure out how to use them properly for drawing on canvas.
Here's the javascript:
var radius = 10;
var step = x = y = 0;
var r = g = b = 255;
var circleHolder = [];
var loop = setInterval(function(){update();}, 30);
function Circle(x, y, radius, r, g, b)
{
this.x = x;
this.y = y;
this.radius = radius;
this.r = r;
this.g = g;
this.b = b;
circleHolder.push(this);
}
Circle.prototype.draw = function()
{
Circle.prototype.ctx = document.getElementById("MyCanvas").getContext("2d");
Circle.prototype.ctx.clearRect(0,0,720,720); // clear canvas
Circle.prototype.ctx.beginPath();
Circle.prototype.ctx.strokeStyle = "rgb("+ this.r +", "+ this.g +", "+ this.b +")";
Circle.prototype.ctx.arc(this.x, this.y, this.radius, 0, 2 * Math.PI);
Circle.prototype.ctx.stroke();
}
Circle.prototype.update = function ()
{
step += .02;
step %= 2 * Math.PI;
this.x = parseInt((Math.sin(step)) * 150) + 360;
this.y = parseInt((Math.cos(step)) * 150) + 360;
this.radius += 16;
if (this.radius > 200)
{
for (i in circleHolder)
{
if (circleHolder[i]==this)
{
circleHolder.splice(i, 1);
}
}
}
}
function update()
{
var ci = new Circle(x, y, radius, r, g, b);
for (i in circleHolder)
{
ci = circleHolder[i];
ci.update();
ci.draw();
}
}
I'm pretty sure my problem lies within update() {} but I can't figure out how to do it properly.
EDIT: Okay, I've got it working with some changes! Check this Fiddle! I'm getting "ci not defined" error in the console though, and it has a strange bug: Changing the "if (this.radius > 128)" to higher integer it will make the circles spin faster, I don't know why. If you want you can try to change it to 256 and see what happens.
for (var i=0; i < allCircles; i++)
{
ci = circleHolder[i]; <----- This is causing the error
ci.update();
ci.draw();
}
it's not 100% clear to me what you're trying to do, but I tried to fix the main problem
One problem is your for loop.. you shouldn't use for in for arrays, do this instead:
for (var i=0 ; i<circleHolder.length ; i++)
{
ci = circleHolder[i];
ci.update();
ci.draw();
}
see this fiddle
Also I moved your get context and other things that should happen only once into the constructor, instead of having it in the update function.
You're also clearing the canvas before each draw, so the it will only show the last drawn circle per frame. (if you remove the clearRect it looks like one of those old spirographs).
You were also drawing the circles with (255,255,255)(white) so it wasn't showing until the color was changed.
Edit:
Really there are a few problems with this code:
The context shouldn't be inside a circle class if you plan on having many of them.
You should have some object which contains the canvas/context and an array of all circles.
Then have that object manage the updating/drawing.
For starters, unless there's something else going on, outside of this code:
You are using for ... in ... on an array, for-in is for objects, when used on arrays, most browsers will include methods like .splice and .forEach, and not just the numeric 0...n index.
function splice () {}.draw(); doesn't end well.
Also, what is the colour of your page's background? You're setting the rgb colour of each circle to 100% white. You're also clearing the canvas... ...which might well mean that the whole thing is transparent. So if you've got a transparent canvas, white circles and a white background, chances are great you're not going to be seeing anything at all, if this is even working without spitting out an error.
It might make a lot more sense to move your logic around in a way that lets you follow what's going on.
If you make a circle constructor, don't have it do anything but make a new circle.
Inside of your update, create a circle.
THEN put it inside of your circle collection (not in the circle constructor).
In a large application, you will typically call update on ALL objects, and then call draw on ALL objects, rather than updating and drawing one at a time.
Imagine a game that didn't bother to check if you had been hit by a bullet before drawing you and letting you move, for instance.
So inside of your loop, you should have an update and a draw.
Inside of the update, create your circles add them to the list and update the positions of them.
Inside of the draw, draw the circles.
In the future, this will give you the benefit of having things like collision-detection, without having to redraw everything, multiple times per frame.
Also, don't do DOM-access inside of a function that's going to be called many, many times (Circle.draw).
That will demolish your framerate in the future.
Instead, pass the function a dependency (the canvas).
// inside of the main game's scope
var screen = document.getElementById(...).getContext("2d");
// inside of your new draw function, in the animation/game loop
var i = 0, allCircles = circleHolder.length, currentCircle;
for (; i < allCircles; i += 1) {
currentCircle = circleHolder[i];
currentCircle.draw(screen);
}
Related
i’m working on a complex visualisation project, using p5.js i want to create multiple clusters of shapes to visualise all sorts of data, so i’m looping through the data, checking the objects specified by user and drawing them to canvas, this happens in a nested for loop…the problem i’m facing is when an inner loop is done drawing a cluster of shapes and the overall loop continues and re-enters the inner loop again to draw the next cluster, the older cluster of drawn shapes disappears…i don’t understand why, i thought this had to do with that the shapes are related to the inner loop context/scope and tried to recreate the problem based on that but it appears that this is not the case.
this code for example works perfectly, it draws 5 circles on 20 lines, run by a nested loop
function setup() {
createCanvas(window.innerWidth, window.innerHeight).parent('myContainer');
}
function draw() {
background(220);
x = 100;
y = 100;
for (let i = 1; i <= 20; i++) {
for(let j=1;j<=5;j++){
fill(color(0, 255, 0));
stroke(0);
ellipse(x, y, 80);
x += 90;
}
x = 100;
y += 90;
}
}
while in my code, after the inner loop moves to the next cluster the previous shapes just disappear and only the last cluster is shown…i did a lot of console logging and debugging and made sure that every cluster is actually getting drawn but it just disappears when a new one is created…can anyone please help?
is there anyway to emphasise the library to draw a NEW separate shape…like this?
new ellipse(x,y,rad)
this is my project’s code, it shows where the bug happens
function setup() {
createCanvas(viewWidth, viewHeight).parent('myContainer');
}
function draw() {
//code...
var objects_len = objects.length;
for (var cluster_index = 1; cluster_index < cluster_len; cluster_index++) {
array_center = partition(cluster_index - 1, cluster_len - 1);
for (var object_index = 0; object_index < objects_len; object_index++) {
if (!objects[object_index].deleted) {
draw_simple_object(objects[object_index], data, cluster_index, array_center, cluster_len);
}
}
addClusterName(cluster_index, data, array_center);
}
function draw_simple_object(object, data, cluster_index, array_center, cluster_len) {
.
.
.
//this is where the drawing happens
var center_shape = [array_center[0] + object.centerOffsetX * array_center[2], array_center[1] + object.centerOffsetY * array_center[3]];
if (object.shape === 'ellipse' && enable) {
var size_min = Math.min(array_center[2], array_center[3]) * 2;
fill(colorFill);
stroke(object.colorBorder);
ellipse(center_shape[0], center_shape[1], size_min * width * proportion, size_min * length * proportion);
}
.
.
.
}
}
please help me
Can it be because you are calling the background() function inside the loop? (that part is actually not shown in your snippet)
Solved.
i was using resizeCanvas() in one of the function inside draw() which cleared all content off the canvas before resizing. removing resizeCanvas solves the problem.
I have a page of code in which i want 3 "ghost.png" to be displayed onto a canvas with random coordinates using math.random at a press of a button. Beyond that im also trying to use another function to draw "sac.jpg" onto the canvas aswell as drawing the same ghosts on the same positions as they have been determined by math.random. then in the same function, i want to be able to animate the image "sac.png" using the arrow keys on the keyboard without making the ghost image move along with it. With this goal, i tried to create a code but it does not seem to work. can someone explain?
recap:
function hasard (activates when button is pressed):
a) math.random 6 times to choose x,y coordinates of 3 of the same image
b) place the image in a variable
function dessiner (activates when arrow key is pressed):
a) draw background
b) draw the image from function hasard in the same spot each time until i press the button from the first function to randomize a new pair of coordinates.
c) draw the image "sac.gif" and program it to be able to move using arrow keys.
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title></title>
<link rel="stylesheet" href="Jeu.css">
</head>
<body>
<canvas id="canvas" width="400" height="400"
style="border-color: 'black'; border-width: 3px; border-style: solid">
</canvas>
<br>
<button type="button"onclick="hasard()"><img src="start.jpg"></button>
<script type="text/javascript">
var canvas = document.getElementById("canvas");
var contexte = canvas.getContext("2d");
var x;
var y;
var x1;
var y1;
var x2;
var y2;
var ghost = new Image();
ghost.src = "ghost.png";
function hasard(){
x = 32 +(Math.random() * (400-64));
y = 32 +(Math.random() * (400-64));
x1 = 32 +(Math.random() * (400-64));
y1 = 32 +(Math.random() * (400-64));
x2 = 32 +(Math.random() * (400-64));
y2 = 32 +(Math.random() * (400-64));
contexte.drawImage(ghost,x,y,60,60)
contexte.drawImage(ghost,x1,y1,60,60)
contexte.drawImage(ghost,x2,y2,60,60)
};
var bg = new Image();
bg.src = 'haunted.jpg';
var sac = new Image();
sac.src = "sac.gif";
var a=100;
var b=100;
var dx=0;
var dy=0;
T_fleche_gauche = 37;
T_fleche_droite = 39;
T_fleche_haut = 38;
T_fleche_bas = 40;
document.onkeydown = miseAJourTouche;
function miseAJourTouche(e){
toucheCourante = e.keyCode;
if (toucheCourante == T_fleche_gauche){
dx= -1;
dy=0;
draw(dx,dy);
}
if (toucheCourante == T_fleche_droite){
dx= 1;
dy=0;
draw(dx,dy);
}
if (toucheCourante == T_fleche_haut){
dy= -1;
dx=0;
draw(dx,dy);
}
if (toucheCourante == T_fleche_bas){
dy= 1;
dx=0;
draw(dx,dy);
}
}
function dessiner(x,y){
bg.addEventListener('load', function() {
contexte.drawImage(bg, 100, 20, 200, 200);
image.onload = function(){
contexte.drawImage(ghost,x,y,60,60)
contexte.drawImage(ghost,x1,y1,60,60)
contexte.drawImage(ghost,x2,y2,60,60)
image.onload = function(){
context.drawImage(sac, 0, 0, 70,90);
context.translate(10+a,10+b);
a = a+dx;
b = b+dy;
if (a > 400) a = -80; if (a <-80) a = 400;
if (b > 400) b = -40; if (b <-40) b = 400;
}
window.requestAnimFrame(function() { dessiner(dx,dy) });
}
dessiner();
};
</script>
</body>
</html>
From what I can tell, you are trying to animate ghosts and bag, with the bag being the player controllable item.
While I can't debug the program for you, I can help telling you to use the dev tools of your browser. F12 or Fn + F12 will bring you to the Inspector, where one of the other tabs is console.
Now, in your program, I see you assign image.onload to two functions, with one of them missing a closing curly brace.
image.onload = function(){
contexte.drawImage(ghost,x,y,60,60)
contexte.drawImage(ghost,x1,y1,60,60)
contexte.drawImage(ghost,x2,y2,60,60)
image.onload = function(){
context.drawImage(sac, 0, 0, 70,90);
context.translate(10+a,10+b);
a = a+dx;
b = b+dy;
if (a > 400) a = -80; if (a <-80) a = 400;
if (b > 400) b = -40; if (b <-40) b = 400;
}
I would add a curly brace after the three calls to contexte.drawImage, but I question the choice for two assignments. Also, I don't think image is declared in this scope, but I might be wrong. That's all I see.
If I understand well, you want to draw 3 ghosts with random positions, and a bag also at a random position, and would like to be able to move it with the arrow keys, is that right?
Then, I may suggest several things:
Don't put the update and drawing codes together. You have a drawing function dessiner(x, y) but you also draw in the hasard() and miseAJourTouche(e) functions. This makes things confusing and doesn't help debugging.
Factorize more: for example, in your hasard() function, you makes the calculations 32 +(Math.random() * (400-64)); 6 times. Instead, you could use a function to generate a random number between two values, then initialize your values in an init() function. You can initialize all the variables inside as well as load your images. Depending on what you aim to make, you could also use objects, like a Ghost object that would initialize its coordinates, with its own draw() method, etc. It could be much simpler for the rest.
Be consistent: if you put spaces before and after a +, then do it all the time. Don't use multiple languages inside the same code, you used both English and French (and English makes things easier when asking for help). Also, you can use a tool such as your IDE beautifier function or an online one (I used https://beautifier.io/) to make your code more readable or spot typing issues, like missing curly braces.
I took the liberty of refactoring your code a bit: https://jsfiddle.net/nmerinian/t0c3sh8f/4/
Notes:
I didn't write any condition to prevent the refresh if no key is
pressed, but it's easy to add.
I commented the background drawing part since I don't have the image.
I also commented some variables I didn't use.
I hope this helps.
I am coding a game that is currently in its very early stages for a project to try to learn more about coding. In my game, objects generate randomly (green squares), and the player (red square), avoids them. I am having trouble trying to get the green squares to generate from a random position on the x-axis. I already have a formula to generate a random number for X, but after it selects a number randomly, all the "projectiles" generate there, rather than all generating from a different area. How would I get all the "projectiles" to generate from different positions on the x-axis randomly?
var randomX = Math.floor(Math.random() * 480) + 15;
function updateGameArea() {
var x, y;
for (i = 0; i < projectiles.length; i += 1) {
if (player.crashWith(projectiles[i])) {
gameArea.stop();
return;
}
}
gameArea.clear();
gameArea.frameNo += 1;
if (gameArea.frameNo == 1 || everyinterval(150)) {
x = randomX;
y = gameArea.canvas.height;
projectiles.push(new component(40, 40, "green", x, y));
}
for (i = 0; i < projectiles.length; i += 1) {
projectiles[i].y += -1; // the shape is using its coordinates to build downward from its y position
projectiles[i].update();
}
player.newPos();
player.update();
}
function everyinterval(n) {
if ((gameArea.frameNo / n) % 1 == 0) {return true;}
return false;
Expected: Green squares generate in random positions on the x- axis every 3 seconds and move upwards
Actual: Green squares all generate from the same random position on the X-axis.
You should reset X every time you're adding a new projectile:
if (gameArea.frameNo == 1 || everyinterval(150)) {
randomX = Math.floor(Math.random() * 480) + 15;
x = randomX;
y = gameArea.canvas.height;
projectiles.push(new component(40, 40, "green", x, y));
}
Otherwise, the randomX value stays constant as the value originally evaluated on line 1 when the interpreter reached it.
Here's your problem:
var randomX = Math.floor(Math.random() * 480) + 15;
// Generates a random number and stores it to randomX
// Called using 'randomX'
You need to turn it into a function if you want it to run each time:
var randomX = function() { Math.floor(Math.random() * 480) + 15 };
// Returns a new number each time
// Called using 'randomX()'
Both shivashriganesh mahato and natelouisdev have, essentially responded to how to fix the issue but since you are learning coding here is a tip. When you code, the code will run in a particular order. If you want something to be reassigned repeatedly, in this case a randomized number being used, and you want it to occur only after an event, you need to make sure that it gets trigger within each event.
natelouisdev has a good approach because, by using it as a function, you can call your randomizer more cleanly in your code and make it reassign the value of x each time.
Since you are building a game, it is also a good idea to compartmentalize your code. It'll make it easier to keep your ideas in order for each event trigger.
Example:
function gameLoss(){} - Define event return upon game loss. You can
then create editable rules to reason for loss without having to edit
the loss
function gameActive(){} - Defines what is normal gameplay. everything that occurs during normal gameplay should be managed here.
function gameArea(){} - Defines game canvas that function more for UI than for gameplay (scores, lifes, size of screen, etc)
Had you created individual functions you'd realize you only need a randomized 'x' value upon regular play thus you'd assign it within the gameActive() function and not as a global variable. Then you'd call the gameActive() function as many times as needed within a time interval to ensure a unique value is created each time.
-Side note: Don't litter unnecessary global variables. It'll make a mess off of your code when debugging. -
This is a link to my github. https://github.com/inventive-dev217/arcade-game . I need for the items in my allEnemies array to move at random speeds. Right now they are all moving at the same speed. Any ideas how to implement this will be greatly appreciated.
Hi I'm not an expert in JS, but something like this should work :
var Enemy = function(x, y) {
// Variables applied to each of our instances go here,
// we've provided one for you to get started
// The image/sprite for our enemies, this uses
// a helper we've provided to easily load images
this.sprite = 'images/enemy-bug.png';
this.x = x;
this.y = y;
this.speed = Math.floor(Math.random() * MaxSpeedValue) + MinSpeedValue;
}
As part of a project, I have to render a video on a JS-player from a text file which has the points - all the changed coordinates along-with the color in each frame. Below is the code I'm using to draw these point on the screen.
But the issue is that the number of changed pixels in each frame are as high as ~20,000 and I need to display these in less than 30ms (inter-frame time difference). So, when I run this code the browser hangs for almost each frame. Could someone suggest an improvement for this?
Any help is really appreciated.
c.drawImage(img,0,0,800,800);
setInterval(
function(){
while(tArr[index]==time) {
var my_imageData = c.getImageData(0,0,width, height);
color(my_imageData,Math.round(yArr[index]),Math.round(xArr[index]),Math.round(iArr[index]),255);
c.putImageData(my_imageData,0,0);
index=index+1;
}
time = tArr[index];
}
,30);
xArr, yArr, iArr, tArr are arrays of x-coordinate, y-coordinate, intensity value and time of appearance respectively for the corresponding point to be rendered
function color(imageData,x,y,i,a){ //wrapper function to color the point
var index = (x + y * imageData.width) * 4;
imageData.data[index+0] = i;
imageData.data[index+1] = i;
imageData.data[index+2] = i;
imageData.data[index+3] = a;
}