How to detect if, one sprite node is the same colour as another sprite node then add to score if they are the same if not "restart game" - sprite-kit

So I have a "Ball" (player) that you control, when the player hits a "colorOrb" the Ball changes to that colour all at random, so that part works, but I'm stuck on how to detect if the player changes to a random colour on how to detect if once it goes through a wall also of a random colour if they are both the same colour and add +1 or whatever to score.
I've tried multiple ways but can't seem to figure it out. thanks in advance :)

Given a Ball and a Wall class
class Ball: SKSpriteNode { }
class Wall: SKSpriteNode { }
In your didBegin(contact:) just compare the color property.
class GameScene: SKScene, SKPhysicsContactDelegate {
func didBegin(_ contact: SKPhysicsContact) {
let nodeA = contact.bodyA.node
let nodeB = contact.bodyB.node
guard let nodes: (ball:Ball, wall:Wall) = (nodeA, nodeB) as? (Ball, Wall) ?? (nodeB, nodeA) as? (Ball, Wall) else { return }
if nodes.ball.color == nodes.wall.color {
// same color
// TODO...
}
}
}

Related

GameplayKit find path to moving sprite

I want a sprite to follow the player and I tried to achieve this with the Pathfinding of GameplayKit. This is working, but there's problem. I get the path from the enemy sprite to the player sprite with the following code:
func moveEnemy(to playerPos: CGPoint){
guard !moving else { return }
moving = true
let startNode = GKGraphNode2D(point: float2(Float(self.position.x), Float(self.position.y)))
let endNode = GKGraphNode2D(point: float2(Float(playerPos.x), Float(playerPos.y)))
PhysicsHelper.graph.connectUsingObstacles(node: startNode)
PhysicsHelper.graph.connectUsingObstacles(node: endNode)
let path = PhysicsHelper.graph.findPath(from: startNode, to: endNode)
guard path.count > 0 else{ return }
var actions = [SKAction]()
for node in path{
if let point2d = node as? GKGraphNode2D{
let point = CGPoint(x:CGFloat(point2d.position.x) , y:CGFloat(point2d.position.y))
let action = SKAction.move(to: point, duration: 1)
actions.append(action)
}
}
let seq = SKAction.sequence(actions)
self.run(seq) {
self.moving = false
}
}
This is called in another function of my enemy in this block:
case .follow:
if allowMovement{
if self.action(forKey: "Hurt") == nil{
// orientEnemy(to: playerPos)
moveEnemy(to: playerPos)
// animateWalk()
}
}
and this function is called in the update function of my GameScene in this block:
if enemy.checkCircularIntersection(player: player, node: enemy, radius: 40){
print("Enemy is close to player")
enemy.update(playerPos: player.position)
}
The checkCircularIntersection function only checks, if the player is near the enemy.
My problem is that the enemy follows the player but when the player moves, the enemy moves to the point where the player stands before, then it stops for a second and then it moves again to the point where the player stood. But the player is already moved away.
Is there a way to let the enemy permanently follow the player and avoid obstacles without stopping?
Your code specifically guards against moving again until the existing move is completed.
guard !moving else { return }
moving = true
If you want to use paths you will have to recompute and reapply the path movement regularly to account for the target's movement.
However, to achieve your aim, Paths are not the most suitable GameKit tool to use. GKAgents are specifically designed for that kind of thing. It will require a significant refactoring of your code, but take a look at Agents, Goals and Behaviours.

Sprite Kit - Sometimes ball disappearing from the screen?

