Getting rid of two elements in an array at the same time - javascript

So I'm designing a game in JavaScript, and I'm having some trouble with the way things are removed relating to my collision detection function.
It's Asteroids, so some of the objects are named accordingly.
My collision detection function is designed to check if there are collisions between the player and an asteroid, or a bullet and an asteroid. In the case of a bullet and an asteroid, one would expect both the bullet and the asteroid to disappear.
However, given how I'm checking for collisions, removing both the bullet and the asteroid it's collided with seems to be a challenge. Here is my relevant code:
for (var i=0;i<$_.mapObjs.length;i++) { //get one object to check
var superBox = $_.mapObjs[i].hitBox; //get it's properties
var objectName = $_.mapObjs[i].name;
var isAsteroid =(objectName.indexOf("asteroid") == -1)?false:true; //is the object an asteroid?
for (var y=0;y<$_.mapObjs.length;y++) { //get the object to check it against
if (objectName !== $_.mapObjs[y].name) { //if we are not checking the object against its self
var subName = $_.mapObjs[y].name;
var subIsAsteroid = (subName.indexOf("asteroid") == -1)?false:true; //is the second object an asteroid?
if (!(isAsteroid) || !(subIsAsteroid)) { //if we are not checking two asteroids against each other
var subBox = $_.mapObjs[y].hitBox;
var collision = rectIntersectsRect(superBox,subBox); //check for a collision using rectIntersectsRect
if (collision) { //if there is a collision
if ((objectName == "player" && subIsAsteroid) || (isAsteroid && subName == "player")) { //if either of the objects are the player, then the player dies
if (!player.invincible)
player.death();
} else if ((objectName == "blankObject" && subIsAsteroid) || (isAsteroid && subName == "blankObject")) { //if either of the objects are a bullet, then we destroy the asteroid
Console.log(i + "," + y); //this is the problem area
Console.log("getting rid of " + objects[i].name + " and " + objects[y].name);
if (subIsAsteroid) { //splice the asteroid out of the second array
objects.splice(y,1);
} else { //splice the asteroid out of the first array
objects.splice(i,1);
}
}
}
}
}
}
}
Now, because I need both the asteroid and bullet to dissapear when they collide, I change
if (subIsAsteroid) {
objects.splice(y,1);
} else {
objects.splice(i,1);
}
to
objects.splice(y,1);
objects.splice(i,1);
but then, whenever there is a collision, the function randomly deletes two objects off the array, even though the positions of both y and i refer to the bullet and asteroid objects. What am i doing wrong?

.splice() doesn't just remove random elements, but after the .splice(y,1) operation removes one element the indexes of all the elements after y will be one less than before - all of these later elements "move up". So if i is greater than y it will no longer refer to the element you want it to.
If you remove whichever element has a higher index first it should be fine:
objects.splice(Math.max(y,i), 1);
objects.splice(Math.min(y,i), 1);
Having said that, I can't tell from your code what the relationship between $_.mapObjs and objects is but I think it might cause problems. You have nested loops iterating over $_.mapObjs with y and i as loop indexes, but then you remove elements from objects based on the y and i indexes from $_.mapObjs. Do these two arrays have the same elements in the same order? This could explain your "random" element removal. Should you be splicing out of $_.mapObjs? If so, you'd need to adjust y and i after the removal so that you don't skip elements on the next loop iteration.

This is probably because when you splice the first index, your array changes. This means that the second index may or may not be correct; because the indices of the elements have changed. I'd recommend you to do something like this:
objects[y] = null;
objects[i] = null;
while( (remIndex = objects.indexOf(null)) !== -1 ){
objects.splice(remIndex, 1);
}
Demo

Related

Using a grid of points to detect if a object is in range

