Collision Detection between an instance in motion and array does not work? - arrays

I am trying to test the collisions between a bullet and an array of enemies in Actionscript 2. However it is not sensing a collision. This is the code in the bullet.
onClipEvent(load)
{
facing = _root.player.facing;
speed = 1;
i = 0;
}
onClipEvent(enterFrame)
{
if (this._name != "bullet")
{
this._x += facing * speed;
while (i < _root.enemyID)
{
if (Math.abs(this._x - _root.enemies[i]._x)<10)
{
trace("hit enemy");
}
i++;
}
}
}

The variable i looks like it is being set to 0 only on load. So it will be checking all the enemies on the first frame, but since i will now always be greater than enemyID, it will never go into the loop again.
Try setting i = 0; just before the while loop.

Related

The reset of one array affecting a different array

I'm not entirely sure whats going on here. When you shoot all three bullets and the last one leaves the screen the "asteroids" array also resets.
EDIT:
Is it because the bullets array ends up being spliced to an undefined when they all leave the screen? Or will it return the base empty array? Even then, it doesn't explain why the asteroids array is voided as well. I also found that the asteroids don't even start falling unless I've shot at least once.
Code on p5web editor
What is it causing this to happen? This is the first time I've coded something this large, code amount wise, but I made sure to use obvious variable names for the most part.
The problem is in your asteroids class:
check(bullets) {
for (let i = 0; i < bullets.length; i++) {
if (dist(bullets[i].x, bullets[i].y, this.pos.x, this.pos.y) < this.r) {
return false;
} else {
return true;
}
}
}
If there are no bullets to check, this function returns undefined implicitly, which is taken as falsey by the calling code, which promptly destroys the asteroid as if it had collided with a bullet.
Also, if there are bullets to check and the first one happens to not collide, the loop breaks prematurely with else { return true; }, possibly missing collisions.
Change it to:
check(bullets) {
for (let i = 0; i < bullets.length; i++) {
if (dist(bullets[i].x, bullets[i].y, this.pos.x, this.pos.y) < this.r) {
return false;
}
}
return true; // no collisions (or bullets.length === 0)
}
Having said this, the function name check is pretty unclear. I'd rename it as collidesWith(bullets) and invert the boolean--it makes more sense to say "true, yes, we did collide with a bullet" than "false, yes, we did collide with a bullet". We can also make use of the for ... of loop construct. This gives us:
collidesWith(bullets) {
for (const bullet of bullets) {
if (dist(bullet.x, bullet.y, this.pos.x, this.pos.y) < this.r) {
return true;
}
}
return false;
}
You could shorten this further to:
collidesWith(bullets) {
return bullets.some(e => dist(e.x, e.y, this.pos.x, this.pos.y) < this.r);
}
Similarly, I'd change bullet.check() to bullet.outOfBounds() or similar.
Another tip: iterate in reverse over any arrays you plan to slice the current element from:
for (let j = asteroids.length - 1; j >= 0; j--) {
// code that could call `.splice(j, 1)`
}
Otherwise, after splicing, your loop will skip an element and you might miss a collision.
A minor design point, but player.controls() seems strange--why should the player be responsible for listening for keypresses? I'd listen in the keyPressed() function and send the changes to update the player position from there. I'd also break up draw into smaller functions. But these are minor design decisions so the above is enough to get you rolling.
Here's a first revision for the draw function, adjusted to match the modified booleans:
function draw() {
background(0);
scene();
if (tick <= 0) {
if (asteroids.length < asteroidsLimit) {
asteroids.push(new Asteroid());
}
tick = 60;
}
for (let i = asteroids.length - 1; i >= 0; i--) {
if (asteroids[i].collidesWith(bullets)) {
asteroids.splice(i, 1);
}
else {
asteroids[i].update();
asteroids[i].display();
}
}
for (let i = bullets.length - 1; i >= 0; i--) {
if (bullets[i].outOfBounds()) {
bullets.splice(i, 1);
}
else {
bullets[i].update();
bullets[i].display();
}
}
image(ship, player.x, player.y);
player.controls();
tick--;
}

cocos2dx : Change Array to Vector

