SpriteKit find point on perimeter of screen - ios

I need to find a generate a random point just outside the screen to spawn an enemy. How can I do this? I looked up other topics on this but they were all confusing.

You could use a method like this :
+(CGPoint)randomPointInRects:(NSArray*)rects
{
// choose one of the rects in the array randomly
int randomIndex = arc4random() % rects.count;
CGRect rect = [(NSValue*)rects[randomIndex] CGRectValue];
// get a random x and y location based on the chosen rect
float randX = arc4random() % (int)rect.size.width + rect.origin.x;
float randY = arc4random() % (int)rect.size.height + rect.origin.y;
// store value in point
CGPoint randomPoint = CGPointMake(randX, randY);
return randomPoint;
}
Then to get that random point on say a 1024x768 screen you might do something like this :
NSMutableArray *rects = [NSMutableArray array];
// define spawn areas with CGRect's
//top
[rects addObject:[NSValue valueWithCGRect:CGRectMake(0, 768, 1024, 50)]];
// bottom
[rects addObject:[NSValue valueWithCGRect:CGRectMake(0, -50, 1024, 50)]];
//left
[rects addObject:[NSValue valueWithCGRect:CGRectMake(-50, 0, 50, 768)]];
//right
[rects addObject:[NSValue valueWithCGRect:CGRectMake(1024, 0, 50, 768)]];
// pass array to method to return a random point in those rects.
CGPoint randomPoint = [self randomPointInRects:rects];
You basically define the areas you want to have nodes spawned, store them in an array, pass them to the method and it will return a random point from those rects.
I added a 50 pixel buffer for each edge.
I did this quickly, so it might have a bug, but the logic of the approach should get you there if so! :)
This solution would also work for any situation where you wanted to have something spawned in multiple areas, defined by CGRects.

Related

