Rotating, scaling Cocos2d inherited layer with 2 sprites as children - objective-c

Straight to the point, I have a class which is basically inherited from CCLayer and has 2 sprites as children.
some_layer.h
#interface some_layer : CCLayer {
CCSprite *back;
CCSprite *front;
}
some_layer.mm
// on "init" you need to initialize your instance
-(id) init
{
if( (self=[super init])) {
back = [CCSprite spriteWithFile:#"back.png"];
front = [CCSprite spriteWithFile:#"front.png"];
[front setOpacity:0];
[self addChild:back];
[self addChild:front];
//this is after modification
back.positionInPixels = CGPointMake(winSizeInPixels.width/2,winSizeInPixels.height/2);
front.positionInPixels = CGPointMake(winSizeInPixels.width/2,winSizeInPixels.height/2);
}
return self;
}
//after the edit
-(void) setPositionInPixels:(CGPoint)positionInPixels {
[super setPositionInPixels:CGPointMake(positionInPixels.x -(winSizeInPixels.width/2), positionInPixels.y -(winSizeInPixels.height/2))];
}
-(void) setPosition:(CGPoint)position {
[super setPosition:CGPointMake(position.x -(winSize.width/2), position.y -(winSize.height/2))];
}
Of course that is not all the implementation of the layer, but that code is the only one related to the problem, let me know if you think you need more.
Now in some part of the parent layer am trying to do this
float x = winSizeInPixels.width/2;
float y = winSizeInPixels.height/2;
[self setPositionInPixels:CGPointMake(x, y)];
mylayer = [[some_layer alloc] init];
[mylayer setPositionInPixels:CGPointMake(-offset, -offset)];
[self addChild:mylayer];
[mylayer runAction:[CCEaseInOut actionWithAction:[CCRotateTo actionWithDuration:speed*20 angle:angle] rate:4]];
The output of this code, is that I can see the sprites rotating, but not around the center of some_layer, I want them to rotate and stay in their place (rotate around themselves), what am trying to do is rotate, scale them at the same time while keeping their positions the same on screen,
The sprites are keeping in rotating around some random point ( Maybe around parent of their parent which is self in the last code )
BTW, I'm not touching the sprites back and front positions, I only deal with that "some_layer", I tried setting their positions to (0,0)
The only time the position of "some_layer" is correct is when it's rotation angle is 0, and it's scale is 1.0, if there's something unclear please let me know, thanks a lot, I can provide some screen shots
Edit : I modified the code above, it works properly as a workaround, but I think this is not the right way of doing it!!! Please check the comments, I don't know what's strange happening!!
Thank you!
Edit 2: Answer found
Answer to my self
Add this to the init method of some_layer.mm
[self setContentSize:CGSizeMake(0, 0)];
Thanks for everyone who tried to help me :)
Analysing the code is the best solution!
BTW I deleted the overridden methodes for both setPos and deleted the code that changes the Sprites positions, it's like this now
// on "init" you need to initialize your instance
-(id) init
{
if( (self=[super init])) {
back = [CCSprite spriteWithFile:#"back.png"];
front = [CCSprite spriteWithFile:#"front.png"];
[front setOpacity:0];
[self addChild:back];
[self addChild:front];
[self setContentSize:CGSizeMake(0, 0)];
}
return self;
}

Are you setting some anchorPoint for the Layer or Sprite which you are using. if yes then please check that the anchor point should be set at center :
someSprite.anchorPoint=ccp(0.5,0.5);
For more details : http://www.qcmat.com/understanding-anchorpoint-in-cocos2d/

Related

Xcode, SpriteKit: Rotating a whole scene, and editing properties of a sprite inside an array

I'm using Xcode, SpriteKit to create a game. 2 questions:
1) I need the whole scene to rotate whenever a function is called:
- (void)mouseClick {
// RotateWholeScene somehow
}
I know about rotating a sprite by changing its zrotation, but changing scene.zrotation doesn’t work.
Also, some things in the scene need to stay in the same position, like the score, etc.
I’d prefer it if there is a way which I can rotate the whole scene as if the camera view has changed, as there are calculations (like falling objects) which would be much easier to do if they aren’t needed to be changed in the code.
2) Right now the program creates a sprite each frame, and places the sprite into an array (which I declared at the start).
NSMutableArray *SomeArray = [NSMutableArray arrayWithObjects: nil];
And later on in the code:
- (void)CalledEachFrame {
[self addChild: SomeSprite];
[SomeArray addObject: SomeSprite];
}
Elsewhere in the code, I need:
- (void)AlsoCalledEachFrame {
for (int i; i < X; i++) {
SomeArray[i].position = A;
}
}
This (and several other attempts) doesn't work, and I get an error:
***EDIT: My bad. In the image, I showed, I set the position to an integer rather than a CGPoint. Nevertheless, the same error occur even if I put a CGPoint instead of those 99s.
You asking two questions at once here. I should discourage that. Nevertheless, here goes:
1) I faced a similar problem and my solution to this problem was putting the scene nodes and the hud nodes each in a separate child nodes of the SKScene subclass, like so:
#interface SKSceneSubclass : SKScene
// ...
#property (strong, nonatomic, readonly) SKNode *sceneContents;
#property (strong, nonatomic, readonly) SKNode *hud;
#end
In the scene initializer, I'd put:
- (instancetype)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
self.sceneContents = [SKNode new];
[self addChild:self.sceneContents];
self.hud = [SKNode new];
[self addChild:self.hud];
self.hud.zPosition = 20.f; // ensures the hud on top of sceneContents
// your custom stuff
// ...
}
return self;
}
This requires you to decide upon adding child nodes which node (sceneContents or hud) will be their parent.
[self.sceneContents addChild:newSprite];
[self.hud addChild:someCoolHudButton];
Then, you can just run [SKAction rotateByAngle:angle duration:duration] on the sceneContents node. The hud and its children will not be affected. I am currently employing this method to move and zoom sceneContents while having a stationary HUD which behaves as it should.
2) The problem is that you are not telling the compiler the item you're accessing is an SKNode object, so the compiler does not allow you to access the position property. If you are certain only the array will only contain SKNodes or subclasses thereof, use the forin iterator method.
for (<#type *object#> in <#collection#>) {
<#statements#>
}
In your case, it would look like this.
CGPoint newPosition = CGPointMake(x, y);
for (SKNode *node in SomeArray) {
node.position = newPosition;
}
You can manually rotate the view with this code:
CGAffineTransform transform = CGAffineTransformMakeRotation(-0.1); // change value to desires angle
self.view.transform = transform;

