Can't position SKShapeNode (rectangle) in grid precisely - sprite-kit

Being new to the obj-c language and Sprite kit I'm having difficulties positioning a rectangle in a grid...
There's an offset in the rectangles I create - if I manage to get the code to position a rectangle in a chosen field another rectangle will be offset...
I have tried several different approches and I can get it to work using JavaFX. What am I doing wrong?
The photo below shows my problem clearly.
My code is rather simple and can be seen here:
#import "MyScene.h"
#implementation MyScene
const int ROWS = 10;
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
/* Setup your scene here */
self.backgroundColor = [SKColor darkGrayColor];
[self createBoardWithRows:ROWS];
[self createBoxPositionX:1 positionY:1];
[self createBoxPositionX:3 positionY:3];
[self createBoxPositionX:5 positionY:5];
}
return self;
}
-(void) createBoardWithRows: (int) rows{
for (int i = 1; i < rows; i++){
//Horisontal lines
int yPos = self.size.height/rows * i;
SKShapeNode *lineH = [SKShapeNode node];
CGMutablePathRef pathToDraw = CGPathCreateMutable();
CGPathMoveToPoint(pathToDraw, NULL, 0, yPos);
CGPathAddLineToPoint(pathToDraw, NULL, self.size.width, yPos);
lineH.path = pathToDraw;
lineH.lineWidth = 1.0;
[lineH setStrokeColor:[UIColor blackColor]];
//Vertical Lines
int xPos = self.size.width/rows * i;
SKShapeNode *lineV = [SKShapeNode node];
CGPathMoveToPoint(pathToDraw, NULL, xPos, 0);
CGPathAddLineToPoint(pathToDraw, NULL, xPos, self.size.height);
lineV.path = pathToDraw;
lineV.lineWidth = 1.0;
[lineV setStrokeColor:[UIColor blackColor]];
//Add lines
[self addChild:lineH];
[self addChild:lineV];
}
}
-(void) createBoxPositionX:(int) fieldIndexX positionY:(int) fieldIndexY{
int width = self.size.width/ROWS;
int height = self.size.height/ROWS;
int x = (width * fieldIndexX);
int y = (height * fieldIndexY);
CGRect box = CGRectMake(x, y, width, height);
SKShapeNode *shapeNode = [[SKShapeNode alloc] init];
shapeNode.path = [UIBezierPath bezierPathWithRect:box].CGPath;
shapeNode.fillColor = SKColor.yellowColor;
//Stroke settings
shapeNode.strokeColor = [SKColor clearColor];
shapeNode.lineWidth = 0;
[self addChild:shapeNode];
//Alternative rectangle
//SKSpriteNode spriteNodeWithColor:CGSize:
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
/* Called when a touch begins */
}
-(void)update:(CFTimeInterval)currentTime {
/* Called before each frame is rendered */
}
#end

It almost seems like you're not taking into account the line width of the border in each box - so when you create the yellow boxes they are displaced by the number of points equivalent to the number of "borders".
To fix this, change these two lines:
int x = (width * fieldIndexX);
int y = (height * fieldIndexY);
to:
int x = (width * fieldIndexX) + fieldIndexX;
int y = (height * fieldIndexY) + fieldIndexY;

If you are getting 1 pixel shifts into any direction it might be anchor point issue.
By default it is 0.5,0.5 - at center of the sprite.
So if your sprite has even length for example 4, the middle can fall on either 2 or 3 and you will get 1 pixel off positioning.
The fix for this is clear, set the anchor point of node to 0,0, like so:
node.anchorPoint = CGPointMake(0,0);
What this does is that the sprite is pinned to it parent with lower left corner instead of center. Since the lower left corner always starts at 0,0 pixel you will have correct positions regardless of pixel count of your nodes.
But you will have to adjust the nodes position to accomodate this change.
Hope this helps.

Related

How to perform animation in objective c

