Need to know about ENUM for contact in SpriteKit - swift

I tried to make contact but it didn't work .
I create an enum:
enum object:UInt32{
case BB = 1
case TR = 2
case GAP = 3
}
Then I have 3 nodes:
balloon.physicsBody?.categoryBitMask = object.BB.rawValue
balloon.physicsBody?.contactTestBitMask = object.TR.rawValue
balloon.physicsBody?.collisionBitMask = object.GAP.rawValue
tree2.physicsBody?.categoryBitMask = object.TR.rawValue
tree2.physicsBody?.contactTestBitMask = object.BB.rawValue
gap.physicsBody?.categoryBitMask = object.GAP.rawValue
gap.physicsBody?.collisionBitMask = object.GAP.rawValue
gap.physicsBody?.contactTestBitMask = object.BB.rawValue
In DidBeginContact:
let result = contact.bodyA.categoryBitMask + contact.bodyB.categoryBitMask
switch result{
case object.BB.rawValue + object.TR.rawValue:
xoaBB(contact.bodyA.node as! SKSpriteNode, TR: contact.bodyB.node as! SKSpriteNode, toaDo: contact.contactPoint)
case object.BB.rawValue + object.GAP.rawValue:
score++
scorelabel.text = "\(score)"
default:
return
}
The problem is:
"case object.BB.rawValue + object.GAP.rawValue" didn't work
I want the ballon fly through the gap to score but it can't. Balloon just contact to gap and gets stuck there as shown here.
Can anyone help me, I really need "how to use Enum tutorial too"

As mentioned in my comment your bitmask-values doesn't quite make sense. How about something a little like this (I have not checked for typos):
enum MaskType : UInt32 {
case Balloon = 1
case Gap = 2
case Wall = 4
case None = 0
}
Then you need to decide what you are after. The following example does not register collisions, meaning you want have physical interactions happening between the objects.
balloon.physicsBody?.categoryBitMask = MaskType.Balloon.rawValue
balloon.physicsBody?.collisionBitMask = MaskType.None.rawValue
balloon.physicsBody?.contactTestBitMask = MaskType.Gap.rawValue | MaskType.Wall.rawValue
The balloon sprite will now register contacts with objects having either the .Gap or .Wall value as their categoryBitMask.
If you now setup your other spriteNode's physicsBodies appropriately you can do some nice things in the didBeginContact function. Perhaps something like this:
func didBeginContact(contact: SKPhysicsContact) {
let contactA = contact.bodyA
let contactB = contact.bodyB
func ballonHitType() -> Uint32 {
if contactA.categoryBitMask == MaskType.Baloon.rawValue {
return contactB.categoryBitMask
} else {
return contactA.categoryBitMask
}
}
// Then simply perform your wanted actions based on whatever ballonHitType you receive
}

Related

Gamescene spriteNodes in array

Getting an error on this pice of code.
I Don't want to go through the spriteNode on GameScene.sks manually (in code) - the for loop must work somehow? Any ideas...
let sprites:[SKSpriteNode] = [block1, block2, block3, block4,
block5, block6, block7, block8, block9, block10, block11,
block12, block13, block14, block15, block16, block17,
block18, block19, block20, block21, block22, block23,
block24, block25, block26, block27, block28, block29,
block30, block31, block32, block33, block34, block35,
block36, block37, block38, block39, block40, block41,
block42, block43, block44, block45, block46, block47,
block48, block49, block50, block51, block52, block53,
block54, block55, block56, block57, block58, block59,
block60, block61, block62, block63, block64, block65,
block66, block67, block68, block69, block70, block71,
block72, block73, block74, block75, block76, block77,
block78, block79, block80, block81, block82, block83, block84]
for sprite in sprites {
self.sprite; in sprites = (self.childNode(withName: sprite.name) as? SKSpriteNode)!
sprite.name = "Green"
sprite.zPosition = 3
}
I'm guessing that from your not very descriptive description that you have a bunch of SKSpriteNodes in your .sks file and you want to loop through them in code to setup them up???
something like this might work better for you, if that is indeed what you are looking for.
let spriteNames: [String] = ["block1", "block2", "block3"]
let sprites = [SKSpriteNode]()
for spriteName in spriteNames {
if let sprite = self.childNode(withName: spriteName) as? SKSpriteNode {
sprite.name = "Green"
sprite.zPosition = 3
sprites.append(sprite)
}
}

"Attemped to add a SKNode which already has a parent:" in Repeat Loop. Any simple work around?

