Cocos2D Infinite Background Picture - objective-c

I'm curious as to how to create an infinite background in cocos2d. For example lets say I was building an app with a man running from left to right, and I want him to run infinitely. Well in that case I would have to have an endless background so the man could keep running. I've continuously searched on this matter and have found nothing that actually works.
Any types of suggestions, answers, and tips are much appreciated.
Thanks

Try This:
#define IS_IPAD (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
#define MM_BG_SPEED_DUR ( IS_IPAD ? (6.0f) : (2.0f) )
-(void)onEnter
{
[super onEnter];
[self initBackground];
[self schedule: #selector(tick:)];
}
-(void)initBackground
{
NSString *tex = #"BG/Background.png";//[self getThemeBG];
mBG1 = [CCSprite spriteWithFile:tex];
mBG1.position = ccp(s.width*0.5f,s.height*0.5f);
[self addChild:mBG1 z:LAYER_BACKGROUND];
mBG2 = [CCSprite spriteWithFile:tex];
mBG2.position = ccp(s.width+s.width*0.5f,s.height*0.5f);
mBG2.flipX = true;
[self addChild:mBG2 z:LAYER_BACKGROUND];
}
-(void)scrollBackground:(ccTime)dt
{
CGSize s = [[CCDirector sharedDirector] winSize];
CGPoint pos1 = mBG1.position;
CGPoint pos2 = mBG2.position;
pos1.x -= MM_BG_SPEED_DUR;
pos2.x -= MM_BG_SPEED_DUR;
if(pos1.x <=-(s.width*0.5f) )
{
pos1.x = pos2.x + s.width;
}
if(pos2.x <=-(s.width*0.5f) )
{
pos2.x = pos1.x + s.width;
}
mBG1.position = pos1;
mBG2.position = pos2;
}
-(void)tick:(ccTime)dt
{
[self scrollBackground:dt];
}

The easiest way would be to include two background images that mesh seamlessly together. (CCSprite would work fine for this) In your update method as soon as the first background is completely off of the screen move it back to the other side of the screen directly next to the second background and continually move both background images. Repeat this process for the second background as well.

CCTMXTiledMap can help you, but I'm afraid that you must handle end of map and add another manually. Check this tutorials how to use tiled maps in side-scrolling games, hope it will be useful for you:
http://www.raywenderlich.com/15230/how-to-make-a-platform-game-like-super-mario-brothers-part-1

Try this. It is very easily to implement and works well. Just follow the tutorial on the read me.

Related

How to trick an OS X app into thinking the mouse is a finger?

I'm writing a Mac app that contains a collection view. This app is to be run on a large touchscreen display (55" EP series from Planar). Due to hardware limitation, the touchscreen doesn't send scroll events (or even any multitouch events). How can I go about tricking the app into thinking a "mousedown+drag" is the same as a "mousescroll"?
I got it working halfway by subclassing NSCollectionView and implementing my own NSPanGestureRecognizer handler in it. Unfortunately the result is clunky and doesn't have the feeling of a normal OS X scroll (i.e., the velocity effect at the end of a scroll, or scroll bounce at the ends of the content).
#implementation UCTouchScrollCollectionView
...
- (IBAction)showGestureForScrollGestureRecognizer:(NSPanGestureRecognizer *)recognizer
{
CGPoint location = [recognizer locationInView:self];
if (recognizer.state == NSGestureRecognizerStateBegan) {
touchStartPt = location;
startOrigin = [(NSClipView*)[self superview] documentVisibleRect].origin;
} else if (recognizer.state == NSGestureRecognizerStateEnded) {
/* Some notes here about a future feature: the Scroll Bounce
I don't want to have to reinvent the wheel here, but it
appears I already am. Crud.
1. when the touch ends, get the velocity in view
2. Using the velocity and a constant "deceleration" factor, you can determine
a. The time taken to decelerate to 0 velocity
b. the distance travelled in that time
3. If the final scroll point is out of bounds, update it.
4. set up an animation block to scroll the document to that point. Make sure it uses the proper easing to feel "natural".
5. make sure you retain a pointer or something to that animation so that a touch DURING the animation will cancel it (is this even possible?)
*/
[self.scrollDelegate.pointSmoother clearPoints];
refreshDelegateTriggered = NO;
} else if (recognizer.state == NSGestureRecognizerStateChanged) {
CGFloat dx = 0;
CGFloat dy = (startOrigin.y - self.scrollDelegate.scrollScaling * (location.y - touchStartPt.y));
NSPoint scrollPt = NSMakePoint(dx, dy);
[self.scrollDelegate.pointSmoother addPoint:scrollPt];
NSPoint smoothedPoint = [self.scrollDelegate.pointSmoother getSmoothedPoint];
[self scrollPoint:smoothedPoint];
CGFloat end = self.frame.size.height - self.superview.frame.size.height;
CGFloat threshold = self.superview.frame.size.height * kUCPullToRefreshScreenFactor;
if (smoothedPoint.y + threshold >= end &&
!refreshDelegateTriggered) {
NSLog(#"trigger pull to refresh");
refreshDelegateTriggered = YES;
[self.refreshDelegate scrollViewReachedBottom:self];
}
}
}
A note about this implementation: I put together scrollScaling and pointSmoother to try and improve the scroll UX. The touchscreen I'm using is IR-based and gets very jittery (especially when the sun is out).
In case it's relevant: I'm using Xcode 6 beta 6 (6A280e) on Yosemite beta (14A329r), and my build target is 10.10.
Thanks!
I managed to have some success using an NSPanGestureRecognizer and simulating the track-pad scroll wheel events. If you simulate them well you'll get the bounce from the NSScrollView 'for free'.
I don't have public code, but the best resource I found that explained what the NSScrollView expects is in the following unit test simulating a momentum scroll. (See mouseScrollByWithWheelAndMomentumPhases here).
https://github.com/WebKit/webkit/blob/master/LayoutTests/fast/scrolling/latching/scroll-iframe-in-overflow.html
The implementation of mouseScrollByWithWheelAndMomentumPhases gives some tips on how to synthesize the scroll events at a low level. One addition I found I needed was to actually set an incrementing timestamp in the event in order to get the scroll-view to play ball.
https://github.com/WebKit/webkit/blob/master/Tools/WebKitTestRunner/mac/EventSenderProxy.mm
Finally, in order to actually create the decaying velocity, I used a POPDecayAnimation and tweaked the velocity from the NSPanGestureRecognizer to feel similar. Its not perfect but it does stay true to NSScrollView's bounce.
I have a (dead) project on Github that does this with an NSTableView, so hopefully it will work well for an NSCollectionView.
Disclaimer: I wrote this while I was still learning GCD, so watch for retain cycles... I did not vet what I just posted for bugs. feel free to point any out :) I just tested this on Mac OS 10.9 and it does still work (originally written for 10.7 IIRC), not tested on 10.10.
This entire thing is a hack to be sure, it looks like it requires (seems to anyway) asynchronous UI manipulation (I think to prevent infinite recursion). There is probably a cleaner/better way and please share it when you discover it!
I havent touched this in months so I cant recall all the specifics, but the meat of it surely is in the NBBTableView code, which will paste snippets of.
first there is an NSAnimation subclass NBBScrollAnimation that handles the "rubber band" effect:
#implementation NBBScrollAnimation
#synthesize clipView;
#synthesize originPoint;
#synthesize targetPoint;
+ (NBBScrollAnimation*)scrollAnimationWithClipView:(NSClipView *)clipView
{
NBBScrollAnimation *animation = [[NBBScrollAnimation alloc] initWithDuration:0.6 animationCurve:NSAnimationEaseOut];
animation.clipView = clipView;
animation.originPoint = clipView.documentVisibleRect.origin;
animation.targetPoint = animation.originPoint;
return [animation autorelease];
}
- (void)setCurrentProgress:(NSAnimationProgress)progress
{
typedef float (^MyAnimationCurveBlock)(float, float, float);
MyAnimationCurveBlock cubicEaseOut = ^ float (float t, float start, float end) {
t--;
return end*(t * t * t + 1) + start;
};
dispatch_sync(dispatch_get_main_queue(), ^{
NSPoint progressPoint = self.originPoint;
progressPoint.x += cubicEaseOut(progress, 0, self.targetPoint.x - self.originPoint.x);
progressPoint.y += cubicEaseOut(progress, 0, self.targetPoint.y - self.originPoint.y);
NSPoint constraint = [self.clipView constrainScrollPoint:progressPoint];
if (!NSEqualPoints(constraint, progressPoint)) {
// constraining the point and reassigning to target gives us the "rubber band" effect
self.targetPoint = constraint;
}
[self.clipView scrollToPoint:progressPoint];
[self.clipView.enclosingScrollView reflectScrolledClipView:self.clipView];
[self.clipView.enclosingScrollView displayIfNeeded];
});
}
#end
You should be able to use the animation on any control that has an NSClipView by setting it up like this _scrollAnimation = [[NBBScrollAnimation scrollAnimationWithClipView:(NSClipView*)[self superview]] retain];
The trick here is that the superview of an NSTableView is an NSClipView; I dont know about NSCollectionView, but I suspect that any scrollable control uses NSClipView.
Next here is how the NBBTableView subclass makes use of that animation though the mouse events:
- (void)mouseDown:(NSEvent *)theEvent
{
_scrollDelta = 0.0;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
if (_scrollAnimation && _scrollAnimation.isAnimating) {
[_scrollAnimation stopAnimation];
}
});
}
- (void)mouseUp:(NSEvent *)theEvent
{
if (_scrollDelta) {
[super mouseUp:theEvent];
// reset the scroll animation
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
NSClipView* cv = (NSClipView*)[self superview];
NSPoint newPoint = NSMakePoint(0.0, ([cv documentVisibleRect].origin.y - _scrollDelta));
NBBScrollAnimation* anim = (NBBScrollAnimation*)_scrollAnimation;
[anim setCurrentProgress:0.0];
anim.targetPoint = newPoint;
[anim startAnimation];
});
} else {
[super mouseDown:theEvent];
}
}
- (void)mouseDragged:(NSEvent *)theEvent
{
NSClipView* clipView=(NSClipView*)[self superview];
NSPoint newPoint = NSMakePoint(0.0, ([clipView documentVisibleRect].origin.y - [theEvent deltaY]));
CGFloat limit = self.frame.size.height;
if (newPoint.y >= limit) {
newPoint.y = limit - 1.0;
} else if (newPoint.y <= limit * -1) {
newPoint.y = (limit * -1) + 1;
}
// do NOT constrain the point here. we want to "rubber band"
[clipView scrollToPoint:newPoint];
[[self enclosingScrollView] reflectScrolledClipView:clipView];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
NBBScrollAnimation* anim = (NBBScrollAnimation*)_scrollAnimation;
anim.originPoint = newPoint;
});
// because we have to animate asyncronously, we must save the target value to use later
// instead of setting it in the animation here
_scrollDelta = [theEvent deltaY] * 3.5;
}
- (BOOL)autoscroll:(NSEvent *)theEvent
{
return NO;
}
I think that autoscroll override is essential for good behavior.
The entire code is on my github page, and it contains several other "touch screen" emulation tidbits, if you are interested, such as a simulation for the iOS springboard arrangeable icons (complete with "wiggle" animation using NSButtons.
Hope this helps :)
Edit: It appears that constrainScrollPoint: is deprecated in OS X 10.9. However, It should fairly trivial to reimplement as a category or something. Maybe you can adapt a solution from this SO question.

