didBeginContact entering too early - sprite-kit

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..

Related

Detect what instance of SKSpriteNode was touched?

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.

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.

cocos2d: How do I Control a scrolling map

I made my map to scroll automatically however I would like to know how can I slow down the scrolling before the map reaches the end and when it does I would like the map to stop scrolling. I want to know how to do this because my game is about the player trying to shoot the enemy before he reaches the end of the map where there is shelter for him to hide. I would like that before the enemy reaches the shelter have the scrolling slow down and when the enemy reaches the shelter I would like the map to stop scrolling.
Here is the code:
#implementation GameplayScrollingLayer
// Scrolling with TileMap Layers inside of a Parallax Node
-(void)addScrollingBackgroundWithTileMapInsideParallax {
CGSize screenSize = [[CCDirector sharedDirector] winSize];
CGSize levelSize = [[GameManager sharedGameManager]
getDimensionsOfCurrentScene];
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
tileMapNode = [CCTMXTiledMap
tiledMapWithTMXFile:#"Level2TileMap.tmx"];
} else {
tileMapNode = [CCTMXTiledMap
tiledMapWithTMXFile:#"Level2TileMapiPhone.tmx"];
}
CCTMXLayer *groundLayer = [tileMapNode layerNamed:#"GroundLayer"];
CCTMXLayer *rockColumnsLayer = [tileMapNode
layerNamed:#"RockColumnsLayer"];
CCTMXLayer *rockBoulderLayer = [tileMapNode
layerNamed:#"RockBoulderLayer"];
parallaxNode = [CCParallaxNode node];
[parallaxNode setPosition:
ccp(levelSize.width/2,screenSize.height/2)];
float xOffset = 0.0f;
xOffset = (levelSize.width/2);
[groundLayer retain];
[groundLayer removeFromParentAndCleanup:NO];
[groundLayer setAnchorPoint:CGPointMake(0.5f, 0.5f)];
[parallaxNode addChild:groundLayer z:30 parallaxRatio:ccp(1,1)
positionOffset:ccp(0,0)];
[groundLayer release];
xOffset = (levelSize.width/2) * 0.8f;
[rockColumnsLayer retain];
[rockColumnsLayer removeFromParentAndCleanup:NO];
[rockColumnsLayer setAnchorPoint:CGPointMake(0.5f, 0.5f)];
[parallaxNode addChild:rockColumnsLayer z:20
parallaxRatio:ccp(0.2,1)
positionOffset:ccp(xOffset, 0.0f)];
[rockColumnsLayer release];
xOffset = (levelSize.width/2) * 0.3f;
[rockBoulderLayer retain];
[rockBoulderLayer removeFromParentAndCleanup:NO];
[rockBoulderLayer setAnchorPoint:CGPointMake(0.5f, 0.5f)];
[parallaxNode addChild:rockBoulderLayer z:30
parallaxRatio:ccp(0.7,1)
positionOffset:ccp(xOffset, 0.0f)];
[rockBoulderLayer release];
[self addChild:parallaxNode z:1];
}
// Accelerometer
-(void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration { //new
shipSpeedY = 9.0 + acceleration.x*20;
shipSpeedX = -acceleration.y*20;
}
// Updating ship based on accelerometer
-(void)updateShip {
float maxY = winSize.height - ship.contentSize.height/2;
float minY = ship.contentSize.height/2;
float newY = ship.position.y + shipSpeedY;
newY = MIN(MAX(newY, minY), maxY);
float maxX = winSize.width - ship.contentSize.width/2;
float minX = ship.contentSize.width/2;
float newX = ship.position.x + shipSpeedX;
newX = MIN(MAX(newX, minX), maxX);
ship.position = ccp(newX, newY);
}
// Making background scroll automatically
-(void)update:(ccTime)dt {
[self updateShip];
CGPoint backgroundScrollVel = ccp(-100, 0);
parallaxNode.position = ccpAdd(parallaxNode.position, ccpMult(backgroundScrollVel, dt));
}
// Adding sprite ship
-(id)init {
self = [super init];
if (self != nil) {
winSize = [CCDirector sharedDirector].winSize;
ship=[CCSprite spriteWithFile:#"ship.png"];
ship.position=ccp(60,160);
[self addChild:ship z:100];
self.isAccelerometerEnabled = YES; //new
[self scheduleUpdate]; //new
[self addScrollingBackgroundWithTileMapInsideParallax];
}
return self;
}
- (void) dealloc
{
[super dealloc];
}
#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.

Determine if Sprite Rotated 360 Degrees - Cocos2d

I have a sprite that rotates with touch. I need to be able to determine if it has rotated 360 degrees 3 times. Is there any way to tell?
Here is what I have so far
#import <Foundation/Foundation.h>
#import "cocos2d.h"
#import "GameScene.h"
#interface G : CCLayer {
CCSprite *g;
CGFloat gRotation;
}
#end
------------------------------------------
#import "G.h"
#implementation G
-(id) init
{
if ((self = [super init]))
{
CCLOG(#"%#: %#", NSStringFromSelector(_cmd), self);
g = [CCSprite spriteWithFile:#"g.png"];
[self addChild:g z:-1];
}
return self;
}
- (void)update:(ccTime)delta
{
g.rotation = gRotation;
}
- (void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
}
- (void)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint firstLocation = [touch previousLocationInView:[touch view]];
CGPoint location = [touch locationInView:[touch view]];
CGPoint touchingPoint = [[CCDirector sharedDirector] convertToGL:location];
CGPoint firstTouchingPoint = [[CCDirector sharedDirector] convertToGL:firstLocation];
CGPoint firstVector = ccpSub(firstTouchingPoint, g.position);
CGFloat firstRotateAngle = -ccpToAngle(firstVector);
CGFloat previousTouch = CC_RADIANS_TO_DEGREES(firstRotateAngle);
CGPoint vector = ccpSub(touchingPoint, g.position);
CGFloat rotateAngle = -ccpToAngle(vector);
CGFloat currentTouch = CC_RADIANS_TO_DEGREES(rotateAngle);
gRotation += currentTouch - previousTouch;
}
- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
}
- (void) dealloc
{
CCLOG(#"%#: %#", NSStringFromSelector(_cmd), self);
[super dealloc];
}
#end
GameScene
#import "GameScene.h"
#import "MainMenu.h"
#import "G.h"
#implementation GameScene
+(CCScene *) scene
{
CCScene *scene = [CCScene node];
GameScene *layer = [GameScene node];
[scene addChild: layer];
return scene;
}
-(void) tapG: (id) sender
{
G *gView;
gView = [[G alloc] init];
gView.position = ccp(100, 100);
[self.parent addChild:gView z:1001];
[gView scheduleUpdate];
[gView release];
}
-(id) init
{
if ((self = [super init]))
{
tG = [CCMenuItemImage itemFromNormalImage:#"tp.png" selectedImage:#"tp.png" disabledImage:#"tpaperd.png" target:self selector:#selector(tapG:)];
gt = [CCMenu menuWithItems:tG, nil];
gt.position = ccp(210, 80);
[gt alignItemsHorizontallyWithPadding:10];
[self addChild:gt z:0];
}
return self;
}
- (void) dealloc
{
CCLOG(#"%#: %#", NSStringFromSelector(_cmd), self);
[super dealloc];
}
Can anyone help? Thanks in advance
cocos2d can take rotations more than 360. but if your going left and right then its a bit more complicated than just checking if sprite.rotation == 1080. if the rotation is happening on your touchesMoved method then what you should do is that you should record your highest rotation (rotation in right maybe) and lowest rotation (the other way) and then the difference should be bigger than 360*3. so add 2 class vars to your G layer float maxRot,minRot;
- (void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
maxRot = mySprite.rotation; // here you set the ivars to defaults.
minRot = mySprite.rotation; // im setting them to your sprite initial rotation
} // incase it is not 0
at the end of your touchesMoved method you check for your conditions:
if (mySprite.rotation > maxRot)
maxRot = mySprite.rotation;
else if (mysprite.rotation < minRot)
minRot = mySprite.rotation;
if ((maxRot - minRot) >= (360*3)) {
// your condition is satisfied
}
i havent tested this so it could be just wrong.. but its worth a shot
EDIT:
the code above will not work unless the rotations are happening in the same direction.. it wont work for your right, left, right condition. I guess one way is to track the direction of your rotation in touchesMoved. so again youll need class vars
int numOfRots;
float previousRot, currentRot, accumRot;
BOOL isPositive, isPreviousPositive;
your touches methods:
- (void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
previousRot = mySprite.rotation;
currentRot = mySprite.rotation;
accumRot = 0;
numOfRots = 0;
isPositive = NO;
isPreviousPositive = NO;
}
at the end of touchesMoved you will have the following:
currentRot = mySprite.rotation;
if (currentRot > previousRot)
isPositive = YES;
else
isPositive = NO;
if (isPositive != isPreviousPositive) {
// now we have a change in direction, reset the vars
accumRot = 0;
}
if (isPositive) {
accumRot += abs(currentRot - previousRot);
}
else {
accumRot += abs(previousRot - currentRot);
}
if (accumRot >= 360) {
//now we have one rotation in any direction.
numOfRots++;
//need to reset accumRot to check for another rot
accumRot = 0;
if (numOfRots == 3) {
//BINGO!!! now you have 3 full rotations
}
}
previousRot = currentRot;
isPreviousPositive = isPositive;
hope this helps