I wrote the following code to detect collisions between the player, a wall and a npc:
struct PhysicsCategories{
static let player: UInt32 = 0x1 << 1
static let npc: UInt32 = 0x1 << 2
static let wall: UInt32 = 0x1 << 3
static let all: UInt32 = 0xFFFFFFFF
}
these are the setting for the player:
self.physicsBody!.categoryBitMask = Constants.PhysicsCategories.player
self.physicsBody!.collisionBitMask = Constants.PhysicsCategories.npc | Constants.PhysicsCategories.wall
self.physicsBody!.contactTestBitMask = Constants.PhysicsCategories.npc
these are the settings for the wall:
tileNode.physicsBody!.categoryBitMask = Constants.PhysicsCategories.wall
tileNode.physicsBody!.collisionBitMask = Constants.PhysicsCategories.player | Constants.PhysicsCategories.npc
these are the settings for the npc:
npc.physicsBody!.categoryBitMask = Constants.PhysicsCategories.npc
npc.physicsBody!.collisionBitMask = Constants.PhysicsCategories.wall | Constants.PhysicsCategories.player | Constants.PhysicsCategories.npc
npc.physicsBody!.contactTestBitMask = Constants.PhysicsCategories.player
I want the npc to collide with the wall and the player. The player should collide with the npc and the wall.
The result is that the player can collide with the wall and the npc, but the npc can only collide with the player and goes through the walls..
Has anyone an idea what i'm doing wrong here? The npc is walking randomly around the scene, is the collisionbitmask behavior affected by this?
EDIT: If I set the dynamic property of the npc to true, it works. But I don't want the npc to be dynamic because I don't want that the player can push the npc away.
Why is it only working when the npc is dynamic?
If you don't want the player to be able to push the npc around, don't mess around with the dynamic property, just switch off collisions between the npc and the player:
npc.physicsBody!.collisionBitMask &= ~Constants.PhysicsCategories.player
This will set the bit representing the player's category to 0 in npc's collisionBitMask without affecting any of npc's other collision settings.
or don't set them in the first place. Change:
npc.physicsBody!.collisionBitMask = Constants.PhysicsCategories.wall | Constants.PhysicsCategories.player | Constants.PhysicsCategories.npc
to
npc.physicsBody!.collisionBitMask = Constants.PhysicsCategories.wall | Constants.PhysicsCategories.npc
So npc no longer collides with player - this means that if the 2 come into contact, npc will be unaffected by player and carry on moving completely unaffected by the collision. The contactTestbitMask is still set however, so your code will be notified of the collision.
The player, however, will bounce off the npc unless you also do the same for the player:
player.physicsBody!.collisionBitMask &= ~Constants.PhysicsCategories.npc
When you say :
The npc is walking randomly around the scene, is the collisionbitmask
behavior affected by this?
Do you mean it's just bouncing off things and bouncing around the screen, or is there a problem with your movement code? If the former, then do as Kod said and set restitution to 0. From https://developer.apple.com/documentation/spritekit/skphysicsbody :
The restitution property determines how much energy a body maintains
during a collision—its bounciness.
If I understood correctly, you want the player and the NPC to "bounce off the walls", but not bounce off each other. The solution would be to not set their collisionBitMasks to each other‘s categoryBitMasks, but only their contactTestBitMasks.
As to why the NPC needs to be dynamic to bounce off the walls: One participant must be dynamic for a collision to show effect: the one that bounces off.
Related
I am making my first 3d fps in Godot and I don't understand how to spawn enemies in a general area. If someone could show me a tutorial or something that would be great.
Make a scene with only your enemy character, Give it control scripts as needed (movement, etc), and save it as a scene (Ex: myEnemy.tscn).
In your main script (or wherever you're calling it from), load the enemy scene and store it as a variable by writing:
onready var loadedEnemy = preload("res://myEnemy.tscn")
Then in your _process() or _ready() function (depending on what you need it for):
Instance the enemy by writing
var enemy = loadedEnemy.instance()
Add the instance to the scene with
add_child(enemy)
3.Specify the location of the enemy placement. For a random placement somewhere in a 10 x 10 area on the ground level (Y=0) by writing
enemy.transform.origin = Vector3( rand_range(0,10), 0, rand_range(0,10) )
You can also specify rotation with
enemy.transform.basis = Vector3(90deg, 0, 0) (example)
You can add more enemies by repeating these steps beginning from var enemy = loadedEnemy.instance() (Ex: The next enemy would be var enemy2 = loadedEnemy.instance())
If you need them to appear at different times, add them in the on_timer_timeout() function of a different Timer nodes.
Good Luck
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.
I have a game in which two balls should be able to pass through a third object, which functions as a finish line. The two balls are SKShapeNodes, the finish 'line' is an SKSpriteNode. All of them have SKPhysicBodys attached to them with categories set as follows:
struct PhysicsCategory {
static let ball: UInt32 = 0x1 << 1
static let line: UInt32 = 0x1 << 2
}
ball.physicsBody = SKPhysicsBody(circleOfRadius: ballSize)
ball.physicsBody?.categoryBitMask = PhysicsCategory.ball
ball.physicsBody?.contactTestBitMask = PhysicsCategory.line
ball.physicsBody!.collisionBitMask = 1
line.physicsBody = SKPhysicsBody(rectangleOf: line.size)
line.physicsBody?.isDynamic = false
line.physicsBody?.categoryBitMask = PhysicsCategory.line
line.physicsBody?.contactTestBitMask = PhysicsCategory.ball
line.physicsBody!.collisionBitMask = 1
Since I want the balls to be able to cross the finish line without collision, while still being able to get notified about this, I have set the collisionBitMask for both the balls and line to the same value.
This gives me the result I'm looking for. However, this seems to have as a side-effect that the two balls are able to pass through each other as well, which shouldn't happen.
Also, if I set the value of collisionBitMask to anything other than 1, the balls and line bump of each other. I have read through Apple's documentation on this, but I think I misunderstood / missed something here.
My guess is that since both balls see each other as having the same collisionBitMask. the overlap happening is to be expected. What I don't get is how I can avoid this, without having to add a separate category per ball.
Can anyone point me in the right direction? How can I make the balls pass through the finish line, while not being able to overlap each other?
I tend to get this mixed up from time to time, but hopefully I am recalling this correctly.
If all you care about is getting the collision call back on the line that is primarily controlled by contactTestBitMask to ball as you have. That is what sends the contact to the delegate method. collisionBitMask controls what actually controls physic calculations ie what will bounce off each other.
By default collisionBitMask will collide with everything so you want to specify only the things you want it to calculate physics on. You have everything set to 1 which isn't anything specific in your scene. This is why everything passes through each other. I believe 2 would be ball and 4 is line with what you provided (if you print out PhysicsCategory.ball and PhysicsCategory.line that is what it logs out). Typically is is best practice to use the enums or structs you create instead of specifying a number. With that said I tend to use 0 for nothing.
To fix this you want to tell your balls that you want them to collide with PhysicsCategory.ball and you want your line to collide with nothing so you want to set it to 0.
struct PhysicsCategory {
static let ball: UInt32 = 0x1 << 1
static let line: UInt32 = 0x1 << 2
}
ball.physicsBody = SKPhysicsBody(circleOfRadius: ballSize)
ball.physicsBody?.categoryBitMask = PhysicsCategory.ball
ball.physicsBody?.contactTestBitMask = PhysicsCategory.line
ball.physicsBody!.collisionBitMask = PhysicsCategory.ball //changed to ball
line.physicsBody = SKPhysicsBody(rectangleOf: line.size)
line.physicsBody?.isDynamic = false
line.physicsBody?.categoryBitMask = PhysicsCategory.line
line.physicsBody?.contactTestBitMask = PhysicsCategory.ball
line.physicsBody!.collisionBitMask = 0 //you want no physics calculations
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()
}
}
}
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