Instantly share code, notes, and snippets.

Embed
What would you like to do?
A basic nine-grid sprite node for Cocos2D. You set the contentSize property of the sprite and it will do the layout automatically. The layout also respects the anchorPoint property so it's easy to align the sprites however you want.
CCSprite *sprite = [CCSprite spriteWithFile:@"button9grid.png"];
NGNineGridSprite *nineGrid = [NGNineGridSprite spriteWithSpriteFrame:sprite.displayFrame andCornerSize:CGSizeMake(10, 10)];
nineGrid.position = ccp(160, 240);
nineGrid.anchorPoint = ccp(0.5, 0.5);
nineGrid.contentSize = CGSizeMake(150, 150);
[self addChild:nineGrid];
//
// NGNineGridSprite.h
// Created by Nick Gravelyn
//
// Distributed under terms of MIT License:
// http://www.opensource.org/licenses/mit-license.php
//
#import <Foundation/Foundation.h>
#import "cocos2d.h"
@interface NGNineGridSprite : CCNode
- (id)initWithSpriteFrame:(CCSpriteFrame *)frame andCornerSize:(CGSize)cornerSize;
+ (NGNineGridSprite *)spriteWithSpriteFrame:(CCSpriteFrame *)frame andCornerSize:(CGSize)cornerSize;
@end
//
// NGNineGridSprite.m
// Created by Nick Gravelyn
//
// Distributed under terms of MIT License:
// http://www.opensource.org/licenses/mit-license.php
//
#import "NGNineGridSprite.h"
@interface NGNineGridSprite ()
{
CGSize _contentSize;
CGSize _cornerSize;
CCSprite *corners[4];
CCSprite *horizontalEdges[2];
CCSprite *verticalEdges[2];
CCSprite *center;
}
- (void)updateChildScales;
- (void)updateChildPositions;
@end
@implementation NGNineGridSprite
- (id)initWithSpriteFrame:(CCSpriteFrame *)frame andCornerSize:(CGSize)cornerSize
{
if (self = [super init])
{
_contentSize = CGSizeMake(0, 0);
_cornerSize = cornerSize;
// we're going to put all the pieces into a batch node so all 9 pieces are drawn at once
CCSpriteBatchNode *batchNode = [CCSpriteBatchNode batchNodeWithTexture:frame.texture capacity:9];
[self addChild:batchNode];
// temporary rect variable used all over during construction
CGRect tempRect;
// create the four corner sprites
tempRect = CGRectMake(frame.rect.origin.x, frame.rect.origin.y, cornerSize.width, cornerSize.height);
corners[0] = [CCSprite spriteWithTexture:frame.texture rect:tempRect];
corners[0].anchorPoint = ccp(0, 1);
corners[0].position = ccp(0, frame.rect.size.height);
[batchNode addChild:corners[0]];
tempRect.origin = CGPointMake(frame.rect.origin.x + frame.rect.size.width - cornerSize.width,
frame.rect.origin.y);
corners[1] = [CCSprite spriteWithTexture:frame.texture rect:tempRect];
corners[1].anchorPoint = ccp(1, 1);
corners[1].position = ccp(frame.rect.size.width, frame.rect.size.height);
[batchNode addChild:corners[1]];
tempRect.origin = CGPointMake(frame.rect.origin.x,
frame.rect.origin.y + frame.rect.size.height - cornerSize.height);
corners[2] = [CCSprite spriteWithTexture:frame.texture rect:tempRect];
corners[2].anchorPoint = ccp(0, 0);
corners[2].position = ccp(0, 0);
[batchNode addChild:corners[2]];
tempRect.origin = CGPointMake(frame.rect.origin.x + frame.rect.size.width - cornerSize.width,
frame.rect.origin.y + frame.rect.size.height - cornerSize.height);
corners[3] = [CCSprite spriteWithTexture:frame.texture rect:tempRect];
corners[3].anchorPoint = ccp(1, 0);
corners[3].position = ccp(frame.rect.size.width, 0);
[batchNode addChild:corners[3]];
// create the horizontal edges
tempRect = CGRectMake(frame.rect.origin.x + cornerSize.width, frame.rect.origin.y,
frame.rect.size.width - (cornerSize.width * 2), cornerSize.height);
horizontalEdges[0] = [CCSprite spriteWithTexture:frame.texture rect:tempRect];
horizontalEdges[0].anchorPoint = ccp(0, 1);
horizontalEdges[0].position = ccp(cornerSize.width, frame.rect.size.height);
[batchNode addChild:horizontalEdges[0]];
tempRect.origin = CGPointMake(frame.rect.origin.x + cornerSize.width, frame.rect.origin.y + frame.rect.size.height - cornerSize.height);
horizontalEdges[1] = [CCSprite spriteWithTexture:frame.texture rect:tempRect];
horizontalEdges[1].anchorPoint = ccp(0, 0);
horizontalEdges[1].position = ccp(cornerSize.width, 0);
[batchNode addChild:horizontalEdges[1]];
// create the vertical edges
tempRect = CGRectMake(frame.rect.origin.x, frame.rect.origin.y + cornerSize.height,
cornerSize.width, frame.rect.size.height - (cornerSize.height * 2));
verticalEdges[0] = [CCSprite spriteWithTexture:frame.texture rect:tempRect];
verticalEdges[0].anchorPoint = ccp(0, 0);
verticalEdges[0].position = ccp(0, cornerSize.height);
[batchNode addChild:verticalEdges[0]];
tempRect.origin = CGPointMake(frame.rect.origin.x + frame.rect.size.width - cornerSize.width, frame.rect.origin.y + cornerSize.height);
verticalEdges[1] = [CCSprite spriteWithTexture:frame.texture rect:tempRect];
verticalEdges[1].anchorPoint = ccp(1, 0);
verticalEdges[1].position = ccp(frame.rect.size.width, cornerSize.height);
[batchNode addChild:verticalEdges[1]];
// create the center
tempRect = CGRectMake(frame.rect.origin.x + cornerSize.width, frame.rect.origin.y + cornerSize.height,
frame.rect.size.width - (cornerSize.width * 2), frame.rect.size.height - (cornerSize.height * 2));
center = [CCSprite spriteWithTexture:frame.texture rect:tempRect];
center.anchorPoint = ccp(0, 0);
center.position = ccp(cornerSize.width, cornerSize.height);
[batchNode addChild:center];
// set up default size which handles all scaling and positioning of the sprites
self.contentSize = frame.rect.size;
}
return self;
}
- (void)setContentSize:(CGSize)contentSize
{
// always maintain integer size
contentSize.width = roundf(contentSize.width);
contentSize.height = roundf(contentSize.height);
// if the content size is different, store it and update both scale and position of children
if (_contentSize.width != contentSize.width || _contentSize.height != contentSize.height)
{
_contentSize = contentSize;
[self updateChildScales];
[self updateChildPositions];
}
}
- (CGSize)contentSize
{
return _contentSize;
}
- (void)setAnchorPoint:(CGPoint)anchorPoint
{
// get the old anchor point
CGPoint oldAnchorPoint = self.anchorPoint;
// if anchor point changed, store it and update position of children
if (oldAnchorPoint.x != anchorPoint.x || oldAnchorPoint.y != anchorPoint.y)
{
[super setAnchorPoint:anchorPoint];
[self updateChildPositions];
}
}
- (void)updateChildScales
{
// adjust the width to account for corners
int adjustedWidth = _contentSize.width - (_cornerSize.width * 2);
// adjust the X scale of the horizontal edges and center
horizontalEdges[0].scaleX = adjustedWidth / horizontalEdges[0].contentSize.width;
horizontalEdges[1].scaleX = adjustedWidth / horizontalEdges[1].contentSize.width;
center.scaleX = adjustedWidth / center.contentSize.width;
// adjust the height to account for corners
int adjustedHeight = _contentSize.height - (_cornerSize.height * 2);
// adjust the Y scale of the vertical edges and center
verticalEdges[0].scaleY = adjustedHeight / verticalEdges[0].contentSize.height;
verticalEdges[1].scaleY = adjustedHeight / verticalEdges[1].contentSize.height;
center.scaleY = adjustedHeight / center.contentSize.height;
}
- (void)updateChildPositions
{
// use the anchor to adjust all positions
CGPoint anchorShift = CGPointMake(-self.anchorPoint.x * _contentSize.width, -self.anchorPoint.y * _contentSize.height);
// update all the corners
corners[0].position = ccpAdd(ccp(0, _contentSize.height), anchorShift);
corners[1].position = ccpAdd(ccp(_contentSize.width, _contentSize.height), anchorShift);
corners[2].position = ccpAdd(ccp(0, 0), anchorShift);
corners[3].position = ccpAdd(ccp(_contentSize.width, 0), anchorShift);
// update the horizontal edges
horizontalEdges[0].position = ccpAdd(ccp(_cornerSize.width, _contentSize.height), anchorShift);
horizontalEdges[1].position = ccpAdd(ccp(_cornerSize.width, 0), anchorShift);
// update the vertical edges
verticalEdges[0].position = ccpAdd(ccp(0, _cornerSize.height), anchorShift);
verticalEdges[1].position = ccpAdd(ccp(_contentSize.width, _cornerSize.height), anchorShift);
// update the center
center.position = ccpAdd(ccp(_cornerSize.width, _cornerSize.height), anchorShift);
}
+ (NGNineGridSprite *)spriteWithSpriteFrame:(CCSpriteFrame *)frame andCornerSize:(CGSize)cornerSize
{
return [[[NGNineGridSprite alloc] initWithSpriteFrame:frame andCornerSize:cornerSize] autorelease];
}
@end
@nickgravelyn

This comment has been minimized.

Owner

nickgravelyn commented May 29, 2012

Some more details on the class:

  • Rotations aren't supported because I made this for my UI and my UI doesn't rotate.
  • The type takes in a CCSpriteFrame because while you may pass in a full texture (like my example) you may also have the sprite in a texture atlas. CCSpriteFrame seemed a good solution for getting all the necessary data.
  • I haven't severely tested this other than having the content size waver based on sin/cos waves and setting anchor point to (0.5, 0.5) and (0, 0) and observing the results. Let me know if you find bugs. :)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment