How to stop a force getting applied to SKSprtieNode? - xcode

I have couple of small paddles alligned next to each other with size of roughly 50x10 moving from left to right and right to left constantly, which would also randomly stop moving from time to time and become stationary. There is a ball that falls off towards them, and upon hitting a paddle, if its not moving and is stationary, the ball will bounce off it and go straight back up. However, if the paddles are moving and the ball hits one of the paddles during that time, instead of the ball hitting them and going right back up, it hits the paddle and starts moving either right or left (depending on which direction the paddles are moving) while going back up. I'm trying to have the ball go right back up even if it hits the paddle while its moving (pretty much a forever bouncing ball against the paddles).
I have tried couple of different ways to solve this, but I think I'm missing something important that's making it behave like this. Here is the code:
class GameScene: SKScene, SKPhysicsContactDelegate {
let greenPaddle = SKSpriteNode(imageNamed: "Green")
var ball = SKSpriteNode()
let ballCategory: UInt32 = 1 << 0
let greenPaddleCategory: UInt32 = 1 << 1
override func didMoveToView(view: SKView) {
self.physicsWorld.contactDelegate = self
greenPaddle.position = CGPoint(x: 0, y: frame.size.height / -6.5)
greenPaddle.physicsBody = SKPhysicsBody(rectangleOfSize: greenPaddle.size)
greenPaddle.physicsBody!.affectedByGravity = false
greenPaddle.physicsBody!.dynamic = false
greenPaddle.physicsBody!.categoryBitMask = greenPaddleCategory
greenPaddle.physicsBody!.contactTestBitMask = ballCategory
ball = SKSpriteNode(imageNamed: "Red")
ball.position = CGPoint(x: self.frame.size.width / 2, y: self.frame.size.height / 2)
ball.physicsBody = SKPhysicsBody(circleOfRadius: ball.size.height / 2)
ball.physicsBody!.friction = 0
ball.physicsBody!.restitution = 1
ball.physicsBody!.linearDamping = 0
ball.physicsBody!.allowsRotation = false
ball.physicsBody!.categoryBitMask = ballCategory
ball.physicsBody!.contactTestBitMask = greenPaddleCategory
addChild(circle)
}
func didBeginContact(contact: SKPhysicsContact) {
let bodyA = (contact.bodyA.categoryBitMask <= contact.bodyB.categoryBitMask) ? contact.bodyA : contact.bodyB
let bodyB = (contact.bodyA.categoryBitMask <= contact.bodyB.categoryBitMask) ? contact.bodyB : contact.bodyA
if (bodyA.categoryBitMask) == ballCategory && (bodyB.categoryBitMask) == greenPaddleCategory {
greenPaddle.physicsBody!.friction = 0
greenPaddle.physicsBody!.restitution = 0
}
}

I do not really understand the problem; however, you have forgotten to set the physics world in the didmovetoview.
self.physicsWorld.contactDelegate = self
If the problem is that the contact do not work, this would work:)

Related

How to Animate a UIImage out of the Screen in Swift

