I want to create pulsing platforms and have some problems with CCScale.
I have some objects of my Platforms class in NSMuttableArray.
#interface Platforms : CCNode{
CCSprite *platformSprite;
int typeP;
}
#property (nonatomic, retain) CCSprite *platformSprite;
#property int typeP;
#implementation
- (id)init:(int)type
{
self = [super init];
if (self != nil) {
typeP = type;
platformSprite = [CCSprite spriteWithFile:#"ball.png" rect:CGRectMake(0, 0, 51, 52)];
[self addChild:platformSprite];
}
return self;
}
I added all objects to screen
for (int i = 0 ; i < [_array count]; i++) {
Platforms* _platforms2 = [[_array objectAtIndex:i ]retain];
[self addChild:_platforms2];
[_platforms2 release];
}
So ... i want to get all objects where typeP == 1
i get all this objects and add them to NSMuttableArray
for (int i = 0 ; i < [platformsArrayOnScreen count]; i++) {
Platforms* temp = [platformsArrayOnScreen objectAtIndex:i];
if (temp.typeP == 1) {
[scaleArray addObject:temp];
}
Than i want to scale all of them to [CCScaleTo actionWithDuration:0.5 scale:0.2f];
and after that scale to [CCScaleTo actionWithDuration:0.5 scale:1.0f];
i created function [self schedule:#selector(checkForScale:)];
-(void)checkForScale:(ccTime)dt {
for (int i = 0; i < [scaleArray count]; i++) {
Platforms* temp2 = [scaleArray objectAtIndex:i];
if ([temp2.platformSprite scale] == 1.0f) {
[self scaleDown:temp2];
}
if([temp2.platformSprite scale] == 0.2f){
[self scaleUp:temp2];
}
}
}
-(void)scaleDown:(Platforms *)tempSprite {
id scaleDown = [CCScaleTo actionWithDuration:0.3 scaleX:0.2f scaleY:0.2f];
[tempSprite.platformSprite runAction:scaleDown];
}
-(void)scaleUp:(Platforms *)tempSprite {
id scaleUp = [CCScaleTo actionWithDuration:0.3 scaleX:1.0f scaleY:1.0f];
[tempSprite runAction:scaleUp];
}
But it's don't work (( my platforms don't pulsing. Where is my problem?
Thanks.
if you want to do this you can CCSequence.
[tempSprite.platformSprite runAction:[CCSequence actions:[CCScaleTo actionWithDuration:3.0f scale:0.2f],[CCScaleTo actionWithDuration:3.0f scale:1.0f], nil]];
Need not to do all this :
-(void)checkForScale:(ccTime)dt
-(void)scaleDown:(Platforms *)tempSprite
-(void)scaleUp:(Platforms *)tempSprite
Related
I'm looking for some guidance on how, using Cocos2d, I can have an object (let's say a rocket) that is in constant motion at a constant speed, and I have two buttons to simply change its direction.
I've found a few pieces of information on how to change the orientation of the rocket, however I'm stuck on just having the rocket be in constant motion.
I'll later want to make the rocket be able to fly off the screen and re-appear on the other side of the screen, but that's for another question (I've found some help on this already).
I'm new to Cocos2d and have searched for a few hours, but haven't been able to find what I'm after.
Could somebody point me in the right direction in being able to get my rocket constantly moving, and responding to changes in direction?
So, here's a first pass at how you could do something like that:
// ProjectileTest holds a projectile class that simply knows how to
move itself
// WrapBoundaryTest is the example scene showing potential usage of
ProjectileTest
ProjectileTest.h
#import "cocos2d.h"
#interface ProjectileTest : CCNode
{
CCSprite *sprite;
CGPoint velocity;
CGRect bounds;
BOOL isActive;
float steerAmount;
}
#property CCSprite *sprite;
#property CGPoint velocity;
#property CGRect bounds;
#property BOOL isActive;
#property float steerAmount;
+(id) projectileWithBounds:(CGRect) boundary
andVelocity:(CGPoint) v
andSteeringAmount:(float) steeringAmount;
-(void) step:(CCTime) dt;
-(void) steerLeft;
-(void) steerRight;
#end
ProjectileTest.m
#import "ProjectileTest.h"
#implementation ProjectileTest
#synthesize sprite, velocity, bounds, isActive, steerAmount;
+(id) projectileWithBounds:(CGRect) boundary andVelocity:(CGPoint) v andSteeringAmount:(float) steeringAmount
{
ProjectileTest *proj = [[self alloc] init];
proj.bounds = boundary;
proj.velocity = v;
proj.steerAmount = steeringAmount;
proj.isActive = YES;
[proj calculateAngleForVelocity: v];
return proj;
}
-(id) init
{
if(( self = [super init]))
{
sprite = [CCSprite spriteWithImageNamed:#"Icon.png"];
[self addChild:sprite];
[sprite setScale:0.50f];
bounds = CGRectZero;
velocity = CGPointZero;
isActive = NO;
steerAmount = 1.0f;
}
return self;
}
-(void) calculateAngleForVelocity:(CGPoint) v
{
float rads = ccpToAngle(v);
float degs = -CC_RADIANS_TO_DEGREES(rads);
sprite.rotation = degs + 90.0f;
}
-(void) steerLeft
{
[self steer: YES];
}
-(void) steerRight
{
[self steer: NO];
}
-(void) steer:(BOOL) left
{
if(left)
{
velocity = ccpRotateByAngle(velocity, CGPointZero, -CC_DEGREES_TO_RADIANS(-steerAmount));
}
else // right
{
velocity = ccpRotateByAngle(velocity, CGPointZero, -CC_DEGREES_TO_RADIANS(steerAmount));
}
}
-(void) step:(CCTime)dt
{
if(isActive)
{
if(CGRectContainsPoint(bounds, self.position))
{
self.position = ccpAdd(self.position, velocity);
[self calculateAngleForVelocity: velocity];
}
else
{
float nudge = 0.5f;
if(self.position.x >= bounds.size.width && velocity.x > 0.0f)
{
self.position = ccp(bounds.origin.x + nudge, self.position.y);
}
else if(self.position.x <= bounds.origin.x && velocity.x < 0.0f)
{
self.position = ccp(bounds.size.width - nudge, self.position.y);
}
if(self.position.y >= bounds.size.height && velocity.y > 0.0f)
{
self.position = ccp(self.position.x, bounds.origin.y + nudge);
}
else if(self.position.y <= bounds.origin.y && velocity.y < 0.0f)
{
self.position = ccp(self.position.x, bounds.size.height - nudge);
}
}
}
}
#end
WrapBoundaryTest.h
#import "cocos2d.h"
#import "cocos2d-ui.h"
#interface WrapBoundaryTest : CCScene
{
NSMutableArray *projectiles;
CCButton *left;
CCButton *right;
}
#property __strong NSMutableArray *projectiles;
+(id) scene;
#end
WrapBoundaryTest.m
#import "WrapBoundaryTest.h"
#import "ProjectileTest.h"
#implementation WrapBoundaryTest
#synthesize projectiles;
+(id) scene
{
return [[self alloc] init];
}
-(id) init
{
if(( self = [super init]))
{
projectiles = [NSMutableArray array];
CGRect projectileBounds = CGRectMake(
0.0f,
0.0f,
[CCDirector sharedDirector].designSize.width,
[CCDirector sharedDirector].designSize.height
);
CGPoint origin = ccp(200.0f, 150.0f);
[self addProjectileWithBoundsRect:projectileBounds andVelocity:ccp( 1.0f, 5.0f) andSteeringAmount:2.0f atStartingPosition:origin];
[self addProjectileWithBoundsRect:projectileBounds andVelocity:ccp(-1.0f, -5.0f) andSteeringAmount:1.0f atStartingPosition:origin];
[self addProjectileWithBoundsRect:projectileBounds andVelocity:ccp(-1.0f, 1.0f) andSteeringAmount:0.5f atStartingPosition:origin];
[self addProjectileWithBoundsRect:projectileBounds andVelocity:ccp( 8.0f, 1.0f) andSteeringAmount:7.5f atStartingPosition:origin];
left = [CCButton buttonWithTitle:#"Left" fontName:#"Arial" fontSize:16.0f];
left.positionType = CCPositionTypeNormalized;
left.position = ccp(0.1f, 0.1f);
[self addChild:left];
right = [CCButton buttonWithTitle:#"Right" fontName:#"Arial" fontSize:16.0f];
right.positionType = CCPositionTypeNormalized;
right.position = ccp(0.9f, 0.1f);
[self addChild:right];
}
return self;
}
-(void) addProjectileWithBoundsRect:(CGRect) boundsRect
andVelocity:(CGPoint) velocity
andSteeringAmount:(float) steeringAmount
atStartingPosition:(CGPoint) startingPosition
{
ProjectileTest *proj = [ProjectileTest projectileWithBounds: boundsRect
andVelocity: velocity
andSteeringAmount: steeringAmount
];
proj.position = startingPosition;
[self addChild:proj];
[projectiles addObject:proj];
}
-(void) update:(CCTime)delta
{
for(ProjectileTest *p in projectiles)
{
if(left.touchInside)
{
[p steerLeft];
}
if(right.touchInside)
{
[p steerRight];
}
[p step:delta];
}
}
#end
// HTH
I'm using one plugin for scrollable layer. It has two files as shown below.
FGScrollLayer.h
#import <Foundation/Foundation.h>
#import "cocos2d.h"
#class FGScrollLayer;
#protocol FGScrollLayerDelegate
#optional
/** Called when scroll layer begins scrolling.
* Usefull to cancel CCTouchDispatcher standardDelegates.
*/
- (void) scrollLayerScrollingStarted:(FGScrollLayer *) sender;
/** Called at the end of moveToPage:
*/
- (void) scrollLayer: (FGScrollLayer *) sender scrolledToPageNumber: (int) page;
#end
/** Vertical scrolling layer for items.
*
* It is a very clean and elegant subclass of CCLayer that lets you pass-in an array
* of layers and it will then create a smooth scroller.
* Every sub-layer should have the same size in current version.
*
* #version 0.1.01
*/
#interface FGScrollLayer : CCLayer
{
NSObject <FGScrollLayerDelegate> *delegate_;
// The screen coord of initial point the user starts their swipe.
CGFloat startSwipe_;
// The coord of initial position the user starts their swipe.
CGFloat startSwipeLayerPos_;
// For what distance user must slide finger to start scrolling menu.
CGFloat minimumTouchLengthToSlide_;
// Internal state of scrollLayer (scrolling or idle).
int state_;
BOOL stealTouches_;
#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
// Holds the touch that started the scroll
UITouch *scrollTouch_;
#endif
// Holds pages.
NSMutableArray *layers_;
// Holds current pages width offset.
CGFloat pagesOffset_;
// Holds the height of every page
CGFloat pageHeight_;
// Holds the width of every page
CGFloat pageWidth_;
// Holds the maximum upper position
CGFloat maxVerticalPos_;
// Holds the real responsible rect in the screen
CGRect realBound;
/*Decoration and slide bars*/
// Scroll bars on the right
CCSprite* scrollBar;
CGFloat scrollBarPosY;
// Scroll block that indicates the current position in whole scorll view content
CCSprite* scrollBlock;
CGFloat scrollBlockUpperBound;
CGFloat scrollBlockLowerBound;
// Decoration
// Holds position to maintain their position fixed even in setPosition
CCSprite* upperBound;
CGFloat upperBoundPosY;
CCSprite* lowerBound;
CGFloat lowerBoundPosY;
}
#property (readwrite, assign) NSObject <FGScrollLayerDelegate> *delegate;
#pragma mark Scroll Config Properties
/** Calibration property. Minimum moving touch length that is enough
* to cancel menu items and start scrolling a layer.
*/
#property(readwrite, assign) CGFloat minimumTouchLengthToSlide;
/** If YES - when starting scrolling FGScrollLayer will claim touches, that are
* already claimed by others targetedTouchDelegates by calling CCTouchDispatcher#touchesCancelled
* Usefull to have ability to scroll with touch above menus in pages.
* If NO - scrolling will start, but no touches will be cancelled.
* Default is YES.
*/
#property(readwrite) BOOL stealTouches;
#pragma mark Pages Control Properties
/** Offset, that can be used to let user see next/previous page. */
#property(readwrite) CGFloat pagesOffset;
/** Page height, this version requires that each page shares the same height and width */
#property(readonly) CGFloat pageHeight;
#property(readonly) CGFloat pageWidth;
/** Returns array of pages CCLayer's */
#property(readonly) NSArray *pages;
- (void) updatePages;
-(void)updatePagesAvailability;
#pragma mark Init/Creation
/** Creates new scrollLayer with given pages & width offset.
* #param layers NSArray of CCLayers, that will be used as pages.
* #param pageSize indicates the size of every page, now this version requires each page
* share the same page size
* #param widthOffset Length in X-coord, that describes length of possible pages
* #param visibleRect indicates the real position and size on the screen
* intersection. */
+(id) nodeWithLayers:(NSArray *)layers pageSize:(CGSize)pageSize pagesOffset: (int) pOffset visibleRect: (CGRect)rect;
/** Inits scrollLayer with given pages & width offset.
* #param layers NSArray of CCLayers, that will be used as pages.
* #param pageSize indicates the size of every page, now this version requires each page
* share the same page size
* #param pagesOffset Length in X-coord, that describes length of possible pages
* #param visibleRect indicates the real position and size on the screen
* intersection. */
-(id) initWithLayers:(NSArray *)layers pageSize:(CGSize)pageSize pagesOffset: (int) pOffset visibleRect: (CGRect)rect;
#pragma mark Misc
/**
* Return the number of pages
*/
-(int) totalPagesCount;
#pragma mark Moving/Selecting Pages
/* Moves scrollLayer to page with given number.
* Does nothing if number >= totalScreens or < 0.
*/
-(void) moveToPage:(int)page;
#end
FGScrollLayer.m
//
// FGScrollLayer.m
// Fall G
//
// Created by Dai Xuefeng on 23/9/12.
// Copyright 2012 Nofootbird.
//
#import "FGScrollLayer.h"
enum
{
kFGScrollLayerStateIdle,
kFGScrollLayerStateSliding,
};
#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
#interface CCTouchDispatcher (targetedHandlersGetter)
- (id<NSFastEnumeration>) targetedHandlers;
#end
#implementation CCTouchDispatcher (targetedHandlersGetter)
- (id<NSFastEnumeration>) targetedHandlers
{
return targetedHandlers;
}
#end
#endif
#implementation FGScrollLayer
#synthesize delegate = delegate_;
#synthesize minimumTouchLengthToSlide = minimumTouchLengthToSlide_;
#synthesize pagesOffset = pagesOffset_;
#synthesize pages = layers_;
#synthesize stealTouches = stealTouches_;
#synthesize pageHeight = pageHeight_;
#synthesize pageWidth = pageWidth_;
- (int) totalPagesCount
{
return [layers_ count];
}
+(id) nodeWithLayers:(NSArray *)layers pageSize:(CGSize)pageSize pagesOffset:(int)pOffset visibleRect:(CGRect)rect{
return [[[self alloc] initWithLayers: layers pageSize:pageSize pagesOffset:pOffset visibleRect:rect] autorelease];
}
-(id) initWithLayers:(NSArray *)layers pageSize:(CGSize)pageSize pagesOffset:(int)pOffset visibleRect:(CGRect)rect{
if ( (self = [super init]) )
{
NSAssert([layers count], #"FGScrollLayer#initWithLayers:widthOffset: you must provide at least one layer!");
// Enable Touches/Mouse.
#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
self.isTouchEnabled = YES;
#endif
self.stealTouches = YES;
// Set default minimum touch length to scroll.
self.minimumTouchLengthToSlide = 30.0f;
// Save offset.
self.pagesOffset = pOffset;
// Save array of layers.
layers_ = [[NSMutableArray alloc] initWithArray:layers copyItems:NO];
// Save pages size for later calculation
pageHeight_ = pageSize.height;
pageWidth_ = pageSize.width;
maxVerticalPos_ = pageHeight_ * [layers_ count] - rect.size.height + 5;
realBound = rect;
[self updatePages];
}
return self;
}
- (void) dealloc
{
self.delegate = nil;
[layers_ release];
layers_ = nil;
[super dealloc];
}
- (void) updatePages
{
// Loop through the array and add the screens if needed.
int i = 0;
for (CCLayer *l in layers_)
{
l.position = ccp(realBound.origin.x, realBound.origin.y + (realBound.size.height - i * (pageHeight_ - self.pagesOffset)));
if (!l.parent){
[self addChild:l];
}
i++;
}
[self updatePagesAvailability];
}
/**
* According to current position, decide which pages are visible
*/
-(void)updatePagesAvailability{
CGPoint currentPos = [self position];
if (currentPos.y > 0) {
int visibleBoundUp = currentPos.y / pageHeight_;
visibleBoundUp = MIN([layers_ count], visibleBoundUp);
for (int i = 0; i < visibleBoundUp; i++) {
[[layers_ objectAtIndex:i] setVisible:NO];
}
if (visibleBoundUp < [layers_ count]) {
int visibleBoundDown = (currentPos.y + realBound.size.height) / pageHeight_;
visibleBoundDown = MIN([layers_ count] - 1, visibleBoundDown);
for (int i = visibleBoundUp; i <= visibleBoundDown; i++) {
[[layers_ objectAtIndex:i] setVisible:YES];
}
if (visibleBoundDown < [layers_ count] - 1) {
for (int i = visibleBoundDown + 1; i <= [layers_ count] - 1; i++) {
[[layers_ objectAtIndex:i] setVisible:NO];
}
}
}
}
else if (currentPos.y <= 0){
CGFloat gapY = -currentPos.y;
int visibleBound = (realBound.size.height - gapY) / pageHeight_;
// index visibleBound itself should be invisible
if (visibleBound < 0) {
for (int i = 0; i < [layers_ count]; i++) {
[[layers_ objectAtIndex:i] setVisible:NO];
}
return;
}
visibleBound = MIN([layers_ count] - 1, visibleBound);
for (int i = 0; i <= visibleBound; i++) {
[[layers_ objectAtIndex:i] setVisible:YES];
}
for (int i = visibleBound + 1; i < [layers_ count]; i++) {
[[layers_ objectAtIndex:i] setVisible:NO];
}
}
}
-(void)setRealBound:(CGPoint)position size:(CGPoint)size{
realBound = CGRectMake(position.x, position.y, size.x, size.y);
}
-(void)setPosition:(CGPoint)position{
[super setPosition:position];
[self updatePagesAvailability];
CGFloat scrollBlockDesiredY = scrollBlockUpperBound - (scrollBlockUpperBound - scrollBlockLowerBound) * position.y / maxVerticalPos_;
if (scrollBlockDesiredY > scrollBlockUpperBound) {
scrollBlockDesiredY = scrollBlockUpperBound;
}else if (scrollBlockDesiredY < scrollBlockLowerBound){
scrollBlockDesiredY = scrollBlockLowerBound;
}
[scrollBlock setPosition:ccp([scrollBlock position].x, scrollBlockDesiredY - position.y)];
[lowerBound setPosition:ccp([lowerBound position].x, lowerBoundPosY - position.y)];
[upperBound setPosition:ccp([upperBound position].x, upperBoundPosY - position.y)];
[scrollBar setPosition:ccp([scrollBar position].x, scrollBarPosY - position.y)];
}
#pragma mark Moving To / Selecting Pages
-(void) moveToPage:(int)page
{
if (page < 0 || page >= [layers_ count]) {
CCLOGERROR(#"FGScrollLayer#moveToPage: %d - wrong page number, out of bounds. ", page);
return;
}
CGFloat desiredPos = page * pageHeight_;
if (desiredPos > maxVerticalPos_) {
desiredPos = maxVerticalPos_;
}
[self runAction:[CCMoveTo actionWithDuration:0.3 position:ccp([self position].x, desiredPos)]];
}
#pragma mark Touches
#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
/** Register with more priority than CCMenu's but don't swallow touches. */
-(void) registerWithTouchDispatcher
{
#if COCOS2D_VERSION >= 0x00020000
CCTouchDispatcher *dispatcher = [[CCDirector sharedDirector] touchDispatcher];
int priority = kCCMenuHandlerPriority - 1;
#else
CCTouchDispatcher *dispatcher = [CCTouchDispatcher sharedDispatcher];
int priority = kCCMenuTouchPriority - 1;
#endif
[dispatcher addTargetedDelegate:self priority: priority swallowsTouches:NO];
}
/** Hackish stuff - stole touches from other CCTouchDispatcher targeted delegates.
Used to claim touch without receiving ccTouchBegan. */
- (void) claimTouch: (UITouch *) aTouch
{
#if COCOS2D_VERSION >= 0x00020000
CCTouchDispatcher *dispatcher = [[CCDirector sharedDirector] touchDispatcher];
#else
CCTouchDispatcher *dispatcher = [CCTouchDispatcher sharedDispatcher];
#endif
// Enumerate through all targeted handlers.
for ( CCTargetedTouchHandler *handler in [dispatcher targetedHandlers] )
{
// Only our handler should claim the touch.
if (handler.delegate == self)
{
if (![handler.claimedTouches containsObject: aTouch])
{
[handler.claimedTouches addObject: aTouch];
}
}
else
{
// Steal touch from other targeted delegates, if they claimed it.
if ([handler.claimedTouches containsObject: aTouch])
{
if ([handler.delegate respondsToSelector:#selector(ccTouchCancelled:withEvent:)])
{
[handler.delegate ccTouchCancelled: aTouch withEvent: nil];
}
[handler.claimedTouches removeObject: aTouch];
}
}
}
}
-(void)ccTouchCancelled:(UITouch *)touch withEvent:(UIEvent *)event
{
[scrollBar setVisible:NO];
[scrollBlock setVisible:NO];
if( scrollTouch_ == touch ) {
scrollTouch_ = nil;
}
}
// these two variables are to make a sliding effect on scroll view
static CGFloat previousTouchPointY = -1;
static CGFloat moveSpeed = 0;
-(BOOL) ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event
{
if( scrollTouch_ == nil ) {
scrollTouch_ = touch;
} else {
return NO;
}
CGPoint touchPoint = [touch locationInView:[touch view]];
touchPoint = [[CCDirector sharedDirector] convertToGL:touchPoint];
startSwipe_ = touchPoint.y;
startSwipeLayerPos_ = [self position].y;
state_ = kFGScrollLayerStateIdle;
return YES;
}
- (void)ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event
{
if( scrollTouch_ != touch ) {
return;
}
CGPoint touchPoint = [touch locationInView:[touch view]];
touchPoint = [[CCDirector sharedDirector] convertToGL:touchPoint];
// If finger is dragged for more distance then minimum - start sliding and cancel pressed buttons.
// Of course only if we not already in sliding mode
if ( (state_ != kFGScrollLayerStateSliding)
&& (fabsf(touchPoint.y-startSwipe_) >= self.minimumTouchLengthToSlide) )
{
state_ = kFGScrollLayerStateSliding;
// Avoid jerk after state change.
startSwipe_ = touchPoint.y;
startSwipeLayerPos_ = [self position].y;
previousTouchPointY = touchPoint.y;
if (self.stealTouches)
{
[self claimTouch: touch];
}
if ([self.delegate respondsToSelector:#selector(scrollLayerScrollingStarted:)])
{
[self.delegate scrollLayerScrollingStarted: self];
}
}
if (state_ == kFGScrollLayerStateSliding)
{
CGFloat desiredY = startSwipeLayerPos_ + touchPoint.y - startSwipe_;
[self setPosition:ccp(0, desiredY)];
// enable scroll bar to be visible
[scrollBar setVisible:YES];
[scrollBlock setVisible:YES];
// update scrolling effect variables
moveSpeed = touchPoint.y - previousTouchPointY;
previousTouchPointY = touchPoint.y;
}
}
/**
* After touching, generate an inertia effect.
*/
- (void)moveToDesiredPos:(CGFloat)desiredY{
CCAction* slidingAction = nil;
if (desiredY > maxVerticalPos_) {
slidingAction = [CCSequence actions:[CCMoveTo actionWithDuration:0.10 position:ccp([self position].x, desiredY)], [CCMoveTo actionWithDuration:0.15 position:ccp([self position].x, maxVerticalPos_)], nil];
}
else if (desiredY < 0){
slidingAction = [CCSequence actions:[CCMoveTo actionWithDuration:0.10 position:ccp([self position].x, desiredY)],[CCMoveTo actionWithDuration:0.15 position:ccp([self position].x, 0)], nil];
}
else{
CGFloat interPosY = (desiredY - [self position].y) * 0.7 + [self position].y;
slidingAction = [CCSequence actions:[CCMoveTo actionWithDuration:0.15 position:ccp([self position].x, interPosY)],[CCMoveTo actionWithDuration:0.3 position:ccp([self position].x, desiredY)], nil];
}
[self runAction:slidingAction];
}
- (void)ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event
{
[scrollBar setVisible:NO];
[scrollBlock setVisible:NO];
if( scrollTouch_ != touch )
return;
scrollTouch_ = nil;
if (ABS(moveSpeed) > 10) {
CGFloat desiredDesY = [self position].y + moveSpeed * 5;
[self moveToDesiredPos:desiredDesY];
}
else{
if ([self position].y > maxVerticalPos_) {
[self runAction:[CCMoveTo actionWithDuration:0.3 position:ccp([self position].x, maxVerticalPos_)]];
}else if ([self position].y < 0){
[self runAction:[CCMoveTo actionWithDuration:0.3 position:ccp([self position].x, 0)]];
}
}
// restore scrolling effect variables to default value
moveSpeed = 0;
previousTouchPointY = -1;
}
#endif
#end
Now in my HelloWorldLayer.m init i do like Following:
-(id) init
{
if( (self=[super init]) ) {
NSMutableArray *persons = [NSMutableArray array];
for (int i = 0; i < 10; i++) {
[persons addObject:[NSString stringWithFormat:#"%d",i]];
}
NSArray *arrayOfPersons = [NSArray arrayWithArray:persons];
//scrollNode.position = ccp(0, 0);
scrollNode = [FGScrollLayer nodeWithLayers:arrayOfPersons pageSize:CGSizeMake(100, 20) pagesOffset:1 visibleRect:CGRectMake(30, 30, 500, 900)];
}
return self;
}
But it is giving error:-[__NSCFString setPosition:]: unrecognized selector sent to instance 0x960e170
I searched for this error and i came to know that it is Fundamental Subclassing error.,
Can anybody guide me to solve this issue?
You create an array of persons. They're all NSString objects. You pass that array (after needlessly copying it) into the scroll layer class which expects an array of CCLayer objects.
I need to create an outline like this dynamically:
Not for a CCSprite, but for multiple animated CCSprites united in one CCNode. I'm thinking about:
copying CCNode's content to a texture (like canvasBitmapData.draw(sourceDisplayObject) in AS3)
creating CCSprite with the resulting texture
tinting the sprite to outline color and scaling it up a bit
placing the sprite behind other sprites in the node
I have no idea how to perform step 1. And maybe it is faster to draw "true stroke" around the texture's opaque pixels instead of tint-scale in step 3?
I totally forgot to post an answer for this question. Here's the code for a very smooth stroke. It's not fast but worked great for a couple of big sprites on the first iPad.
The idea is to draw tiny colored and blurred balls around the sprite and place them onto their own texture. It can be used both for CCNode and CCSprite. The code also shifts anchor points because the resulting sprites will have a bit larger width and height.
Resulting outline (body and 2 hands, about 0.3s on iPad1):
White balls examples:
5f: http://i.stack.imgur.com/e9kos.png
10f: http://i.stack.imgur.com/S5goU.png
20f: http://i.stack.imgur.com/qk7GL.png
CCNode category, for Cocos2d-iPhone 2.1:
#implementation CCNode (Outline)
- (CCSprite*) outline
{
return [self outlineRect:CGRectMake(0, 0, self.contentSize.width, self.contentSize.height)];
}
- (CCSprite*) outlineRect:(CGRect)rect
{
NSInteger gap = dscale(4);
CGPoint positionShift = ccp(gap - rect.origin.x, gap - rect.origin.y);
CGSize canvasSize = CGSizeMake(rect.size.width + gap * 2, rect.size.height + gap * 2);
CCRenderTexture* renderedSpriteTexture = [self renderTextureFrom:self shiftedFor:positionShift onCanvasSized:canvasSize];
CGSize textureSize = renderedSpriteTexture.sprite.contentSize;
CGSize textureSizeInPixels = renderedSpriteTexture.sprite.texture.contentSizeInPixels;
NSInteger bitsPerComponent = 8;
NSInteger bytesPerPixel = (bitsPerComponent * 4) / 8;
NSInteger bytesPerRow = bytesPerPixel * textureSizeInPixels.width;
NSInteger myDataLength = bytesPerRow * textureSizeInPixels.height;
NSMutableData* buffer = [[NSMutableData alloc] initWithCapacity:myDataLength];
Byte* bytes = (Byte*)[buffer mutableBytes];
[renderedSpriteTexture begin];
glReadPixels(0, 0, textureSizeInPixels.width, textureSizeInPixels.height, GL_RGBA, GL_UNSIGNED_BYTE, bytes);
[renderedSpriteTexture end];
//SEE ATTACHMENT TO GET THE FILES
NSString* spriteFrameName;
if (IS_IPAD) spriteFrameName = (CC_CONTENT_SCALE_FACTOR() == 1) ? #"10f.png" : #"20f.png";
else spriteFrameName = (CC_CONTENT_SCALE_FACTOR() == 1) ? #"5f.png" : #"10f.png";
CCSprite* circle = [CCSprite spriteWithSpriteFrameName:spriteFrameName];
circle.anchorPoint = ccp(0.48, 0.48);
float retinaScale = (CC_CONTENT_SCALE_FACTOR() == 1) ? 1.0 : 0.5;
CCRenderTexture* strokeTexture = [CCRenderTexture renderTextureWithWidth:textureSize.width height:textureSize.height pixelFormat:kCCTexture2DPixelFormat_RGBA8888];
[strokeTexture beginWithClear:0 g:0 b:0 a:0];
for (NSInteger x = 0; x < textureSizeInPixels.width; x++)
{
for (NSInteger y = 0; y < textureSizeInPixels.height; y++)
{
NSInteger idx = y * bytesPerRow + x * bytesPerPixel + 3;
NSInteger w = 1;
if (bytes[idx] <= 254)
{
BOOL shouldBeStroked = NO;
for (NSInteger nx = -w; nx <= w; nx++)
{
for (NSInteger ny = -w; ny <= w; ny++)
{
if (x + nx < 0 || y + ny < 0 || x + nx >= textureSizeInPixels.width || y + ny >= textureSizeInPixels.height)
continue;
if (bytes[idx + nx * bytesPerPixel + ny * bytesPerRow] == 255)
{
shouldBeStroked = YES;
break;
}
}
}
if (shouldBeStroked == YES)
{
circle.position = ccp(x * retinaScale, y * retinaScale);
[circle visit];
}
}
}
}
[strokeTexture end];
CCSprite* resultSprite = [CCSprite spriteWithTexture:strokeTexture.sprite.texture];
[resultSprite.texture setAntiAliasTexParameters];
resultSprite.flipY = YES;
if ([self isKindOfClass:[CCSprite class]]) {
CGPoint oldAnchorInPixels = ccp(roundf(self.contentSize.width * self.anchorPoint.x), roundf(self.contentSize.height * self.anchorPoint.y));
resultSprite.anchorPoint = ccp((oldAnchorInPixels.x + gap) / resultSprite.contentSize.width, (oldAnchorInPixels.y + gap) / resultSprite.contentSize.height);
resultSprite.position = self.position;
} else { //CCNode
resultSprite.anchorPoint = CGPointZero;
resultSprite.position = ccpAdd(self.position, ccp(rect.origin.x - gap, rect.origin.y - gap));
}
return resultSprite;
}
- (CCRenderTexture*) renderTextureFrom:(CCNode*)node shiftedFor:(CGPoint)posShift onCanvasSized:(CGSize)size
{
SoftAssertion(!CGSizeEqualToSize(size, CGSizeZero), #"node has zero size");
BOOL isSprite = [node isMemberOfClass:[CCSprite class]];
CGPoint apSave = node.anchorPoint;
CGPoint posSave = node.position;
BOOL wasVisible = node.visible;
CCRenderTexture* rtx = [CCRenderTexture renderTextureWithWidth:size.width
height:size.height
pixelFormat:kCCTexture2DPixelFormat_RGBA8888];
[rtx beginWithClear:0 g:0 b:0 a:0];
node.anchorPoint = CGPointZero;
node.position = posShift;
node.visible = YES;
if (isSprite) [node visit];
else [[self cloneCCNode:node] visit];
node.anchorPoint = apSave;
node.position = posSave;
node.visible = wasVisible;
[rtx end];
return rtx;
}
- (CCNode*) cloneCCNode:(CCNode*)source
{
CCNode* clone = [CCNode node];
void (^copyCCNodeProperties)(CCNode*, CCNode*) = ^(CCNode* source, CCNode* clone)
{
clone.visible = source.visible;
clone.rotation = source.rotation;
clone.position = source.position;
clone.anchorPoint = source.anchorPoint;
clone.zOrder = source.zOrder;
clone.tag = source.tag;
};
for (CCNode* srcSubnode in source.children) {
CCNode* subNode;
if ([srcSubnode isMemberOfClass:[CCSprite class]]) {
CCSprite* srcSprite = (CCSprite*)srcSubnode;
subNode = [CCSprite spriteWithTexture:srcSprite.texture];
CCSprite* subSprite = (CCSprite*)subNode;
subSprite.flipX = srcSprite.flipX;
subSprite.flipY = srcSprite.flipY;
subSprite.displayFrame = srcSprite.displayFrame;
subSprite.opacity = srcSprite.opacity;
}
else if ([srcSubnode isMemberOfClass:[CCLabelTTF class]]) {
CCLabelTTF* srcLabel = (CCLabelTTF*)srcSubnode;
subNode = [CCLabelTTF labelWithString:srcLabel.string fontName:srcLabel.fontName fontSize:srcLabel.fontSize dimensions:srcLabel.dimensions hAlignment:srcLabel.horizontalAlignment vAlignment:srcLabel.verticalAlignment];
CCSprite* subLabel = (CCSprite*)subNode;
subLabel.flipX = srcLabel.flipX;
subLabel.flipY = srcLabel.flipY;
subLabel.color = srcLabel.color;
}
else {
subNode = [self cloneCCNode:srcSubnode];
}
copyCCNodeProperties(srcSubnode, subNode);
[clone addChild:subNode];
}
copyCCNodeProperties(source, clone);
return clone;
}
I have a general purpose function I built up from various sources (that I'm ashamed to say I can't reference here). What it does is take a CCSprite, create a stroke that you can put behind it and return in a CCRenderTexture. If the sprite passed in has children (as yours might) I see no reason why it wouldn't do what you want, but I haven't tried.
Here it is in case it works:
#implementation Cocosutil
+(CCRenderTexture*) createStrokeForSprite:(CCSprite*)sprite size:(float)size color:(ccColor3B)cor
{
CCRenderTexture* rt = [CCRenderTexture renderTextureWithWidth:sprite.texture.contentSize.width+size*2 height:sprite.texture.contentSize.height+size*2];
CGPoint originalPos = [sprite position];
ccColor3B originalColor = [sprite color];
BOOL originalVisibility = [sprite visible];
[sprite setColor:cor];
[sprite setVisible:YES];
ccBlendFunc originalBlend = [sprite blendFunc];
[sprite setBlendFunc:(ccBlendFunc) { GL_SRC_ALPHA, GL_ONE }];
CGPoint bottomLeft = ccp(sprite.texture.contentSize.width * sprite.anchorPoint.x + size, sprite.texture.contentSize.height * sprite.anchorPoint.y + size);
CGPoint positionOffset = ccp(sprite.texture.contentSize.width * sprite.anchorPoint.x - sprite.texture.contentSize.width/2,sprite.texture.contentSize.height * sprite.anchorPoint.y - sprite.texture.contentSize.height/2);
CGPoint position = ccpSub(originalPos, positionOffset);
[rt begin];
for (int i=0; i<360; i+=30)
{
[sprite setPosition:ccp(bottomLeft.x + sin(CC_DEGREES_TO_RADIANS(i))*size, bottomLeft.y + cos(CC_DEGREES_TO_RADIANS(i))*size)];
[sprite visit];
}
[rt end];
[sprite setPosition:originalPos];
[sprite setColor:originalColor];
[sprite setBlendFunc:originalBlend];
[sprite setVisible:originalVisibility];
[rt setPosition:position];
return rt;
}
#end
and here is code where I use it:
- (id) initWithSprite:(CCSprite*)sprite color:(ccColor3B)color strokeSize:(float)strokeSize strokeColor:(ccColor3B)strokeColor {
self = [super init];
if (self != nil) {
strokeColor_ = strokeColor;
strokeSize_ = strokeSize;
CCRenderTexture *stroke = [CocosUtil createStrokeForSprite:sprite size:strokeSize color:strokeColor];
[self addChild:stroke z:kZStroke tag:kStroke];
[self addChild:sprite z:kZLabel tag:kLabel];
[self setContentSize:[sprite contentSize]];
}
return self;
}
i create a sprite object which sets a CCSprite as its visual
//shapes.h
#interface Shapes : CCSprite
{
CCSprite *mySprite;
GamePlayLayerShapes *theGame;
NSInteger shapeID;
}
#property (nonatomic, retain) CCSprite *mySprite;
#property (nonatomic, retain) GamePlayLayerShapes *theGame;
#property (nonatomic, readwrite) NSInteger shapeID;
#end
then in shapes.m i initialize the object
//shapes.m
#implementation Shapes
#synthesize mySprite, theGame, active, shapeID;
-(id) initWithGame:(GamePlayLayerShapes *)game andFile:(NSString *)file andNumber: (NSInteger)number andZ:(NSInteger)z
{
self = [super init];
if(self != nil)
{
self.theGame = game;
if (number > 25)
{
mySprite = [CCSprite spriteWithFile:file];
}
else
{
mySprite = [CCSprite spriteWithSpriteFrameName:file];
}
[theGame addChild:mySprite z:z];
[mySprite setPosition:ccp(500, 200)];
self.shapeID = number;
}
return self;
}
then in gameplaylayershapes.h i set up two NSMutableArray (i didn't add all irrelevant variables.
#interface GamePlayLayerShapes : CCLayer
{
CCSpriteBatchNode *shapesSpriteBatchNode;
NSMutableArray *shapeArray;
NSMutableArray *targetShapeArray;
Shapes *newShape;
}
then i set it all up in gameplaylayershapes.m
-(id) init
{
self = [super init];
if(self != nil)
{
srandom(time(NULL));
shapeArray = [[NSMutableArray alloc] init];
targetShapeArray = [[NSMutableArray alloc] init];
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"GameplayShapes.plist"];
shapesSpriteBatchNode = [CCSpriteBatchNode batchNodeWithFile:#"GameplayShapes.png"];
[self addChild:shapesSpriteBatchNode];
[self createTargetShape];
[self runGame];
}
return self;
}
-(void)createTargetShape
{
//fills 4 variables with random numbers between 1 and 25
targetShape1 = (arc4random() % 25) +1;
targetShape2 = (arc4random() % 25) +1;
targetShape3 = (arc4random() % 25) +1;
targetShape4 = (arc4random() % 25) +1;
for (int x=1; x<5; x++)
{
switch (x)
{
case 1:
shapeFileName = [NSString stringWithFormat:#"shape%i.png",targetShape1];
shapeNumber = targetShape1;
break;
case 2:
shapeFileName = [NSString stringWithFormat:#"shape%i.png",targetShape2];
shapeNumber = targetShape2;
break;
case 3:
shapeFileName = [NSString stringWithFormat:#"shape%i.png",targetShape3];
shapeNumber = targetShape3;
break;
case 4:
shapeFileName = [NSString stringWithFormat:#"shape%i.png",targetShape4];
shapeNumber = targetShape4;
break;
default:
break;
}
newShape = [[Shapes alloc] initWithGame:self andFile:shapeFileName andNumber:shapeNumber andZ:10];
[targetShapeArray addObject:newShape];
[newShape release];
}
}
now what I'm trying to do is change the shapeID and mySprite for each shape object in targetshapearray. i can get the shapeID to change, but i cant for the life of me get the mySprite image to change which would change the appearance of it on my game. i keep getting a EXC_BAD_ACCESS error. Any Help would be appreciated
-(void)createNewTargetShapes
{
//fills 4 variables with random numbers between 1 and 25
targetShape1 = (arc4random() % 25) +1;
targetShape2 = (arc4random() % 25) +1;
targetShape3 = (arc4random() % 25) +1;
targetShape4 = (arc4random() % 25) +1;
for (int x=0; x<4; x++)
{
Shapes * n = (Shapes *) [targetShapeArray objectAtIndex:x];
switch (x)
{
case 0:
n.shapeID = targetShape1;
shapeFileName = [NSString stringWithFormat:#"shape%i.png",targetShape1];
n.mySprite = [CCSprite spriteWithFile:shapeFileName];
break;
case 1:
n.shapeID = targetShape2;
shapeFileName = [NSString stringWithFormat:#"shape%i.png",targetShape2];
n.mySprite = [CCSprite spriteWithFile:shapeFileName];
break;
case 2:
n.shapeID = targetShape3;
shapeFileName = [NSString stringWithFormat:#"shape%i.png",targetShape3];
n.mySprite = [CCSprite spriteWithFile:shapeFileName];
break;
case 3:
n.shapeID = targetShape4;
shapeFileName = [NSString stringWithFormat:#"shape%i.png",targetShape4];
n.mySprite = [CCSprite spriteWithFile:shapeFileName];
break;
default:
break;
}
}
}
To change a CCSprites image you must change its texture. Here is an example block of code for how to do that:
CCTexture2D* tex = [[CCTextureCache sharedTextureCache] addImage:#"spriteimagefile.png"];
[sprite setTexture: tex];
This will change the sprites image to "spriteimagefile.png".
Use the above code instead of
n.mySprite = [CCSprite spriteWithFile:shapeFileName];
I hope that's what you're looking for!
I have multiple sprites placed onto a background sprite like this:
//my background
CCSprite *bg = [CCSprite spriteWithFile:#"imageName.png"];
[self addchild:bg];
And then I add my items onto bg
//this is how i add my items
CCSprite *items = [CCSprite spriteWithFile:#"itemName.png"];
[bg addchild:items];
Oh and not forgetting my car sprite
//my car
CCSprite *car = [CCSprite spriteWithFile:#"car.png"];
[self addchild:car];
I use a loop to add multiple sprites onto the bg.
Now the question is how do I detect whether the car collided with the multiple sprites that I have placed onto the bg?
I've tried using CGRectIntersectsRect and it doesn't work.
I've tried using the pythagoras theorem method and once again it doesn't work.
There was a method which involved adding the items sprites into a NSMutableArray and it doesn't work either.
Can anyone suggest a method whereby I can try?
Additional code:
-(void) initializeCarAndItems
{
car = [CCSprite spriteWithFile:#"android.png"];
car.position = ccp(screenSize.width/2, screenSize.height * 0.30);
[self addChild:car z:1];
carRect = [car boundingBox];
}
-(void) initializeMap
{
bg1 = [CCSprite spriteWithFile:#"racingBG.png"];
bg1.anchorPoint = ccp(0, 0);
bg1.position = ccp(0, 0);
[self addChild:bg1 z:-1];
bg2 = [CCSprite spriteWithFile:#"racingBG2.png"];
bg2.anchorPoint = ccp(0,0);
bg2.position = ccp(0, bg1.boundingBox.size.height - 1);
[self addChild:bg2 z:-1];
convertedWidth = (int)bg1.boundingBox.size.width;
convertedHeight = (int)bg1.boundingBox.size.height;
for (y = 0; y < 15; y++)
{
positionX = arc4random()%convertedWidth;
positionY = arc4random()%convertedHeight;
items = [CCSprite spriteWithFile:#"item.png"];
items.position = ccp(positionX, positionY + 300);
[bg1 addChild:items z:100];
[itemsArray addObject:items];
}
for (y = 0; y < 15; y++)
{
positionX = arc4random()%convertedWidth;
positionY = arc4random()%convertedHeight;
items = [CCSprite spriteWithFile:#"item.png"];
items.position = ccp(positionX, positionY);
[bg2 addChild:items z:100];
[itemsArray addObject:items];
}
}
-(void) accelerate
{
bg1.position = ccp(0, bg1.position.y - accelerateNumber);
bg2.position = ccp(0, bg2.position.y - accelerateNumber);
if (bg1.position.y < -bg1.boundingBox.size.height)
{
questionCount++;
bg1.position = ccp(0, bg2.position.y + bg2.boundingBox.size.height - 1);
[self question];
[bg1 removeAllChildrenWithCleanup:YES];
for (y = 0; y < 15; y++)
{
positionY = arc4random()%convertedHeight;
positionX = arc4random()%convertedWidth;
items.position = ccp(positionX, positionY);
items = [CCSprite spriteWithFile:#"item.png"];
[bg1 addChild:items z:100];
[itemsArray addObject:items];
}
}
else if (bg2.position.y < -bg2.boundingBox.size.height)
{
questionCount++;
bg2.position = ccp(0, bg1.position.y + bg1.boundingBox.size.height - 1);
[self question];
[bg2 removeAllChildrenWithCleanup:YES];
for (y = 0; y < 15; y++)
{
positionY = arc4random()%convertedHeight;
positionX = arc4random()%convertedWidth;
items.position = ccp(positionX, positionY);
items = [CCSprite spriteWithFile:#"item.png"];
[bg2 addChild:items z:100];
[itemsArray addObject:items];
}
}
}
-(void) update:(ccTime)deltaTime
{
[self ifEdgeOfScreen];
[self accelerate];
for (CCSprite *itemFromArray in itemsArray)
{
CGRect itemRect = [itemFromArray boundingBox];
if (CGRectIntersectsRect(carRect, itemRect))
{
NSLog(#"Collision!");
}
}
if (leftButton.active == TRUE)
{
[self moveLeftRight:1];
}
else if (rightButton.active == TRUE)
{
[self moveLeftRight:2];
}
}
UPDATE:
It's fixed :)
-(void) update:(ccTime)deltaTime
{
car = [car boundingbox];
[self ifEdgeOfScreen];
[self accelerate];
for (CCSprite *itemFromArray in itemsArray)
{
if (CGRectIntersectsRect(carRect, [itemFromArray boundingbox]))
{
NSLog(#"Collision!");
}
}
if (leftButton.active == TRUE)
{
[self moveLeftRight:1];
}
else if (rightButton.active == TRUE)
{
[self moveLeftRight:2];
}
}
I found so many problems with the code....
When you call removeAllChildren.. Make sure you also remove objects from array.. Removing sprite from parents does not remove it from array.
update the car rect in update Method. So in your update method
-(void) update:(ccTime)deltaTime
{
[self ifEdgeOfScreen];
[self accelerate];
carRect = [car boundingBox];
...........
}
Hope this helps.. :)