I am a bit confused about changing the texture of a CCSprite.
I have:
aTexture[NUM_WALLS+11] = [[CCTexture2D alloc]initWithImage:[UIImage imageNamed:#"shop1.png"]];
[aSprite setTexture:aTexture[NUM_WALLS+11]];
and
aTexture[NUM_WALLS+9] = [[CCTexture2D alloc]initWithImage:[UIImage imageNamed:#"bush2.png"]];
[aSprite setTexture:aTexture[NUM_WALLS+9]];
The two images have two different sizes. However, the sprite does NOT change size when I change the texture. Instead, the image scales to the size of the sprite. I thought the sprite was supposed to change size.
Can someone please clarify?
The image files may have a different size, but the textures may use the same size since they usually scale up to the next nearest power of two (with NPOT texture support being disabled by default in cocos2d).
Meaning: if one image is 150x150 and the other is 250x250 their textures will both have 256x256 dimensions. If you load a sprite from an image file, cocos2d adjusts the actual part of the texture that is drawn to the size of the image (contentSize). If you change the texture, cocos2d will simply use the size of the texture regardless of the size of the image stored in the texture - because that information is lost after a texture has been created.
In that case you will have to manually call setTextureRect: on the sprite to draw only the area of the actual image in the texture.
The better solution is to create a texture atlas with both textures and then just change the sprite frame displayed by the sprite. It's a lot easier and saves memory, too.
For those who still want to use setTexture.
I found that this here works:
CCTexture2D* tex2d = [[CCTexture2D alloc] initWithImage:[UIImage imageNamed:_texture]];
CGSize texSize = [tex2d contentSize];
[sprite setTexture:tex2d];
[sprite setTextureRect:CGRectMake(0, 0, texSize.width, texSize.height)];
the order of setTexture and setTextureRect is important. Otherwise it wouldn't work.
as far as i know, CCSprite use it's contentSize property and position to calculate array of vertices to be drawn using OpenGL. In such way, sprite will not change it's vizible size until you change it's contentSize property.
CCTexture2D* tex2d = [[CCTexture2D alloc] initWithImage:[UIImage
imageNamed:_texture]]; CGSize texSize = [tex2d contentSize]; [sprite
setTexture:tex2d]; [sprite setTextureRect:CGRectMake(0, 0,
texSize.width, texSize.height)];
WARNING
This code stores a texture but it may cause a memory leak because the dealloc function is never called.
You can check it by subclassing CCTexture2D class. At least alloc-init always works this wrong way for CCSprite objects
Related
The reason for the question is -- is it better to composite a texture at runtime (and make a sprite on that), or just use multiple sprites?
Example: Say you have a small image source for a repeating pattern, where you need many sprites to fill the view (as in a background).
A SKTexture is just image data. A SKSpriteNode is a display object.
The quick answer is no, you cannot draw a SKTexture to the screen without SKSpriteNode.
This answer goes over that limitation : How do you set a texture to tile in Sprite Kit
However, I wanted to answer to give you an option to achieve your ultimate goal.
What you could do is use an SKNode as a container for however many SKSpriteNodes you need to create your background. Then using the SKView method textureFromNode you can create one single SKTexture from that SKNode, that you can use to create a single SKSpriteNode for your background.
Hopefully upcoming version of SpriteKit for iOS 8 has some better tiling options.
Update
Also, in doing some research tonight, since I had a need for this same functionality, I found this :
http://spritekitlessons.wordpress.com/2014/02/07/tile-a-background-image-with-sprite-kit/
Which is doing similar to what I was pondering doing. Gonna copy the code here, in case that page ends up gone :
CGSize coverageSize = CGSizeMake(2000,2000); //the size of the entire image you want tiled
CGRect textureSize = CGRectMake(0, 0, 100, 100); //the size of the tile.
CGImageRef backgroundCGImage = [UIImage imageNamed:#"image_to_tile"].CGImage; //change the string to your image name
UIGraphicsBeginImageContext(CGSizeMake(coverageSize.width, coverageSize.height));
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextDrawTiledImage(context, textureSize, backgroundCGImage);
UIImage *tiledBackground = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
SKTexture *backgroundTexture = [SKTexture textureWithCGImage:tiledBackground.CGImage];
SKSpriteNode *backgroundTiles = [SKSpriteNode spriteNodeWithTexture:backgroundTexture];
backgroundTiles.yScale = -1; //upon closer inspection, I noticed my source tile was flipped vertically, so this just flipped it back.
backgroundTiles.position = CGPointMake(0,0);
[self addChild:backgroundTiles];
I setting up an OpenGL iOS project. I´m read a lot about the projection matrix an GluUnproject. I want to draw on an 3D model dynamically. Therefore I need the corresponding points from my window to the 3D object.
From GluUnproject I get a ray through my 3D scene. After that I can find collision point with iterative algorithms (raytracing)...
Now the problems:
How do I get the corresponding texture?
How do I get the corresponding vertices/pixels?
How can I write on that perspective texture/pixel?
How do I get the corresponding texture?
Getting the texture should be easy enough if you are using an object based approach to the objects in your scene. Just store a reference to the texture file name in the class and then iterate through your scene objects in your raycasting method, grabbing the texture name when you get a match.
How do I get the corresponding vertices/pixels?
Again this should be easy if you have used an object based approach for your object drawing (i.e an instantiation of a custom object class for each object in the scene). Assuming all your scene objects are in an NSMutableArray, you can just iterate through the array until you find a match on the raycasted object.
How can I write on that perspective texture/pixel?
If you are looking at writing text on a new texture one way of doing this is to use the layer of a UILabel as a texture (e.g see below), but if you are looking at drawing on an existing texture this is much more difficult (and to be honest to be avoided).
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, width, height)];
label.text = text;
lLabel.font = [UIFont fontWithName:#"Helvetica" size:12];
UIGraphicsBeginImageContext(standLabel.bounds.size);
CGContextTranslateCTM(UIGraphicsGetCurrentContext(), 0, height);
CGContextRef myContext = UIGraphicsGetCurrentContext();
// flip transform used to transform the coordinate system from origin for OpenGL.
CGAffineTransform flipTransform = CGAffineTransformConcat(CGAffineTransformMakeTranslation(0.f, height),
CGAffineTransformMakeScale(1.f, -1.f));
CGContextConcatCTM(myContext, flipTransform);
CGContextScaleCTM(UIGraphicsGetCurrentContext(), 1.0, -1.0);
[standLabel.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *layerImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
I am facing an unknown error since from yesterday. I am creating CCSprites or CCMenuItemImage but it set black background instead of background image. Following is my code, I know its fine because I used it before many times.
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"challenge_screen.plist"];
CCSprite *bg = [CCSprite spriteWithFile:#"ads.png"];
[bg setPosition:background.position];
// [bg setContentSize:CGSizeMake(100, 100)];
[self addChild:bg z:1000];
//CGSize windowSize = [[CCDirector sharedDirector] winSize];
CCMenuItemImage *coinMenuItem = [[CCMenuItemImage alloc] initWithNormalSprite:[CCSprite spriteWithSpriteFrame:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:#"coin.png"]] selectedSprite:nil disabledSprite:nil block:^(id sender)
{
NSLog(#"I am Tapped");
}];
coinMenuItem.position = ccp(100, 100);
CCMenu *mainMenu = [CCMenu menuWithItems:coinMenuItem, nil];
mainMenu.position = CGPointZero;
[self addChild:mainMenu];
Attached is screenshot.
Thanks in advance.
I am guessing that you are loading this sprite sheet (challenge_screen.plist and the associated texture file, which frequently is challenge_screen.png or challenge_screen.pvr.*) in a color mode that doesn't have transparency.
First, make sure that the associated texture file shows transparency itself. Maybe something messed with this particular texture.
Once you checked that, if the associated texture is .PNG. then you have to set the texture loading format in code like this. You have to set the texture format before loading the texture itself (the texture loads as a side effect of adding the SpriteFrames to the cache).
[CCTexture2D setDefaultAlphaPixelFormat:kCCTexture2DPixelFormat_RGBA4444];
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"challenge_screen.plist"];`
...
You can also try the kCCTexture2DPixelFormat_RGBA8888 mode if RGBA444 produces banding with your graphics and if you are good regarding free memory.
On the other hand, if the texture is a PVR.*, then the format in which the texture loads is embedded in the file, and setting the texture format in code doesn't make a difference. You will then need to regenerate your sprite sheet using the appropriate format (through TexturePacker or similar).
Is your background in the sprite sheet? If so try:
CCSprite *bg = [CCSprite spriteWithSpriteFrameName:#"ads.png"];
If it is the menu item and you know the code works, it must be an asset issue.
I am animating a whole body through SpriteSheets with CCSpriteBatchNode and CCSpriteFrameCache. Now user can add his own pic to that body which when i try to addChild to Spritesheet crashes with error "CCSprite is not using the same texture id"
Now i know the face CCSprite was not in that cache/texture(it was created through texturepacker) and the crash was normal but i wanted to know if there was a workaround to this as i have to add a face to that body through user interaction and animate that body. And by far using spritesheets is the best option for animation. anyone??
In this case what you can do is You take picture of user , then you make texture from user's image .
Then Add that texture to the CCTextureCache . Now you have texture of user image. Now you can use that texture in animation.
Make Texture from Sprite(You can make sprite from user image)
CCSprite *spr = nil;//your sprite
CCRenderTexture* renderTexture = [CCRenderTexture renderTextureWithWidth:spr.contentSize.width height:spr.contentSize.height];
spr.anchorPoint = ccp(0, 0);
spr.position = ccp(0, 0);
[renderTexture addChild:spr];
[renderTexture begin];
[spr draw]; // or [spr visit];
[renderTexture end];
CCTexture2D *result = renderTexture.sprite.texture;
Add that Texture in to Texture Cache.
[[CCTextureCache sharedTextureCache] addTexture]
When you create a CCSpriteBatchNode it is linked to a single texture. This is the point of a CCSpriteBatchNode: to draw different sprites that use the same texture to reduce OpenGL draw calls and increase efficiency.
The easiest workaround (if you have not reached a performance-critical point) would be to use a regular CCLayer instead of a CCSpriteBatchNode.
If you still want to add different CCSprites (say, the body, the limbs and the head of your character) to the same CCSpriteBatchNode, the you need to build a single sprite sheet (or texture pack) which contains all the body parts that you need to add to the CCSpriteBatchNode. This single sprite sheet will be the only one that the CCSpriteBatchNode will use. You won't be able to add CCSprites that are not using this sprite sheet.
You can not Manually add CCSprite to SpriteSheets. Because When you create animated Sprite using texturepacker then i hope that you know it also created with .plist file and it loas image from it with its size.
When you add manually CCSprite then it is nor found from SpriteFramesWithFile. may be you got error.
another way for add animated CCSprite without use of texturepacker
CCSprite *dog = [CCSprite spriteWithFile:#"dog1.gif"];
dog.position = ccp(winSize.width/2, winSize.height/2);
[self addChild:dog z:2];
NSMutableArray *animFrames = [NSMutableArray array];
for( int i=1;i<=5;i++)
{
NSString* file = [NSString stringWithFormat:#"dog%d.gif", i];
CCTexture2D* texture = [[CCTextureCache sharedTextureCache] addImage:file];
CGSize texSize = texture.contentSize;
CGRect texRect = CGRectMake(0, 0, texSize.width, texSize.height);
CCSpriteFrame* frame = [CCSpriteFrame frameWithTexture:texture rect:texRect];
[animFrames addObject:frame];
}
CCAnimation * animation = [CCAnimation animationWithSpriteFrames:animFrames];
animation.delayPerUnit = 0.07f;
animation.restoreOriginalFrame = YES;
CCAnimate *animAction = [CCRepeatForever actionWithAction:[CCAnimate actionWithAnimation:animation]];
[dog runAction:animAction];
Above code is just describe that how can you add animated CCSprite without use of texturepacker
Here you can also change array of Images so you may be add manually image also.
I have recently switched from using separate resource files to using a texture atlas. I experimented with replacing a few [CCSprite spriteWithFile] with [CCSprite spriteWithSpriteFrameName]. This works fine except one thing. The CCSprite's texture dimensions are incorrect. Here is my code :
CGSize screenSize = [[CCDirector sharedDirector]winSize];
CCSprite * leftArrow = [CCSprite spriteWithSpriteFrameName:#"smallLeftArrow.png"];
CGSize arrowSize = [leftArrow texture].contentSizeInPixels;
CCSprite * selectedLA = [CCSprite spriteWithSpriteFrameName:#"smallLeftArrow.png"];
selectedLA.opacity = 100;
CCMenuItem * leftArrowItem = [CCMenuItemSprite itemFromNormalSprite:leftArrow selectedSprite:selectedLA target:[HelloWorldLayer sharedHelloWorldLayer] selector:#selector(doLeft)];
leftArrowItem.position = ccp(- arrowSize.width, arrowSize.height);
CCLOG(#"arrow.width = %f arrow.height %f",arrowSize.width, arrowSize.height);
This is the output on the debugger:
2011-08-29 18:27:04.239 Zero Gravity Combat[454:307] arrow.width = 1024.000000 arrow.height 1024.000000
The size of the entire texture atlas is 1024 X 1024. I am using texture packer to create the texture atlas. Is there a way to fix this or do I need to create a texture manually to determine dimensions?
You obviously want to get the size of the arrow sprite. Thats simple: leftArrow.contentSize.
What you've done is getting the size of the used texture, which is of size 1024x1024. The size of the texture is not always the same size as the sprite's size.