Time based sprite movement when coming back from background

I'm pretty new working with SpriteKit. I have a working example of an game I want to develop. I have some time based movements of sprites, with these I mean:
SKSpriteNode * track = (SKSpriteNode *) node;
CGPoint trackVelocity = CGPointMake(0, -objectVelocity);
CGPoint amtToMove = CGPointMultiplyScalar(trackVelocity,_dt);
track.position = CGPointAdd(track.position, amtToMove);
where "_dt" is:
-(void)update:(CFTimeInterval)currentTime {
if (_lastUpdateTime)
{
_dt = currentTime - _lastUpdateTime;
}
else
{
_dt = 0;
}
_lastUpdateTime = currentTime;
}
The problem I'm having is that when the user goes to background and comes back to the app a long time later that _dt is HUGE, so all the sprites that are moved with the _dt variable are gone from screen and never come back... I can't find a way of setting this _dt to a correct value.
How can I achieve this?
Thanks a lot!!
I already have in the AppDelegate the following added:
- (void)applicationWillResignActive:(UIApplication *)application
{
SKView *view = (SKView *)self.window.rootViewController.view;
view.paused = YES;
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
SKView *view = (SKView *)self.window.rootViewController.view;
view.paused = NO;
}
And it pauses the game, but the _dt that is set in the update is still being changed to a HUGE number and all my sprites go out of screen.
Here is the output of when going to background and coming back:
NSLog(#"%f %f",_lastUpdateTime,_dt);
2014-06-30 08:34:00.988 TestingSpriteKit[26303:60b] 224490.130906 0.033162
2014-06-30 08:34:19.761 TestingSpriteKit[26303:60b] 224508.904119 18.773212
2014-06-30 08:34:19.804 TestingSpriteKit[26303:60b] 224508.947477 0.043359
You can set a maximum _dt value in your update method such as
if _dt > 1 {
_dt = 1.0 / 60.0
}
This is the logic you will see in the Adventure demo app from Apple. Look for kMinTimeInterval to see how they do it.
I think this is a wise solution as there could be additional scenarios, aside from coming back from background, that result in a large delta time.
Without going into too many options, it sounds like you need to set a BOOL and manage your _dt based on it.
so in applicationWillResignActive:(UIApplication *)application set the bool property in the scene to:
theMainScene.appWentIntoBackground = YES;
and then at the beginning of your update method, manage your time interval based on the bool. I don't know whether you want a default amount, or just to skip the entire gap, etc, but something like this maybe:
if (_appWentIntoBackground == YES) {
_appWentIntoBackground = NO;
_lastTime = _currentTime;
}
You should pause your SKScene and it's SKView when app goes to background.
When SKView is paused, update: method doesn't get called.

How to turn off UIWebView horizontal bounce

Simple problem, I have a webview that is supposed to hold just an image for the user to be able to zoom in and out. To keep my look clean, I want to completely disable bouncing on this view, but still allow scrolling. This solution does work for the vertical bounce, but as soon as I zoom the image to a size larger than the screen, horizontal bounce is still possible:
for (id subview in webView.subviews
{
if ( [[subview class] isSubclassOfClass:[UIScrollView class]] )
{
((UIScrollView*) subview).bounces = NO;
((UIScrollView*) subview).alwaysBounceVertical = NO;
((UIScrollView*) subview).alwaysBounceHorizontal = NO;
((UIScrollView*) subview).bouncesZoom = NO;
}
}
The following code did the trick for us to stop bouncing:
NSString* scriptToPreventBouncing = #"<script type=\"text/javascript\"> document.ontouchmove = function(e){ e.preventDefault(); } </script>";
NSString* footerHTML = #"<div>All rights reserved</div>";
[footer loadHTMLString: [scriptToPreventBouncing stringByAppendingString:footerHTML] baseURL:[NSURL URLWithString:#"http://somewebsite.com"]];
I am not sure if this will disable the zooming of image. But it should stop the bouncing.
On iOS 5 you can solve your problem this way:
UIWebView *webView = [[UIWebView alloc] ....];
webView.scrollView.scrollEnabled = NO;
webView.scrollView.bounces = NO;
NOTE: this will work only on iOS5 and higher

Slight pause in scrolling animation (iPad)

I am relatively new to programming on the iPad and I was trying to put together a simple program. Basically, it's a children's book and I need the functionality of a comic book style (or photo) viewer, where people swipe to change "pages" (or images).
Each image is 1024x768. Currently, they are stored as JPGs because of the very large file sizes PNGs seem to produce. For this story, there are 28 pages.
I took a look at the PageControl example, implementing a UIScrollView. On initialization, I create a big enough scrollview area. Then as the user scrolls, I load in the previous and next images. Again, just like the example only without implementing the page control at the bottom.
The problem I am running into is a very slight pause in the animation when I am flipping. Once the images are loaded or cached, this doesn't happen. Now, I know the photo application doesn't do this and I'm not sure what is causing it.
Here is my code for the scrollViewDidScroll method. I keep up with the page number and it will only call the loadPageIntoScrollView when a page has changed - I was thinking that the insane number of calls it was making was causing the slight pause in animation, but it turned out not to be the case.
- (void) scrollViewDidScroll: (UIScrollView *) sender
{
CGFloat pageWidth = scrollView.frame.size.width;
int localPage = floor( (scrollView.contentOffset.x - pageWidth / 2 ) / pageWidth ) + 1;
if( localPage != currentPage )
{
currentPage = localPage;
[self loadPageIntoScrollView:localPage - 1];
[self loadPageIntoScrollView:localPage];
[self loadPageIntoScrollView:localPage + 1];
}
} // scrollViewDidScroll
And here is my loadPageIntoScrollView method. I'm only creating a UIImageView and loading an image into that - I don't see how that could be much "leaner". But somehow it's causing the pause. Again, it's not a HUGE pause, just one of those things you notice and is enough to make the scrolling look like it has a very. very slight hiccup.
Thank you in advance for any help you could provide.
- (void) loadPageIntoScrollView: (int)page
{
if( page < 0 || page >= kNumberOfPages )
return;
UIImageView *controller = [pages objectAtIndex:page];
NSLog( #"checking pages" );
if( (NSNull *)controller == [NSNull null] )
{
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleSingleTap:)];
NSString *pageName = [[NSString alloc] initWithFormat:#"%d.jpg", page];
controller = [[UIImageView alloc]initWithImage:[UIImage imageNamed:pageName]];
[controller setUserInteractionEnabled:YES];
[controller addGestureRecognizer:singleTap];
[pages replaceObjectAtIndex:page withObject:controller];
[controller release];
} // if controller == null
// add the page to the scrollview
if( controller.superview == nil )
{
NSLog(#"superview was nil, adding page %d", page );
CGRect frame = scrollView.frame;
frame.origin.x = frame.size.width * page;
frame.origin.y = 0;
controller.frame = frame;
[scrollView addSubview:controller];
} // if
} // loadPageIntoScrollView
Since you say after an image is loaded in it no longer lags, I'd suspect that it is disk access that is causing your lag, but you should run your app through instruments to try to rule out cpu-spikes as well as evaluate file system usage. You may try to pre-load images to the left and right of whatever image you are on so that the user doesn't perceive as much lag.
First off, you should be able to use PNG's just fine. I have build several apps that do exactly what you are doing here, you can fit 3 1024 x 768 PNGs in memory without running out (but you can't do much more). You should also use PNG's as they are the preferred format for iOS as they are optimized when the app is bundled together during build.
The slight lag is caused by loading the image, in this line:
controller = [[UIImageView alloc]initWithImage:[UIImage imageNamed:pageName]];
What I usually do is load the images in a separate thread, using something like this:
[self performSelectorInBackground:#selector(loadPageIntoScrollView:) withObject:[NSNumber numberWithInt:localPage]];
Note that you need to put your localPage integer into a NSNumber object to pass it along, so don't forget to change your loadPageIntoScrollView: method:
- (void) loadPageIntoScrollView: (NSNumber *)pageNumber
{
int page = [pageNumber intValue];
....

When sliding sprite, if sprite disappears off the side, it will wrap around to the opposite side?

When sliding sprite, if sprite disappears off the side, i want to make it wrap around to the opposite side but I do not know how to do this while the sprite is simultaneously being pushed off one side i want the other bit that you can't see to appear on the opposite side like a loop some sort of wormhole thing.
here is my code so far but it crashes and it only transports the sprite once the whole of the sprite disappears of the side. Loop also needs to run as an infinite loop until someone quits the app.
for (int i =0; i<16; ++i) {
MyNode *currentSprite = [c1array objectAtIndex:i];
if (currentSprite.contentSize.height>=320 || currentSprite.position.y-currentSprite.contentSize.height/2<=0 ){
MyNode *Bsprite = currentSprite;
MyNode *Tsprite = currentSprite;
Bsprite.scale = 1.0;
Tsprite.scale = 1.0;
if(currentSprite.position.y >=253){
Bsprite.position = ccp(currentSprite.position.x,-35);
[self addChild:Bsprite];
Bsprite.visible = TRUE;
}
if (currentSprite.position.y <=0) {
Tsprite.position = ccp(currentSprite.position.x,324);
[self addChild:Tsprite];
Tsprite.visible = TRUE;
}
MyNode *isChanging;
if ((Tsprite.visible == TRUE && currentSprite.visible == TRUE) || (Bsprite.visible == TRUE && currentSprite.visible == TRUE)) {
isChanging = TRUE;
}
if (isChanging == FALSE) {
[self removeChild:Tsprite cleanup:YES];
[self removeChild:Bsprite cleanup:YES];
}
}
}
It is not possible to do with one sprite. But you can have two sprites. In common situation when your sprite is sliding along the screen only one sprite will be visible. But when it reaches the border the second one will be visible too. When the second one will completely enter the screen - remove (or hide) the first one.
The best way to implement this is to create a CCNode subclass that will contain first and second sprite and will swap them if required. In this way all your logic will be very simple. You will just work with one CCNode (subclass) and will not think about swaping sprites - it will be done automatically by your class
EDIT
#interface MyNode : CCNode
{
CCSprite *sprite1;
CCSprite *sprite2;
CCSprite *currentSprite;
bool isChanging; //indicates that now two sprites are visible
}
#end