Background position alternating position every time I build in Xcode 5

I am just beginning programming games in Xcode 5 using cocos2D and found this pretty strange. I'm starting out fresh on a menu scene and was importing a background and a button. The following code positions my background just fine sometimes, but then other times it's adjusted upwards about 50 pixels (my simulator is on it's side, or it's length is lying horizontal, so technically it's shifting about -50 pixels in the "width" direction, although to the simulator it shifts upwards).
Note I found that every time I run my program, it alternates between being properly aligned and shifted. Why would this be happening ugh! Below is the code I'm using.
Note 2 I'm using Kobold2D and the framework I'm using has a config.lua that's a little beyond my scope for me to understand everything. The config.lua code is located here http://snipt.org/BEt6
-(id) init
{
if ((self = [super init]))
{
CCSprite *sprite = [CCSprite spriteWithFile:#"background.png"];
sprite.anchorPoint = CGPointZero;
[self addChild:sprite z:-1];
sprite = [CCSprite spriteWithFile:#"button.png"];
sprite.anchorPoint = CGPointZero;
sprite.position = CGPointMake(200,200);
[self addChild:sprite z:0];
}
return self;
}
The only problem with your code is that you are initializing sprite twice, which is NOT very good.
I would think it is causing your problem.
Try this code:
-(id) init
{
if ((self = [super init]))
{
CCSprite *sprite = [CCSprite spriteWithFile:#"background.png"];
sprite.anchorPoint = CGPointZero;
[self addChild:sprite z:-1];
CCSprite *sprite2 = [CCSprite spriteWithFile:#"button.png"];
sprite2.anchorPoint = CGPointZero;
sprite2.position = CGPointMake(200,200);
[self addChild:sprite2 z:0];
}
return self;
}

I just want to have dofferentcolor iTems on my UITabbar How I can make my colored UITabBar

Please help me I want to make a colored UITabBar for Each Items I need differentColor.
As explained in this post on Stack Overflow, subclass UITabBarController, then override the -(void)viewDidLoad method. Here's an example:
- (void)viewDidLoad {
[super viewDidLoad];
// Alternate between red and blue. Obviously this is a pretty terrible example,
// since the number of items shouldn't be a fixed integer. Replace this
// with whatever you want to do with your UITabBarItems.
int colorIndex = 0;
NSArray *colorArray = [NSArray arrayWithObjects:[UIColor redColor], [UIColor blueColor], nil];
for (int itemIndex = 0; itemIndex < 3; itemIndex++) {
// Create a frame for a subview which will overlap over the UITabBarItem.
CGRect frame = CGRectMake((self.tabBar.bounds.size.width/2.0)*itemIndex,
0.0,
self.tabBar.bounds.size.width/2.0,
self.tabBar.bounds.size.height);
// Initialize a UIView using the above frame.
UIView *v = [[UIView alloc] initWithFrame:frame];
// Cycle through the colors.
if (++colorIndex >= [colorArray count]) {
colorIndex = 0; // Keep colorIndex from going out of bounds.
}
// Set alpha to 0.5 so OP can see the view overlaps over the original UITabBarItem.
[v setAlpha:0.5];
[v setBackgroundColor:[colorArray objectAtIndex:colorIndex]];
// Add subview to UITabBarController view.
[[self tabBar] addSubview:v];
// v is retained by the parent view, so release it.
[v release];
}
}
Also, you should know that it's good practice to search the answers here on Stack Overflow before asking a new question. You could have made a lot of progress on your own using the link above. You should also post code detailing what you've tried so far--people will be more inclined to help you if you do.
I posted my code because I am still a beginner myself and I enjoyed the challenge presented by your question. If anyone has any suggestions for my code, please comment!
Anyway, hope this helps!

Stop drawing of CATiledLayer

Is is possible to stop CATiledLayer to draw (drawLayer:inContext)?
It draws asynchronously and when i try to release CGPDFDocumentRef, which is used by CATiledLayer, the app crashes (EXC_BAD_ACCESS).
That's my view:
#implementation TiledPDFView
- (id)initWithFrame:(CGRect)frame andScale:(CGFloat)scale{
if ((self = [super initWithFrame:frame])) {
CATiledLayer *tiledLayer = (CATiledLayer *)[self layer];
tiledLayer.levelsOfDetail = 4;
tiledLayer.levelsOfDetailBias = 4;
tiledLayer.tileSize = CGSizeMake(512.0, 512.0);
myScale = scale;
}
return self;
}
// Set the layer's class to be CATiledLayer.
+ (Class)layerClass {
return [CATiledLayer class];
}
- (void)stopDrawing{
CATiledLayer *tiledLayer = (CATiledLayer *)[self layer];
[tiledLayer removeFromSuperlayer];
tiledLayer.delegate = nil;
}
// Set the CGPDFPageRef for the view.
- (void)setPage:(CGPDFPageRef)newPage
{
CGPDFPageRelease(self->pdfPage);
self->pdfPage = CGPDFPageRetain(newPage);
//self->pdfPage = newPage;
}
-(void)drawRect:(CGRect)r
{
}
// Draw the CGPDFPageRef into the layer at the correct scale.
-(void)drawLayer:(CALayer*)layer inContext:(CGContextRef)context
{
// First fill the background with white.
CGContextSetRGBFillColor(context, 1.0,1.0,1.0,1.0);
CGContextFillRect(context,self.bounds);
CGContextSaveGState(context);
// Flip the context so that the PDF page is rendered
// right side up.
CGContextTranslateCTM(context, 0.0, self.bounds.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
// Scale the context so that the PDF page is rendered
// at the correct size for the zoom level.
CGContextScaleCTM(context, myScale,myScale);
CGContextDrawPDFPage(context, pdfPage);
CGContextRestoreGState(context);
}
// Clean up.
- (void)dealloc {
CGPDFPageRelease(pdfPage);
[super dealloc];
}
And this is where i try to stop and release PDF in view controller:
v is instance of TiledPDFView
-(void) stopDwaring {
[v stopDrawing];
[v removeFromSuperview];
[v release];
[self.view removeFromSuperview];
self.view = nil;
CGPDFDocumentRelease(pdf);
}
this post helped me solving my own trouble with CATiledLayer. I used TiledPDFview.m from Apple's documentation as example.
Since I need to redraw the entire view and all tiles at some point, I use a CATiledLayer as property.
When exiting and deallocating the viewcontroller, it crashed with [CATiledLayer retain]: Message sent to deallocated instance.
Here is my dealloc method of the view controller:
- (void)dealloc {
self.tiledLayer.contents=nil;
self.tiledLayer.delegate=nil;
[self.tiledLayer removeFromSuperlayer];
// note: releasing the layer still crashes-
// I guess removeFromSuperlayer releases it already,
// but couldn't find documentation so far.
// So that's why it's commented out:
// [self.tiledLayer release], self.tiledLayer=nil;
//release the other viewcontroller stuff...
[super dealloc];
}
That works for me. Hope it helps someone.
Remove the CATiledLayer from its superlayer before releasing the CGPDFDocumentRef.
[yourTiledLayer removeFromSuperlayer];
Dont forget to set it's delegate to nil too.
yourTiledLayer.delegate = nil;
After that, you can safely release your CGPDFDocumentRef.
Edit after OP adds code:
Did you get pdfPage using CGPDFDocumentGetPage()? If so, you shouldn't release it, it is an autoreleased object.
Regarding how to add it as sublayer:
You don't actually need TiledPDFView. In your view controller, you can simply do this:
CATiledLayer *tiledLayer = [CATiledLayer layer];
tiledLayer.delegate = self; //sets where tiledLayer will look for drawLayer:inContext:
tiledLayer.tileSize = CGSizeMake(512.0f, 512.0f);
tiledLayer.levelsOfDetail = 4;
tiledLayer.levelsOfDetailBias = 4;
tiledLayer.frame = CGRectIntegral(CGRectMake(0.0f, 0.0f, 512.0f, 512.0f));
[self.view.layer addSublayer:tiledLayer];
Then move your drawLayer:inContext: implementation to your view controller.
Then in your view controller's dealloc, release it as:
[tiledLayer removeFromSuperlayer];
tiledLayer.delegate = nil;
CGPDFDocumentRelease(pdf);
Note that you can't do this on a UIView subclass, as the drawLayer:inContext: will conflict with the UIView's main layer.
object.layer.contents = Nil
This should wait for the thread to finish. It helped in my case.
TiledPDFView *pdfView;
In dealloc of pdfView's superview class, write below line of codes.
- (void)dealloc {
if (nil != self.pdfView.superview) {
self.pdfView.layer.delegate = nil;
[self.pdfView removeFromSuperview];
}
}
This works for me. Hope it will help.
I've had a similar problem.
I ended up setting a float variable "zoom" in my TiledPDFView which I set as the zoomScale of PDFScrollView in the UIScrollview Delegate method: scrollViewDidZoom
Then in my drawLayer method inside TiledPDFView I only called the contents of that method if the float variable "zoom" was above 2.
This fixes any issues of someone leaving the view without zooming. It may not be ideal for your case as this error still occurs if someone zooms above 2 then releases the viewcontroller quickly, but you might be able to find a similar technique to cover all bases.
It looks like you're doing the same thing I am, which is borrowing the ZoomingPDFView code and integrating it into your project. If your UIScrollView delegate methods in PDFScrollView are unchanged, you can solve your problem by just commenting both lines of your dealloc method (in TiledPDFView). That stuff should only be happening when you kill the parent view, anyway.

Displaying some text on the screen using the Framework Cocos2D

I want to display a text on the screen using the framework Cocos2D.
I'm thinking off using the draw method. But I don't know the exact way of doing that.
I would be glad if anyone could help me on this topic.
The simplest way would be to create a CCLabelTTF node and add it to your scene.
Here is a possible way :
This code is within a simple scene class :
// on "init" you need to initialize your instance
-(id) init
{
// always call "super" init
// Apple recommends to re-assign "self" with the "super's" return value
if( (self=[super init]) )
{
// create and initialize a Label
CCLabelTTF *label = [CCLabelTTF labelWithString:#"Hello World" fontName:#"Marker Felt" fontSize:64];
// ask director for the window size
CGSize size = [[CCDirector sharedDirector] winSize];
// position the label on the center of the screen
label.position = ccp( size.width /2 , size.height/2 );
// add the label as a child to this Layer
[self addChild: label];
}
return self;
}
Hope it can help you !