Draw Bubble Graph in iOS? [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 7 years ago.
Improve this question
hope you guys are doing well.
How can I draw graph similar to below image?
To be more specific, my graph will have low, normal and high risk with orange, green and red colours respectively. Each containing bubble with info in x- axis.
I wanna make exactly like above image any help would be appreciated and also please be specific while posting some third party libs and all.
How can I achieve the above graph?
Note- In X- axis (dates) and Y- axis [(values) in bubbles] are there.
EDIT
y-axis is fixed with some range by x-axis is from server took in NSMutableArray
Thanks in advance.
First step in drawing graph is to create X-Y axis layout. You can draw X and Y axis line using UIBezierPath and CAShapeLayer.
UIBezierPath *graphPath = [[UIBezierPath alloc]init];
[graphPath setLineWidth:GRAPH_LAYOUT_LINE_WIDTH];
[[UIColor blackColor] setStroke];
[graphPath moveToPoint:CGPointMake(ORIGN_X, ORIGN_Y)];
[graphPath addLineToPoint:CGPointMake((ORIGN_X + TOTAL_X_DIST),ORIGN_Y)];
[graphPath stroke]; // drawing path
//Creating graph layout
CAShapeLayer *graphLayout = [CAShapeLayer layer];
graphLayout.fillColor = [[UIColor clearColor] CGColor];
graphLayout.strokeColor = [GRAPH_LAYOUT_COLOR CGColor];
graphLayout.lineWidth = GRAPH_LAYOUT_LINE_THICKNESS;
graphLayout.path = [graphPath CGPath];
[self.layer addSublayer:graphLayout];
Repeat the above code for drawing Y axis
Second you need to create X and Y axis scale
float minX = 0;
float maxX = 10;
float minY = 0;
float maxY = 100;
float x1Unit = 1;
float y1Unit = 1;
self.spacingX = TOTAL_X_DIST/((maxX - minX)/x1Unit);
self.spacingY = TOTAL_Y_DIST/((maxY - minY+1)/y1Unit);
Now you need to get the values(cartesian coordinate) of X and Y axis in an array, here i am just hard coding the points
//Enter the co-ordinates
[self.pointsArray addObject:[NSValue valueWithCGPoint:CGPointMake(0, 50)]];
[self.pointsArray addObject:[NSValue valueWithCGPoint:CGPointMake(1, 20)]];
[self.pointsArray addObject:[NSValue valueWithCGPoint:CGPointMake(2, 35)]];
[self.pointsArray addObject:[NSValue valueWithCGPoint:CGPointMake(3, 55)]];
[self.pointsArray addObject:[NSValue valueWithCGPoint:CGPointMake(4, 80)]];
[self.pointsArray addObject:[NSValue valueWithCGPoint:CGPointMake(5, 60)]];
[self.pointsArray addObject:[NSValue valueWithCGPoint:CGPointMake(6, 85)]];
[self.pointsArray addObject:[NSValue valueWithCGPoint:CGPointMake(7, 50)]];
[self.pointsArray addObject:[NSValue valueWithCGPoint:CGPointMake(8, 30)]];
[self.pointsArray addObject:[NSValue valueWithCGPoint:CGPointMake(9, 10)]];
[self.pointsArray addObject:[NSValue valueWithCGPoint:CGPointMake(10, 05)]];
Next we need to convert cartesian coordinate values to values of x and y with respect to parent view.
// Creating (x,y) points based on superview coordinate system
for (NSValue *point in self.pointsArray)
{
CGPoint coordinate;
coordinate.x = (STARTING_X*x1Unit + ([point CGPointValue].x * self.spacingX) + self.spacingX/1.5)/x1Unit;
coordinate.y = (STARTING_Y*y1Unit - ([point CGPointValue].y * self.spacingY))/y1Unit;
[self.coordinateArray addObject:[NSValue valueWithCGPoint:coordinate]];
}
Now it's time to add beautiful bubbles
for (int a = 0; a < [self.coordinateArray count]; a++)
{
CGPoint point = [[self.coordinateArray objectAtIndex:a] CGPointValue];
//Creating bubble for points on the graph.
UIView *bubble = [[UIView alloc] initWithFrame:BUBBLE_FRAME];
bubble.Layer.cornerRadius = BUBBLE_FRAME_WIDTH/2;
//Giving the color for the bubble based on the Y-Axis value
if (point.y <= 50)
{
bubble.backgroundColor = RED_COLOR;
}
else if (point.y > 50 && point.y <= 80)
{
bubble.backgroundColor = YELLOW_COLOR;
}
else
{
bubble.backgroundColor = GREEN_COLOR;
}
[self addSubview:bubble];
}
Hope this helps you

How to cut up image based on coordinates

I have a question related to cutting up an image to know which part is tapped by the user. It's a rather simple exercise for some, I think, but I've been cracking my head over this and can't find a proper way.
What I would like to do is tap a body part on this image and have the device tell me what body part it is:
But I have absolutely no clue on how to program this.
I've been thinking on setting an array of outline CGPoints for every body part, but how to get those? Photoshop coordinates? And then calculate the touched point onto the closest CGPoint?
Somebody gave me the idea to cut up the image in the different zones, different images for every body part. But here the problem is that every image is a rectangle, which makes it very hard to know what exactly got touched. Would also need an array of points, especially for the overlapping images.
Another person told me this would only be possible using SpriteKit.
Let me know, smart peeps :)
UPDATE:
I figured it out! I'm making a lot of arrays that contain the points to the hand, the legs, ... An example:
CGPoint p1 = CGPointMake(x + 432, y + 200);
CGPoint p2 = CGPointMake(x + 523, y + 188);
CGPoint p3 = CGPointMake(x + 530, y + 277);
CGPoint p4 = CGPointMake(x + 432, y + 277);
CGPoint p5 = CGPointMake(x + 523, y + 367);
CGPoint p6 = CGPointMake(x + 432, y + 354);
CGPoint p7 = CGPointMake(x + 325, y + 355);
CGPoint p8 = CGPointMake(x + 296, y + 362);
NSArray *handL = [NSArray arrayWithObjects:
[NSValue valueWithCGPoint:p1],
[NSValue valueWithCGPoint:p2],
[NSValue valueWithCGPoint:p3],
[NSValue valueWithCGPoint:p4], nil];
[shapes addObject:#{#"name":NSLocalizedString(#"Left Hand", #""), #"points":handL}];
NSArray *handR = [NSArray arrayWithObjects:
[NSValue valueWithCGPoint:p5],
[NSValue valueWithCGPoint:p6],
[NSValue valueWithCGPoint:p7],
[NSValue valueWithCGPoint:p8], nil];
[shapes addObject:#{#"name":NSLocalizedString(#"Right Hand", #""), #"points": handR}];
Afterwards in I read these values and layer them:
NSArray *shapes = [[NumbersHelper sharedNumbersHelper] getScreenThreeShapes];
for (int outside = 0; outside < [shapes count]; outside++) {
CAShapeLayer *shape = [[CAShapeLayer alloc] init];
[self.scrollView.layer addSublayer:shape];
shape.opacity = 0.5;
shape.lineWidth = 2;
shape.lineJoin = kCALineJoinRound;
NSArray *points = [[[shapes valueForKey:#"points"] allObjects] objectAtIndex:outside];
UIBezierPath *path = [[UIBezierPath alloc] init];
for (int inside = 0; inside < points.count; inside++) {
if (inside == 0) {
[path moveToPoint:[[points objectAtIndex:inside] CGPointValue]];
} else {
[path addLineToPoint:[[points objectAtIndex:inside] CGPointValue]];
}
}
[path closePath];
shape.path = [path CGPath];
shape.name = [[[shapes valueForKey:#"name"] allObjects] objectAtIndex:outside];
}
When the user taps the view I do this:
- (void)tappedOnView:(UITapGestureRecognizer *)sender
{
if ([sender locationInView:self.scrollView].x > 2048 && [sender locationInView:self.scrollView].x < 3072) {
screenTwoTouchedPoint = [sender locationInView:self.scrollView];
CGPoint touchPoint = [sender locationInView:self.scrollView];
NSString *name;
for (id sublayers in self.scrollView.layer.sublayers) {
if ([sublayers isKindOfClass:[CAShapeLayer class]]) {
CAShapeLayer *shape = sublayers;
if (CGPathContainsPoint(shape.path, 0, touchPoint, YES)) {
name = shape.name;
break;
}
}
}
[self tappedBody:sender onPart:name];
}
}
A crude but simple way would be to have transparent buttons over various body parts. In case a body part is not covered properly by a single button, add multiple smaller buttons.
You can add tag to the buttons to identify which button/body part got tapped or connect them to unique Actions.
A solution that worked for me. Create a 2d array for the entire size of the image. If the image is 250x250, your array would be the same.
Fill this array with NSObjects, having and int bodyInteger
Then drag your finger on the hand of the image, and in your touchesMoved take the co-ordinates from location, and pick the object from your 2d array by using the co-ordinates and setting its bodyInteger to 1.
Now when someone puts finger on hand, you take the respective object from 2d array and see what bodyInteger it has.
If you have memory constraints, you can create a structure instead of an NSObject and save only for the points that are within the body.
Alternatively, you can create a boundary for each body part and save it. When user touches with finger, you can see if the point is within a saved polygon or not.

IOS Adding random nodes in different position

I am trying to make an iPhone game and I am trying to add random images in different position.
Here is what I want to do
There is 6 different color oval nodes (enemy)
When the game starts I want there to be 5 enemy nodes.
When the player node contacts enemy node, enemy node will disappear and then right away another
enemy node will be added in different location.
But some times some nodes appear in same location so it looks like there is 4 nodes instead of 5.
If there is a node already in a specific location how can I not add another node there but some other location?
Below I added a part of the code I wrote.
It might be something very easy but I am new to programming and I could not figure that out.
Thank you,
-(void) addWaterBall {
for (int i = 0; i < 5; i++) {
NSUInteger randomWaterBall = [Util randomWithMin:0 max:8];
WaterBall *waterBall = [WaterBall waterBallOfType:randomWaterBall];
float y = self.frame.size.height - ((((self.frame.size.height/2)-10)/10) * [Util randomWithMin:1 max:10]);
float x = (self.frame.size.width/10) * [Util randomWithMin:1 max:10];
waterBall.position = CGPointMake(x, y);
waterBall.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:waterBall.size.width/2];
waterBall.physicsBody.dynamic = YES;
waterBall.physicsBody.affectedByGravity = NO;
waterBall.physicsBody.categoryBitMask = waterBallCategory;
waterBall.physicsBody.contactTestBitMask = sharkCategory ;
//waterBall.physicsBody.collisionBitMask = ;
[self addChild:waterBall];
}
}
What I would do is stick in a [self enumerateChildNodesWithName…….] after generating the random co-ordinates and compare the random x and y co-ordinates with those on each enumerated node, if they are the same or too close then generate new random co-ordinates. This is probably best done in a while loop.
-(void)addWaterBall
{
NSUInteger randomWaterBall = [Util randomWithMin:0 max:8];
WaterBall *waterBall = [WaterBall waterBallOfType:randomWaterBall];
waterBall.name = #"WaterBall";
[self enumerateChildNodesWithName:#"WaterBall" usingBlock:^(SKNode *node, BOOL *stop) {
float y = self.frame.size.height - ((((self.frame.size.height/2)-10)/10) * [Util randomWithMin:1 max:10]);
float x = (self.frame.size.width/10) * [Util randomWithMin:1 max:10];
node.position = CGPointMake(x, y);
node.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:waterBall.size.width/2];
node.physicsBody.dynamic = YES;
node.physicsBody.affectedByGravity = NO;
node.physicsBody.categoryBitMask = waterBallCategory;
node.physicsBody.contactTestBitMask = sharkCategory ;
//waterBall.physicsBody.collisionBitMask = ;
[self addChild:waterBall];
}];
}

Adding numbers to a clock face?

I saw this clock example on objc.io and I wanted to add the numbers to the clock face.
http://www.objc.io/issue-12/animating-custom-layer-properties.html
So, in the -(id)init{} method I added this code using a formula that I found on the Internet:
float Cx = self.bounds.size.width / 2;
float Cy = self.bounds.size.height / 2;
float Rx = 100;
float Ry = 100;
for (int i=1; i<=12; i++) {
float theta = i / 12.0 * 2.0 * M_PI;
float X = Cx + Rx * cos(theta);
float Y = Cy + Ry * sin(theta);
CATextLayer *aTextLayer = [[CATextLayer alloc] init];
[aTextLayer setFont:#"Helvetica-Bold"];
[aTextLayer setFontSize:20];
[aTextLayer setFrame:CGRectMake(X, Y, 50, 20)];
[aTextLayer setString:[NSString stringWithFormat:#"%d", i]];
[aTextLayer setAlignmentMode:kCAAlignmentCenter];
[aTextLayer setForegroundColor:[[UIColor greenColor] CGColor]];
[self addSublayer:aTextLayer];
}
For some reason, my 12 o'clock has shifted to 3 o'clock.
Can anyone spot what I'm doing wrong?
The trig functions wind from the positive x-axis toward the positive y-axis. In a CALayer, positive y is down, so the start of your loop is 3 o'clock, and the angle proceeds clockwise, which is how you want it for a clock, and the opposite of how things work on the standard (+y==UP) cartesian plane.
The next thing to notice is that it's simpler to loop twelve positions 0..11 and compute the hours and angles from the position.
Finally, the frames of your labels are set to an arbitrary size #{50,20} and the text is centered, this is probably the cause of the x offset. You want the computed x,y to be the labels' centers, so you'll need to fudge the origins a little...
for (int position=0; position <12; position ++) {
int hour = (position+3) % 12;
float theta = position / 6.0 * M_PI;
float X = Cx + Rx * cos(theta);
float Y = Cy + Ry * sin(theta);
// X,Y calculated as you have done here should be the center of the layer
// the quick fix is to subtract half the width and height to get the origin
CGRect frame = CGRectIntegral(CGRectMake(X-25, Y-10, 50, 20));
CATextLayer *aTextLayer = [[CATextLayer alloc] init];
[aTextLayer setFont:#"Helvetica-Bold"];
[aTextLayer setFontSize:20];
[aTextLayer setFrame:frame];
// note that we use "hour" here...
[aTextLayer setString:[NSString stringWithFormat:#"%d", hour]];
[aTextLayer setAlignmentMode:kCAAlignmentCenter];
[aTextLayer setForegroundColor:[[UIColor greenColor] CGColor]];
[self addSublayer:aTextLayer];
}
You might find it helpful to use a radius for the positioning of these labels that's distinct from the radius used to draw the circle. To position the numbers inside, use a smaller radius, or use a larger one to draw them outside the filled dial.
From your formula it looks like if you swapped the Cos and Sin then negated the Cos term you might get what you're looking for.
Edit: meant to include this too. I noticed that they did this too at the link you posted one of there lines of code where they draw the clock hands is this:
CGContextAddLineToPoint(ctx, center.x + sin(angle) * 80, center.y - cos(angle) * 80);
it looks like they also swapped the cos and sin and negated the cos term

How to spawn CCSprites in random heights like flappy bird

In the iOS game flappy bird, there are pipes that generate after a certain distance and they generate at random heights
I am also trying to make flappy bird pipes (I called it a tree branch in my code instead of pipe). Except the pipes are moving vertically instead of horizontally because it is a vertical scrolling game (it scrolls like the game doodle jump)
This is a drawing of what I want it to be: https://docs.google.com/drawings/d/18bxsVsNOlScCvgi1mwuzD2At7R6xKM3QCh6BfAVMuMo/edit?usp=sharing
(The horizontal lines are the branches)
So this is what I have tried to do so far to make the vertical branches (or pipes)...
in my .h
CCSprite *branch;
NSMutableArray *_branches;
CCSprite *obstacle;
CCNode *previousBranch;
CGFloat previousBranchYPosition;
in my .m
#implementation HelloWorldLayer
static const CGFloat firstBranchPosition = 426.f;
static const CGFloat distanceBetweenBranches = 140.f;
#define ARC4RANDOM_MAX 0x100000000
static const CGFloat minimumXPositionRightBranch = 280.f;
static const CGFloat maximumXPositionLeftBranch = 50.f;
static const CGFloat pipeDistance = 100.f;
static const CGFloat maximumXPositionRightBranch = maximumXPositionLeftBranch - pipeDistance;
setBranchInitialPosition method
/* This is where I am setting the initial position of the branches.
So I am specifying the position of the first branch and the other branches after it so it gets placed every time a certain distance is passed. I have a left branch and a right branch*/
-(void) setBranchInitialPosition {
CGFloat random = ((double)arc4random() / ARC4RANDOM_MAX);
CGFloat range = maximumXPositionRightBranch - minimumXPositionRightBranch;
_rightBranch.position = ccp(minimumXPositionRightBranch + (random * range), _rightBranch.position.y);
_leftBranch.position = ccp(_rightBranch.position.x + pipeDistance, _leftBranch.position.y);
}
spawnNewBranches method
// This is how I want the branches to spawn and I want to add them to an array full of branches
- (void)spawnNewBranches {
previousBranch = [_branches lastObject];
previousBranchYPosition = previousBranch.position.y;
if (!previousBranch) {
// this is the first obstacle
previousBranchYPosition = firstBranchPosition;
}
_rightBranch = [CCSprite spriteWithFile:#"branch.png"];
_leftBranch = [CCSprite spriteWithFile:#"branch.png"];
[_leftBranch addChild:_rightBranch];
[self setBranchInitialPosition];
obstacle = [CCSprite node];
[obstacle addChild:_leftBranch];
obstacle.position = ccp(160, previousBranchYPosition + distanceBetweenBranches);
[self addChild:obstacle];
[_branches addObject:obstacle];
}
scroll method
-(void) scroll:(ccTime)dt
{
// moves the bg
background.position = ccp(screenCenter.x, background.position.y + [[NSUserDefaults standardUserDefaults] integerForKey:#"scrollSpeed"]*dt);
bg2.position = ccp(screenCenter.x, background.position.y-background.contentSize.height);
// it adds the new bg's to the screen before the old bg's move off the screen
if (background.position.y >= screenSize.height*1.5)
{
background.position = ccp(screenCenter.x, (screenCenter.y)-(background.size.height/2));
} else if (bg2.position.y >= screenSize.height*1.5) {
bg2.position = ccp(screenCenter.x, (screenCenter.y)-(bg2.size.height/2));
}
// This is where I want them to appear every certain distance and also move with the brackground
obstacle.position = ccp(obstacle.position.x, obstacle.position.y*[[NSUserDefaults standardUserDefaults] integerForKey:#"scrollSpeed"]*dt);
NSMutableArray *offScreenObstacles = nil;
if (obstacle.position.y >= screenSize.height*1.5) {
[offScreenObstacles addObject:obstacle];
}
for (CCNode *obstacleToRemove in offScreenObstacles) {
[obstacleToRemove removeFromParent];
[_branches removeObject:obstacleToRemove];
// for each removed obstacle, add a new one
[self spawnNewBranches];
}
}
Right now, the branches are appearing, but they stay in the bottom left corner and they dont move or spawn at all. I want to make them move with the background and spawn after a certain distance while also being generated in random heights. I provided you with all my code, do you know how I can make this work? Thanks in advance!
You may want to try placement of the pipes based on a trigonometric curve like sine or cosine (https://en.wikipedia.org/wiki/Trigonometric_functions). It seems like you are placing the pipes within a fairly define random range though if you change this range to an offset from the plot of the trigonometric curve it would take into account the ability of the player to transition between the open gaps better. At least that's my feel. I think the code would be easier to follow as well as I'm a bit confused going through it. You can also easily vary the difficulty of the curve by changing the parameters such as increasing the amplitude or frequency.
I created a copy of Flappy Bird just for fun. I used this code to create the pipes:
-(void)createPipes{
//Create Random
int from = 65;
int max = [[UIScreen mainScreen] bounds].size.height - 124;
int delta = max - from - dy;
int y = from + arc4random() % (delta - from);
//Pipe Bottom
UIImageView *pipeBottom = [[UIImageView alloc] init];
[pipeBottom setContentMode:UIViewContentModeTop];
[pipeBottom setImage:[UIImage imageNamed:#"pipeBottom"]];
[pipeBottom setFrame:CGRectMake(320, y+dy, 60, max - y - dy)];
[pipeBottom setClipsToBounds:YES];
//Pipe Top
UIImageView *pipeTop = [[UIImageView alloc] init];
[pipeTop setFrame:CGRectMake(320, 0, 60, y)];
[pipeTop setContentMode:UIViewContentModeBottom];
[pipeTop setImage:[UIImage imageNamed:#"pipeTop"]];
[self.view insertSubview:pipeTop atIndex:1];
[self.view insertSubview:pipeBottom atIndex:1];
if (!self.pipes)
self.pipes = [[NSMutableArray alloc] init];
[self.pipes addObject:pipeBottom];
[self.pipes addObject:pipeTop];
}
and to move them:
-(void)moveArray:(NSMutableArray *)array{
float ds = dv * dt;
NSMutableArray *trash = [NSMutableArray array];
for (UIImageView *obj in array) {
CGRect frame = obj.frame;
frame.origin.x -= ds;
if (frame.origin.x < -frame.size.width) {
[obj removeFromSuperview];
[trash addObject:obj];
}else{
obj.frame = frame;
}
}
[array removeObjectsInArray:trash];
}
-(void)movePipes{
[self moveArray:self.pipes];
}
I call this function every 0.01 seconds, to run the game:
-(void)runGame{
_time += dt;
if (_time >= 180.0/dv) {
_time = 0;
[self createPipes];
}
[self movePipes];
[self moveEnemies];
[self moveYoshi];
[self moveBar];
[self verifyScore];
[self verifyCollision];
[self verifyState];
}
I defined dt = 0.01 and dv = 110.
You can see my parody in youtube: (http://www.youtube.com/watch?v=tTcYdpSIKJg)
I hope this help you.
Best, Rafael Castro.

Resources