i have searched around the web but nothing could answer my question.
I am trying to animate an UIImageview out of the Screen, set the alpha of it to 0, reset its position and reset the alpha to 1 again.
I can't seem to get the right position where to animate the Image to.
I have set up a gesturerecognizer to drag the image around and when the center of the image is above a certain point, i want to animate it out of screen.
This is the Regocnizercode :
#objc func imageDragged(gestureRecognizer: UIPanGestureRecognizer){
//Current Point where the label is dragged
let draggedLabelPoint = gestureRecognizer.translation(in: view)
//Updating the center of the label : viewcenter +/- currentpoint
matchImageView.center = CGPoint(x: view.bounds.width/2 + draggedLabelPoint.x, y: view.bounds.height/2 + draggedLabelPoint.y)
let xFromCenter = view.bounds.width/2 - matchImageView.center.x
var rotation = CGAffineTransform(rotationAngle: xFromCenter / -500)
let scale = min(100/abs(xFromCenter),1)
var scaledAndRotated = rotation.scaledBy(x: scale, y: scale)
//Transforming the Item respective to the distance from center
matchImageView.transform = scaledAndRotated
if gestureRecognizer.state == .ended {
if matchImageView.center.x < (view.bounds.width/2 - 100) {
animateImageToSide(target: CGPoint(x: view.bounds.width - 200, y: matchImageView.center.y))
matchImageView.alpha = 0
}
if matchImageView.center.x > (view.bounds.width/2 + 100) {
animateImageToSide(target: CGPoint(x: view.bounds.width + 200, y: matchImageView.center.y))
matchImageView.alpha = 0
}
//Reset the scaling variables and recentering the Item after swipe
rotation = CGAffineTransform(rotationAngle: 0)
scaledAndRotated = rotation.scaledBy(x: 1, y: 1)
matchImageView.transform = scaledAndRotated
matchImageView.center = CGPoint(x: view.bounds.width/2, y: view.bounds.height/2)
}
}
I am sorry if the resetting part is irrelevant for you, but im not sure if this could cause this behavior.
And this is my current Animationmethod :
func animateImageToSide(target:CGPoint){
UIImageView.animate(withDuration: 0.5, delay: 0, options: .curveLinear, animations: {self.matchImageView.center = target}) { (success: Bool) in
self.updateImage()
self.fadeInImage()//This is where i set the alpha back to 1
}
}
I am not sure if a CGPoint is what i need to use.
The current behavoir of the image is very weird. Most of the time it snaps to a specific place, regardless of the targetposition. Maybe my attempt on this is entirely wrong.
Thanks for your help !

SceneKit: orient one node toward another in 3Dspace

