Detect what instance of SKSpriteNode was touched? - sprite-kit

I'm using this code to detect and see if the users tap was inside the frame of my SKSpriteNode, and if it is, remove the node from the screen. But I only want the node that was tapped to disappear.
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
/* Called when a touch begins */
for (UITouch *touch in touches) {
CGPoint location = [touch locationInNode:self];
if ((location.x > self.crate.frame.origin.x && location.x < self.crate.frame.origin.x + self.crate.frame.size.width) &&
(location.y > self.crate.frame.origin.y && location.y < self.crate.frame.origin.y + self.crate.frame.size.height)) {
[self.crate removeFromParent];
}
}
}
In my update method, I am calling a method, addCrate: to spawn the node every second.
- (void)updateWithTimeSinceLastUpdate:(CFTimeInterval)timeSinceLast {
self.lastSpawnTimeInterval += timeSinceLast;
if (self.lastSpawnTimeInterval > 1) {
self.lastSpawnTimeInterval = 0;
[self addCrate];
}
}
- (void)update:(NSTimeInterval)currentTime {
// Handle time delta.
// If we drop below 60fps, we still want everything to move the same distance.
CFTimeInterval timeSinceLast = currentTime - self.lastUpdateTimeInterval;
self.lastUpdateTimeInterval = currentTime;
if (timeSinceLast > 1) { // more than a second since last update
timeSinceLast = 1.0 / 60.0;
self.lastUpdateTimeInterval = currentTime;
}
[self updateWithTimeSinceLastUpdate:timeSinceLast];
}
This is the method that it is calling.
- (void)addCrate {
// Create sprite
self.crate = [SKSpriteNode spriteNodeWithColor:[UIColor redColor] size:CGSizeMake(30, 30)];
//self.crate.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:self.crate.frame.size];
// Determine where to spawn the crate along the X axis
int minX = self.crate.size.width / 2;
int maxX = self.frame.size.width - self.crate.size.width / 2;
int rangeX = maxX - minX;
int actualX = (arc4random_uniform(rangeX)) + minX;
// Create the crate slightly off-screen along the top,
// and along a random position along the X axis as calculated above
self.crate.position = CGPointMake(actualX, self.frame.size.height + self.crate.size.height/2);
[self addChild:self.crate];
self.crate.size = CGSizeMake(50, 50);
// Determine speed of the crate
int actualDuration = 3.5;
// Create the actions
SKAction * actionMove = [SKAction moveTo:CGPointMake(actualX, -self.crate.size.height/2) duration:actualDuration];
SKAction * actionMoveDone = [SKAction removeFromParent];
[self.crate runAction:[SKAction sequence:#[actionMove, actionMoveDone]]];
}
But when I run on my iPhone, only sometimes the tap is registered and the block is removed from the screen, and sometimes it doesn't. Again, I want the node that was tapped on to disappear and only that node.
Thank you!
U1:
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
self.backgroundColor = [SKColor colorWithRed:0.15 green:0.15 blue:0.3 alpha:1.0];
[self addCrate];
}
return self;
}
- (void)addCrate {
// Create sprite
self.crate = [SKSpriteNode spriteNodeWithColor:[UIColor redColor] size:CGSizeMake(30, 30)];
self.crate.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(30, 30)];
self.crate.userInteractionEnabled = YES;
//self.crate.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:self.crate.frame.size];
// Determine where to spawn the crate along the X axis
int minX = self.crate.size.width / 2;
int maxX = self.frame.size.width - self.crate.size.width / 2;
int rangeX = maxX - minX;
int actualX = (arc4random_uniform(rangeX)) + minX;
// Create the crate slightly off-screen along the top,
// and along a random position along the X axis as calculated above
self.crate.position = CGPointMake(actualX, self.frame.size.height + self.crate.size.height/2);
[self addChild:self.crate];
self.crate.size = CGSizeMake(50, 50);
// Determine speed of the crate
int actualDuration = 3.5;
// Create the actions
SKAction * actionMove = [SKAction moveTo:CGPointMake(actualX, -self.crate.size.height/2) duration:actualDuration];
SKAction * actionMoveDone = [SKAction removeFromParent];
[self.crate runAction:[SKAction sequence:#[actionMove, actionMoveDone]]];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint touchLocation = [touch locationInNode:self];
SKNode *touchedNode = [self nodeAtPoint:touchLocation];
NSLog(#"touchLocation x: %f and y: %f", touchLocation.x, touchLocation.y);
if (touchedNode != self) {
NSLog(#"Removed from parent.");
[touchedNode removeFromParent];
}
}
- (void)updateWithTimeSinceLastUpdate:(CFTimeInterval)timeSinceLast {
self.lastSpawnTimeInterval += timeSinceLast;
if (self.lastSpawnTimeInterval > 1) {
self.lastSpawnTimeInterval = 0;
[self addCrate];
}
}
- (void)update:(NSTimeInterval)currentTime {
// Handle time delta.
// If we drop below 60fps, we still want everything to move the same distance.
CFTimeInterval timeSinceLast = currentTime - self.lastUpdateTimeInterval;
self.lastUpdateTimeInterval = currentTime;
if (timeSinceLast > 1) { // more than a second since last update
timeSinceLast = 1.0 / 60.0;
self.lastUpdateTimeInterval = currentTime;
}
[self updateWithTimeSinceLastUpdate:timeSinceLast];
}

I think you should use combination of setting node.name property while creating crates and checking them in touchBegan: method.
Something like this:
SKSpriteNode *crate = [SKSpriteNode spriteNodeWithTexture:tex];
crate.name = #"crate";
And touchBegan: method:
.....
if ([touchedNode.name isEquelToString:#"crate"]){
// do something with that node
}
.....
Upd1:
Instead of writing this stuff:
if ((location.x > self.crate.frame.origin.x && location.x < self.crate.frame.origin.x + self.crate.frame.size.width) &&
(location.y > self.crate.frame.origin.y && location.y < self.crate.frame.origin.y + self.crate.frame.size.height)) {
[self.crate removeFromParent];
}
use:
if(CGRectContainsPoint(self.frame, touchPoint)){
// do something
}
Upd2:
Don't see in your code that you are setting userInteractionEnabled = YES on crate nodes.
Upd3:
Here is an example:
//
// BGMyScene.m
// Test1
//
// Created by AndrewShmig on 3/10/14.
// Copyright (c) 2014 Bleeding Games. All rights reserved.
//
#import "BGMyScene.h"
#implementation BGMyScene
- (id)initWithSize:(CGSize)size
{
if (self = [super initWithSize:size]) {
/* Setup your scene here */
self.backgroundColor = [SKColor colorWithRed:0.15
green:0.15
blue:0.3
alpha:1.0];
// first label
SKLabelNode *myLabel = [SKLabelNode labelNodeWithFontNamed:#"Chalkduster"];
// myLabel.userInteractionEnabled = YES;
myLabel.text = #"Hello, World!";
myLabel.fontSize = 30;
myLabel.position = CGPointMake(CGRectGetMidX(self.frame),
CGRectGetMidY(self.frame));
[self addChild:myLabel];
// second label
SKLabelNode *myLabel2 = [SKLabelNode labelNodeWithFontNamed:#"Chalkduster"];
// myLabel2.userInteractionEnabled = YES;
myLabel2.text = #"Hello, World!";
myLabel2.fontSize = 30;
myLabel2.position = CGPointMake(100, 100);
[self addChild:myLabel2];
}
return self;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint touchLocation = [touch locationInNode:self];
SKNode *touchedNode = [self nodeAtPoint:touchLocation];
NSLog(#"touchLocation x: %f and y: %f", touchLocation.x, touchLocation.y);
if (touchedNode != self) {
NSLog(#"Removed from parent.");
[touchedNode removeFromParent];
}
}
- (void)update:(CFTimeInterval)currentTime
{
/* Called before each frame is rendered */
}
#end
You'll see following screen:
After tapping on "Hello, World!" labels they will be removed from parent node.

Related

didBeginContact entering too early

I am trying to stick a ball to a spinner when they collide at the contact point. However, it seems as though the didBeginContact is being called before the contact starts. Image is below showing them both spinning together but there is a big space.
Code is below:
#import "GameScene.h"
#implementation GameScene
#synthesize _flowIsON;
NSString *const kFlowTypeRed = #"RED_FLOW_PARTICLE";
const float kRED_DELAY_BETWEEN_PARTICLE_DROP = 0.01; //delay for particle drop in seconds
static const uint32_t kRedParticleCategory = 0x1 << 0;
static const uint32_t kSpinnnerCategory = 0x1 << 1;
NSString *const kStartBtn = #"START_BTN";
NSString *const kLever = #"Lever";
NSString *const START_BTN_TEXT = #"Start Game";
CFTimeInterval lastTime;
-(void)didMoveToView:(SKView *)view {
_bkgNode = (SKSpriteNode *)[self.scene childNodeWithName:#"Background"];
[self initializeScene];
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
for (UITouch *touch in touches) {
CGPoint location = [touch locationInNode: self];
SKNode *node = [self nodeAtPoint:location];
if ([node.name isEqualToString:kStartBtn]) {
[node removeFromParent];
//initalize to ON
_flowIsON = YES;
//[self initializeScene];
}
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
}
-(void)update:(CFTimeInterval)currentTime {
float deltaTimeInSeconds = currentTime - lastTime;
//NSLog(#"Time is %f and flow is %d",deltaTimeInSeconds, _flowIsON);
if ((deltaTimeInSeconds > kRED_DELAY_BETWEEN_PARTICLE_DROP)) {
//TBD
SKAction *rotation = [SKAction rotateByAngle: M_PI/8.0 duration:0];
[_spinner runAction:rotation];
//only if its been past 1 second do we set the lasttime to the current time
lastTime = currentTime;
}
}
- (void) initializeScene {
self.physicsWorld.contactDelegate = self;
//create ball
SKSpriteNode *ball = [SKSpriteNode spriteNodeWithImageNamed:#"Ball"];
ball.size = CGSizeMake(50, 50);
ball.position = CGPointMake(320, 1050);
ball.zPosition = 1;
ball.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:25];
ball.physicsBody.restitution = 0.0;
ball.physicsBody.categoryBitMask = kRedParticleCategory;
ball.physicsBody.contactTestBitMask = kSpinnnerCategory;
ball.physicsBody.collisionBitMask = kSpinnnerCategory;
ball.name = #"Ball";
NSLog(#"Ball size is %f",ball.size.width);
[self addChild:ball];
//Create spinner
_spinner = [SKSpriteNode spriteNodeWithImageNamed:#"Spinner"];
_spinner.size = CGSizeMake(300, 300);
_spinner.position = CGPointMake(320, 500);
_spinner.zPosition = 1;
_spinner.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:150];
_spinner.physicsBody.affectedByGravity = NO;
_spinner.physicsBody.allowsRotation = YES;
_spinner.physicsBody.dynamic = NO;
_spinner.physicsBody.restitution = 0.0;
_spinner.physicsBody.categoryBitMask = kSpinnnerCategory;
_spinner.physicsBody.contactTestBitMask = kRedParticleCategory;
_spinner.physicsBody.collisionBitMask = kRedParticleCategory;
_spinner.name = #"Spinner";
[self addChild:_spinner];
//create pipe
// CGPoint center = CGPointMake(400, 600) ;
//
// UIBezierPath *bezierPath = [UIBezierPath bezierPath];
// [bezierPath addArcWithCenter:center radius:400 startAngle:1.825777 endAngle:2.011118 clockwise:YES];
// [bezierPath addLineToPoint:center];
// [bezierPath closePath];
//
// SKShapeNode *shapeNode = [SKShapeNode shapeNodeWithPath:bezierPath.CGPath];
// shapeNode.strokeColor = [UIColor whiteColor];
// shapeNode.fillColor = [UIColor whiteColor];
// [self addChild:shapeNode];
}
# pragma mark -- SKPhysicsContactDelegate Methods
- (void)didBeginContact:(SKPhysicsContact *) contact {
if ([contact.bodyA.node.name isEqualToString:#"Ball"] && [contact.bodyB.node.name isEqualToString:#"Spinner"]) {
[self connectNode1:(SKSpriteNode *)contact.bodyA.node toNode2:(SKSpriteNode *)contact.bodyB.node withContact:contact];
}
}
- (void)didEndContact:(SKPhysicsContact *) contact {
//NSLog(#"didEndContact called");
}
- (void) connectNode1:(SKSpriteNode *)node1 toNode2:(SKSpriteNode *)node2 withContact: (SKPhysicsContact *)contact
{
SKPhysicsJointFixed *joint = [SKPhysicsJointFixed jointWithBodyA:node1.physicsBody
bodyB:node2.physicsBody
anchor:node2.position];
[self.physicsWorld addJoint:joint];
}
#end
If I comment out the did begin contact method you can see the images are correctly sized becuase when they collide they rest on each other perfectly.
How come the contact.contactPoint is not the same as the point at which both bodys are colliding when I comment out the didEnterContact method? Any idea how to fix?
'didBeginContact' is not called too early.. the problem is that actions are evaluated earlier in the scene cycle: first 'didEvaluateActions()' then 'didSimulatePhysics()'. So, even though your SKactions may look correct, there comes a physics evaluation afterwards.. I suggest not to use actions to make rotation corrections when working with the physics engine.. Perhaps use constraints, those come after the physics update..

Can't get the bullet image to spawn at right location

hi guys I have this Sprite Kit cannon game that it works perfectly but with one problem. if I point the cannon to a very top or very bottom as you can see in the image, the bullet since it is spawning from the center of the cannon's sprite, it shows clearly that the bullet is being fired not from the cannon's tunnel but from the center of the image, how can I fix this bullet position so that it would always spawn from the mouth of the tunnel.
here is my code so far.
- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
CGPoint location = [_Player position];
UITouch *touch = [touches anyObject];
CGPoint touchLocation = [touch locationInNode:self.scene];
bullet = [SKSpriteNode spriteNodeWithImageNamed:#"cannonbullet"];
bullet.xScale = 0.06;
bullet.yScale = 0.06;
bullet.position =
CGPointMake(location.x+_Player.zRotation,location.y+_Player.zRotation);
bullet.zPosition = 0;
CGPoint offset = rwSub(touchLocation, bullet.position);
if (offset.x <= 0) return;
[self addChild:bullet];
CGPoint direction = rwNormalize(offset);
CGPoint shootAmount = rwMult(direction, 400);
CGPoint realDest = rwAdd(shootAmount, bullet.position);
float velocity = 480.0/1.0;
float realMoveDuration = self.size.width / velocity;
SKAction * actionMove = [SKAction moveTo:realDest duration:realMoveDuration];
SKAction * actionMoveDone = [SKAction removeFromParent];
[bullet runAction:[SKAction sequence:#[actionMove, actionMoveDone]]];
[self animStarter];
}
as you can see in the image the bullet is spawning from a very off location if the cannon is not shooting straight.
Make sure that your cannon's barrel sprite image is lined up with the center X axis of the image (see the picture). You can drag out or copy the image if you want to duplicate the project.
Below is the code to rotate the cannon to the touch location and fire a cannonball once it has reached the the desired rotation angle. I think you are still using the zombie conga code so I used its functions for your convenience.
#import "MyScene.h"
static inline CGPoint CGPointSubtract(const CGPoint a,
const CGPoint b)
{
return CGPointMake(a.x - b.x, a.y - b.y);
}
static inline CGPoint CGPointMultiplyScalar(const CGPoint a,const CGFloat b)
{
return CGPointMake(a.x * b, a.y * b);
}
static inline CGFloat CGPointLength(const CGPoint a)
{
return sqrtf(a.x * a.x + a.y * a.y);
}
static inline CGPoint CGPointNormalize(const CGPoint a)
{
CGFloat length = CGPointLength(a);
return CGPointMake(a.x / length, a.y / length);
}
static inline CGFloat CGPointToAngle(const CGPoint a)
{
return atan2f(a.y, a.x);
}
static inline CGFloat ScalarSign(CGFloat a)
{
return a >= 0 ? 1 : -1;
}
static inline CGFloat ScalarShortestAngleBetween(const CGFloat a, const CGFloat b)
{
CGFloat difference = b - a;
CGFloat angle = fmodf(difference, M_PI * 2);
if (angle >= M_PI) {
angle -= M_PI * 2;
}
return angle;
}
static const float ROTATE_RADIANS_PER_SEC = 4 * M_PI;
static const float MOVE_POINTS_PER_SEC = 120.0;
#implementation MyScene
{
SKSpriteNode *cannon;
NSTimeInterval _lastUpdateTime;
NSTimeInterval _dt;
CGPoint _velocity;
CGPoint _lastTouchLocation;
BOOL fireCannon;
CGPoint destination;
}
-(id)initWithSize:(CGSize)size
{
if (self = [super initWithSize:size])
{
[self createCannon];
fireCannon = false;
}
return self;
}
-(void)createCannon
{
cannon = [SKSpriteNode spriteNodeWithImageNamed:#"cannon"];
cannon.position = CGPointMake(self.size.height/2, self.size.width/2);
[self addChild:cannon];
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint touchLocation = [touch locationInNode:self.scene];
fireCannon = true;
destination = touchLocation;
_lastTouchLocation = touchLocation;
CGPoint offset = CGPointSubtract(touchLocation, cannon.position);
CGPoint direction = CGPointNormalize(offset);
_velocity = CGPointMultiplyScalar(direction, MOVE_POINTS_PER_SEC);
}
-(void)update:(CFTimeInterval)currentTime
{
if (_lastUpdateTime) {
_dt = currentTime - _lastUpdateTime;
} else {
_dt = 0;
}
_lastUpdateTime = currentTime;
[self rotateSprite:cannon toFace:_velocity rotateRadiansPerSec:ROTATE_RADIANS_PER_SEC];
}
- (void)rotateSprite:(SKSpriteNode *)sprite
toFace:(CGPoint)velocity
rotateRadiansPerSec:(CGFloat)rotateRadiansPerSec
{
float targetAngle = CGPointToAngle(velocity);
float shortest = ScalarShortestAngleBetween(cannon.zRotation, targetAngle);
float amtToRotate = rotateRadiansPerSec * _dt;
if (ABS(shortest) < amtToRotate)
{
amtToRotate = ABS(shortest);
}
sprite.zRotation += ScalarSign(shortest) * amtToRotate;
if ((ABS(shortest) == amtToRotate) && (fireCannon == true))
{
fireCannon = false;
[self fire:targetAngle];
}
}
-(void)fire:(float)targetAngle
{
SKSpriteNode *cannonBall = [SKSpriteNode spriteNodeWithColor:[SKColor redColor] size:CGSizeMake(5, 5)];
cannonBall.position = cannon.position;
[self addChild:cannonBall];
int x = cannon.position.x + 1000 * cos(targetAngle);
int y = cannon.position.y + 1000 * sin(targetAngle);
[cannonBall runAction:[SKAction moveTo:CGPointMake(x, y) duration:2]];
}
#end

Animation in cocos2d crashes

I am having trouble with my game, it was running fine until i tried to add the animation, now whenever i go to shoot it crashes my game and i cant work out what is wrong with my animation code. Below i will put in all the code that causes the ninja star to shoot but as i said i believe the error is in the animation.
ccsprite Projectile is what gets shoot and what im trying to animate
- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
if (StrategyBullet > 0) {
//SOME IF STATEMENTS
if (Strategyscore == 47) {
StrategyBullet = StrategyBullet +5;
}
if (Strategyscore == 97) {
StrategyBullet = StrategyBullet +5;
}
if (Strategyscore == 197) {
StrategyBullet = StrategyBullet +10;
}
// Choose one of the touches to work with
UITouch *touch = [touches anyObject];
CGPoint location = [self convertTouchToNodeSpace:touch];
// Set up initial location of projectile
CGSize winSize = [[CCDirector sharedDirector] winSize];
CCSprite *projectile = [CCSprite spriteWithFile:#"ninja star 1.png"];
[self addChild:projectile z:2];
{
NSString *animationName = #"UNIQUE_ANIMATION_NAME";
CCAnimation* animation = nil;
animation = [[CCAnimationCache sharedAnimationCache] animationByName:animationName];
if(!animation)
{
NSMutableArray *animFrames = [NSMutableArray array];
for( int i=1;i<=5;i++)
{
NSString* path = [NSString stringWithFormat:#"ninja star %d.png", i];
CCTexture2D* tex = [[CCTextureCache sharedTextureCache] addImage:path];
CGSize texSize = tex.contentSize;
CGRect texRect = CGRectMake(0, 0, texSize.width, texSize.height);
CCSpriteFrame* frame = [CCSpriteFrame frameWithTexture:tex rect:texRect];
[animFrames addObject:frame];
}
animation = [CCAnimation animationWithSpriteFrames:animFrames];
animation.delayPerUnit = 0.175f;
animation.restoreOriginalFrame = YES;
[[CCAnimationCache sharedAnimationCache] addAnimation:animation name:animationName];
}
if(animation)
{
CCAnimate *animAction = [CCAnimate actionWithAnimation:animation];
[projectile runAction:animAction];
}
}
projectile.position = ccp(20, winSize.height/2);
// Determine offset of location to projectile
CGPoint offset = ccpSub(location, projectile.position);
// Bail out if you are shooting down or backwards
if (offset.x <= 0) return;
// Ok to add now - we've double checked position
[self addChild:projectile];
int realX = winSize.width + (projectile.contentSize.width/2);
float ratio = (float) offset.y / (float) offset.x;
int realY = (realX * ratio) + projectile.position.y;
CGPoint realDest = ccp(realX, realY);
// Determine the length of how far you're shooting
int offRealX = realX - projectile.position.x;
int offRealY = realY - projectile.position.y;
float length = sqrtf((offRealX*offRealX)+(offRealY*offRealY));
float velocity = 480/1; // 480pixels/1sec
float realMoveDuration = length/velocity;
// collison stuff
projectile.tag = 2;
[_projectiles addObject:projectile];
StrategyBullet --;
[StrategyBulletLabel setString:[NSString stringWithFormat:#"%d", StrategyBullet]];
// Move projectile to actual endpoint
[projectile runAction:
[CCSequence actions:
[CCMoveTo actionWithDuration:realMoveDuration position:realDest],
[CCCallBlockN actionWithBlock:^(CCNode *node) {
[node removeFromParentAndCleanup:YES];
// CCCallBlockN in ccTouchesEnded
[_projectiles removeObject:node];
}],
nil]];
}
}
The crash is because you addChild projectile twice (my best guess). The rest looks ok, although i tend to use sprite sheets for animations as opposed to file-based frames as you do.

Paginate carousel item

I have developed a carousel view that looks like the picture in post below
How do I calculate the position of a view animated from a 3-D carousel?
#pragma mark -
#pragma mark - VIEW LIFE CYCLE
- (void)viewDidLoad
{
[super viewDidLoad];
// VIEW FRAME
CGRect Frame = CGRectMake(-40, -40, 80, 80);
// CREATE 6 VIEWS
_CarouselView = [[NSMutableArray alloc] initWithCapacity:6];
int c = 5;
while(c--)
{
UIView *View = [[UIView alloc] initWithFrame:Frame];
View.backgroundColor = [UIColor colorWithRed:(c&4) ? 1.0 : 0.0 green:(c&2) ? 1.0 : 0.0 blue:(c&1) ? 1.0 : 0.0 alpha:1.0];
[_CarouselView addObject:View];
[self.view addSubview:View];
}
_CurrentAngle = _LastAngle = 0.0f;
[self setCarouselAngle:_CurrentAngle];
}
- (void)viewDidUnload
{
}
#pragma mark -
#pragma mark - VIEW TOUCH GESTURE
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
if(!_TrackingTouch)
{
_TrackingTouch = [touches anyObject];
[_AnimationTimer invalidate];
_AnimationTimer = nil;
_LastAngle = _CurrentAngle;
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
if([touches containsObject:_TrackingTouch])
{
// USE MOVEMENT TO DEVICE HOW MUCH TO ROTATE THE CAROUSEL
CGPoint LocationNow = [_TrackingTouch locationInView:self.view];
CGPoint LocationThen = [_TrackingTouch previousLocationInView:self.view];
_LastAngle = _CurrentAngle;
_CurrentAngle += (LocationNow.x - LocationThen.x) * 180.0f / self.view.bounds.size.width;
// the 180.0f / self.view.bounds.size.width just says "let a full width of my view
// be a 180 degree rotation"
// and update the view positions
[self setCarouselAngle:_CurrentAngle];
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
if([touches containsObject:_TrackingTouch])
{
_TrackingTouch = nil;
_AnimationTimer = [NSTimer scheduledTimerWithTimeInterval:0.04 target:self selector:#selector(animateAngle) userInfo:nil repeats:YES];
}
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
[self touchesEnded:touches withEvent:event];
}
#pragma mark -
#pragma mark - CAROUSEL ACTIONS
- (void)setCarouselAngle:(float)Angle
{
// ANGLE TO ADD AFTER EACH ELEM
_AngleToAdd = 360.0f / [_CarouselView count];
// APPLY TO ALL VIEWS
for(UIView *View in _CarouselView)
{
float AngleInRadians = Angle * M_PI / 180.0f;
// GET LOCATION
float x = (self.view.bounds.size.width * 0.5f) + 100.0f * sinf(AngleInRadians);
float y = ((self.view.bounds.size.height-200) * 0.5f) + 90.0f * cosf(AngleInRadians);
// ADJUST SCALE BETWEEN 0.75 0.25
float Scale = 0.75f + 0.25f * (cosf(AngleInRadians) + 1.0);
// APPLY TRANSFORMATION
View.transform = CGAffineTransformScale(CGAffineTransformMakeTranslation(x, y), Scale, Scale);
// TWEAK ALPHA
View.alpha = 0.1f + 0.5f * (cosf(AngleInRadians) + 1.0);
// SETTING Z POSITION
View.layer.zPosition = Scale;
// GET THE NEXT ANGLE
Angle += _AngleToAdd;
}
}
- (void)animateAngle
{
float AngleNow = _CurrentAngle;
_CurrentAngle += (_CurrentAngle - _LastAngle) * 0.97f;
_LastAngle = AngleNow;
// PUSH THE NEW ANGLE
[self setCarouselAngle:_CurrentAngle];
if(fabsf(_LastAngle - _CurrentAngle) < 0.001)
{
[_AnimationTimer invalidate];
_AnimationTimer = nil;
}
}
Everything works fine but i am having difficulty understanding where to place the pagination code
I hope this code can help you. You only need to create an ViewController and then copy-paste the code.
// CarruselViewController.m
//
// Created by Rosendo Castillo Perez on 2/25/14.
// Copyright (c) 2014 Fundtech. All rights reserved.
//
#import "CarruselViewController.h"
#interface CarruselViewController ()
{
NSMutableArray *_CarouselView;
CGFloat _CurrentAngle,_LastAngle, _AngleToAdd;
UITouch * _TrackingTouch;
NSTimer * _AnimationTimer;
}
#end
#implementation CarruselViewController
#pragma mark -
#pragma mark - VIEW LIFE CYCLE
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:(BOOL)animated];
// VIEW FRAME
CGRect Frame = CGRectMake(-40, -40, 80, 40);
// CREATE 6 VIEWS
_CarouselView = [[NSMutableArray alloc] initWithCapacity:6];
int c = 0;
while(c < 5)
{
UILabel *View = [[UILabel alloc] initWithFrame:Frame];
View.backgroundColor = [UIColor colorWithRed:(c&4) ? 1.0 : 0.0 green:(c&2) ? 1.0 : 0.0 blue:(c&1) ? 1.0 : 0.0 alpha:1.0];
[View setTextAlignment:NSTextAlignmentCenter];
[View setTextColor:[UIColor whiteColor]];
[View setFont:[UIFont boldSystemFontOfSize:14]];
[View setText:[NSString stringWithFormat:#"%d",c]];
[_CarouselView addObject:View];
[self.view addSubview:View];
c++;
}
_CurrentAngle = _LastAngle = 0.0f;
[self setCarouselAngle:_CurrentAngle];
}
#pragma mark -
#pragma mark - VIEW TOUCH GESTURE
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
if(!_TrackingTouch)
{
_TrackingTouch = [touches anyObject];
[_AnimationTimer invalidate];
_AnimationTimer = nil;
_LastAngle = _CurrentAngle;
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
if([touches containsObject:_TrackingTouch])
{
// USE MOVEMENT TO DEVICE HOW MUCH TO ROTATE THE CAROUSEL
CGPoint LocationNow = [_TrackingTouch locationInView:self.view];
CGPoint LocationThen = [_TrackingTouch previousLocationInView:self.view];
_LastAngle = _CurrentAngle;
_CurrentAngle += (LocationNow.x - LocationThen.x) * 180.0f / self.view.bounds.size.width;
// the 180.0f / self.view.bounds.size.width just says "let a full width of my view
// be a 180 degree rotation"
// and update the view positions
[self setCarouselAngle:_CurrentAngle];
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
if([touches containsObject:_TrackingTouch])
{
_TrackingTouch = nil;
_AnimationTimer = [NSTimer scheduledTimerWithTimeInterval:0.04 target:self selector:#selector(animateAngle) userInfo:nil repeats:YES];
}
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
[self touchesEnded:touches withEvent:event];
}
#pragma mark -
#pragma mark - CAROUSEL ACTIONS
- (void)setCarouselAngle:(float)Angle
{
// ANGLE TO ADD AFTER EACH ELEM
_AngleToAdd = 360.0f / [_CarouselView count];
// APPLY TO ALL VIEWS
for(UIView *View in _CarouselView)
{
float AngleInRadians = Angle * M_PI / 180.0f;
// GET LOCATION
float x = (self.view.bounds.size.width * 0.5f) + 100.0f * sinf(AngleInRadians);
float y = ((self.view.bounds.size.height-200) * 0.5f) + 40.0f * cosf(AngleInRadians);
// ADJUST SCALE BETWEEN 0.75 0.25
float Scale = 0.75f + 0.25f * (cosf(AngleInRadians) + 1.0);
// APPLY TRANSFORMATION
View.transform = CGAffineTransformScale(CGAffineTransformMakeTranslation(x, y), Scale, Scale);
// TWEAK ALPHA
View.alpha = 0.1f + 0.5f * (cosf(AngleInRadians) + 1.0);
// SETTING Z POSITION
View.layer.zPosition = Scale;
// GET THE NEXT ANGLE
Angle += _AngleToAdd;
}
}
- (void)animateAngle
{
float AngleNow = _CurrentAngle;
_CurrentAngle += (_CurrentAngle - _LastAngle) * 0.97f;
_LastAngle = AngleNow;
// PUSH THE NEW ANGLE
[self setCarouselAngle:_CurrentAngle];
if(fabsf(_LastAngle - _CurrentAngle) < 0.1)
{
_CurrentAngle = ((int)_CurrentAngle % 360);
if (_CurrentAngle < 0.0)
{
_CurrentAngle += 360.0;
}
[self setCarouselAngle:_CurrentAngle];
int currentAngle = (int) _CurrentAngle;
int angleToAdd = (int) _AngleToAdd;
int residuo = currentAngle % angleToAdd;
if (residuo != 0)
{
forward = (residuo > (angleToAdd / 2));
[_AnimationTimer invalidate];
_AnimationTimer = [NSTimer scheduledTimerWithTimeInterval:0.02 target:self selector:#selector(centerAngle) userInfo:nil repeats:YES];
}
else
{
NSLog(#"angle: %d - index: %d",currentAngle,([_CarouselView count] - ((currentAngle / angleToAdd) % [_CarouselView count])));
}
}
}
BOOL forward = TRUE;
- (void)centerAngle
{
_CurrentAngle += (forward?1.0:-1.0);
[self setCarouselAngle:_CurrentAngle];
int currentAngle = (int) _CurrentAngle;
int angleToAdd = (int) _AngleToAdd;
int residuo = currentAngle % angleToAdd;
if (residuo == 0)
{
_LastAngle = _CurrentAngle = currentAngle;
[self setCarouselAngle:_CurrentAngle];
[_AnimationTimer invalidate];
_AnimationTimer = nil;
NSLog(#"angle: %d - index: %d",currentAngle,([_CarouselView count] - ((currentAngle / angleToAdd) % [_CarouselView count])));
}
}
#end

Level Selector - Cocos2d

I'm trying to add 10 levels and 1 per page, which is 10 pages. How can I use this code to do that? Right now it only has two pages. Can anyone help?
-(id) init
{
if ((self = [super init]))
{
CGSize s = [[CCDirector sharedDirector] winSize];
self.isTouchEnabled = YES;
isDragging = NO;
lastX = 0.0f;
xVel = 0.0f;
contentWidth = s.width * 10.0;
currentPage = 0;
// main scrolling layer - add as child to this page layer.
scrollLayer = [[[LevelScene alloc] init] autorelease];
scrollLayer.anchorPoint = ccp(0, 1);
scrollLayer.position = ccp(0, 0);
[self addChild:scrollLayer];
[self schedule:#selector(moveTick:) interval:0.02f];
}
return self;
}
- (void) moveTick: (ccTime)dt
{
float friction = 0.99f;
CGSize s = [[CCDirector sharedDirector] winSize];
if (!isDragging)
{
// inertia
xVel *= friction;
CGPoint pos = scrollLayer.position;
pos.x += xVel;
// to stop at bounds
pos.x = MAX(-s.width, pos.x);
pos.x = MIN(0, pos.x);
if (pos.x == -s.width)
{
xVel = 0;
currentPage = 1;
}
if (pos.x == 0)
{
xVel = 0;
currentPage = 0;
}
// snap to page by quickly moving to it: e.g.: xVel = 40
if (fabsf(xVel) < 10)
{
if (pos.x < -s.width/2.0)
{
xVel = -40;
}
else {
xVel = 40;
}
}
scrollLayer.position = pos;
}
else {
xVel = (scrollLayer.position.x - lastX)/2.0;
lastX = scrollLayer.position.x;
}
}
- (void) ccTouchesBegan: (NSSet *)touches withEvent: (UIEvent *)event
{
isDragging = YES;
}
- (void) ccTouchesMoved: (NSSet *)touches withEvent: (UIEvent *)event
{
CGSize s = [[CCDirector sharedDirector] winSize];
UITouch *touch = [touches anyObject];
// simple position update
CGPoint a = [[CCDirector sharedDirector] convertToGL:[touch previousLocationInView:touch.view]];
CGPoint b = [[CCDirector sharedDirector] convertToGL:[touch locationInView:touch.view]];
CGPoint nowPosition = scrollLayer.position;
nowPosition.x += (b.x - a.x);
nowPosition.x = MAX(-s.width, nowPosition.x);
nowPosition.x = MIN(0, nowPosition.x);
scrollLayer.position = nowPosition;
}
- (void) ccTouchesEnded: (NSSet *)touches withEvent: (UIEvent *)event
{
isDragging = NO;
}
Any help is greatly appreciated! Thanks!
Jon, it seems like you're trying to recreate a UIScrollView in cocos2d. If that's the case, might I suggest using an existing open source project known as CCScrollLayer (HERE) Out of the box it should do everything you need to do and is pretty easy to extend to meet your needs better.