I'm trying to create 10 balloons in my app. I create a random CGPoint to set my node's position and check, in case that already exist a node at this point I try again.
I don't know why, the balloons keep being placed over another balloon.
Any ideia of what I am doing wrong?
Here is my code:
-(void)gerarBaloes:(int)quantidade
{
balloons = [NSMutableArray new];
u_int32_t maxHorizontal = 900;
u_int32_t minHorizontal = 100;
u_int32_t maxVertical = 650;
u_int32_t minVertical = 100;
for (int i=1; i<= quantidade; i++) {
CGPoint posicao = CGPointMake(arc4random_uniform(maxHorizontal - minHorizontal + 1) + minHorizontal, arc4random_uniform(maxVertical - minVertical + 1) + minVertical);
while (![self validPosition:posicao]) {
posicao = CGPointMake(arc4random_uniform(maxHorizontal - minHorizontal + 1) + minHorizontal, arc4random_uniform(maxVertical - minVertical + 1) + minVertical);
}
balao* nBalao = [[balao alloc]initWithPosition:posicao];
SKLabelNode* numero = [[SKLabelNode alloc]initWithFontNamed:#"Arial Rounded MT Bold"];
numero.text = [NSString stringWithFormat:#"%d",i];
numero.zPosition = 1;
nBalao.body.zPosition = 0;
[nBalao addChild:numero];
nBalao.body.name = #"balloon";
[balloons addObject:nBalao];
[self addChild:nBalao];
}
}
And this is my validation method:
-(BOOL)validPosition:(CGPoint)position
{
NSArray* nodes = [self nodesAtPoint:position];
NSLog(#"total de nodes %d",nodes.count);
if (nodes.count > 0) {
for (SKNode* n in nodes) {
if ([n isKindOfClass:[balao class]]) {
return NO;
}
}
return YES;
}else{
return YES;
}
}
Balao.m class:
#implementation balao
-(id)initWithPosition:(CGPoint)pos
{
if (self = [super init]) {
self.position = pos;
int n = arc4random_uniform(4);
_balloonAtlas = [SKTextureAtlas atlasNamed:[NSString stringWithFormat:#"balloon%d",n]];
_explosionFrames = [NSMutableArray array];
int numImages = _balloonAtlas.textureNames.count;
for (int i=1; i<= numImages; i++){
NSString *textureName = [NSString stringWithFormat:#"balloon_%d",i];
SKTexture *temp = [_balloonAtlas textureNamed:textureName];
[_explosionFrames addObject:temp];
}
SKTexture *textInicial = [_explosionFrames[0] copy];
[_explosionFrames removeObjectAtIndex:0];
self.body = [SKSpriteNode spriteNodeWithTexture:textInicial];
[self addChild:_body];
}
return self;
}
- (void)explodeBalloon
{
SKAction* explosao = [SKAction animateWithTextures:_explosionFrames timePerFrame:0.02f resize:NO restore:YES];
[self.body runAction:explosao completion:^(void){
[self removeFromParent];
}];
[self runAction:[SKAction playSoundFileNamed:#"popSound.mp3" waitForCompletion:NO]];
}
This is how the balloons appears:
Edit:
I tried the suggested method, with CGRectCointainsPoint, but it didn't work too. :/
Checking for nodes at a given point is not enough to prevent overlap. You need to check whether the point falls inside the frame of another balloon. You can modify the function like this
-(BOOL)validPosition:(CGPoint)position
{
NSArray* nodes = [self children];
if (nodes.count > 0) {
for (SKNode* n in nodes) {
if ([n isKindOfClass:[balao class]]) {
if (CGRectContainsPoint([n getBalloonFrame], position)) // Changed line
{
return NO
}
}
}
return YES;
}
return YES;
}
CGRectContainsPoint checks whether a given rectangle contains a point.
Inside balao class define the following function. Also note the changed line in the above code.
-(void)getBalloonFrame
{
return CGRectOffset(self.body.frame, self.frame.origin.x, self.frame.origin.y)
}
Related
I have this array with strings and I want to randomize it and display the output in a label. But with this code I'm having always the same strange output which is "2"...Any ideas what I'm doing wrong?
- (void)viewDidLoad {
[super viewDidLoad];
self.quoteInput.delegate=self;
self.quoteLabel=_quotationLabel;
self.correctLabel.alpha=0;
self.wrongLabel.alpha=0;
_quoteInput.delegate=self;
_levelOne = #[
[words Quotes:#"First Quote."],
[words Quotes:#"Second Quote."],
[words Quotes:#"And so on."]];
}
-(void)randomQuotes{
for (NSInteger x = 0; x < [_levelOne count]; x++) {
NSInteger randInt = arc4random() % ([_levelOne count] - x) + x;
[_levelOne objectAtIndex:randInt];
NSString *one = [NSString stringWithFormat:#"%d", randInt];
self.quoteLabel.text = one;
}
}
- (IBAction)generateQuote:(UIButton *)sender {
[self randomQuotes];
}
EDIT
The words class:
+ (instancetype)Quotes:(NSString *)quotes
{
return [[words alloc] initWithQuotes:quotes];
}
Unless you can explain what [word Quotes:] method does, it should be much simpler than what you are trying to do.
_levelOne = #[#"First Quote.", "Second Quote.", #"And so on."];
-(void) randomQuotes{
NSInteger randInt = arc4random_uniform( [_levelOne count] );
self.quoteLabel.text = [_levelOne objectAtIndex:randInt];
}
In .m file i have methods like
-(void) textdata
{
long i = search.text.length;
if (i > 0) {
searchButton.enabled = YES;
}
else
{
searchButton.enabled = NO;
}
[self buttonstate];
}
-(int) buttonstate
{
if ([searchButton isEnabled]) {
j = 1;
}
else
j = 0;
NSLog(#" j value is %d",j);
return j;
}
- (void) textFieldDidChange
{
[self textdata];
NSLog(#"test data is %#",search.text);
}
And in the tests.m file i have test like
-(void) testwithoutData
{
myapiViewController *apiViw = [[myapiViewController alloc]init];
[apiViw textdata];
int kn = [apiViw buttonstate];
NSLog(#"the value is %ld",(long)kn);
}
You have to do alloc init because due to this instance variable getting nil. Try this -
In ViewController.m -
-(int) buttonstate {
long i = self.searchTxt.text.length;
if (i > 0) {
self.searchBtn.enabled = YES;
}
else
{
self.searchBtn.enabled = NO;
}
if ([self.searchBtn isEnabled]) {
j = 1;
}
else
j = 0;
NSLog(#" j value is %d",j);
return j;
}
In ViewControllerTests.m -
- (void)testExample {
// This is an example of a functional test case.
XCTAssert(YES, #"Pass");
self.vcToTest.searchTxt = [[UITextField alloc] init];
self.vcToTest.searchBtn = [[UIButton alloc] init];
self.vcToTest.searchTxt.text = #"test";
int kn = [self.vcToTest buttonstate];
NSLog(#"the value is %ld",(long)kn);
}
Check the attached screenshot output -
I am making a drawing app, and I have a NSArray with a list of all the pixels the user forget or didn't clean, what i want is make groups of that list pixels using proximity. so i can highlight all the pixel area with a black circle. this is what i got for the moment but is not grouping well
-(NSMutableArray*)orderMissingPixelsByGroups:(NSMutableArray*)missingPixels
{
NSMutableArray *finalArray=[[NSMutableArray alloc]init];
NSMutableArray *auxArray = [[NSMutableArray alloc]init];
for (int i=0; i<[missingPixels count]; i++) {
NSValue *value = [missingPixels objectAtIndex:i];
if (i<[missingPixels count]-1) {
NSValue *nextValue = [missingPixels objectAtIndex:i+1];
CGPoint point = value.CGPointValue;
CGPoint point2 = nextValue.CGPointValue;
if (abs(point.x-point2.x)<5 ) {
[auxArray addObject:[NSValue valueWithCGPoint:point]];
}
else if (abs(point.y-point2.y)<5){
[auxArray addObject:[NSValue valueWithCGPoint:point]];
}
else
{
[auxArray addObject:[NSValue valueWithCGPoint:point]];
[finalArray addObject:auxArray];
auxArray = [[NSMutableArray alloc]init];
}
}
else
{
[auxArray addObject:value];
[finalArray addObject:auxArray];
}
}
return finalArray;
}
If you have advices I will very thankful.
Yes I can imagine this is not the best approach. Try doing something like this. Note I did not test it also there is just too much room for optimizing this approach but I hope it will help you get a bit better understanding on how to create "blobs". Otherwise you can try to search for some fast approach under "blob" keyword...
- (NSMutableArray *)createBlobsFromPixels:(NSMutableArray *)pixels {
NSMutableArray *initialBlobs = [[NSMutableArray alloc] init];
for(NSValue *pixel in pixels)
{
[initialBlobs addObject:#[pixel]];
}
while (YES) {
NSIndexSet *indexSet = [self joinBlobs:initialBlobs minimumRadius:5.0f];
if(indexSet.count < 2) {
break;
}
else {
[self mergeBlobs:initialBlobs atIndexA:indexSet.firstIndex andIndexB:indexSet.lastIndex];
}
}
return initialBlobs;
}
- (NSIndexSet *)joinBlobs:(NSMutableArray *)blobs minimumRadius:(CGFloat)minRadius {
// this method will try to join 2 blobs
// if successfull it will return an index set with 2 blob indices which should be joined
// if no blobs are able to join the method will return nil
if(blobs.count < 2) {
// can not join a single blub
return nil;
}
NSUInteger count = blobs.count;
// iterate through every pair
for(NSUInteger i=0; i<count-1; i++) {
for(NSUInteger j=i+1; j<count; j++) {
NSArray *blobA = blobs[i];
NSArray *blobB = blobs[j];
// check if any of the pixels from blob A are close enough from any pixel in blob B and merge those two blobs
for(NSValue *pixelA in blobA) {
for(NSValue *pixelB in blobB) {
CGPoint pointA = [pixelA CGPointValue];
CGPoint pointB = [pixelB CGPointValue];
if(fabsf(pointA.x-pointB.x)<minRadius && fabsf(pointA.y-pointB.y)<minRadius) {
// can join these 2 blobs
NSMutableIndexSet *indexSet = [[NSMutableIndexSet alloc] init];
[indexSet addIndex:i];
[indexSet addIndex:j];
return indexSet; // returns YES as the blob can been joined
}
}
}
}
}
return nil;
}
- (void)mergeBlobs:(NSMutableArray *)blobs atIndexA:(NSUInteger)indexA andIndexB:(NSUInteger)indexB {
NSArray *blobA = blobs[indexA];
NSArray *blobB = blobs[indexB];
[blobs removeObject:blobA];
[blobs removeObject:blobB];
[blobs addObject:[blobA arrayByAddingObjectsFromArray:blobB]];
}
I am very new to objective-C, so the task seems typical for me. I am plotting a custom map of rectangular blocks, and also need to draw an interactive route between two given blocks. All the thing is fine, but the problem is that the route drawn using A-Star algorithm goes diagonally looks very scattered. I want a smooth route made by simple lines without any diagonal.
-(void)findPath:(int)startX :(int)startY :(int)endX :(int)endY
{
int x,y;
int newX,newY;
int currentX,currentY;
NSMutableArray *openList, *closedList;
if((startX == endX) && (startY == endY))
return;
openList = [NSMutableArray array];
//BOOL animate = [shouldAnimateButton state];
if(animate)
pointerToOpenList = openList;
closedList = [NSMutableArray array];
PathFindNode *currentNode = nil;
PathFindNode *aNode = nil;
PathFindNode *startNode = [PathFindNode node];
startNode->nodeX = startX;
startNode->nodeY = startY;
startNode->parentNode = nil;
startNode->cost = 0;
[openList addObject: startNode];
while([openList count])
{
currentNode = [self lowestCostNodeInArray: openList];
if((currentNode->nodeX == endX) && (currentNode->nodeY == endY))
{
//********** PATH FOUND ********************
aNode = currentNode->parentNode;
while(aNode->parentNode != nil)
{
tileMap[aNode->nodeX][aNode->nodeY] = TILE_MARKED;
aNode = aNode->parentNode;
}
return;
//*****************************************//
}
else
{
[closedList addObject: currentNode];
[openList removeObject: currentNode];
currentX = currentNode->nodeX;
currentY = currentNode->nodeY;
for(y=-1;y<=1;y++)
{
newY = currentY+y;
for(x=-1;x<=1;x++)
{
newX = currentX+x;
if(y || x) //avoid 0,0
{
if((newX>=0)&&(newY>=0)&&(newX<H)&&(newY<W))
{
if(![self nodeInArray: openList withX: newX Y:newY])
{
if(![self nodeInArray: closedList withX: newX Y:newY])
{
if(![self spaceIsBlocked: newX :newY])
{
//the 'cost':
aNode = [PathFindNode node];
aNode->nodeX = newX;
aNode->nodeY = newY;
aNode->parentNode = currentNode;
aNode->cost = currentNode->cost + 1;
//distance, added to the existing cost
aNode->cost += (abs((newX) - endX) + abs((newY) - endY));
//aNode->cost += MAX(abs(newX - endX), abs(newY - endY));
//aNode->cost += sqrt(pow(newX - endX, 2) + pow(newY - endY, 2));
NSLog(#"Path: %d",aNode->cost);
[openList addObject: aNode];
// if(animate) // animation
// [self setNeedsDisplay];
}
}
}
}
}
}
}
}
}
//**** NO PATH FOUND *****
UIAlertView *alert=[[UIAlertView alloc]initWithTitle:#"OOPs!!" message:#"Couldn't find a path, Please try for another nearest Exhibitor." delegate:self cancelButtonTitle:#"Done" otherButtonTitles:nil, nil];
[alert show];
}
Finally i am running a loop to check tileMap[][], and the node array tileMap[x][y] marked as TILE_MARKED is the nodes to be drawn in blue colored route.
I am attaching the image below, can anyone kindly help me?
Thanks in advance and have a great day.
-asim
I have a ball sprite that falls from the top and a boy underneath it. When the ball hits the boy's head I want it to follow a parabolic path. I tried using CCJumpTo as follows but I cannot get it to work. I'm calling the action inside the update loop. Am I not allowed to do that? Cant I call CCJumpTo inside the update loop?
- (void) jump
{
if(!method_called)
{
method_called=TRUE;
CCActionInterval *jump1 = [CCJumpTo actionWithDuration:3 position:CGPointMake(400, 400) height:150 jumps:1];
[_ball runAction:jump1];
NSLog(#"something");
}
else
{
NSLog(#"do nothing");
}
}
- (void)update:(ccTime)dt
{
time ++;
if ( dpad.leftJoystick.velocity.x > 0 && x < 2000 ) {
x = x + 10;
} else if ( dpad.leftJoystick.velocity.x < 0 && x > 0 ) {
x = x - 10;
}
if (x > 10 && x < 2000)
_boy.position = ccp(x, 100);
_ball.position = ccp(ball_x, ball_y);
ball_y = _ball.position.y - (speed * rebound);
_boy.anchorPoint = ccp(0, 0);
CGRect boyBox = CGRectMake(_boy.position.x, _boy.position.y, [_boy boundingBox].size.width, [_boy boundingBox].size.height);
_ball.anchorPoint = ccp(0, 0);
CGRect ballBox = CGRectMake(_ball.position.x, _ball.position.y, [_ball boundingBox].size.width, [_ball boundingBox].size.height);
if (CGRectIntersectsRect(boyBox, ballBox) && flag == 0)
{
rebound = -rebound;
flag = 1;
topFlag = 0;
[_ball stopAllActions];
[self jump];
}
if (_ball.position.y > 700 && topFlag == 0)
{
rebound = -rebound;
flag = 0;
topFlag = 1;
}
}
Thanks in advance.
EDIT:
Here's my init method
-(id) init
{
// always call "super" init
// Apple recommends to re-assign "self" with the "super's" return value
if( (self=[super init]) ) {
CGSize winSize = [[CCDirector sharedDirector] winSize];
self.ball = [CCSprite spriteWithFile:#"ball.png"
rect:CGRectMake(0, 0, 50, 50)];
self.ball.position = ccp(275, 999);
[self addChild:self.ball];
x = 0;
ball_y = 650;
ball_x = 300;
rebound = 1;
flag = 0;
topFlag = 0;
speed = 5;
time = 0;
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:
#"boy.plist"];
CCSpriteBatchNode *spriteSheet = [CCSpriteBatchNode batchNodeWithFile:#"boy.png"];
//spriteSheet.scaleX = 0.5;
//spriteSheet.scaleY = 0.5;
NSMutableArray *walkAnimFrames = [NSMutableArray array];
for(int i = 1; i <= 8; ++i) {
[walkAnimFrames addObject:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:[NSString stringWithFormat:#"boy%d.png", i]]];
CCAnimation *walkAnim = [CCAnimation animationWithFrames:walkAnimFrames delay:.1f];
self.boy = [CCSprite spriteWithSpriteFrameName:#"boy1.png"];
_boy.position = ccp(100000, 100000);
self.walkAction = [CCRepeatForever actionWithAction:[CCAnimate actionWithAnimation:walkAnim]];
[CCAnimate actionWithAnimation:walkAnim restoreOriginalFrame:NO];
[_boy runAction:_walkAction];
_boy.flipX = YES;
[spriteSheet addChild:_boy];
dpad = [[MyJoystick alloc] init];
[self addChild:dpad z:10];
[self schedule:#selector(update:)];
}
[self addChild:spriteSheet];
}
return self;
}
Keep your action code inside a method and call it from update method using a boolean variable so that it would be called once only. Something like this:
take a boolean variable method_called in .h file:
-(void)update:(ccTime)dt
{
_boy.anchorPoint = ccp(0, 0);
CGRect boyBox = CGRectMake(_boy.position.x, _boy.position.y, [_boy boundingBox].size.width, [_boy boundingBox].size.height);
_ball.anchorPoint = ccp(0, 0);
CGRect ballBox = CGRectMake(_ball.position.x, _ball.position.y, [_ball boundingBox].size.width, [_ball boundingBox].size.height);
if (CGRectIntersectsRect(boyBox, ballBox) && flag == 0)
{
flag = 1; topFlag = 0;
[self callJumpAction];
}
}
-(void)callJumpAction
{
if(!method_called)
{
method_called=true;
CCActionInterval *jump1 = [CCJumpTo actionWithDuration:3 position:CGPointMake(400, 400) height:150 jumps:1];
[_ball runAction:jump1];
}
else
{
NSLog(#"do nothing");
}
}
Hope this will help.