I need to orient one node to point its Z-axis at another node in 3D. Yeah, the perfect job for the LookAtConstraint. And for most of my work LookAt is fine. But when I apply LookAt to a particular node, I can no longer animate that node's translation with SCNAction. Picture a hydrogen atom leaving a molecule as it ionizes. The orientation is needed to properly rotate the bond (a cylinder) bewteen the hydrogen and an oxygen atom on the molecule.
I can orient the bond FROM the oxygen TO the hydrogen and animate. But this disorients most of the other bonds which were getting by just fine with LookAt's.
I gave this a mighty try before realizing it answers a somewhat different question:
Calculate rotations to look at a 3D point?
I had a similar issue with a project. What I eventually realized was that I need to use multiple constraints. One for translation (movement) and the other using the look at constraint.
I would move the object and then apply the look at constraint; in this case, it was a camera following an objects being moved using actions. Code snippet follows:
let targetNodeConstraint = SCNLookAtConstraint(target: someObject)
targetNodeConstraint.gimbalLockEnabled = true
let followObjectConstraint = SCNTransformConstraint(inWorldSpace: true, withBlock: { (node, matrix) -> SCNMatrix4 in
let transformMatrix = SCNMatrix4MakeTranslation(
self.someObject.position.x - 1.0,
self.someObject.position.y, self.someObject.position.z + 1.0)
return transformMatrix
})
// Position the object behind the other object & rotate it to
roadCamera.constraints = [followObjectConstraint, targetNodeConstraint]
The important thing to note is the order in which the constraints are added to the object using an array. In the code above, I am ignoring the current matrix before I apply a transform matrix (I should re-write this code someday)
The complete source code of this "experiment" is on GitHub as I try things out.
https://github.com/ManjitBedi/CubeTrip
Hopefully, this is helpful.
My solution here. Deal with situation that node continuously translate in space and should always toward a position.
#discardableResult
func yew(_ node:SCNNode, toPosition position:SCNVector3) -> Float
{
var eularAngle = SCNVector3Zero
let tranform = node.transform
var forward = GLKVector3Make(tranform.m31, tranform.m32, tranform.m33)
var toWard = GLKVector3Make(position.x - node.position.x, position.y - node.position.y, position.z - node.position.z)
forward = GLKVector3Normalize(GLKVector3Make(forward.x, 0, forward.z))
toWard = GLKVector3Normalize(GLKVector3Make(toWard.x, 0, toWard.z))
var dotProduct = GLKVector3DotProduct(forward,toWard)
dotProduct = (dotProduct > 1) ? 1 : ((dotProduct < -1) ? -1 : dotProduct)
var yew = acos(dotProduct)
if yew < 0 {
assert(false)
}
//toward is clockwise of forward
let isCW = GLKVector3CrossProduct(forward, toWard).y < 0
if isCW {
yew = -yew
}
eularAngle.y = yew
node.eulerAngles = SCNVector3Make(eularAngle.x + wrapperNode.eulerAngles.x,
eularAngle.y + wrapperNode.eulerAngles.y,
eularAngle.z + wrapperNode.eulerAngles.z)
return yew
}
#discardableResult
func pitch(_ node:SCNNode, toPosition position:SCNVector3) -> Float{
var eularAngle = SCNVector3Zero
let tranform = node.transform
var toWard = GLKVector3Make(position.x - node.position.x, position.y - node.position.y, position.z - node.position.z)
var forward = GLKVector3Make(tranform.m31, tranform.m32, tranform.m33)
forward = GLKVector3Normalize(forward)
toWard = GLKVector3Normalize(toWard)
var dotProduct = GLKVector3DotProduct(forward,toWard)
dotProduct = (dotProduct > 1) ? 1 : ((dotProduct < -1) ? -1 : dotProduct)
var pitch = acos(dotProduct)
//toward is clockwise of forward, if right vector of model and crossProfuct.x has same direction
let crossProduct = GLKVector3CrossProduct(forward, toWard)
let isCW = (crossProduct.x <= 0) != (tranform.m11 <= 0)
if isCW {
pitch = -pitch
}
eularAngle.x = pitch
node.eulerAngles = SCNVector3Make(eularAngle.x + node.eulerAngles.x,
eularAngle.y + node.eulerAngles.y,
eularAngle.z + node.eulerAngles.z)
return pitch
}
func orient(_ node:SCNNode, toPosition position:SCNVector3) {
self.yew(node, toPosition: position)
self.pitch(node, toPosition: position)
}

during Spritenode animation and movement appear red large x

Here is problem in which I add zoombie sprite to the scene every one second. When I add another sub animated zoombie to the zoombie node, sometimes it loads animated texture, and other times appear red large X.
func addMonster() {
let zoombieSprite = SKSpriteNode(color: SKColor.greenColor(), size: CGSizeMake(40, 60))
// Determine where to spawn the monster along the Y axis
let actualY = randRange(lower: zoombieSprite.size.height, upper: size.height - zoombieSprite.size.height)
// Position the monster slightly off-screen along the right edge,
// and along a random position along the Y axis as calculated above
zoombieSprite.position = CGPoint(x: size.width + zoombieSprite.size.width/2, y: actualY)
zoombieSprite.physicsBody = SKPhysicsBody(rectangleOfSize: zoombieSprite.size) // 1
zoombieSprite.physicsBody?.dynamic = true // 2
zoombieSprite.physicsBody?.categoryBitMask = PhysicsCategory.Monster // 3
zoombieSprite.physicsBody?.contactTestBitMask = PhysicsCategory.Projectile // 4
zoombieSprite.physicsBody?.collisionBitMask = PhysicsCategory.None // 5
addChild(zoombieSprite)
//zoombieSprite.addChild(createAnimatedZoombie())
let zoombieAnimation = SKAction.runBlock({
zoombieSprite.addChild(self.createAnimatedZoombie())
})
// Determine speed of the monster
let actualDuration = randRange(lower: 6.0, upper: 10.0)
//print("actualDuration = \(actualDuration)")
let actionMove = SKAction.moveTo(CGPoint(x: -zoombieSprite.size.width/2, y: actualY), duration: NSTimeInterval(actualDuration))
// Create the actions
let actionMoveDone = SKAction.removeFromParent()
zoombieSprite.runAction(SKAction.sequence([zoombieAnimation ,actionMove,actionMoveDone]))
}
//MARK: - ANIMATE FRAME AND MOVE ZOOMBIE
func createAnimatedZoombie () -> SKSpriteNode {
let animatedZoobieNode = SKSpriteNode(texture: spriteArray[0])
let animationFrameAction = SKAction.animateWithTextures(spriteArray, timePerFrame: 0.2)
let durationTime = SKAction.waitForDuration(0.1)
let repeatAction = SKAction.repeatActionForever(animationFrameAction)
let quenceAction = SKAction.sequence([durationTime, repeatAction])
animatedZoobieNode.runAction(quenceAction)
return animatedZoobieNode
}
Thanks very much my respectable brother Joseph Lord and Thank God i solved my problem by just dividing sprite kit atlas array count property by 2 because in this folder i had put both #2x and #3x images so when i used to get number of images from this atlas folder it used to return the number which was addition of #2x and #3x images.

