Currently, I have a box(size of the iPhone that has a ball bouncing in it. When it hits the boundary of a wall, it bounces back as expected.
Now what I want to do is when the ball is going to the left and hits the left wall, I want it to appear on the right side and to continue going to the left. Also, if the ball moves right and hits the wall on the right then it should appear on the left still going right. (Like the old Asteroids game)
I thought that I could use didBeginContact and simply change the position. Problem is that changing the position using setPosition: doesn't actually change it. If I use the moveTOx:, which does work, the problem then is that the ball can't be moved to the right edge because of the call to didBeginContact on the right side gets called and it gets moved back to the left side.
The balls should move smoothly from one edge to the next. Perhaps didBeginContact is not the correct place to do this.
Any suggestions? I can't imagine this is a unique problem...
I found the solution (https://github.com/macshome/RockBuster/tree/master) and here it is:
// Create a method to be called from the update:
-(void)update:(NSTimeInterval)currentTime {
/* Called before each frame is rendered */
[self updateSpritePositions];
}
// In your method, iterate through your objects and set the position:
- (void)updateSpritePositions {
[self enumerateChildNodesWithName:#"ball" usingBlock:^(SKNode *node, BOOL *stop) {
// Get the current position
CGPoint nodePosition = CGPointMake(node.position.x, node.position.y);
// If we've gone beyond the edge warp to the other side.
if (nodePosition.x > (CGRectGetMaxX(self.frame) + 20)) {
node.position = CGPointMake((CGRectGetMinX(self.frame) - 10), nodePosition.y);
}
if (nodePosition.x < (CGRectGetMinX(self.frame) - 20)) {
node.position = CGPointMake((CGRectGetMaxX(self.frame) + 10), nodePosition.y);
}
if (nodePosition.y > (CGRectGetMaxY(self.frame) + 20)) {
node.position = CGPointMake(nodePosition.x, (CGRectGetMinY(self.frame) - 10));
}
if (nodePosition.y < (CGRectGetMinY(self.frame) - 20)) {
node.position = CGPointMake(nodePosition.x, (CGRectGetMaxY(self.frame) + 10));
}
}];
}
Related
I would like to know how to move a sprite in spritekit depending on the side of the screen the user is pressing down on. Like if the user taps the right side of the screen the sprite should move to the right and when the user removes his finger the sprites stops moving.
Any suggestions?
thank you so much
use the touchesBegan func and get position of touch with:
for location: AnyObject in touches {
var positionOfTouch = location.locationInNode(self)
//position of touch is of value CGPoint
}
store the value of xposition and yposition
var xPostion = positionOfTouch.position.x
var yPosition = positionOfTouch.position.y
//all in the touchesBegan func
make an if else statement to see if the user touches a side or not
if xPosition < 10 && yPosition > 10 && yPosition < self.size.height-10 {
//left side
let moveToLeftSide = SKAction.moveTo(x: 0, duration: 10)
object.run(moveToLeftSide)
}
continue setting values for the sides, then set the touchesEnded func
inside the touchesEnded func, write:
object.removeAllActions()
hope it helped... if you found this answer helpful remember to set this answer to correct ; )
So i'm using a spritenode with an image and I don't set the position of it. Funny enough I was having issues setting the position of it so I didn't and it by default was set to center of page (for obvious reason) which ended up being perfect. So now in my touchesbegan method i'm looking to change the said image IF the original image was pressed so i'm checking if the touched location is equal to the node (of the image)'s position. Then if that is true, I replace it with the new image which is called "nowaves".
for touch in touches
{
let location = touch.location(in: self)
if (location == audioPlaying.position)
{
audioPlaying = SKSpriteNode(imageNamed: "nowaves")
}
else
{
Do you guys think I need to readd it? Well right as I asked that I tested it to no result. This is what I tried:
audioPlaying.size = CGSize(width: frame.size.width / 13, height: frame.size.height / 20)
addChild(audioPlaying)
If anyone has any idea what the problem is I would love some feedback, thank you :D
By default, the position property returns the coordinates of the center of the node. In other words, unless you touched the exact center of the sprite, if (location == audioPlaying.position) will never be true. However, you should be aware that touching a node's exact center point is practically impossible.
So we use another method. We just need to check if the touched node is the node you want.
let touchedNode = self.nodes(at: touch.location(in: self)).first
if touchedNode == audioPlaying {
// I think setting the texture is more suitable here, instead of creating a new node.
audioPlaying.texture = SKTexture(imageNamed: "nowaves")
}
To add on to #sweeper ,
I had all my initial image content in the scenedidLoad method, rather than that I created a new method that I was already using for another functionality to start the game and had the initial image there instead and now everything works well.
I have a problem with SpriteKit and Swift.
I'm adding SKSpriteNodes at different points of the code to the scene - some of them are clickable, some are not. I use the clickable Nodes as a Menu for the player. So, for example - if he clicks the InventoryButtonNode he jumps into the Inventory. In the Inventory he can touch the PlayButton and jumps back into the game. So, first i add the Nodes:
override func didMoveToView(view: SKView) {
PlayButton = SKSpriteNode(imageNamed: "PlayButton")
PlayButton.size = CGSize(width: 100, height: 100)
PlayButton.position = CGPoint ... // not important
PlayButton.zPosition = 200
PlayButton.name = "PlayButton"
self.addChild(PlayButton)
InventoryButton = SKSpriteNode(imageNamed: "InventoryButton")
InventoryButton.size = CGSize(width: 100, height: 100)
InventoryButton.position = CGPoint ... // different position than PlayButton
InventoryButton.zPosition = 200
InventoryButton.name = "PlayButton"
self.addChild(InventoryButton)
In the override func touchesBegan I use these "menu"-Nodes, here for example the InventoryButton-Node.
if InventoryButton.containsPoint(touch.locationInNode(self)) {
print("Show Inventory")
ShowInventory()
}
Now in the ShowInventory() function i want to remove those "menu"-Buttons from view so that i can add other nodes to show the Inventory of the Player.
func ShowInventory(){
PlayButton.removeFromParent()
InventoryButton.removeFromParent()
}
If I build and run this, the Nodes will be removed - or lets better say - they will get invisible.
Because, if i touch now at the position of InventoryButton I still get the print "Show Inventory" - so the function still reacts to my touch even if the node is not visible.
My problem is, that i have like 4 different functions like ShowInventory() .. I have ShowGame() and so on .. and i want in these functions to completely remove the Nodes and the "Touch"-Ability ..
I need a function which can remove the Nodes completely ..
I have even tried:
func ShowInventory(){
self.removeAllChildren()
}
I get a grey Background without any nodes .. but still - if I touch where the Inventory Buttons position was i get the function called and the print "Show Inventory" ... it's frustrating.
It is because your check for which button is pressed does not take into account whether the button is visible.
Even if a node has been removed from its parent, it still has a position and size. Those two properties are used by containsPoint: to determine if a point is in a node
The easiest way to fix it would be to just check if the button has a parent node before checking to see if the button contains the point.
if InventoryButton.parrent != nil && InventoryButton.containsPoint(touch.locationInNode(self)) {
print("Show Inventory")
ShowInventory()
}
In my game, I can't really use the CameraNode because it'll screw up the way everything looks and moves properly. My player is moving along the x axis and I have this set up in my update() func:
if player.position.x > 50.0 {
foregroundNode.position = CGPointMake(-(player.position.x - 50), 0)
}
However, my player still moves off screen when tapped too much. How do I make it so that it will stay on the screen but the foreground node will still move backwards as my player moves forward? Will post more code if necessary.
you want to do something like
if player.position.x < 50
{
let dx = 50 - player.position.x // if player is at 49, the change is 1
player.position.x = 50
background.position.x += dx // we want to add dx and not subtract because the background moves in the opposite direction of the player
}
I am trying to animate multiple SKSpriteNode's of the same type using single SKAction.
What I would like to do, is to display four SKSpriteNode's of the same type on the screen.
Then animate them all when the button is pressed and move them to the left.
As soon as the first SKSpriteNode would leave the screen I want to add another SKSpriteNode off the screen position on the right side and add him to this SKAction loop.
Image below shows in detail what I am after.
So far I was able to display four SKSpriteNode's of the same type on the screen and add them to the array.
var squareBox = SKSpriteNode()
func addSquares(size:CGSize){
for var i = 0; i < 4; i++ {
// Create a new sprite node from an image
squareBox = SKSpriteNode(imageNamed: "noBox")
// Square pysics settings
squareBox.physicsBody = SKPhysicsBody(rectangleOfSize: self.squareBox.frame.size)
squareBox.physicsBody!.dynamic = false
squareBox.physicsBody!.categoryBitMask = squareBoxCategory
squareBox.physicsBody!.contactTestBitMask = leftEdgeCategory | edgeCategory
// SquareBox positioning on X
var xPos = size.width/5 + squareBox.size.height/2
var xPosInt = Int(xPos) * (i + 1)
xPos = CGFloat(xPosInt)
var yPos = size.height/2 + (squareBox.size.height/2)
squareBox.position = CGPointMake(xPos - squareBox.size.height/2, yPos)
self.addChild(squareBox)
squareArray.append(squareBox)
}
}
I am not sure if that is correct way to animate four SKSpriteNode's of the same type, but when the button is pressed they all seem to move to the left as desired.
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
self.nodeAtPoint(location)
if self.nodeAtPoint(location) == self.button {
let move = SKAction.moveByX(-size.width/5 - squareBox.size.height/2, y: 0, duration: 1)
print("button clicked!")
for box in squareArray {
box.runAction(move)
}
}
}
}
I created an invisible 1px line on the left edge of the screen to define when the first SKSpriteNode leaves the screen.
func addLeftEdge(size:CGSize){
let leftEdge = SKNode()
leftEdge.physicsBody = SKPhysicsBody(edgeFromPoint: CGPointMake(1, 1), toPoint: CGPointMake(1, size.height))
leftEdge.physicsBody!.categoryBitMask = leftEdgeCategory
self.addChild(leftEdge)
}
The problem with this approach is that SKSpriteNode's in the array when I animate them don't respond on didBeginContact when they touch the 1px line.
func didBeginContact(contact: SKPhysicsContact) {
println("contact!")
}
In the end I want to be able to apply the Force to all SKSpriteNode's on the screen for a few seconds to move them to the left and descend the force speed over the time until they all stop. My idea was to create four objects of the same type and add them to the screen + add them to the array var squareArray = [SKSpriteNode]().
Then when the first SKSpriteNode leaves the screen I would remove it from array and add it again at the end and position it off the screen on the right so it will be moved in seamless animation.
I have a feeling that my entire approach is wrong.
Please advise what is the best approach to animate multiple SKSpriteNode's to achieve my goal as described above. Am I on the right track ?
Please help, thank you.
In order to detect contact, one of the SKSpriteNodes must have physicsBody.Dynamic= YES;
Since you want neither to be dynamic, there is a quick fix I have found:
leftEdge.physicsBody.Dynamic=YES; //This allows it to enter onContact
leftEdge.physicsBody.collisionBitMask=0; //Disables edge from being moved by collision
leftEdge.physicsBody.AffectedByGravity=NO; //Prevents any in game gravity from moving the edge
This allows you have an edge that is dynamic and can trigger onContact yet the edge will still act as if it is not dynamic.
I think this solution isn't very clean and requires a lot more code than what is necessary to get this effect. In the update() function since there are only 4 or 5 boxes why don't you simply loop through the objects and check if any of their frame's minX's are less than 0 and if they are reset them to the starting point which I'm assuming is (view.frame.width + box.size.width) because that's completely offscreen. That way there's no array manipulation in that you don't have to remove and append objects to the array. Also you don't need to attach a physicsBody where none is required. And you don't need the leftEdge