Node on top of another Node, both with Physics Bodies, Only want to detect most top physics body - SpriteKit / Swift - swift

Creating a game in Swift and SprieKit.
I have 2 nodes, a BridgeNode and WaterNode. One on top of the other.
Both have physics bodies to detect when the player is either on the bridge on in the water. Both nodes are added independently as child nodes of the Scene.
When the player node jumps onto the Bridge, DidBegin detects contact with
both the Water and Bridge nodes. I only want it to detect the Bridge node as the player is safely on the Bridge OR if the player is in the water.
func didBegin(_ contact: SKPhysicsContact) {
// Did Begin Contact - Contact Testing and actions
let player1 = (contact.bodyA.categoryBitMask == player1Mask) ? contact.bodyA : contact.bodyB
let other = (player1 == contact.bodyA) ? contact.bodyB : contact.bodyA
if other.categoryBitMask == bridgeMask {
print("BRIDGE CONTACT")
}
else if other.categoryBitMask == waterMask {
// Contacted Water
print("WATER CONTACT")
}
}
The console is printing both print statements always in a random order.
Hope someone can help me to just detect one or the other.

You mentioned that it is a top-down game, so when you have the bridge on top of the water the player will obviously contact both at the same time, there is no "blocking" of the physicsBody underneath the bridge. You need to do something like this in your SKPhysicsContactDelegate:
var playerIsOnBridge = false
func didBegin(_ contact: SKPhysicsContact) {
let player1 = (contact.bodyA.categoryBitMask == player1Mask) ? contact.bodyA : contact.bodyB
let other = (player1 == contact.bodyA) ? contact.bodyB : contact.bodyA
if other.categoryBitMask == bridgeMask || playerIsOnBridge {
playerIsOnBridge = true
print("BRIDGE CONTACT")
} else if other.categoryBitMask == waterMask {
print("WATER CONTACT")
}
}
func didEnd(_ contact: SKPhysicsContact) {
let bitmaskA = contact.bodyA.categoryBitMask
let bitmaskB = contact.bodyB.categoryBitMask
if bitmaskA == bridgeMask || bitmaskB == bridgeMask {
playerIsOnBridge = false
}
}

Related

How to detect the collision nodes name in skspritekit?

I would like to detect the nodes name my player node colides with. Is that possible?
func didBegin(_ contact: SKPhysicsContact) {
let collision: UInt32 = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask
for i in CollectionObject {
if collision == Player.physicsBody!.categoryBitMask | i.physicsBody!.categoryBitMask{
print("hit the object") //here I would like to print the nodes name
}
}
}
You can use
contact.bodyA.node?.name
and
contact.bodyB.node?.name
to get the names of the 2 nodes colliding with each other.
So if your player node has a name, then you could use something like the below:
if contact.bodyA.node?.name == "PlayerName" {
print(contact.bodyB.node?.name)
} else if contact.bodyB.node?.name == "PlayerName" {
print(contact.bodyA.node?.name)
}

Checking current spritenode

Have that situation: I have bouncing red ball and several randomly generated platforms.
In case of contact with bottom of the platform ball should pass platform through. In other case, it will be top of platform and ball should bounce. I changing .collisionBitMask and .contactTestBitMask params for player to pass through platform or bounce on it. But my code working only for the last one platform, you can see on screenshot. For other platforms not working - ball pass it through from the top side too. I have array of that platforms and don't know how to check current platform.
Here is part of my code:
var platforms = [SKSpriteNode]()
....
extension GameScene: SKPhysicsContactDelegate {
func didBegin(_ contact: SKPhysicsContact) {
let floor = BitMaskCategory.floor
let player = BitMaskCategory.player
let platformCategory = BitMaskCategory.platform
let bodyA = contact.bodyA.categoryBitMask
let bodyB = contact.bodyB.categoryBitMask
if bodyA == player && bodyB == platformCategory || bodyA == platformCategory && bodyB == player {
for platform in platforms {
if self.player.position.y > platform.position.y {
self.player.physicsBody?.collisionBitMask = floor | platformCategory
self.player.physicsBody?.contactTestBitMask = floor | platformCategory
} else if self.player.position.y < platform.position.y {
self.player.physicsBody?.collisionBitMask = floor
self.player.physicsBody?.contactTestBitMask = floor
}
}
}
}
func didEnd(_ contact: SKPhysicsContact) {
let player = BitMaskCategory.player
let bar = BitMaskCategory.platform
let floor = BitMaskCategory.floor
let bodyA = contact.bodyA.categoryBitMask
let bodyB = contact.bodyB.categoryBitMask
if bodyA == bar && bodyB == player || bodyA == player && bodyB == bar {
for platform in platforms {
if self.player.position.y > platform.position.y {
self.player.physicsBody?.collisionBitMask = floor
self.player.physicsBody?.contactTestBitMask = floor
}
}
}
}
}
So, I don't know how to check current platform. If I placing 3 constant platforms by hand all is working fine :D
Please help, don't know how to realize this task.
I have this approach for solutions (Red platforms)
You can get code example here: https://github.com/Maetschl/SpriteKitExamples/tree/master/PlatformTest
This use object velocity for getting the direction and add collision mask
override func update(_ currentTime: TimeInterval) {
player.physicsBody?.collisionBitMask = 0b0000 // Reset the mask
// For UP only Platform
if (player.physicsBody?.velocity.dy)! < CGFloat(0.0) {
player.physicsBody?.collisionBitMask |= 0b0001 // The pipe | operator adds the mask by binary operations
}
// For Down only platforms
if (player.physicsBody?.velocity.dy)! > CGFloat(0.0) {
player.physicsBody?.collisionBitMask |= 0b0010 // The pipe | operator adds the mask by binary operations
}
}

