I am creating a Snake game (like those on old phones). I have a snippet of code below, which seems to exhibit the bizarre behavior of an object changing its value between lines.
The function makeSnakeArray is called when the game starts, or when the snake touches itself (the game restarts). It returns the new snake which is an array of objects with x and y properties to be stored in the global variable snakeArray.
The first time it is called, everything works fine. However, when it is called to restart the game, the x and y values are different in consoleLog1 and consoleLog2 (see code comment).
In consoleLog1, the x and y values are as what I expected as calculated in the function. However, in consoleLog2, the tempArray prints out what the snakeArray was when it called for the game to restart (and I made sure to clear the snakeArray by setting snakeArray = []; before calling the makeSnakeArray function). As a result, the snake does not start in the middle of the screen like it does the first time, but it just seems to continue around where it left off.
Why does this happen?
Function:
function makeSnakeArray(){
var tempArray = [];
//Get the position of the head of the snake
var halfWidth = Math.floor(canvasWidth/2) * blockSize;
var halfHeight = Math.floor(canvasHeight/2) * blockSize;
//Add in each block of the snake to the snake array, starting with the head
for (var i = 0; i < startingSnakeLength; i++){
//Create and initialize the snakeBlock
var snakeBlock = {
x: halfWidth,
y: halfHeight + (i*blockSize),
}
console.log(snakeBlock); //consoleLog1
tempArray.push(snakeBlock);
}
console.log(tempArray);//consoleLog2
return tempArray;
}
Example output:
consoleLog1
{x: 180, y: 180}
{x: 180, y: 195}
{x: 180, y: 210}
{x: 180, y: 225}
{x: 180, y: 240}
consoleLog2
0:{x: 60, y: 270}
1:{x: 60, y: 285}
2:{x: 60, y: 300}
3:{x: 60, y: 315}
4:{x: 60, y: 330}
Here is the current version of the snake game, if you wanted to see the full code: https://codepen.io/vrsivananda/pen/NvJyGJ?editors=0010
I did a debugging of your code with dev tools, and makeSnakeArray() function seem to work well. The issue is with updateSnake() function.
//Push this into the front of the snakeArray
snakeArray.unshift(newHead);
//If the head is the same place as the apple, then get a new apple and do not pop the tail off the snake
if(newHead.x == apple.x && newHead.y == apple.y){
apple = placeRandomApple();
}
else{
//Delete the tail fo the snakeArray
snakeArray.pop();
}
//Redraw the canvas
drawCanvas();
At this part you shouldn't update snake with a new head, if you know that game just was restarted. Plus you shouldn't cut the tail in this case either.
The easiest thing will be just to put a return statement after you know that game got restarted:
for (var i = 0; i < snakeArray.length; i++){
//If it is, restart the game
if(newHead.x == snakeArray[i].x && newHead.y == snakeArray[i].y){
restartSnakeGame();
return;
console.log("restarting");
}
}
To avoid all snake body manipulation
Related
I'm making a tower defense game using JavaScript and p5.js library. I have 2 images a base and a gun. The gun is drawn on top of the base to make it appear as a single unit. I need my gun to point towards the enemy(which follows a path). I need help to make the gun rotate on a point(not centre), keeping in mind that there can be several of these towers. I have tried translating(to change centre) and rotating but this doesn't work for many objects and obviously can't keep track of many objects.
I haven't seen any other question regarding this matter either, is there a better solution/alternative to what I'm trying to accomplish?
Required Code
Some Important Variables
var isFireTowerPressed = false; // checks if tower is placed
var FireTowerPos = []; // location of every tower => [] - 2d array
Preloaded Stuff
function preload() {
backgroundImg = loadImage("http://127.0.0.1:8080/img/extra/map1.png");
[...]
firetowerbaseImg = loadImage("http://127.0.0.1:8080/img/towers/firetowerbase.png");
firetowerturretImg = loadImage("http://127.0.0.1:8080/img/towers/firetowergun.png");
}
Draw Every Frame
function draw()
{
background(60, 238, 161);
[...]
if (isFireTowerPressed == true) //checks if I have pressed the button to place the tower
{
image(firetowerbaseImg, mouseX - 28, mouseY - 28);
// show range circle
noFill();
stroke(0,0,0);
strokeWeight(1);
circle(mouseX, mouseY, 300);
}
for (var i = 0; i < FireTowerPos.length; i++)
{
image(firetowerbaseImg, FireTowerPos[i][0], FireTowerPos[i][1]);
image(firetowerturretImg, FireTowerPos[i][0], FireTowerPos[i][1]-20);
}
}
Mouse Click Event
function mouseClicked()
{
if (isFireTowerPressed==true && mouseX+28 <= 750) // place-able area
{
FireTowerPos.push([mouseX-28, mouseY-28]);
isFireTowerPressed = false;
}
}
The picture shows the 2 pictures I'm using(base & gun). I need to be able to rotate the gun towards the enemy
Any help is appreciated, thank you
Translating and rotating works for several objects if you also use the push() and pop() methods, from the p5 library, which allow you to store the current settings.
To rotate towards a given point the function atan2(y2 - y1, x2 - x1) is the commonly used method.
To illustrate, I wrote this quick snippet in which the guns will rotate towards the mouse only if the mouse is within range of the gun.
let gunSprite
function preload(){
gunSprite = loadImage("https://i.imgur.com/ayvg9J2.jpg", ()=>{
gunSprite.resize(20, 40)
})
}
let guns = [
{ x: 160, y: 80, angle: 0, range: 100 },
{ x: 300, y: 200, angle: 0, range: 150 },
{ x: 100, y: 240, angle: 0, range: 120 }
]
function setup(){
createCanvas(600, 400)
noFill()
}
function draw(){
background(200)
for(let gun of guns)
drawGun(gun)
}
function drawGun(gun){
const isWithinRange = dist(mouseX, mouseY, gun.x, gun.y) < gun.range
if(isWithinRange)
gun.angle = atan2(mouseY - gun.y, mouseX - gun.x) + radians(90)
push()
translate(gun.x, gun.y)
rect(-25, -20, 50, 40) // Draw the gun base
ellipse(0, 0, gun.range*2) // display the gun range
rotate(gun.angle)
image(gunSprite, -10, -40) // Set the offset of the gun sprite and draw the gun
pop()
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.1.9/p5.min.js"></script>
Okay, so in code.org's game lab, they offer a pre-made function called "line();" which should draw a line stemming from a given x1 y1, and x2 y2 coordinate. When I attempted to create a for-loop that would allow me to draw 3 line segments on the same y pixel, the for-loop simply ran forever inside the draw loop, (but not correctly, it just moved the x2 and y2 to the right infinitely), so I moved it outside of the draw loop and now I'm getting no output at all...
var spaceXPos = 70;
var spaceXPos2 = spaceXPos + 25;
for (var i = 0; i < 4; i++){
stroke(rgb(255, 255, 255));
strokeWeight(2);
line(spaceXPos, 200, spaceXPos2, 200);
spaceXPos = spaceXPos + 20;
}
function draw() {
background("wheat");
}
This is for an independent project, I'm using code.org because it offers an easy-to-use screen for animations and drawing, and I'm also taking an AP CompSci class through it as well.
Here's a link to this code in the Game Lab
https://studio.code.org/projects/gamelab/_w391nq3oEo62S3WOOpWg6NrMrMHNvo8n20gVMDu5hg
When your line() command is outside of the draw loop, as in the sample code you posted, the following happens, in order:
Setup (once): Your setup code (everything outside of your draw loop) runs first. This includes your for loop, which ends up calling line() four times.
Draw (every 33ms): Your draw() function calls background("wheat"), painting over the output of your setup code. This happens many times each second.
So you see no output (except for the background color) because your draw() function is painting over the lines your for loop drew.
If, as you describe above, you move the for loop into draw() so that your code looks like this:
var spaceXPos = 70;
var spaceXPos2 = spaceXPos + 25;
function draw() {
background("wheat");
for (var i = 0; i < 4; i++){
stroke(rgb(255, 255, 255));
strokeWeight(2);
line(spaceXPos, 200, spaceXPos2, 200);
spaceXPos = spaceXPos + 20;
}
}
...then the following happens:
Setup (once): spaceXPos is set to 70 and spaceXPos2 is set to 95.
Draw (first time): The background is drawn, and then your lines are drawn. Every time you draw a line you are adding 20 to spaceXPos, but spaceXPos2 is not changing, so the four lines drawn in the first draw call are:
line(70, 200, 95, 200)
line(90, 200, 95, 200)
line(110, 200, 95, 200)
line(130, 200, 95, 200)
Draw (second time): The background is drawn again, and then your lines are drawn again. However, the value of spaceXPos didn't get reset, so it keeps moving to the right. Notice that the x2 value 95 hasn't changed, so one end of the line is staying in the same place.
line(150, 200, 95, 200)
line(170, 200, 95, 200)
line(190, 200, 95, 200)
line(210, 200, 95, 200)
Draw (and so on): One end of the line keeps moving to the right until it's off the screen, and no more changes are visible, so you end up with something like this:
If your goal is to draw three line segments in a row, you'll need to make sure both the x1 and x2 values change for each call, and make sure they start in the same place on every draw call. Here's one way to do that:
var START_X = 70;
var FIXED_Y = 200;
var WIDTH = 20;
var GAP = 5;
function draw() {
background("wheat");
stroke(rgb(255, 255, 255));
strokeWeight(2);
for (var i = 0; i < 3; i++){
line(
(WIDTH + GAP) * i + START_X,
FIXED_Y,
(WIDTH + GAP) * i + START_X + WIDTH,
FIXED_Y
);
}
}
I am trying to create a simple game. In which there will be 6 spaceships flying around and user will be able to select one and move around. I was able to do that.
Now, I want to add a new feature in which user will add a new spaceship by entering its parameters like speed, direction, etc. I tried doing it, but there a big in my code that I can't debug.
Here is my code: https://jsfiddle.net/fvtjzLhr/4/
I am storing the parameters of each spaceship in an global array called ships.
I am generating the initial parameters of the 6 spaceships by calling addShip function and passing parameters randomly:
var ships = [];
function addShip(x, y, speed, topSpeed, altitude, direction, id){
ships.push({
selected: false,
x: x,
y: y,
speed: speed,
topSpeed: topSpeed,
altitude: altitude,
id: id,
direction: direction
});
}
for(var i = 0; i < 6; i++) {
addShip(getRand(1, canvas.width), getRand(1, canvas.height), getRand(1, 100), getRand(1, 100), getRand(1, 100), getRand(1, 100), i+1);
}
To allow user to enter parameters of a new spaceship, I am using <input type="text"> fields.
I am reading those parameters and pushing it into ships:
// Creating a new Spaceship
var xd = document.getElementById('x');
var yd = document.getElementById('y');
var speed = document.getElementById('speed');
var topSpeed = document.getElementById('topSpeed');
var altitude = document.getElementById('altitude');
var direction = document.getElementById('direction');
document.getElementById('submit').onclick = function () {
ships.push({
selected: false,
x: xd.value,
y: yd.value,
speed: speed.value,
topSpeed: topSpeed.value,
altitude: altitude.value,
id: ships.length+1,
direction: direction.value
});
};
BUG:
When user pressed submit button, a new very big spaceship is added. (I have no idea why, I am not using scaling at all):
When you select this new spaceship (it will turn blue) and move it around (press up, then down, then right, then left) it will become of normal size as other spaceships:
In order to debug it, I am printing x value of newly added spaceship in function renderSpaceships
And it is printing 1000000…… when user enter 1 of x. Remove comment of startFlying(); to see it yourself.
//DEBUG
if(ship.id == 7){
document.getElementById("demo").innerHTML = ship.x + " ";
}
I solved this by making the following changes in my code:
// Creating a new Spaceship
var xd = document.getElementById('x');
var xz = parseInt(xd.value);
var yd = document.getElementById('y');
var yz = parseInt(yd.value);
var speed = document.getElementById('speed');
var topSpeed = document.getElementById('topSpeed');
var altitude = document.getElementById('altitude');
var direction = document.getElementById('direction');
document.getElementById('submit').onclick = function () {
ships.push({
selected: false,
x: xz,
y: yz,
speed: speed.value,
topSpeed: topSpeed.value,
altitude: altitude.value,
id: ships.length+1,
direction: direction.value
});
};
I am using the latest version of PaperJs but when I run the following code from their sample the output is "NaN."
window.onload = function () {
paper.setup('myCanvas');
with (paper) {
// Create a point whose x is between 0 and 50,
// and y is between 0 and 100
var point = new Point(50, 100) * Point.random();
console.log(point);
}
}
The code works online in sketch.paperjs.org but doesn't work when I try locally via the code above or the following (also outputs "NaN"):
// Make the paper scope global, by injecting it into window:
paper.install(window);
window.onload = function () {
// Setup directly from canvas id:
paper.setup('myCanvas');
// Create a point whose x is between 0 and 50,
// and y is between 0 and 100
var point = new Point(50, 100) * Point.random();
console.log(point);
}
The following works, and all my other PaperJs code works; it's just that I can't seem to create a random point per the documentation.
console.log(new Point(50, 100), Point.random());
Outputs:
Point {x: 50, y: 100} Point {x: 0.8624748098043336, y: 0.8705165661914955}
The documenation: http://paperjs.org/tutorials/geometry/mathematical-operations/#random-values
Are you sure you use the paper.js language and not javascript?
Because the multiply operator can't be overloaded in javascript, you have to use pointA.multiply(pointB);.
I have an array of images which I want to draw to the HTML5 canvas in specific locations (i.e. using a 'grid' type layout).
I have a function called drawImage(imageObj) which currently draws all of the images in the array to random locations on the canvas. However, I want to be able to predetermine a set number of locations at which the images should be drawn, and then choose which image is drawn at which of these set locations randomly (it doesn't matter if more than one image is drawn to the same location).
My drawImage(imageObj) function currently looks like this:
function drawImage(imageObj) {
var canvasImage = new Kinetic.Image({
image: imageObj,
width: 50,
height: 50,
/* puts the images in random locations on the canvas */
x: stage.getWidth() / 20*Math.floor(Math.random()*20),
y: stage.getHeight() / 15*Math.floor(Math.random()*8+2),
draggable: true
});
// add cursor styling
canvasImage.on('mouseover', function() {
document.body.style.cursor = 'pointer';
});
canvasImage.on('mouseout', function() {
document.body.style.cursor = 'default';
});
imagesLayer.add(canvasImage);
}
I tried altering the function slightly to create a 'grid' of locations, and then draw each image to a random 'cell' in the grid:
function drawImage(imageObj) {
/* Create arrays of X & Y coordinates, and select the array elements randomly for use as
coordinates at which to draw the images*/
var xPos = new Array();
xPos[0] = 10;
xPos[1] = 70;
xPos[2] = 130;
xPos[3] = 190;
xPos[4] = 250;
xPos[5] = 310;
xPos[6] = 370;
xPos[7] = 430;
xPos[8] = 490;
xPos[9] = 550;
xPos[10] = 610;
xPos[11] = 670;
xPos[12] = 730;
xPos[13] = 790;
xPos[14] = 850;
xPos[15] = 910;
var yPos = new Array();
yPos[0] = 10;
yPos[1] = 70;
yPos[2] = 130;
yPos[3] = 190;
yPos[4] = 250;
yPos[5] = 310;
yPos[6] = 370;
yPos[7] = 430;
var canvasImage = new Kinetic.Image({
image: imageObj,
width: 50,
height: 50,
/* Now select a random X & Y position from the arrays to draw the images to */
x: xPos(getRandomXPosition),
y: yPos(getRandomYPosition),
draggable: true
/* puts the images in random locations on the canvas
x: stage.getWidth() / 20*Math.floor(Math.random()*20),
y: stage.getHeight() / 15*Math.floor(Math.random()*8+2),
draggable: true */
});
I had expected that the lines
x: xPos(getRandomXPosition),
y: yPos(getRandomYPosition),
would set the x and y coordinates of the image that is being drawn to the canvas to a random 'cell' in my 'grid' determined by which random elements of the xPos and yPos arrays were set as the x and y values of the image that was to be drawn.
However, when I view my page in the browser, I'm getting a console error which says that "xPos is not a function" on the line
x: xPos(getRandomXPosition),
I can't figure out why this is- does anyone have any ideas? I assume I will have the same error on the line
y: yPos(getRandomYPosition),
for the same reason.
I know that xPos is not a function- it is an array, and I am simply trying to retrieve the array element at position 'getRandomXPosition'.
I thought that this might be because 'getRandomXPosition' is not an int itself, it is a function, so I tried storing its output in a variable by changing those function definition lines to:
var randomXPosition = function getRandomXPosition(minX, maxX){
return Math.floor(Math.random()*(maxX - minX +1)) +minX;
}
var randomYPosition = function getRandomYPosition(minY, maxY){
return Math.floor(Math.random()*(maxY - minY +1)) +minY;
}
and then updating where I was using them so that I was now passing the variables as parameters instead of the functions:
x: xPos(randomXPosition),
y: yPos(randomYPosition),
draggable: true
However, when viewing the page in the browser, I am still getting the console error that says that "xPos is not a function" on the line
x: xPos(randomXPosition),
I can't figure out why this is- can anyone point me in the right direction? It's probably also worth mentioning that I'm using the kineticJS library to make the images 'draggable' when they're drawn to the canvas- just to give a more complete picture of my code.
Any advice would be much appreciated!
Edited 28/01/2013 # 18:05
Ok, I think I know why the images are all being drawn in the top left corner- the drawImage function that is being called is the one from the KineticJS library, not my own one. I am using a copy of the library that I've saved locally, as there are a few things that I have changed regarding the functionality that the library provides. Would it make sense to copy the code creating the arrays of positions and selecting the random elements from those positions into the drawImage function in the library instead?
Edited 29/01/2013 # 23:15
Right, I've had a look at the drawImage function in the kineticJS library (I'm using a local copy of the library), and it looks like this:
drawImage: function() {
var context = arguments[0];
context.save();
var a = Array.prototype.slice.call(arguments);
if(a.length === 6 || a.length === 10) {
if(a.length === 6) {
context.drawImage(a[1], a[2], a[3], a[4], a[5]);
}
else {
context.drawImage(a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9]);
}
}
context.restore();
}
I'm not sure that I fully understand all of the code here... What is the line
var a = Array.prototype.slice.call(arguments);
doing? I've not seen this 'slice' function before...
Can anyone point out how I would edit this function to include the code I've written to enable the images all to be drawn in separate locations by selecting the coordinates randomly from the arrays of coordinates? Presumably, I should just be able to copy and paste this code into the function, but I'm not sure where in the function I should put it... any suggestions?
xPos is an array , so you need to use array format to get its element , like xPos[key] ,
and randomXPosition is a function , you need to execute it to get its return value , like randomXPosition() .
In conclusion ,
X: xPos[randomXPosition()],
Well, to access a certain index in the array you have to use [] not (), like so:
xPos[randomXPosition] // if randomXPosition = 1, then this gives you the value at xPos[1].
xPos(randomXPosition) // <-- this is a function call, what it is expecting is something like:
function xPos(number){
var value = number * Math.random();
return value; //return some value maybe?
};