CreateJS - updating children coordinates with parent - javascript

I'd like to change the coordinates of 2 children containers within a parent container. Originally, I was looping through the children of the parent container and changing their x,y individually... but then it made more sense to just change the parent container's x,y...
The issue with that is... I need to get the individual altered coordinates of each child... But changing the parent container's coordinates doesn't seem to change the children's coordinates in relation to the stage...
The question is... how can I get the changing x,y of the children when I alter their parent's x,y?
Thanks
So if I'm moving the container of children around as such:
function NPCMove() {
if (pointA) {
if (ContainerOfAnimals.x < 400) {
ContainerOfAnimals.x +=2;
}
else {
pointA = false;
pointB = true;
}
}
else if (pointB) {
if (ContainerOfAnimals.x > 100) {
ContainerOfAnimals.x-=2;
}
else {
pointB = false;
pointA = true;
}
}
}
I can check the distance of the player to each child in the Parent Container as such using localToGlobal? (NPC_Array contains Parent Containers)
for (var i = 0; i < NPC_Array.length; i++) {
//get children containers of each big Container
for (var j = 0; j < NPC_Array[i].children.length; j++) {
//need child.x's global location now...
var pt = NPC_Array[i].localToGlobal(NPC_Array[i].children[j].x, NPC_Array[i].children[j].y);
var distX = Math.abs(players_Array[0].x - pt.x);
var distY = Math.abs(players_Array[0].y - pt.y);
if (distX < 50 && distY < 50) {
//Player is near child...

You would do a localToGlobal:
var stage = new createjs.Stage("test");
var p = new createjs.Container();
p.x = 200;
p.y = 200;
var c1 = new createjs.Shape();
c1.graphics.beginFill("#FF0000");
c1.graphics.drawRect(0, 0, 100, 100);
c1.graphics.endFill();
var c2 = new createjs.Shape();
c2.graphics.beginFill("#00FF00");
c2.graphics.drawRect(0, 0, 100, 100);
c2.graphics.endFill();
c2.x = 100;
p.addChild(c1);
p.addChild(c2);
stage.addChild(p);
stage.update();
var pt = p.localToGlobal(c2.x, c2.y);
alert("Stage x of c2: " + pt.x);
See it in action

Related

Enemy detection and turret animation/control in a JavaScript p5.js game

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();
}
}
}

Get div's width for p5.js

I'm learning on my own JavaScript so I'm doing something like a website using p5.js
The thing is that a div holding my canvas for p5.js and I want it to be responsive. In this canvas, I do have an object that needs the div width and height to be constructed.
Problem is that I don't know how to get this information. I tried jQuery but I don't know how to extract value out of a jQuery function and I'm don't know if it's an excessive way to do it.
//--------------------------constant--------------------------------------------
//Canvas
const DROPNUMBER = 1500;
//--------------------------classe---------------------------------------------
function Drop(width, heigth) {
//declaring and setting drop's attribute
this.spawn = function(width) {
//size and position
this.x = Math.random() * -width*1.5;
this.y = Math.random() * heigth;
this.size = Math.random() * 20 ;
//color
this.colorR = 138 + Math.random() * 50;
this.colorV = 43 + Math.random() * 50;
this.colorB = 226 + Math.random() * 50;
this.colorA = Math.random() * 127 +50;
//speed and landing
this.speed = Math.random();
this.hasLanded = false;
}
//call func to set the attribute
this.spawn(width);
//make the drop falls
this.fall = function() {
//if the drop can fall
if (this.x < width) {
this.x = this.x + this.speed;
this.speed = this.speed + 0.01;
//if the drop did land
if (this.y + this.size > width && this.hasLanded == false) {
this.hasLanded = true;
}
}
//if the drop did fall
else {
this.spawn(width);
}
}
//display the drop
this.display = function() {
noStroke();
//Some kind of purple color
fill(this.colorR, this.colorV, this.colorB, this.colorA);
rect(this.x, this.y, this.size, this.size)
}
}
//--------------------------setup---------------------------------------------
function setup() {
clientHeight = document.getElementById('header').clientHeight;
clientWidth = document.getElementById('header').clientWidth;
canvas = createCanvas(clientWidth, clientHeight);
canvas.parent('sketch-holder');
window.canvas = canvas;
}
//-------------------------Variable---------------------------------------------
var n = DROPNUMBER;
var drops = new Array();
//creating an array of drop for the rain
for (let i = 0; i < n; i++) {
//800 800 is height and the width that i want to change !
drops.push(new Drop(800,800));
}
//--------------------------draw------------------------------------------------
function draw() {
background(48, 64, 96);
//each drop
for (let i = 0; i < n; i++) {
//Make them falling
drops[i].fall();
//display the result
drops[i].display();
}
}
The code is just showing that drops ( the object that needs height and width) are being constructed out of draw or setup function.
I did search already on Stack Overflow to this kind of problem too.
use a regular DOM js it's worked:
let b = document.getElementById("parentElement");
let w = b.clientWidth;
let h = b.clientHeight;
console.log(w, h);
You don't need to use JQuery for this. Take a look at the P5.dom library that provides a bunch of functionality for manipulating HTML elements in P5.js.
I'm not sure exactly what you're trying to do, but here's a simple example. If we have a div on the page with and ID of myId, then to get its width we could use P5.dom to do this:
var myDiv = select('myId');
var myWidth = myDiv.style.width;
console.log('width: ' + myWidth);
To get the width of an element in JavaScript, you can select that element using document.querySelector(). Any CSS selector is a valid first argument to that function. E.g. the following will select the <body> tag:
document.querySelector('body')
Once you have an element selected, you can get it's width by accessing the clientWidth property. E.g. the following tells you the width of the <body> tag:
document.querySelector('body').clientWidth
So just replace body with a CSS selector for the element you want to select.

