Spawn UIImageView So That It Does Not Intersect With Existing UIImageViews - ios

Basically, I have an app where a user dodges bombs and collects coins. When a user collects a coin, another coin is spawned. I want each coin to not spawn on a bomb. (By the way these are all uiimageviews). In the code below, I have an array of my bombs called bombArray and the UIImageview called "one" is the coin UIImageView. I know the code below doesn't work, but what other method(s) could I use? Thanks, and here is the code:
UIImageView *one = [[UIImageView alloc]initWithImage:[UIImage imageNamed:#"goldCoin.png"]];
CGRect rectOne = CGRectMake(arc4random() % (900), arc4random() % (700), 40, 40);
[one setFrame:rectOne];
[self.view addSubview:one];
for (UIImageView* two in bombArray)
{
while (CGRectIntersectsRect(two.frame, one.frame))
{
one.center=CGPointMake(arc4random() % (900), arc4random() % (700));
}
}
*Note: I completely understand why the code above does not work, but I cannot find another solution to my problem.

First of all to make such a thing I'd use the new SpriteKit that gives you physics (and thus collision detection) out of the box.
But if you want to do this with standard UIViews what you can do for being absolutely sure that images don't collide between them is to divide the container in a grid and then loop through each cell of your grid and "flip a coin" to randomly put a coin or not in that cell. Then if you want to fill more space instead of dividing for rectangular cells you can divide for hexagonal cells, in this way you will cover more space.
So a fast and absolutely not too much checked version could be something like this:
int x = 0;
int y = 0;
int side = 40;
int it = 0;
do {
do {
CGRect imageFrame = CGRectMake(x, y, side, side);
UIImageView* imageView = [[UIImageView alloc] initWithFrame:imageFrame];
imageView.layer.borderColor = [UIColor blackColor].CGColor;
imageView.layer.borderWidth = 1.0;
imageView.layer.cornerRadius = side / 2;
imageView.layer.masksToBounds = YES;
[self.containerView addSubview:imageView];
BOOL flip = arc4random() & 0x1;
if (flip) {
[imageView setImage:[UIImage imageNamed:#"coin_image"]];
}
} while ((x+=side) < (self.containerView.frame.size.width - side / 2));
it++;
if (it % 2 != 0) {
x = side / 2;
} else {
x = 0;
}
} while ((y+=(side * 0.85)) < (self.containerView.frame.size.height - side) );
Mind that this is NOT efficient, it is executed in the main thread and doesn't take in account possible (actually probable) memory constraints.

Related

How to generate a line of images programmatically on Xcode

I am trying to make an iOS game and it requires for the players to catch as many coins as it can. I know to show the coins programmatically, but how do I make a line of coins with ease. Similarly like the game line runner where the runner has to catch the coins or jetpack joyride?
I'm thinking of something like
The game is landscape
randomPlace = arc3random()%315;
coinImage.center = CGPointMake(coinImage.center.x - 1, randomPlace);
coinImage1.center = CGPointMake(coinImage1.center.x - 1, randomPlace);
I don't even know if I'm on the right track but can anyone help me? thank you!
You could use something like this to create 5 images in a horizontal line:
//Generate your variables
float width = 15, height = 15;
float originX = 20, originY = 50, spacing = 5;
//Create 5 image views
for (int i = 0; i < 5; i++) {
UIImageView *imgV = [[UIImageView alloc] initWithFrame:CGRectMake(originX + ((spacing + width) * i), originY, width, height)];
[imgV setImage:[UIImage imageNamed:#"YourImage.png"]];
[self.view addSubview:imgV];
}
Which will produce something like this:

Fill view with buttons at random positions

i want to fill area (UIView) with buttons(UIButton) so that they do not intersect with each others.
My idea:
create initial button at random position in view;
fill view with other buttons from initial button (count < 20) that they do not intersect ~10 pixels from each other.
What have i done so far:
I created method:
-(void)generateButtonsForView:(UIView *)view buttonCount:(int)count
{
//get size of main view
float viewWidth = view.frame.size.width;
float viewHeight = view.frame.size.height;
//set button at random position
UIButton *initialButton = [[UIButton alloc] initWithFrame:CGRectMake(arc4random() % (int)viewWidth,
arc4random() % (int)viewHeight,
buttonWidth, buttonHeight)];
[initialButton setBackgroundImage:[UIImage imageNamed:#"button"] forState:UIControlStateNormal];
[view addSubview:initialButton];
// set count to 20 - max number of buttons on screen
if (count > 20)
count = 20;
//fill view with buttons from initial button +- 10 pixels
for (int i=0;i<=count;i++)
{
//button
UIButton *otherButtons = [[UIButton alloc] init];
[otherButtons setBackgroundImage:[UIImage imageNamed:#"button"] forState:UIControlStateNormal];
...//have no idea what to do here
}
}
So i'm confused on the place where i want to generate the position of the other buttons, depending on the initial button. I do not know how to generate theirs position that they were at a distance of 5-10 pixels from each other... Any ideas how to realise this? Thanks!
Here's an example with views rather than buttons, but the concept is the same. I use CGRectInset to give the new potential view a buffer of 10 points around it, then look for whether the new view intersects any of the other views. If there's no intersection, add the subview, if there is, try again with a new random location.
-(void)generateButtonsForView {
float viewWidth = self.view.frame.size.width;
float viewHeight = self.view.frame.size.height;
UIView *initialView = [[UIView alloc] initWithFrame:CGRectMake(arc4random() % (int)viewWidth, arc4random() % (int)viewHeight, 50, 30)];
initialView.backgroundColor = [UIColor redColor];
[self.view addSubview:initialView];
int numViews = 0;
while (numViews < 19) {
BOOL goodView = YES;
UIView *candidateView = [[UIView alloc] initWithFrame:CGRectMake(arc4random() % (int)viewWidth, arc4random() % (int)viewHeight, 50, 30)];
candidateView.backgroundColor = [UIColor redColor];
for (UIView *placedView in self.view.subviews) {
if (CGRectIntersectsRect(CGRectInset(candidateView.frame, -10, -10), placedView.frame)) {
goodView = NO;
break;
}
}
if (goodView) {
[self.view addSubview:candidateView];
numViews += 1;
}
}
}

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.

Multiple views with shadow and rotation becomes very slow

I'm trying to make multiple stacks of uiview's. Each of those UIView's with a shadow, slight rotation and scale. As a test I'm making 10 stack's of 10 views. Drawing all this is very slow.. Is there a good way to optimise this? I tried making the shadow and background from a image but that was ugly and equally as slow. I put these stacks in a UIScrollView
for (int k = 0; k < 9; k++) {
for (int i = 0; i < 9; i++) {
UIView *stack = [[UIView alloc] initWithFrame:CGRectMake((i * 106), (k * 106), 110, 110)];
for (int i = 0; i < 10; i++) {
CardView *cardView = [[CardView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
//cardView.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"card_background.png"]];
cardView.layer.shadowOffset = CGSizeMake(0, 0);
cardView.layer.shadowOpacity = 0.3f;
cardView.layer.shadowPath = [UIBezierPath bezierPathWithRect:(CGRect){CGPointZero, cardView.layer.bounds.size}].CGPath; // Set shadow path, without this the performance is *really* bad
cardView.transform = CGAffineTransformMakeRotation(((arc4random() % 20) - 10.0f) / 100.0f);
cardView.transform = CGAffineTransformScale(cardView.transform, 0.35, 0.35);
cardView.layer.shouldRasterize = YES;
cardView.layer.rasterizationScale = 0.5;
cardView.center = CGPointMake(55, 55);
[stack addSubview:cardView];
}
[_backgroundView addSubview:stack];
}
}
Edit 1; Tried some stuff, disabling rasterzation isn't helping much, disabling the shadow doesn't help much either, rotation and scaling are recourse intensive with this much uiview's too. Would async drawing (one stack at the time) be an option?
Edit 2; Guess making 100 UIView's is just slow anyway. I'll report back if I've found a better (I guess async or something like that) solution
Are you using these images in some sort of animation? If not you can try turning off rasterizing to increase performance.
cardView.layer.shouldRasterize = NO;
Shadows are very computationally intensive. If shouldRasterize doesn't do it, make a UIImage for the shadow, and if your views can change size, load the image with resizableImageWithCapInsets.

How do I animate a sprite sheet in Xcode without Cocos2d?

I'm developing a simple application that animates an image as the user moves a slider. This could easily be done with individual images, but for obvious reasons that method is inefficient.
Currently, I have the animation broken up into 14 sprite sheets with 16 images per sheet. I created a method that uses CGImageCreateWithImageInRect to find the current image dictated by the slider and update the image view with that image. This works, but not fluidly. I think I understand why, but I have no clue what to do otherwise. While I could use Cocos2d or OpenGL ES, I am stubborn and convinced that this is possible without them. I just want to know how.
Here's some example code:
- (void)setUp{
NSString *string;
NSString *bundleString = [[NSBundle mainBundle] bundlePath];
dsRedPathArray = [[NSMutableArray alloc] initWithCapacity:15];
for (int i = 0; i < 14; i++)
{
string = [bundleString stringByAppendingFormat:#"/dsRedAni_%d.png", i];
[dsRedPathArray addObject:string];
}
//initial image starts at (0, 1) of image dsRedAni_9
currentImage = [UIImage imageWithContentsOfFile:[dsRedPathArray objectAtIndex:9]];
currentRef = CGImageCreateWithImageInRect(currentImage.CGImage, CGRectMake(495, 0, kModelWidth, kModelHeight));
modelView.image = [UIImage imageWithCGImage:currentRef];
}
- (IBAction)sliderMoved:(UISlider*)sender
{
[self animateModel:sender.value];
}
- (void)animateModel:(int)index
{
index += 1;
imageIndex = (index / 16) + 9;
if (imageIndex > 13)
{
imageIndex = -14 + imageIndex;
}
currentX = kModelWidth * (index % 4);
currentY = kModelHeight * ((index / 4) % 4);
currentRect = CGRectMake(currentX, currentY, kModelWidth, kModelHeight);
currentImage = [UIImage imageWithContentsOfFile:[dsRedPathArray objectAtIndex: (imageIndex)]];
currentRef = CGImageCreateWithImageInRect(currentImage.CGImage, currentRect);
modelView.image = [UIImage imageWithCGImage:currentRef];
}
Thanks in advance for any help.
I finally found a rather quick and efficient, if unconventional, way of cycling through each section of my sprite sheets without any outside help from APIs. Rather than spending time and power cutting up the image into contexts or individual files, I found that it was more efficient for me to create a UIView in the size of the image I needed and then adding the entire sprite sheet to the view as a UIImageView.
With view.clipsToBounds set to YES, the view acts as a mask for my sprite sheet, limiting the visible portion to the size of each image I want to cycle through on the sheet. In order to give the effect of animation, I simply use imageview.center to move the sprite sheet around the view to the desired image coordinates. So, in a sprite-sheet with 16 images I only have to make one call to show the image, then move it around per each frame in the animation.
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self setUp];
self.backgroundColor = [UIColor clearColor];
self.clipsToBounds = YES;
}
return self;
}
- (void)setUp
{
//assign |lastImageIndex| a number not equal to |imageIndex|
lastImageIndex = -1;
modelImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 1924, 1708)];
NSString *string;
NSString *bundleString = [[NSBundle mainBundle] bundlePath];
UIImage *image;
if (!imageArray)
{
imageArray = [[NSMutableArray alloc] init];
NSLog(#"Init egfp |imageArray|");
}
for (int i = 0; i < 10; i++)
{
string = [bundleString stringByAppendingFormat:#"/moleculeAni_%d.png", i];
image = [UIImage imageWithContentsOfFile:string];
[imageArray addObject:image];
if (i == 9)
{
NSLog(#"Filled egfp |imageCache|");
}
}
[self addSubview:modelImageView];
}
- (void)animateModel:(int)index
{
if (index != 1)
{
index -= 1;
}
imageIndex = (index / 16);
if (imageIndex < 9)
{
currentX = 962 - (481 * (index % 4));
currentY = 854 - (427 * ((index / 4) % 4));
}
else
{
currentX = 962 - (481 * (index % 4));
currentY = 427 - (427 * ((index / 4) % 4));
}
if (imageIndex != lastImageIndex)
{
if (imageIndex < 9 && onLastFrame)
{
modelImageView.frame = CGRectMake(0, 0, 1924, 1708);
onLastFrame = NO;
}
else if (imageIndex == 9 && !onLastFrame)
{
modelImageView.frame = CGRectMake(0, 0, 1924, 854);
onLastFrame = YES;
}
NSLog(#"Image: %d", imageIndex);
tempImage = [imageArray objectAtIndex:imageIndex];
modelImageView.image = tempImage;
}
modelImageView.center = CGPointMake(currentX, currentY);
lastImageIndex = imageIndex;
}
Most of the code here is spent determining where the imageview needs to move in order to display the correct image. This is taking the method I explained above and is spreading it across 10 sprite sheets each with 16 images spread evenly (except the last which has 8). The only slowdown came when the program was swapping between images every 16th frame. To get around this, my controller has the view quickly cycle through all the images beneath another view (like a curtain) so the user is only privy to a loading screen. Once the images have been cycled, the curtain view is removed and the animation (controlled by the user with a slider) moves like butter.
One way to do this would be to cut up the images with CGImageCreateWithImageInRect, like you started to above. Then, add them to an array:
myArray addObject:[UIImage imageWithCGImage:currentRef];
Next use a UIImageView with setAnimationImages, like this:
UIImageView *myAnimationImageView;
[myAnimationImageView setAnimationImages:myArray];
Then You can start the animation.
There is a good project doing this here: https://github.com/r3econ/UIImage-Sprite-Additions

Resources