im developing a game where i have time to finish the level, during wich i have a filling bar animation, on that particular animation im using CCMoveBy:
self.animatedBar = [CCMoveBy actionWithDuration:time position: ccp(12, -20)];
CCNode* animatedContainer = [self getChildByTag:1];
[animatedContainer runAction:self.animatedBar];
is there any way to say something like [animatedBar pause] and [animatedBar resume] ?
Or the best bet is to put this into my game loop and pause it there? ( im doing a return if the BOOL paused is set to true ).
I will have more animations attached to this on the future ( not game core related, but just to make it more "shinny" ) so i want to avoid using:
[animatedContainer pauseSchedulerAndActions];
There is no pause/resume method for action in cocos2d, so the first way - you can implement your own CCAction subclass, that will allow this, or just stop current action and recreate it instead of unpause.
Related
I have a simple spine project in cocos2d and I want to know the current animation that's being played (the string name) but I can't find any function to get that in the CCSkeletonAnimation class,
Do you know if there's a way to get that?
Thanks
EDIT - SOLUTION
Hey I found how to to it
AnimationState *a = [mAnimationNode getAnimationState:0];
NSLog(#"Layer touched: %s",a->animation->name);
Where mAnimationNode is a CCSkeletonAnimation,
that works perfectly!!
Hey I found how to to it
AnimationState *a = [mAnimationNode getAnimationState:0];
NSLog(#"Layer touched: %s",a->animation->name);
Where mAnimationNode is a CCSkeletonAnimation,
that works perfectly!!
If Spine does not support a clear way to do it, you might be better off by storing the animation name in a property of your scene when you start it.
On the other hand, since an animation is usually implemented in cocos2d through an action, you could inspect the CCActionManager's numberOfRunningActionsInTarget: method implementation to check how you can access the list of running actions in a given target node.
This is for cocos2d 1.1:
tHashElement *element = NULL;
HASH_FIND_INT(targets, &target, element);
element->actions is the ccArray containing all the running actions.
I'm trying to find a solution to a problem I just stumbled upon.
Tried to search for it but it's not that I'm looking for.
I'm doing a GPS-app with two tabbars. I track the distance in the map-view (using CLLocation) and when I change tab to a different view the stringtext that say what distance it is from when I started don't update immediately, it take a couple of seconds.
And when I press the stop button I want it to either wait those couple of seconds so the real distance updates. But I dont want to freeze the app.
(*NSDate future = [NSDate dateWithTimeIntervalSinceNow: 3.0 ];
[NSThread sleepUntilDate:future];)
I'm saving the string with the distance in a cell in the second tab, so if I multitask while the app is going I want to just start the app and press stop. And then the new distance will be correct and saved. Hope I didn't confuse you with this much text I hope you understand what I asking for!
thx
[label performSelector:#selector(setText:) withObject:newText afterDelay:3.0];
So what you want to do is use dispatch_after:
dispatch_after(3 seconds, dispatch_get_main_queue(), ^
{
myLabel.text = <the value you want to appear>;
} );
In my application i am using CABasicAnimation for animation. I want to change the speed of the animation dynamically so i have added one slider to change the speed. Following is my animation code. But i am not able to change the speed, when i change the value of speed nothing happens.
CABasicAnimation * a = [CABasicAnimation animationWithKeyPath:#"position"];
[a setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]];
CGPoint startPt = CGPointMake(self.view.bounds.size.width + displayLabel.bounds.size.width / 2,
displayLabel.frame.origin.y);
CGPoint endPt = CGPointMake(displayLabel.bounds.size.width / -2, displayLabel.frame.origin.y);
[a setFromValue:[NSValue valueWithCGPoint:startPt]];
[a setToValue:[NSValue valueWithCGPoint:endPt]];
[a setAutoreverses:NO];
[a setDuration:speeds];
[a setRepeatCount:HUGE_VAL];
[displayLabel.layer addAnimation:a forKey:#"rotationAnimation"];
- (IBAction)speedSlider:(id)sender {
speeds = slider.value;
}
I think the best way to change speed is change your layer's time system
displayLabel.layer.timeOffset =
[displayLabel.layer convertTime:CACurrentMediaTime() fromLayer:nil];
displayLabel.layer.beginTime = CACurrentMediaTime();
displayLabel.layer.speed= slider.value;
You can see this for advance. https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CoreAnimation_guide/AdvancedAnimationTricks/AdvancedAnimationTricks.html#//apple_ref/doc/uid/TP40004514-CH8-SW2
EDIT: It looks like you will have a further problem, though: it doesn't look like you can change values like that on a running animation. You will have to remove the current animation and add a new one with the altered value. This may need a bit of care to prevent a jarring effect when you add the new animation.
From the thread above, you might be able to do this by not repeating the animation, but by using a delegate (see here) to keep re-adding the animation, and setting the new speed for the next animation cycle instead.
Original post:
You are changing the value that you had originally passed in to the animation. This isn't going to affect the running animation. You'll need to get a reference to that, and change the duration property of the animation object. Something like this in your action method:
CABasicAnimation *a = [displayLabel.layer animationForKey:#"rotationAnimation"];
a.duration = slider.value;
I think jrturton is correct that you can't change properties on an animation that is already running. But what you could do is break the animation into short segments and change the speed for the next segment when the slider value changes.
Instead of animating from point A to point D, you'd animate from A-B, then B-C, then C-D. Use the parent class's animationDidStop to check the current point, check the slider value, and kick off the next animation.
This might produce jerky motion, but if you use very small segments, you might be able to smooth it out.
u should stop the animation and restart a new with a new duration time
but remember to log down the fromValue and the toValue , and use the old toValue as the new fromValue to perform a seamless change
set speed as what you need.
a.duration=0.5;
Try this...
If you just want Autoscrolling text then you can also use one class
http://blog.stormyprods.com/2009/10/simple-scrolling-uilabel-for-iphone.html
It might also work in your case, try it.
The UIScrollView has a lot of information available to the programmer, but I dont see an obvious way to control the location that the control stop at after decelerating from a scroll gesture.
Basically I would like the scrollview to snap to specific regions of the screen. The user can still scroll like normal, but when they stop scrolling the view should snap to the most relevant location, and in the case of a flick gesture the deceleration should stop at these locations too.
Is there an easy way to do something like this, or should I consider the only way to accomplish this effect to write a custom scrolling control?
Since the UITableView is a UIScrollView subclass, you could implement the UIScrollViewDelegate method:
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView
withVelocity:(CGPoint)velocity
targetContentOffset:(inout CGPoint *)targetContentOffset
And then compute what the closest desired target content offset is that you want, and set that on the inout CGPoint parameter.
I've just tried this and it works well.
First, retrieve the unguided offset like this:
CGFloat unguidedOffsetY = targetContentOffset->y;
Then Figure out through some math, where you'd want it to be, noting the height of the table header. Here's a sample in my code dealing with custom cells representing US States:
CGFloat guidedOffsetY;
if (unguidedOffsetY > kFirstStateTableViewOffsetHeight) {
int remainder = lroundf(unguidedOffsetY) % lroundf(kStateTableCell_Height_Unrotated);
log4Debug(#"Remainder: %d", remainder);
if (remainder < (kStateTableCell_Height_Unrotated/2)) {
guidedOffsetY = unguidedOffsetY - remainder;
}
else {
guidedOffsetY = unguidedOffsetY - remainder + kStateTableCell_Height_Unrotated;
}
}
else {
guidedOffsetY = 0;
}
targetContentOffset->y = guidedOffsetY;
The last line above, actually writes the value back into the inout parameter, which tells the scroll view that this is the y-offset you'd like it to snap to.
Finally, if you're dealing with a fetched results controller, and you want to know what just got snapped to, you can do something like this (in my example, the property "states" is the FRC for US States). I use that information to set a button title:
NSUInteger selectedStateIndexPosition = floorf((guidedOffsetY + kFirstStateTableViewOffsetHeight) / kStateTableCell_Height_Unrotated);
log4Debug(#"selectedStateIndexPosition: %d", selectedStateIndexPosition);
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:selectedStateIndexPosition inSection:0];
CCState *selectedState = [self.states objectAtIndexPath:indexPath];
log4Debug(#"Selected State: %#", selectedState.name);
self.stateSelectionButton.titleLabel.text = selectedState.name;
OFF-TOPIC NOTE: As you probably guessed, the "log4Debug" statements are just logging. Incidentally, I'm using Lumberjack for that, but I prefer the command syntax from the old Log4Cocoa.
After the scrollViewDidEndDecelerating: and scrollViewDidEndDragging:willDecelerate: (the last one just when the will decelerate parameter is NO) you should set the contentOffset parameter of your UIScrollView to the desired position.
You also will know the current position by checking the contentOffset property of your scrollview, and then calculate the closest desired region that you have
Although you don't have to create your own scrolling control, you will have to manually scroll to the desired positions
To add to what felipe said, i've recently created a table view that snaps to cells in a similar way the UIPicker does.
A clever scrollview delegate is definitely the way to do this (and you can also do that on a uitableview, since it's just a subclass of uiscrollview).
I had this done by, once the the scroll view started decelerating (ie after scrollViewDidEndDragging:willDecelerate: is called), responding to scrollViewDidScroll: and computing the diff with the previous scroll event.
When the diff is less than say a 2 to 5 of pixels, i check for the nearest cell, then wait until that cell has been passed by a few pixels, then scroll back in the other direction with setContentOffset:animated:.
That creates a little bounce effect that is very nice for user experience, as it gives a good feedback on the snapping.
You'll have to be clever and not do anything when the table is bouncing at the top or bottom (comparing the offset to 0 or the content size will tell you that).
It works pretty well in my case because the cells are small (about 80-100px high), you might run into problems if you have a regular scroll view with bigger content areas.
Of course, you will not always decelerate past a cell, so in this case i just scroll to the nearest cell, and the animation looks jerky. Turns out with the right tuning, it barely ever happens, so i'm cool with this.
Spend a few hours tuning the actual values depending on your specific screen and you can get something decent.
I've also tried the naive approach, calling setContentOffset:animated: on scrollViewDidEndDecelerating: but it creates a really weird animation (or just plain confusing jump if you don't animate), that gets worse the lower the deceleration rate is (you'll be jumping from a slow movement to a much faster one).
So to answer the question:
- no, there is no easy way to do this, it'll take some time polishing the actual values of the previous algorithm, which might not work at all on your screen,
- don't try to create your own scroll view, you'll just waste time and badly reinvent a beautiful piece of engineering apple created with truck loads of bug. The scrollview delegate is the key to your problem.
Try something like this:
- (void) snapScroll;
{
int temp = (theScrollView.contentOffset.x+halfOfASubviewsWidth) / widthOfSubview;
theScrollView.contentOffset = CGPointMake(temp*widthOfSubview , 0);
}
- (void) scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate;
{
if (!decelerate) {
[self snapScroll];
}
}
- (void) scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
[self snapScroll];
}
This takes advantage of the int's drop of the post-decimal digits. Also assumes all your views are lined up from 0,0 and only the contentOffset is what makes it show up in different areas.
Note: hook up the delegate and this works perfectly fine. You're getting a modified version - mine just has the actual constants lol. I renamed the variables so you can read it easy
I'm coding in Objective-C for the iPhone and I am trying create an array that plays a series of sounds. For example the first time I press the button I want it play sound "0.wav", but the second time I want it to play "1.wav", then "2.wav", "3.wav", etc. Then when I've played a total of 14 sounds (up to "13.wav") I want the loop to start over playing with 0.wav. I've looked around Google and the Apple development documentation for almost 4 days without much luck. So if someone could help me generate a code for this that would be greatly appreciated and if you wouldn't mind could you attempt to explain the code briefly so that I can learn what each part does.
Thank you, Joey
EDIT
Okay I've gotten the Array part down (thanks to Thomas) however neither of us are sure how to implement the soundID from the array to the action where I play the sound and how to play that sound. I used the format Thomas used for his array below if that helps you with your answer.
Thanks, Joey
First, create the array of your different sounds and a variable to hold the current index:
NSArray *sounds = [[NSArray alloc] initWithObjects:#"0.wav", #"1.wav", nil]; // add all your files here
NSUInteger currentSound = 0;
Then, when you handle your button tapped event, go to the next sound, and play it. If it's at the end of your NSArray, go back to index 0:
currentSound++;
if (currentSound >= [sounds count]) {
currentSound = 0;
}
// play the sound. Just call your own method here
[self playSoundWithFilename:[sounds objectAtIndex:currentSound]];