Detect a mouse click on an overlapping grid of boxes - JavaScript Canvas

I am currently trying to detect a mouse click on two grids of boxes simultaneously. One grid is easy, and I've just been using:
var gridPosX = Math.floor(mouseClickX/BoxWidth);
var gridPosY = Math.floor(mouseClickY/BoxHeight);
Now I also want to detect a mouse click on a secondary grid of boxes, located at the corners of the first grid of boxes. This could be achieved in a similar way to the first grid. The problem comes in because I want to detect a click on either the first grid, or the second one, at the same time. What is the best way to differentiate a click on the first grid verses a click on the second grid? I've tried to remove the Math.floor and used the greater than and less than operators (> <) to see if the click was closer to one grid spot than the other, but I've had no luck with that so far.
This is an image example of the grid. The black being the main one, the red being the second one
var WIDTH = 1280, HEIGHT = 1280;
var canvas, context;
var grid = [];
var grid2 = [];
var gridWidth = 10, gridHeight = 10;
var boxWidth = WIDTH/gridWidth, boxHeight = HEIGHT/gridHeight;
function main(){
canvas = document.createElement("canvas");
canvas.width = WIDTH;
canvas.height = HEIGHT;
context = canvas.getContext("2d");
document.body.appendChild(canvas);
canvas.onmousedown = function(e){
if(e.which == 1){
var gridPosX = Math.floor(e.offsetX/boxWidth);
var gridPosY = Math.floor(e.offsetY/boxHeight);
grid[gridPosX][gridPosY] = 0;
}
}
init();
setInterval(draw, 30);
}
function init(){
for(var x = 0; x < gridWidth; x++){
grid[x] = [];
grid2[x] = [];
for(var y = 0; y < gridHeight; y++){
grid[x][y] = 1;
grid2[x][y] = 1;
}
}
}
function draw(){
for(var x = 0; x < gridWidth; x++){
for(var y = 0; y < gridHeight; y++){
if(grid[x][y] == 1){
context.fillStyle = 'gray';
context.fillRect(x*boxWidth, y*boxHeight, boxWidth, boxHeight);
context.strokeRect(x*boxWidth, y*boxHeight, boxWidth, boxHeight);
}
}
}
for(var x = 0; x < gridWidth; x++){
for(var y = 0; y < gridHeight; y++){
if(grid2[x][y] == 1){
context.fillStyle = 'red';
context.fillRect((x*boxWidth)+(boxWidth)-(boxWidth/4), (y*boxHeight)+(boxHeight)-(boxHeight/4), boxWidth/2, boxHeight/2);
context.strokeRect((x*boxWidth)+(boxWidth)-(boxWidth/4), (y*boxHeight)+(boxHeight)-(boxHeight/4), boxWidth/2, boxHeight/2);
}
}
}
}
main();
Since the red grids show on top of the gray ones, I think you can first decide whether a mouse event is on a red grid or not. If not, then it must be on gray grids.
Based on the calculations below to check if a red grid is clicked:
var xRedIndex = Math.floor((e.offsetX - 3 / 4 * boxWidth) / (boxWidth / 2));
var yRedIndex = Math.floor((e.offsetY - 3 / 4 * boxHeight) / (boxHeight / 2));
if (xRedIndex % 2 === 0 && yRedIndex % 2 === 0) {
console.log("red");
console.log("Red grid x: " + (xRedIndex / 2));
console.log("Red grid y: " + (yRedIndex / 2));
} else {
console.log("gray");
var gridPosX = Math.floor(e.offsetX / boxWidth);
var gridPosY = Math.floor(e.offsetY / boxHeight);
grid[gridPosX][gridPosY] = 0;
}
Basically, you first subtract the initial gray area in the first column/row from the offsetX/Y, then see if the rest of the offsetX/Y contains an odd or even number of boxSize/2 (side length of red grid). An even number means the click is on red grids, otherwise it falls on the uncovered gray area.
Working fiddle: https://jsfiddle.net/mwxzgth6/1/

algorithm to randomly & efficiently place 100 circles without any overlap?

