sprites are not removed - swift - ios

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.

Related

How to compare values within two List<List<CustomClass>> in Dart?

I have a 2048-type game where the gameboard grid is made up of a list of 4 other lists (each of which contain 4 Tiles).
Each time a move is made, the newGameboardGrid is saved in yet another list (so that removeLast can be called when a player wants to undo a move).
When a player swipes, I need to compare the newGameboardGrid grid with the previous one to see if any actual movement took place or if the tile values are still the same (as would happen if a player swiped in a direction where no movement was possible).
I tried this code:
if (newGameboardGrid == listOfGameboardGrids.last) {
// do something
}
It almost works in that it is comparing the <List<List< Tile>> from the new move with the <List<List< Tile>> of the last move, but the problem is that it never results in the two <List<List< Tile>> as being equal, even when all the tile values are identical. I believe it's because it is comparing hashcodes and/or other things.
The Tile class has lots of stuff in it, but the thing I would like to compare is that int named "value". Is it possible to compare only the "value" variable for these two <List<List< Tile>>?
Thanks in advance for any help! (And apologies if any of my terms are imprecise.)
Dart list implementations do not override the operator== of Object, so they are only ever equal to themselves. The elements are not checked.
IF you want to compare the elements (and here, the elements of the list elements), you can use the Equality classes from package:collection:
const boardEquality = ListEquality(ListEquality(DefaultEquality()));
This creates a ListEquality object which compares two lists of lists of elements by checking that they contain the same number of lists, which again contains the same number of equal elements. (I assume that Tile implements operator==).
You can the use it as: if (boardEquality.equals(newGameboardGrid, listOfGameboardGrids.last)) { ... }.
You could do an extension method on your particular list type and make an isEqual method for that compares each Tile:
extension TileListComp on List<List<Tile>> {
bool isEqual(List<List<Tile>> other) {
if(this.length != other.length || this[0]?.length != other[0]?.length) {
return false;
}
for(int x = 0; x < this.length; x++) {
for(int y = 0; y < this[0].length; y++) {
if(this[x][y] != other[x][y]) {
return false;
}
}
}
return true;
}
}
If you have not implemented any kind of comparison for your Tile class, you will need to do so. I can advise if necessary.

Generation random (positive and negative) numbers for a quiz

I am writing a Math Quiz app for my daughter in xcode/swift.Specifically, I want to produce a question that will contain at least one negative number to be added or subtracted against a second randomly generated number.Cannot be two positive numbers.
i.e.
What is (-45) subtract 12?
What is 23 Minus (-34)?
I am struggling to get the syntax right to generate the numbers, then decide if the said number will be a negative or positive.
Then the second issue is randomizing if the problem is to be addition or subtraction.
It's possible to solve this without repeated number drawing. The idea is to:
Draw a random number, positive or negative
If the number is negative: Draw another number from the same range and return the pair.
If the number is positive: Draw the second number from a range constrained to negative numbers.
Here's the implementation:
extension CountableClosedRange where Bound : SignedInteger {
/// A property that returns a random element from the range.
var random: Bound {
return Bound(arc4random_uniform(UInt32(count.toIntMax())).toIntMax()) + lowerBound
}
/// A pair of random elements where always one element is negative.
var randomPair: (Bound, Bound) {
let first = random
if first >= 0 {
return (first, (self.lowerBound ... -1).random)
}
return (first, random)
}
}
Now you can just write...
let pair = (-10 ... 100).randomPair
... and get a random tuple where one element is guaranteed to be negative.
Here's my attempt. Try running this in a playground, it should hopefully get you the result you want. I hope I've made something clean enough...
//: Playground - noun: a place where people can play
import Cocoa
let range = Range(uncheckedBounds: (-50, 50))
func generateRandomCouple() -> (a: Int, b: Int) {
// This function will generate a pair of random integers
// (a, b) such that at least a or b is negative.
var first, second: Int
repeat {
first = Int(arc4random_uniform(UInt32(range.upperBound - range.lowerBound))) - range.upperBound
second = Int(arc4random_uniform(UInt32(range.upperBound - range.lowerBound))) - range.upperBound
}
while (first > 0 && second > 0);
// Essentially this loops until at least one of the two is less than zero.
return (first, second)
}
let couple = generateRandomCouple();
print("What is \(couple.a) + (\(couple.b))")
// at this point, either of the variables is negative
// I don't think you can do it in the playground, but here you would read
// her input and the expected answer would, naturally, be:
print(couple.a + couple.b)
In any case, feel free to ask for clarifications. Good luck !

Strange SpriteKit For Loop Bug

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.

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.'

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