Cocos2D-iPhone: ShadowPaths ala Core Animation - core-animation

In core animation it is possible to put a shadow round a CGPathRef, while maintaining very high graphics performance.
if (self.displayShadow)
{
self.shadowLayer = [CALayer layer];
self.shadowLayer.shadowPath = self.pOutline;
self.shadowLayer.shadowOpacity = 1.0;
self.shadowLayer.zPosition = -1.;
self.shadowLayer.position = self.center;
self.shadowLayer.shadowOffset = CGSizeMake(+3., +3.);
[self.layer addSublayer: self.shadowLayer];
}
How does it do this? And is it possible to get the same behaviour from Cocos2D?

Create a subclass of CCSprite and create a dark shadow sprite shaped like your sprite.
Create an instance of the subclassed CCSprite and add the shadow as a child to the sprite.
Override the "visit" method with:
-(void)visit{
if(shadow!=NULL){
int cx = self.contentSize.width / 2;
int cy = self.contentSize.height / 2;
float rot = (int)self.rotation%360 + 30;
float px = cx + SHADOW_OFFSET*sinf(rot*M_PI / 180.0);
float py = cy - SHADOW_OFFSET*cosf(rot*M_PI / 180.0);
shadow.position = CGPointMake(px, py);
}
[super visit];
}

Related

How to draw a sine line using SpriteKit?

This question is almost self-explanatory: I need to use SpriteKit to draw a line that looks like a sine wave, but I will also need to vary the amplitude of this wave later.
The basic steps...1) Create an SKShapeNode, 2) Generate a sinusoid CGPath, and 3) Assign the CGPath to the shape node's path attribute
-(void)didMoveToView:(SKView *)view {
self.scaleMode = SKSceneScaleModeResizeFill;
// Create an SKShapeNode
SKShapeNode *node = [SKShapeNode node];
node.position = CGPointMake(300.0, 300.0);
// Assign to the path attribute
node.path = [self sineWithAmplitude:20.0 frequency:1.0 width:200.0
centered:YES andNumPoints:32];
[self addChild:node];
}
// Generate a sinusoid CGPath
- (CGMutablePathRef)sineWithAmplitude:(CGFloat)amp frequency:(CGFloat)freq
width:(CGFloat)width centered:(BOOL)centered
andNumPoints:(NSInteger)numPoints {
CGFloat offsetX = 0;
CGFloat offsetY = amp;
// Center the sinusoid within the shape node
if (centered) {
offsetX = -width/2.0;
offsetY = 0;
}
CGMutablePathRef path = CGPathCreateMutable();
// Move to the starting point
CGPathMoveToPoint(path, nil, offsetX, offsetY);
CGFloat xIncr = width / (numPoints-1);
// Construct the sinusoid
for (int i=1;i<numPoints;i++) {
CGFloat y = amp * sin(2*M_PI*freq*i/(numPoints-1));
CGPathAddLineToPoint(path, nil, i*xIncr+offsetX, y+offsetY);
}
return path;
}

UIScrollVIew with UIImageView, zoomed image sitck to right edge

I have made UIScrollView with three UIScrollViews inside and UImageView inside those three scollviews (Like in this answer: https://stackoverflow.com/a/25768875/2911156). Almost everything works, but zooming in landscape mode is broken. When user zoom in image, after zooming is finished, image is moved to the right edge and cut. How to center image after zoom, or what is wrong here?
UI Structure:
Main Scroll view Constraints:
Image View constraint (all three had same pattern)
I'm not doing much in code (ScrollViews and ImageViews are based on autolayout), just done this:
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
return _photoImage;
}
and this:
- (void) setMaxMinZoomScaleForBonds {
self.contentInset = UIEdgeInsetsZero; //need to reset it before load image, because previous insets can be wrong
self.minimumZoomScale = 1.0;
self.zoomScale = 1.0;
self.maximumZoomScale = 2*[UIScreen mainScreen].scale;
}
One more thing done, but it does not work:
- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(CGFloat)scale {
//center image when zooming.
CGFloat x = 0;
CGFloat y = 0;
CGFloat screenWidth = [[UIScreen mainScreen] bounds].size.width;
CGFloat screenHight = [[UIScreen mainScreen] bounds].size.height;
CGFloat viewWidth = view.frame.size.width;
CGFloat viewHight = view.frame.size.height;
if(viewWidth < screenWidth)
x = screenWidth/ 2;
if(viewHight < screenHight)
y = screenHight / 2 ;
if(scale<=1.1)
self.contentInset = UIEdgeInsetsZero;
else
self.contentInset = UIEdgeInsetsMake(y, x, y, x);
}
That is all related to zoom action.

CATiledLayers on OS X