I need to change Array to Vector as it is being depracted in cocos2dx.
Earlier it was running but after deprecation its giving error.
As I am quite new to cocos2dx I am not able to resolve this issue.
Here is my code:
int BaseScene::generateRandom()
{
//int rn = arc4random()%6+1;
int rn = rand() % 6 + 1;
Array * balls = (Array*)this->getChildren();
Array * ballsTypeLeft = Array::create();
// if(balls->count() <= 7)
{
for (int j=0; j<balls->count(); j++)
{
Node * a = (Node*)balls->objectAtIndex(j);
if(a->getTag() >= Tag_Ball_Start)
{
Ball * currentBall = (Ball*)balls->objectAtIndex(j);
bool alreadyHas = false;
for(int k=0;k<ballsTypeLeft->count();k++)
{
if(strcmp(((String*)ballsTypeLeft->objectAtIndex(k))->getCString(), (String::createWithFormat("%d",currentBall->type))->getCString()) == 0)
{
alreadyHas = true;
}
}
if(alreadyHas)
{
}
else
{
ballsTypeLeft->addObject(String::createWithFormat("%d",currentBall->type));
}
}
}
}
// CCLog("%d",ballsTypeLeft->count());
if(ballsTypeLeft->count() <=2)
{
// int tmp = arc4random()%ballsTypeLeft->count();
int tmp = rand() % ballsTypeLeft->count();
return ((String*)ballsTypeLeft->objectAtIndex(tmp))->intValue();
}
return rn;
}
How can I make this method working?
Please convert this method using Vector.
Thanks
To change cocos2d::Array to cocos2d::Vector, you must first understand it. cocos2d::Vector is implemented to mimick std::vector. std::vector is part of the STL in c++. cocos2d::Vector is built specifically to handle cocos2d::Ref. Whenever you add a Ref type to Vector it automatically retained and then released on cleanup.
Now to change Array to Vector in your code:
Store children this way:
Vector <Node*> balls = this->getChildren();
Access ball at index i this way:
Ball* ball = (Ball*)balls.at (i);
Add elements to vector this way:
balls.pushBack (myNewBall);
EDIT -
From what I understand, you want to get a random ball from the scene/layer. You can perform this by simply returning the Ball object:
Ball* BaseScene::generateRandom()
{
Vector <Node*> nodeList = this->getChildren();
Vector <Ball*> ballList;
for (int i = 0; i<nodeList.size(); i++)
{
if (ball->getTag() >= Tag_Ball_Start)
{
Ball * ball = (Ball*)nodeList.at(i);
ballList.pushBack(ball);
}
}
if (ballList.size() > 0)
{
return ballList[rand() % ballList.size()];
}
return nullptr;
}
If there is no ball it will return NULL which you can check when you call the function. The code you have linked below seems to make use of Arrays outside the function. You need to make the changes to accommodate that. I suggest studying the documentation for Vector.

action script 3 - issue with arrays

So here im checking for when a bullet hits an enemy ship in my game. Im trying to check the type of enemy in the array by object name to do specific things for that enemy, code is below.
for (var i = bullets.length - 1; i >= 0; i--) {
for (var j = enemies.length - 1; j >= 0; j--) {
if (_bullets[i].hitTestObject(enemies[j])) {
if (enemies[j] == EnemyYellow) {
trace("do something");
}
stage.removeChild(enemies[j]);
stage.removeChild(bullets[i]);
bullets.splice(i, 1);
enemies.splice(j, 1);
return;
}
}
}
This is something like I thought would work, but I would appreciate if anyone could help me out as im not sure how to do it.
if (enemies[j] == EnemyYellow) {
trace("do something");
}
You can use the keyword is
if (enemies[j] is EnemyYellow) {
trace("do something");
}
You can also add a method getType to the Enemy class. This solution is not better for this particular case but may be useful in some other cases. For example, you can have enemies of the same class but returning different types.
if (enemies[j].getType() == EnemyType.ENEMY_YELLOW) // do something

Unity3D the best way to loop through multiple dimension arrays

