I'm making a 2d Game, in which I need instances of a sprite to fly randomly across the screen. They will spawn in randomly just beyond the boundaries of the iPhone screen, then move within the screen. When they hit the edges, they will appear back on the other side. All I need to know is how to get the sprite to move randomly.
Add this method to your layer class - it takes in a sprite and then moves it randomly around the screen for ever:
-(void)moveRandom:(CCSprite*)s
{
CGPoint randomPoint = ccp(arc4random()%480, arc4random()%320);
NSLog(#"%#", NSStringFromCGPoint(randomPoint));
[s runAction:
[CCSequence actions:
[CCMoveTo actionWithDuration:arc4random()%5+1 position: randomPoint],
[CCCallBlock actionWithBlock:^{
[self performSelector:#selector(moveRandom:) withObject:s afterDelay:0.5];
}],
nil]
];
}
As you can see it's pretty easy - generate a random point on the screen and then run move action on the sprite to that point. When that's done - just repeat.
To add a sprite on the screen and start the process, put this (probably) in your scene init method or wherever you do the scene initialization:
CCSprite* s = [CCSprite spriteWithFile:#"yourImage.png"];
[self addChild: s];
[self moveRandom:s];
Related
all.
I am now developing an ios game using sprite kit.The game mechanics include: infinitely scroll sprites from top to bottom.
My game scene holds just 3 sprites,top,middle,bottom.
I have dozens of sprite images,so my solution is dynamic create 3 sprites ( so the sprites can fill the whole screen),when the bottom sprite is off screen,then destroy it;when the top sprite
goes down the top frame,create a new sprite node,then exchange the bottom,middle,top sprite.
The pseudo code:
in the interface file:
- GameScene:SKScene
{
...
SKSpriteNode *_topSprite;
SKSpriteNode *_middleSprite;
SKSpriteNode *_bottomSprite;
...
}
in the implementation file:
- (void)update:(CFTimeInterval)currentTime
{
// 1 compute time interval
// 2 update sprite node
_topSprite.position move down 100*timeInterval
_middleSprite.position move down 100*timeInterval
_bottomSprite.position move down 100*timeInterval
// 3 crop the off-screen bottom sprite node
if (_bottomSprite is offScreen)
{
[_bottomSprite removeFromParent]
}
// 4 check the highest sprite position visible
// if the highest position is below the frame top,then create new sprite
_bottomSprite = _middleSprite;
_middleSprite = _topSprite;
_topSprite = [self createNewNode]; // random a sprite image
}
the game fps is 60,and the nodes counts is not increasing.
All seems good,but i found sometimes the game just suddenly has a very shot choke causing the moving frame not continuous.
In my instinct,I thought the main reason is caused by step 4.
But I don't have any idea to solve it.
Any suggestions will be appreciated.
Here is the createNewNode method:
- (SKSpriteNode *)createNewNode {
NSString *blockName = [self randomImageName];
SKSpriteNode *sprite = [[SKSpriteNode alloc] initWithImageNamed:blockName];
sprite.position = _highestPosition;
sprite.anchorPoint = CGPointMake(0.5, 0);
NSLog(#"putAnotherNodeOnTop %#", blockName);
[self addChild:sprite];
return sprite;
}
A couple of suggestions.
Since you are using the same three images, you don't need to re-create the sprites each time. It should be less expensive to set the hidden value to YES, move the sprite somewhere, set the texture and then show it again with hidden to NO. SpriteKit doesn't draw hidden nodes, or ones off screen.
Your code basically asks for a PNG on disk every frame in update when createNewNode: is called. It should be more performant to load the textures in memory and assign them to sprites as needed. If you have just a few, making them SKTexture instance variables would be an easy way to do it.
Use spritesheets (texture atlases), it is much faster to draw an image from memory at mapped coordinates than to load it from disk each time. For example with three images in a spritesheet this can be a single draw call. From disk this is three separate draw calls.
You are returning a sprite and adding it to the scene in the createNewNode: method. Maybe that is what you want for some reason. It stood out as possible duplicate effort.
I am using an array to try to load images into specific UIImageViews. I have 10 UIImageViews set up on my storyboard.
I also have a Game Over method that runs whenever the frame of one UIImageView intersects with another UIImageView. For some reason, whenever I try to use the array to load my UIImageViews, my Game Over method immediately triggers. I can't figure out why. As soon as I remove the initWithImage line, the game runs normally. Any ideas?
- (void)viewDidLoad
{
KanaCharacters = [[NSArray alloc] initWithObjects:[UIImage imageNamed:#"a.png"],...
nil];
int imageDisplay = arc4random_uniform([KanaCharacters count]);
Monster1 = [[UIImageView alloc] initWithImage:[KanaCharacters objectAtIndex:imageDisplay]];
}
-(void) Collision
{
if ((CGRectIntersectsRect(Monster1.frame, Ship.frame)) && (Monster1Hit==NO))
{
[self GameOver];
}
}
-(void)GameOver
{
WinOrLose.hidden=NO;
WinOrLose.text = [NSString stringWithFormat:#"You Lose!"];
}
I suspect that Collision if firing once an image in Monster1 and Ship.frame.
This is because I suspect your Monster is being placed where your ship is?
you may want to move the ship or the monster before you add the image into the view.
Every example I see on how to use transitions is with CCScene like this:
[[CCDirector sharedDirector] replaceScene:[CCTransitionCrossFade transitionWithDuration:0.5f scene:[NewScene scene]]];
My app is setup with a GameScene, MenuScene ..etc. Within the GameScene I setup the DPad and the HUD and under that I have the hero moving to different levels which are of type CCNode. I want to use the native transitions to move from one level to the next.
I found a question similar to what I need here but that user was essentially told to build the transitions himself.
The confusing part to me is that CCNode contains a method called:
- (void) onEnterTransitionDidFinish
So, is there a way to use transitions with CCNode that I'm missing? Is my app setup incorrectly and the levels should be CCScene? If so how to I keep the HUD and Dpad over the transition?
Thanks in advance for any help.
CCTransitions are specifically for scenes. CCNodes contain onEnterTransitionDidFinish, as it gets called on every CCNode child of a CCScene. Fortunately, you can just check the CCTransition code and see how they are implemented and then roll your own for layers (many have done and shared this).
For example, I wrote a simple little method which will use blocks to give the same fade-in, fade-out effect as a scene transition, but for an individual layer (along with some added features). Just pass in the layer you want to cover up, the speed of the fade out and fade in, the color to fade to, and the block you wish to execute while the layer is hidden.
-(void)fadeLayer:(CCLayer*)layer withOutDuration:(float)outTime inDuration:(float)inTime color:(ccColor3B)color withBlock: (void(^)())block
{
CGSize winSize = [[CCDirector sharedDirector] winSize];
CCLayerColor *toplayer = [CCLayerColor layerWithColor:ccc4(color.r, color.g, color.b, 0) width:winSize.width height:winSize.height];
[layer addChild:toplayer z:INT_MAX];
[toplayer runAction:
[CCSequence actions:
[CCFadeIn actionWithDuration:outTime],
[CCCallBlock actionWithBlock:block],
[CCFadeOut actionWithDuration:inTime],
[CCCallBlockN actionWithBlock:^(CCNode *node) {
[node removeFromParentAndCleanup:YES];
}],
nil]
];
}
I'm currently working on a game in which I add a CCSprite to a scene (an animated ghost), and have it move from a randomly generated point to another randomly generated point, fade out, and then be removed from the scene. When the player taps the ghost, 1 is added to the score.
Everything is working beautifully, but I want this action to be repeated until a condition is met (specifically, when the score reaches 50). I can't figure out the best way to do this. Every time I try to run a loop, it repeats infinitely and crashes the game.
What is the best way to achieve such a loop? I've heard mention of creating arrays for sprites, but there will only ever be one iteration of the ghost on the scene at one time. Are there any other ways of achieving this?
Ideally, what I want to achieve is: run the actions, wait for them to finish, and then run them again until the score is 50.
Here's my code:
-(void) ghostSpawner {
CGPoint endPoint = [self randomPointGenerator];
[self birthGhost];
CCSprite * Ghost = (CCSprite *) [self getChildByTag:2];
//...then fade him in
CCFiniteTimeAction *ghostBegin = [CCSequence actions:[CCDelayTime actionWithDuration:1],[CCTintTo actionWithDuration:0 red:0 green:0 blue:0],[CCTintTo actionWithDuration:.5 red:255 green:255 blue:255], nil];
//...move him over a given amount of time, then fade him out and DIE
CCFiniteTimeAction *ghostEnd = [CCSequence actions:[CCMoveTo actionWithDuration:2 position:endPoint],[CCTintTo actionWithDuration:1 red:0 green:0 blue:0],[CCCallFunc actionWithTarget:self selector:#selector(killGhost)], nil] ;
[Ghost runAction:[CCRepeatForever actionWithAction:[CCSequence actions:ghostBegin,ghostEnd, nil]]];
}
For generating the sprite:
-(void) birthGhost {
CGPoint spawnPoint = [self randomPointGenerator];
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"ghostidle.plist"];
CCSprite * Ghost = [CCSprite spriteWithSpriteFrameName:#"Ghost0001.png"];
[Ghost setColor:ccc3(0,0,0)];
Ghost.anchorPoint = ccp(.5, .5);
Ghost.position = spawnPoint;
Ghost.anchorPoint = ccp(0.5f,0.5f);
Ghost.scale = .4;
Ghost.tag = 2;
[Ghost runAction:ghostIdleAnimation()];
[self addChild:Ghost z:0];
}
The 'ghostSpawner' function is called in my 'init' on the scene
[self ghostSpawner];
I hope this is enough. Any feedback would be awesome, since this is one of my first real iPhone projects. Thanks in advance!
Alright, here's how I would do it:
Send ghostSpawner to self on init (as you do now).
In ghostSpawner, take your sprite, reset it (change the opacity to 0, reset the position, whatever), then send it 2 runAction messages: one to fade in, the other a sequence to move to the end position, then send a killGhost message to self.
In killGhost, check whether the score is less than 50. If it is, send ghostSpawner to self again, otherwise do whatever you do when they have reached 50 points.
Your sprite creation code should be in init. You can just reuse the same sprite. Also, there's CCFadeIn you can use instead of CCTintTo.
I'm making an iPhone App which involves balloons (UIImageViews) being spawned from the bottom of the screen and moving up to the top of the screen.
The aim is to have the UIImageViews set up so that a tap gesture on a balloon causes the balloons to be removed from the screen.
I have set up the GestureRecognizer and taps are being recognized.
The problem I am having is that the animation method I am using does not provide information about where the balloon is being drawn at a specific point in time, and treats it as already having reached the final point mentioned.
This causes the problem of the taps being recognized only at the top of the screen(which is the balloon's final destination).
I have pasted my code for animation here:
UIViewAnimationOptions options = UIViewAnimationOptionBeginFromCurrentState|UIViewAnimationOptionAllowUserInteraction;
[UIView animateWithDuration:2.0 delay:0.0 options:options animations:^{
balloonImageView.center = p;
} completion:^(BOOL finished) {
if (finished) {
balloonImageView.hidden= TRUE;
balloonImageView.image=nil;
}
I keep track of where an image is by giving a unique tag to a UIImageView and checking if gesture.view.tag = anyBalloonObject.tag and if it does I destroy the balloon.
My basic question is, is there another animation tool I can use? I understand that I can set a balloon's finish point to slightly higher than where it is on screen until it reaches the top but that seems like it is very taxing on memory.
Do you have any recommendations? I looked through the Core Animation reference, and didn't find any methods that suited my needs.
In addition will the Multi-threading aspects of UIViewAnimation affect the accuracy of the tap system?
Thank You,
Vik
You shouldnt animate game-objects (objects you interact with while they are moving) like that.
You should rather use a NSTimer to move the ballons.
-(void)viewDidLoad {
//Set up a timer to run the update-function
timer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:#selector(update) userInfo:NULL repeats:YES];
}
-(void) update {
for (int i = [balloonArray count] - 1; i >= 0; i--) { //Loop through an array containing all your balloons.
UIImageView *aBalloon = [balloonArray objectAtIndex:i]; //Select balloon at the current loop index
aBallon.center = CGPointMake(aBallon.center.x, aBalloon.center.y + 1); //Change the center of that balloon, in this case: make it go upwards.
}
}
Here is what you need to declare in .h:
NSTimer *timer;
NSMutableArray *ballonArray;
//You should use properties on these also..
You will have to create a spawn method which adds balloons to the screen AND to the array.
When you have done that, it's simple to implement the touch methods, You can just loop trough the balloons and check:
//You need an excact copy of the loop from earlier around this if statement!
If (CGRectContainsPoint(aBalloon.frame, touchlocation)) { //you can do your own tap detection here inthe if-statement
//pop the balloon and remove it from the array!
}
Hope that isnt too scary-looking, good luck.