I have a grid of points and I made them using a for loop at the beginning of my app. Each point has two arrays, one is named objectsAroundMe and the other is called pointsAroundMe.
The objective is to detect if the object is near the point (using for loop for both objects and points)
After detection then if the object is in range we push it to the point.objectsAroumdMe array.
I have all of this fine and working, but the problem is getting the point to release the reference when the object is no longer near, I've tried running an if statement to do it and make the reference null but it doesn't work. If there were an efficient way of doing this that made it so only one reference was moving from array to array then that would be perfect. next I'm gonna try using array.splice and slice to copy amd paste references. But for now I've tried using array.filter and indexof and findindex none worked. But I'm newish to classes so if their is a difference between using a for loop iteration Id and using the"this" statement to clarify the object then please give me an example of how I would find the index of a "this" object and delete it's reference from the point array.
onHitTest(){
for (let ii = 0; ii < jsEngine.pointGrid.length; ii++) {
let point = jsEngine.pointGrid[ii];
let distanceBetween = calcTotalDistance(this.transform.x,this.transform.y,point.x,point.y);
let pointPosition = point.x + point.y;
if (!point.objectsAroundMe.includes(this)) {
if ( distanceBetween < mapWidth/density*1.4) {
point.objectsAroundMe.push(this);
this.hitTestArray = point.objectsAroundMe;
this.pointArray = point.pointsAroundMe;
//console.log(this.hitTestArray);
}
if(point.objectsAroundMe.includes(this)) {
if (pointPosition - distanceBetween > 100000) {
let indx= point.objectsAroundMe.indexOf(this);
point.objectsAroundMe[indx] = null;
}
}
}
}
//// second for loop for hit testing the passed array from the point to the object.
for (let i = 0 ; i < this.hitTestArray.length; i++){
let hitTestObject = this.hitTestArray[i];
if(hitTestObject.transform=== null)
continue;
if(hitTestObject === this)
continue;
let distance = calc_distance(this.transform,hitTestObject.transform);
if (distance < hitTestObject.transform.width + hitTestObject.transform.width
&& distance < this.transform.height + this.transform.height){
//console.log("hit!")
}
}
}
Mapwidth = 1000000 and density is 10.
distanceBetween: The distance between the object and the point using: return Math.sqrt((x1 - x2)**2 + (y1 - y2)**2);
this = the object in question (to avoid double for loop)
pointGrid= a grid of points with a total of 90 points equally spaced by mapwidth/density
I found out after finally giving up on this technique after 2 weeks that it really was not performing as well as expected, now I am going to take a similar approach and I will upload my code via: (functions and order of operation) shortly.

How to grab an objects property that is stored in an array on a click event

