Strange SpriteKit For Loop Bug - ios

I am encountering a very strange bug that I just can't figure out. In my game, you are driving a car on a 2D plane trying to avoid obstacles (like a rock or car). The obstacles can appear on 3 different paths. Sometimes there is just one obstacle on any of the paths or sometimes there is two obstacles on two of the three paths.
The problem I am experiencing is that when two obstacles appear and then get deleted when they go off the screen, one of the obstacles currently moving on the screen is also "deleted" (I know this because the console prints "deleted object" 3 times). But, the obstacle that is randomly "deleted" is not removed from view. Instead, it freezes where it previously was and never goes away.
Here is the code block I am having problems with:
var canGetScore = true
for (num,obj) in enumerate(obstaclesToMove) {
obj.position.y -= CGFloat(gameSpeed)
if obj.position.y <= userCar.position.y && obj.name == "NotPassed" {
obj.name = "Passed"
if canGetScore {
canGetScore = false
score += 1
scoreLabel.text = "\(score)"
}
}
if obj.position.y <= -CGRectGetMidY(self.frame) {
obj.removeFromParent()
obstaclesToMove.removeAtIndex(num)
println("deleted object")
}
}
Variable Key:
"obstaclesToMove" is an array of obstacles(SKSpriteNodes) that should be moved down the screen.
"gameSpeed" is an integer of how many pixels the screen moves every frame (the value is 5 in this instance).
"userCar" is the character that the user controls (SKSpriteNode).
The "canGetScore" thing is to eliminate another bug I was experiencing before. I know for a fact this is not causing the current bug I am having.
Does anyone have any ideas to why this is happening? If you need any more explanation just ask.
EXTRA NOTE: The user's car technically never moves, but the background and the obstacles move.

I had the same thing to implement in my game which is very similar to yours, so I guess that everything I've done can be applied to your situation. What I did instead of using indexes to remove obstacles from array of obstacles is that I've used addObject and removeObjectIdenticalTo methods(self.obstacles is NSMutableArray):
[self.obstacles addObject:asteroide]; //asteroide is subclass of SKSpriteNode
[self.obstacles addObject:enemyShip];//enemyShip is subclass of SKSpriteNode
And when I removing them, I just do something like:
[self.obstacles removeObjectIdenticalTo:asteroide];
[self.obstacles removeObjectIdenticalTo:enemyShip];
This worked for me... One more thing I would like to point is how you can remove obstacles when offscreen in a different way than yours. This is how I do that:
SKAction *sequence = [SKAction sequence:#[moveAsteroide,[SKAction runBlock:^{[asteroide removeFromParent]; [self.obstacles removeObjectIdenticalTo:asteroide];
}]]];
[asteroide runAction:sequence withKey:#"moving"];
So I run the sequence which first move object to desired location, and that is some point which is offscreen (y = -obstacle.size.height) and after that, I remove it from parent and update obstacles array. This works for me because I use actions to move obstacles, but maybe it can work for you if you are moving obstacles in same way. I like this more than constantly checking the location of obstacle in update method.
This is just my way though, and all this can be done in many ways, and you have to choose which one suits you better. Hope this make sense and helps a bit :-)

It seems to me that the problem is in how you remove your object from obstaclesToMove
Imagine you have three objects in your array:
obstaclesToMove = [object1, object2, object3]
You have to remove object 1 & 2 because they have to go off screen, so what you do in your code is first remove object 1 then object 2, except in your code it translates to :
obstaclesToMove.removeAtIndex(0)
obstaclesToMove.removeAtIndex(1)
You would think this isn't a problem, except look what happens to your array while you do that:
obstaclesToMove = [object1, object2, object3]
obstaclesToMove.removeAtIndex(0)
// obstaclesToMove = [object2, object3]
obstaclesToMove.removeAtIndex(1)
// obstaclesToMove = [object2]
Usually it is a bad idea to remove object from an array that you are looping through. What you could do to make it safer is:
Collect all the index you need to remove in an array
Sort them in reverse order
Delete the items from the array
You could do it like that:
var indexToDelete: [MyObstacleObject] = []
var canGetScore = true
for (num,obj) in enumerate(obstaclesToMove) {
obj.position.y -= CGFloat(gameSpeed)
if obj.position.y <= userCar.position.y && obj.name == "NotPassed" {
obj.name = "Passed"
if canGetScore {
canGetScore = false
score += 1
scoreLabel.text = "\(score)"
}
}
if obj.position.y <= -CGRectGetMidY(self.frame) {
indexToDelete.append(num)
}
}
indexToDelete.sort({ $0 > $1 })
for item in indexToDelete {
obstaclesToMove[item].removeFromParent()
obstaclesToMove.removeAtIndex(item)
println("deleted object")
}
That way when you delete items from your array you can be sure that they will still have the right index since you will be first deleting items with a higher index.