I am developing a game that has a winning combination array:
var allwinning = [
['000','010','020'],
['000','100','200'],
['000','001','002'],
['000','101','202'],
['000','011','022'],
['000','110','220']];
The player will need to pick more than 3 numbers randomly. If all numbers are within any of the combinations in allwinning, the player wins.
For example, if the player picks '111','110','000','220', the player will win because allwinning[5] has the combination['000','110','220'].
My question is, what is the best way to do this winning loop? I cannot figure out the optimum way to do this.
Currently, I have a playerpick array to keep what player had picked and possiblewin array:
var playerpick = new Array(['111','110','000','220']);
var playerpicksingle = playerpick[0];
var possiblewin = new Array([]);
Then I go through a loop to capture out the possible win combination first:
for(var i=0 ; i < allwinning.length - 1 ; i++)
{
for(var j=0 ; j <3 ; j++)
{
if(allwinning[i][j]==playerpicksingle)
{
possiblewin.Push(allwinning[i]);
}
}
}
Then I am stuck at this point. I really don't know what else to do.
I can think of two ways. One requires you to change your data structure and the other doesn't.
Without changes:
Sort the user input:
pickedNumbers.sort();
and start comparing. By sorting the values beforehand you know when you can back out and continue with the next set of numbers, i.e. you can back out early and don't have to compare all the values (in the average case).
function wins(picked, winning) {
var winningSet = [];
for (var i = 0; i < winning.length && winningSet.length < 3; i++) {
var set = winning[i];
winningSet = [];
var j = 0;
var k = 0;
while (j < set.length && k < picked.length && winningSet.length < 3) {
if (picked[k] === set[j]) {
winningSet.push(set[j]);
j++; // advance to next element in winning set
} else if (picked[k] > set[j]) {
// continue with the next set
break;
}
// maybe the next element in players picks will match
k++;
}
}
return winningSet.length === 3 ? winningSet : false;
}
The worst case scenario of this solution is O(n*m*l), but since the input is sorted, the average case will be better.
DEMO
With Array#some and Array#every the code becomes much more concise, though it looses the advantage of using sorted input. If your arrays are small it won't make a difference though:
function wins(picked, winning) {
return winning.some(function(set) {
return set.every(function(val) {
return picked.indexOf(val) !== -1;
});
});
}
It also won't give you the actual numbers that matched. The runtime is the same.
The second way would be to build some kind of trie instead of using an array of arrays:
var allwinning = {
'000': {
'010': {
'020': true
},
'100': {
'200': true
},
// ...
}
};
The structure should also be sorted, i.e. the keys of a level are all smaller then the keys of its sublevel etc.
Sort the user input as well and iterate over it. Whenever you found a matching key, you go one level deeper until you have three matches:
function wins(picked, winning) {
var winningSet = [];
for (var i = 0; i < picked.length && winningSet.length < 3; i++) {
if (picked[i] in winning) {
winningSet.push(picked[i]);
winning = winning[picked[i]];
}
}
return winningSet.length === 3 ? winningSet : false;
}
This solution has the worst case scenario of O(n), where n is the number of values the user picked (not taking into account the time it takes to test whether an object contains a specific property name. Often this is assumed to constant).
DEMO

destroying an array of CCPhysicsSprites

In my code below, which I call in the update method, the CCPhyscisSprites are removed and their bodies are destroyed when array elements are off the screen. I put a CCLOG to check the array count and I always get 1 when all the sprites are off screen. Though I don't see the sprite, it's most likely still around. What could be the cause and how can I solve it?
-(void)ballScheduler {
if (ballArray != NULL) {
for (int i = 0; i < ballArray.count; i++) {
CCLOG(#"ball array count is %d", ballArray.count);
CCPhysicsSprite* ballPhysicsSprite = [ballArray objectAtIndex:i];
b2Vec2 ballForce = b2Vec2(forceX, forceY);
ballPhysicsSprite.b2Body->ApplyForce(ballForce, ballPhysicsSprite.b2Body->GetWorldCenter());
if (ballPhysicsSprite.position.x < -ballPhysicsSprite.contentSize.width/2) {
ballWorld->DestroyBody(ballPhysicsSprite.b2Body);
ballPhysicsSprite.b2Body = NULL;
[ballArray removeObject:ballPhysicsSprite];
[ballBatchNode removeChild:ballPhysicsSprite];
}
}
}
}
Do not remove objects from array while iterating over it.

Resources