I want to move my sprite up and down using a button. Since you cant use UIButtons in sprite Kit i am using different SpriteKitNodes. The nodes will be arrow images. But i want to use the arrow images to move my original sprite but simply touching it. I thought that I would use SKAction but I'm stuck. Is it possible to move one sprite using another?
There may not be any provision for buttons in SpriteKit, but one can easily subclass SKSpriteNode to act as a button. I have created such a class which is available on GitHub here.
Using SKAction for movement based on direction buttons is not advisable. Instead, you need to use flags for direction buttons upon which you will move the node in the -update: method.
Maintain the flags as instance variables.
#implementation MyScene
{
BOOL upDirection;
BOOL downDirection;
}
Initialise them to FALSE in the -initWithSize: method.
This is how you should handle the flags in the -update method:
-(void)update:(CFTimeInterval)currentTime
{
/* Called before each frame is rendered */
if (upDirection)
{
myNode.position = CGPointMake(myNode.position.x, myNode.position.y + 5); //Increment value can be adjusted
}
if (downDirection)
{
myNode.position = CGPointMake(myNode.position.x, myNode.position.y - 5); //Decrement value can be adjusted
}
}
Related
So here is what I am trying to achieve;
I am trying to create a tile map system. So when the player presses the up button for example, the player stays central and the map moves down to give the look that the player is walking up the world (its a top-down game).
I have created an array of SpriteNode's and a function that creates 64x64 tiles and adds each new object to the array. I am trying to figure out a way that I can apply a function to all of the tiles.
So for example, when the up button is pressed all the tiles start to move down.
I could do this manually:
SpriteNode *tile00;
SpriteNode *tile01;
SpriteNode *tile02;
...
and then change the position of each one manually when the player moves but this is going to be very tedious (especially considering that I plan to create rather large maps)
My Question: Can I apply functions to several SpriteNodes?
You should definitely create functions:
- (void) moveUp {
// Loop through array of nodes and move them down
}
- (void) moveDown {
// Loop through array of nodes and move them up
}
- (void) moveRight {
// Loop through array of nodes and move them left
}
- (void) moveLeft {
// Loop through array of nodes and move them right
}
You can loop through the nodes if you create an NSArray of nodes and do this:
for (SKSpriteNode *n in NodeArray){ // NodeArray is you NSArray
// Do something to n
}
If you have an NSArray of tile sprites, applying an action to all of them could be done like this
SKAction *moveAction = //move all tiles down, up, etc.;
for (SKSpriteNode *tile in self.tiles) /* an NSArray containing the tile sprites */
{
[tile runAction:moveAction];
}
I'm using the code below to animate an intro-animation for buttons in an iOS Swift game. This code is inside of an update function and is the same for a lot of buttons.
if self.creditsButton.size.width < 40 {
self.creditsButton.size.width += 1
self.creditsButton.size.height += 1
}
My question is; is there a better (more clean) way to animate scaling/sizes of buttons/menu's?
If the button is a subclass of SKSpriteNode then SKAction enables you to schedule the animation without needing to regularly update, e.g. (apologies for the objective-C):
SKAction *scale = [SKAction resizeToWidth:40.0 duration:0.4];
[spriteNode runAction:scale];
In an application I have, I initialise a SKSpriteNode (Ball) the didMoveToView method. I have a method that picks up on swipes up (That works fine, I tested it).
When that is called, it calls a method called [self jump];
In this method, I want it to run a few actions using the SKSpriteNode, Ball.
I used to know, but I have forgotten the code that allows you to access already initialised nodes. Say if I was going to write [Ball runAction:myAction];
, the compiler will replace 'Ball' with '_Ball'.
How can I manipulate the SKSpriteNode 'Ball' within another method?
I don't think that I need to add any code but if it helps I will comply.
Thank you in advance.
You can either create a class property for ball to access it everywhere.
#property (nonatomic,strong) SKSpriteNode *ball
You can use self.ball to access the ball in every method.
self.ball = //SKSpriteNode initialize
[self addChild:self.ball]
// Add Child
SKNodes can also be searched by using the unique name of its children.
For example you can set
// Initialise
ball.name = #"Ball"
// Add Child
Then the SKScene can be searched using the function childWithName:
SKSpriteNode *ball = [self childNodeWithName:#"Ball"]
The first method is easier in your case.
As suggested in the comments you could keep a pointer to the ball node, however another way could be to use the name property of SKNode.
When you instantiate the ball node assign the name property with a string identifier -
SKSpriteNode *ball = // create instance here
ball.name = #"TheBall";// set its name
[self addChild:ball];// add to scene and forget about it
Whenever you need to access it from within your SKScene subclass use -
SKSpriteNode *sameBall = (SKSpriteNode*)[self childNodeWithName:#"TheBall"];// don't forget the typecast
Now you can perform whatever actions you need to on the ball node.
An interesting read is the section on Searching The Node Tree found in the SKNode Class Reference by Apple.
I am trying to create a conveyor belt effect using SpriteKit like so
MY first reflex would be to create a conveyor belt image bigger than the screen and then move it repeatedly forever with actions. But this does not seem ok because it is dependent on the screen size.
Is there any better way to do this ?
Also obviously I want to put things (which would move independently) on the conveyor belt so the node is an SKNode with a the child sprite node that is moving.
Update : I would like the conveyor belt to move "visually"; so the lines move in a direction giving the impression of movement.
Apply physicsBody to all those sprites which you need to move on the conveyor belt and set the affectedByGravity property as NO.
In this example, I am assuming that the spriteNode representing your conveyor belt is called conveyor. Also, all the sprite nodes which need to be moved are have the string "moveable" as their name property.
Then, in your -update: method,
-(void)update:(CFTimeInterval)currentTime
{
[self enumerateChildNodesWithName:#"moveable" usingBlock:^(SKNode *node, BOOL *stop{
if ([node intersectsNode:conveyor])
{
[node.physicsBody applyForce:CGVectorMake(-1, 0)];
//edit the vector to get the right force. This will be too fast.
}
}];
}
After this, just add the desired sprites on the correct positions and you will see them moving by themselves.
For the animation, it would be better to use an array of textures which you can loop on the sprite.
Alternatively, you can add and remove a series of small sprites with a sectional image and move them like you do the sprites which are travelling on the conveyor.
#akashg has pointed out a solution for moving objects across the conveyor belt, I am giving my answer as how to make the conveyor belt look as if it is moving
One suggestion and my initial intuition was to place a larger rectangle than the screen on the scene and move this repeatedly. Upon reflecting I think this is not a nice solution because if we would want to place a conveyor belt on the middle, in a way we see both it ends this would not be possible without an extra clipping mask.
The ideal solution would be to tile the SKTexture on the SKSpriteNode and just offset this texture; but this does not seem to be possible with Sprite Kit (no tile mechanisms).
So basically what I'm doing is creating subtextures from a texture that is like so [tile][tile](2 times a repeatable tile) and I just show these subtextures one after the other to create an animation.
Here is the code :
- (SKSpriteNode *) newConveyor
{
SKTexture *conveyorTexture = [SKTexture textureWithImageNamed:#"testTextureDouble"];
SKTexture *halfConveyorTexture = [SKTexture textureWithRect:CGRectMake(0.5, 0.0, 0.5, 1.0) inTexture:conveyorTexture];
SKSpriteNode *conveyor = [SKSpriteNode spriteNodeWithTexture:halfConveyorTexture size:CGSizeMake(conveyorTexture.size.width/2, conveyorTexture.size.height)];
NSArray *textureArray = [self horizontalTextureArrayForTxture:conveyorTexture];
SKAction *moveAction = [SKAction animateWithTextures:textureArray timePerFrame:0.01 resize:NO restore:YES];
[conveyor runAction:[SKAction repeatActionForever:moveAction]];
return conveyor;
}
- (NSArray *) horizontalTextureArrayForTxture : (SKTexture *) texture
{
CGFloat deltaOnePixel = 1.0 / texture.size.width;
int countSubtextures = texture.size.width / 2;
NSMutableArray *textureArray = [[NSMutableArray alloc] initWithCapacity:countSubtextures];
CGFloat offset = 0;
for (int i = 0; i < countSubtextures; i++)
{
offset = i * deltaOnePixel;
SKTexture *subTexture = [SKTexture textureWithRect:CGRectMake(offset, 0.0, 0.5, 1.0) inTexture:texture];
[textureArray addObject:subTexture];
}
return [NSArray arrayWithArray:textureArray];
}
Now this is still not ideal because it is necessary to make an image with 2 tiles manually. We can also edit a SKTexture with a CIFilter transform that could potentially be used to create this texture with 2 tiles.
Apart from this I think this solution is better because it does not depend on the size of the screen and is memory efficient; but in order for it to be used on the whole screen I would have to create more SKSpriteNode objects that share the same moveAction that I have used, since tiling is not possible with Sprite Kit according to this source :
How do you set a texture to tile in Sprite Kit.
I will try to update the code to make it possible to tile by using multiple SKSpriteNode objects.
I'm having trouble getting a repeatable background to work in my game menu.
The user can slide a finger across the screen to select a character to play.
I have a parallax effect working with various backgrounds as the characters slide into view.
Sample below.
- (void)didMoveToView:(SKView *)view
{
self.pan = [[UIPanGestureRecognizer alloc]initWithTarget:self action:#selector(dragScene:)];
self.pan.minimumNumberOfTouches = 1;
self.pan.delegate = self;
[self.view addGestureRecognizer:self.pan];
}
- (void)dragScene:(UIPanGestureRecognizer *)gesture
{
CGPoint trans = [gesture translationInView:self.view];
SKAction *moveSky = [SKAction moveByX:trans.x*0.03 y:0 duration:0];
[_skyBackground runAction:moveSky];
}
I would like to repeat the backgrounds. I know how to do this with automatically scrolling backgrounds but I can't seem to get it to work here. It needs to repeat in both directions, left and right.
Thanks for any help!
You can create two more background nodes - one to the left of your current background node and one to the right. Move them aswell any time you move your existing _skyBackground node.
Then, in the update method, check if any of the three nodes needs to be "shifted" - either to behind the other two or in front. You're basically swapping the three nodes' positions if needed.
-(void)update:(NSTimeInterval)currentTime {
//get the left background node (or if using an ivar just use _leftNode)
SKSpriteNode *leftNode = (SKSpriteNode*)[self childNodeWithName:#"leftNode"];
//my positioning might be off but you'll get the idea
if (leftNode.position.x < -leftNode.size.width*2)
{
leftNode.position = CGPointMake(leftNode.size.width, leftNode.position.y);
}
if (leftNode.position.x > leftNode.size.width*2)
{
leftNode.position = CGPointMake(-leftNode.size.width, leftNode.position.y);
}
//repeat the same for _skyBackground and _rightNode
}
You may need more than 3 images if there's a slight gap between images as they're shifted.