I am pretty Newbie to programming. And I am trying to pile up the random blocks dynamically till it hits the upper frame. But it seems that Swift doesn't let me to do so. Did I miss anything please? Any input are appreciated.
let blocks =[block1,block2,block3,block4,block5,block6,block7,block8,block9,block10,block11,block12]
var block:SKSpriteNode!
let blockX:Double = 0.0
var blockY:Double = -(self.size.height/2)
repeat{
block = blocks.randomBlock()
block.zPosition = 2
block.position = CGPoint(x:blockX, y:blockY)
block.size.height = 50
block.size.width = 50
self.addChild(block)
blockY += 50
} while( block.position.y < self.size.height)
extension Array {
func randomBlock()-> Element {
let randint = Int(arc4random_uniform(UInt32(self.count)))
return self[randint]
}
}
you need to have someway of tracking which blocks have been selected and ensure that they don't get selected again. The method below uses an array to store the indexes of selected blocks and then uses recursion to find a cycle through until an unused match is found.
private var usedBlocks = [Int]()
func randomBlock() -> Int {
guard usedBlocks.count != blocks.count else { return -1 }
let random = Int(arc4random_uniform(UInt32(blocks.count)))
if usedBlocks.contains(random) {
return randomBlock()
}
usedBlocks.append(random)
return random
}
in your loop change your initializer to
let index = randomBlock()
if index > -1 {
block = blocks[index]
block.zPosition = 2
block.position = CGPoint(x:blockX, y:blockY)
}
remember that if you restart the game or start a new level, etc. you must clear all of the objects from usedBlocks
usedBlocks.removeAll()

SpriteKit collision not being detected

