I am trying to create a game using javascript. The function drawItems draws 5 rectangles which are meant to resemble coins. When the player collides with the coin thanks to the itemCollision function (which works), the coin should disappear. I have tried different ways of doing this such as assigning a status which should only allow the rect() method to draw if the status is 1. That didn't work. I also tried deleting the array item and that didnt work either. I also tried another way of using an if statement with the clearRect function at the itemX and itemY positions but to no avail. The updateGameArea method is called in a setInterval function with a 20ms timeout and I understand that in the latter the item is cleared however it is redrawn on the next loop however I have no idea how to get around this. Any suggestions please?
let itemWidth = 15;
let itemHeight = 15;
let itemX = 150;
let itemY = 600;
items = {
x:0,
y:0,
status : 1
};
function drawItems() {
for (var i = 1; i < 5; i++) {
itemX = i*100;
items[i] = {x: 0, y: 0, status: 1};
console.log(items[i].status);
if (items[i].status === 1){
items[i].x = itemX;
items[i].y = itemY;
ctx.beginPath();
ctx.rect(itemX, itemY, itemWidth, itemHeight);
ctx.fillStyle = "purple";
ctx.fill();
ctx.closePath();
}
// console.log(itemX + " " + player.x);
}
}
function itemCollision(){
for (var i = 1; i < 5; i++) {
var b = items[i];
if (b.status === 1) {
if (player.x > b.x && player.x < b.x + itemWidth + 10 && player.y > b.y && player.y < b.y + itemHeight) {
b.status = 0;
delete items[i];
}else b.status = 1;
}
}
}
function updateGameArea() {
window.addEventListener("keydown", controller.keyListener);
window.addEventListener("keyup", controller.keyListener);
movePlayer();
// console.log(platform.y + " and " + player.y);
ctx.clearRect(0, 0, gameAreaWidth, gameAreaHeight);
drawItems();
itemCollision();
In the for loop, you set items[i].status to be 1 with this code: items[i] = {x: 0, y: 0, status: 1};. And immediately after, you check if(items[i].status === 1). Of course, the condition of the if statement is always true, so the rectangle always gets drawn.
Keep the array initialization part in a separate function, and call it only once, before the game begins. Do something like this:
const items = []; // empty array
function gameInitialize() { // call it before the game begins
for (let i = 1; i < 5; i++) {
items.push({x: 0, y: 0, status: 1});
}
}
function drawItems() {
for (let i = 1; i < 5; i++) {
itemX = i*100; // this line is a bit suspicious, maybe it does not belong here, I don't know
if (items[i].status === 1) {
items[i].x = itemX;
items[i].y = itemY;
ctx.beginPath();
ctx.rect(itemX, itemY, itemWidth, itemHeight);
ctx.fillStyle = "purple";
ctx.fill();
ctx.closePath();
}
}
}
Also, do not use delete on array items (unless you really know what you're doing, but even then, better not to). To remove the i-th item from an array items, you can use items.splice(i, 1);. However, you don't need to do this with your status setting approach.
Related
So, at the moment I'm doing that, but the way my leaves are falling is weird, they're sticking to the same pattern as if they were still all stuck together, I know I could make them a class and give them random magnitude so each one of them would follow it's own path with its own velocity, but I am new to code and don't quite know how to do it, would there be a simpler way??? Thank you in advance (also it'd be nice to know if there is a way to add some time between the sprouting and the falling of the leaves).
var leaves = [];
var count = 0;
function setup() {
createCanvas(windowWidth, windowHeight);
var a = createVector (width / 2, height);
var b = createVector (width / 2, height*3/4);
tree[0] = root;
}
function mousePressed() {
count++;
if (count % 3 === 0) {
for (var i = 0; i < tree.length; i++) {
if (!tree[i].finished) {
var leaf = tree[i].end.copy();
leaves.push(leaf);
}
}
function draw() {
background(51);
for (var i = 0; i < leaves.length; i++) {
fill(255, 204, 100);
noStroke();
ellipse(leaves[i].x, leaves[i].y, 2.7, 5);
leaves[i].x += random (0, 1.7);
leaves[i].y += random (0, 1.5);
}
}
I'm making a tower defense game using JavaScript and p5.js library. My enemy follows a path and their location is always stored in a list. I have a base and a gun, the gun rotates around the base(as 1 unit) and is supposed to point towards the nearest enemy. I have a function that will allow me to make the gun point towards the enemy pointEnemy however, I'm not able to get the correct condition to make it point towards the nearest enemy in it's range. I need the correct argument for enemyx & enemyy. I'm currently spawning 100 enemies and they keep moving, their location is stored in globalenemy1position. Any help is appreciated, thank you.
Required Code
Some important variables
var numberOfEnemy1 = 100
let classenemy1 = new Enemy1(numberOfEnemy1);
var globalenemy1position = [];
var isFireTowerPressed = false;
var FireTowerPos = []; // Position of all FireTowers => [x,y]
var FireTowerRange = 300;
var FireTowerAngle = 0;
My Enemy Class
class Enemy1
{
constructor(number_of_enemies)
{
this.number_of_enemies = number_of_enemies;
this.enemy_position = [];
this.enemy1speed = 4;
}
enemy1_spawn()
{
let randomx = random(-300, -100);
for(var i=0; i<this.number_of_enemies; i++)
{
var positionx = randomx;
var positiony = 100;
this.enemy_position.push([positionx + (-i*50), positiony]);
globalenemy1position.push([positionx + (-i*50), positiony]);
image(enemy1, this.enemy_position[i][0], this.enemy_position[i][1]);
}
}
enemy1_move()
{
for(var i = 0; i < this.enemy_position.length; i++)
{
image(enemy1, this.enemy_position[i][0], this.enemy_position[i][1]);
if (this.enemy_position[i][0] >= 200 && this.enemy_position[i][1] <= 450 && this.enemy_position[i][0] < 599)
{
this.enemy_position[i][1] += this.enemy1speed;
globalenemy1position[i][1] += this.enemy1speed;
}
else if (this.enemy_position[i][1] >= 100 && this.enemy_position[i][0] >= 600)
{
this.enemy_position[i][1] -= this.enemy1speed;
globalenemy1position[i][1] -= this.enemy1speed;
}
else if (this.enemy_position[i][0] >= 750)
{
this.enemy_position[i][0] = 750;
lives --;
this.enemy_position.shift();
globalenemy1position.shift();
}
else
{
this.enemy_position[i][0] += this.enemy1speed;
globalenemy1position[i][0] += this.enemy1speed;
}
}
}
}
Draw Function - Redraws Every Frame
function draw()
{
background(60, 238, 161);
[...]
classenemy1.enemy1_move();
rect(750, 70, 50, 100);
ShowLives();
if (isFireTowerPressed == true)
{
image(firetowerbaseImg, mouseX - 28, mouseY - 28);
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]);
if (globalenemy1position.length >= 1)
{
var gunx = FireTowerPos[i][0] +28;
var guny = FireTowerPos[i][1]+25;
var gunrange = FireTowerPos[i][3];
for (j=0; j<globalenemy1position.length; j++)
{
// Need help with this statement here
pointEnemy(globalenemy1position[j][0], globalenemy1position[j][1], gunx, guny, FireTowerPos[i][2], FireTowerPos[i][3]);
}
}
else
{
image(firetowerturretImg, FireTowerPos[i][0], FireTowerPos[i][1]-20);
}
}
}
Function to make the gun point towards Enemy - I need the proper value for enemyx & enemyy
function pointEnemy(enemyx, enemyy, gunx, guny, gunangle, gunrange)
{
const isWithinRange = dist(enemyx, enemyy, gunx, guny) < gunrange;
if(isWithinRange)
{
gunangle = atan2(enemyy - guny, enemyx - gunx) + radians(90);
}
push();
translate(gunx, guny);
// rect(-25, -20, 50, 40) // Draw the gun base
// ellipse(0, 0, gun.range*2) // display the gun range
rotate(gunangle);
image(firetowerturretImg, -28, -45); // Set the offset of the gun sprite and draw the gun
pop();
}
Here is a picture to help visualise the problem
As you can see, currently I'm just iterating through all the enemies and giving their location, so it's basically pointing to every enemy nearby.
Updates
1
I tried the approach given by #user3386109 , but wasn't able to implement it, also if possible I want the turret/gun to point towards the enemy till it leaves the range and not always point towards the closest enemy. It should start off with the closest and then keep pointing towards it till it leaves or the enemy dies(position removed from the list), whichever comes first. The function should then restart again and continue the process.
This process is the complete aiming for the tower. Add this to draw and it searches for enemies.
for (var i = 0; i < FireTowerPos.length; i++)
{
// image(firetowerbaseImg, FireTowerPos[i][0], FireTowerPos[i][1]);
// pointEnemy(mouseX, mouseY, FireTowerPos[i][0] +28, FireTowerPos[i][1]+25, FireTowerPos[i][2], FireTowerPos[i][3]);
image(firetowerbaseImg, FireTowerPos[i][0], FireTowerPos[i][1]);
var enemiesInRange = [];
let firetowerx = FireTowerPos[i][0];
let firetowery = FireTowerPos[i][1];
for (var j = 0; j < globalenemy1position.length; j++)
{
var checkDist = dist(globalenemy1position[j][0], globalenemy1position[j][1], firetowerx, firetowery);
let thisenemyx = globalenemy1position[j][0];
let thisenemyy = globalenemy1position[j][1];
if (checkDist < FireTowerRange)
{
enemiesInRange.push([thisenemyx, thisenemyy]);
pointEnemy(enemiesInRange[0][0], enemiesInRange[0][1], FireTowerPos[i][0] +28, FireTowerPos[i][1]+25, FireTowerPos[i][2], FireTowerPos[i][3]);
}
else
{
enemiesInRange.shift();
}
}
}
Currently I'm drawing vertices to create polygons, I would like to add sliders to allow a user to increase or decrease the amount of sides the polygons have and the amount drawn. Also having the canvas update with refreshing.
I've tried adding a slider to control the noOfSides but I've had no luck.
Thanks for your time and help.
let noOfShapes = 3;
let noOfSides;
let rx, ry, drx, dry, rd1, rd2, drd1, drd2;
let threshold = 1000;
let fillColour;
let strokeThick;
let sidesSlider;
function setup() {
createCanvas(1240, 1754);
noLoop();
// background(0, 230)
colorMode(RGB)
rectMode(CENTER);
strokeWeight(3);
//noOfSides = 3;
fillColour = random(0, 255);
sidesSlider = createSlider(4, 12, 3);
sidesSlider.position(width + 20, 0);
sidesSlider.style('width', '150px');
}
function draw() {
background(0, 230)
noOfSides = sidesSlider.value();
for (let x = 0; x < noOfShapes; x++) {
do {
rx = [];
ry = [];
rd1 = [];
rd2 = [];
for (let y = 0; y < noOfSides; y++) {
rx.push(random(10, width - 20));
ry.push(random(10, height - 20));
rd1.push(rx[y] * 1 + ry[y] * 1);
rd2.push(rx[y] * 1 - ry[y] * 1);
}
drx = max(rx) - min(rx);
dry = max(ry) - min(ry);
drd1 = max(rd1) - min(rd1);
drd2 = max(rd2) - min(rd2);
}
while (drx < threshold || dry < threshold || drd1 < threshold || drd2 < threshold)
beginShape();
stroke(255);
fill(random(1, 255), random(1, 255), random(1, 255), 150);
for (let y = 0; y < rx.length; y++) {
vertex(rx[y], ry[y]);
}
endShape(CLOSE);
}
for (let x = 20; x <= width; x = x + 20) {
blendMode(DODGE);
stroke(255);
beginShape();
vertex(x, 0)
vertex(x, height + 20)
endShape();
}
}
First things first, you need to re-draw your shapes whenever slider changes since you are using noLoop().
To do that you can easily define an on change event like this:
sidesSlider.input(sliderChange);
And i suggest you to assign noOfSides variable's value in that function. After that call the draw funtion again.
function sliderChange() {
noOfSides = sidesSlider.value();
draw();
}
Since you would remove assigning the value to noOfSides in draw function you need to set a default value to that variable either on initialization or in `setup function.
...
noOfSides = 3;
...
Then you are almost good to go, only thing that i don't quite understand was you last part of the code. I removed that part and it works as expected at the moment.
Please be aware that you are setting background color with alpha value. That leads: on each rendering of shapes, latest shapes silhouette are still barely visible.
Here is the latest version of your code:
https://editor.p5js.org/darcane/sketches/5wpp6UgXI
So i have been working hard at a game using HTML5 and JavaScript. I am trying to make a space invaders styled game and as a result i have an array of enemies. I have separate functions dedicated to creating the array of enemies, drawing them to screen, moving the enemies and finally removing them.
the removal of enemies however is causing an issue. This is my logic
if an enemies health is less than or equal to 0, delete the enemy from the array and shrink the arrays length by 1. Now logic would dictate that this can be a calamity, if you start shooting and killing enemies from the start of the array, as the arrays length will be reduced and this is exactly my problem, so low and behold my code.
function hostile(x, y) {
this.speed = 1;
this.health = 100;
this.x = x;
this.y = y;
this.height = 32;
this.width = 32;
this.isDead = false;
this.direction = 0;
this.deadCount = 0;
this.firing = false;
//this.moving = true;
this.move = function () {
if (this.isDead === false && gameStart === true) {
context.clearRect(0, 0, canvas1.width, canvas1.height);
if (this.x > canvas.width - 64) {
this.y += 10;
this.direction = 0;
}
if (this.x < 0) {
this.y += 10;
}
if (this.direction === 1) {
this.x += this.speed;
} else {
this.x -= this.speed;
}
if (this.x < 0) {
this.direction = 1;
}
if (this.y > 420) {
this.x = 600;
}
}
};
this.draw = function () {
context.drawImage(sprite, 0, 480, 65, 68, this.x, this.y, 65, 65);
};
this.reset = function () {
context.clearRect(this.x, this.y, 65, 65);
this.x = 20;
this.y = 20;
this.health = 100;
};
};
var enemylist = [];
function createEnemies() {
for (var i = 0; i < 6; i++) {
enemylist.push(new hostile(75 * i, 20));
}
};
function deleteEnemy(a) {
enemylist.splice(a);
enemyBulletList.splice(a);
//enemylist.length = enemylist.length-1;
//enemyBulletList.length = enemyBulletList.length - 1;
};
createEnemies();
function moveEnemies() {
for (var i = 0; i < enemylist.length; i++) {
if (enemylist[i].isDead === false && gameStart === true) {
if (enemylist[i].x > canvas.width - 64) {
enemylist[i].y += 10;
enemylist[i].direction = 0;
}
if (enemylist[i].x < 0) {
enemylist[i].y += 10;
}
if (enemylist[i].direction === 1) {
enemylist[i].x += enemylist[i].speed;
} else {
enemylist[i].x -= enemylist[i].speed;
}
if (enemylist[i].x < 0) {
enemylist[i].direction = 1;
}
if (enemylist[i].y > 420) {
enemylist[i].x = 600;
}
}
}
};
So to explain my problem, i can shoot and kill enemies from the array, this will also remove them from the screen. However if i shoot an enemy from the start of the array i cause all the enemies to cleared off the screen and the game to crash. If you need more information, feel free to ask.
By request, i have submitted more code relating to my question. The code above includes the hostile function and other functions associated directly with it.
EDIT: Vitim.us had offered a very useful tip regarding my question, he suggested to create a flag of some sort (var dead = false/true etc.) and once the value is changed the specific instance can simply be removed from the screen away from the player.
Don't manually update the array like that. delete is for removing named properties. If you want to remove elements from an Array, use splice:
function deleteEnemy(a) { // capitalized functions are usually reserved for constructors
enemylist.splice(a);
enemyBulletList.splice(a);
}
Also, var enemylist = [6] doesn't do what you think it does. It creates an array with a single element, [6]. You should create an empty array and then enemylist.push(new hostile(...)) to add them to the array.
This will avoid you having to manually set the length (which you should not ever have to do.)
You can implement this in various ways
You can implement a method to hostile to remove itself from screen,
To this each instance should keep a reference to itself on screen, it can be a direct reference to DOM or it can be a property like hostile.entitieId that correlate to the onscreen object.
When hostile.health<=0 you flag hostile.isDead = true; and automatically call a method to remove it from screen, than notify other method to finally delete the instance from your enemyList[]
You can either check this on the game tick or you can build it in a event dispatch fashion, using getter and setter for the health property.
this.setHeath = function(value){
//set entity heath
if(health<=0){
//remove itself from screen
//notify other method to clear this instance from the enemyArray[]
}
}
I'm coding a tile based game in javascript using canvas and was wondering how I could create a simple event handler for when the mouse enters the dimensions of a tile.
I've used jquery's http://api.jquery.com/mousemove/ in the past but for a very simple application but can't seem to wrap my head around how I'll do it in this case (quickly).
Hmm..
I started writing this post without a clue of how to do it, but I just tried using the jquery mousemove like I started above. I have a working version, but it seems 'slow' and very clunky. It's doesn't seem smooth or accurate.
I put all mode code into a js fiddle to share easily:
http://jsfiddle.net/Robodude/6bS6r/1/
so what's happening is:
1) jquery's mousemove event handler fires
2) Sends the mouse object info to the GameBoard
3) Sends the mouse object info to the Map
4) Loops through all the tiles and sends each one the mouse object
5) the individual tile then determines if the mouse coords are within its boundaries. (and does something - in this case, I just change the tiles properties to white)
but here are the sections I'm most concerned about.
$("#canvas").mousemove(function (e) {
mouse.X = e.pageX;
mouse.Y = e.pageY;
game.MouseMove(mouse);
Draw();
});
function GameBoard() {
this.Map = new Map();
this.Units = new Units();
this.MouseMove = function (Mouse) {
this.Map.MouseMove(Mouse);
};
}
function Map() {
this.LevelData = Level_1(); // array
this.Level = [];
this.BuildLevel = function () {
var t = new Tile();
for (var i = 0; i < this.LevelData.length; i++) {
this.Level.push([]);
for (var a = 0; a < this.LevelData[i].length; a++) {
var terrain;
if (this.LevelData[i][a] == "w") {
terrain = new Water({ X: a * t.Width, Y: i * t.Height });
}
else if (this.LevelData[i][a] == "g") {
terrain = new Grass({ X: a * t.Width, Y: i * t.Height });
}
this.Level[i].push(terrain);
}
}
};
this.Draw = function () {
for (var i = 0; i < this.Level.length; i++) {
for (var a = 0; a < this.Level[i].length; a++) {
this.Level[i][a].Draw();
}
}
};
this.MouseMove = function (Mouse) {
for (var i = 0; i < this.Level.length; i++) {
for (var a = 0; a < this.Level[i].length; a++) {
this.Level[i][a].MouseMove(Mouse);
}
}
};
this.BuildLevel();
}
function Tile(obj) {
//defaults
var X = 0;
var Y = 0;
var Height = 40;
var Width = 40;
var Image = "Placeholder.png";
var Red = 0;
var Green = 0;
var Blue = 0;
var Opacity = 1;
// ...
this.Draw = function () {
ctx.fillStyle = "rgba(" + this.Red + "," + this.Green + "," + this.Blue + "," + this.Opacity + ")";
ctx.fillRect(this.X, this.Y, this.Width, this.Height);
};
this.MouseMove = function (Mouse) {
if ((Mouse.X >= this.X) && (Mouse.X <= this.Xmax) && (Mouse.Y >= this.Y) && (Mouse.Y <= this.Ymax)) {
this.Red = 255;
this.Green = 255;
this.Blue = 255;
}
};
}
If you have a grid of tiles, then given a mouse position, you can retrieve the X and Y index of the tile by dividing the X mouse position by the width of a tile and Y position with the height and flooring both.
That would make Map's MouseMove:
this.MouseMove = function (Mouse) {
var t = new Tile();
var tileX = Math.floor(mouse.X / t.Width);
var tileY = Math.floor(mouse.Y / t.Height);
this.Level[tileY][tileX].MouseMove(Mouse);
};
Edit: You asked for some general suggestions. Here you go:
It's more common to use initial uppercase letters for only classes in JavaScript.
Mouse is a simple structure; I don't think it needs to have its own class. Perhaps use object literals. (like {x: 1, y: 2})
You may want to use JavaScript's prototype objects, rather than using this.method = function() { ... } for every method. This may increase performance, since it only has to create the functions once, and not whenever a new object of that class is made.