I am building a Snake arcade game, and though the game is up and working well I am attempting to tweak the game so that the apple will never appear in the same location as one of the snake segments (for those who don't know Snake, it is a classic arcade game in which a snake slithers around the screen eating apples that randomly appear, trying to avoid running into itself or the wall and growing in length each time it eats an apple). Here is the code below that moves the apple around the screen:
Apple.prototype.move = function() {
var randomCol = Math.floor(Math.random()*(widthInBlocks-2)) +1;
var randomRow = Math.floor(Math.random()*(heightInBlocks-2)) +1;
this.position = new Block(randomCol, randomRow);
for (i = 0; i < Snake.segments; i++) {
if (this.position === Snake.segments[i].col && Snake.segments[i].row) {
this.move();
};
};
}
The constructor for the snake is as follows:
var Snake = function() {
this.segments = [
new Block(7, 5),
new Block(6, 5),
new Block(5, 5)
];
this.direction = "right";
this.nextDirection = "right";
};
And the code to draw the snake is as follows:
//function to toggle the colour of the snake's segments
function alternateColour(i) {
var colours = ["limegreen", "yellow"];
if (i % 2 === 0) {
return colours[0];
} else {
return colours[1];
};
};
//draw snake method
Snake.prototype.draw = function() {
this.segments[0].drawSquare("blue"); //snake head will always be blue
for (i = 1; i < this.segments.length; i++) {
this.segments[i].drawSquare(alternateColour(i));
};
};
To me this all looks right, but I am still a big newbie and still new to OOP. I am not sure if I have propertly written the Apple.prototype.move method so that it will never place the apple in the location of a segment of the snake's body. The probability of it happening is low, so in order to know for sure I would have to sit and play the game for hours potentially. Any input would be greatly appreciated.
Note: the game play area is divided into a grid system of rows and columns made up of 10x10 pixel areas using the following code:
var blockSize = 10;
var widthInBlocks = width/blockSize;
var heightInBlocks = height/blockSize;
Thank you.
It looks like you have a mistake in your Apple.prototype.move function when checking whether the apple position clashes with a snake segment.
You're calculating the apple position (randomCol, randomRow), but then not using these values when checking against each snake segment.
Also, you call this.move() when you expect to encounter a clash. But after that function has returned, you will continue to finish any remaining iterations in the previous for loop.
These issues can be resolved with the following changes:
for (i = 0; i < Snake.segments; i++) {
if (randomCol === Snake.segments[i].col && randomRow === Snake.segments[i].row) {
return this.move();
};
};
Related
i have finished the code of the popular snake game, it runs perfectly.
then i decided to add a level 2, so i put a condition that after the apples eaten by the snake become 10, there will be a level up and we will pass to level 2.
in level 2 there are 2 new lines drawn on the board, with the condition that if the snake hit these lines, the game will be over ( same for borders and the snake body )
here is the code:
here, game = setInterval(draw, gameSpeed), where gamespeed is the time in milliseconds and draw is the function that draws everything on the canvas
if(apples_eaten == 10){
clearInterval(game);
levelUp();
}
here is the levelup() function that should only run once in the whole game as there are only 2 levels.
function levelUp() {
lvl = 2;
apples_eaten = 0;
let display_lvl = document.getElementById("level");
display_lvl.innerHTML = "level: " + lvl;
snake.length = 1;
snake[0] = {
x : 15 * unit,
y : 10 * unit
};
apple = {
x : generate(1, w/unit),
y : generate(1, h/unit),
color : "red"
};
gameSpeed = 70;
game = setInterval(draw, gameSpeed);
}
the problem is that after reaching level 2, the game still runs correctly, except that if the snake eat a single apple, it starts bugging, moving in every direction and throwing the "you hit something" alert, that normally is thrown when you hit a border, line or itself
I have a snake game I made in Phaser 2. I am trying to create a phaser 3 version. When my snake gets too long, it really lags. This was the original code from Phaser 2
var part = snakePath.pop();
part.setTo(snakeHead.x, snakeHead.y);
snakePath.unshift(part);
for (var i = 1; i <= numSnakeSections - 1; i++)
{
snakeSection[i].x = (snakePath[i * snakeSpacer]).x;
snakeSection[i].y = (snakePath[i * snakeSpacer]).y;
if(i==numSnakeSections-3){
emitter.x = snakeSection[i].x;
emitter.y = snakeSection[i].y;
}
}
snakePath is an array where each section of the snake will follow. Each snakeSpace is the amount of space between each body section.
Phaser 3's version is as follows:
var part = snakePath.pop();
part=new Phaser.Geom.Point(snakeHead.x, snakeHead.y);
snakePath.unshift(part);
for (var i = 1; i <= numSnakeSections - 1; i++)
{
snakeSection[i].x = (snakePath[i * snakeSpacer]).x;
snakeSection[i].y = (snakePath[i * snakeSpacer]).y;
}
The body segments between become far apart as the game cannot keep up with the rendering. Is there a solution?
P.S. I based my code off of a Phaser 2 example: https://phaser.io/examples/v2/arcade-physics/snake
Found out that my sprite images were too large. It was 1000x1000. I set it to 25x25 and it worked fine
I'm trying to make all my enemies simultaneously shoot bullets at the player. My code works for one enemy shooting at a time, but what I'm trying to do is to make them all fire simultaneously.
For brevity's sake I'm using this simple condition to trigger the fire_bullets function : if (player.x > thisEnemy.x) { //fire bullets. What I intended to do is, the farther the player moves to the right, the more enemies start shooting bullets. Problem is the condition is only working for the first enemy in the array, while the other ones simply won't fire any bullet. You can see demo here :
https://jsfiddle.net/Lau1989/796xr7vg/
Here is my fire_bullets() function :
function fire_bullets(thisEnemy) {
var HTMLbullets = document.querySelectorAll('.HTMLbullets');
for (var b = 0; b < HTMLbullets.length; b++) {
var time = new Date().getTime()/1000 - startTime;
if (bullets[b].isOut && time > 0.5) {
if (player.x > thisEnemy.x) {
bullets[b] = bullets_pool(thisEnemy.x, thisEnemy.y);
}
startTime = new Date().getTime()/1000;
}
if (!bullets[b].isOut) {
bullets[b].y -= 4;
if (bullets[b].y <= 50) { //check if bullet is out of range and recycle it
bullets[b].isOut = true;
var thisBullet = bullets.splice(b, 1)[b];
thisBullet.x = -100;
bullets.push(thisBullet);
}
}
HTMLbullets[b].style.left = bullets[b].x +"px";
HTMLbullets[b].style.top = bullets[b].y +"px";
}
}
My latest try was to add the fire_bullets function as a property to each enemy object and then call it whenever their x position is lower than the player x position, but it only made the last enemy being triggered to fire its bullets, while the other ones stopped shooting theirs.
How should I proceed to make all my enemies simultaneously fire bullets at the player ? Is there a good performance-wise solution to do it, considering that each level frequently has dozens of enemies firing bullets at the same time ?
I'm building a turn based HTML game based on a 2D square grid. Each grid square could take a variable number of movement points to cross (IE: 1 MP for roads, 1.5 MP for grasslands, 2 MP for forests, etc). When the user clicks on a unit I want to determine all possible movable spaces with said unit's allotted movement points so that I can highlight them and make them clickable.
Is there a free library available to do this? I've seen a few pathing algorithms but nothing about determining movable area. How do other game developers handle this problem? I'm open to both vanilla JS and JQuery solutions.
Well, I decided to try and attack this myself. I've never been great at these sorts of algorithms so I'm sure there's a more efficient way to handle it than what I've done. However, for my purposes it runs quickly enough and is very simple and easy to understand.
In case it's helpful to anyone else looking to do the same, I've included the code below. This is an updated version of my original answer, which I modified to also store the path taken so that you can show the units moving through the correct spaces. This answer uses JQuery in the lower examples, but only in a few places; you can easily enough replace them with vanilla JS. And the first block of code, containing the actual path/area finding functionality, is pure JS.
<script>
var possibleMovementAreaArray = new Array(); // This array will hold our allowable movement tiles. Your other functions can access this after running possibleMovementArea().
function possibleMovementArea(unitIndex) {
// I'm storing each unit in my game in an array. So I pass in the index of the unit I want to determine the movement area for.
var x = unitList[unitIndex][10]; // x coordinate on the playgrid
var y = unitList[unitIndex][11]; // y coordinate on the playgrid
var mp = unitList[unitIndex][15]; // number of movement points
possibleMovementAreaArray.length = 0; // Clear our array so previous runs don't interfere.
findPossibleMovement(x, y, mp);
}
function findPossibleMovement(x, y, mp, prevStepX, prevStepY) {
// This is a recursive function; something I'm not normally too good at.
for (var d=1; d<=4; d++) {
// We run through each of the four cardinal directions. Bump this to 8 and add 4 more cases to include corners.
if (d == 1) {
// Check Up
var newX = x;
var newY = y - 1;
} else if (d == 2) {
// Check Down
var newX = x;
var newY = y + 1;
} else if (d == 3) {
// Check Left
var newX = x - 1;
var newY = y;
} else if (d == 4) {
// Check Right
var newX = x + 1;
var newY = y;
}
// Check to see if this square is occupied by another unit. Two units cannot occupy the same space.
spaceOccupied = false;
for (var j=1; j<=numUnits; j++) {
if (unitList[j][10] == newX && unitList[j][11] == newY)
spaceOccupied = true;
}
if (!spaceOccupied) {
// Modify this for loop as needed for your usage. I have a 2D array called mainMap that holds the ID of a type of terrain for each tile.
// I then have an array called terList that holds all the details for each type of terrain, such as movement points needed to get past.
// This for loop is just looking up the ID of the terrain for use later. Sort of like a "SELECT * FROM terrainInfo WHERE ID=terrainOfCurrentTile".
for (var j=1; j<=numTerrains; j++) {
if (newX > 0 && newX <= mapWidth && newY > 0 && newY <= mapHeight && terList[j][1] == mainMap[newX][newY])
break; // After finding the index of terList break out of the loop so j represents the correct index.
}
if (j <= numTerrains) { // Run if an actual terrain is found. No terrain is found if the search runs off the sides of the map.
var newMp = mp - terList[j][7]; // Decrement the movement points for this particular path.
if (newMp >= 0) { // Only continue if there were enough movement points to move to this square.
// Check to see if this square is already logged. For both efficiency and simplicity we only want each square logged once.
var newIndex = possibleMovementAreaArray.length
var alreadyLogged = false
if (possibleMovementAreaArray.length > 0) {
for (var j=0; j<possibleMovementAreaArray.length; j++) {
if (possibleMovementAreaArray[j][1] == newX && possibleMovementAreaArray[j][2] == newY) {
alreadyLogged = true;
var alreadyLoggedIndex = j;
}
}
}
if (!alreadyLogged) {
// This adds a row to the array and records the x and y coordinates of this tile as movable
possibleMovementAreaArray[newIndex] = new Array(6);
possibleMovementAreaArray[newIndex][1] = newX;
possibleMovementAreaArray[newIndex][2] = newY;
possibleMovementAreaArray[newIndex][3] = prevStepX; // This tracks the x coords of the steps taken so far to get here.
possibleMovementAreaArray[newIndex][4] = prevStepY; // This tracks the y coords of the steps taken so far to get here.
possibleMovementAreaArray[newIndex][5] = newMp; // Records remaining MP after the previous steps have been taken.
}
if (alreadyLogged && newMp > possibleMovementAreaArray[alreadyLoggedIndex][5]) {
// If this tile was already logged, but there was less MP remaining on that attempt, then this one is more efficient. Update the old path with this one.
possibleMovementAreaArray[alreadyLoggedIndex][3] = prevStepX;
possibleMovementAreaArray[alreadyLoggedIndex][4] = prevStepY;
possibleMovementAreaArray[alreadyLoggedIndex][5] = newMp;
}
if (newMp > 0) {
// Now update the list of previous steps to include this tile. This list will be passed along to the next call of this function, thus building a path.
if (prevStepX == '') {
var newPrevStepX = [newX];
var newPrevStepY = [newY];
} else {
// This code is required to make a full copy of the array holding the existing list of steps. If you use a simple equals then you just create a reference and
// subsequent calls are all updating the same array which creates a chaotic mess. This way we store a separate array for each possible path.
var newPrevStepX = prevStepX.slice();
newPrevStepX.push(newX);
var newPrevStepY = prevStepY.slice();
newPrevStepY.push(newY);
}
// If there are still movement points remaining, check and see where we could move next.
findPossibleMovement(newX, newY, newMp, newPrevStepX, newPrevStepY);
}
}
}
}
}
}
</script>
After running the above, you can then loop through the array to find all usable tiles. Here is how I did it:
<script>
// Shows the movement area based on the currently selected unit.
function showMovement() {
var newHTML = "";
curAction = "move";
possibleMovementArea(curUnit); // See above code
for (x=0; x<possibleMovementAreaArray.length; x++) {
// Loop over the array and do something with each tile. In this case I'm creating an overlay that I'll fade in and out.
var tileLeft = (possibleMovementAreaArray[x][1] - 1) * mapTileSize; // Figure out where to absolutely position this tile.
var tileTop = (possibleMovementAreaArray[x][2] - 1) * mapTileSize; // Figure out where to absolutely position this tile.
newHTML = newHTML + "<img id='path_" + possibleMovementAreaArray[x][1] + "_" + possibleMovementAreaArray[x][2] + "' onClick='mapClk(" + possibleMovementAreaArray[x][1] + ", " + possibleMovementAreaArray[x][2] + ", 0);' src='imgs/path.png' class='mapTile' style='left:" + tileLeft + "px; top:" + tileTop + "px;'>";
}
$("#movementDiv").html(newHTML); // Add all those images into a preexisting div.
$("#movementDiv").css("opacity", "0.5"); // Fade the div to 50%
$("#movementDiv").show(); // Make the div visible.
startFading(); // Run a routine to fade the div in and out.
}
</script>
Since we determined the path, we can easily show movement as well by looping through the stored information:
<script>
for (j=0; j<possibleMovementAreaArray[areaIndex][3].length; j++) {
// This loop moves the unit img to each tile on its way to its destination. The final destination tile is not included.
var animSpeed = 150; // Time in ms that it takes to move each square.
var animEase = "linear"; // We want movement to remain a constant speed through each square in this case.
var targetLeft = (possibleMovementAreaArray[areaIndex][3][j]-1) * mapTileSize; // This looks at each step in the path array and multiplies it by tile size to determine the new horizonal position.
var targetTop = (possibleMovementAreaArray[areaIndex][4][j]-1) * mapTileSize; // This looks at each step in the path array and multiplies it by tile size to determine the new vertical position.
$("#char_"+curUnit).animate({"left":targetLeft, "top":targetTop}, animSpeed, animEase); // Do the animation. Subsequent animations get queued.
}
// Now we need to move to that last tile.
newLeft = (x-1) * mapTileSize;
newTop = (y-1) * mapTileSize;
$("#char_"+curUnit).animate({"left":newLeft, "top":newTop}, 400, "easeOutCubic"); // Slow unit at the end of journey for aesthetic purposes.
$("#char_"+curUnit).addClass("unitMoved", 250); // Turns the image grayscale so it can easily be seen that it has already moved.
</script>
Hopefully this is helpful to others.
I am learning ways of manipulating HTML 5 Canvas, and decided to write a simple game, scroller arcade, for better comprehension. It is still at very beginning of development, and rendering a background (a moving star field), I encountered little, yet annoying issue - some of the stars are blinking, while moving. Here's the code I used:
var c = document.getElementById('canv');
var width = c.width;
var height = c.height;
var ctx = c.getContext('2d');//context
var bgObjx = new Array;
var bgObjy = new Array;
var bgspeed = new Array;
function init(){
for (var i = 1; i < 50; i++){
bgObjx.push(Math.floor(Math.random()*height));
bgObjy.push(Math.floor(Math.random()*width));
bgspeed.push(Math.floor(Math.random()*4)+1);
}
setInterval('draw_bg();',50);
}
function draw_bg(){
var distance; //distace to star is displayed by color
ctx.fillStyle = "rgb(0,0,0)";
ctx.fillRect(0,0,width,height);
for (var i = 0; i < bgObjx.length; i++){
distance = Math.random() * 240;
if (distance < 100) distance = 100;//Don't let it be too dark
ctx.fillStyle = "rgb("+distance+","+distance+","+distance+")";
ctx.fillRect(bgObjx[i], bgObjy[i],1,1);
bgObjx[i] -=bgspeed[i];
if (bgObjx[i] < 0){//if star has passed the border of screen, redraw it as new
bgObjx[i] += width;
bgObjy[i] = Math.floor(Math.random() * height);
bgspeed[i] = Math.floor (Math.random() * 4) + 1;
}
}
}
As you can see, there are 3 arrays, one for stars (objects) x coordinate, one for y, and one for speed variable. Color of a star changes every frame, to make it flicker. I suspected that color change is the issue, and binded object's color to speed:
for (var i = 0; i < bgObjx.length; i++){
distance = bgspeed[i]*30;
Actually, that solved the issue, but I still don't get how. Would any graphics rendering guru bother to explain this, please?
Thank you in advance.
P.S. Just in case: yes, I've drawn some solutions from existing Canvas game, including the color bind to speed. I just want to figure out the reason behind it.
In this case, the 'Blinking' of the stars is caused by a logic error in determining the stars' distance (color) value.
distance = Math.random() * 240; // This is not guaranteed to return an integer
distance = (Math.random() * 240)>>0; // This rounds down the result to nearest integer
Double buffering is usually unnecessary for canvas, as browsers will not display the drawn canvas until the drawing functions have all been completed.
Used to see a similar effect when programming direct2d games. Found a double-buffer would fix the flickering.
Not sure how you would accomplish a double(or triple?)-buffer with the canvas tag, but thats the first thing I would look into.