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.
Related
Thank you for taking the time to look at my question, As I am very new to programming any help or pointers in the right direction is much appreciated.
Overview...
I have a simple game I am trying to create and it includes a class for 'Ball' sprite that is fired from the left of the screen to the right. The speed at which this "Ball" sprite is created is random and each time the "Ball" moves past the right of the screen a point is added to the score.
Problem....
The problem I have is when 2 "Balls" fire at almost the same time (which happens some times as its random) they pass the right hand of the screen less then a second apart and when this happens it only seems to add 1 when it should be adding 2 to the score (as 2 balls has passed the right side).
Below is the code for my Ball launcher class (use this class to call the ball randomly).
If you have used or gone through 'Ray Wenderlichs' Learn Cocos2D then allot of this will look familiar as after going through his book I have tried to edit his code to do what I want (to me after reading the book its the best way to learn, by messing with the code etc).
#implementation BL
#synthesize delegate;
#synthesize lauchingAnim;
#synthesize afterlauchingAnim;
-(void) dealloc {
delegate = nil;
[lauchingAnim release];
[afterlauchingAnim release];
[super dealloc];
}
//--The below shootPhaser method takes the current direction the launcher is facing and asks the delegate (gameplaylayer) to create the ball moving in that direction.
//--The below createPhaserWithDirection method was declared (created) in the GamePlayLayerDelegate protocol.
-(void)shootPhaser {
CGPoint phaserFiringPosition;
PhaserDirection phaserDir;
CGPoint position = [self position];
float xPosition = position.x + position.x * 0.51f;
float yPosition = position.y + position.y * 0.045f;
{
phaserDir = kDirectionRight;
}
phaserFiringPosition = ccp(xPosition, yPosition);
[delegate createPhaserWithDirection:phaserDir andPosition:phaserFiringPosition];
}
-(void)changeState:(CharacterStates)newState {
[self stopAllActions];
id action = nil;
characterState = newState;
switch (newState) {
case kStatespawning:
// CCLOG(#“launcher->Changing State to Spwaning");
[self setDisplayFrame:
[[CCSpriteFrameCache sharedSpriteFrameCache]
spriteFrameByName:#“lancher_1.png"]];
break;
case kStateIdle:
// CCLOG(#“laucher->Changing state to idle");
[self setDisplayFrame:
[[CCSpriteFrameCache sharedSpriteFrameCache]
spriteFrameByName:#"lancher_1.png"]];
break;
case kStateFiring:
// CCLOG(#“launcher->Changing State to firing");
action = [CCSequence actions:
[CCAnimate actionWithAnimation:lauchingAnim],
[CCCallFunc actionWithTarget:self
selector:#selector(shootPhaser)],
[CCAnimate actionWithAnimation:afterlauchingAnim],
[CCDelayTime actionWithDuration:2.0f],
nil];
lauchingAnim.restoreOriginalFrame = NO;
afterlauchingAnim.restoreOriginalFrame = NO;
[self changeState:kStateIdle];
break;
case kStateDead:
CCLOG(#“launcher->changing state to dead");
break;
default:
CCLOG(#"unhandled state %d in launcher", newState);
break;
}
if (action !=nil) {
[self runAction:action];
}
}
-(void)updateStateWithDeltaTime: (ccTime)deltaTime andListOfGameObjects:(CCArray*)listOfGameObjects {
if (characterState == kStateFiring) {
// 5
if (characterState != kStateFiring) {
// If RadarDish is NOT already taking Damage
[self changeState:kStateFiring];
return;
}
}
if ((([self numberOfRunningActions] == 0) && (characterState != kStateDead)) ) {
// CCLOG(#"launcher Going to Idle!!!");
[self changeState:kStateIdle];
return;
}
}
-(void)initAnimations {
[self setLauchingAnim:[self loadPlistForAnimationWithName:#"lauchingAnim" andClassName:NSStringFromClass([self class])]];
[self setAfterlauchingAnim:[self loadPlistForAnimationWithName:#"afterlauchingAnim" andClassName:NSStringFromClass([self class])]];
}
-(id) initWithSpriteFrameName:(NSString*)frameName{
if ((self=[super init])) {
if ((self = [super initWithSpriteFrameName:frameName])) {
CCLOG(#"### Laauncher initialized");
[self initAnimations];
characterHealth = 3.0f;
gameObjectType = kBallLaucher;
// 3
[self changeState:kStateIdle];
}
}
return self;
}
#end
Below is the code for my "Ball" class...
#import “Ball.h"
#implementation Ball
#synthesize delegate;
#synthesize myDirection;
#synthesize travelingAnim;
#synthesize ScoreAnim;
-(void) dealloc {
delegate = nil;
[travelingAnim release];
[ScoreAnim release];
[super dealloc];
}
-(void)changeState:(CharacterStates)newState {
[self stopAllActions];
[self setCharacterState:newState];
CGSize screenSize1 = [CCDirector sharedDirector].winSize;
CGPoint position = [self position];
CGPoint endPosition = ccp(screenSize1.width*1.5f, screenSize1.height*0.20f);
id action = nil;
//id action1 = nil;
switch (newState) {
case kStatespawning:
CCLOG(#“Spawning Ball");
[self setDisplayFrame:[[CCSpriteFrameCache
sharedSpriteFrameCache]
spriteFrameByName:#"Ball_1.png"]];
break;
case kStateTravelling: {
movementAction = [CCMoveTo actionWithDuration:5.0f
position:endPosition];
}
break;
case kStateScore:
PLAYSOUNDEFFECT(SCORE);
CCLOG(#“Ball Past Left Of Screen => Add 1 to Score");
action = [CCSequence actions:
[CCCallFunc actionWithTarget:self selector:#selector(removeSelf)],
nil];
break;
default:
CCLOG(#“Ball -> Unknown CharState %d",
characterState);
break;
}
if (action !=nil)
[self runAction:action];
}
-(void)removeSelf{
CCLOG(#"Removing Ball Object Has Scored.");
[self setVisible:NO];
[self removeFromParentAndCleanup:YES];
return;
}
-(void)updateStateWithDeltaTime:(ccTime)deltaTime andListOfGameObjects:(CCArray *)listOfGameObjects {
CGPoint currentSpitePosition = [self position];
CGSize screenSize = [CCDirector sharedDirector].winSize;
if (currentSpitePosition.x > screenSize.width*1.1f) {{
[self changeState:kStateScore];
}
}
return;
}
if ([self numberOfRunningActions] == 0) {
if (characterState == kStatespawning) {
[self changeState:kStateTravelling];
return;
}
}
}
-(void)initAnimations {
[self setTravelingAnim:[self loadPlistForAnimationWithName:#"travelingAnim" andClassName:NSStringFromClass([self class])]];
[self setScoreAnim:[self loadPlistForAnimationWithName:#"ScoreAnim" andClassName:NSStringFromClass([self class])]];
}
-(id) initWithSpriteFrameName:(NSString*)frameName{
if ((self=[super init])) {
if ((self = [super initWithSpriteFrameName:frameName])) {
CCLOG(#"### Ball Initialised");
[self initAnimations];
characterHealth = 3.0f;
gameObjectType = kEnemyTypeBall;
[self changeState:kStatespawning];
}
}
return self;
}
#end
Below is the code for my "GameplayLayer" class...
#import "GamePlayLayer.h"
#import “Ball.h"
#import “BL.h"
#implementation GamePlayLayer
#synthesize delegate;
-(void) dealloc {
delegate = nil;
[super dealloc];
}
-(void) update:(ccTime)deltaTime {
CCArray *listOfGameObjects =
[sceneSpriteBatchNode children];
for (GameCharacter *tempChar in listOfGameObjects) {
[tempChar updateStateWithDeltaTime:deltaTime andListOfGameObjects:listOfGameObjects];
}
GameCharacter *tempChar = (GameCharacter*)[sceneSpriteBatchNode
getChildByTag:kEnemyTypeBall];
if ([tempChar characterState] == kStateScore) <==HERE I AM SEEING IF THE BALL HAS SCORED - THIS IS ALSO WHERE I THINK I MAY BE GOING WRONG SOMEHOW.
{
CCLOG(#"Add 1 Points To Score");
[self addPoint];
return;
}
}
-(void)addPoint
{
score = score + 1;
[scoreLabel setString:[NSString stringWithFormat:#"$%i", score]]; <===ADDING THE POINT TO THE SCORE
return;
}
-(void) createObjectOfType: (GameObjectType)objectType
withHealth:(int)initialHealth atLocation:(CGPoint)spawnLocation withZValue:(int)ZValue {
if (kBallLaucher == objectType) {
CCLOG(#"Creating launcher Object");
BL *ballLauncher = [[[BL alloc] init] initWithSpriteFrameName:#“launcher_1.png"];
[ballLauncher setCharacterHealth:initialHealth];
[ballLauncher setPosition:spawnLocation];
[sceneSpriteBatchNode addChild:tBT z:ZValue tag:kBallLaucher ];
[ballLauncher setDelegate:self];
[ballLauncher release];
}
}
**BELOW IS HOW I CONTROL WHEN THE BALL IS FIRED**
-(void)ChangeStateLaucher:(int)brandnewState withState:(CharacterStates)newState andObject:(GameObjectType)objectType; {
BL *bL = (BL*)
[sceneSpriteBatchNode getChildByTag:kBallLaucher];
int x = (arc4random() % 2);
if (x==0) {
CCLOG(#"Start Laucher Firing");
[bL changeState:kStateFiring];
count = 0;
}
if (x==1) {
CCLOG(#"No Laucher Firing");
count = count + 1;
if (count == 2) {
CCLOG(#"No Laucher Firing x2 - Start Laucher Firing");
[bL changeState:kStateFiring];
} else if (count > 3) {
CCLOG(#"No Laucher Firing x3 - Start Laucher Firing");
[bL changeState:kStateFiring];
}
}
[delegate ChangeStateLaucher:x withState:kStateFiring andObject:objectType];
}
-(void)createPhaserWithDirection:(PhaserDirection)phaserDirection andPosition:(CGPoint)spawnPosition {
CCLOG(#"Creating Ball from Gameplay Layer");
Ball *ballSprite = [[Ball alloc]initWithSpriteFrame:[[CCSpriteFrameCache
sharedSpriteFrameCache]
spriteFrameByName:#"Ball_1.png"]];
[ballSprite setPosition:spawnPosition];
[ballSprite setMyDirection:phaserDirection];
[ballSprite setCharacterState:kStatespawning];
[ballSprite setCharacterHealth:3.0f];
[sceneSpriteBatchNode addChild:ballSprite z:20 tag:kEnemyTypeBall];
[ballSprite release];
}
-(id)init {
self = [super init];
if (self !=nil) {
CGSize screenSize = [CCDirector sharedDirector]. winSize;
self.TouchEnabled = YES;
srandom(arc4random()); // Seeds the random number generator
[[CCSpriteFrameCache sharedSpriteFrameCache]
addSpriteFramesWithFile:#"scene1atlas.plist"]; // 1
sceneSpriteBatchNode =
[CCSpriteBatchNode batchNodeWithFile:#"scene1atlas.png"]; // 2
[self createObjectOfType:kBallLaucher withHealth:3 atLocation:ccp(screenSize.width * 0.05f, screenSize.height * 0.822f) withZValue:10];
[gameBeginLabel setPosition:ccp(screenSize.width/2,screenSize.height/2)]; // 2
[self addChild:gameBeginLabel]; // 3
id labelAction = [CCSpawn actions:
[CCFadeOut actionWithDuration:2.5f],
nil]; // 4
[gameBeginLabel runAction:labelAction];
lives = 3;
scoreLabel = [CCLabelBMFont labelWithString:#"$0"
fntFile:#“BallTest.fnt"];
scoreLabel.position = ccp(screenSize.width * 0.5f, screenSize.height * 0.9f);
[self addChild:scoreLabel
z:-1 tag:kNewScoreTagValue];
[self scheduleUpdate];
}
return self;
}
#end
Once again I would like to thank anyone that has taken the time to look at this question regardless if you have posted a pointer or answer or not.
Thanks.
I think that when there are more than one Ball objects on the screen, the GamePlayLayer update method is unable to pick up on the state change on one of the balls at times because that ball is removed as soon as it enters kStateScore.
If you add a certain delay before removing the ball , the update method should pick up the changes in all the balls and your problem should be resolved.
In your Ball.m -(void)changeState:(CharacterStates)newState function , modify the action on state kStateScore like
action = [CCSequence actions:[CCDelayTime actionWithDuration:0.2],
[CCCallFunc actionWithTarget:self selector:#selector(removeSelf)],
nil];
Try this out and let me know if this worked for you!
EDIT
The above solution would end up adding more than one point as the ball would remain in kStateScore state for a long time due to delay. In order to counter that problem , we can introduce a new state call kStateDead and change the ball state to kStateDead after adding the point. This will ensure a point is added only once and also we can put the responsibility of removing the ball in kStateDead state. The new code would like as follows :
Ball.m
case kStateScore:
PLAYSOUNDEFFECT(SCORE);
CCLOG(#“Ball Past Left Of Screen => Add 1 to Score");
break;
case kStateDead:
action = [CCSequence actions:
[CCCallFunc actionWithTarget:self selector:#selector(removeSelf)],
nil];
default:
CCLOG(#“Ball -> Unknown CharState %d",
characterState);
break;
and in your Gameplayer.m
if ([tempChar characterState] == kStateScore) <==HERE I AM SEEING IF THE BALL HAS SCORED - THIS IS ALSO WHERE I THINK I MAY BE GOING WRONG SOMEHOW.
{
CCLOG(#"Add 1 Points To Score");
[self addPoint];
[tempChar changeState:kStateDead]; //change the state to dead after the point is added
return;
}
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 having an issue with the HelloWorldLayer class' update: method not being called when building and running the applications on my iPad.
Not really sure what the issue is as init: and accelerometer: didAccelerate: are called as expected.
Here is the implementation of HelloWorldLayer:
// Import the interfaces
#import "HelloWorldLayer.h"
// Needed to obtain the Navigation Controller
#import "AppDelegate.h"
const float MaxPlayerAccel = 400.0f;
const float MaxPlayerSpeed = 200.0f;
#pragma mark - HelloWorldLayer
// HelloWorldLayer implementation
#implementation HelloWorldLayer
{
CGSize _winSize;
CCSprite *_playerSprite;
UIAccelerationValue _accelerometerX;
UIAccelerationValue _accelerometerY;
float _playerAccelX;
float _playerAccelY;
float _playerSpeedX;
float _playerSpeedY;
}
// Helper class method that creates a Scene with the HelloWorldLayer as the only child.
+(CCScene *) scene
{
// 'scene' is an autorelease object.
CCScene *scene = [CCScene node];
// 'layer' is an autorelease object.
HelloWorldLayer *layer = [HelloWorldLayer node];
// add layer as a child to scene
[scene addChild: layer];
// return the scene
return scene;
}
// on "init" you need to initialize your instance
- (id)init
{
if (self = [super initWithColor:ccc4(94, 63, 107, 255)])
{
_winSize = [CCDirector sharedDirector].winSize;
_playerSprite = [CCSprite spriteWithFile:#"Player-hd.png"];
_playerSprite.position = ccp(_winSize.width - 50.0f, 50.0f);
[self addChild:_playerSprite];
self.accelerometerEnabled = YES;
NSLog(#"init: method executed");
}
return self;
}
- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration
{
const double FilteringFactor = 0.75;
_accelerometerX = acceleration.x * FilteringFactor + _accelerometerX * (1.0 - FilteringFactor);
_accelerometerY = acceleration.y * FilteringFactor + _accelerometerY * (1.0 - FilteringFactor);
if (_accelerometerY > 0.05)
{
_playerAccelX = -MaxPlayerAccel;
}
else if (_accelerometerY < 0.05)
{
_playerAccelX = MaxPlayerAccel;
}
if (_playerAccelX < -0.05)
{
_playerAccelY = -MaxPlayerAccel;
}
else if (_playerAccelX > -0.05)
{
_playerAccelY = MaxPlayerAccel;
}
}
- (void)update:(ccTime)dt
{
NSLog(#"update: method is being called");
// 1
_playerSpeedX += _playerAccelX * dt;
_playerSpeedY += _playerAccelY * dt;
// 2
_playerSpeedX = fmaxf(fminf(_playerSpeedX, MaxPlayerSpeed), - MaxPlayerSpeed);
_playerSpeedY = fmaxf(fminf(_playerSpeedY, MaxPlayerSpeed), - MaxPlayerSpeed);
// 3
float newX = _playerSprite.position.x + _playerSpeedX * dt;
float newY = _playerSprite.position.y + _playerSpeedY * dt;
// 4
newX = MIN(_winSize.width, MAX(newX, 0));
newY = MIN(_winSize.height, MAX(newY, 0));
_playerSprite.position = ccp(newX, newY);
}
#end
You need to schedule the update in your init method:
[self scheduleUpdate];
Edit: Also, happened to see another possible issue with your code - you're loading the sprite image as image-hd.png. In Cocos2D, you only need to name the file without the -hd prefix: image.png, and it will automatically select the HD version if the screen is retina.
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 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.. :)