Why are methods sometimes called more than once in collision checking? - sprite-kit

Like many game programmers in SpriteKit, I use collision detection between certain objects to call methods or set properties. However, sometimes my actions happen twice in a collision. Here is an example from inside my didBeginContact method:
uint32_t collision = (contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask);
if (collision == (WWPhysicsCategoryShip | WWPhysicsCategoryWeapon)) {
SKNode *weapon = (contact.bodyA.categoryBitMask == WWPhysicsCategoryWeapon) ? contact.bodyA.node : contact.bodyB.node;
[weapon removeFromParent];
_localPlayerHP = _localPlayerHP - 5;
NSLog(#"My Health is now at %i.", _localPlayerHP);
}
You'll notice that my ship's health is decreased by 5 and the enemy weapon that contacted the ship is removed. But when testing the game, I find that my ship's health is often reduced by 10, in increments of 5 (indicating that my decrease health method has fired more than once). I find this odd since the weapon is removed on first contact. How can I ensure that my health is only decreased by 5 once per contact?

Maybe you can try this
if weapon.parent != nil {
_localPlayerHP = _localPlayerHP - 5;
}
This question talks about another way by making the ship invulnerable for a few seconds after it gets hit.
didBeginContact is being called multiple times for the same SKPhysicsBody

Related

Sprite Kit: Waiting to call a function until condition X is met

I'm working on a game (top-down shooter) and have run into a bit of a snag. Up to this point, I've spawned enemies with functions that just work with delays:
Wave One Function - Delay 3, spawn enemies //
Wave Two Function - Delay 6, spawn enemies
I do this because I haven't found a way to wait for all actions in a given function to complete before calling the next - as things stand, functionWaveOne calls functionWaveTwo, which calls functionWaveThree (etc).
This has worked until now. What's changed is I want two enemies to remain on-screen and until they're dead, I don't want the next wave to come. My initial solution to this was to have a running count of how many enemies died in wave four:
Detect collision -> Apply Damage -> Check if dead -> If yes, deadWaveFourEnemies++
Here's my problem: I have a primary weapon that's two parallel lasers, so they have potential to collide with an enemy at exactly the same time. This results in false positives, making the dead enemy count go higher than it should. I even tried adding an "am I alive" function to the enemy class, but to no avail.
Have any of you got a recommendation on how to either call functions in a better way or to get around these false positives? Thanks!
In case it helps:
if([enemySprite.name isEqual: #"shooter"]){
enemyShooter *enemyClass = (enemyShooter *)enemySprite;
itDied = [enemyClass doDamageWithAmount:secondaryWeaponDamage];
scoreIncrease = [enemyClass getScoreIncrease];
stillAlive = [enemyClass amIAlive];
}
[weaponSprite removeFromParent];
if(itDied){
[self increasePlayerScoreBy:scoreIncrease];
if(inWaveFour == 1 && stillAlive == 1){
waveFourKilled++;
NSLog(#"Seconday / Number killed: %i", waveFourKilled);
}
}

How to use correctly Scenekit's category mask, collision mask and physicsbody, contactTestBitMask and make proper collisions?

I have been trying to learn scenekit and have finished one book but only collision detection part is not understood, maybe the most important part.
There is category mask, collusion mask and physicsbody?.contactTestBitMask.
I want to create a simple game to get this but I got stuck. I am not sure I get the idea.
In game the game, there is a ball and collects pearls, and stays away from rocks and holes. In this case:
Category masks:
ball = 0b0001
pearls = 0b0010
rocks = 0b0100
holes = 0b1000
physicsBody?.contactTestBitMask:
ball = pearl || rocks // means 0b1110
pearls = 1
rocks = 1
Collusion masks are 1 because they all collide with each other.
I am not sure I get this collision issue. So before I begin to write code, I wanted to be sure. In SCNPhysicsContactDelegate, function below solves how to learn when they contact with each other:
physicsWorld(_ didBegin contact: _) {
var contactNode:SCNNode!
if contact.nodeA.name == "ball" {
contactNode = contact.nodeB
} else {
contactNode = contact.nodeA
}
if contactNode.physicsBody?.categoryBitMask == 0b0010 {
// mean pearls
// raise score etc
}
if contactNode.physicsBody?.categoryBitMask == 0b0100 || 0b1000{
if contactNode.name == "Rock" { print("You rocked") }
if contactNode.name == "Hole" { print("You need to climb") }
}
}
I have searched youtube and stack but there is only one explanation.
Stack Link
Youtube videos are not explaining these.
The book examples are copyrighted, so I can't put them on here.
Thank you,
Have a nice day.
You are not using bitwise operators. https://docs.swift.org/swift-book/LanguageGuide/AdvancedOperators.html
change
if contactNode.physicsBody?.categoryBitMask == 0b0100 || 0b1000
to
if contactNode.physicsBody?.categoryBitMask == 0b0100 | 0b1000
The single | and & are the correct operators to use for bitwise operations such as these.
There are 3 function to set when you do collision:
mBallBody->setCategoryBitmask(CATEGORY_BALL);
mBallBody->setContactTestBitmask(CATEGORY_WALL_LEFT_RIGHT | CATEGORY_WALL_TOP | CATEGORY_BOX | CATEGORY_LAUNCHER);
mBallBody->setCollisionBitmask(CATEGORY_WALL_LEFT_RIGHT | CATEGORY_WALL_TOP | CATEGORY_BOX | CATEGORY_LAUNCHER);
setCategoryBitmask() --> to set what category this physics belong to!
setContactTestBitmask() --> a mask that defines which categories of bodies cause intersection notifications with this physics body
setCollisionBitmask() --> a mask that defines which categories of phyics bodies can collide with this physics body
so let say ball and wall, ball can collide with wall, so you set contact test bitmask to ball | wall, using | is the bitmask operation.
So you need to set collision category bitmask 1 2 4 8 16 32 and so on.

Multiple for loops executing at the same time? Swift

Basically I am creating a game that spawns a ball after a certain amount of time has passed. However, as time goes on and the player score increases, balls that are more difficult to kill will spawn.
Prior to this, all the spawn methods I have set in place function as I would like them to, but they were contained in a switch statement that calculated the probability a ball would spawn.
//pseudo:
let spawnProbability = arc4random_uniform(100)
switch spawnProbability {
case 0…60: //60% spawn chance
//spawn small ball
case 61…90: //~30% spawn chance
//spawn medium ball
case 91…100: //~9% chance
//spawn large ball
default:
break
}
This worked as a plan B, but I'd like to implement what I originally had designed which was the method described above.
My thought process to accomplish what I originally had in mind is:
for loop that spawns small balls until the score counter reached a certain amount
after the score counter reached, say, 100pts, another for loop would be executed in addition to the first loop. So, medium and small balls spawn after a delay.
after the score counter reached another mark, large balls would begin to spawn on top of medium balls and small balls.
Here's what I mean: (pseudocode again)
for(var i = 0; i < 10; i++) {
//delay
//spawn small ball
}
if score >= 100 {
for (var x = 0; x < 10; x++) {
//delay
//spawn medium ball
}
}
// . . . and so on
I've put alot of thought into how I could accomplish what I want but I can't come up with a better approach to this problem. I understand why this isn't working and why multiple for loops can't be executed with delay in the same class at the same time (or maybe they can and I'm just going about it in the wrong way) but I'm stuck in the sense that I don't know a better way to implement the spawn technique that I want.. Any help is greatly appreciated!!

Can't understand how collision bit mask works

I am trying to use collision bit masks and contact test bit masks in Swift, I want two objects not to collide together, so I am doing :
firstNode.physicsBody?.collisionBitMask = 0b01
secondNode?.collisionBitMask = 0b10
Since SpriteKit does an AND operation on those two numbers, shouldn't the result be 00 since 10 & 01 = 00 ?
So why are collisions happening anyway ?
Thank you.
That is not how collision handling works. When two bodies are in intersection, physics engine performs logical AND operator between current's body collisionBitMask and other's body categoryBitMask:
When two physics bodies contact each other, a collision may occur.
This body’s collision mask is compared to the other body’s category
mask by performing a logical AND operation. If the result is a nonzero
value, this body is affected by the collision. Each body independently
chooses whether it wants to be affected by the other body. For
example, you might use this to avoid collision calculations that would
make negligible changes to a body’s velocity.
The source.
So the result depending on how you set categoryBitMask on those two bodies. A default value for categoryBitMask is 0xFFFFFFFF, means all bits set. So when you perform & between 0xFFFFFFFF and 0b10 or 0b01, the result would be non-zero value, thus the collision.
So for example, setting your bodies like this:
spriteA.physicsBody?.categoryBitMask = 0b01
spriteA.physicsBody?.collisionBitMask = 0b01
and
spriteB.physicsBody?.categoryBitMask = 0b10
spriteB.physicsBody?.collisionBitMask = 0b10
will give you the result you want. Also, this is probably not the exact setup you need, it is just a simple example, and you will have to change values according to your needs. In this case, spriteA will collide only with bodies which have categoryBitMask set to 0b01. Same goes for spriteB, it will collide with bodies which have categoryBitMask set to 0b10.
Also, in the case if you don't want these sprites to be able to collide with anything, simply set their collisionBitMask properties to 0.
That's not how you test interaction between nodes with collision bits.
The categoryBitMask is the category of the object.
The collisionBitMask is what the object responds to when collided with.
The contactTestBitMask is used for notifications when intersection occurs for the specified bit mask.
Suppose I have the following:
struct PC {
static var player: UInt32 = 0b10 //2
static var enemy: UInt32 = 0b100 //4
static var rock: UInt32 = 0b1000 //8
}
player.physicsBody!.categoryBitMask = PC.player
player.physicsBody!.collisionBitMask = PC.enemy | PC.rock
enemy.physicsBody!.categoryBitMask = PC.enemy
enemy.physicsBody!.collisionBitMask = PC.player
So when you are checking when interaction occurs in the didBeginContact function, you check if their interaction occurred by using bit logic.
func didBeginContact(contact: SKPhysicsCountact) {
//1
let collision: UInt32 = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask
//2
if collision == PC.player | PC.enemy {
//An interaction occured between the player and enemy.
}
The variable collision is using bitwise OR, which is |. In this case (if the player touches an enemy), it gets the category of the player (bodyA) which is 2, and it gets the category of the enemy (bodyB), which is 4. So 2 (0b10) OR 4 (0b100) is equal to 6 (0b110) which is assigned to collision.
So then in the if statement, it checks if the collision of 6 is equal to (PC.player | PC.enemy), which is true, therefore an interaction occurred between the player and enemy since it would be if 6 == 6.
You can use the variable collision to test any interaction. For example in my test game I have the following function that tests what objects touched.
func didBeginContact(contact: SKPhysicsContact) {
let collision: UInt32 = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask
//Collision between the penguin and land
if collision == PhysicsCategory.Land | PhysicsCategory.Animal {
lostLevel()
} else if collision == PhysicsCategory.Animal | PhysicsCategory.Pillow {
animalCounter -= 1
if animalCounter == 0 {
wonLevel()
}
}
}

Sprite Kit Multiple Collisions

I'm going through a tutorial/class making a game in Sprite Kit and I'm having issues with multiple collisions happening at the same time.
I'm implementing the didBeginContact: method for all collisions which make the ball bounce because there seems to be a known issue where objects may "stick" to a body if the velocity is too low and angle is too narrow.
By making
_ball.physicsBody.collisionBitMask = 0;
and putting the below line in the didBeginContact
if(other body == firstbody && _ball == secondbody)
secondBody.velocity = (CGVectorMake(secondBody.velocity.dx * -1.0, secondBody.velocity.dy));
(or dy * -1.0 for vertical collisions)
I can make the object bounce naturally and it works.
The issue I'm having is when there multiple contacts are called on the _ball. If 1 collision makes the _ball reverse its direction and it hits another object of the same type which reverses direction again, the _ball will continue moving in it's original direction (Double Negative). The _ball just moves through these objects. I can make it bounce back if I
secondBody.categoryBitMask = 0;
but i then i have the issue of returning the _ball to it's original category.
Does anyone know if you an cycle through contacts or stop contacts on a body after it processes a contact once?
Any thoughts?
I had many problems with correct amount of collisions. Sometimes it would get one, sometimes none. So I tried this and it works. The only thing to change is in didBeginContact method.
I will presume that you declared categories like that you correctly implemented physics delegate. So:
//define collision categories on the top of implementation file
static const uint32_t category1 = 0x1 << 0;
static const uint32_t category2 = 0x1 << 1;
static const uint32_t category3 = 0x1 << 2;
Try to replace your code in didBeginContact with this one. I remember that correct collisions finally got to work after I did this.
-(void)didBeginContact:(SKPhysicsContact *)contact
{
SKNode *newFirstBody = contact.bodyA.node;
SKNode *newSecondBody = contact.bodyB.node;
uint32_t collision = newFirstBody.physicsBody.categoryBitMask | newSecondBody.physicsBody.categoryBitMask;
if (collision == (category1 | category2))
{
NSLog(#"hit");
}
}
Hope it helps