I have this Object and I spawn it onto the canvas after one is picked and pushed into my array.
function grabObjProp(){
var randomInt = Math.floor(Math.random() * 10) + 1;
goodProp = {};
goodProp["name"] = "goodguy";
goodProp["MC"] = new lib.goodObj();
badProp = {};
badProp["name"] = "badguy";
badProp["MC"] = new lib.badObj();
if(randomInt < 7)
ObjArray.push(goodProp);
else
ObjArray.push(badProp);
createObj();
}
I then run an if/else to see if I am clicking the good or the bad and based off that I then either run a gameOver function or I continue the game. My current issue is that my if/else checks the last array that was added and if I badguy is added when I click a goodguy the condition is met and the game ends. I wanted to know how to get the name of the object I clicked store it to a variable then compare that variable when I click and object.
function Click(e){
if(ObjArray[ObjArray.length - 1].name != "goodguy"){
addGameOverScreen();
}else{
numClicked +=1;
scoreTxt.text = numClicked;
exportRoot.removeChild(e.currentTarget);
grabObjProp();
//}
}
Edit** added -1 to if(ObjArray[ObjArray.length].name != "goodguy")so that the if was always check the last index of the array.

Javascript delete does not removed the desired property

I am using nodejs to create a game. It happens that this time I am not able to use Delete. It is not working and I have no idea why.
I execute this line:
delete Bullet.list[i]
And Bullet.list is an object with properties and after executing this line, the desired property is not removed.
Here is my code (you can see the above line of code in context here below):
var Map = require("./Map.js")
var Player = require("./Player.js");
var Bullet = require("./Bullet.js");
var Fly = require("./Fly.js");
var Settings = require("./Settings.js");
var Simulation = function(SOCKET_LIST){
//Update Bullets
for(var i in Bullet.list){
var bullet = Bullet.list[i];
bullet.update();
var shooter = Player.list[bullet.parent];
//Bullets collide with flies
for(var i in Fly.list){
var fly = Fly.list[i];
if(!bullet.toRemove && bullet.getDistance(fly) < 15){
if(shooter){
shooter.updateCoins(fly.killCoins);
shooter.updateXp(fly.killXp);
}
bullet.toRemove = true;
fly.toRemove = true;
}
}
//Collide with player
for(var i in Player.list){
var player = Player.list[i];
if(player.death) continue;
if(!bullet.toRemove && bullet.getDistance(player) < 32 && shooter !== player.id && shooter.team != player.team && !player.immune){
player.hp -= 1;
player.hpChanged = true;
if(player.hp <= 0){
if(shooter)
player.kill(shooter);
else
player.kill();
}
bullet.toRemove = true;
}
}
//Collide with map
if(typeof shooter == "undefined" || shooter.shootsCollideMap && Map.isColliding(bullet))
bullet.toRemove = true;
//Remove bullets
if(bullet.toRemove){
delete Bullet.list[i]; /*HERE IS THE PROBLEM. THE PROPERTY IS NOT BEING DELETED*/
Bullet.removePack.push[bullet.id];
}
}
I am not able to delete the property "i" from the object Bullet.list.
Your three for loops are all sharing the same variable i and thus you aren't deleting the i index you want to. This is because var i is function scoped, not scoped to the individual for loop.
When you do this:
delete Bullet.list[i];
The value of i is whatever i is after your previous for loop and probably not the item you actually want to delete. Perhaps you mean to break out of the for loop previously so that i will be a specific item you want to remove?
Or you need to separately save to another variable an index of an item that you want to remove. Or, just remove an item inside the for loop when the value of i is current.
Remember that var is function scoped. So both your var i declarations in your two nested for loops are actually referencing the exact same variable, not declaring a new one. I would recommend changing the name of the loop index in one of the two loops to be a separate variable.
If you are running in an environment where let is fully supported, you could use let i instead of var i and then the value of i would be uniquely scoped to only the for loop in which it was declared.
When you delete an array element, the array length is not affected. This holds even if you delete the last element of the array.
You could try Bullet.list.splice(i, 1) as an alternative to delete.
There is a lot going on in this code,, hope this helps!

Nested 'for' loop - array undefined

I am working on a JS where I want to create a simple game that starts by chosing number of players, name of each player and whether a player is a dealer or not. There can be only one dealer for each game:
function player(playerName, playerDealer) {
this.playerName = playerName;
this.playerDealer = playerDealer;
}
var playerNumber = prompt('Nr of players?');
var playersArray = [];
for (i = 0; i < playerNumber; i++) {
var j = i + 1;
var dealerAssigned = false; // control variable to check whether dealer has been assigned
var inputName = prompt('Name of player nr ' + j);
var inputDealer = prompt('Is player ' + inputName + ' also a dealer? (yes/no)');
playersArray[i] = new player(inputName, inputDealer);
for (k=0;k<playerNumber;k++){ // I want to go through the players array to check if dealer has been assigned
if (playersArray[k].playerDealer == 'yes') {
dealerAssigned=true;
break;
};
};
if(dealerAssigned){ //if dealer has been assigned, don't add the current player to the array and continue with the next iteration
alert("already assigned");
continue;
};
};
I need to include a simple test into the loop that would check if the dealer has been appointed. If so, I want the script only to alert 'already assigned' and skip to the next player. But I am constantly getting the following error
TypeError: playersArray[k] is undefined
Can anybody explain why is it undefined?/What am I doing wrong?
The bug you're specifically asking about appears to me to be that you're iterating over undefined array values, as the error you're getting suggests.
You're getting the number of players you want in line
var playerNumber = prompt('Nr of players?');
Then, you proceed to have two iterations (one nested in the other), in which the inner loop is trying to access values that haven't yet been assigned since the outer loop hasn't gotten there yet:
for (i = 0; i < playerNumber; i++) {
playersArray[i] = new player(inputName, inputDealer);
for (k=0; k < playerNumber; k++) {
if (playersArray[k].playerDealer == 'yes') {
...
}
}
}
It appears to me that the logical error here is the nested loop. I recommend just initializing all players in one loop, then verify that all players have an assigned dealer afterward.
I should add that I'm being intentionally myopic here and focusing very narrowly on the question asked and overlooking other issues I see.
Your for loop inside a for loop is iterating over an array that hasn't been filled yet.
First iteration playersArray[j] = new Player(...) makes the array [Player] or an array of one element! Yet the second loop is looking for an array of many elements. once you look for playersArray[1] but there is only playerArray[0] you get undefined and so undefined.playerDealer causes a TypeError.
`This is your structure stipped-down:
for (i = 0; i < playerNumber; i++) {
playersArray[i] = new player(inputName, inputDealer);
for (k=0;k<playerNumber;k++)...{
//anything with index k > i is undefined, since your outer loop
//hasn't initialized it yet.
}
}
It seems that your i-loop is trying to insert elements for the size of the array to be, but your k-loop is trying to also access the entire array instead of just the initialized portions. Limit this to for (k=0; k<i+1 ;k++) so you only check the previously initialized values of you playersArray

Create multiple arrays based on frequency of coordinates in an array

Using JavaScript, I'd like to split one big array of coordinates into smaller arrays based on coinciding points. I am not 100% sure how to write the following in code but it describes what I'm attempting to achieve:
Iterate through the array
var A = [(1,2)(1,3)(2,3)(9,10)(9,11)(10,11)];
Combine the pairs that contain any matching/identical coordinate points:
var B = (1,2)(1,3)(2,3)
var C = (9,10)(9,11)(10,11)
Combine the matching/identical points and create new, smaller arrays from the combinations in point #2
var D = [1,2,3]
var E = [9,10,11]
Can I get help please?
Working answer: http://jsfiddle.net/y3h9L/
OK, so if I understand the requirement A is a one-dimensional array that is assumed to have an even number of elements in x,y pairs.
A = [1,2, 1,3, 2,3, 9,10, 9,11, 10,11]
// output should be
[ [1,2,3], [9,10,11] ]
// but if you add an extra pair that links the two halves, say add 2,11
A2 = [1,2, 1,3, 2,3, 9,10, 9,11, 10,11, 2,11]
// then all are related so output should be
[ [1,2,3,9,10,11] ]
I've made no effort to pretty-up or optimise the following code, but it works:
// single dimensional array of x,y pairs
var A = [1,2, 1,3, 2,3, 9,10, 9,11, 10,11];
// create a working copy of A so that we can remove elements
// and still keep the original A intact.
var workingCopy = A.slice(0, A.length),
matchedPairs = [],
currentMatches,
finalCombinations = [],
x, y, i, j,
tempArray;
while (workingCopy.length > 0) {
currentMatches = [];
currentMatches.push([workingCopy.shift(),workingCopy.shift()]);
workingCopyLoop:
for (x=0,y=1; x < workingCopy.length;) {
for (i=0; i < currentMatches.length; i++){
if (workingCopy[x] === currentMatches[i][0]
|| workingCopy[y] === currentMatches[i][1]) {
currentMatches.push([workingCopy.shift(),workingCopy.shift()]);
// go back to the beginning of workingCopyLoop
x=0;
y=1;
continue workingCopyLoop;
}
}
x += 2;
y += 2;
}
matchedPairs.push(currentMatches);
}
for (i=0; i<matchedPairs.length; i++){
tempArray = [];
for (j=0; j<matchedPairs[i].length; j++) {
// I assume you have a new enough version of JS that you have Array.indexOf()
if (-1 === tempArray.indexOf(matchedPairs[i][j][0]))
tempArray.push(matchedPairs[i][j][0]);
if (-1 === tempArray.indexOf(matchedPairs[i][j][1]))
tempArray.push(matchedPairs[i][j][1]);
}
finalCombinations.push(tempArray);
}
for (i=0; i<finalCombinations.length; i++)
console.log(finalCombinations[i]);
// console.log shows that finalCombinations = [ [1,2,3], [9,10,11] ]
If it's not obvious how this works, follow it through with a debugger and/or pencil and paper.
I must say your question is rather unclear, but i think i got it.
In other words what you're saying is:
I have an array containing a bunch of numbers, logically they represent coordinates, it's not that the coordinates are subarrays inside the master array, is just looking them 2 by 2, but it's a linear array.
What you want is something that detects coordinates that are adjacent and generate a new array containing them.
After that you want to go thru the new arrays and generate new arrays containing unique-elements.
Well that's the question, now the answer. First, the second point depends on how far you want to go, i'm thinking it's anormal grid of x,y coordinates, but how adjacent you want to go? The following just applies to the inmediate adjacent, up to 8 points can be adjacent to a single point.
[1,1][2,1][3,1]
[1,2][2,2][3,2]
[1,3][2,3][3,3]
May that be a representation of the grid, if your master array has the [2,2] coordinate, you want to build an array that begins with that one and all adjacents you find, lets say like master array has [3,2], then you want to add it to the subarray of [2,2].
I'm really not writing the code i'm just gonna explain sorts of algorithm you could use.
To build the second point arrays, lets call them Adjacents Arrays (AA) you could:
First coordinate will always build the first AA
To find adjacents you will cycle thru the master array and perform an "Adjacency Check" to every coordinate which would be: second x == ( first x-1, x or x+1) AND second y == ( first y-1, y or y+1), if it passes then pop/push, if not... next.
In case you finish cycling thru the master array means that AA is complete, and you have to start a new AA with the next coordinate.
Repeat until master array is empty.
Then to create the unique-element-array is quite a simple cycle, i wrote a similar function that does something like that but it creates an array with the element and how many times it appears in the array (instances):
function uniqueCnt( ori) { // agroups and counts unique elements of an array, scrubs '' elements
var res = []; // resulting array, ori parameter stands for original array
for( let cntA = 0; cntA < ori.length; cntA++) {
for( cntB = 0; cntB < res.length; cntB += 2) if( ori[cntA] == res[cntB]) { res[cntB + 1]++; break; } // if it matches means it's another instance then increase that element count
if( cntB == res.length && ori[cntA] != '') res.push( ori[cntA], 1); // New element found then push it and start count
}
return res; // returns the agrouped array 0:element 1:instances...
}
If you don't want a count of instances, then you would need an even simpler function, you could try modify this one.

Categories

Resources