This has been driving me crazy.. I have a large image, and need to have a view that is both zoomable, and scrollable (ideally it should also be able to rotate, but I've given up on that part). Since the image is very large, I plan on using CATiledLayer, but I simply can't get it to work.
My requirements are:
I need to be able to zoom (on mouse center) and pan
The image should not change its width:height ratio (shouldn't resize, only zoom).
This should run on Mac OS 10.9 (NOT iOS!)
Memory use shouldn't be huge (although up to like 100 MB should be ok).
I have the necessary image both complete in one file, and also tiled into many (even have it for different zoom levels). I prefer using the tiles, as that should be easier on memory, but both options are available.
Most of the examples online refer to iOS, and thus use UIScrollView for the zoom/pan, but I can't get to copy that behaviour for NSScrollView. The only example for Mac OS X I found is this, but his zoom always goes to the lower left corner, not the middle, and when I adapt the code to use png files instead of pdf, the memory use gets around 400 MB...
This is my best try so far:
#implementation MyView{
CATiledLayer *tiledLayer;
}
-(void)awakeFromNib{
NSLog(#"Es geht los");
tiledLayer = [CATiledLayer layer];
// set up this view & its layer
self.wantsLayer = YES;
self.layer = [CALayer layer];
self.layer.masksToBounds = YES;
self.layer.backgroundColor = CGColorGetConstantColor(kCGColorWhite);
// set up the tiled layer
tiledLayer.delegate = self;
tiledLayer.levelsOfDetail = 4;
tiledLayer.levelsOfDetailBias = 5;
tiledLayer.anchorPoint = CGPointZero;
tiledLayer.bounds = CGRectMake(0.0f, 0.0f, 41*256, 22*256);
tiledLayer.autoresizingMask = kCALayerNotSizable;
tiledLayer.tileSize = CGSizeMake(256, 256);
self.frame = CGRectMake(0.0f, 0.0f, 41*256, 22*256);
self.layer = tiledLayer;
//[self.layer addSublayer:tiledLayer];
[tiledLayer setNeedsDisplay];
}
-(void)drawRect:(NSRect)dirtyRect{
CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort];
CGFloat scale = CGContextGetCTM(context).a;
CGSize tileSize = tiledLayer.tileSize;
tileSize.width /= scale;
tileSize.height /= scale;
// calculate the rows and columns of tiles that intersect the rect we have been asked to draw
int firstCol = floorf(CGRectGetMinX(dirtyRect) / tileSize.width);
int lastCol = floorf((CGRectGetMaxX(dirtyRect)-1) / tileSize.width);
int firstRow = floorf(CGRectGetMinY(dirtyRect) / tileSize.height);
int lastRow = floorf((CGRectGetMaxY(dirtyRect)-1) / tileSize.height);
for (int row = firstRow; row <= lastRow; row++) {
for (int col = firstCol; col <= lastCol; col++) {
NSImage *tile = [self tileForScale:scale row:row col:col];
CGRect tileRect = CGRectMake(tileSize.width * col, tileSize.height * row,
tileSize.width, tileSize.height);
// if the tile would stick outside of our bounds, we need to truncate it so as
// to avoid stretching out the partial tiles at the right and bottom edges
tileRect = CGRectIntersection(self.bounds, tileRect);
[tile drawInRect:tileRect];
}
}
}
-(BOOL)isFlipped{
return YES;
}
But this deforms the image, and doesn't zoom or pan correctly (but at least the tile selection works)...
I can't believe this is so hard, any help would be greatly appreciated. Thanks :)
After a lot of research and tries, I finally managed to get this to work using this example. Decided to post it for future reference. Open the ZIP > CoreAnimationLayers> TiledLayers, there's a good example there. That's how CATiledLayer works with OS X, and since the example there doesn't handle zoom very well, I leave here my zoom code
-(void)magnifyWithEvent:(NSEvent *)event{
[super magnifyWithEvent:event];
if (!isZooming) {
isZooming = YES;
BOOL zoomOut = (event.magnification > 0) ? NO : YES;
if (zoomOut) {
[self zoomOutFromPoint:event.locationInWindow];
} else {
[self zoomInFromPoint:event.locationInWindow];;
}
}
}
-(void)zoomInFromPoint:(CGPoint)mouseLocationInWindow{
if(zoomLevel < pow(2, tiledLayer.levelsOfDetailBias)) {
zoomLevel *= 2.0f;
tiledLayer.transform = CATransform3DMakeScale(zoomLevel, zoomLevel, 1.0f);
tiledLayer.position = CGPointMake((tiledLayer.position.x*2) - mouseLocationInWindow.x, (tiledLayer.position.y*2) - mouseLocationInWindow.y);
}
}
-(void)zoomOutFromPoint:(CGPoint)mouseLocationInWindow{
NSInteger power = tiledLayer.levelsOfDetail - tiledLayer.levelsOfDetailBias;
if(zoomLevel > pow(2, -power)) {
zoomLevel *= 0.5f;
tiledLayer.transform = CATransform3DMakeScale(zoomLevel, zoomLevel, 1.0f);
tiledLayer.position = CGPointMake((tiledLayer.position.x + mouseLocationInWindow.x)/2, (tiledLayer.position.y + mouseLocationInWindow.y)/2);
}
}

Cocos2d: Testing for collisions, says that the two sprites always intersect

Hello I am making a cocos2d side scroller. I am trying to test for collisions between two sprites. I checked and the rects I am making for the sprites are what they are supposed to be, but it says that the two rects intersect all of the time whether or not they actually do. Here is the code:
-(void)checkForRedEnemyCollisions{
CGRect playerRect = CGRectMake(player.position.x - (player.playerSprite.contentSize.width/2),
player.position.y - (player.playerSprite.contentSize.height/2),
player.playerSprite.contentSize.width,
player.playerSprite.contentSize.height);
CGRect redEnemyRect = CGRectMake(redEnemy.position.x - (redEnemy.bulletSprite.contentSize.width / 2) ,
redEnemy.position.y - (redEnemy.bulletSprite.contentSize.height /2 ),
redEnemy.bulletSprite.contentSize.width,
redEnemy.bulletSprite.contentSize.height);
if (CGRectIntersectsRect(playerRect, redEnemyRect)) {
CCLOG(#"collision");
}
}
Here is more code:
-(id)init{
if((self = [super init])){
CGSize size = [[CCDirector sharedDirector]winSize];
screenWidth = size.width;
screenHeight = size.height;
gravity = 2;
playerSprite = [CCSprite spriteWithFile:#"thefinalcharacter.png"];
playerSprite.scale = 1.5;
playerSprite.position = ccp(screenWidth/3.4, screenHeight/2);
[self addChild:playerSprite z:-3];
[self schedule: #selector(flight:)interval:1.0f/7.0f];
}
return self;
}
-(void)flight:(ccTime)delta{
flightCounter ++;
if (flightCounter % 2){
[playerSprite setTexture:[[CCSprite spriteWithFile:#"thefinalcharacter.png"]texture]];
}else{
[playerSprite setTexture:[[CCSprite spriteWithFile:#"thefinalcharacter2.png"]texture]];
}
[self schedule:#selector(updatePosition:)interval:1.0f/30.0f];
}
-(void)updatePosition:(ccTime)delta{
if(playerSprite.position.y < 35){
gravity = 0;
}else if(playerSprite.position.y > screenHeight - 150) {
playerSprite.position = ccp(playerSprite.position.x, playerSprite.position.y - 100);
}else{
gravity = 2;
}
playerSprite.position = ccp(playerSprite.position.x, playerSprite.position.y - gravity);
}
make sure that you are creating the bounding rectangles in world coordinates.
try to convert the points to world coordinates like the following;
CGPoint playerWorldPosition = [player ConvertToWorldSpace:player.position];
CGRect playerRect = CGRectMake(playerWorldPosition.x - (player.playerSprite.contentSize.width/2),
playerWorldPosition.y - (player.playerSprite.contentSize.height/2),
player.playerSprite.contentSize.width,
player.playerSprite.contentSize.height);
do the same for redEnemy. this will make sure you are comparing them in world space but not in another space down the hierarchy of layers.
if you using anchor point instead of the default (0.0,0.0) on your player or redEnemies. use the ConvertToWorldSpaceAR function instead of the ConvertToWorldSpace.

Stroking paths using a pattern color to simulate brush texture

I am making a finger painting app and I am having a hard time giving my brush a nice brush-like feel and texture.
I have this code:
UIColor * brushTexture = [UIColor colorWithPatternImage:[UIImage imageNamed:#"Brush_Black.png"]];
CGContextSetStrokeColorWithColor(UIGraphicsGetCurrentContext(), brushTexture.CGColor);
but my output is like this:
The texture of my brush is tiled. How can I make it fit to the stroke Size and stop the tiling of the image?
Here i found a solution!
UIImage *texture = [UIImage imageNamed:"brush.png"]
CGPoint vector = CGPointMake(currentPoint.x - endPoint.x, currentTPoint.y - endPoint.y);
CGFloat distance = hypotf(vector.x, vector.y);
vector.x /= distance;
vector.y /= distance;
for (CGFloat i = 0; i < distance; i += 1.0f) {
CGPoint p = CGPointMake(endPoint.x + i * vector.x, endPoint.y + i * vector.y);
[texture drawAtPoint:p blendMode:blendMode alpha:0.5f];
}