Move SKSpriteNode until touches ended

I created this game in Spite Kit in Swift. I create a ship that I can determine its location be touching above or underneath the location of the ship. The ship will move towards the Y axis where I placed my finger. However at this point the ship will only move by a specific amount, in this case 30 or -30. How can I rewrite this code so the ship will keep on moving until my release of my finger?
Thanks!
func addShip() {
// Initializing spaceship node
ship = SKSpriteNode(imageNamed: "spaceship")
ship.setScale(0.45)
ship.zRotation = CGFloat(-M_PI/2)
// Adding SpriteKit physics body for collision detection
ship.physicsBody = SKPhysicsBody(rectangleOfSize: ship.size)
ship.physicsBody?.categoryBitMask = UInt32(shipCategory)
ship.physicsBody?.affectedByGravity = false
ship.physicsBody?.dynamic = true
ship.physicsBody?.contactTestBitMask = UInt32(obstacleCategory)
//NEWLY ADDED
ship.physicsBody?.contactTestBitMask = coinCategory
ship.physicsBody?.collisionBitMask = 0
ship.name = "ship"
ship.position = CGPointMake(120, 160)
self.addChild(ship)
actionMoveUp = SKAction.moveByX(0, y: 30, duration: 0.2)
actionMoveDown = SKAction.moveByX(0, y: -30, duration: 0.2)
}
i don't like to use SKActions for these types of things because they're really meant for one off simple animations.. However, it may be appropriate depending your game's requirements..
if you insist on using SKActions then instead of doing moveBy, why don't you just use moveTo utilizing touch location? The only thing you'd have to do is come up with a ratio based on the distance your sprite needs to travel so that your animation duration is correct. example:
let curPoint = self.sprite.position
let destinationPoint = touch.position
let diffPoint = CGPoint(
x: curPoint.x - destinationPoint.x,
y: curPoint.y - destinationPoint.y
)
let distance = sqrt(pow(diffPoint.x, 2) + pow(diffPoint.y, 2))
let durationRatio = distance / CGFloat(600)
let moveAction = SKAction.moveTo(point, duration: NSTimeInterval(durationRatio))
self.sprite.runAction(moveAction, withKey: "move")
then when you want to cancel the movement. in touchesEnded you do
self.sprite.removeActionForKey("move")
using physics velocity
let curPoint = CGPoint(x: 10, y: 10)
let destinationPoint = CGPoint(x: 100, y: 100)
let diffPoint = CGPoint(
x: curPoint.x - destinationPoint.x,
y: curPoint.y - destinationPoint.y
)
let distance = sqrt(pow(diffPoint.x, 2) + pow(diffPoint.y, 2))
let normalizedVector = CGPoint(
x: diffPoint.x / distance,
y: diffPoint.y / distance
)
let speed = CGFloat(100)
let velocity = CGVector(
dx: normalizedVector.x * speed,
dy: normalizedVector.y * speed
)
self.sprite.physicsBody!.velocity = velocity
in touchesEnded you do
self.sprite.physicsBody!.velocity = CGVector(dx: 0.0, dy: 0.0)

