I'm sure this is a simple question but I'm asking it anyways. Basically I'm trying to do the following in javascript (no jquery) with less code:
var Ground = [];
function gameLoop()
{
Gravity += 0.2
if(Ground[0].isCollided(Player))
{
Player.dy = 0;
Gravity = 0;
}
if(Ground[1].isCollided(Player))
{
Player.dy = 0;
Gravity = 0;
}
if(Ground[2].isCollided(Player))
{
Player.dy = 0;
Gravity = 0;
}
Player.dy = Gravity;
}
I've simplified the code extremely from the code in my game I'm making using javascript and the html5 canvas.
What you see is my Ground mechanic as it stands. When the player collides with the Ground Block, the Player's .dy value will no longer change along with the Gravity.
What you saw above is how I would normally do it however this can take up a large amount of space if I have 50 FallingM variables. Is it possible to do something like:
if(Ground[i].isCollided(Player))
{
Player.dy = 0;
Gravity = 0;
}
So that the function will still run no matter how many Ground variables I add? Thanks ahead of time!
You should use a loop:
for(var x of Ground) {
if(x.isCollided(Player)) {
Player.dy = 0;
Gravity = 0;
break;
}
}
Remember, a Jedi's strength flows from the loops. But beware. Map,
reduce, some. The dark side are they. Once you start down the dark
path, forever will it dominate your destiny.
if (grounds.some(ground => ground.isCollided(player))) {
player.dy = 0;
gravity = 0;
}
Related
I recently got into p5 from watching The Coding Train, and I wish to generate two-dimensional surfaces in 3D (like using WEBGL) given by a function of two variables.
If there is a function in Processing that does the job and I’m not just seeing it, then this is a Processing question. Otherwise, this could be a general question on graphing algorithms. I have no background in CS or algorithms.
My first goal is to render a surface z = x^2 + y^2 only for z <= 50.
Here is my initial attempt:
var angleSliderX;
var contour = [];
var cols = 10;
var rows = 10
var d = 10;
function f(x, y) {
return x**2 + y**2;
}
for (i=0; i<cols; i++) {
contour.push([]);
for (j=0; j<rows; j++) {
contour[i].push(f(i, j));
}
}
function setup() {
createCanvas(400, 400, WEBGL);
angleSliderX = createSlider(-PI/2, PI/2, PI/4, 0.01);
}
function draw() {
background(220);
rotateX(angleSliderX.value());
noFill();
for (j=0; j<rows-1; j++) {
for (i=0; i<cols-1; i++) {
if (contour[i][j] < 50) {
beginShape()
vertex(i*d, j*d, contour[i][j]);
vertex((i+1)*d, j*d, contour[i+1][j]);
vertex((i+1)*d, (j+1)*d, contour[i+1][j+1]);
vertex(i*d, (j+1)*d, contour[i][j+1]);
endShape(CLOSE);
}
}
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.0.0/p5.min.js"></script>
I’m concerned that this creates jagged boundaries which I’m not so happy with. Are there known best practices to deal with such concerns without consuming too much computing power? Ideally, I wish to have a flat cut-out at z = 50 of the surface.
Thank you for your time!
My simple JavaScript game is a Space Invaders clone.
I am using the p5.js client-side library.
I have tried many things to attempt at speeding up the game.
It start off fast, and then over time it get slower, and slower, it isn't as enjoyable.
I do not mean to show every bit of code I have. I am not showing every class, but I will show the main class where everything is happening.
Could someone eyeball this and tell me if you see anything major?
I am new to JS and new to making games, I know there is something called update()
that people use in scripting but I am not familiar with it.
Thank you.
var ship;
var flowers = []; // flowers === aliens
var drops = [];
var drops2 = [];
function setup() {
createCanvas(600, 600);
ship = new Ship();
for (var i = 0; i < 6; i ++) {
flowers[i] = new Flower(i * 80 + 80, 60);
}
flower = new Flower();
}
function draw() {
background(51);
ship.show();
ship.move();
shipDrops();
alienDrops();
dropsAndAliens();
dropDelete();
drop2Delete();
}
// if 0 drops, show and move none, if 5, etc..
function shipDrops() {
for (var i = 0; i < drops.length; i ++) {
drops[i].show();
drops[i].move();
for (var j = 0; j < flowers.length; j++) {
if(drops[i].hits(flowers[j]) ) {
flowers[j].shrink();
if (flowers[j].r === 0) {
flowers[j].destroy();
}
// get rid of drops after it encounters ship
drops[i].evaporate();
}
if(flowers[j].toDelete) {
// if this drop remove, use splice function to splice out of array
flowers.splice(j, 1); // splice out i, at 1
}
}
}
}
function alienDrops() {
// below is for alien/flower fire drops 2
for (var i = 0; i < drops2.length; i ++) {
drops2[i].show();
drops2[i].move();
if(drops2[i].hits(ship) ) {
ship.shrink();
drops2[i].evaporate(); // must evap after shrink
ship.destroy();
if (ship.toDelete) {
delete ship.x;
delete ship.y;
} // above is in progress, deletes after ten hits?
}
}
}
function dropsAndAliens() {
var randomNumber; // for aliens to shoot
var edge = false;
// loop to show multiple flowers
for (var i = 0; i < flowers.length; i ++) {
flowers[i].show();
flowers[i].move();
// ******************************************
randomNumber = Math.floor(Math.random() * (100) );
if(randomNumber === 5) {
var drop2 = new Drop2(flowers[i].x, flowers[i].y, flowers[i].r);
drops2.push(drop2);
}
//**************** above aliens shooting
// below could be method, this will ensure the flowers dont
//go offscreen and they move
//makes whtever flower hits this space become the farther most
//right flower,
if (flowers[i].x > width || flowers[i]. x < 0 ) {
edge = true;
}
}
// so if right is true, loop thru them all again and reset x
if (edge) {
for (var i = 0; i < flowers.length; i ++) {
// if any flower hits edge, all will shift down
// and start moving to the left
flowers[i].shiftDown();
}
}
}
function dropDelete() {
for (var i = drops.length - 1; i >= 0; i--) {
if(drops[i].toDelete) {
// if this drop remove, use splice function to splice out of array
drops.splice(i, 1); // splice out i, at 1
}
}
}
function drop2Delete() {
for (var i = drops2.length - 1; i >= 0; i--) {
if(drops2[i].toDelete) {
// if this drop remove, use splice function to splice out of array
drops2.splice(i, 1); // splice out i, at 1
}
}
}
function keyReleased() {
if (key != ' ') {
ship.setDir(0); // when i lift the key, stop moving
}
}
function keyPressed() {
// event triggered when user presses key, check keycode
if(key === ' ') {
var drop = new Drop(ship.x, height); // start ship x and bottom of screen
drops.push(drop); // when user hits space, add this event to array
}
if (keyCode === RIGHT_ARROW) {
// +1 move right
ship.setDir(1);
} else if (keyCode === LEFT_ARROW) {
// -1 move left
ship.setDir(-1);
} // setir only when pressing key, want continuous movement
}
Please post a MCVE instead of a disconnected snippet that we can't run. Note that this should not be your entire project. It should be a small example sketch that just shows the problem without any extra code.
But to figure out what's going on, you need to debug your program. You need to find out stuff like this:
What is the length of every array? Are they continuously growing over time?
What is the actual framerate? Is the framerate dropping, or does it just appear to be slower?
At what point does it become slower? Try hard-coding different values to see what's going on.
Please note that I'm not asking you to tell me the answers to these questions. These are the questions you need to be asking yourself. (In fact, you should have all of these answers before you post a question on Stack Overflow!)
If you still can't figure it out, then please post a MCVE in a new question post and we'll go from there. Good luck.
Background: Over the last week I've been working on a game that is essentially multi-directional Tron, using Canvas and JavaScript. I opted not to clear the Canvas every frame so that my little line segments leave a trail. For collision detection, I use this function:
// 8 sensors for collision testing, positioned evenly around the brush point
var detectionRadius = this.width / 2 + 1; //points just outside the circumference
var counter = 0;
var pixelData;
for (var i = 0; i < 16; i += 2) {
//collisionPixels[] is an array of 8 (x, y) offsets, spaced evenly around the center of the circle
var x = this.x + collisionPixels[i] * detectionRadius;
var y = this.y + collisionPixels[i + 1] * detectionRadius;
pixelData = context.getImageData(x,y,1,1).data; //pixel data at each point
if (pixelData[3] != 0) {
counter++;
}
}
if (counter > 4) {
this.collision();
}
The purpose here is to get the alpha values of 8 pixels around the brushpoint's surface; alpha values of 0 are just on the background. If the number of colliding pixels, out of the total 8, is greater than 4 (this is including the trail behind the player) then I call the collision() method. This function actually works really well (and this IS inside a function, so these declarations are local).
The problem is that context.getImageData() skyrockets my memory usage, and after 3 or 4 games tanks the framerate. Cutting just that line out and assigning pixelData some other value makes everything run very smoothly, even while doing the other computations.
How do I fix this memory leak? And, if there's a less convoluted way to do collision detection of this type, what is it?
EDIT: at request, here is my loop:
function loop() {
now = Date.now();
delta = now - lastUpdate;
lastUpdate = now;
if (!paused) {
for (var i = 0; i < numPlayers; i++) {
if (players[i].alive) {
players[i].update(delta);
players[i].render();
}
}
}
requestAnimationFrame(loop);
}
EDIT 2: So I tried Patrick's UInt8ClampedArrays idea:
//8 sensors for collision testing, positioned evenly around the brush point
var detectionRadius = this.width / 2 + 1;
var counter = 0;
for (var i = 0; i < 16; i += 2) {
var x = this.x + collisionPixels[i] * detectionRadius;
var y = this.y + collisionPixels[i + 1] * detectionRadius;
//translate into UInt8ClampedArray for data
var index = (y * canvas.width + x) * 4 + 3; //+3 so we're at the alpha index
if (canvasArray[index] != 0) {
counter++;
}
}
And, at the top of my loop I added a new global variable, updated once per frame:
var canvasArray = context.getImageData(0,0,canvas.width,canvas.height).data;
Hope I did that right. It works, but the memory and framerate still get worse each round you play. Going to upload some heap snapshots.
EDIT 3:
Snapshot 1: https://drive.google.com/open?id=0B-8p3yyYzRjeY2pEa2Z5QlgxRUk&authuser=0
Snapshot 2: https://drive.google.com/open?id=0B-8p3yyYzRjeV2pJb1NyazY3OWc&authuser=0
Snapshot 1 is after the first game, 2 is after the second.
EDIT 4: Tried capping the framerate:
function loop() {
requestAnimationFrame(loop);
now = Date.now();
delta = now - lastUpdate;
//lastUpdate = now;
if (delta > interval) {
lastUpdate = now;
if (!paused) {
for (var i = 0; i < numPlayers; i++) {
if (players[i].alive) {
players[i].update(delta);
players[i].render();
}
}
}
}
}
Where
interval = 1000 / fps;
It delays the eventual performance hit, but memory is still climbing with this option.
EDIT 5: While I'm sure there must be a better way, I found a solution that works reasonably well. Capping the framerate around 30 actually worked in terms of long-term performance, but I hated the way the game looked at 30 FPS.. so I built a loop that had an uncapped framerate for all updating and rendering EXCEPT for collision handling, which I updated at 30 FPS.
function loop() {
requestAnimationFrame(loop);
now = Date.now();
delta = now - lastUpdate;
lastUpdate = now;
if (!paused) {
for (var i = 0; i < numPlayers; i++) {
if (players[i].alive) {
players[i].update(delta);
players[i].render();
}
}
if (now - lastCollisionUpdate > collisionInterval) {
canvasData = context.getImageData(0, 0, context.canvas.width, context.canvas.height).data;
for (var i = 0; i < numPlayers; i++) {
if (players[i].alive) {
if (players[i].detectCollisions()) {
players[i].collision();
}
}
}
lastCollisionUpdate = now;
}
canvasData = null;
}
}
Thanks for the answers.. a lot of your ideas found their way into the final(?) product, and I appreciate that. Closing this thread.
Is there some point at which you could call context.getImageData(0, 0, context.canvas.width, context.canvas.height).data so that you can use that single UInt8ClampedArray instead of however many you're using? Also when you're done with the image data (the ImageData that is, not the TypedArray inside it), you could try calling delete on it, though I'm not certain if that will deallocate the memory.
While I'm sure there must be a better way, I found a solution that works reasonably well. Capping the framerate around 30 actually worked in terms of long-term performance, but I hated the way the game looked at 30 FPS.. so I built a loop that had an uncapped framerate for all updating and rendering EXCEPT for collision handling, which I updated at 30 FPS.
//separate update cycle for collision detection
var collisionFPS = 30;
var lastCollisionUpdate;
var collisionInterval = 1000 / collisionFPS;
var canvasData;
function loop() {
requestAnimationFrame(loop);
now = Date.now();
delta = now - lastUpdate;
lastUpdate = now;
if (!paused) {
for (var i = 0; i < numPlayers; i++) {
if (players[i].alive) {
players[i].update(delta);
players[i].render();
}
}
if (now - lastCollisionUpdate > collisionInterval) {
canvasData = context.getImageData(0, 0, context.canvas.width, context.canvas.height).data;
for (var i = 0; i < numPlayers; i++) {
if (players[i].alive) {
if (players[i].detectCollisions()) {
players[i].collision();
}
}
}
lastCollisionUpdate = now;
}
canvasData = null;
}
}
Might not be the best solution, but it's consistent.
This is my jsfiddle: http://jsfiddle.net/seekpunk/whZ44/17/
As you can see in my jsfiddle when the ball reach a block the jump function is not working.I know i am missing something in my algorithm that i am using to make the ball jump but i can't figure it out i would appreciate some help from you
if (InAir) {
if (InAir == 1) {
if (ball.y > jumpMaxHeight) {
ball.y -= gravity;
} else {
InAir = 2;
}
}
if (InAir == 2) {
if (ball.y < 390) {
ball.y += gravity;
} else {
ball.y = 390;
InAir = false;
ball.color = "black";
}
}
} else if (keydown.up && !InAir) {
InAir = 1;
ball.color = "#4285F4";
}
for (var j = 0; j < Blocks.collection.length; j++) {
if (Collision(ball, Blocks.collection[j])) {
//console.log("collision");
ball.y = (Blocks.collection[j].blockY - ball.radius) ;
}
}
Maybe here is something to start with: http://jsfiddle.net/d247V/3/
I updated the jump to have a default jump of 80 (high enough to get to next platform), and then on keydown.up i update that with the current ball.y position + the default
I think though the issue is how you are tracking the ball being in the air or not, when a collision is detected, the ball is technically no longer in the air, but you didn't have anything saying as such, see code snipped below basically how #mainguy said but with a slight modification to not keep reassigning ball.y, in the fiddle it works pretty well:
if( InAir )
{
for (var j = 0; j < Blocks.collection.length; j++) {
if (Collision(ball, Blocks.collection[j])) {
//console.log("collision");
var calc = (Blocks.collection[j].blockY - ball.radius);
if( ball.y != calc ) {
ball.y = calc;
ball.color = "black";
InAir = 0;
}
}
}
}
My fiddle though broke the gravity portion of the code, because you now need to also know if the x of the circle is passed the x barrier of the rectangle, and fall if not. So this is by no means a complete solution, but if you jump and go right or left from a barrier, the gravity process works as expected as long as no collisions exist, you will fall down to the bottom. There are slight other tweaks but I think you will be able to find them by the comment //Tweaked next to the parts I added.
It seems like your collision detection is working pretty well.
Its the gameloop that has some issues.
You should not do a collision detection on keypress up because this will only fire once. You need to check for collides all the time while the ball is in the air.
Like so:
... } else if (keydown.up && !InAir) {
InAir = 1;
ball.color = "#4285F4";
}
if (InAir){
for (var j = 0; j < Blocks.collection.length; j++) {
if (Collision(ball, Blocks.collection[j])) {
//console.log("collision");
ball.y = (Blocks.collection[j].blockY - ball.radius) ;
InAir=false;
}
}
}
With his code you can jump on the first plattform. (Note also that i changed ball.y positioning).
But some issues remain:
The Ball can not leave the plattform because as soon as he is airborne the collision with the plattform he is lying on kicks in.
If you move the Ball left or right over the plattform border it would be way more fun if he starts falling down again, instead of hovering in the air. (Trust me, i played some games).
Maybe you should delay collision detection until the ball has gained some height to escape the collision detection.
I update your fiddle and converted it to a plunker (with firebug you have way better debugging functionality here) Play with me!
Not a perfect answer but i hope this will help you.
I'm building a game using HTML5 canvas.
You can find it here, along with the source code: www.techgoldmine.com.
I'd make a jsFiddle, but in all honesty my attention span is too short (and myself mostly too stupid) to learn how it works.
I'm currently stuck at a function that looks at the positioning of certain elements on either side of the canvas and moves them so that the y-axis area they cover does not overlap. I call them turbines, but thin white rectangles would be more accurate. I suggest refreshing a few times to visually understand what's going on.
This is the function that spawns the turbines:
function gameStateNewLevel(){
for (var i = 0; i < 4; i++){
turbine = {};
turbine.width = 10;
turbine.height = 150;
turbine.y = Math.floor(Math.random()*600)
if (Math.random()*10 > 5){
turbine.side = leftSide;
}else{
turbine.side = rightSide;
}
turbine.render = function (){
context.fillStyle = "#FFFFFF"
context.fillRect(turbine.side, turbine.y, turbine.width,turbine.height);
}
turbine.PositionTop = turbine.y;
turbine.PositionBottom = turbine.y + turbine.height;
turbines.push(turbine);
}
context.fillStyle = "#FFFFFF"
switchGameState(GAME_STATE_PLAYER_START);
}
So far I've built (with the help of you wonderful people) a function (that is part of a loop) picking out each of these turbines, and starts comparing them to one another. I'm completely stumped when it comes to understanding how I'll get them to move and stop when needed:
function updateTurbines(){
var l = turbines.length-1;
for (var i = 0; i < l; i++){
var tempTurbine1 = turbines[i];
tempTurbine1.PositionTop = tempTurbine1.y;
tempTurbine1.PositionBottom = tempTurbine1.y + tempTurbine1.height;
for (var j = 0; j < l; j++) {
var tempTurbine2 = turbines[j];
tempTurbine2.PositionTop = tempTurbine2.y;
tempTurbine2.PositionBottom = tempTurbine2.y + tempTurbine2.height;
if ((tempTurbine1 !== tempTurbine2) && FIXME == true){
if(tempTurbine1.PositionBottom >= tempTurbine2.PositionTop){
turbines[j].y -=2;
//A while loop breaks the browser :(
}
}
}FIXME = false;
}
}
Any ideas or requests for additional explanation and info are more than welcome. I also have a feeling I'm severely over complicating this. Goddamn my head hurts. Bless you.
I'm afraid your code is a little bit messy do I decided to begin with a clean slate.
Use getters/setters for bottom and right. You can calculate them given the left/width and top/height values, respectively. This will save you from altering the complementary variable right when modifying e.g. left.
You seem to be looking for a collison detection algorithm for rectangles. This is quite easy if the rectangles have the same x-coordinate - two such rectangles do not collide if the bottom of the first is above the top of the other, or if the top of the first is under the bottom of the other. Use this algorithm along with a while loop to generate a new turbine as long as they collide.
This is what I ended up with (it's a separate piece of code as I stated, so you'll have to blend it into your game): http://jsfiddle.net/eGjak/249/.
var ctx = $('#cv').get(0).getContext('2d');
var Turbine = function(left, top, width, height) {
this.left = left;
this.top = top;
this.width = width;
this.height = height;
};
Object.defineProperties(Turbine.prototype, {
bottom: {
get: function() {
return this.top + this.height;
},
set: function(v) {
this.top = v - this.height;
}
},
right: {
get: function() {
return this.left + this.width;
},
set: function(v) {
this.left = v - this.width;
}
}
});
var turbines = [];
function turbineCollides(tn) {
for(var i = 0; i < turbines.length; i++) {
var to = turbines[i];
// they do not collide if if one's completely under
// the other or completely above the other
if(!(tn.bottom <= to.top || tn.top >= to.bottom)) {
return true;
}
}
return false; // this will only be executed if the `return true` part
// was never executed - so they the turbine does not collide
}
function addTurbine() {
var t, h;
do { // do while loop because the turbine has to be generated at least once
h = Math.floor(Math.random() * 300);
t = new Turbine(0, h, 15, 100);
} while(turbineCollides(t));
turbines.push(t);
}
// add two of them (do not add more than 4 because they will always collide
// due to the available space! In fact, there may be no space left even when
// you've added 2.)
addTurbine();
addTurbine();
function draw() {
ctx.fillStyle = "black";
ctx.fillRect(0, 0, 400, 400);
ctx.fillStyle = "white";
for(var i = 0; i < turbines.length; i++) {
var turbine = turbines[i];
ctx.fillRect(turbine.left, turbine.top,
turbine.width, turbine.height);
}
}
draw();