I would like to test which nodes are near another particular node.
In the following example, the green field is used to check which fields are nearby. It should return the yellow, orange (horizontal to green and only one field removed) and blue (diagonal and only one field removed) field.
Each field is a node:
let greenField = SKSpriteNode(imageNamed: "greenField")
greenField.zPosition = 1
let yellowField = SKSpriteNode(imageNamed: "yellowField")
yellowField.zPosition = 1
...
Does anyone have any idea how to test this? I would be very grateful for any answer.
You can check the distance of each node.
CGFloat distance = hypotf(p1.x - p2.x, p1.y - p2.y);
Tip: Make sure the anchor point is in the center to make it easier.
Related
I'm using code from the pymunk index_video to create a generic function that creates multiple cars which race each other and if they reach the right extreme of the screen, they are removed from Space and re-generated on the left extreme of the screen.
The problem is, that in the example code, each part of the car (chassis, pin joint, motor, wheels) is added to Space separately. I wanted to treat the entire car as a single body whose coordinates I can keep track of by storing the reference of the entire car body in a List and add or delete it to the Space easily.
Also, if the wheels are too close to the chassis, they collide with each other. I presume using a ShapeFilter can help avoid such collisions, but for that I need all parts of the car as a single body.
Please bear with me. I'm completely new to this jargon.
def car(space):
pos = Vec2d(100,200)
wheel_color = 52,219,119
shovel_color = 219,119,52
mass = 100
radius = 25
moment = pymunk.moment_for_circle(mass, 20, radius)
wheel1_b = pymunk.Body(mass, moment)
wheel1_s = pymunk.Circle(wheel1_b, radius)
wheel1_s.friction = 1.5
wheel1_s.color = wheel_color
space.add(wheel1_b, wheel1_s)
mass = 100
radius = 25
moment = pymunk.moment_for_circle(mass, 20, radius)
wheel2_b = pymunk.Body(mass, moment)
wheel2_s = pymunk.Circle(wheel2_b, radius)
wheel2_s.friction = 1.5
wheel2_s.color = wheel_color
space.add(wheel2_b, wheel2_s)
mass = 100
size = (50,30)
moment = pymunk.moment_for_box(mass, size)
chassi_b = pymunk.Body(mass, moment)
chassi_s = pymunk.Poly.create_box(chassi_b, size)
space.add(chassi_b, chassi_s)
vs = [(0,0),(25,45),(0,45)]
shovel_s = pymunk.Poly(chassi_b, vs, transform = pymunk.Transform(tx=85))
shovel_s.friction = 0.5
shovel_s.color = shovel_color
space.add(shovel_s)
wheel1_b.position = pos - (55,0)
wheel2_b.position = pos + (55,0)
chassi_b.position = pos + (0,-25)
space.add(
pymunk.PinJoint(wheel1_b, chassi_b, (0,0), (-25,-15)),
pymunk.PinJoint(wheel1_b, chassi_b, (0,0), (-25, 15)),
pymunk.PinJoint(wheel2_b, chassi_b, (0,0), (25,-15)),
pymunk.PinJoint(wheel2_b, chassi_b, (0,0), (25, 15))
)
speed = 4
space.add(
pymunk.SimpleMotor(wheel1_b, chassi_b, speed),
pymunk.SimpleMotor(wheel2_b, chassi_b, speed)
)
So this question is actually two questions.
A. How to make a "car object" that consists of multiple parts
There is no built in support for this, you have keep track of it yourself.
One way to do it is to create a car class that contains all the parts of the car. Something like this (not complete code, you need to fill in the full car)
class Car():
def __init__(self, pos):
self.wheel_body = pymunk.Body()
self.wheel_shape = pymunk.Circle()
self.chassi_body = pymunk.Body()
self.chassi_shape = pymunk.Poly()
self.motor = pymunk.SimpleMotor(wheel_body, chassi_body, 0)
def add_to_space(self, space)
space.add(self.wheel_body, self.wheel_shape, self.chassi_body, self.chassi_shape, self.motor)
def set_speed(self, speed)
self.motor.rate = speed
def car_position(self)
return self.chassi_body.position
B. How to make parts of the car to not collide with each other
This is quite straight forward, just as you already found the ShapeFilter is the way to go. For each "car", create a ShapeFilter and set a unique non-zero group on it. Then set that ShapeFilter as the filter property on each shape that makes up the car. It doesnt matter if the shapes belong to the same body or not, any shape with a ShapeFilter with a group set will not collide to other shapes with the same group set.
Hello I'm trying to spawn bullets at the bottom of my screen to travel upwards but the current code that I have spawns the bullets at the top of the screen. I've tried making the height negative and nothing happened. Here's the code I'm working with, thanks.
let randomBulletPosition = GKRandomDistribution(lowestValue: -300, highestValue: 300)
let position = CGFloat(randomBulletPosition.nextInt())
bullet.position = CGPoint(x: position, y: self.frame.size.height + bullet.size.height)
Some nice conversions will help you.
Now, do not do this all the time, this should be a one and done type deal, like in a lazy property.
First, we want to get the bottom of our view
let viewBottom = CGPoint(x:scene!.view!.midX,y:scene!.view!.frame.maxY) //In a UIView, 0,0 is the top left corner, so we look to bottom middle
Second, we want to convert the position to the scene
let sceneBottom = scene!.view!.convert(viewBottom, to:scene!)
Finally we want to convert to whatever node you need it to be a part of. (This is optional if you want to place it on the scene)
let nodeBottom = scene!.convert(sceneBottom,to:node)
Code should look like this:
let viewBottom = CGPoint(x:scene!.view!.midX,y:scene!.view!.frame.maxY)
let sceneBottom = scene!.view!.convert(viewBottom!, to:scene!)
let nodeBottom = scene!.convert(sceneBottom,to:node)
Of course, this is a little ugly.
Thankfully we have convertPoint and convert(_from:) to clean things up a little bit
let sceneBottom = scene.convertPoint(from:viewBottom)
Which means we can clean up the code to look like this:
let sceneBottom = scene.convertPoint(from:CGPoint(x:scene!.view!.midX,y:scene!.view!.frame.maxY))
let nodeBottom = node.convert(sceneBottom,from:scene!)
Then we can make it 1 line as:
let nodeBottom = node.convert(scene.convertPoint(from:CGPoint(x:scene!.view!.midX,y:scene!.view!.frame.maxY),from:scene!)
As long as the node is available to the class, we can make it lazy:
lazy var nodeBottom = self.node.convert(self.scene!.convertPoint(CGPoint(x:self.scene!.view!.midX,y:self.scene!.view!.frame.maxY),from:self.scene!)
This means the first time you call nodeBottom, it will do these calculations for you and store it into memory. Everytime after that, the number is preserved.
Now that you know where the bottom of the screen is in the coordinate system you want to use, you can assign the x value to whatever your random is producing, and you can subtract the (node.height * (1 - node.anchorPoint.y)) to fully hide your node from the scene.
Now keep in mind, if your node moves between various parents, this lazy will not update.
Also note, I unwrapped all optionals with !, you may want to be using ? and checking if it exists first.
I currently have two SKSpriteNodes on top of each other like so (The white part is one and the brown round circle part is the other one):
The white part is positioned 1/3 the way down on top of the round brown sprite node. In the picture, the brown round part has a SKPhysicsBody applied to it already as seen by the light blue outline around it. When I add a SKPhysicsBody around the top ovalish white part it pushes it up and not in the position I wanted it.
How can I have a SKPhysics body coving both bodies of sprites but not have the physics bodies push on one another which makes the white part move upwards? I would like the white part to stay in the position it was in the first image.
Thanks for anyone help!
Here's the code I used for the SKPhysicsBody's:
// create, position, scale & add the round body
roundBody = SKSpriteNode( imageNamed: "roundBody" )
roundBody.position = CGPoint( x: 207, y: 70 )
roundBody.zPosition = 1
roundBody.xScale = 0.3
roundBody.yScale = 0.3
// add sprite node to view
self.addChild( roundBody )
// create, position, scale & add the head
theHead!.position = CGPoint( x: 207, y: roundBody.frame.maxY / 1.15 )
theHead!.zPosition = 2
theHead!.xScale = 0.3
theHead!.yScale = 0.3
// setting up a SKPhysicsBody for the round body
roundBody.physicsBody = SKPhysicsBody( circleOfRadius: roundBody.size.width / 4 )
roundBody.physicsBody!.dynamic = true
roundBody.physicsBody!.affectedByGravity = true
roundBody.physicsBody!.allowsRotation = false
roundBody.physicsBody!.pinned = false
// setting up a SKPhysicsBody for the head
theHead!.physicsBody = SKPhysicsBody(circleOfRadius: theHead!.size.width / 2 )
theHead!.physicsBody!.dynamic = true
theHead!.physicsBody!.affectedByGravity = false
theHead!.physicsBody!.allowsRotation = false
theHead!.physicsBody!.pinned = false
I was able to figure out that if you use SKPhysicsJointPin it does the exact thing I needed! (Which was to basically pin a sprite head on it's body and share a physics body)
let joinTogether = SKPhysicsJointPin.jointWithBodyA(
roundBody.physicsBody!,
bodyB:theHead!.physicsBody!,
anchor: GPointMake(CGRectGetMidX(roundBody.frame),
CGRectGetMinY(theHead!.frame)))
scene!.physicsWorld.addJoint(joint)
Hope this helps someone in the future!
If you never want it to move, set it's .dynamic property to false. Then other objects may or may not bounce/collide with it (depending upon their collisionBitMask) but it won't move in response to those collisions.
Your own answer is correct and a better solution but just to explain further.
The reason of the bodies colliding is that by default a physics body's collision bit mask is set to all categories which means it will collide with everything. In your code you are not calling
roundBody.physicsBody?.collisionBitMask = ...
which is why its using the default values.
To change that you could give your body and head a different collisionBitMask.
Im sure you will deal with this sooner or later when you handle collisions
Also as a tip it's a better idea to not force unwrap the physics bodies unless you have too, even though you know they exist. So you should replace your ! with ? whenever possible.
I have 4 nodes of enemies. They are appearing on a scene from the right to the left bound. But often they are appearing in groups for some reason. It looks unpleasantly so I want to set the distance between them. I know that I can do it with SKConstraint, but XCode says that I'm doing something wrong. That's how I tried to do it:
mouse = SKSpriteNode(texture: mouseArray[0]);
self.mouse.position = CGPointMake(CGRectGetMaxX(self.frame) + self.mouse.size.width, CGRectGetMidY(self.frame) - 138)
self.mouse.size = CGSizeMake(self.mouse.size.width + self.mouse.size.width / 2, self.mouse.size.height + mouse.size.height / 2)
let constraint = SKConstraint.distance(range: 50...100, toNode: cat)
Xcode tells me that argument for parameter ToPoint is missing. I don't know why and what point it is talking about.
I couldn't find examples of using SKConstraint.distance online. How to use constraints properly for this purpose? Thanks!
SKConstraint's distance class method takes an SKRange as its first argument not a closed range (e.g., 10...50). For an example,
Swift 2
let range = SKRange(lowerLimit:50, upperLimit:100)
let constraint = SKConstraint.distance(range, toNode:cat)
mouse.constraints = [constraint]
Swift 3
let range = SKRange(lowerLimit:50, upperLimit:100)
let constraint = SKConstraint.distance(range, to: cat)
mouse.constraints = [constraint]
I am using Sprite Kit to add some circle icons to a scene. I have added some code to create a border around the outside of the scene size this is used to detect contact and remove the node from parent.
// Outside border collision detection
var largeBorder = CGRectMake(0, 0, size.width, size.height)
largeBorder.origin.x -= (mainIconRef.size.width + mainIconRef.size.width/3)
largeBorder.origin.y -= (mainIconRef.size.height + mainIconRef.size.height/3)
largeBorder.size.width += ((mainIconRef.size.width + mainIconRef.size.width/3) * 2)
largeBorder.size.height += ((mainIconRef.size.height + mainIconRef.size.height/3) * 2)
let pathMainView = CGPathCreateWithRect(largeBorder, nil)
self.physicsBody = SKPhysicsBody (edgeLoopFromPath: pathMainView)
self.physicsBody?.dynamic = false
self.physicsBody?.categoryBitMask = ColliderCategory.Wall.rawValue
self.physicsBody?.contactTestBitMask = ColliderCategory.Tap1.rawValue | ColliderCategory.Tap2.rawValue | ColliderCategory.Tap3.rawValue | ColliderCategory.TapFire.rawValue
self.physicsBody?.usesPreciseCollisionDetection = true
This is all working as expected. What I would like to do now is add another path/border/box in the middle of the screen and detect when the icons contact this. This is so I can tell they are at least a certain part of the size on the screen/scene itself.
What I am not sure about is that we set the self.physicsBody above. I do not want to override to it, I just want to add an additional border which is invisible (not shown) that I can track contact with (not collision). Is this possible without adding as a node?
Why not use an invisible node? Just turn off the physics for the node and pick the right shape for the physicsbody.
Set the right collisionbitmask, categorybitmask, contactbitmask etc and the icons will pass through the node and register "contact".
It'll do all of what you want.
Why don't you want to use a node?