Every example I see on how to use transitions is with CCScene like this:
[[CCDirector sharedDirector] replaceScene:[CCTransitionCrossFade transitionWithDuration:0.5f scene:[NewScene scene]]];
My app is setup with a GameScene, MenuScene ..etc. Within the GameScene I setup the DPad and the HUD and under that I have the hero moving to different levels which are of type CCNode. I want to use the native transitions to move from one level to the next.
I found a question similar to what I need here but that user was essentially told to build the transitions himself.
The confusing part to me is that CCNode contains a method called:
- (void) onEnterTransitionDidFinish
So, is there a way to use transitions with CCNode that I'm missing? Is my app setup incorrectly and the levels should be CCScene? If so how to I keep the HUD and Dpad over the transition?
Thanks in advance for any help.
CCTransitions are specifically for scenes. CCNodes contain onEnterTransitionDidFinish, as it gets called on every CCNode child of a CCScene. Fortunately, you can just check the CCTransition code and see how they are implemented and then roll your own for layers (many have done and shared this).
For example, I wrote a simple little method which will use blocks to give the same fade-in, fade-out effect as a scene transition, but for an individual layer (along with some added features). Just pass in the layer you want to cover up, the speed of the fade out and fade in, the color to fade to, and the block you wish to execute while the layer is hidden.
-(void)fadeLayer:(CCLayer*)layer withOutDuration:(float)outTime inDuration:(float)inTime color:(ccColor3B)color withBlock: (void(^)())block
{
CGSize winSize = [[CCDirector sharedDirector] winSize];
CCLayerColor *toplayer = [CCLayerColor layerWithColor:ccc4(color.r, color.g, color.b, 0) width:winSize.width height:winSize.height];
[layer addChild:toplayer z:INT_MAX];
[toplayer runAction:
[CCSequence actions:
[CCFadeIn actionWithDuration:outTime],
[CCCallBlock actionWithBlock:block],
[CCFadeOut actionWithDuration:inTime],
[CCCallBlockN actionWithBlock:^(CCNode *node) {
[node removeFromParentAndCleanup:YES];
}],
nil]
];
}
Related
I know this question has been asked and answered before but none of the answers seem to fit what I am trying to do. I need to be able to pause animations but still be able to have my pause menu come up and ask the user for input. Is this possible and if so how do I go about it.
There are 2 methods pause and resume for this purpose in CCDirector:
[[CCDirector sharedDirector] pause];
and
[[CCDirector sharedDirector] resume];
From the documentation:
The running scene will be drawed but all scheduled timers will be paused While paused, the draw rate will be 4 FPS to reduce CPU consumption
I suggest you to push the "pause scene" on the cocos2d scene stack.
CCScene * pauseScene = ...;
[[CCDirector sharedDirector] pushScene:pauseScene];
This will automatically pause your current scene (which I assume is the level scene) and will run the "pause scene" so that user can interact with it.
It is very important to remember that after the instructions above have been executed, you will have 2 scenes in the scenes stack of cocos2d (the level scene [frozen] + the pause scene [running]).
So, once you are in the "pause scene", if you want resume the level you just need to pop the pause scene from the stack.
[[CCDirector sharedDirector] popScene];
This will automatically remove the "pause scene" and resume the previous scene in the stack (which is the level scene).
Please remember that you will always need to "pop" the pause scene sometime. It is important not to forget the "level scene" is still in the scene stack of cocos2d!
E.g.: if in the "Pause Scene" you have a button to load the level selection menu and permanently leave the current level, you should implement it as follow:
CCScene * levelSelector = ...
[[CCDirector sharedDirector] popScene]; // to remove the "pause scene" from the stack
[[CCDirector sharedDirector] replaceScene:levelSelector]; // replace the "level scene" with the "levelSelector scene"
I tried all of the suggestions but none of them worked in my case, so this is what I ended up doing which worked really well for me. This is to pause.
[self pauseSchedulerAndActions];
for (CCNode *child in [self children]) {
[child pauseSchedulerAndActions];
}
And this to resume:
[selfReference resumeSchedulerAndActions];
for (CCNode *child in [selfReference children]) {
[child resumeSchedulerAndActions];
}
With Cocos 2D Version 2 the action manager is no more a singleton. But rather the code has been fused with the CCDirector.
So this is the way to pause all actions in a particular scene and resume them as well.
-(void)pauseGamePlayScene{
for(CCSprite *sprite in [self children]) {
[[[CCDirector sharedDirector] actionManager]pauseTarget:sprite];
}
}
-(void)resumeGamePlayScene{
for(CCSprite *sprite in [self children]) {
[[[CCDirector sharedDirector] actionManager]resumeTarget:sprite];
}
}
Cocos 2d Ver 2 Action Manager Documentation
Hope this helps.
Stopping just animations?
[ [ CCDirector sharedDirector ] stopAnimation ];
[ [ CCDirector sharedDirector ] startAnimation ];
If you want to stop everything, pause as you have seen done everywhere else (after the menu has been moved into place), then unpause once the user touches a certain thing.
I am new to cocos2D and have used seperate game-loop (rather than scheduleupdate) and am unscheduling it along with some more schedulers. It would automatically pause the gameplay at the same time i am placing a new layer (i.e. Pauselayer) ontop of the gamelayer.
Appears pretty complex, but worked for me.
I use:
#interface CCNode (PauseAdded)
#end
#implementation CCNode (PauseAdded)
-( void ) traversePause
{
CCNode* a = [children_ lastObject];
CCARRAY_FOREACH( children_, a )
{
[scheduler_ pauseTarget:a];
[actionManager_ pauseTarget:a];
[a traversePause];
}
[scheduler_ pauseTarget:self];
[actionManager_ pauseTarget:self];
}
-( void ) traverseResume
{
CCNode* a = [children_ lastObject];
CCARRAY_FOREACH( children_, a )
{
[a traverseResume];
[scheduler_ resumeTarget:a];
[actionManager_ resumeTarget:a];
}
[scheduler_ resumeTarget:self];
[actionManager_ resumeTarget:self];
}
#end
That way you can just do something like this assuming your game layer is separated from the pause layer:
[scene.game_layer traversePause];
And to unpause:
[scene.game_layer traverseResume];
Try this PauseButton class https://github.com/iabtyagi/custom-pause-cocos2d .
does exactly what you are looking for.
CCPhysicsNode *_physics;
_physics.paused = true;// It will pause your game but not actions.
_physics.paused = false;// It will resume your spinning and falling of sprites while button is pressable like during pause game.
After I overlay an application over my previous application, I go back to previous application and encounter a few errors:
certain components have disappeared
only way to make the components visible is to resize the window
that seems to redraw the whole canvas.
Weird thing is that there are only a couple of components and drawn images that are missing
It doesn't always happen but only a couple of times
I haven't found a solid way to reproduce the problem.
Anybody have an Idea why this is happening?
I experienced exactly the same issue (view was updated correctly only after resizing), except that I've used OpenGL drawing in OSX game.
My problem was solved by adding this:
GLint vblSynch = 1;
[[self openGLContext] setValues:&vblSynch forParameter:NSOpenGLCPSwapInterval];
in my custom NSOpenGLView init method.
Then I've implemented:
- (void)drawRect:(NSRect)rect
{
[self destroyFramebuffer]; // glDeleteFramebuffers..
[self createFramebuffer]; // [super prepareOpenGl], glGenFrame(Render)Buffers, bind buffers, etc
[self drawView]; // [[self openGLContext] makeCurrentContext], make some drawing, [[self openGLContext] flushBuffer]..
}
like this.
After these changes, when window gets focus it redraws itself (without any resizing stuff :) ).
Hope this helps!
I want this to be a background in my app for Ipad.
I'm building everything in objective C (native app)
I need a little help figuring out how to animate each of the triangles over the image (overlay) so it fades in and out independently of each other, the goal to make a constant shimmer like effect so the image doesn't feel sos tatic. Do i have to animate each triangle independently? Is there any algorithm that i should be looking at so it seems kinda random but isn't.
Here is the background image
I need some guidance on where to start and how to approach this problem, and feedback would be appreciated.
a) My advice is to use UIViewAnimationWithBlocks introduced in iOS 4. If you have a solid grasp on blocks, they can be very useful. Here's an example I created in as little as 5 minutes to illustrate:
typedef void(^FadeInOutBlock)(void);
#interface PMViewController ()
#property (nonatomic, copy) FadeInOutBlock fadeInOutBlock;
#end
Here we declare a typedef to save us from doing the block syntax all over again. We also create a property to hold the animation block.
#implementation PMViewController
#synthesize myView;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
__block PMViewController *_self = self;
self.fadeInOutBlock = ^{
[UIView animateWithDuration:0.5f animations:^{
// fade out effect
_self.myView.alpha = 0.0f;
} completion:^(BOOL success){
[UIView animateWithDuration:0.5f animations:^{
// fade in effect
_self.myView.alpha = 1.0f;
} completion:^(BOOL success){
// recursively fire a new animation
if (_self.fadeInOutBlock)
_self.fadeInOutBlock();
}];
}];
};
}
We create the animation, within an animation. You start off with the fade out, where myView's alpha will be reduced to 0.0f in 0.5f seconds. After it's completed, a second animation will be fired, restoring the alpha for myView back to 1.0f and finally, firing out the first animation, again. (Animception)
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
if (self.fadeInOutBlock)
self.fadeInOutBlock();
}
#end
And finally, in view did appear you fire it off for the first time.
b)
Now, for the shimmering animation that you mention, I suggest you separate each triangle into it's own UIView and use the technique above, using different durations and alphas.
If you have to many small UIViews, group them up into a bigger UIView (by using the addSubview method) and apply the animation to those 'container' UIViews.
For instance, you could have four separate UIView containers, that have a bunch of separate UIViews as their children. You could then create four block animations, one for each container, and then apply the animation to them. I bet experimenting with that, you would be able to create pretty good effects.
You can do this without any special librarys. So you need to create a black UIView with an alpha of 0. Then create an NSTimer that increases the alpha of the UIView.
I have a problem that I think is solvable with some hackery, but I'm very curious if there is an easier way to get the job done without having to do all of that.
I have a stack of NSViews (layer-backed, if that somehow helps provides some better solution), as shown below:
The thing here is that this is essentially a menu, but is hover-sensitive. If the user hovers over one of the exposed parts of the lower-level views, I need to perform an action depending on what that view is. It is a dynamic system so the number of stacked menu items like this may change, making static calculations more difficult. As you can see, they are basically all a copy (shape-wise) of the first item, but then rotated a bit the further you go down the stack via simple transform rotation.
My question to the SO community is what do you all think the best approach to getting mouseEntered: and mouseExited: events for just the literally visible portions of these views?
What I have attempted to do is use an NSTrackingArea on the visibleRect portion of these views, which sounds much more handy than it really is in this situation. In reality, the visibleRect seems to be "visible" for all of them, all the time. Nothing is explicitly blocked or hidden by anything more than just a partially overlapping NSView. All that happens is I get a spammed console from all of the views screaming out at once that a mouse entered their rect.
Something I am considering is making sub-NSView's of each menu item and having each of those be responsible for the tracking area... each menu item having a "strip" view along the right and bottom sides that could report, but that's still a bit of a hack and is icky.
Does anyone have a better idea? Perhaps one from experience?
Thanks!
I know you already have a solution, but I thought I would try a different approach, that didn't require getting tons of mouseMoved events. I created 3 custom views in code, added tracking rects for them and sent all mouseEntered and mouseExited messages to the same method that does a hitTest to determine which view is top most. This is the code for the content view of the window.
#implementation MainView
#synthesize oldView;
-(void)awakeFromNib {
oldView = nil;
Card *card1 = [[Card alloc]initWithFrame:NSMakeRect(150, 150, 200, 150) color:[NSColor redColor] name:#"Red Box"];
NSTrackingArea *area1 = [[NSTrackingArea alloc]initWithRect:card1.frame options:NSTrackingMouseEnteredAndExited|NSTrackingActiveInActiveApp owner:self userInfo:nil];
[self addTrackingArea:area1];
[self addSubview:card1];
Card *card2 = [[Card alloc]initWithFrame:NSMakeRect(180, 120, 200, 150) color:[NSColor yellowColor] name:#"Yellow Box"];
NSTrackingArea *area2 = [[NSTrackingArea alloc]initWithRect:card2.frame options:NSTrackingMouseEnteredAndExited|NSTrackingActiveInActiveApp owner:self userInfo:nil];
[self addTrackingArea:area2];
[self addSubview:card2];
Card *card3 = [[Card alloc]initWithFrame:NSMakeRect(210, 90, 200, 150) color:[NSColor greenColor] name:#"Green Box"];
NSTrackingArea *area3 = [[NSTrackingArea alloc]initWithRect:card3.frame options:NSTrackingMouseEnteredAndExited|NSTrackingActiveInActiveApp owner:self userInfo:nil];
[self addTrackingArea:area3];
[self addSubview:card3];
}
-(void)mouseEntered:(NSEvent *)theEvent {
[self reportTopView:theEvent];
}
-(void)mouseExited:(NSEvent *)theEvent {
[self reportTopView:theEvent];
}
-(void)reportTopView:(NSEvent *)theEvent {
id topView = [self hitTest:[theEvent locationInWindow]];
if (![topView isEqual:oldView]) {
oldView = topView;
([topView isKindOfClass:[Card class]])? NSLog(#"%#",[(Card *)topView name]):NULL;
}
}
This is the code for what I called cards (colored rectangles):
#implementation Card
#synthesize name,fillColor;
- (id)initWithFrame:(NSRect)frame color:(NSColor *)color name:(NSString *)aName{
self = [super initWithFrame:frame];
if (self) {
self.fillColor = color;
self.name = aName;
}
return self;
}
- (void)drawRect:(NSRect)rect {
[self.fillColor drawSwatchInRect:rect];
}
I finally came to a solution on Twitter via Steven Troughton-Smith. Here's how it works:
In each menu item, I am disregarding anything related to NSTrackingArea or direct mouse position interpretation. Instead, the parent controller view is handling all of the tracking and receiving mouse movement events.
Each menu item has an overridden hitTest: method that does the point conversion and returns whether or not the point being tested is within the background image (there are shadows and stuff in there, making it more difficult than the vanilla implementation).
I then setup a sort of "hover menu item changed" callback in the controller so that I can handle hover menu changes.
This was a pretty straightforward solution. Very glad I decided to stop and ask, rather than hack something together with my previous idea.
Thanks Steven!
Overlapping tracking-areas:
All you have to do is hitTest from view you are in. if this is true:
window.view.hitTest(window.mousePos) === self/*sudo code*/
What this code does is that it returns the view under the mouse position. Now all you have to do is setup a few "if" and "else" clauses to verify that your mouse is off or on the view.
Full code example:
https://gist.github.com/eonist/537ae53b86d5fc332fd3
Full description of the concept here: (perma link)
http://stylekit.org/blog/2015/12/20/Overlapping-tracking-areas/
VS the default enter and exit behaviour:
I had to add another answer to this question as this is another approach to solve the problem. This approach now also includes path assertion (think rects with round edges or other custom paths)
The answer is long winded but it works:
http://stylekit.org/blog/2016/01/28/Hit-testing-sub-views/
it involves using the apple provided method: CGPathContainsPoint(path,transform,point)
If you follow the link to that blog post and then from there check the styleKit repo on github. You will find the code need to achieve the gif animation example given above. Im providing this as a pointer to the answer as it may take you significantly less time than trying to research this on your own. I use this technique in all my UI elements and it works flawlessly.
I'm making a 2d Game, in which I need instances of a sprite to fly randomly across the screen. They will spawn in randomly just beyond the boundaries of the iPhone screen, then move within the screen. When they hit the edges, they will appear back on the other side. All I need to know is how to get the sprite to move randomly.
Add this method to your layer class - it takes in a sprite and then moves it randomly around the screen for ever:
-(void)moveRandom:(CCSprite*)s
{
CGPoint randomPoint = ccp(arc4random()%480, arc4random()%320);
NSLog(#"%#", NSStringFromCGPoint(randomPoint));
[s runAction:
[CCSequence actions:
[CCMoveTo actionWithDuration:arc4random()%5+1 position: randomPoint],
[CCCallBlock actionWithBlock:^{
[self performSelector:#selector(moveRandom:) withObject:s afterDelay:0.5];
}],
nil]
];
}
As you can see it's pretty easy - generate a random point on the screen and then run move action on the sprite to that point. When that's done - just repeat.
To add a sprite on the screen and start the process, put this (probably) in your scene init method or wherever you do the scene initialization:
CCSprite* s = [CCSprite spriteWithFile:#"yourImage.png"];
[self addChild: s];
[self moveRandom:s];