I have two nodes, one "cat" and one "rat", but for some reason I can't get their collision to be detected. I'm using this method for masks:
enum CollisionTypes: UInt32 {
case holder = 1
case chef = 2
case powerups = 4
case ingredients = 8
case utensils = 16
case floor = 32
case bag = 64
case table = 128
case tip = 256
case rat = 512
case cat = 1024
}
Here is where I initialize their physics bodies:
// Cat physics body, the node's name is "cat"
public func initializeAt(position: CGPoint) {
sprite.position = position
sprite.zPosition = 5
sprite.name = "cat"
sprite.alpha = 0.7
scene.sceneContent.addChild(sprite)
sprite.physicsBody = SKPhysicsBody(rectangleOf: sprite.size)
sprite.physicsBody!.isDynamic = false
sprite.physicsBody!.categoryBitMask = CollisionTypes.cat.rawValue
sprite.physicsBody!.contactTestBitMask = CollisionTypes.rat.rawValue
sprite.physicsBody!.collisionBitMask = CollisionTypes.rat.rawValue
// Rat physics body, the nodes name is "rat"
init() {
node.name = "rat"
node.zPosition = 5
node.physicsBody = SKPhysicsBody(rectangleOf: node.size)
node.physicsBody!.isDynamic = false
node.physicsBody!.categoryBitMask = CollisionTypes.rat.rawValue
node.physicsBody!.contactTestBitMask = CollisionTypes.cat.rawValue
node.physicsBody!.collisionBitMask = CollisionTypes.cat.rawValue
setupFrames()
}
Here is my didBegin() method. However, neither of the if statements get executed and I don't know why because I am using this method for a number of other things in my project.
func didBegin(_ contact: SKPhysicsContact) {
if let node1 = contact.bodyA.node as? SKSpriteNode,
let node2 = contact.bodyB.node as? SKSpriteNode {
if node1.name == "rat" && node2.name == "cat" {
for rat in rats {
if node1 == rat.node {
rat.die()
}
}
Cat.shared.resetPosition()
return
}
else if node1.name == "cat" && node2.name == "rat" {
for rat in rats {
if node2 == rat.node {
rat.die()
}
}
Cat.shared.resetPosition()
return
}
If I try playing around with the contactTestBitMasks and making them something different like "ingredients", then I can see that the cat and rat are interacting with ingredients but it seems like they just wont interact with eachother.
rats and cats won't trigger contacts with each other because both have isDynamic set to false. At least one of them needs to be dynamic before a contact is triggered.
From https://developer.apple.com/documentation/spritekit/skphysicsbody
The isDynamic property controls whether a volume-based body is
affected by gravity, friction, collisions with other objects, and forces or impulses you directly apply to the object.

Drop Chance in swift [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 7 years ago.
Improve this question
I recentrly started to work with swift, and me and my Mate are trying to make a Small game to test what we learned..
we made a sort of monster fighting game ,no moving object just clicking on the monster.
So far so good evrything works as it should, got Upgrade to get more Hp or Str to do more damage on the monster, and to get more Hp when it hits back.
that said. i'm made items to the game but i want them to drop by the monster.
made different types and would like to know if anyone knows a code to get a Drop Chance on the Items...
like
"Normal" = 73%
"Rare" = 25%
"Legend" = 2%
I Looked around on evry search engine but can't find anything that i need.
Hope to get a reply soon
Thanks.
Greetz Kristof.V
This logic is the same of the answer by Avt, just a different implementation.
let random = Int(arc4random_uniform(100))
switch random {
case 0...72: print("Normal")
case 73...97: print("Rare")
case 98...99: print("Legend")
default: fatalError("Wrong number")
}
Use a random function arc4random_uniform
Returns a random number between 0 and the inserted parameter minus 1.
For example arc4random_uniform(3) may return 0, 1 or 2 but not 3.
You can use it like:
let random = Int(arc4random_uniform(100))
if random < 73 {
// drop normal
}
else if random < 73+25 {
// drop rare
} else {
// drop legend
}
(Edited: to include also weapon type as per follow-up question Random number to print array (Swift))
As above, use the arc4random function. A more complete "Monster" example follows.
It could be appropriate to hold different rarity and weapon types as enum cases
enum Rarities {
case Normal
case Rare
case Legendary
}
enum Weapons {
case Knife
case Sword
case Katana
}
Each monster could be an instance of a Monster class with it's own specific item and rarity drop rates, initialized when creating the Monster object.
class Monster {
var name : String
var dropRatesRarity = [Rarities:Double]()
var dropRatesWeapons = [Weapons:Double]()
init(name: String, dropRatesRarity: [Rarities:Double], dropRatesWeapons: [Weapons:Double]) {
self.name = name
var rateSum = dropRatesRarity.values.reduce(0.0, combine: +)
var dropRatesCumSum = 0.0
for (k, v) in dropRatesRarity {
self.dropRatesRarity[k] = v/rateSum + dropRatesCumSum
dropRatesCumSum += v/rateSum
}
rateSum = dropRatesWeapons.values.reduce(0.0, combine: +)
dropRatesCumSum = 0.0
for (k, v) in dropRatesWeapons {
self.dropRatesWeapons[k] = v/rateSum + dropRatesCumSum
dropRatesCumSum += v/rateSum
}
}
func dropItem() -> (Weapons,Rarities) {
return (generateItemType(), generateRarity())
}
func generateRarity() -> Rarities {
let random = Double(Float(arc4random()) / Float(UINT32_MAX))
return dropRatesRarity.filter({ (k, v) in v >= random }).minElement({ $0.1 < $1.1 })?.0 ?? .Normal
}
func generateItemType() -> Weapons {
let random = Double(Float(arc4random()) / Float(UINT32_MAX))
return dropRatesWeapons.filter({ (k, v) in v >= random }).minElement({ $0.1 < $1.1 })?.0 ?? .Knife
}
}
Example for some Monster instance:
/* Example */
var myDropRatesRarity = [Rarities:Double]()
myDropRatesRarity[.Normal] = 73 // relative drop rates, needn't sum to 100
myDropRatesRarity[.Rare] = 25
myDropRatesRarity[.Legendary] = 2
var myDropRatesWeapons = [Weapons:Double]()
myDropRatesWeapons[.Knife] = 50
myDropRatesWeapons[.Sword] = 30
myDropRatesWeapons[.Katana] = 20
var myMonster = Monster(name: "Godzilla", dropRatesRarity: myDropRatesRarity, dropRatesWeapons: myDropRatesWeapons)
var myItem = myMonster.dropItem()
print(myMonster.name + " dropped a \(myItem.0) of \(myItem.1) rarity!")
/* "Godzilla dropped a Katana of Normal rarity!" */
/* ... most likely Normal rarity ... keep grinding! */

SKPhysicsBody avoid collision Swift/SpriteKit

I have 3 SKSpriteNodes in my Scene. One bird, one coin and a border around the scene. I don't want the coin and the bird to collide with each other but withe the border.
I assign a different collisionBitMask and categoryBitMask to every node:
enum CollisionType:UInt32{
case Bird = 1
case Coin = 2
case Border = 3
}
Like so:
bird.physicsBody!.categoryBitMask = CollisionType.Bird.rawValue
bird.physicsBody!.collisionBitMask = CollisionType.Border.rawValue
coin.physicsBody!.categoryBitMask = CollisionType.Coin.rawValue
coin.physicsBody!.collisionBitMask = CollisionType.Border.rawValue
But the coin and the bird still collide with each other.
What am I doing wrong?
The bitmask is on 32 bits. Declaring them like you did corresponds to :
enum CollisionType:UInt32{
case Bird = 1 // 00000000000000000000000000000001
case Coin = 2 // 00000000000000000000000000000010
case Border = 3 // 00000000000000000000000000000011
}
What you want to do is to set your border value to 4. In order to have the following bitmask instead :
enum CollisionType:UInt32{
case Bird = 1 // 00000000000000000000000000000001
case Coin = 2 // 00000000000000000000000000000010
case Border = 4 // 00000000000000000000000000000100
}
Keep in mind that you'll have to follow the same for next bitmask : 8, 16, ... an so on.
Edit :
Also, you might want to use a struct instead of an enum and use another syntax to get it easier (it's not mandatory, just a matter of preference) :
struct PhysicsCategory {
static let None : UInt32 = 0
static let All : UInt32 = UInt32.max
static let Bird : UInt32 = 0b1 // 1
static let Coin : UInt32 = 0b10 // 2
static let Border : UInt32 = 0b100 // 4
}
That you could use like this :
bird.physicsBody!.categoryBitMask = PhysicsCategory.Bird
bird.physicsBody!.collisionBitMask = PhysicsCategory.Border
coin.physicsBody!.categoryBitMask = PhysicsCategory.Coin
coin.physicsBody!.collisionBitMask = PhysicsCategory.Border