I have an Objective-C project. In the project, I want to use some CCAction on the image(UIImage). So, I think I need to change all UIImage into CCSprite.
I have a function which add image to view for all images in the project:
UIImage* firstImage = [imageArray objectAtIndex:0];
UIImageView* imageView = [[UIImageView alloc] initWithImage:firstImage];
[targetView addSubview:imageView];
[imageView release];
return imageView;
At this point, I don't know how to do addSubview on CCSprite. As I know, in cocos2d, only one CCGLView is using.
Is there any hints? thank you.
Some useful Notes for you:
« You can add one CCSprite to other CCSprite by using addChild method.
« Creating CCSprite from UIImage:
CCTexture2D *tex = [[[CCTexture2D alloc] initWithImage:uiImage] autorelease];
CCSprite *sprite = [CCSprite spriteWithTexture:tex];
« Creating UIImage from CCSprite:
- (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];
}
//call syntax
CCSprite *sprite = (CCSprite *)node;
UIImage *p = [self imageFromSprite:sprite]
Related
I'm trying to add frosted glass effect for nodes in my game. For example http://bit.ly/1vNMvAG
What is the right way to do that?
I must be missing something, as I would just stay with SpriteKit, as suggested in original comments, but maybe I'll get schooled and learn something new. :-) EDIT, simplified everything, and now you can just move the crop mask by setting it as a property and then changing its position dynamically as you go. Obviously you could jazz it up with various sprite layers.
SKSpriteNode *bgImage = [SKSpriteNode spriteNodeWithImageNamed:#"bgImage.png"];
bgImage.anchorPoint = CGPointZero;
[self addChild:bgImage];
cropMaskNode = [SKSpriteNode spriteNodeWithImageNamed:#"clippingImage.png"];
SKCropNode *cropNode = [SKCropNode node];
SKSpriteNode *bgInsideOfCrop = [SKSpriteNode spriteNodeWithImageNamed:#"bgImage.png"];
bgInsideOfCrop.anchorPoint = CGPointZero;
SKEffectNode *effectNode = [SKEffectNode node];
effectNode.filter = [self blurFilter];
effectNode.shouldEnableEffects = YES;
effectNode.shouldCenterFilter = YES;
effectNode.shouldRasterize = YES;
[self addChild: cropNode];
[effectNode addChild:bgInsideOfCrop];
[cropNode addChild:effectNode];
cropNode.maskNode = cropMaskNode;
this effect is only available in ios 7 to my knowlwdge. Engin Kurutepe has posted it on Github
- (void)willMoveToSuperview:(UIView *)newSuperview
{
[super willMoveToSuperview:newSuperview];
if (newSuperview == nil) {
return;
}
UIGraphicsBeginImageContextWithOptions(newSuperview.bounds.size, YES, 0.0);
[newSuperview drawViewHierarchyInRect:newSuperview.bounds afterScreenUpdates:YES];
UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
UIImage *croppedImage = [UIImage imageWithCGImage:CGImageCreateWithImageInRect(img.CGImage, self.frame)];
UIGraphicsEndImageContext();
self.backgroundImage = [croppedImage applyBlurWithRadius:11
tintColor:[UIColor colorWithWhite:1 alpha:0.3]
saturationDeltaFactor:1.8
maskImage:nil];
}
Code Sample Ref
Is there an efficient way to do this? currently i use
[renderTexture begin];
[self.view visit];
[renderTexture end];
but app jitters because of it.
I read I can copy contents of frame buffer for this. Is that true? Can someone guide me how to do that.
I used a method that worked for me. It does a screenshot, and then selects a frame of it. You can change it, so it takes a screenshot from the whole screen.
Here's the code.
-(UIImage*) takeScreenShot
{
[CCDirector sharedDirector].nextDeltaTimeZero = YES;
CGSize winSize = [CCDirector sharedDirector].winSize;
//aux layer to make screenshot
CCLayerColor* blankLayer = [CCLayerColor layerWithColor:ccc4(255, 255, 255, 0) width:winSize.width height:winSize.height];
blankLayer.position = ccp(winSize.width/2, winSize.height/2);
CCRenderTexture* rtx = [CCRenderTexture renderTextureWithWidth:winSize.width height:winSize.height];
//this part is the same as yours
[rtx begin];
[blankLayer visit];
[[[CCDirector sharedDirector] runningScene] visit];
[rtx end];
//this part makes a rectangle from the screenshot with size.width and 200 height. Change it
UIImage *tempImage =[rtx getUIImage];
CGRect imageBoundary = CGRectMake(0, 0, winSize.width, 200);
UIGraphicsBeginImageContext(imageBoundary.size);
CGContextRef context = UIGraphicsGetCurrentContext();
// translated rectangle for drawing sub image
CGRect drawRect = CGRectMake(-imageBoundary.origin.x, -imageBoundary.origin.y, tempImage.size.width, tempImage.size.height);
// clip to the bounds of the image context
CGContextClipToRect(context, CGRectMake(0, 0, imageBoundary.size.width, imageBoundary.size.height));
// draw image
[tempImage drawInRect:drawRect];
UIImage* subImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return subImage;
//and there you have an UIImage
}
This code works well when making screenshots of a part of the screen. Maybe you can try deleting the code part where it makes a temp image with a CGRect. But I recommend you to change the values like this:
CGRect imageBoundary = CGRectMake(0, 0, winSize.width, winSize.height);
So it will make a full screenshot.
You can use "Kamcord" framework, with this framework you can take screenshots as well as you can also record video of your game play and you can replay them..
I use it. This is for Cocos2d v3. Weak needs for memory returning. Because there're some problems with it. If you'll remove weak, you memory usage will increase every time you call this method. I think it's best way to do.
+(UIImage *)screenshot
{
[CCDirector sharedDirector].view.alpha = 1.0;
[CCDirector sharedDirector].nextDeltaTimeZero = YES;
CGSize winSize = [[CCDirector sharedDirector] viewSize];
CCRenderTexture* renTxture = [CCRenderTexture renderTextureWithWidth:winSize.width
height:winSize.height
pixelFormat:CCTexturePixelFormat_RGB565];
[renTxture begin];
MainScene* tempScene = (MainScene *)[[CCDirector sharedDirector] runningScene];
MainScene* __weak scene = tempScene;
[scene visit];
[renTxture end];
UIImage *tempImage = [renTxture getUIImage];
UIImage * __weak image = tempImage;
return image;
}
I would like to take the screenshot of device camera and CClayer with image at the same time.
But as I wrote the following code, the result is that:
I can only take the screenshot of CClayer, not both of them.
The macro 'UIGetScreenImage' can solve this problem but as I heard that Apple rejects that code because of it's illegal.
Any ideas/links/details on how to solve this problem?
I've reviewed that link but it doesn't help me to solve the problem.
PS I use Cocos2D 2.1 but I'm the beginner.
//in AppDelegate.h
#property (nonatomic, retain) UIView *overlay;
//in AppDelegate.m
[CCDirector sharedDirector].view.backgroundColor = [UIColor clearColor];
[CCDirector sharedDirector].view.opaque = NO;
//director_.view.opaque=NO;
//director_.view.backgroundColor= [UIColor clearColor];
glClearColor(0.0, 0.0, 0.0, 0.0);
// prepare the overlay view and add it to the window
self.overlay = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.overlay.opaque = NO;
self.overlay.backgroundColor = [UIColor clearColor];
[window_ addSubview:self.overlay];
[window_ bringSubviewToFront:overlay];
self.overlay.hidden = YES;
//in CameraScene.m
-(id) init
{
#define CAMERA_TRANSFORM 1.24299
if( (self = [super init])) {
UIView* overlay = self.appdelegate.overlay;
overlay.hidden = NO;
#try {
uip = [[[UIImagePickerController alloc] init] autorelease];
uip.sourceType = UIImagePickerControllerSourceTypeCamera;
uip.showsCameraControls = NO;
uip.toolbarHidden = YES;
uip.navigationBarHidden = YES;
uip.wantsFullScreenLayout = YES;
uip.cameraViewTransform = CGAffineTransformScale(uip.cameraViewTransform, CAMERA_TRANSFORM, CAMERA_TRANSFORM);
}
#catch (NSException * e) {
[uip release];
uip = nil;
}
#finally {
if(uip) {
[overlay addSubview:[uip view]];
[overlay release];
}
}
// ask director the the window size
self.touchEnabled=YES;
_state=kIdle;
[self createGUI];
[self createAndAnimateModel];
}
return self;
}
//for screeshot
- (UIImage*)makeaShot {
/*CGImageRef screen = UIGetScreenImage();
UIImage* screenImage = [UIImage imageWithCGImage:screen];
CGImageRelease(screen);
return screenImage;*/
/*UIGraphicsBeginImageContext(self.winSize);
[[CCDirector sharedDirector].view renderInContext:UIGraphicsGetCurrentContext()];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;*/
/*[CCDirector sharedDirector].nextDeltaTimeZero = YES;
CGSize winSize = [CCDirector sharedDirector].winSize;
CCRenderTexture* rtx = [CCRenderTexture renderTextureWithWidth:winSize.width height:winSize.height];
[rtx beginWithClear:0 g:0 b:0 a:1.0f];
[[[CCDirector sharedDirector] runningScene] visit];
[rtx end];
return [rtx getUIImage];*/
//return [AWScreenshot takeAsImage];
/*CGRect rect = [[UIScreen mainScreen] bounds];
UIGraphicsBeginImageContext(rect.size);
CGContextRef context = UIGraphicsGetCurrentContext();
[[CCDirector sharedDirector].view.layer renderInContext:context];
UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return img;0*/
/*CGImageRef screen = [AWScreenshot takeAsCGImage];
UIImage* screenImage = [UIImage imageWithCGImage:screen];
CGImageRelease(screen);
return screenImage;*/
[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:winSize.width height:winSize.height];
[rtx begin];
[whitePage visit];
[[[CCDirector sharedDirector] runningScene] visit];
[rtx end];
return [rtx getUIImage];
}
I want to make a CCMenuItemImage with a UIImage. How should I do that?
You can make CCSprite from UIImage using this code:
CCTexture2D *tex = [[[CCTexture2D alloc] initWithImage:uiImage] autorelease];
CCSprite *sprite1 = [CCSprite spriteWithTexture:tex];
After creating CCSprite you can make CCMenuItem using code:
CCMenuItem *mnItem = [CCMenuItemSprite itemFromNormalSprite:sprite1 selectedSprite:sprite1 disabledSprite:sprite1 target:self selector:#selector(yourSelector:)];
You can create CCSprite to use it in CCMenuItemSprite with
+(id) spriteWithCGImage: (CGImageRef)image key:(NSString*)key;
constructor.
To get UIImage object has CGImage property, that returns CGImageRef.
I've got a problem in my cocos2d game: In the Game's level layer I've 3-5 big sprites for background (1920*640 ~100kb each) ~ 400-500kb in each level. When I switch between menu and various game's level 4-5 times the game crashes to main IPhone menu
[[CCDirector sharedDirector] replaceScene:transition];
without this big sprites all work perfect!
-(id) init
{
....
if( (self=[super init])) {
_spriteBGLevel1 = [CCSprite spriteWithFile: [NSString stringWithFormat:#"level_fon_%i_1.png", _currentLevel]];
_spriteBGLevel1.anchorPoint = ccp(0, 0);
_spriteBGLevel1.position = CGPointMake(0.0, 0.0);
[self addChild:_spriteBGLevel1 z:-5];
_spriteBGLevel2 = [CCSprite spriteWithFile: [NSString stringWithFormat:#"level_fon_%i_2.png", _currentLevel]];
_spriteBGLevel2.anchorPoint = ccp(0, 0);
_spriteBGLevel2.position = CGPointMake(0.0, 0.0);
[self addChild:_spriteBGLevel2 z:-5];
_spriteBGLevel3 = [CCSprite spriteWithFile: [NSString stringWithFormat:#"level_fon_%i_3.png", _currentLevel]];
_spriteBGLevel3.anchorPoint = ccp(0, 0);
_spriteBGLevel3.position = CGPointMake(0.0, 0.0);
[self addChild:_spriteBGLevel3 z:-5];
....
- (void) dealloc
{
....
_spriteBGLevel1=nil;
_spriteBGLevel2=nil;
_spriteBGLevel3=nil;
[super dealloc];
}
Background.m
#import "Background.h"
#import "GameConfig.h"
#import "XMLReader.h"
#import "CCParallaxNode-Extras.h"
#import "SettingsManager.h"
#implementation Background
#synthesize backgrounds=_backgrounds;
#synthesize backgroundNode=_backgroundNode;
+(id) scene
{
CCScene *scene = [CCScene node];
Background *layer = [Background node];
[scene addChild: layer];
return scene;
}
-(id) init
{
if ((self = [super init]))
{
CGSize screenSize = [[CCDirector sharedDirector] winSize];
int currentLevel = [[SettingsManager sharedSettingsManager] getCurrentLevel];
_backgroundNode = [CCParallaxNode node];
_backgroundNode.anchorPoint = CGPointMake(0, 0);
_backgroundNode.position = CGPointMake(0, 0);
[self addChild:_backgroundNode z:-1];
NSData *xmlData = [NSData dataWithContentsOfFile:[[NSBundle bundleForClass:[self class]] pathForResource:#"gameScene" ofType:#"xml"]];
NSError *error = nil;
NSDictionary *dictionary = [XMLReader dictionaryForXMLData:xmlData error:&error];
NSDictionary *levelsDict = [dictionary valueForKeyPath:#"levels.level"];
NSDictionary *levelDict;
_backgrounds = [[[NSMutableArray alloc] init] retain];
// [[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile: [NSString stringWithFormat:#"level_fon_%i.plist", currentLevel]];
for (levelDict in levelsDict)
{
int idLevel = [[levelDict valueForKeyPath:#"id"] intValue];
if(idLevel==currentLevel)
{
NSDictionary *fonsDict = [levelDict valueForKeyPath:#"background.fon"];
NSDictionary *fonDict;
for (fonDict in fonsDict){
NSString *name=[fonDict valueForKeyPath:#"name"];
int zIndex=[[fonDict valueForKeyPath:#"z"] intValue];
float ratio=[[fonDict valueForKeyPath:#"ratio"] floatValue];
float offsetx=[[fonDict valueForKeyPath:#"offsetx"] floatValue];
float offsety=[[fonDict valueForKeyPath:#"offsety"] floatValue];
if(zIndex<0)
{
//CCTexture2D* tex = [[CCTextureCache sharedTextureCache] addImage:[NSString stringWithFormat:#"%#", name]];
//CCSprite *fon_level_1 = [CCSprite spriteWithSpriteFrameName: [NSString stringWithFormat:#"%#", name]];
//CCSprite *fon_level_2 = [CCSprite spriteWithSpriteFrameName: [NSString stringWithFormat:#"%#", name]];
//CCSprite *fon_level_1 = [CCSprite spriteWithTexture:tex];
//CCSprite *fon_level_2 = [CCSprite spriteWithTexture:tex];
fon_level_1 = [CCSprite spriteWithFile:[NSString stringWithFormat:#"%#", name]];
fon_level_2 = [CCSprite spriteWithFile:[NSString stringWithFormat:#"%#", name]];
fon_level_1.anchorPoint = CGPointMake(0, 0);
fon_level_1.position = CGPointMake(0, 0);
fon_level_2.anchorPoint = CGPointMake(0, 0);
fon_level_2.position = CGPointMake(0, 0);
//[_backgroundNode addChild:fon_level_1 z:zIndex parallaxRatio:ccp(ratio, ratio) positionOffset:ccp(offsetx, offsety*screenSize.height)];
//[_backgroundNode addChild:fon_level_2 z:zIndex parallaxRatio:ccp(ratio, ratio) positionOffset:ccp(fon_level_1.contentSize.width, offsety*screenSize.height)];
[_backgrounds addObject:fon_level_1];
[_backgrounds addObject:fon_level_2];
fon_level_1=nil;
fon_level_2=nil;
}
}
break;
}
}
NSLog(#"count: %d",_backgrounds.count);
[self scheduleUpdate];
}
return self;
}
- (void)scheduleUpdate
{
[self schedule:#selector(updateBackgroud:)];
}
-(void) updateBackgroud:(ccTime)delta
{
CGPoint backgroundScrollVel = ccp(-1000, 0);
_backgroundNode.position = ccpAdd(_backgroundNode.position, ccpMult(backgroundScrollVel, delta));
for (CCSprite *background in _backgrounds) {
if (([_backgroundNode convertToWorldSpace:background.position].x+background.contentSize.width/10) < -(background.contentSize.width)) {
[_backgroundNode incrementOffset:ccp(background.contentSize.width*2,0) forChild:background];
}
}
}
- (void) dealloc
{
_backgroundNode = nil;
// _backgrounds = nil;
[_backgrounds removeAllChildren];
[_backgrounds release];
[super dealloc];
}
#end
Well, here's how I have gone about it:
Get as close as possible to a POT texture size. 1920x640 costs you as much memory as a 2048x2048. Maybe you could get your artists to combine the three-five sprites on a single 2048x2848 png, crop with an plist (like for animation).
Whenever possible, load textures 'just in time', ie before they are required to be visible. If load time became an issue (perceivable lag at an inappropriate game circumstance), convert the texture to .pvr format (faster load), later to .pvr.gz (still faster load time, smaller app download size). PVR will cause some artefacts, so check with graphic people on your project before comiting.
Coming out of any level transition (either to some kind of menu or to some cut-scene), cleanup memory used by textures. This means that if the game logic requires returning to the main scene, reload textures just-in-time, during the transition.
Last ditch, change the depth setting before loading these large textures (code snippet below). Setting to RGBA444 will save 75% of the memory requirement, BUT, you take a hit on some graphic quality, especially for high saturation images. Once again, if you need to go there, get your graphics people to optimize the images to this depth, so they are satisfied with the result on device.
cleanup:
NSLog(#"GESprite<removeUnusedSprites> : before purging all caches");
[[CCTextureCache sharedTextureCache] dumpCachedTextureInfo];
[[CCSpriteFrameCache sharedSpriteFrameCache] removeUnusedSpriteFrames];
[[CCDirector sharedDirector] purgeCachedData];
NSLog(#"GESprite<removeUnusedSprites> : after purging all caches");
[[CCTextureCache sharedTextureCache] dumpCachedTextureInfo];
depth (in a CCSprite derivative class, some utility methods), you can do this anywhere anytime:
+(void) mediumPixelFormat{
[CCTexture2D setDefaultAlphaPixelFormat:kTexture2DPixelFormat_RGBA4444];
}
+(void) highPixelFormat{
[CCTexture2D setDefaultAlphaPixelFormat:kTexture2DPixelFormat_RGBA8888];
}