I am trying to write a script to place 100 circles of varying sizes onto a stage. I've outlined the concise requirements below.
Given the following:
var stage; // contains a "width" and "height" property.
var circle; // the circle class. contains x, y, radius & a unique id property.
var circleArray; // contains 100 circle instances
requirements:
write a function to place 100 circles of varying radius onto the stage.
placements must be random but evenly distributed (no clumping).
placement must be performant - this will be executing on a mobile web browser.
circles must not intersect/overlap other circles.
circle.x >= 0 must be true.
circle.y >= 0 && circle.y <= stage.height must be true.
circles may have any of the following radius sizes (assigned at creation):
150
120
90
80
65
My current attempt is a brute-force method, which does not operate efficiently. If I attempt to insert any more than ~10 circles, the browser hangs. Below is my current implementation, which I am completely OK with throwing away in favor of a more performant / better one.
Here is a live demo (NOTE: there is no actual drawing code, just the logic, but it will still lock up the browser so be warned!!) http://jsbin.com/muhiziduxu/2/edit?js,console
function adjustForOverlap (circleArray) {
// a reference to the circle that is invoking this function.
var _this = this;
// remove this circle from the array we are iterating over.
var arr = circleArray.filter(function (circle){
return circle.id !== _this.id;
});
// while repeat == true, the circle may be overlapping something.
var repeat = true;
while(repeat) {
var hasOverlap = false;
for (var i=0; i<arr.length; i++) {
var other = arr[i];
var dx = _self.x - other.x;
var dy = _self.y - other.y;
var rr = _self.radius + other.radius;
if (dx * dx + dy * dy < rr * rr) {
// if here, then an overlap was detected.
hit = true;
break;
}
}
// if hit is false, the circle didn't overlap anything, so break.
if (hit === false) {
repeat = false;
break;
} else {
// an overlap was detected, so randomize position.
_self.x = Math.random() * (stage.width*2);
_self.y = Math.random() * stage.height;
}
}
}
There are lots of efficient collision detection algorithms. Many of them work by dividing up the space into cells and maintaining a separate data structure with efficient lookup of other objects in the cell. The basic steps are:
Identify a random spot for your new circle
Determine which cells it's in
Look in each of those cells for a collision
If there's a collision, goto 1.
Else, add the new circle to each of the cells it overlaps.
You can use a simple square grid (i.e. a 2-d array) for the cell data structure, or something else like a quadtree. You can also in some cases get a bit of extra speed by trying a cheap-but-coarse collision check first (do the bounding boxes overlap), and if that returns true try the slightly more expensive and exact check.
Update
For quadtrees, check out d3-quadtree, which ought to give you a pretty good implementation, with examples.
For a (very quick, untested) 2-d array implementation:
function Grid(radius, width, height) {
// I'm not sure offhand how to find the optimum grid size.
// Let's use a radius as a starting point
this.gridX = Math.ceil(width / radius);
this.gridY = Math.ceil(height / radius);
// Determine cell size
this.cellWidth = width / this.gridX;
this.cellHeight = height / this.gridY;
// Create the grid structure
this.grid = [];
for (var i = 0; i < gridY; i++) {
// grid row
this.grid[i] = [];
for (var j = 0; j < gridX; j++) {
// Grid cell, holds refs to all circles
this.grid[i][j] = [];
}
}
}
Grid.prototype = {
// Return all cells the circle intersects. Each cell is an array
getCells: function(circle) {
var cells = [];
var grid = this.grid;
// For simplicity, just intersect the bounding boxes
var gridX1Index = Math.floor(
(circle.x - circle.radius) / this.cellWidth
);
var gridX2Index = Math.ceil(
(circle.x + circle.radius) / this.cellWidth
);
var gridY1Index = Math.floor(
(circle.y - circle.radius) / this.cellHeight
);
var gridY2Index = Math.ceil(
(circle.y + circle.radius) / this.cellHeight
);
for (var i = gridY1Index; i < gridY2Index; i++) {
for (var j = gridX1Index; j < gridX2Index; j++) {
// Add cell to list
cells.push(grid[i][j]);
}
}
return cells;
},
add: function(circle) {
this.getCells(circle).forEach(function(cell) {
cell.push(circle);
});
},
hasCollisions: function(circle) {
return this.getCells(circle).some(function(cell) {
return cell.some(function(other) {
return this.collides(circle, other);
}, this);
}, this);
},
collides: function (circle, other) {
if (circle === other) {
return false;
}
var dx = circle.x - other.x;
var dy = circle.y - other.y;
var rr = circle.radius + other.radius;
return (dx * dx + dy * dy < rr * rr);
}
};
var g = new Grid(150, 1000, 800);
g.add({x: 100, y: 100, radius: 50});
g.hasCollisions({x: 100, y:80, radius: 100});
Here's a fully-functional example: http://jsbin.com/cojoxoxufu/1/edit?js,output
Note that this only shows 30 circles. It looks like the problem is often unsolvable with your current radii, width, and height. This is set up to look for up to 500 locations for each circle before giving up and accepting a collision.

Creating Mouse Event Handlers For Canvas Shapes

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.

Categories