Multiple Contacts Issue in SpriteKit Game

In my game, I have a ball, walls and gaps between these walls. Ball and walls are SKSpriteNode, gap is SKNode. I use gap to count the score.
I have the following code to handle the contacts and collisions. I checked plenty of documents and tutorials but cant find why there are multiple gap contacts happen when my ball contacts the gap. It prints "gap contact" for 6 times every time.
Bodytypes definitions:
enum BodyType:UInt32 {
case ball = 1
case wall = 2
case gap = 4
}
Ball:
var ball = SKSpriteNode(texture: SKTexture(imageNamed: "img/ball.png"))
ball.position = CGPointMake(frame.width/2, 50)
ball.physicsBody = SKPhysicsBody(texture: ball.texture, size: ball.size)
ball.physicsBody?.dynamic = true
ball.physicsBody?.allowsRotation = false
ball.physicsBody?.categoryBitMask = BodyType.ball.rawValue
ball.physicsBody?.collisionBitMask = BodyType.wall.rawValue
ball.physicsBody?.contactTestBitMask = BodyType.gap.rawValue | BodyType.wall.rawValue
Walls (walls are created through scheduledTimerWithTimeInterval every 2 seconds):
var wall1 = SKSpriteNode(texture: nil, color: UIColor.blackColor(), size: CGSizeMake(frame.size.width, 20))
var wall2 = SKSpriteNode(texture: nil, color: UIColor.blackColor(), size: CGSizeMake(frame.size.width, 20))
let gapSize = 70
let gapMargin = 50
let gapPoint = CGFloat(arc4random_uniform(UInt32(frame.size.width) - gapSize - (gapMargin*2)) + gapMargin)
wall1.position = CGPointMake(-wall1.size.width/2 + gapPoint, self.frame.size.height)
wall2.position = CGPointMake(wall1.size.width/2 + CGFloat(gapSize) + gapPoint, self.frame.size.height)
wall1.physicsBody = SKPhysicsBody(rectangleOfSize: wall1.size)
wall1.physicsBody?.dynamic = false
wall2.physicsBody = SKPhysicsBody(rectangleOfSize: wall2.size)
wall2.physicsBody?.dynamic = false
wall1.physicsBody?.categoryBitMask = BodyType.wall.rawValue
wall2.physicsBody?.categoryBitMask = BodyType.wall.rawValue
Gap Node:
var gap = SKNode()
gap.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(CGFloat(gapSize), wall1.size.height))
gap.position = CGPointMake(CGFloat(gapSize)/2 + gapPoint, self.frame.size.height)
gap.runAction(moveAndRemoveWall)
gap.physicsBody?.dynamic = false
gap.physicsBody?.collisionBitMask = 0
gap.physicsBody?.categoryBitMask = BodyType.gap.rawValue
Contact Handling:
func didBeginContact(contact: SKPhysicsContact) {
let contactMask = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask
switch(contactMask) {
case BodyType.ball.rawValue | BodyType.gap.rawValue:
println("gap contact")
default:
println("wall contact")
movingObjects.speed = 0
explosion(ball.position)
gameOver = 1
}
}
Wall collision works perfectly and prints "wall contact" only once when I hit the wall but I couldnt fix this multiple contact issue between the ball and gap.
UPDATE: The problem is with the ball's physicsbody. If I use circleOfRadius it works perfectly but then, this doesnt provide a great experience to the player as my ball shape is not a perfect circle so I want to use the texture to cover the physicsbody.

Resources