I want to convert CCSpriteBatchNode to a UIImage, is there a possible way to do it? I know it is possible to convert CCSprite to UIImage, but when I try it with CCSpriteBatchNode, it crashes :
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'CCSpriteBatchNode should NOT be root node'
Thanks!
Here is the code to convert CCSprite to UIImage.
+ (UIImage *)renderUIImageFromSprite:(CCSprite *)sprite {
CCRenderTexture *renderer = [CCRenderTexture renderTextureWithWidth:sprite.contentSize.width; height:sprite.contentSize.height;];
[renderer begin];
[sprite visit];
[renderer end];
return [renderer getUIImageFromBuffer];
}
EDIT :
This is the working code.
+ (UIImage *)renderUIImageFromCCLabelBMFont:(CCLabelBMFont *)bmfont {
GLProgram *program = [[CCShaderCache sharedShaderCache] programForKey:kCCShader_PositionTextureColor];
glUseProgram(program->program_);
int textureWidth = bmfont.contentSize.width;
int textureHeight = bmfont.contentSize.height;
CCRenderTexture *renderer = [CCRenderTexture renderTextureWithWidth:textureWidth height:textureHeight];
bmfont.anchorPoint = ccp(0.0, 0.0);
CCNode *node = [CCNode node];
[node addChild:bmfont];
[renderer begin];
[node visit];
[renderer end];
return [renderer getUIImageFromBuffer];
}
Needed this myself. This is ARC code and set up as a category for CCLabelBMFont.
CCLabelBMFont+UIImage.h
#interface CCLabelBMFont (UIImage)
- (UIImage *)image;
#end
CCLabelBMFont+UIImage.m
#implementation CCLabelBMFont (UIImage)
- (UIImage *)image
{
CCGLProgram *program = [[CCShaderCache sharedShaderCache] programForKey:kCCShader_PositionTextureColor];
glUseProgram(program->program_);
CCRenderTexture *renderer = [[CCRenderTexture alloc] initWithWidth:(int)self.contentSize.width
height:(int)self.contentSize.height
pixelFormat:kCCTexture2DPixelFormat_RGBA8888];
CCLabelBMFont *labelBMFont = [[CCLabelBMFont alloc] initWithString:string_
fntFile:fntFile_
width:width_
alignment:alignment_
imageOffset:imageOffset_];
labelBMFont.anchorPoint = ccp(0.0, 0.0);
CCNode *node = [CCNode node];
[node addChild:labelBMFont];
[renderer begin];
[node visit];
[renderer end];
return [renderer getUIImage];
}
#end
Related
I am working on a game that has the player controlling a spaceShip. I want to make it such that if the spaceShip sprite collides with spaceJunk the junk disappears.
Here is my code so far.
HelloWorldLayer.h
#import <GameKit/GameKit.h>
// HelloWorldLayer
#interface HelloWorldLayer : CCLayerColor
{
CCSprite *_starShip;
CCSprite *_Paddle1;
CCSprite *_paddle2;
}
// returns a CCScene that contains the HelloWorldLayer as the only child
+(CCScene *) scene;
#end
HelloWorldLayer.M
// Import the interfaces
#import "HelloWorldLayer.h"
// Needed to obtain the Navigation Controller
#import "AppDelegate.h"
#pragma mark - HelloWorldLayer
// HelloWorldLayer implementation
//Import SpaceThings
#import "SpaceThings.h"
#implementation HelloWorldLayer{
}
// 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;
}
-(void)gameLgoic:(ccTime)dt
{
[self addSpaceThings];
}
-(void) addSpaceThings
{
// _junk = [CCSprite spriteWithFile:#"catplaceholder.png"];
SpaceThings * spaceThings = nil;
if (arc4random() % 2 == 0){
spaceThings = [[[Astroids alloc] init] autorelease];
}else{
spaceThings = [[[SpaceJunk alloc]init] autorelease];
}
//determine where to spawn the space junk along the X-axis
CGSize winSize = [CCDirector sharedDirector].winSize;
int minX = spaceThings.contentSize.width/2;
int maxX = winSize.width - spaceThings.contentSize.width/2;
int rangeX =maxX - minX;
int actualX = (arc4random() % rangeX) + minX;
//spawn the sprite slightly above the screen
spaceThings.position = ccp(actualX, winSize.height+_starShip.contentSize.height/2);
[self addChild:spaceThings];
//set the speed of junk
int minDuration = spaceThings.minMoveDirection;
int maxDuarton = spaceThings.maxMoveDirection;
int rangeDuration = maxDuarton - minDuration;
float actualDuraton = (arc4random() % rangeDuration)+ minDuration;
//move junk logic
CCMoveTo * actionMove = [CCMoveTo actionWithDuration:actualDuraton position:ccp(actualX, -spaceThings.contentSize.height/2)];
CCCallBlockN *actionMoveDone = [CCCallBlockN actionWithBlock:^(CCNode *node){[node removeFromParentAndCleanup:YES];}];
[spaceThings runAction:[CCSequence actions:actionMove, actionMoveDone, nil]];
if (CGRectIntersectsRect(_starShip.boundingBox, spaceThings.boundingBox)){
[self removeChild:spaceThings cleanup:YES];
}
}
// on "init" you need to initialize your instance
-(id) init
{
if (self =[super initWithColor:ccc4(255,255,255,255)]){
CGSize winSize = [CCDirector sharedDirector].winSize;
_starShip = [CCSprite spriteWithFile:#"theShip.gif"];
_starShip.position = ccp(winSize.width/2, 1.25*_starShip.contentSize.height);
//make the starship appear
[self addChild:_starShip];
//make the paddles
//bottom left one
_Paddle1 = [CCSprite spriteWithFile:#"spritePaddle.jpeg"];
int bottomOfScreenX = 0 + _Paddle1.contentSize.width/2;
int bottomOfScreenY = 0+_Paddle1.contentSize.height/2;
_Paddle1.position = ccp(bottomOfScreenX,bottomOfScreenY);
[self addChild:_Paddle1];
//bottom right one
_paddle2 = [CCSprite spriteWithFile:#"spritePaddle.jpeg"];
int bottomRightOfScreenX = winSize.width - _paddle2.contentSize.width/2;
_paddle2.position = ccp(bottomRightOfScreenX, bottomOfScreenY);
[self addChild:_paddle2];
//make thingsInSpace spawn at a set interval
int setInterval = (arc4random() % 3);
[self schedule:#selector(gameLgoic:) interval:setInterval];
//enable touch
[self setIsTouchEnabled:YES];
}
return self;
}
// on "dealloc" you need to release all your retained objects
- (void) dealloc
{
// in case you have something to dealloc, do it in this method
// in this particular example nothing needs to be released.
// cocos2d will automatically release all the children (Label)
// don't forget to call "super dealloc"
[super dealloc];
}
-(void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
//make the screens and create events
CGSize winSize = [CCDirector sharedDirector].winSize;
NSSet *allTouches = [event allTouches];
UITouch *touch = [allTouches anyObject];
CGPoint location = [touch locationInView:[touch view]];
location = [[CCDirector sharedDirector] convertToGL:location];
//check to see if the left paddle is being pressed
if (CGRectContainsPoint([_Paddle1 boundingBox], location)){
int bottomLeftOfScreenX = 0 + _Paddle1.contentSize.width/1.25;
//make an action
//the Y-coordinate of starShip must be the same since the ship must move parallel to it.
id action = [CCMoveTo actionWithDuration:2 position:ccp(bottomLeftOfScreenX,1.25*_starShip.contentSize.height) ];
//have starship call this action
[_starShip runAction:action];
[action setTag:1];
}
//check to see if the right paddle is being pressed
if (CGRectContainsPoint([_paddle2 boundingBox], location)){
int bottomRightOfScreenX = winSize.width - _paddle2.contentSize.width/1.25;
//make an action
//the Y-coordinate of starShip must be the same since the ship moves parallel to it
id action2 = [CCMoveTo actionWithDuration:2 position:ccp(bottomRightOfScreenX,1.25*_starShip.contentSize.height) ];
//starShip is now running this specified action
[_starShip runAction:action2];
[action2 setTag:2];
}
}
-(void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
//if the player stops touching the pads, all the aciton should stop.
//left button stop
[_starShip stopActionByTag:1];
//right button stop
[_starShip stopActionByTag:2];
}
#pragma mark GameKit delegate
-(void) achievementViewControllerDidFinish:(GKAchievementViewController *)viewController
{
AppController *app = (AppController*) [[UIApplication sharedApplication] delegate];
[[app navController] dismissModalViewControllerAnimated:YES];
}
-(void) leaderboardViewControllerDidFinish:(GKLeaderboardViewController *)viewController
{
AppController *app = (AppController*) [[UIApplication sharedApplication] delegate];
[[app navController] dismissModalViewControllerAnimated:YES];
}
#end
spaceThings.h
#import <Foundation/Foundation.h>
#import "cocos2d.h"
#interface SpaceThings : CCSprite
#property (nonatomic, assign) int minMoveDirection;
#property (nonatomic, assign) int maxMoveDirection;
-(id)initWithFile:(NSString *)file minMoveDirection:(int)minMoveDirection maxMoveDirection:(int)maxMoveDirection;
#end
#interface Astroids : SpaceThings
#end
#interface SpaceJunk : SpaceThings
#end
SpaceThings.M
#import "SpaceThings.h"
#implementation SpaceThings
-(id)initWithFile:(NSString *)file minMoveDirection:(int)minMoveDirection maxMoveDirection:(int)maxMoveDirection
{
if ((self =[super initWithFile:file])){
self.minMoveDirection = minMoveDirection;
self.maxMoveDirection = maxMoveDirection;
}
return self;
}
#end
#implementation Astroids
-(id)init
{
if (self = [super initWithFile:#"astroid.jpeg" minMoveDirection:2 maxMoveDirection:5]){
}
return self;
}
#end
#implementation SpaceJunk
-(id)init
{
if (self = [super initWithFile:#"blueDot.jpg" minMoveDirection:4 maxMoveDirection:7]){
}
return self;
}
#end
I'm wondering as to where to put CGRectInteresectRect part of the code. As I've currently tried placing within the SpaceJunk alloc init part and it doesn't remove the child when the ship collides with the junk.
int setInterval = (arc4random() % 3);
[self schedule:#selector(gameLgoic:) interval:setInterval];
Make setInterval value to float and give small value for schedular.
Also not do [spaceThings runAction inside loop. if required then do some condition check and run only if required.
Then your CGRectIntersectsRect will fire when intersect each other.
Happy Coding.
I'm very headache about this problem, I'm using these code for capturing a part of the screen
-(UIImage *) glToUIImage {
userphotoCount++;
[CCDirector sharedDirector].nextDeltaTimeZero = YES;
CGSize winSize = [CCDirector sharedDirector].winSize;
CCLayerColor* whitePage = [CCLayerColor layerWithColor:ccc4(255, 255, 255, 0) width:winSize.width height:winSize.height];
whitePage.position = ccp(winSize.width/2, winSize.height/2);
CCRenderTexture* rtx = [CCRenderTexture renderTextureWithWidth:815 height:532];
[rtx begin];
[whitePage visit];
[[[CCDirector sharedDirector] runningScene] visit];
[rtx end];
return [rtx getUIImageFromBuffer];
}
I can get my UIImage by
image = [self glToUIImage];
Then I try to use this code to generate a sprite by the UIImage
-(void) ImageSprite{
image = [self glToUIImage];
userAns = [CCSprite spriteWithCGImage:image.CGImage key:[NSString stringWithFormat:#"photoCap%d.png", userphotoCount]];
userAns.position=ccp(780,410);
userAns.scale=0.6;
[self addChild:userAns z:20];
}
I can get image by these code at first time, but after I changed the screen's contents and use these code to generate new sprite(new UIImage) with the new contents, I'm failed.... the image doesn't change to new image...
How can I get the new image sprite when every time I run the "imageSprite" code?
You can get the new image sprite, but you must to change key
[CCSprite spriteWithCGImage:image.CGImage key:key];
I'm using a CCParallaxNode for a scrolling background in my GameLayer and I'm getting a bunch of errors and I can't figure out why.
"Initializer element is not a compile-time constant" and also "Unk
#implementation Background
// Load the sprites for each parallax layer, from background to foreground.
CCSprite* para1 = [CCSprite spriteWithFile:#"color.png"];
CCSprite* para2= [CCSprite spriteWithFile:#"ship-hd.png"];
CCSprite* para3 = [CCSprite spriteWithFile:#"twit.png"];
CCSprite* para4 = [CCSprite spriteWithFile:#"Start.png"];
// Set the correct offsets depending on the screen and image sizes.
para1.anchorPoint = CGPointMake(0, 1);
para2.anchorPoint = CGPointMake(0, 1);
para3.anchorPoint = CGPointMake(0, 0.6f);
para4.anchorPoint = CGPointMake(0, 0);
CGPoint topOffset = CGPointMake(0, screenSize.height);
CGPoint midOffset = CGPointMake(0, screenSize.height / 2);
CGPoint downOffset = CGPointZero;
// Create a parallax node and add the sprites to it.
CCParallaxNode* paraNode = [CCParallaxNode node];
[paraNode addChild:para1 z:1 parallaxRatio:CGPointMake(0.5f, 0) ositionOffset:topOffset];
[paraNode addChild:para2 z:2 parallaxRatio:CGPointMake(1, 0) positionOffset:topOffset];
[paraNode addChild:para3 z:4 parallaxRatio:CGPointMake(2, 0) positionOffset:midOffset];
[paraNode addChild:para4 z:3 parallaxRatio:CGPointMake(3, 0) positionOffset:downOffset];
[self addChild:paraNode z:0 tag:ParallaxSceneTagParallaxNode];
// Move the parallax node to show the parallaxing effect.
CCMoveBy* move1 = [CCMoveBy actionWithDuration:5 position:CGPointMake(−160, 0)];
CCMoveBy* move2 = [CCMoveBy actionWithDuration:15 position:CGPointMake(160, 0)];
CCSequence* sequence = [CCSequence actions:move1, move2, nil];
CCRepeatForever* repeat = [CCRepeatForever actionWithAction:sequence];
[paraNode runAction:repeat];
#end
You need to enclose your code in an -(id)init method, like this:
#implementation Background
- (id)init
{
if (self = [super init]) {
// Load the sprites for each parallax layer, from background to foreground.
CCSprite* para1 = [CCSprite spriteWithFile:#"color.png"];
// ... the rest of your code ... //
}
return self;
}
#end
As the title, I have mentioned the code below
CCSprite *sprite = (CCSprite *)node;
CCTexture2D *texture2d = [sprite texture];
How can I get a UIImage *p from sprite?
In your case just add next methood into your class:
- (UIImage *) imageFromSprite :(CCSprite *)sprite
{
int tx = sprite.contentSize.width;
int ty = sprite.contentSize.height;
CCRenderTexture *renderer = [CCRenderTexture renderTextureWithWidth:tx height:ty];
sprite.anchorPoint = CGPointZero;
[renderer begin];
[sprite visit];
[renderer end];
return [renderer getUIImage];
}
how to use:
CCSprite *sprite = (CCSprite *)node;
UIImage *p = [self imageFromSprite:sprite]
uhh...
I think this is a better to way. Add this as a category on CCNode.
-(UIImage*)image {
CCRenderTexture* renderer = [CCRenderTexture renderTextureWithWidth:self.contentSize.width height:self.contentSize.height];
const CGPoint ANCHORBEFORE = self.anchorPoint;
self.anchorPoint = CGPointZero;
[renderer begin];
[self visit];
[renderer end];
self.anchorPoint = ANCHORBEFORE;
return [renderer getUIImage];
}
If you are using cocos2d 2.x, render sprite into CCRenderTexture and call getUIImage method of that render texture.
By looking at the source code of both CCSprite and CCTexture2D you can see that the image is used only for creating the texture data and the image object is not kept but temporarily used.
CCTexture2D source :
http://code.google.com/p/cocos2d-iphone/source/browse/trunk/cocos2d/CCTexture2D.m?r=1882
CCSprite source :
http://code.google.com/p/cocos2d-iphone/source/browse/trunk/cocos2d/CCSprite.m?r=1747
Look in the init methods that use CGImageRef
I like to draw a line, but cocos2d inside ccDrawLine serrate, how to draw a blurring of the line, who can help me?
ccDrawLine( ccp(StartP.x, StartP.y), ccp(EndP.x, EndP.y) );
I did not use ccDrawLine but I created a line with a Sprite and I animated it (see code below). In this way I was able to use custom line images made (but nevermind. that's how I did).
If you want to stick to primitives I guess you should set the opacity of the line primitive (see this post which explains how) and then you could create a sequence of action that set the opacity level (e.g. start with opacity at 100%, then 75%, then back to 100% like I did with my images but using the method in the link above) to get the blurring effect..
Code using images:
CCSprite * string = [self getChildByTag:tag];
[string setOpacity:100];
NSMutableArray* frames = [[NSMutableArray alloc]initWithCapacity:3];
NSString*lineFrame = [NSString stringWithString:#"line0.png"];
CCSpriteFrame* frame = [[CCSpriteFrameCache sharedSpriteFrameCache]spriteFrameByName:lineFrame];
[frames addObject:frame];
lineFrame = [NSString stringWithString:#"line1.png"];
frame = [[CCSpriteFrameCache sharedSpriteFrameCache]spriteFrameByName:lineFrame];
[frames addObject:frame];
lineFrame = [NSString stringWithString:#"line0.png"];
frame = [[CCSpriteFrameCache sharedSpriteFrameCache]spriteFrameByName:lineFrame];
[frames addObject:frame];
CCAnimation* anim = [CCAnimation animationWithFrames:frames delay:0.1f];
CCAnimate* animate = [CCAnimate actionWithAnimation:anim];
//CCRepeatForever* repeat = [CCRepeatForever actionWithAction:animate];
[string runAction:animate];
[string setOpacity:75];
Hope that this helps..
#import "cocos2d.h"
#interface CClineSprite : CCLayer
{
CCRenderTexture * renderTarget;
NSMutableArray * pathArray;
CCSprite * pathBrush;
CGPoint prePosition;
}
-(void)setLinePosition:(CGPoint)position;
-(void)setLineOpacity:(GLubyte) anOpacity;
-(void)setLineScale:(float) scale;
-(void)setLineColor:(ccColor3B) color;
#end
#import "CClineSprite.h"
#implementation CClineSprite
- (id) init
{
self = [super init];
if (self)
{
CGSize s = [[CCDirector sharedDirector] winSize];
renderTarget = [CCRenderTexture renderTextureWithWidth:s.width height:s.height];
[renderTarget setPosition:ccp(s.width/2, s.height/2)];
[self addChild:renderTarget z:1];
pathBrush = [CCSprite spriteWithFile:#"dots.png"];
pathBrush.color = ccWHITE;
[pathBrush setOpacity:100];
[pathBrush setScale:0.5];
pathArray = [[NSMutableArray alloc]init];
}
return self;
}
-(void)setLineOpacity:(GLubyte) anOpacity
{
[pathBrush setOpacity:anOpacity];
}
-(void)setLineScale:(float) scale
{
[pathBrush setScale:scale];
}
-(void)setLineColor:(ccColor3B) color
{
[pathBrush setColor:color];
}
-(void)setLinePosition:(CGPoint)position
{
[pathArray addObject:[NSValue valueWithCGPoint:position]];
[self renderPath];
}
- (void) renderPath
{
[renderTarget clear:0 g:0 b:0 a:0];
[renderTarget begin];
for (int i = 0; i < pathArray.count-1;i++)
{
CGPoint pt1;
CGPoint pt2;
[[pathArray objectAtIndex:i] getValue: &pt1];
[[pathArray objectAtIndex:i + 1] getValue: &pt2];
float distance = ccpDistance(pt1, pt2);
if (distance > 1)
{
int d = (int)distance;
for (int i = 0; i < d; i += 10)
{
float difx = pt2.x - pt1.x;
float dify = pt2.y - pt1.y;
float delta = (float)i / distance;
[pathBrush setPosition:ccp(pt1.x + (difx * delta), pt1.y + (dify * delta))];
[pathBrush visit];
}
}
}
[renderTarget end];
}
#end
USAGE:
CClineSprite* streak = [CClineSprite node];
[self addChild:streak z:999];
[streak setLinePosition:associatedTurtleObject.position];
[streak setLineScale:0.4];
To dynamically update or extend the line just use
[streak setLinePosition:ccp(x,y)];
Hope this will give you more flexibility for use