Related

sprites are not removed - swift

I am trying to remove some sprites, but not all are removed. Please check the code below.
func removeSquare(squareSprite : SKNode) {
for (index, value) in squares.enumerate() {
if Int(value.sprite.position.x) == Int(squareSprite.position.x) {
for i in 0..<2 {
if index - i >= 0 {
squares[index - i].sprite.removeFromParent()
squares.removeAtIndex(index - i)
print("index - i is \(index - i)")
print("squares.count is \(squares.count)")
}
}
}
}
When i is greater than zero the sprite is not removed, otherwise when it's zero, it removes the sprite.
squaresis an array containing square sprites which have a sprite property of type SKSpriteNode. I checked the count of squares also and the number reduces appropriately, but the sprite is still on the screen.
The issue is that you are reducing your array while iterating through it. Try to comment this line out, to check if your algorithm is working in principle and the correct sprites are removed:
squares.removeAtIndex(index - i)
If yes, change your algorithm to start at the end of the array
It sounds like the indexes at squares will change since you are doing squares.removeAtIndex(index - i), making your array smaller with each iteration, so i at 1 becomes 0 in the next loop, you want to go in reverse direction 1..>=0 so that the array becomes smaller without changing the indexes for the objects in the array.

SpriteKit: detect complete node overlap

I have two SKShapeNodes – one with an edge-based SKPhysicsBody, one volume-based – and I want to detect their intersection without collision. I've got this working fine, with the SKPhysicsContactDelegate contact methods getting called as one passes over another, but my issue is that didEndContact gets called when the edges no longer intersect, even when one body is completely contained within the other. What's the best way to determine true contact or overlap, not just edge intersection? I've tried usesPreciseCollisionDetection, to no avail.
CGPoint locObj1 = [sprite1 locationInNode:self];
CGPoint locObj2 = [sprite2 locationInNode:self];
if([sprite1 containsPoint: locObj2]) return;
if([sprite2 containsPoint: locObj1]) return;
Add this to the beginning of didBeginContact and didEndContact. This checks to see if one of the nodes contains the other node. If it does, it does nothing which will alleviate your issue of didBeginContact and didEndContact being unessesarily called. I am not on my mac so you may need to play with the syntax a bit. Hope this sends you in the right direction.
As meisenman suggested, it looks like the best way to do this is using the containsPoint method to determine true node overlap. The docs state that this "returns a Boolean value that indicates whether a point lies inside the node's bounding box", but in my experimentation it looks like this works for concave edge-based shapes as well.
So if I want to detect when two objects no longer overlap, it makes sense to do a containsPoint check within didEndContact – but it turns out didEndContact gets called when each edge is no longer intersected, even if another edge of the same shape still intersects the same object. For example, a ball exiting a rectangle through its corner will yield two didEndContact events. Therefore, it's necessary to keep an absolute count of contact events (the difference between begin and end), and only test for containment when this count is zero.
My solution, in Swift:
var _contactCount = 0
func didBeginContact(contact: SKPhysicsContact!) {
if ((contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask) == (CATEGORY_ONE | CATEGORY_TWO)) {
if (_contactCount == 0) {
// Contact is actually beginning
}
_contactCount += 1
}
}
func didEndContact(contact: SKPhysicsContact!) {
if ((contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask) == (CATEGORY_ONE | CATEGORY_TWO)) {
_contactCount -= 1
let overlap = contact.bodyA.node.containsPoint(contact.bodyB.node.position) || contact.bodyB.node.containsPoint(contact.bodyA.node.position)
if (!overlap && _contactCount == 0) {
// Contact is actually ending
}
}
}
These are my thoughts on #meisenman example for swift 3. Instead of collision detection through masks, let's say we wanted to to know if an node is inside a node. With #meisenman, the location of the node is being used for each.
This following image is what we actually what I want done.
The code #meisenman uses does not require SKPhysics. Here is the code in Swift 3.0
let Object_1: CGPoint! = Sprite_Object_1.position
//this obtains the location of the sprite node, through CGPoint.
if Object_Sprite_2.contains(Object_1){
print("Then it is true, object 2 is within the location of object 1")
}
The only limitations that I have found are that if the center of these nodes are not within the body of the desired node, then this if statement will not be true. This has not been tested by setting the node's body defination (mask).
What I like, is having the ability to use this if statement throughout different functions or overide fuctions, this is not limited to only 'did begin' or 'touches moved.'

Attempt to index global 'player' (a nil value)

I'm getting a (a nil value) error when i try to do this :
player = display.newSprite( imageSheet, "sequenceDataPlayer"..math.random(1, 7) )
Looking at a test print :
print ("sequenceDataPlayer"..math.random(1, 7) )
It prints the data oky 'sequenceDataPlayer1'
What Im i doing wrong here ?
Your print statement is just printing the string "sequenceDataPlayer" concatenated with a random number between 1 and 7.
It took me a little while to figure out how to use sprites in Corona, but here's how I do it. I'll use Player for the variables since that's what you're using.
First I create an options variable to get the frames from my Player.lua file:
optionsPlayer =
{
frames = require("player").frames,
}
Then I create a variable for the image sheet:
playerSheet = graphics.newImageSheet( "player.png", optionsPlayer )
After that, I create a variable to set up the name, the sequence of frames, the time it takes to play, and set how many times it will loop:
spriteOptionsPlayer = { name="Player", start=1, count=10, time=500, loopCount = 1}
Finally, I create the new sprite:
spriteInstancePlayer = display.newSprite( playerSheet, spriteOptionsPlayer )
Once I've done all this, I usually set up the x and y positions, xScale and yScale, and other properties along with adding it to a display group.
Last of all, then I play the sprite somewhere:
spriteInstancePlayer:play()
From what it looks like, you want to have 7 different sprites to choose from. Personally, I would just create seven different sprites using all of the steps above and then put them in a table.
sprites = { spriteInstancePlayer, spriteInstancePlayer2, spriteInstancePlayer3, etc.. }
Then when I wanted to play them, I would set the position and visibility and just do:
r = math,random(1, 7)
sprites[r].x = x position
sprites[r].y = y position
sprites[r].isVisible = true
sprites[r]:play()
Of course, then I would want to set listeners to either completely remove the sprite or set the visibility to false when it's done playing, there's a collision(you'd have to add a physics body and set that all up), or whatever else might happen...
There are probably simpler ways to do it, but that's what I do.
Hope this helps.

Stop in front of obstacles in Cocos3d

I already know how to check for collisions with the doesintersectNode-method in Cocos3d, but in my case, I want to avoid obstacles, before I get in touch with them. In example, I want to stop in front of a wall, before I crash against it.
For this reasons I wrote the methods getNodeAtLocation in my subclass of CC3Scene and -(BOOL)shouldMoveDirectionallywithDistance:(float)distance in the class of my person, which should move around.
Unfortunately, I have some problems with the algorithm of the last method. Here the code:
-(BOOL)shouldMoveDirectionallywithDistance:(float)distance
{
BOOL shouldMove = NO;
float x = self.person.globalLocation.x;
float z = self.person.globalLocation.z;
int times = 5;
for (int i = 0; i < times; i++) {
CC3Vector newPos = cc3v(x, 0.5, z);
CC3PODResourceNode *obstacle = (CC3PODResourceNode *)[myScene getNodeAtLocation:newPos];
if (obstacle) {
return NO;
}else{
shouldMove = YES;
}
x += self.person.globalForwardDirection.x * distance / times;
z += self.person.globalForwardDirection.z * distance / times;
}
return shouldMove;
}
In this method, I get the important parts of the coordinates (for my proposal just the x- and z-values) and increase them by a fifth of the forwardDirection. I decided, that this makes sense, when the obstacle is i.e. a thin wall. But for reasons I don't know, this method doesn't work, and the person is able to walk through this wall. So where is the problem in my code?
I strongly believe, that the getNodeAtLocation-method works correctly, as I tested it multiple times, but maybe there are my mistakes:
-(CC3Node *)getNodeAtLocation:(CC3Vector )position
{
CC3Node *node = nil;
for (CC3PODResourceNode *aNode in self.children) {
if ([aNode isKindOfClass:[CC3PODResourceNode class]] ) {
for (CC3PODResourceNode *child in aNode.children) {
if (CC3BoundingBoxContainsLocation(child.globalBoundingBox, position)) {
node = aNode;
}
}
}
}
return node;
}
To conclude, in my view the mistake is in the -(BOOL)shouldMoveDirectionallywithDistance:(float)distance-method. I suppose, that something is wrong with the increase of the x- and z-values, but I couldn't figure out, what exactly is incorrect.
If you are still interested in finding an answer to this problem. I may be able to provide you with an alternative solution. I am about to release free source for a 3d collision engine I ported to cocos3d, and it will give you more flexibility than simply stoping an object in front of another.
I am currently polishing out the code a little for easy use, but if you are interested you can email me to: waywardson07#aol.com
you could also get a little preview of the engine in action here: http://www.youtube.com/watch?v=QpYZlF7EktU
Note: the video is a little dated.
After several attempts, it appears to be easier as I thought:
Just using the doesIntersectNode method has the right effect for me.
But please notice, that this is not a real solution to the problem of stopping in front of obstacles.

multi image collision detection

heres the problem
i have 5 balls floating around the screen that bounce of the sides, top and bottom. thats working great.
what i want to do now is work out if any of them collide with each other.
i know about
if (CGRectIntersectsRect(image1.frame, image2.frame))
{
}
but that only checks two images, i need to check all and each of them..
ive checked everywhere but cant find the answer, only others searching the same thing, any ideas?
thanks in advance
Spriggsy
edit:
im using this to find the CGRect and store it in an array
ball1 = NSStringFromCGRect(image1.frame);
ball2 = NSStringFromCGRect(image2.frame);
ball3 = NSStringFromCGRect(image3.frame);
ball4 = NSStringFromCGRect(image4.frame);
ball5 = NSStringFromCGRect(image5.frame);
bingoarray = [NSMutableArray arrayWithObjects:ball1,ball2,ball3,ball4,ball5,nil];
this then gets passed to a collision detection method
-(void)collision {
for (int i = 0; i<[bingoarray count]-1 ; i++) {
CGRect ballA = CGRectFromString([bingoarray objectAtIndex:i]);
if (CGRectIntersectsRect(ballA, image1.frame)) {
NSLog(#"test");
}
}
this i guess should check one ball against all the others.
so ball 1 gets checked against the others but doesnt check ball 2 against them. is this nearly there?
}
The ideal solution is to store all the rectangles into a interval tree or a segment tree in order to efficiently compute any overlapping areas. Note that you will have to generalize to 2 dimensions for your use case.
Another efficient approach would be to use a K-d tree to find the nearest other balls and compare against the nearest neighbor until there isn't a collision.
The simple approach is to simply iterate over all the balls and compare them to all other balls with a higher ID (to avoid double checking ball1 -> ball2 and ball2 -> ball1).
Since you only have 5 at once, the iterative approach is likely fast enough to not be dropping frames in the animation, but you should consider a more scalable solution if you plan to support more balls since the simple appreach run in quadratic time.
That is a fun little math problem to avoid being redundant.
You can create an array of the images. And loop through it, checking if each member collides with any successive members.
I can spell it out more with code if need be.
EDIT I couldn't resist
// the images are in imagesArray
//where you want to check for a collision
int ballCount = [imagesArray count];
int v1Index;
int v2Index;
UIImageView * v1;
UIImageView * v2;
for (v1Index = 0; v1Index < ballCount; v1Index++) {
v1 = [imagesArray objectAtIndex:v1Index];
for (v2Index = v1Index+1; v2Index < ballCount; v2Index++) {
v2 = [imagesArray objectAtIndex:v2Index];
if (CGRectIntersectsRect(v1.frame, v2.frame)) {
// objects collided
// react to collision here
}
}
}

Resources