Im coding Snake from JS via this tutorial: https://medium.com/free-code-camp/think-like-a-programmer-how-to-build-snake-using-only-javascript-html-and-css-7b1479c3339e
But Im not that familiar with Canvas.
In two functions, one for collision and one for creating the food there's the following code that I need explained:
function didGameEnd() {
for (let i = 4; i < snake.length; i++) {
if (snake[i].x === snake[0].x && snake[i].y === snake[0].y) return true
}
const hitLeftWall = snake[0].x < 0;
const hitRightWall = snake[0].x > gameCanvas.width - 10;
const hitToptWall = snake[0].y < 0;
const hitBottomWall = snake[0].y > gameCanvas.height - 10;
return hitLeftWall || hitRightWall || hitToptWall || hitBottomWall
}
In the function didGameEnd Im wondering what this stands for:
"< 0;" & "> gameCanvas.width - 10;" ? Im thinking the coordinates for the canvas but I can't really find a clear answer.
and also:
function createFood() {
foodX = randomTen(15, gameCanvas.width - 10);
foodY = randomTen(15, gameCanvas.height - 10);
generate a new food location
snake.forEach(function isFoodOnSnake(part) {
const foodIsoNsnake = part.x == foodX && part.y == foodY;
if (foodIsoNsnake) createFood();
});
}
And for createFood its also "gameCanvas.width - 10" and why it says 15 before ?
I believe my problem might have a simple solution, but I can't figure it out how to fix it...
Codepen
let x;
let op;
let horamin = 10;
let horamax = 15;
function hora (x, op){
if (x > horamin && x < horamax) {
if (op == 'soma') {
document.querySelector(".hora").innerText = x + 1
} else {
document.querySelector(".hora").innerText = x - 1
} ;
}
}
document.querySelector(".horaU").addEventListener('click', (event) = > {
x = parseInt(document.querySelector(".hora").innerText);
op = "soma";
hora(x, op);
//document.querySelector(".hora").innerText = x+1;
});
document.querySelector(".horaD").addEventListener('click', (event) = > {
x = parseInt(document.querySelector(".hora").innerText);
op = "diminui";
hora(x, op)
});
Everything works as intendend, until I reach horamin or horamax value. Then it just stops working, no matter how much I click. How can I fix it?
If the number gets out of range you just block increasing & decreasing, I guess you want to always allow one of the actions:
if (op == 'soma') {
if(x >= horamax) return;
document.querySelector(".hora").innerText = x +1
} else {
if(x <= horamin) return;
document.querySelector(".hora").innerText = x -1
}
I've been working on a project for my class and I've completed it theoretically but it has some problems where it does not execute as fast as I'd like. The task is as follows: You have a 10x10 board to make moves on. You go up, down, left or right randomly. If a move takes you off the board skip it. Do this until 1,000,000 steps are taken or you reach the top right of the board. We are also supposed to count the max number of steps that a single tile received and the same with the minimum. I have done this using a 2D array and it counts sometimes and will output however it takes multiple button clicks to get an output. I'm not sure if this is a memory allocation error related to how I am accessing the 2D array to keep track of number of steps or not. I am relatively new to javascript so I don't know if my way was all that efficient.
Code
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<h1>Path Game</h1>
<!-- Starts Game -->
<button onclick="doGame()">Click Here to start Game</button>
<p id="MaxSteps"></p>
<p id="MinSteps"></p>
<p id="totalSteps"></p>
<p id="reachedSteps"></p>
<p id="reachedSquare"></p>
<p id="reachedBoth"></p>
<!-- JS -->
<script>
function doGame()
{
var gameBoard = [0,0];
var stepCount = 0;
//10x10 array to hold step counts (may be a better way. check back later)
//Cell counter
var cells = [
[0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0]
];
while(true)
{
var oneMove = Math.floor(1+(Math.random()*4));
//Conditional checks
//Check if both square and step counts are satisfied
if(gameBoard[0] == 9 && gameBoard[1] == 9 && stepCount == 1000000)
{
document.getElementById("reachedBoth").innerHTML = "Reached 1,000,000 steps and top square";
break;
}
//Reached Top right before 1,000,000 steps
else if(gameBoard[0] == 9 && gameBoard[1] == 9)
{
document.getElementById("reachedSquare").innerHTML = "Reached Top right square";
break;
}
//Reached 1,000,000 steps before top right
else if(stepCount == 1000000)
{
document.getElementById("reachedSteps").innerHTML = "Reached 1,000,000 steps";
break;
}
//Movement on the board
var x = gameBoard[0];
var y = gameBoard[1];
cells[x][y] += 1;
//Move left
if(oneMove == 1)
{
//Initialized at 1 so less than is suitable
if(gameBoard[0] < 0)
{
gameBoard[0] = 0; //Reset
}
else{
gameBoard[0]--;//Goes left
}
}
//Move right
else if(oneMove == 2)
{
//If its at the edge, keep it there or keeps from going over
if(gameBoard[0] >= 9)
{
gameBoard[0] = 9; //Reset
}
else{
gameBoard[0]++;//Goes right
}
}
//Move up
else if(oneMove == 3)
{
//If its at the edge, keep it there or keeps from going over
if(gameBoard[1] >= 9)
{
gameBoard[1] = 9; //Reset
}
else{
gameBoard[1]++;//Goes up
}
}
//Move down
else if(oneMove == 4)
{
//Initialized at 1 so less than is suitable
if(gameBoard[1] < 0)
{
gameBoard[1] = 0; //Reset
}
else{
gameBoard[1]--;//Goes down
}
}
stepCount++; //Count the steps
}
var max = 0;
var min = Infinity;
//Find max
for(var i = 0; i < cells.length;i++)
{
for(var j = 0; j < cells[i].length; j++)
{
if(max < cells[i][j])
{
max = cells[i][j];
}
}
}
//Find min
for(var i = 0; i < cells.length;i++)
{
for(var j = 0; j < cells[i].length; j++)
{
if(min > cells[i][j])
{
min = cells[i][j];
}
}
}
//Total Steps print
document.getElementById("MaxSteps").innerHTML = "Max steps were: " + max;
document.getElementById("MinSteps").innerHTML = "Min steps were: " + min;
document.getElementById("totalSteps").innerHTML = "Total steps were: " + stepCount;
}
</script>
</body>
</html>
This block is the one that strikes me as particularly inefficient:
if(oneMove == 1)
{
//Initialized at 1 so less than is suitable
if(gameBoard[0] < 1)
{
gameBoard[0] = 1; //Reset
}
else{
gameBoard[0]--;//Goes left
}
}
//Move right
else if(oneMove == 2)
{
//If its at the edge, keep it there or keeps from going over
if(gameBoard[0] >= 10)
{
gameBoard[0] = 10; //Reset
}
else{
gameBoard[0]++;//Goes right
}
}
//Move up
else if(oneMove == 3)
{
//If its at the edge, keep it there or keeps from going over
if(gameBoard[1] >= 10)
{
gameBoard[1] = 10; //Reset
}
else{
gameBoard[1]++;//Goes up
}
}
//Move down
else if(oneMove == 4)
{
//Initialized at 1 so less than is suitable
if(gameBoard[1] < 1)
{
gameBoard[1] = 1; //Reset
}
else{
gameBoard[1]--;//Goes down
}
}
This is for a class, so I won't offer you an answer directly, but can you think of a solution that instead of incrementing or decrementing your gameboard counters directly using the random value that was generated?
For example, if I had a simple 1-dimension gameboard like so:
var gameboard = [0,0,0];
var position = 0,
direction = 0;
function move() {
direction = Math.round(Math.random()) * 2 - 1;
position += direction;
}
The only thing that is missing is to account for the possibility that I have moved off the gameboard. If the requirement were to start your marker on the other side of the board (think PacMan), this could also be accomplished using the modulo operator, which in JS is %:
function move() {
direction = Math.round(Math.random()) * 2 - 1;
position += direction;
position = (position + 3) % 3;
}
Unfortunately, given your requirements, I don't see a way around if conditions to stay on the board:
position = position < 0 ? 0 : position;
position = position > 2 ? 2 : position;
Hopefully this should get you going in the right direction. The above three lines of code could actually be combined into one line, though I prefer to make them a bit more readable.
A few more notes:
Storing your x and y positions in a two-element array called
gameBoard is just confusing. Just call them x and y (as you do at the
end of your code).
Though the requirements don't call for it, try
generating the game board and performing all math so that instead of
10 elements, the game could be changed to n elements by changing only
one value in your code. It's good to have the smallest set of
controls as possible.
Good luck with your class!
I am trying to make unblock me game. I have array of objects and I made boundaries collision. But now I am stuck with collision between objects. I made cycle over objects in array but it stops on the last one. How can I make collision check on each object everytime I move with my selected object?
Full code here: http://foxfcb.sweb.cz/ I am newbie in programming so please be patient.
canvas.addEventListener('mousemove', function (e) {
...
var shapes = myState.shapes;
var l = shapes.length;
for (var i = 0; i < l; i++) {
var shape = myState.shapes[i];
var selection = myState.selection;
// collision between objects
if (selection.x < (shape.x + shape.w) && (selection.x + selection.w) > shape.x &&
selection.y < (shape.y + shape.h) && (selection.y + selection.h) > shape.y) {
myState.valid = true; //stop
}
// boundaries collision
else if (myState.selection.x < 0 || myState.selection.y < 0 || myState.selection.x + myState.selection.w > 600 || myState.selection.y + myState.selection.h > 600) {
myState.valid = true; //stop
}
else {
myState.valid = false; //moving
}
}
}
You are resetting the valid flag when you check other objects.
So here is your collision detection as a function. Notice I set state = false once before the loop and if there is a collision I break out of the loop as there is no point detecting other collisions as the flag is either true or false. You were setting the flag back to false on all but the last object if you detected a collision.
var textCollision = function(){
var shapes, l, shape, selection, i;
shapes = myState.shapes;
l = shapes.length;
myState.valid = false; // assume no collision
for (i = 0; i < l; i++) {
shape = myState.shapes[i];
selection = myState.selection;
if (selection.x < (shape.x + shape.w) && (selection.x + selection.w) > shape.x &&
selection.y < (shape.y + shape.h) && (selection.y + selection.h) > shape.y) {
myState.valid = true; //stop
// there is no point testing any others as it will make no dif
break; // step out of the for loop.
}
// boundaries collision
else if (myState.selection.x < 0 || myState.selection.y < 0 || myState.selection.x + myState.selection.w > 600 || myState.selection.y + myState.selection.h > 600) {
myState.valid = true; //stop
// there is no point testing any others as it will make no dif
break; // step out of the for loop.
}
}
}
Break.
Break is a javascript reserved token and is used to break out of loops and switches.
For loop
for(i = 0; i < 10; i++){
if(i === 2){
break; // exit the loop at 2
}
}
While loop
while(i < 10){
if(i === 2){
break; // exit out of the while loop
}
}
Also does the same on do{ }While() loops.
Break only exits the current loop;
for(j = 0; j < 10; j++){ // j loop
for(i = 0; i < 10; i++){ // i loop
if(i === 2){
break; // exit the i loop at 2
}
}
// the break moves execution to the first line after the loop it is in
// the j loop continues as normal
}
Break is also used in switch statements
function num(i){
switch(i){
case 1:
console.log("one");
// no break token so execution continues inside the switch
case 2:
console.log("two");
}
}
num(1); // outputs "one" "two"
To prevent this use break
function num(i){
switch(i){
case 1:
console.log("one");
break; // break out of the switch
case 2:
console.log("two");
// no point having a break here as it is at the end anyways
}
// break moves execution to here
}
num(1); // outputs "one"
Alright so I just tried to cut down on lines of code by changing manually writing out everything into an array.. My problem is that he now teleports and gravity doesnt work...
first of all I have a grid of cell objects which basically are a 32x32 grid "640X480".
These objects are all passed onto an array like so-
var gridcellarray = [750];
gridcellarray[0] = cell0;
gridcellarray[1] = cell1;
gridcellarray[2] = cell2;
and so on for 750 32x32 cells...
Now as for the collision script I have this...
function collisioncheck(obj) {
obj = obj;
for(var i = 0; i < gridcellarray.length; i++){
//really long if statement// sorry...
if ((gridcellarray[i].solid == true) && ((obj.PosY >= gridcellarray[i].y - obj.maskImg.height) && !(obj.PosY >= gridcellarray[i].y ) && !((obj.PosX > gridcellarray[i].x + solidOriginX + solidImg.width/2-5) || (obj.PosX < gridcellarray[i].x - solidOriginX - solidImg.width/2)))){
if(obj.onground == 0){
obj.PosY = gridcellarray[i].y - obj.maskImg.height;
obj.VelY = 0;
obj.onground = 1;
obj.jump = 0;
}
}
else if (obj.PosY >= canvas.height - obj.maskImg.height){
if(obj.onground == 0){
obj.VelY = 0;
obj.onground = 1;
obj.jump = 0;
obj.PosY = canvas.height - obj.maskImg.height;
}
}
else {
obj.VelY += 1;
obj.onground = 0;
}
}
}
now this code worked just fine before If I had manually copied it 750 times for each cell
and the problem is that Now that I have one iteration of it cycling through them as an array it gets confused and teleports me to the bottom and If I try to jump even when not below or on a block it teleports me back to the bottom.
//edit
I think all the variables should explain their purpose by name but if you have any questions please ask.
//edit
All Variables apply the object your checking collision for such as collisioncheck(player)
here is a link to a running demo of the code... Zack Bloom's code is in it and it works great applied to the unposted horizontal collision scripts but vertically, it wont reset and acknowledge your standing on a block ie ongroud = true;
Demo link
//edit
Ok now that Zack pointed out resetting the y to and x amount it helped alot but he is still not able to jump, as the onground variable doesnt want to reset when the collision is detected... oh and the teleporting is due to my upwards script -
//Upwards Collision//
if ((cell.solid == true) && ((obj.PosY >= cell.y - 32) && !(obj.PosY > cell.y+32) && !((obj.PosX > cell.x + solidOriginX + solidImg.width/2-5) || (obj.PosX < cell.x - solidOriginX - solidImg.width/2)))){
if (obj.onground == 0){
obj.VelY += 1;
obj.onground = 0;
obj.PosY = cell.y + obj.maskImg.height-13;
}
}
Any Ideas on how to fix THIS mess above? to stop him from teleporting? It is only meant to check if the top of the collision mask(red rectangle) is touching the block as if trying to jump through it, but it is meant to stop that from happening so you hit your head and fall back down.
Thanks in Advance!
The else if / else really don't belong in the loop at all, they don't evaluate the cell being looped over, but will be triggered many times each time collisioncheck is called.
function collisioncheck(obj) {
for(var i = 0; i < gridcellarray.length; i++){
var cell = gridcellarray[i];
if (cell.solid && ((obj.PosY >= cell.y - obj.maskImg.height) && !(obj.PosY >= cell.y ) && !((obj.PosX > cell.x + solidOriginX + solidImg.width/2-5) || (obj.PosX < cell.x - solidOriginX - solidImg.width/2)))){
if(!obj.onground){
obj.PosY = cell.x - obj.maskImg.height;
obj.VelY = 0;
obj.onground = 1;
obj.jump = 0;
break;
}
}
}
if (obj.PosY >= canvas.height - obj.maskImg.height){
if(!obj.onground){
obj.VelY = 0;
obj.onground = 1;
obj.jump = 0;
obj.PosY = canvas.height - obj.maskImg.height;
}
} else {
obj.VelY += 1;
obj.onground = 0;
}
}
But an even better way of doing it would be to store each gridcell based on where it was, so you just have to look up the gridcells near the ship.
// You only have to do this once to build the structure, don't do it every time you
// need to check a collision.
var gridcells = {};
for (var i=0; i < gridcellarray.length; i++){
var cell = gridcellarray[i];
var row_num = Math.floor(cell.PosX / 32);
var col_num = Math.floor(cell.PosY / 32);
if (!gridcells[row_num])
gridcells[row_num] = {};
gridcells[row_num][col_num] = cell;
}
// Then to check a collision:
function collisioncheck(obj){
// I'm not sure exactly what your variables mean, so confirm that this will equal
// the width of the object:
var obj_width = solidImg.width;
var obj_height = obj.maskImg.height;
var collided = false;
var left_col = Math.floor(obj.PosX / 32),
right_col = Math.floor((obj.PosX + obj_width) / 32),
top_row = Math.floor(obj.PosY / 32),
bottom_row = Math.floor((obj.PosY + obj_height) / 32);
for (var row=top_row; row <= bottom_row; row++){
for (var col=left_col; col <= right_col; col++){
var cell = gridcells[row][col];
if (cell.solid){
collided = true;
if (row == top_row){
// We collided into something above us
obj.VelY = 0;
obj.PosY = cell.PosY + 32;
} else if (row == bottom_row && !obj.onground){
// We collided into the ground
obj.PosY = cell.x - obj_height;
obj.VelY = 0;
obj.onground = 1;
obj.jump = 0;
}
if (col == left_col){
// We collided left
obj.VelX = 0;
obj.PosX = cell.PosX + 32;
} else if (col == right_col){
// We collided right
obj.VelX = 0;
obj.PosX = cell.PosX - obj_width;
}
}
}
}
if (obj.PosY >= canvas.height - obj_height){
if (!obj.onground){
obj.VelY = 0;
obj.onground = 1;
obj.jump = 0;
obj.PosY = canvas.height - obj_height;
}
}
if (!collided){
obj.VelY += 1;
obj.onground = 0;
}
}
Rather than looping through 720 cells each frame, we are only looking at the cells we know we are overlapping. This is much more efficient and easier to read than all the position calculations.
Some comments on your code:
var gridcellarray = [750];
That creates an array with a single member that has a value of 750. I think you are assuming that it is equivalent to:
var gridcellarray = new Array(750);
which creates an array with a length of 750. There is no need to set the size of the array, just initialise it as an empty array and assign values.
var gridcellarray = [];
gridcellarray[0] = cell0;
This replaces the value of the first member with whatever the value of cell0 is.
function collisioncheck(obj) {
obj = obj;
There is no point in the second line, it just assigns the value of obj to obj (so it's redundant).
for(var i = 0; i < gridcellarray.length; i++){
It is much more efficient in most browsers to save the value of gridcellarray.length, otherwise it must be looked up every time (the compiler may not be able to work whether it can cache it), so:
for (var i = 0, iLen = gridcellarray.length; i < iLen; i++) {
It is more efficient to store a reference to gridcellarray[i] rather than look it up every time. Also, you can use a short name since it's only used within the loop so its purpose is easily found:
var c = gridcellarray[i];
so not only will the code run faster, but the if statement has fewer characters (and you might prefer to format it differently):
if ((c.solid == true) &&
((obj.PosY >= c.y - obj.maskImg.height) && !(obj.PosY >= c.y ) &&
!((obj.PosX > c.x + solidOriginX + solidImg.width/2-5) ||
(obj.PosX < c.x - solidOriginX - solidImg.width/2)))) {
Wow, that really is some if statement. Can you break it down into simpler, logical steps?
Ah, Zack has posted an answer too so I'll leave it here.