1
votes

I'm new to SpriteKit, and I'm not sure if what I'm trying to accomplish is not already possible with built-in objects such as SKPhysics and SKRegion.

In short, I need an event handler to trigger a custom subclass of an SKSpriteNode once an enemy SKSpriteNode enters within a certain range.

I'm building an RTS and so far the biggest problem I've been facing for a few days now is how to have 2 SKSpriteNodes detect whether or not they are in close range of each other.

Right now, my solution is rather hacky it seems. Before an SKSpriteNode in my SKScene moves 50 or -50 points by X or Y anywhere in the scene, it will first check if that movement is permitted.

Here's an example for my abstract class "PathFindingUnit":

override func OrderUnitToMoveOneStepLEFT() -> Bool {
    if isDead == true { return false }
    sprite.playFaceLeftAnimation()
    angleFacing = UnitFaceAngle.Left

    updateMovementBlockerPosition()
    let destination = round(sprite.position.x) - UnitDefaultProperty.Movement.Range
    var pointDestination = sprite.position
    pointDestination.x = destination


    if thereIsAnObstacleInTheWay(pointDestination) == false {
        activateEnemiesNearby(pointDestination)
        sprite.playWalkLEFTAnimation()

            self.sprite.runAction(SKAction.moveToX(self.roundToFifties(destination), duration: 0.2))


        angleFacing = UnitFaceAngle.Left
        unitDidMove(pointDestination)

        return true
    } else {
        return false
    }
}

If this method returns true, the unit's sprite will perform an SKAction to move -50 on the X dimension. Otherwise, nothing will happen.

This is how the unit sprite determines whether or not it's okay to move:

func thereIsAnObstacleInTheWay(destination: CGPoint) -> Bool {
    let getNodesAtDestination = ReferenceOfGameScene!.nodesAtPoint(destination)
    for node in getNodesAtDestination {
        if node is SKBlockMovementSpriteNode {
            return true
        }
    }
    return false
}

For the most part, it's about as powerful as the pathfinding is for units in Warcraft II. But whenever I try to improve upon this method, I'm constantly running into:

Exception: EXC_BAD_ACCESS (code=1, address=0xf91002028))

I started getting this issue a lot as soon as I implemented: activateEnemiesNearby(pointDestination)

Since the unit self, will activate other units and order them to attack self.

So in conclusion, should Units with an SKSpriteNode in an SKScene be calling methods of other Units with a similar SKSpriteNode by invoking the enemy unit's MOVE_TO_POINT method within itself by using reference variables about the enemy unit within itself?

I tried using SKPhysics which worked somewhat well, but the SKSpriteNodes position jitters due to the collision physics effects that happen when 2 SKSpriteNodes touch.

I thought about using SKRegion, but I had a lot of trouble figuring out how they are invoked and inserted into the SKScene, and the official apple docs on SKRegion seems very limited: https://developer.apple.com/library/ios/documentation/SpriteKit/Reference/SKRegion_Ref/

Are SKRegions best for "Unit sight"? in order for Units to detect that an enemy is approaching?

1
SKRegion is not what you want, that won't send any events, it is used to define if a point lies in a region, so you would be using this to determine sprite to ground, if you want to see if 2 sprites are within range, then SKPhysicsBody could help, but may be expensive. if not done correctlyKnight0fDragon
by default collision mask is set to 0xFFFFFFFF on all bodies,set it to 0Knight0fDragon

1 Answers

0
votes

I'm incline to use SKPhysicsBody to detect if a body collide with a specific zone marked with another SKPhysicBody, for example a zone created with a specific CGPath so you can do:

SKPhysicsBody.init(edgeLoopFromPath: zonePath!) // zonePath is a CGPath

however if you don't want to use it there is also this little function that could help you with your calculations:

func getDistance(p1:CGPoint,p2:CGPoint)->CGFloat {
    let xDist = (p2.x - p1.x)
    let yDist = (p2.y - p1.y)
    return CGFloat(sqrt((xDist * xDist) + (yDist * yDist)))
}

About this last way, you could find useful also these methods:

/* Return true if `point' is contained in `rect', false otherwise. */

@available(iOS 2.0, *)
public func CGRectContainsPoint(rect: CGRect, _ point: CGPoint) -> Bool

/* Return true if `rect2' is contained in `rect1', false otherwise. `rect2'
   is contained in `rect1' if the union of `rect1' and `rect2' is equal to
   `rect1'. */

@available(iOS 2.0, *)
public func CGRectContainsRect(rect1: CGRect, _ rect2: CGRect) -> Bool

/* Return true if `rect1' intersects `rect2', false otherwise. `rect1'
   intersects `rect2' if the intersection of `rect1' and `rect2' is not the
   null rect. */

@available(iOS 2.0, *)
public func CGRectIntersectsRect(rect1: CGRect, _ rect2: CGRect) -> Bool