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.
Related
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--;
}
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.
I have two arrays, namely combo and truecombo. The user fills the combo with MovieClips by clicking on various buttons on the stage, truecombo is the correct combination.
At any given point (enterFrame) Flash is checking whether the two are the same, if yes, then do some stuff. For the time being this is my code (altered several times, like with Typecasting the indices, adding .parent at the end of combo[o] etc. 2 things will happen, either one or the other.
Either the statement will not be satisfied, at which point the adding and chopping of the combo array will continue, or the condition will be instantly met when combo.length = 6. Check my code.
UPDATE: I have a dropbox file with my current code. Click this for FLA link and here is the SWF link stripped down as always for ease and security.
/*stage.*/addEventListener(Event.ENTER_FRAME, checkthis);
function checkthis(e:Event)
{
for(var o:int=0;o<= combo.length; o++)
{
if((combo[o] == truecombo[o]) && (combo.length==truecombo.length))
{
equal=true;
}
}
if (equal==true)
{
stage.removeEventListener(Event.ENTER_FRAME, checkthis);
endSeq();
}
}
function endSeq():void
{
bravo.play();
for (var i:int = 0; i < combo.length; i++)
{
var element:DisplayObject = combo[i];
element.parent.removeChild(element);
}
firebb.gotoAndPlay(2);
windbb.gotoAndPlay(2);
spiritbb.gotoAndPlay(2);
earthbb.gotoAndPlay(2);
}
This is how I push my new elements to the combo array.
function add(element:DisplayObject)
{
twist.gotoAndPlay(2);
element.width = WIDTH;
element.height = HEIGHT;
if (this.combo.length >= MAX_ELEMENTS)
{
removeChild(this.combo.shift());
}
this.combo.push(element as DisplayObject);
this.addChild(element);
this.reorder();
}
function reorder()
{
for (var i:int = 0; i < combo.length; i++)
{
var element:DisplayObject = combo[i];
element.x = OFFSET_X + (i * SEP_X);
element.y = OFFSET_Y;
}
}
And this is how I have my truecombo and its contents created.
var fireb:firebtn = new firebtn();
var spiritb:spiritbtn = new spiritbtn();
var earthb:earthbtn = new earthbtn();
var windb:windbtn = new windbtn();
var combo:Array=new Array();
const truecombo:Array = [fireb,windb,spiritb,windb,earthb,fireb];
Sorry for the lack of comments, I'd guess it's pretty self-explanatory. Thanks in advance.
I believe combo[o] & truecombo[o] are two instances of the same class & you want them to be matched. If that is the case you may consider :
getQualifiedClassName(combo[o]) == getQualifiedClassName(truecombo[o])
To match the way you did, you must ensure the objects lying inside truecombo be referring to the same ones on stage & not new instances.
EDIT:
It seems you do not break the loop when the match is a success. Use this instead :
function checkthis(e:Event)
{
for(var o:int=0;o<= combo.length; o++)
if((combo[o] == truecombo[o]) && (combo.length==truecombo.length)) {
equal=true;
break;
}
if (equal) {
stage.removeEventListener(Event.ENTER_FRAME, checkthis);
endSeq();
}
}
Here's a really simple loop:
var equal:Boolean=true
if(combo.length == truecombo.length) {
for(var i:int=0; i<combo.length; i++) {
if(combo[i] != truecombo[i]) {
equal=false;
break;
}
}
} else {
equal=false;
}
if(equal) {
//do whatever
}
This assumes both are equal, until we find out otherwise. So if the lengths are different, they are not equal. If the ith element is different, they are not equal.
In the end, you check if your flag equal is true and do whatever you want to do.
I have an array of sprites called segments and I would like to skip the first element of segments in my for each loop. I'm doing this at the moment:
var first = true;
for each (var segment in this.segments)
{
if(!first)
{
// do stuff
}
first == false;
}
Is there a better way to do this? Thanks!
if its an array why not just:
for(var i:int = 1; i < this.segments.length; i++)
{
}
This can also be done via "slice".
For example
for (var segment in this.segments.slice(1))
{
}
Array#slice will copy the array without the first element.
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.