Get a list of nodes in a specific area?

I work in a game with side games, and I need to know which nodes are in the area in order to implement something like β€œline of sight”. Right now, I'm trying to use enumerateBodyiesInRect (), but it detects bodies that have 20px or more of the estimated rect, and I cannot understand why this is so inaccurate.

Here is what I am doing now:

import SpriteKit import CoreMotion class GameScene: SKScene, SKPhysicsContactDelegate { var player = SKShapeNode() var world = SKShapeNode() var rShape = SKShapeNode() override func didMoveToView(view: SKView) { self.physicsWorld.contactDelegate = self self.scaleMode = SKSceneScaleMode.AspectFit self.size = view.bounds.size // Add world world = SKShapeNode(rectOfSize: view.bounds.size) world.physicsBody = SKPhysicsBody(edgeLoopFromPath: world.path) world.position = CGPointMake(self.frame.size.width/2, self.frame.size.height/2) // Move camera self.addChild(world) // Add player player = SKShapeNode(rectOfSize: CGSize(width: 25, height: 25)) player.physicsBody = SKPhysicsBody(rectangleOfSize: player.frame.size) player.physicsBody.dynamic = false player.strokeColor = SKColor.blueColor() player.fillColor = SKColor.blueColor() player.position = CGPointMake(90, -50) world.addChild(player) } override func update(currentTime: CFTimeInterval) { // Define rect position and size (area that will be evaluated for bodies) var r : CGRect = CGRect(x: 200, y: 200, width: 25, height: 25) // Show rect for debug rShape.removeFromParent() rShape = SKShapeNode(rect: r) rShape.strokeColor = SKColor.redColor() self.addChild(rShape) // Evaluate rect rShape.fillColor = SKColor.clearColor() self.physicsWorld.enumerateBodiesInRect(r) { (body: SKPhysicsBody!, stop: UnsafePointer<ObjCBool>) in self.rShape.fillColor = SKColor.redColor() // Paint the area blue if it detects a node } } } 

This code should show the estimated rectangles and rays on the screen (for debugging purposes) and color them red if they touch the node player. However, in the screenshot you can see how it turns red when the player is at a distance of 25 pixels or more from him, he likes it if the picture is slightly discharged or less than the actual area that is estimated. You can copy it to the project to duplicate the problem.

Could this be because it's just beta, or am I doing something wrong?

The rect should only be red only if the player is inside it (blue square), however it's red when the player is near

+6
source share
6 answers

You create a physical world in which there is a certain rectangle with "special properties" - this is the rectangle that you use in enumerateBodiesInRect() . Why not create an invisible, inert physical body with the required rectangular size, and then use SKPhysicsBody to check for collisions and / or contacts? You can then use allContactedBodies() or some delegate callbacks to find out that other bodies are inside your special rectangle.

Think of it as a "tractor beam" or "warp rectangle."

+1
source

I believe that you need an instance method of SKPhysicsWorld enumerateBodyiesInRect() that will iterate over all the nodes in this rectangle. If you want to get into the world of physics through your scene, use may look like this:

 self.physicsWorld.enumerateBodiesInRect(CGRect(x: 0, y: 0, width: 50, height: 50)) {(body: SKPhysicsBody!, stop: UnsafePointer<ObjCBool>) in // enumerates all nodes in given frame } 
+1
source

I already experimented a bit with enumerateBodiesInRect , and I found it incredibly inaccurate. It seems that it doesn’t have any declared functionality, but instead gets a random result. I honestly cannot even identify any sample from my products.

enumerateBodiesAlongRay seems better, but still very buggy. The problem with this function, apparently, is the conversion between the coordinates of the screen and physics. I would have avoided that too.

I think your solution should simply be to use your existing contact detection system. All necessary functions can be written in the didBeginContact() and didEndContact() functions. This has the added benefit of allowing you to specify various functionalities for entering and exiting the area. You can also add particle effects, animations, etc., and also intentionally ignore certain types of nodes.

The only thing needed for the success of this method is to clarify that the contact area has a unique category, that contactTestBitMask contains all the nodes you need, and collisionBitMask contains 0.

+1
source

There is only the nodesAtPoint: method.

To achieve what you want, you better save all the enemies in an array and have an int variable, something like nextEnemyIndex . This approach allows you to easily return the next enemy node, it is much more effective than trying to find a node on the stage.

0
source

Yes, there may be a problem due to the image of your player, for example, try reducing the body size by 10 pixels:

 player.physicsBody = SKPhysicsBody(rectangleOfSize: CGRectMake(self.frame.origin.x, self.frame.origin.y, self.size.width-10, self.size.height-10))); 
0
source

The enumerateBodiesInRect SKPhysicsWorld method expects the rect parameter to be in the coordinates of the scene . It is important. If you have a hierarchy of node scenes, you need to convert the rectangle that you compute from the node link to the scene coordinates.

I ran into a lot of problems with this method, returning those bodies that were disabled by values ​​such as 30px to the left, etc., and finally realized that the problem is with the rect parameter, which is not defined in the coordinate space of the scene.

In my case, I had a worldNode inside my scene and all objects were created in worldNode. My camera moved the worldNode and used scaling to scale and input it.

To use enumerateBodiesInRect correctly, I had to do something like this:

 // get your world rect based on game logic let worldRect = getWorldRect() // calculate the scene rect let sceneRectOrigin = scene.convertPoint(worldRect.origin, fromNode:scene.worldNode) let worldScale = scene.worldNode.xScale // assert this is not 0 // now to get the scene rect relative to the world rect, in scene coordinates let sceneRect = CGRectMake( sceneRectOrigin.x, sceneRectOrigin.y, worldRect.width / worldScale, worldRect.height / worldScale) world.physicsWorld.enumerateBodiesInRect(sceneRect) { // your code here } 

Hope this helps.

0
source

Source: https://habr.com/ru/post/972533/


All Articles