This function should increase my snake size by increment width when I call in my didbegincontact function. However when contact is made it calls the function once and then never calls it again. I am not sure why when contact is made it does not keep increasing my snake size by increment
func increaseSnakeSize(increment: CGFloat){
snake.snake1.size = CGSizeMake(snake.size.width + increment, snake.size.height + increment)
snake.snake2.size = CGSizeMake(snake.size.width + increment, snake.size.height + increment)
snake.snake3.size = CGSizeMake(snake.size.width + increment, snake.size.height + increment)
print(snake.snake1.size)
}
// didBeginContact
if (firstBody.categoryBitMask & physicsCategory.snakeCategory == 0b1) && (secondBody.categoryBitMask & physicsCategory.foodCategory == 0b10 ){
score++
scoreLabel.text = "\(score)"
food.removeFromParent()
addFood()
increaseSnakeSize(2)
}
When the initial contact is made, the snake grows to 22x22, but its physics body's size stays at 20x20. A solution is to create a new physics body that's 22x22 for the initial contact, 24x24 for the next, 26x26 for the contact after that, ... For example,
func increaseSnakeSize (increment: CGFloat) {
snake.snake1.size = CGSizeMake(snake.size.width + increment, snake.size.height + increment)
// Create a new, larger physics body that matches the size of the snake
snake.snake1.physicsBody = SKPhysicsBody(circleOfRadius:snake.size.width/2.0)
// Configure the physics body's property accordingly
snake.snake1.physicsBody?.affectedByGravity = false
...
}
Another potential issue is the order of contact.bodyA and contact.bodyB can switch between contacts. You will need to check if the snake is the first body and the food is the second and the food is the first body and the snake is the second body. Here's an example
func didBeginContact (contact: SKPhysicsContact) {
var firstBody, secondBody: SKPhysicsBody!
if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
firstBody = contact.bodyA
secondBody = contact.bodyB
} else {
firstBody = contact.bodyB
secondBody = contact.bodyA
}
if (firstBody.categoryBitMask == physicsCategory.snakeCategory) && (secondBody.categoryBitMask == physicsCategory.foodCategory ) {
// Increase size of the snake
}
or
let bitMask = contact.BodyA.categoryBitMask | contact.BodyB.categoryBitMask
if bitMask == physicsCategory.snakeCategory | physicsCategory.foodCategory {
// Increase size of the snake
}
Looks like you do not increase the PhysicsBody of your snake. Do you have physics hit boxes showing in your app? If not, I would recommend turning them on and seeing how it behaves. In your View code, do:self.showsPhysics = true
Related
We are trying to create a charging station for a game that we are creating, with a health bar that increases when the player collides with the charger. In order to do this, we have created a category for the player, as well as a category for the charger. In our didBegin function, which is activated when a collision happens, which can be seen below, the health(which is a global variable) increases, and charging is set to true. In order to make sure that the health does not just continue to increase after the player stops colliding with the charger, we have a didEnd function that only occurs when the collision is no longer happening. This sets the charging variable to false. In our update function, we have conditional statements for what should happen in our code if charging is true or false. When it is true, the health increases by a bit, and doesn’t go above 1 or below zero in order to keep it within bounds. When it is false, the health decreases by a bit, and also does not go above 1 or below zero in order to keep it within bounds. The issue that we are running into is that when the player first collides with the charger, the charging variable is set to true, but as soon as it moves even a little, even if it is still colliding with the charger, the variable is set to false, and it no longer charges. Is there something that we are missing/is there a better way to go about setting up a charging station?
let chargerCategory: UInt32 = 0b1000 //6 if charge is in contact with player, then increase
func didBegin(_ contact: SKPhysicsContact) {
var body1 = SKPhysicsBody()
var body2 = SKPhysicsBody()
if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask{
body1 = contact.bodyA
body2 = contact.bodyB
}
else{
body1 = contact.bodyB
body2 = contact.bodyA
}
if body1.categoryBitMask == playerCategory && body2.categoryBitMask == chargerCategory{
//if player hit charger
//health += 0.001
//HealthBar.xScale = health
print("CHARGING...", health)
charging = true
}
func didEnd(_ contact: SKPhysicsContact) {
var body1 = SKPhysicsBody()
var body2 = SKPhysicsBody()
if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask{
body1 = contact.bodyA
body2 = contact.bodyB
}
else{
body1 = contact.bodyB
body2 = contact.bodyA
}
if body1.categoryBitMask == playerCategory && body2.categoryBitMask == chargerCategory{
charging = false
}
}
override func update(_ currentTime: TimeInterval) {
if charging == false{
health -= 0.0001
HealthBar.xScale = health
if health < 0 {
health = 0
}
if health > 1.0{
health = 1.0
}
}
if charging == true{
health += 0.001
HealthBar.xScale = health
// print("CHARGING...", health)
if health < 0 {
health = 0
}
if health > 1.0{
health = 1.0
}
}
print(health)
print(charging)
}
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
//...//
}
}
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
}
}
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.
At certain points in my game I want a ton of balls to fall onto the screen. Once they hit the ground and bounce a bit, I want them to just sit there, and no longer need them to move.
Once I get up to 200 physics bodies, the game gets very slow, so I'd like to destroy the bodies. Here is what I was trying in my code:
-(void)didBeginContact:(SKPhysicsContact *)contact {
if (contact.contactPoint.y < 150) {
if (contact.bodyA.categoryBitMask == MYPhysicsCategoryBall) {
NSLog(#"body a is ball");
contact.bodyA = nil;
}
if (contact.bodyB.categoryBitMask == MYPhysicsCategoryBall) {
NSLog(#"body b is a weapon");
}
}
This doesn't work, because contact.bodyA and contact.bodyB are both readonly, so I have to fix that, but apart from that, will just setting the actual physics body to nil destroy it and make the physics simulator run faster? Or is there a better way to fix the performance hit? I want to be able to add more than 200 balls, maybe 500 or 600.
Use contact.bodyA.node.physicsBody:
-(void)didBeginContact:(SKPhysicsContact *)contact {
if (contact.contactPoint.y < 150) {
if (contact.bodyA.categoryBitMask == MYPhysicsCategoryBall) {
NSLog(#"body a is ball");
// contact.bodyA = nil;
contact.bodyA.node.physicsBody = nil;
}
if (contact.bodyB.categoryBitMask == MYPhysicsCategoryBall) {
NSLog(#"body b is a weapon");
}
}
I haven't tested it though