I'm new with sprite kit. I have tried simple ball bouncing game with 2 player, another is tracking the ball slowly. But I have discovered a problem. When I move the line to ball (with edge) ball disappearing from the screen. Another times not a problem, ball bouncing. What is the problem?
I have one GameScene, sks and ViewController. My sprite nodes coming from sks. If someone explain this case. It would be better. I have attached what I did below.
My GameScene:
class GameScene: SKScene {
var ball = SKSpriteNode()
var enemy = SKSpriteNode()
var main = SKSpriteNode()
override func didMove(to view: SKView) {
ball = self.childNode(withName: "ball") as! SKSpriteNode
enemy = self.childNode(withName: "enemy") as! SKSpriteNode
main = self.childNode(withName: "main") as! SKSpriteNode
ball.physicsBody?.applyImpulse(CGVector(dx: -20, dy: -20))
ball.physicsBody?.linearDamping = 0
ball.physicsBody?.angularDamping = 0
let border = SKPhysicsBody(edgeLoopFrom: self.frame)
border.friction = 0
border.restitution = 1
self.physicsBody = border
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let location = touch.location(in: self)
main.run(SKAction.moveTo(x: location.x, duration: 0.2))
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let location = touch.location(in: self)
main.run(SKAction.moveTo(x: location.x, duration: 0.2))
}
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
enemy.run(SKAction.moveTo(x: ball.position.x, duration: 0.5))
}
View controller:
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
if let view = self.view as! SKView? {
// Load the SKScene from 'GameScene.sks'
if let scene = SKScene(fileNamed: "GameScene") {
// Set the scale mode to scale to fit the window
scene.scaleMode = .aspectFill
// Present the scene
view.presentScene(scene)
}
view.ignoresSiblingOrder = true
}
}
override var prefersStatusBarHidden: Bool {
return true
}
Pad settings:
Ball settings:
Some updates
I have tried some messages in update function, then encountered with same case ball goes outside from left side of the device (using iPhone 6S)
2016-12-08 14:27:54.436485 Pong[14261:3102941] fatal error: ball out of left bounds: file
You're pinching the ball against the wall, with the enemy. This means that the force is eventually enough to create enough speed of ball movement/force to overcome the physics system, so it pops through the wall. If you make your enemy stop before it pinces the ball against the wall, you should be fine.
This 'pincing' is occurring because of this line of code:
enemy.run(SKAction.moveTo(x: ball.position.x, duration: 0.5))
This is making the enemy chase the ball, which is a good idea for a ball game, but for the way it's being moved is wrong. Using an Action means the enemy has infinite force applied to it, and is aiming for the middle of the ball.
So when the ball gets to the wall, it's stopped against a physics object with infinite static force, then this enemy comes along and applies infinite force from the other side... and the ball either pops inside the bounds of the enemy, or over the other side of the wall, because it's being crushed by infinite forces.
So you either need to take very good care of how you control the enemy with Actions, or use forces to control the enemy, as these won't be infinite, and the physics system will be able to push back on the enemy.
How easy is it to reproduce the problem? In update(), print the ball's position to see where it is when it has 'disappeared'. (this will produce a lot of output, so be warned).
From what you've posted, it doesn't look like the ball is set to collide with the border, meaning the ball will not react (i.e. bounce off) the border and the border itself is immobile (as it's an edge-based physics body). This, combined with a high ball velocity (from a hard hit) might make it possible that you have hit the ball so hard with the 'main' sprite that it's gone through the border - using preciseCollisionDetection=true might resolve this but give the border a category first and add this to the ball's collisionBitMask.
here is an example of what Steve is saying (in your .update())
if ball.position.x > frame.maxX { fatalError(" ball out of right bounds") }
if ball.position.x < frame.minX { fatalError(" ball out of left bounds") }
if ball.position.y > frame.maxY { fatalError(" ball out of top bounds") }
if ball.position.y < frame.minY { fatalError(" ball out of bottom bounds) }
you could also just spam your debug window:
print(ball.position)
This will help you to find out what is going on--if your ball is flying through the boundary, or if it's getting destroyed somewhere, or some other possible bug.
As a workaround (for now) I would just replace the above "fatalError" with "ball.position = CGPoint(x: 0, y: 0)" or some other position to "reset" the ball in case of it getting lost.
You could even store it's last position in a variable, then restore it to that should the above if-statements trigger.
var lastBallLocation = CGPoint(x: 0, y: 0) // Just to initialize
override func update( prams ) {
if ball.position.x > frame.maxX { ball.position = lastBallLocation }
// .. copy the other three cases
lastBallLocation = ball.position // update only on successful position
Or, you could try making the walls thicker (use a shape node or spritenode and lay them on the outside of the frame such as the walls of a house, and your view on screen is the "room")
each wall also has a physics body for bouncing:

How do I use .SKS SKSpriteNode in GameScene?

I have the player all set up with its action and everything in GameScene.swift. The thing is, I don't want it to be a still image so I removed
var player: SKSpriteNode = SKSpriteNode(imageNamed: "player1.png")
and I want to make it so I can use the animation SKSpriteNode I made in GameScene.sks, as the player. The SKSpriteNode in GameScene.sks is named player.
Help me out here?
You can access items created in GameScene.sks in the custom class function didMoveToView;
var player: SKSpriteNode!
override func didMove(to view: SKView) {
guard let player = childNode(withName: "player") as? SKSpriteNode else {
        fatalError("player node not loaded")
    }
    self.player = player
}

Detecting when two of the same colors collide

I have a square consisting of four different colors in the middle of my scene. At the same time, I have smaller squares of the same color randomly generated from each sides of the scene with the intention of colliding with the square in the middle. (Blue to blue, yellow to yellow, etc).
My goal is to have it set up so that when a blue square collides with a blue square or any of the like, it will .removeFromParent(). How should I go about doing this? Will post code if necessary.
Edit:
enum BodyType: UInt32 {
case blueSquare = 1
case redSquare = 2
case yellowSquare = 4
case greenSquare = 8
}
let contactMask = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask
didBeginContact() {
switch(contactMask) {
case BodyType.redSquare.rawValue | BodyType.redSquare.rawValue:
let scoreLabel = childNodeWithName("scores") as! Points
scoreLabel.increment()
let firstNode = contact.bodyB.node
firstNode?.removeFromParent()
default:
return
}
}
First thing you should do is set up the contactTestBitMasks & categoryBitMasks on all of your SKSpriteNodes, like this -
struct PhysicsCatagory {
static let FirstPerson : UInt32 = 0x1 << 1
static let SecondPerson : UInt32 = 0x1 << 2
}
override func didMoveToView(view: SKView) {
...
firstPerson.SKPhysicsBody?.catagoryBitMask = PhysicsCatagory.FirstPerson
firstPerson.SKPhysicsBody?.contactTestBitMask = PhysicsCatagory.SecondPerson
...
secondPerson.SKPhysicsBody?.catagoryBitMask = PhysicsCatagory.SecondPerson
secondPerson.SKPhysicsBody?.contactTestBitMask = PhysicsCatagory.FirstPerson
...
}
This is just setting up the catagoryBitMask and the contactTestBitMask. The categoryBitMask will be equal to the object you are currently editing, whereas, the contactTestBitMask will be equal to the object you want the object to collide with.
Also, before we move on, we want to add the Contact Delegate to our scene.
class GameScene: SKScene, SKPhysicsContactDelegate{...
And then add the delegate to our scene -
override func didMoveToView(view: SKView) {
...
self.physicsWorld.contactDelegate = self
...
Next, you add the didBeginContact
func didBeginContact(contact: SKPhysicsContact) {
let firstBody = contact.bodyA.node as! SKSpriteNode!
let secondBody = contact.bodyB.node as! SKSpriteNode!
}
Lastly inside of that, test...
func didBeginContact(contact: SKPhysicsContact) {
let firstBody = contact.bodyA.node as! SKSpriteNode!
let secondBody = contact.bodyB.node as! SKSpriteNode!
if firstBody.color == secondBody.color{
firstBody.removeFromParent()
secondBody.removeFromParent()
}
}
Hope that helps! :D
Once you detect a collision, compare the colors of the colliding squares and if equal, call .removeFromParent(). If you post code I could try to give the specific methods that would help.
If you want to get fancy you could create a subclass for your squares with a colorTag property (1 = blue, 2 = yellow ect.) and then compare the tags of the colliding squares. Although I doubt the cost of comparing the colors is much.

How to end a game when hitting an object from below?

Hey so I am making this project in which the player has to jump platforms all the way to the top. Some monsters spawn randomly throughout the game. So the idea is to lose the game when you hit them from below, but can go on if you jump on them. I already did the part in which the player jumps on it and you destroy the monster but I am still stuck on that part to lose the game when you hit it from below. Any ideas on how I can manage to do this? For this project I followed Ray Wenderlich's tutorial on How To Make a Game Like Mega Jump.
So on my GameScene, I have the didBeginContact method:
func didBeginContact(contact: SKPhysicsContact) {
var updateHUD = false
let whichNode = (contact.bodyA.node != player) ? contact.bodyA.node : contact.bodyB.node
let other = whichNode as GameObjectNode
updateHUD = other.collisionWithPlayer(player)
if updateHUD {
lblStars.text = String(format: "X %d", GameState.sharedInstance.stars)
lblScore.text = String(format: "%d", GameState.sharedInstance.score)
}
}
Which then calls the method from the GameObjectNode Scene.
class MonsterNode: GameObjectNode {
var monsterType: MonsterType!
override func collisionWithPlayer(player: SKNode) -> Bool {
if player.physicsBody?.velocity.dy < 0 {
player.physicsBody?.velocity = CGVector(dx: player.physicsBody!.velocity.dx, dy: 450.0)
if monsterType == .Normal {
self.removeFromParent()
}
}
When the player jumps on top of the monster, the monster is removed from the parent. I was trying to set that if the player's velocity is greater than 0 when colliding with the monster, then the player is removed from parent. Then when I go back to my GameScene, I could declare something in my update method so that when the player is removed from the parent call the endGame() method.
override func update(currentTime: NSTimeInterval) {
if gameOver {
return
}
if Int(player.position.y) > endLevelY {
endGame()
}
if Int(player.position.y) < maxPlayerY - 500 {
endGame()
}
}
Of course I wasn't able to make that work, and I still can't. So if anyone could help me out on how I can manage to do this, or probably some tutorial which could guide me into doing this I would really appreciate it! Thank you in advance.
First you use the didBeginContact method to establish if a contact between player and monster has been made. Next you compare the y positions of the player and monster. If the monster's y position is greater than than the player's... BANG, player dies.
The code sample assumes you have multiple monsters, each with a unique name, and have them all stored in an array.
- (void)didBeginContact:(SKPhysicsContact *)contact {
uint32_t collision = (contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask);
if (collision == (CategoryMonster | CategoryPlayer)) {
for(SKSpriteNode *object in monsterArray) {
if(([object.name isEqualToString:contact.bodyB.node.name]) || ([object.name isEqualToString:contact.bodyA.node.name])) {
if(object.position.y > player.position.y) {
// moster is above player
}
}
}
}
Couple of notes... If you have more than one monster active at any one time, you will need to have a unique name for each one. Reason being that you will need to know which monster contacted the player and that can only happen if you can differentiate between monsters. Hence the unique name for each one.
The if check for the y position is a simple one and only needs +1 y to be fulfilled. If it is possible for your player to make side contact with a monster and not die, you can change the if condition to be something like if(object.position.y > player.position.y+50) to make sure that the contact was actually from the bottom.
(I am not all too proficient in Swift yet so code sample is in Obj-C.)