How do I speed up my snake? - javascript
So this part of my js is moving the snake.
if(direction === "right"){
newX ++;
} else if(direction === "left"){
newX --;
} else if(direction === "up"){
newY --;
} else if(direction === "down"){
newY ++;
When I tried incrementing it by 2 when score is more than 10, it breaks its body.
Here's the code pen link
As #MikeC suggests, just modify your speed. You can define this function:
function runGame(newSpeed) {
if(typeof loop_game !== "undefined")
clearInterval(loop_game);
loop_game = setInterval(render, newSpeed);
}
Then at the start of the game, you call this function:
function startGame(){
.
.
.
runGame(speed);
}
And later on, when you need to increase the speed:
if (speed>10) {
speed -= 10;
runGame(speed);
}
http://codepen.io/anon/pen/xZJRdQ
I modified your codepen to make it work and scored 18 points :) can u beat me?
Handle the cases when newX overflows past your boundary (which I assume is 10):
newX = (newX + 2 < 10) ? newX + 2 : 10;
This will check whether adding 2 to newX will make it more than 10. If it doesn't, then it will add 2. If it does, then it will simply set newX to 10.
Related
Make a object rotate back to origin in three js
I am a beginner with THREE.js and currently have a animation that spins a 3D model around its Y axis but for the website I am creating I need to rotate it back to origin during the timespan of 90 frames. I have tried using a various different code snippets but the problem is that it seems to work half the time and the other half it totally messes it up. I believe that it messes up more if I let it spin for a while. Here is my current code for the rotation: //angle angle = ((room.rotation.y * 180) / Math.PI) % 360; total_deg = room.rotation.y; //for calculating the lap: if(angle > 0) { positiv = true; } else { positiv = false; } if(angle < last_angle) { direction = "down"; } else { direction = "up"; } if(direction == "up" && positiv == true) { lap = 1; } else if(direction == "down" && positiv == true) { lap = 2; } else if(direction == "down" && positiv == false) { lap = 3; } else if(direction == "up" && positiv == false) { lap = 4; } if(last_lap == 4 && lap == 1) { room.rotation.y = 0; } last_angle = ((room.rotation.y * 180) / Math.PI) % 360; //rotating it back depending on what lap it currently is on if(lap == 4) { room.rotation.y += -total_deg / 90; } if(lap == 3) { room.rotation.y += -(Math.PI - Math.abs(total_deg)) / 90; } if(lap == 2) { room.rotation.y += (Math.PI - Math.abs(total_deg)) / 90; } else { room.rotation.y -= total_deg / 90; } //reset the rotation when the room has spun a lap - don't know why this is works but seems to fix some of the rotation problems if(last_lap == 4 && lap == 1) { room.rotation.y = 0; } Have been stuck with this problem for days so any help would be appreciated
I hope I'm understanding your objective properly but to me it seems that there may be some confusion with degrees and radians as the units. Three.js uses radians, but I'm seeing 360 in your code which I'm unsure about. Anyway this code seems like what you're trying to do, notice how it divides 2 pi (one rotation in radians) into 90 frames. var i=0; function update( event ) { //angle if(i++<90){ this.rotation.y += 2*Math.PI/90; } }
I figured it out using a Quaternion of the objects first rotation and rotating to that later. This example was very helpful: https://threejs.org/examples/#webgl_math_orientation_transform
How to detect the side on which collision occured
This is my first post so I'm trying to make my problem as clear as possible. I'm making a game and I want to improve my collision detection. This is because I want to check what side is being hit and stop the player from moving past it without using something general like if(collision(player, enemy)) player.x = enemy.x - player.w(width) because if the player were to collide with the top it wouldn't keep the player on top. In the code it checks if any one of the statements is true and then returns it but it doesn't tell me which statement was the one that was equal to true so I can stop the player from moving accordingly, if that makes sense. If you have a more efficient collision detection for me to use it would be greatly appreciated. I've already tried to make a position variable to be equal to whatever side gets collided into and then stop the player from moving past it but it only works for the left side and won't let my player jump over the enemy or block. function collision(object1, object2) { return !( object1.x > object2.x + object2.w || object1.x + object1.w < object2.x || object1.y > object2.y + object2.h || object1.y + object1.h < object2.y ) } //Only works for the left side if(collision(player, enemy)) player.x = enemy.x - player.w I expect it to be able to tell me what side is being collided into and then either stop the player from moving past/into it and for the player to be able to be on top of the block/enemy without just being pushed to the left.
You'll want to calculate the distance between the x's and y's and also use the minimum distance that they could be colliding along each axis to find the depth along both axes. Then you can pick the smaller depth and move along that one. Here's an example: if(collision(player, enemy)){ // Most of this stuff would probably be good to keep stored inside the player // along side their x and y position. That way it doesn't have to be recalculated // every collision check var playerHalfW = player.w/2 var playerHalfH = player.h/2 var enemyHalfW = enemy.w/2 var enemyHalfH = enemy.h/2 var playerCenterX = player.x + player.w/2 var playerCenterY = player.y + player.h/2 var enemyCenterX = enemy.x + enemy.w/2 var enemyCenterY = enemy.y + enemy.h/2 // Calculate the distance between centers var diffX = playerCenterX - enemyCenterX var diffY = playerCenterY - enemyCenterY // Calculate the minimum distance to separate along X and Y var minXDist = playerHalfW + enemyHalfW var minYDist = playerHalfH + enemyHalfH // Calculate the depth of collision for both the X and Y axis var depthX = diffX > 0 ? minXDist - diffX : -minXDist - diffX var depthY = diffY > 0 ? minYDist - diffY : -minYDist - diffY // Now that you have the depth, you can pick the smaller depth and move // along that axis. if(depthX != 0 && depthY != 0){ if(Math.abs(depthX) < Math.abs(depthY)){ // Collision along the X axis. React accordingly if(depthX > 0){ // Left side collision } else{ // Right side collision } } else{ // Collision along the Y axis. if(depthY > 0){ // Top side collision } else{ // Bottom side collision } } } } Working example Here's a working example that you can play around with. Use the arrow keys to move the player around. player = { x: 9, y: 50, w: 100, h: 100 } enemy = { x: 100, y: 100, w: 100, h: 100 } output = document.getElementById("collisionType"); canvas = document.getElementById("canvas"); ctx = canvas.getContext("2d") function collision(object1, object2) { return !( object1.x > object2.x + object2.w || object1.x + object1.w < object2.x || object1.y > object2.y + object2.h || object1.y + object1.h < object2.y ) } function draw() { ctx.clearRect(0, 0, 400, 400) ctx.lineWidth = "5" ctx.beginPath(); ctx.strokeStyle = "red"; ctx.rect(player.x, player.y, player.w, player.h); ctx.stroke(); ctx.beginPath(); ctx.strokeStyle = "blue"; ctx.rect(enemy.x, enemy.y, enemy.w, enemy.h); ctx.stroke(); } function handleCollision() { if (collision(player, enemy)) { var playerHalfW = player.w / 2 var playerHalfH = player.h / 2 var enemyHalfW = enemy.w / 2 var enemyHalfH = enemy.h / 2 var playerCenterX = player.x + player.w / 2 var playerCenterY = player.y + player.h / 2 var enemyCenterX = enemy.x + enemy.w / 2 var enemyCenterY = enemy.y + enemy.h / 2 // Calculate the distance between centers var diffX = playerCenterX - enemyCenterX var diffY = playerCenterY - enemyCenterY // Calculate the minimum distance to separate along X and Y var minXDist = playerHalfW + enemyHalfW var minYDist = playerHalfH + enemyHalfH // Calculate the depth of collision for both the X and Y axis var depthX = diffX > 0 ? minXDist - diffX : -minXDist - diffX var depthY = diffY > 0 ? minYDist - diffY : -minYDist - diffY // Now that you have the depth, you can pick the smaller depth and move // along that axis. if (depthX != 0 && depthY != 0) { if (Math.abs(depthX) < Math.abs(depthY)) { // Collision along the X axis. React accordingly if (depthX > 0) { output.innerHTML = "left side collision" } else { output.innerHTML = "right side collision" } } else { // Collision along the Y axis. if (depthY > 0) { output.innerHTML = "top side collision" } else { output.innerHTML = "bottom side collision" } } } } else { output.innerHTML = "No collision" } } keyStates = [] function handleKeys() { if (keyStates[39]) { player.x += 2 //Move right } else if (keyStates[37]) { player.x -= 2 //Move left } if (keyStates[38]) { player.y -= 2 //Move up } if (keyStates[40]) { player.y += 2 //Move down } } function main() { handleKeys(); draw(); handleCollision(); window.requestAnimationFrame(main); } window.onkeydown = function(e) { keyStates[e.keyCode] = true } window.onkeyup = function(e) { keyStates[e.keyCode] = false } main(); <h2 id="collisionType"></h2> <canvas id="canvas" width='300' height='300'></canvas> Reacting to the collision Now that you know the side the collision happened on, it should be fairly trivial to decide how to react. It would be very similar to what you are currently doing for the left side just flip some signs around and change the axis. Other Considerations You may want to take into account your player's velocity (if it has one) otherwise the detection may fail. If the player's velocity is too high, it might 'tunnel' through the enemy and no collision will be detected. The player's movement can also look jittery if the velocity is not stopped upon collision Can your objects rotate or have more than 4 sides? If so, you'll probably want to use another method as described below. Here's a good answer to another post that talks in depth about collision engines Other Methods As for other collision detection methods, there's quite a few but one that comes to mind is Separating Axis Theorem which is a little more complex than what you have but will work with more complex convex shapes and rotation. It also tells you the direction and distance needed to move to resolve the collision. Here's a site that has interactive examples and goes in-depth on the subject. It doesn't appear to give a full implementation but those can be found other places.
JavaScript collision detection with objects in a multi-dimensional array
I'm currently coding a Pac-man clone with p5.js, and have ran into an issue. I have created a function which draws the map by using a multi-dimensional array, drawing a wall block where a 1 is, and nothing where a 0 is. This works fine, however i'm struggling to detect collision between the player and the walls. I have tried to use a for loop to go through the array, checking the x and y co-ordinates to see if there is a collision, however it doesn't register at all.This is the code i have used to detect collision: for(i=0;i<walls.length;i++){ walls[i].draw(); if(player.x > walls[i].x && player.x < walls[i].x + gridsize && player.y > walls[i].y && player.y < walls[i].y + gridsize){ console.log('collision') } } I can't see where the issue is here, as it seems to have worked in other programs. This runs in the Draw() function, meaning it loops 30 times a second. This is the full code, incase the issue lies elsewhere: var gridsize = 20; var walls = []; var dots = []; var player; var score =0; var maps = [[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1], [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1], [1,0,1,1,0,1,1,1,1,1,1,0,1,1,0,1], [1,0,1,1,0,0,0,0,0,0,0,0,1,1,0,1], [1,0,0,0,0,1,1,1,1,1,1,0,0,0,0,1], [1,1,1,1,0,0,0,0,0,0,0,0,1,1,1,1], [1,0,0,0,0,1,1,0,0,1,1,0,0,0,0,1], [1,0,1,0,0,1,0,0,0,0,1,0,0,1,0,1], [1,0,1,0,0,1,0,0,0,0,1,0,0,1,0,1], [1,0,1,0,0,1,1,1,1,1,1,0,0,1,0,1], [1,0,1,0,0,0,0,2,0,0,0,0,0,1,0,1], [1,0,1,1,1,0,1,1,1,1,0,1,1,1,0,1], [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1], [1,0,0,0,1,1,1,1,1,1,1,1,0,0,0,1], [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1], [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]]; function setup(){ createCanvas(320,320); frameRate(30); createMap(); } function draw(){ background(51); for(i=0;i<walls.length;i++){ walls[i].draw(); if(player.x > walls[i].x && player.x < walls[i].x + gridsize && player.y > walls[i].y && player.y < walls[i].y + gridsize){ console.log('collision') } } fill('white'); text('Score: ' + score, 5,10); for(i=0;i<dots.length;i++){ if(player.x == dots[i].x && player.y == dots[i].y && dots[i].collect == false){ dots[i].collect = true; score++; } dots[i].draw(); } player.update(); player.draw(); } function Block(x,y){ this.x = x; this.y = y; this.draw = function(){ fill('black'); rect(this.x,this.y,gridsize,gridsize); } } function Dot(x,y){ this.x = x; this.y = y; this.collect = false; this.draw = function(){ if(!this.collect){ fill('yellow'); ellipse(this.x+gridsize/2,this.y+gridsize/2,gridsize/3,gridsize/3); }else if(this.collect){ noFill(); noStroke(); ellipse(this.x+gridsize/2,this.y+gridsize/2,gridsize/3,gridsize/3); } } } function Player(x,y){ this.x = x; this.y = y; this.update = function(){ if(keyIsDown(UP_ARROW) && frameCount%5 == 0){ player.y -= gridsize; } if(keyIsDown(DOWN_ARROW) && frameCount%5 == 0){ player.y += gridsize; } if(keyIsDown(LEFT_ARROW) && frameCount%5 == 0){ player.x -= gridsize; } if(keyIsDown(RIGHT_ARROW) && frameCount%5 == 0){ player.x += gridsize; } } this.draw = function(){ fill('blue'); ellipse(this.x+gridsize/2,this.y+gridsize/2,gridsize/1.2,gridsize/1.2); } } function createMap(){ for(i=0;i<maps.length;i++){ for(j=0;j<maps[i].length;j++){ if (maps[i][j] == 1){ walls.push(new Block(j*gridsize,i*gridsize)); }else if(maps[i][j] == 0){ dots.push(new Dot(j*gridsize,i*gridsize)) }else if(maps[i][j] = 2){ player = new Player(j*gridsize,i*gridsize) } } } } I presume the issue lies with the fact that the walls are stored in an array, however i have done very similar programs in which the same code works.
PacMan controls The best way to check for this type of map is to use the player's input. The player must line up with the walls so assuming the player position is relative to the top left and the player is one map unit wide and deep. Key input requests a direction to move dx, dy hold the directions which could be more than one at a time. If dx or dy are not 0 then first check if the player is lined up with a passage, if so then check if a block is in the direction of travel. If the player is not lined up or blocked set the movement var to 0 After checking both x and y directions, then if dx or dy have a value then that must be a valid move. Code changes Remove the player collision checking code from the main loop and call the player update function with the current map as the 2D original. player.update(maps); // move the player Change the Player and update function function Player(x,y){ this.x = x; this.y = y; var dx = 0; // hold current movement var dy = 0; const speed = 1; // per Frame pixel speed best as an integer (whole number) and evenly divisible into gridSize // need the map so that must be passed to the update function this.update = function(map){ // assuming keys are held to move up to stop dx = 0; // stop by default dy = 0; if (keyIsDown(UP_ARROW)) { dy = -speed } if (keyIsDown(DOWN_ARROW)) { dy = speed } if (keyIsDown(LEFT_ARROW)) { dx = -speed } if (keyIsDown(RIGHT_ARROW)){ dx = speed } // get player map coords var x = Math.floor(this.x / gridSize); // get map coord var y = Math.floor(this.y / gridSize); // get map coord // the two if statement are best aas function // so you can select which one to call first. Depending on the latest // new keys down and if the result allows movement in that // direction then set the other direction to zero. if (dy !== 0) { // is moving up or down? if (this.y % gridsize === 0) { // only if lined up if (dy > 0){ // is moving down if (map[y + 1][x] === 1) { // down blocked dy = 0; } }else if (map[y - 1][x] === 1) { // up blocked dy = 0; } } else { // block move if not lined up with passage dy = 0; } } if(dx !== 0){ // is moving left or right? if (this.x % gridsize === 0) { // only if lined up if (dx > 0) { // is moving right if (map[y][x + 1] === 1) { // right blocked dx = 0; } } else if (map[y][x - 1] === 1) { // left blocked dx = 0; } } else { // block move if not lined up with passage dx = 0; } } // could have two moves, but can only move left right or up down // you need to add some input smarts to pick which one // this just favours up down if(dy !== 0) { dx = 0 }; // only valid moves will come through the filter above. // so move the player. this.x += dx; this.y += dy; } Adding more smarts Note I have changed the way the player moves, I set a speed per frame (1 pixel) that must be an even divisor of gridSize. The code above is the simplest implementation. This type of games needs some extra smarts in controls. You should check in the direction of the newest key down. Ie if the player traveling down and right is pressed then moving right should have priority. If player moving right and left is pressed then you should move left, not keep moving right. Extras While looking at this question I wanted to visualize the map. Maps as arrays are painful to create and modify, and very hard to find mistakes in. Much easier as a a set of strings that gets converted to an array at run time. As i have done the conversion no point wasting it. maps is identical to the original array but now easier to read and change. const maps = [ "################", "# #", "# ## ###### ## #", "# ## ## #", "# ###### #", "#### ####", "# ## ## #", "# # # # # #", "# # # # # #", "# # ###### # #", "# # 2 # #", "# ### #### ### #", "# #", "# ######## #", "# #", "################" ].map(row => row.split("").map(c => c === "#" ? 1 : c === " " ? 0 : 2));
I'm not quite sure why you're using rectangle-rectangle collision detection when you could just use grid-based collision detection. You could just use the array directly. But since you are using rectangle-rectangle collision, this line looks a little bit off: if(player.x > walls[i].x && player.x < walls[i].x + gridsize && player.y > walls[i].y && player.y < walls[i].y + gridsize){ You're checking whether the left edge of the player is inside the wall and whether the top edge of the player is inside the wall. But you aren't detecting the other edges. Usually you'd want to do something like this: if(rectOneRight > rectTwoLeft && rectOneLeft < rectTwoRight && rectOneBottom > rectTwoTop && rectOneTop < rectTwoBottom){ Notice how this if statement checks all of the edges, not just the top and left. But like I said, you might be better off just using grid collision detection, since you already have a grid of walls. Shameless self-promotion: here is a tutorial on collision detection. It's written for Processing, but everything should translate pretty directly to P5.js.
if the player is not sprite here then point-in-rect collision detection will be appropriate here. // point in rect collision detection function pointInRect (x, y, rect) { return inRange(x, rect.x, rect.x + gridsize) && inRange(y, rect.y, rect.y + gridsize); } // check a value is in range or not function inRange (value, min, max) { return value >= Math.min(min, max) && value <= Math.max(min, max); } // checking player is hitting the wall or not if(pointInRect(player.x,player.y,walls[i].x,walls[i].y)) { console.log('collision') }
how to stop an object when it collides with another object in javascript canvas
OK, so I am making a Pacman game using HTML5 . the problem is whenever I hit one of the brick blocks I want the sprite to stop moving but it keeps going until it hits the left most brick object. how do I fix this? please help... here is the code I'm using to make the sprite stop. here is all my code, if you have time, please parse it, and tell me what I have done wrong. function init(){ var canvas=document.getElementById("ctx"); var ctx = canvas.getContext("2d"); var player = {sx:6,sy:6,sw:15,sh:15,x:230,y:377,w:20,h:20} var ss = new Image(); ss.src="SS.png"; var right=false,left= true,up = false,down = false var b = [{x:0,y:0,w:25,h:((canvas.height/2)-25)},{x:0,y:((canvas.height/2)),w:25,h:((canvas.height/2))},{x:50,y:25,w:50,h:50},{x:125,y:25,w:75,h:50},{x:225,y:0,w:25,h:75},{x:275,y:25,w:75,h:50},{x:375,y:25,w:50,h:50},{x:50,y:100,w:50,h:25},{x:125,y:100,w:25,h:125},{x:125,y:150,w:75,h:25},{x:175,y:100,w:125,h:25},{x:225,y:125,w:25,h:50},{x:325,y:100,w:25,h:125},{x:275,y:150,w:75,h:25},{x:375,y:100,w:50,h:25},{x:25,y:150,w:75,h:75},{x:375,y:150,w:75,h:75},{x:375,y:250,w:75,h:75},{x:25,y:250,w:75,h:75},{x:125,y:250,w:25,h:75},{x:325,y:250,w:25,h:75},{x:175,y:300,w:125,h:25},{x:225,y:325,w:25,h:50},{x:50,y:350,w:50,h:25},{x:75,y:350,w:25,h:75},{x:125,y:350,w:75,h:25},{x:275,y:350,w:75,h:25},{x:375,y:350,w:50,h:25},{x:375,y:350,w:25,h:75},{x:25,y:400,w:25,h:25},{x:125,y:400,w:25,h:75},{x:50,y:450,w:150,h:25},{x:275,y:450,w:150,h:25},{x:325,y:400,w:25,h:50},{x:425,y:400,w:25,h:25},{x:175,y:400,w:125,h:25},{x:225,y:425,w:25,h:50},{x:450,y:0,w:50,h:((canvas.height/2)-25)},{x:450,y:(canvas.height/2),w:50,h:((canvas.height/2))}]; function gen(){ for(var i=0;i<b.length;i++){ ctx.fillStyle="blue" ctx.fillRect(b[i].x,b[i].y,b[i].w,b[i].h) } ctx.drawImage(ss,player.sx,player.sy,player.sw,player.sh,player.x,player.y,player.w,player.h) } function move(){ for(var i=0;i<b.length;i++){ //((a.x + a.width) < b.x) if(left && player.x > b[i].x && (player.x + player.w) < (b[i].x + b[i].w) && player.y > b[i].y && (player.y + player.h) < (b[i].y + b[i].h)) { // here you can tell that the user is colliding an object player.x-=1 } else { } } } function animate(){ ctx.save() ctx.clearRect(0,0,canvas.width,canvas.width); gen() move() ctx.restore(); } var ani = setInterval(animate, 30) } window.addEventListener("load",function(){ init() })
First, I can see a problem with the first part of your if condition: left = true && ... Should be left === true && ... Or even better left && ... Now for the collision part it usually is top-left or in the middle of the object I'd suggest this top-left origin collision check: if(left && (player.x >= b[i].x && player.x + player.w <= b[i].x + b[i].w) && (player.y >= b[i].y && player.y + player.h <= b[i].y + b[i].h) { // here you can tell that the user is colliding an object } It checks several cases, this part (player.x >= b[i].x && player.x + player.w <= b[i].x + b[i].w) Will meet requirements if the player's x (with its width component) inside the occupied x range of the current object The second part (player.y >= b[i].y && player.y + player.h <= b[i].y + b[i].h) Will meet requirements if the player's y (with its height component) is inside the occupied y range of the current object. It will only execute the if statement if the condition is satisfied for both of the above cases. You can tell if you should reposition the player on the left or on the right, by substracting the players x component to the object's x component, same goes for top or bottom with y component. The previous sentence is only valid if you move in a grid cell by cell.
Separating Rects Javascript
Essentially, what I'm doing is placing a bunch of random width/height rects onto a grid (near the center of it), then pushing them all away from each other until none of them overlap. I have another version where I check for collisions before I place them on the grid, but that's not what I'm going for in this build. I'm wondering if someone can explain a better way to go about this? What I've tried so far is something similar to: let r1/r2 = rect1/rect2 do { var ox = Math.max(0, Math.min(r1.x + r1.w, r2.x + r2.w) - Math.max(r1.x, r2.x)), oy = Math.max(0, Math.min(r1.y + r1.h, r2.y + r2.h) - Math.max(r1.y, r2.y)), dx = r2.x - r1.x, dy = r2.y - r1.y; if (ox > 0 && oy > 0) { if (ox >= oy) { if (r1.x >= r2.x && Math.random() > .1) { r1.x += ox; spaced = true; continue; } else { r1.x -= ox; spaced = true; continue; } } else { if (r1.y >= r2.y && Math.random() > .1) { r1.y += oy; spaced = true; continue; } else { r1.y -= oy; spaced = true; continue; } } } } while ( /* stuff */ ) the random is only there because I will run into times when a certain rect gets pushed back and forth and never gets free and causes an infinite loop. This way is horribly inefficient however.
I believe what your trying to accomplish is known as a packing problem http://en.wikipedia.org/wiki/Packing_problem. If you just search stack overflow for "2d bin packing" you should be able to find all you need to roll a much more efficient algorithm.