How To Drag and Drop Sprites with Cocos2D
来源:互联网 发布:好看的日剧推荐 知乎 编辑:程序博客网 时间:2024/05/17 02:03
Like this post? Follow me on Twitter! I’ve received several requests to write a tutorial on how to move sprites in Cocos2D by dragging them with touches. You asked for it, you got it! In this tutorial, you’re going to learn: To make things fun, you’ll be moving some cute animals around the scene drawn by mylovely wife, on a background made by gwebstock. This tutorial assumes you have at least basic knowledge of Cocos2D and have the Cocos2D templates already installed. If you are new to Cocos2D, you may wish to try out the How To Make A Simple iPhone Game with Cocos2D tutorial first. So without further ado, drag your fingers over to the keyboard and let’s get started! Before you implement the touch handling, first you’ll create a basic Cocos2D scene displaying the sprites and artwork. Open up XCode, go to File/New Project, choose User Templates/cocos2d X.X.X/cocos2d Application, and click “Choose…”. Name the project DragDrop, and click Save. Next, go ahead and donwload the images you’ll need for this tutorial. Once you download and unzip the file, drag all of the images into the Resources group in your project. Verify that “Copy items into destination group’s folder (if needed)” is checked, and click Add. Once you’ve added the images to your project, open the Classes group in XCode, and select HelloWorld.h. Inside the @interface declaration, declare three instance variables you’ll need like the following: You’ll be using these later on to keep track of the background image, the currently selected sprite, and the list of sprites that should be moved with touches. Now switch to HelloWorldScene.m. Find the init method, and replace it with the following: There’s some new stuff in here, so let’s go over it step by step. The first part of the method loads the background image for the scene (blue-shooting-stars.png). Note that it sets the anchor point of the image to the lower left of the image (0, 0). In Cocos2D, when you set the posiiton of a sprite, you are actually setting where the anchor point of the image is. By default, the anchor point is set to the exact center of the image. However, by setting the anchor point to the lower left corner, when you set the position of the sprite, you are now setting where the lower left corner is. The method doesn’t set the position of the background anywhere, so the position of the background defaults to (0,0). Hence, the lower left corner of time image is located at (0,0), and so the image (which is about 800 points long) extends off screen to the right. Another thing to note about loading the background image is it switches the pixel format before it loads the image. By default, when you load images with Cocos2D, they are loaded as 32-bit images. This means each pixel takes up 4 bytes in memory. This is good when you want very high quality artwork, but the tradeoff is it takes a large amount of memory to load the images – a premium on mobile devices. When you’re loading large images (such as background images), it’s often good practice to load the background images as 16-bit images instead – basically sacrificing quality for lower memory usage. There are several different texture formats available in Cocos2D – here you choose the best possible level of quality for 16-bit images that don’t need any transparency effects. The next part of the method loops through the list of images to load and creates and places a sprite for each. The sprites are distributed along the length of the screen, to have a nice initial layout. It also puts a reference to each sprite in the array of movable sprites, which will be used later. Speaking of which, one last step – find your dealloc method and add the following lines to release the array: That’s it! Compile and run your code, and you should see a couple cute animals sitting there, just begging to be touched! Now you’ll write the code to determine which sprite is selected based on the user’s current touch. The first step is to enable your HelloWorldLayer to receive touches. Add the following line to the end of your init method to do so: Note that this is a relatively new way to enable touches on your layer – the old way was to set the isTouchEnabled property to yes and implement ccTouchesBegan. If you are curious as to the advantages of this new method over the old method, check out the How To Make a Tile Based Game with Cocos2D Tutorial for more information. Next, add the following new methods to the bottom of HelloWorldScene.m: The first method (selectSpriteForTouch) is a helper method that loops through all of the sprites in the movableSprites array, looking for the first sprite that the touch intersects. Note that CCNode has a helper property called boundingBox that returns the bounding box around the sprite. This is generally better to use than computing the rectangle yourself, because a) it’s easier, and b) it takes the transform of the sprite into consideration. After it finds a matching sprite (if any), it runs a little animation on the sprite so the user knows which they have selected. It first cancels any animation running on the previously selected sprite, then uses a sequence of CCActions to make the sprite appear to “wiggle” back and forth (much like the iOS home screen when rearranging/deleting apps). Finally, the ccTouchBegan method calls the above method based on the user’s touch. Note that it has to convert the touch location from UIView coordinates to layer (node space) coordinates. To do this, it calls a helper method in CCNode called convertTouchToNodeSpace. This method does three things: This is a common conversion to do, so using this method saves some time. Compile and run your code, and you should now be able to click on the animals, and when you tap them they should wiggle in a particularly cute way to show that they are selected! Time to make these animals move! The basic idea is you’ll implement the ccTouchMoved callback, and figure out how much the touch has moved since last time. If an animal is selected, it will move the animal by that amount. If an animal is not selected, it will move the entire layer instead, so that the user can scroll the layer from left to right. Before you add any code though, let’s take a minute to discuss how you can scroll a layer in Cocos2D. Start by taking a look at the image below: As you can see, you’ve set up the background so the anchor point (the lower left) is at (0, 0), and the rest extends off to the right. The black area indicates the current visible area (the size of the window). So if you want to scroll the image 100 points to the right, you can do that by moving the entire Cocos2D layer 100 points to the left, as you can see in the second image. You also want to make sure you don’t scroll too far. For example, you shouldn’t be able to move the layer to the right, since there would be a blank spot. Now that you’re armed with this background information, let’s see what it looks like in code! Add the following new methods to the bottom of your file: The first method (boundLayerPos) is used for making sure you don’t scroll the layer beyond the bounds of the background image. You pass in where you’d like to move the layer, and it modifies what you pass in to make sure you don’t scroll too far. If you have any troubles understanding what’s going on here, consult the picture above and draw it out on paper. The next method (panForTranslation) moves the sprite (if there’s one selected) based on a passed-in translation, and if not moves the layer itself. This is done by setting the position for the sprite or layer. The final method (ccTouchMoved) is the callback you get when the user drags their finger. As you did earlier, you convert the touch to layer coordinates, and then you need to get the information about the previous touch as well. There is no helper moethod such as convertTouchToNodeSpace for the previous touch, so here you have to do the steps to convert the touch coordinates manually. Then it figures out the amount the touch moved by subtracting the current location from the last location, and calls the panForTranslation method you wrote above. Give it a shot – compile and run your code, and you should now be able to move the sprites (and the layer!) around by dragging! There’s another way to accomplish what you just did with Cocos2D touch handling – use gesture recognizers instead! Gesture recognizers are a relatively new addition to the iOS SDK (introduced in iOS SDK 3.2). And let me tell you, they are awesome. Basically, instead of having to write a bunch of crazy looking code to detect the difference between taps, double taps, swipes, pans, or pinches, you simply create a gesture recognizer object for what you want to detect, and add it to the view. It will then give you a callback when that occurs! They are extremely easy to use, nd you can use them with Cocos2D with no troubles. Let’s see how that works. First, go to your init method and comment out the touch registration, since you will be using a different method now: Then, go to DragDropAppDelegate.m and inside applicationDidFinishLaunching, replace the last line in the method with the following: This code gets a reference to the HelloWorld layer (it knows that it’s the only child of the HelloWorld scene), and then creates a UIPanGestureRecognizer. Note that to create a gesture recognizer, you just have to initialize it and pass in where the callback should go to – in this case the handlePanFrom method in the layer. After creating the gesture recognizer, it adds it to the OpenGL view (viewController.view). Next, add the following to the bottom of your HelloWorldScene.m: This callback gets called when the pan gesture begins, changes (i.e the user continues to drag), and ends. The method switches on each case, and does the approprite action. When the gesture begins, it converts the coordinates to node coordinates (note it has to do it the long way because there’s no shortcut method), and calls the selectSpriteForTouch helper you wrote earlier. When the gesture changes, it needs to figure out the amount the gesture moved. One of the nice things about gesture recognizers it actually stores for you the cumulative translation for the gesture so far! However, you have to reverse the y coordinate to take into effect the difference between UIKit coordinates and OpenGL coordinates. After panning for the translation, it resets the translation on the recognizer to zero, because otherwise the translation is cumulative, and you just want the difference each time. When the gesture ends, there’s some new and interesting code in here! Another cool thing a UIPanGestureRecognizer gives you is the velocity of the pan movement. You can use this to animate the layer to slide a bit, so the user can flick quickly to get the layer to slide a bit, like you’re used to seeing in table views. So this section contains a bit of code to calculate a point to move based on the velocity, and running a CCMoveTo action (with CCEaseOut to make it feel a bit better) for a neat effect. Compile and run your code, and you should now be able to slide and move around, all with gesture recognizers! Here is a sample project with all of the code from this tutorial. At this point you should know how to move sprites using touches in your Cocos2D apps and games, and should know the basics of using gesture recognizers with Cocos2D. From here, you could try extending this project with other gesture recognizers, such as perhaps pinch or rotate gesture recognizers. Maybe you can make the cat grow! Thank you to all of those who suggested making this tutorial, hopefully this is useful for you guys. The more requests I see on the topic, the more likely I am to write it, so keep the requests coming! :] Category: iPad, iPhone //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////How To Drag and Drop Sprites with Cocos2D
Getting Started
CCSprite * background;CCSprite * selSprite;NSMutableArray * movableSprites;
-(id) init { if((self = [super init])) { CGSize winSize = [CCDirector sharedDirector].winSize; [CCTexture2D setDefaultAlphaPixelFormat:kCCTexture2DPixelFormat_RGB565]; background = [CCSprite spriteWithFile:@"blue-shooting-stars.png"]; background.anchorPoint = ccp(0,0); [self addChild:background]; [CCTexture2D setDefaultAlphaPixelFormat:kCCTexture2DPixelFormat_Default]; movableSprites = [[NSMutableArray alloc] init]; NSArray *images = [NSArray arrayWithObjects:@"bird.png", @"cat.png", @"dog.png", @"turtle.png", nil]; for(int i = 0; i < images.count; ++i) { NSString *image = [images objectAtIndex:i]; CCSprite *sprite = [CCSprite spriteWithFile:image]; float offsetFraction = ((float)(i+1))/(images.count+1); sprite.position = ccp(winSize.width*offsetFraction, winSize.height/2); [self addChild:sprite]; [movableSprites addObject:sprite]; } } return self;}
Loading the Background
Loading the Images
[movableSprites release];movableSprites = nil;
Selecting Sprites based on Touches
[[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:0 swallowsTouches:YES];
- (void)selectSpriteForTouch:(CGPoint)touchLocation { CCSprite * newSprite = nil; for (CCSprite *sprite in movableSprites) { if (CGRectContainsPoint(sprite.boundingBox, touchLocation)) { newSprite = sprite; break; } } if (newSprite != selSprite) { [selSprite stopAllActions]; [selSprite runAction:[CCRotateTo actionWithDuration:0.1 angle:0]]; CCRotateTo * rotLeft = [CCRotateBy actionWithDuration:0.1 angle:-4.0]; CCRotateTo * rotCenter = [CCRotateBy actionWithDuration:0.1 angle:0.0]; CCRotateTo * rotRight = [CCRotateBy actionWithDuration:0.1 angle:4.0]; CCSequence * rotSeq = [CCSequence actions:rotLeft, rotCenter, rotRight, rotCenter, nil]; [newSprite runAction:[CCRepeatForever actionWithAction:rotSeq]]; selSprite = newSprite; }} - (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event { CGPoint touchLocation = [self convertTouchToNodeSpace:touch]; [self selectSpriteForTouch:touchLocation]; return TRUE; }
Moving Sprites and the Layer based on Touches
- (CGPoint)boundLayerPos:(CGPoint)newPos { CGSize winSize = [CCDirector sharedDirector].winSize; CGPoint retval = newPos; retval.x = MIN(retval.x, 0); retval.x = MAX(retval.x, -background.contentSize.width+winSize.width); retval.y = self.position.y; return retval;} - (void)panForTranslation:(CGPoint)translation { if (selSprite) { CGPoint newPos = ccpAdd(selSprite.position, translation); selSprite.position = newPos; } else { CGPoint newPos = ccpAdd(self.position, translation); self.position = [self boundLayerPos:newPos]; } } - (void)ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event { CGPoint touchLocation = [self convertTouchToNodeSpace:touch]; CGPoint oldTouchLocation = [touch previousLocationInView:touch.view]; oldTouchLocation = [[CCDirector sharedDirector] convertToGL:oldTouchLocation]; oldTouchLocation = [self convertToNodeSpace:oldTouchLocation]; CGPoint translation = ccpSub(touchLocation, oldTouchLocation); [self panForTranslation:translation]; }
How to Use Gesture Recognizers with Cocos2D
//[[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:0 swallowsTouches:YES];
CCScene *scene = [HelloWorld scene];HelloWorld *layer = (HelloWorld *) [scene.children objectAtIndex:0];UIPanGestureRecognizer *gestureRecognizer = [[[UIPanGestureRecognizer alloc] initWithTarget:layer action:@selector(handlePanFrom:)] autorelease];[viewController.view addGestureRecognizer:gestureRecognizer]; [[CCDirector sharedDirector] runWithScene:scene];
- (void)handlePanFrom:(UIPanGestureRecognizer *)recognizer { if (recognizer.state == UIGestureRecognizerStateBegan) { CGPoint touchLocation = [recognizer locationInView:recognizer.view]; touchLocation = [[CCDirector sharedDirector] convertToGL:touchLocation]; touchLocation = [self convertToNodeSpace:touchLocation]; [self selectSpriteForTouch:touchLocation]; } else if (recognizer.state == UIGestureRecognizerStateChanged) { CGPoint translation = [recognizer translationInView:recognizer.view]; translation = ccp(translation.x, -translation.y); [self panForTranslation:translation]; [recognizer setTranslation:CGPointZero inView:recognizer.view]; } else if (recognizer.state == UIGestureRecognizerStateEnded) { if (!selSprite) { float scrollDuration = 0.2; CGPoint velocity = [recognizer velocityInView:recognizer.view]; CGPoint newPos = ccpAdd(self.position, ccpMult(velocity, scrollDuration)); newPos = [self boundLayerPos:newPos]; [self stopAllActions]; CCMoveTo *moveTo = [CCMoveTo actionWithDuration:scrollDuration position:newPos]; [self runAction:[CCEaseOut actionWithAction:moveTo rate:1]]; } } }
Where To Go From Here?
- How To Drag and Drop Sprites with Cocos2D
- Drag and drop with Javascript
- How to add OLE drag-and-drop functionality to a pre-existing MFC application
- How to Drag and Drop in JavaScript(怎样通过JavaScript拽动窗口)
- Drag and Drop -- Adding Drag and Drop to an SWT Application
- Drag and Drop
- 学习drag and drop
- 关于drag and drop
- html5 Drag and drop
- Ajax Drag and Drop
- QT5 drag and drop
- HTML5 Drag and Drop
- drag and drop学习
- android - Drag and Drop
- html5 Drag and Drop
- Qt Drag and Drop
- drag and drop(拖拽)
- Drag And Drop 进阶
- Kingston DataTraveler G3 4G 量产成功
- MongoDB auto-sharding
- 明天你是否会想起
- How to Create and Optimize Sprite Sheets in Cocos2D with Texture Packer and Pixel Formats
- flex ant 例子
- How To Drag and Drop Sprites with Cocos2D
- 帮朋友记录点东西。
- How To Create A Mole Whacking Game with Cocos2D: Part 1/2
- 产品管理与产品营销的区别与合作
- How To Create A Mole Whacking Game with Cocos2D: Part 2/2
- Android 计时器Timer用法
- error C2146: syntax error : missing ';' before identifier 'PVOID64'
- CreateFont(MFC)字体设计
- 互联网巨头逐鹿开放平台