Specific Head On Collision in Swift 4

I am new to iOS development and I made an infinite runner game. As you can see in the function below, the player dies when it collides with an obstacle. However, the player dies whenever it collides with any side of the obstacle. How can I limit this collision detection to a specific side of the frame of the obstacle?
func didBegin(_ contact: SKPhysicsContact) { //so the player can't jump while in the air
var firstBody = SKPhysicsBody()
var secondBody = SKPhysicsBody()
if contact.bodyA.node?.name == "Player" {
firstBody = contact.bodyA
secondBody = contact.bodyB
} else {
firstBody = contact.bodyB
secondBody = contact.bodyA
}
if firstBody.node?.name == "Player" && secondBody.node?.name == "Ground" { //if colliding with ground
canJump = true //can jump when on ground
}
if firstBody.node?.name == "Player" && secondBody.node?.name == "Bus" || firstBody.node?.name == "Player" && secondBody.node?.name == "Obstacle" {
// kill player and prompt buttons
playerDied()
}
}
You may try to calculate the intersection between two colliding nodes, then just checking width and height of the resulting frame should be enough to detect the collision side, a rough explanation:
collision left/right
collision top/down
for a infinite runner game this check should be enough:
if firstBody.node?.name == "Player" && secondBody.node?.name == "Obstacle" {
let intersection = firstBody.node!.frame.intersection(secondBody.node!.frame)
if intersection.height > intersection.width { // collision left/right
playerDied()
} else { // collision top/down
//...//
}
}

Swift SpriteKit Collisions Registered Multiple Times

I'm using SpriteKit and Swift to build an iPhone game, and my collision detection system is not functioning as I want it to. When my "bullet" physics body and my "player" physics body collide, the collision detection function is being called multiple times, usually 4-12 times. I have tried setting "usesPreciseCollisionDetection" to true, but it is still problematic. Also, it seems the method is called more times when the bullet hits the player at an angle rather than straight on. Any thoughts how to fix this?
Collision Types:
enum ColliderType:UInt32 {
case Player = 0b1
case Bullet = 0b10
}
Player Physics Body Settings:
playerBody!.categoryBitMask = ColliderType.Player.rawValue
playerBody!.contactTestBitMask = ColliderType.Bullet.rawValue
playerBody!.collisionBitMask = ColliderType.Bullet.rawValue
Bullet Physics Body Settings:
bulletBody!.categoryBitMask = ColliderType.Bullet.rawValue
bulletBody!.contactTestBitMask = ColliderType.Player.rawValue
bulletBody!.collisionBitMask = ColliderType.Player.rawValue
Collision Detection Method:
func didBeginContact(contact: SKPhysicsContact) {
if (contact.bodyA.categoryBitMask == 0b1) && (contact.bodyB.categoryBitMask == 0b10) {
playerVbullet(contact.bodyA, bullet: contact.bodyB)
}
if (contact.bodyA.categoryBitMask == 0b10) && (contact.bodyB.categoryBitMask == 0b11) {
playerVbullet(contact.bodyB, bullet: contact.bodyA)
}
}
Function Called on Collision:
func playerVbullet(player:SKPhysicsBody, bullet:SKPhysicsBody) {
bullet.node?.removeFromParent()
collisions++
println(collisions)
}
Give your bullet node a name:
let bullet = SKSPriteNode()
bullet.name = "bullet"
Then check your physics contact
if (contact.bodyA.node?.name == "bullet") {
contact.bodyA.node?.removeFromParent()
} else if contact.bodyB.node?.name == "bullet" {
contact.bodyB.node?.removeFromParent()
}
Good luck!

SpriteKit - Make a sprite move similar t another sprite

I'm making a type of a platformer ( think super mario but in a top-down view style ) game and i want my main character to move with the platform it has contact with.
So i used this:
-(void)didBeginContact:(SKPhysicsContact *)contact
{
SKPhysicsBody *firstBody;
SKPhysicsBody *secondBody;
if (contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask) {
firstBody = contact.bodyA;
secondBody = contact.bodyB;
}
else {
firstBody = contact.bodyB;
secondBody = contact.bodyA;
}
if (firstBody.categoryBitMask == PlatformCategory || secondBody.categoryBitMask == PlayerCategory) {
_contact = YES;
}
-(void)didSimulatePhysics
{
if (_contact) {
_player.position = CGPointMake(_platform2.position.x, _platform2.position.y + 10);
}
}
}
It kinda worked, and now my player is moving with that SPECIFIC platform when it touches it, but that's not a very practical way for doing it because i will be randomly generating platforms. So how t achieve that ?
SKPhysicsBody has a property called node, which is the parent sprite it's attached to. In your collision method, test if the physics body is of collision category platform, then:
SKSpriteNode *thisCollidedPlatform = (SKSpriteNode*) nameOfPhysicsBody.node
And then use the position of thisCollidedPlatform to set the player position.