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];
Related
I'm new to Swift and I'm trying to implement a simple game. In this game, once the view is loaded I want periodic animations to happen.
The problem is that I try to animate my buttons with, for instance, button.frame.origin.x += 50, but instead of moving it from the origin to 50px right, it appears at button.frame.origin.x - 50 and goes to its (initial) position.
Funnily enough, at one point I show an AlertDialog to the user, and after it is shown the animation starts to happen as I expected.
My problem is the same of this topic, but the accepted answer just didn't solve it for me.
Any thoughts?
Edit:
Digging into the code and testing a lot I found out the method where I show the AlertDialog also happens to invalidate my timer. I have two timers: one to update the UI time (a TextField), and the other to perform the animation. Both are scheduling a task. From what I read here, it is not possible to have two timers like that.
A timer object can be registered in only one run loop at a time,
although it can be added to multiple run loop modes within that run
loop.
Hence, the obvious solution would be to merge the two selectors (functions) into one, so the timer would work for both tasks. However, if I try to update the UI AND perform animations, the animations don't work as expected anymore. I'm just lost on this problem and can't make both things work.
These are my important pieces of code:
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
...
resetOrCreateGame()
}
func resetOrCreateGame() {
// Do stuff to initialize values
timerToRotate = NSTimer.scheduledTimerWithTimeInterval(2.0, target: self, selector: "rotateBlocks", userInfo: nil, repeats: true)
}
func rotateBlocks() {
// Calculate positions to rotate
UIView.animateWithDuration(0.5, delay: 0, options: UIViewAnimationOptions.CurveEaseOut, animations: {
// Try to update a timer in the UI
//self.timeLeft.text = String(self.seconds)
for i in 0 ... 8 {
println("\(i) before \(self.buttons[i].frame.origin.x) \(self.buttons[i].frame.origin.y)")
self.buttons[i].frame.origin.x = self.positions[self.indicesToRotate[i]].x
self.buttons[i].frame.origin.y = self.positions[self.indicesToRotate[i]].y
println("\(i) after \(self.buttons[i].frame.origin.x) \(self.buttons[i].frame.origin.y)")
}
}, completion: { _ in
println("completed")
})
If I leave the line self.timeLeft.text = String(self.seconds) commented, the animations work fine. No matter how I try to update the timeLeft, if I do so it screws my animations. I tried to update it in a separate thread, dispatch it to the main thread, or even update it inside the animations closure: it just doesn't work.
Try using SKActions to animate.
Set up an SKAction, then on the SpriteKit node you want to animate (replace "node" with the name of the node), and call it action.
Then, call the simple method:
[node runAction:action];
For example, if you want to set up an SKAction to move a node titled button 50 pixels to the right over a timespan of 3 seconds...
SKAction *action = [SKAction moveByX:50.0 y:0.0 duration:3.0];
[button runAction:action];
Heck, if I don't run an action more than once, I'd simply do this:
[button runAction:[SKAction moveByX:50.0 y:0.0 duration:3.0]];
And finally, you can use the runAction:completion: method to not only run your SKAction, but to call an Objective-C block after it's finished, like in this coding example:
SKAction *action = [SKAction moveByX:50.0 y:0.0 duration:3.0];
[button runAction:action completion:^{
// some code here that runs after an animation completes
}];
And, to non-complicate your code if you want to run a sequence of actions on a node (move a button 50 pixels to the right called button over 3 seconds, then fade it out over 1 second), you can program a sequence of actions into one action, like so:
SKAction *firstAction = [SKAction moveByX:50.0 y:0.0 duration:3.0];
SKAction *secondAction = [SKAction fadeAlphaTo:0.0 duration:1.0];
SKAction *theSequence = [SKAction sequence:[NSArray arrayWithObjects:firstAction, secondAction, nil];
[button runAction:theSequence]
You CAN run another action in the completion block, but the method of using a sequence cleans up code.
Finally, I found the solution!
The problem for some reason was Auto Layout. After disabling it everything worked as expected.
Hope it helps someone in the future!
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
}
}
Is there any way to animate CCSprite width/height in cocos2d ? I'm looking to animate the menu panel width, scaleX/scaleY property animation is not the solution for my case.
I'm not 100% sure what you want to do as there is very little data.
But you can take a look at CCMoveTo and CCScaleTo.
These will move and scale your sprite over a given time.
You can group these together (I think) to run them both at the same time.
Yes, there are a lot of animations available in cocos2d. you may want to run multiple action on a sprite sequentially or parallel on a node/sprite. For example i have a sprite and i want to scale its size, blink and then fade out to it. Then i would do
CCSPrite *mySprite = [CCSprite spriteWithFile:#"xyz.png"];
.....
CCScaleTo *scale = [CCScaleTo actionWithDuration:0.3 scaleX:20 scaleY:30];
CCBlink *blink = [CCBlink actionWithDuration:0.3 blinks:2];
CCFadeTo *fade = [CCFadeTo actionWithDuration:0.3 opacity:0];
To run all the above actions one after other:
[mySPrite runAction:[CCSequence actions:scale, blink, fade, nil]];
To run all the actions at same time:
[mySprite runAction:[CSPawn actions:scale, blink, fade, nil]];
There are a lot of action like this you implement inorder to get an animation effect. Read the cocos2d documentaion.
Perhaps this question have been repeated many time, but I couldn't found helpful material. Also this is my first project in cocos2D, i want to implement the ProgressBar, CCProgressTimer in cocos2D. I have two sprites, first is moving and the second one is the player (to which you can move), If user successfully eat the first moving object then the progress should be incremented else if it misses then the progress will be decremented. I need your help. Thanks in advance.
Here is my code I used for rounded CCProgressTimers (it looks like clock). You possibly need to have a background sprite and a "movable" sprite above background sprite.
CCSprite *movableSprite = [CCSprite spriteWithFile:#"health100.png"];
CCProgressTimer *healthBar = [CCProgressTimer progressWithSprite:movableSprite];
healthBar.type = kCCProgressTimerTypeRadial; // This is for round progress timer. Possible value for horizontal bar will be kCCProgressTimerTypeHorizontalBarLR
healthBar.midpoint = ccp(0,0.5); // Here is where all magic is
healthBar.barChangeRate = ccp(1, 0); // If you need horizontal bar progress play with these parameters.
// Here we will start an animation process.
// You can do it without animation just setting up healthBar.progress = 45.0f; (45%)
[healthBar runAction:[CCProgressFromTo actionWithDuration:2.0f from:0.0f to:100.0f]];
healthBar.position = ccp(100, 100); // It's your position
[self addChild:healthBar];
I thought doing a simple animation would be easy but is is taking hours and Im not getting even close to have the expected effect...
I need to simulate a simple Flash Motion Tween like for iphone/ipad using xcode
this is the desired effect: http://www.swfcabin.com/open/1340330187
I already tried setup a timer adding X position and it doens't get the same effect, my cooworkers suggested me cocos 2d to do this using actions and sprites, which might would be fine although I wouldn't like to third party frameworks, but if there is a way to do the same with cocos I would definitively use it.
Does anybody have any suggestions, I feel like it might be simpler than I thought
thanks all
If there is no troubles to you, that you will have to do it insinge OpenGL view, it is really very simple. To show some info, you need CCLabel class. To change it's position, you need CCMoveTo/CCMoveBy action, to change opacity, you need CCFadeTo/CCFadeIn/CCFadeOut actions, to make delay you need CCDelayTime. To make it all work together you need CCSpawn and CCSequence.
CCSpawn will run several actions at the same time(for example fade in and move from right to the center), CCSequence will run several actions one by one (sequence to fade in + move to center, delay for same time, sequence to fade out + move from center to the left). Then you should only schedule method, that will create labels and run actions on them. In code it will be something like
lets define full animation time
#define ANIMATION_TIME 4.f
schedule method in any place you want to start animation
[self schedule:#selector(runNextMessage) interval:ANIMATION_TIME];
it will call runNextMessage method every ANIMATION_TIME seconds
- (void) runNextMesage
{
NSString* message = //get next message
CCLabelTTF* label = [CCLabelTTF labelWithString:message
dimensions:desiredDimensionsOfTheLabel
alignment:UITextAlignmentLeft
lineBreakMode:UILineBreakModeWordWrap
fontName:#"Arial"
fontSize:20.f];
CGSize winSize = [[CCDirector sharedDirector] winSize];
// place the label out the right border
[label setPosition: ccp(winSize.width + label.contentSize.width, winSize.height / 2)];
// adding it to the screen
[self addChild:label];
ccTime spawnTime = ANIMATION_TIME / 3;
// create actions to run
id appearSpawn = [CCAction actionOne:[CCMoveTo actionWithDuration:spawnTime]
two:[CCFadeIn actionWithDuration:spawnTime]];
// create show action and disappear action
// create result sequence
id sequence = [CCSequence actions: appearSpawn, showAction, disappearAction, nil];
[label runAction: sequence];
}