I'm trying to learn how to animate motion of the circle that I created in custom View. However, during the loop in startMovingCircle the circle doesn't show, it appears only when the loop finishes. I thought that if I call setNeedsDisplay every time it should redraw the view and the animation of moving circle should show up. Could someone tell me what am I doing wrong?
Also, I would like to ask if it is correct to program animation this way or should I use CoreAnimation and what are the benefits?
Can CoreAnimation animate shapes like circle, elipses, rectangle programmed in CoreGraphics?
MyDrawingView.m:
-(id)initWithFrame:(CGRect)frame{
self = [super initWithFrame:frame];
if (self) {
xCoordinate = 100.0;
yCoordinate = 100.0;
speed = 1.0;
}
return self;
}
- (void)drawRect:(CGRect)rect
{
NSLog(#"Drawing");
CGContextRef myContext = UIGraphicsGetCurrentContext();
CGContextBeginPath(myContext);
CGContextAddArc(myContext, xCoordinate, yCoordinate, 20.0, 0.0, 2 * M_PI, 0);
CGContextClosePath(myContext);
CGContextSetLineWidth(myContext, 1);
CGContextSetStrokeColorWithColor(myContext, [UIColor whiteColor].CGColor);
CGContextStrokePath(myContext);
}
-(void)moveCircle{
xCoordinate += speed;
yCoordinate += speed;
NSLog(#"Moving circle. X: %f, Y: %f", xCoordinate, yCoordinate);
[self setNeedsDisplay];
}
MyDrawingViewController.m:
-(void)startMovingCircle{
for(int i = 0; i < 1000; i++){
[myView moveCircle];
}
}
in -(void)moveCircle
you should create a core animation (with blocks)
and animate the center property of the view to animate.
You should add a pause in between iterations of your for() loop.

How to draw polygons in cocoa touch using UIBezierPath

I want to draw polygon of different sides (4-12). What is the logic for drawing a polygon. For e.g. if user selects 6 side, it should draw a hexagon, if user enters 8 sides it should draw a octagon. I have found the following code but i also want to resize the UIView in which i am drawing a polygon so that the shape inside of the view also grows along with the view. Any body can help me please. Following is the code i am using currently but it is not positioned at the center also when i resize the view that shape moves to another position in the view.
int radius = MINIMUM(widht, height)*0.4 ;
for (int i = 0; i < _numberOFsides; i++){
CGPoint point = CGPointMake(widht/2+radius *cosf(i*2*M_PI/_numberOFsides), widht/2+radius*sinf(i*2*M_PI/_numberOFsides));
if (i==0) {
[_shapePath moveToPoint:point];
}
else{
[_shapePath addLineToPoint:point];
[_shapePath stroke];
}
}
now to resize ur UIBazierPath you can add below code,
CGRect bazierRect = CGPathGetBoundingBox(bezierpath.CGPath)
CGFloat scaleX = view.frame.size.width / bazierRect.frame.size.width;
CGFloat scaleY = view.frame.size.height / bazierRect.frame.size.height;
CGAffineTransform transform = CGAffineTransformMakeScale(scaleX, scaleY);
CGPathRef newPath = CGPathCreateCopyByTransformingPath(bezierpath.CGPath, &transform);
bezierPath.CGPath = newPath;
CFRelease(newPath);
If you want to make a regular polygon with any number of sides the following code will give you the vertices of each edge and it is easy to rescale in size and number of sides:
int n = 10; //number of edges
float j = 20; //length of each edge
float x = 130;
float y = 250;//the point 130,250 will be at the bottom of the figure
float angle = 2*M_PI;
for (int i = 0; i < n; i++) {
CGRect frame = CGRectMake(x, y, 2, 2);//put a dot on x,y
NSLog(#"%f | %f, %f", angle, x, y);
x = x + j*cosf(angle);
y = y + j*sinf(angle); //move to the next point
angle = angle - 2*M_PI/n; //update the angle
//display the dot
UIView *rect = [[UIView alloc] initWithFrame:frame];
rect.backgroundColor = [UIColor blueColor];
[self.view addSubview:rect];
}
Hope this helps. If you have any questions feel free to ask and have a great day!
~Deadly Porcupine

Core Graphics drawing only black

I'm trying to develop a chess game on the iPhone, and have become stuck on an otherwise insignificant detail but can not seem to get past it. Can anybody see the piece that's missing?
Here's the problem. When a user clicks on a square, it should highlight in some faded color. What actually is happening though, is it just draws black, totally regardless of what color I set it to.
Here's an image of what I get. The black circle should be red, or any color.
Here, notice the UIView I'm drawing to is behind the pieces view. Doubt it matters, but I want to be complete.
Finally, the code for the layer:
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
self.backgroundColor = [UIColor clearColor];
rowSelected = 0;
colSelected = 0;
}
return self;
}
-(void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextClearRect(context, rect);
CGContextSetRGBStrokeColor(context, 255.0, 0.0, 0.0, 1.0);
if (rowSelected && colSelected)
{
int gridsize = (self.frame.size.width / 8);
int sx = (colSelected-1) * gridsize;
int sy = (rowSelected-1) * gridsize;
int ex = gridsize;
int ey = gridsize;
CGContextFillEllipseInRect(context, CGRectMake(sx,sy,ex,ey));
}
}
// Handles the start of a touch
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
int gridsize = (self.frame.size.width / 8);
// Position of touch in view
UITouch *touch = [[event allTouches] anyObject];
CGPoint loc = [touch locationInView:self.superview];
int x = loc.x / gridsize;
int y = loc.y / gridsize;
rowSelected = y + 1;
colSelected = x + 1;
[self setNeedsDisplay];
}
I have tried a number of things, using a value of 1 rather than 255, playing with the alpha, etc, but I can't get anything other than a solid black.
EDIT:
Also, here's the code of this view's superview:
#implementation ChessBoard
-(id)initWithFrame:(CGRect)frame
{
if (self != [super initWithFrame:frame])
return self;
int SQUARE_SIZE = frame.size.width / 8;
currentSet = BW;
UIImage *img = [UIImage imageNamed:#"board.png"];
background = [[UIImageView alloc] initWithImage:img];
[background setFrame:frame];
[self addSubview:background];
overlay = [[ChessBoardOverlay alloc] initWithFrame:frame];
[self addSubview:overlay];
In the function CGContextSetRGBStrokeColor color compontent values (red, green,blue, alpha) need to be between 0 and 1. Since you are assuming that 255 is the maximum for a color you need to divide all of your color component values by 255.0f.
CGContextSetRGBStrokeColor(context, 255.0f/255.0f, 0.0/255.0f, 0.0/255.0f, 1.0);
You say you're drawing to a layer... but a CALayer doesn't have a drawRect: method, it has drawInContext:
I would guess by trying to call drawRect: in your instance of a CALayer that the true context hasn't been built correctly, or it has but you're not tying in to it.
Maybe you should state your color this way:
CGContextSetRGBStrokeColor(context, 255.0/255.0f, 0.0/255.0f, 0.0/255.0f, 1.0f);
This happened to me once using:
[UIColor colorWithRed:12.0/255.0f green:78.0/255.0f blue:149.0/255.0f alpha:1.0f];
until I added the /255.0f so that may work.

How to align UIButtons within a circle?

Say for example I have 20 buttons. How can I add a subView of the UIView of this button in a circular format?
This will place the buttons around a circle of a given radius at a given center.
It will also rotate each button using the transform property of UIView.
Hope it helps. :)
- (void)viewDidLoad
{
[super viewDidLoad];
NSArray *buttons = /* you buttons here */
float curAngle = 0;
float incAngle = ( 360.0/(buttons.count) )*PI/180.0;
CGPoint circleCenter = CGPointMake(160, 200); /* given center */
float circleRadius = 100; /* given radius */
for (UIButton *button in buttons)
{
CGPoint buttonCenter;
buttonCenter.x = circleCenter.x + cos(curAngle)*circleRadius;
buttonCenter.y = circleCenter.y + sin(curAngle)*circleRadius;
button.transform = CGAffineTransformRotate(button.transform, curAngle);
button.center = buttonCenter;
[self.view addSubview:button];
curAngle += incAngle;
}
}
Result:

how to add an animation effect of drawing a circle in cocos2d

i am working on a game to spot the difference between 2 images.
I want to add an effect when you spot it. drawing out a circle would be much better than just showing the circle suddenly. but i've never done core animation or opengl before.
i don't think preparing 100 sprites and changing the sprite frame by frame is a good idea.
here is my code: (just add a circle image to both left and right image. )
-(void) show {
CCSprite* leftCircle = [CCSprite spriteWithFile:#"circle.png"];
CCSprite* rightCircle = [CCSprite spriteWithFile:#"circle.png"];
leftCircle.scaleX = size.width / [leftCircle boundingBox].size.width;
leftCircle.scaleY = size.height / [leftCircle boundingBox].size.height;
rightCircle.scaleX = size.width / [rightCircle boundingBox].size.width;
rightCircle.scaleY = size.height / [rightCircle boundingBox].size.height;
leftCircle.anchorPoint = ccp(0, 1);
rightCircle.anchorPoint = ccp(0, 1);
leftCircle.position = leftPosition;
rightCircle.position = rightPosition;
[[GameScene sharedScene] addChild:leftCircle z: 3];
[[GameScene sharedScene] addChild:rightCircle z: 3];
shown = YES;
}
So how can i implement it? It would be great if you can provide some source code.
As a simple way i can recommend you to create a circle and put it's scale to zero. Then create a CCScale action and run it. It will give you a growing circle. Here is the code:
CCSprite *sprite = [CCSprite spriteWithFile:#"mySprite.png"];
[sprite setScale:0.01];
id scale = [CCScale actionWithDuration:0.3 scale:1];
[sprite runAction:scale];
The other action can be used is CCFadeIn.You can make your sprite invisible after creation and make it fade in:
CCSprite *sprite = [CCSprite spriteWithFile:#"mySprite.png"];
[sprite setOpacity:0];
id fade = [CCFadeIn actionWithDuration:0.3];
[sprite runAction:fade];
Also you can combine this actions:
[sprite runAction:fade];
[sprite runAction:scale];
Also you can make it big (set scale 3 for example) and transparent. And make it fade in and scale down to highlight your image
To Draw a circle via drawing effect you can make it from small parts (arcs) and then build your circle from them. I think it also will be cool if you make don't make the part visible when adding, but make it fade in. I mean something like this:
-(void) init
{
NSMutableArray *parts = [[NSMutableArray array] retain]; //store it in your class variable
parts_ = parts;
localTime_ = 0; //float localTime_ - store the local time in your layer
//create all the parts here, make them invisible and add to the layer and parts
}
-(void) update: (CCTime) dt //add update method to your layer that will be called every simulation step
{
localTime_ += dt;
const float fadeTime = 0.1;
int currentPart = localTime_ / fadeTime;
int i = 0;
for (CCSprite *part in parts)
{
//setup the opacity of each part according to localTime
if (i < currentPart) [part setOpacity:255];
else if (i == currentPart)
{
float localLocalTime = localTime - i*fadeTime;
float alpha = localLocalTime / fadeTime;
[part setOpacity:alpha];
}
++i;
}
}
It is rather simple to create the effect you want.
You can realize it by setting the strokeEnd of a CAShapeLayer.
Here are the details:
create a path you then assign to a CAShapeLayer
UIBezierPath* circlePath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(100.0, 100.0) radius:80.0 startAngle:DEGREES_TO_RADIANS(270) endAngle:DEGREES_TO_RADIANS(270.01) clockwise:NO];
circle = [CAShapeLayer layer];
circle.path = circlePath.CGPath;
circle.strokeColor = [[UIColor blackColor] CGColor];
circle.fillColor = [[UIColor whiteColor] CGColor];
circle.lineWidth = 6.0;
circle.strokeEnd = 0.0;
[self.view.layer addSublayer:circle];
set the strokeEnd to implicitly animate the drawing of the circle
circle.strokeEnd = 1.0;
That's it! The circle will be automatically drawn for you ;). Here is the code from above with a GestureRecognizer to trigger the animation. Copy this to an empty project of type "Single View Application" and paste it to the ViewController.
#define DEGREES_TO_RADIANS(angle) ((angle) / 180.0 * M_PI)
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
UIBezierPath* circlePath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(100.0, 100.0) radius:80.0 startAngle:DEGREES_TO_RADIANS(270) endAngle:DEGREES_TO_RADIANS(270.01) clockwise:NO];
circle = [CAShapeLayer layer];
circle.path = circlePath.CGPath;
circle.strokeColor = [[UIColor blackColor] CGColor];
circle.fillColor = [[UIColor whiteColor] CGColor];
circle.lineWidth = 6.0;
circle.strokeEnd = 0.0;
[self.view.layer addSublayer:circle];
// add a tag gesture recognizer
// add a single tap gesture recognizer
UITapGestureRecognizer* tapGR = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTapGesture:)];
[self.view addGestureRecognizer:tapGR];
}
#pragma mark - gesture recognizer methods
//
// GestureRecognizer to handle the tap on the view
//
- (void)handleTapGesture:(UIGestureRecognizer *)gestureRecognizer {
circle.strokeEnd = 1.0;
}