Best way to randomly move sprite forever in Sprite Kit - ios

I have a sprite and I want to move it to random points forever. I have written this code, but I don't think it is efficient.
-(void)addBoss {
SKSpriteNode *boss = [SKSpriteNode spriteNodeWithImageNamed:#"Spaceship"];
boss.position = CGPointMake(self.size.width + boss.size.width / 2.0, self.size.height / 2.0);
boss.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:boss.size];
boss.physicsBody.dynamic = YES;
boss.physicsBody.categoryBitMask = bossCategory;
boss.physicsBody.contactTestBitMask = bossContact;
boss.physicsBody.collisionBitMask = 0;
boss.zPosition = 1;
self.boss = boss;
self.bossHealth = bossHP;
CGPoint destination = CGPointMake(self.size.width - boss.size.width / 2.0, boss.position.y);
float time = length(boss.position, destination) / bossSpeed;
SKAction *move = [SKAction moveTo:destination duration:time];
[self addChild:boss];
[self.boss runAction:[SKAction sequence:#[move, [SKAction runBlock:^{
[self artificialIntelligence];
}]]]];
}
- (void)moveBoss {
float minimumX = self.size.width / 2.0 + self.boss.size.width / 2.0;
float maximumX = self.size.width - self.boss.size.width / 2.0;
float minimumY = self.boss.size.height / 2.0;
float maximumY = self.size.height - self.boss.size.height / 2.0;
int rangeX = maximumX - minimumX;
int rangeY = maximumY - minimumY;
float x = arc4random() % rangeX + minimumX;
float y = arc4random() % rangeY + minimumY;
CGPoint dest = CGPointMake(x, y);
float duration = length(self.boss.position, dest) / putinSpeed;
[self.boss runAction:[SKAction moveTo:dest duration:duration] completion:^{
[self moveBoss];
}];
}
-(void)artificialIntelligence {
[self moveBoss];
}
This code works fine, but I don't think that calling the move method recursively after movement finished is not the best solution.
What is the best way to solve this kind of problem?

This is a quick and dirty way to do what you asked:
-(void)moveCharacter {
SKNode *playerNode;
// get random position
CGPoint destination = [self randomPosition];
if(!CGPointEqualToPoint(playerNode.position, destination)) {
// check xPos
if(playerNode.position.x > destination.x) {
playerNode.position = CGPointMake(playerNode.position.x-1, playerNode.position.y);
}
if(playerNode.position.x < destination.x) {
playerNode.position = CGPointMake(playerNode.position.x+1, playerNode.position.y);
}
// check yPos
if(playerNode.position.y > destination.y) {
playerNode.position = CGPointMake(playerNode.position.x, playerNode.position.y-1);
}
if(playerNode.position.y < destination.y) {
playerNode.position = CGPointMake(playerNode.position.x, playerNode.position.y+1);
}
} else {
destinationReached = YES;
}
}
-(CGPoint)randomPosition {
CGRect screenRect = [[UIScreen mainScreen] bounds];
int xPos = arc4random() % (int)screenRect.size.width;
int yPos = arc4random() % (int)screenRect.size.height;
return CGPointMake(xPos, yPos);
}
There are many different variations on how to move your character. You could for example divide the difference of x,y into a fixed number of movements as to have the player move in a smooth line from point A to B. If you are physics to move your player, you would have to use CGVector instead of directly changing his x,y position.

Related

Keeping track of SKNodes for a category

I have a function that adds enemies to my scene that get called by an timing interval
How can I keep track of the number of enemies on the Scene to limit the amount of enemies I have in each level?
**In my update function **
CFTimeInterval timeSinceLastEnemy = currentTime - self.lastEnemyUpdateTime;
self.lastEnemyUpdateTime = currentTime;
if (timeSinceLastEnemy > 1) { // more than a second since last update
timeSinceLastEnemy = 1.0 / 60.0;
self.lastEnemyUpdateTime = currentTime;
}
[self spwanEnemyWithTime:timeSinceLastEnemy];
**The timer and The method to add enemies **
- (void)spwanEnemyWithTime:(CFTimeInterval)timeSinceLast {
self.lastEnemySpawn += timeSinceLast;
if (self.lastEnemySpawn > 0.6) {
self.lastEnemySpawn = 0;
[self spawnEnemy];
}
}
-(void) spawnEnemy {
SKSpriteNode *enemy = [SKSpriteNode spriteNodeWithImageNamed: enemySprite];
int minX = 5;
int maxX = self.frame.size.width;
int rangeX = maxX - minX;
int actualX = (arc4random() % rangeX) + minX;
// Create the enemy slightly off-screen along the upper edge,
enemy.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(enemy.size.height - 10 , enemy.size.width)];
enemy.physicsBody.dynamic = YES;
enemy.physicsBody.categoryBitMask = enemyCategory;
enemy.physicsBody.contactTestBitMask = bulletCategory;
enemy.physicsBody.collisionBitMask = 0;
// and along a random position along the X axis as calculated above
enemy.position = CGPointMake(actualX, self.frame.size.height + enemy.size.height);
[self addChild:enemy];
enemy.xScale = 0.2;
enemy.yScale = 0.2;
enemy.zPosition = 4;
// Create the actions
SKAction * actionMove = [SKAction moveToY:(0 - enemy.size.height) duration:4];
SKAction * actionMoveDone = [SKAction removeFromParent];
[enemy runAction:[SKAction sequence:#[actionMove, actionMoveDone]]];
}
I confess I couldn't grasp what you are doing with the double timers, so I will assume that portion works as you want it to. for the enemy count couldn't this be as simple as incrementing a counter on your spawnEnemy and decrementing the counter when the enemy removes itself from the scene?
You'll have to double check my Obj-c I haven't used it in a while.
int enemyCount = 0;
-(void) spawnEnemy {
enemyCount += 1
...
// Create the actions
SKAction *actionMove = [SKAction moveToY:(0 - enemy.size.height) duration:4];
SKAction *actionMoveDone = [SKAction removeFromParent];
SKAction *decrementCount = [SKAction runBlock: ^(void) {
enemyCount -= 1;
}];
[enemy runAction:[SKAction sequence:#[actionMove, actionMoveDone, decrementCount]]];
}
FYI you have a typo in your method "spwanEnemyWithTime"

Spacing sprite onscreen

Hi all I'm hoping someone can tell me how to decrease the spacing between sprites added from an array at the moment they are evenly spaced and centered but I need to decrease the width between the sprites
the whole block of code just incase is kind enough to test the spacing
int images = 5
for(int i = 0; i < images; ++i)
{
///load same ammount of spriteimages as images
SKSpriteNode *sprite =
[SKSpriteNode spriteNodeWithImageNamed:#"Null.jpg"];
sprite.name = #"sprite.name =Sprite%d",[NSString stringWithFormat:#"%i",i];
NSLog(#"***sprite.name %#",[NSString stringWithFormat:#"%i",i]);
///spriteSpacing
float offsetFraction = ((float)(i + 1)) / (images + 1);
float widthOfScreen =self.frame.size.width;
sprite.position = CGPointMake(widthOfScreen * offsetFraction,self.frame.size.height/2 +200);
[self addChild:sprite];
}
try this and see what happens
float widthOfScreen = self.frame.size.width + 100.0;
in the end I did this
float padding = 100.0f;
int blockWidth = [SKSpriteNode spriteNodeWithImageNamed:#"Null.jpg"].size.width;
float xOffset = (self.frame.size.width - (blockWidth * images + padding * (images-1))) / 2;
//float xOffset = (self.frame.size.width - (blockWidth * numberOfBlocks + padding * (numberOfBlocks-1))) / 2;
float StartPoint = xOffset+blockWidth/2;
for(int i = 0; i < images; ++i)
{
SKSpriteNode *sprite = [SKSpriteNode spriteNodeWithImageNamed:#"Null.jpg"];
///set start point of 1st sprite to load
if (i>0&&i<images)
{
StartPoint = StartPoint+ blockWidth+padding;
}
//increment sprite position
else if (i==images)
{
StartPoint = StartPoint+ blockWidth;
}
sprite.position = CGPointMake(StartPoint, self.frame.size.height * 0.8f);
[self addChild:sprite];
}

Spawning sprites randomly on x-axis

Hi I need to spawn balloons which are my sprites along the bottom of the x axis out of the screen randomly. I used this code from Ray Wenderliches website but when i click the button that starts the spawning they don't spawn?
#implementation MyScene2
- (void)addMonster {
// Create sprite
SKSpriteNode * monster = [SKSpriteNode spriteNodeWithImageNamed:#"balloon11.png"];
// Determine where to spawn the monster along the X axis
int minX = monster.size.height / 2;
int maxX = self.frame.size.height - monster.size.height / 2;
int rangeX = maxX - minX;
int actualX = (arc4random() % rangeX) + minX;
// Create the monster slightly off-screen along the right edge,
// and along a random position along the X axis as calculated above
monster.position = CGPointMake(self.frame.size.width + monster.size.width/2, actualX);
[self addChild:monster];
// Determine speed of the monster
int minDuration = 2.0;
int maxDuration = 4.0;
int rangeDuration = maxDuration - minDuration;
int actualDuration = (arc4random() % rangeDuration) + minDuration;
// Create the actions
SKAction * actionMove = [SKAction moveTo:CGPointMake(-monster.size.width/2, actualX)
duration:actualDuration];
SKAction * actionMoveDone = [SKAction removeFromParent];
[monster runAction:[SKAction sequence:#[actionMove, actionMoveDone]]];
}
- (void)updateWithTimeSinceLastUpdate:(CFTimeInterval)timeSinceLast {
self.lastSpawnTimeInterval += timeSinceLast;
if (self.lastSpawnTimeInterval > 1) {
self.lastSpawnTimeInterval = 0;
[self addMonster];
}
}
- (void)update:(NSTimeInterval)currentTime {
// Handle time delta.
// If we drop below 60fps, we still want everything to move the same distance.
CFTimeInterval timeSinceLast = currentTime - self.lastUpdateTimeInterval;
self.lastUpdateTimeInterval = currentTime;
if (timeSinceLast > 1) { // more than a second since last update
timeSinceLast = 1.0 / 60.0;
self.lastUpdateTimeInterval = currentTime;
}
[self updateWithTimeSinceLastUpdate:timeSinceLast];
}
Your main problem lies in this lines of code:
monster.position = CGPointMake(self.frame.size.width + monster.size.width/2 , actualX);
That method works like this: CGPointMake( x_coord , y_coord );.
Right now, you are setting the x value to width of the frame plus the width of the sprite, so this will cause the sprite to be spawned off the screen. You are giving the y coordinate the randomized value. Instead, use
CGPointMake(actualX , self.frame.size.height);
Now there is another problem with this code. These coordinates are based on the view, but monster.position is based on the SKScene. The SKScene origin usually does not match up with the origin for the view. So you have to do it like this:
monster.position = [CGPointMake(actualX , self.frame.size.height); locationInNode: self];
Also, I noticed that you are using frame.size.width for intended Y axis coordinates and frame.size.height for intended X axis coordinates. Width is for x and height is for y, so make sure you are using those properly.

Changing Sprite Spawns

Here is my code that I m using below and i want it to spawn from the bottom of the x-axis in portrait mode right now in spawns from the top in portrait mode how do i edit the code to do this?
- (void)addBalloon {
// Create sprite
SKSpriteNode * monster = [SKSpriteNode spriteNodeWithImageNamed:#"balloon"];
// Determine where to spawn the monster along the X axis
int minX = monster.size.width / 2;
int maxX = self.frame.size.width - monster.size.width / 2;
int rangeX = maxX - minX;
int actualX = (arc4random_uniform(rangeX)) + minX;
// Create the monster slightly off-screen along the top,
// and along a random position along the X axis as calculated above
monster.position = CGPointMake(actualX, self.frame.size.height +
monster.size.height/2);
[self addChild:monster];
// Determine speed of the monster
int minDuration = 2.0;
int maxDuration = 4.0;
int rangeDuration = maxDuration - minDuration;
int actualDuration = (arc4random() % rangeDuration) + minDuration;
// Create the actions
SKAction * actionMove = [SKAction moveTo:CGPointMake(actualX, -
monster.size.height/2) duration:actualDuration];
SKAction * actionMoveDone = [SKAction removeFromParent];
[monster runAction:[SKAction sequence:#[actionMove, actionMoveDone]]];
}
I'm not sure what you are asking here. You want to spawn a sprite on the bottom of the X-axis?
In case it is what I think you are asking, you only have to switch:
monster.position = CGPointMake(actualX, self.frame.size.height + monster.size.height/2);
&&
SKAction * actionMove = [SKAction moveTo:CGPointMake(actualX, - monster.size.height/2) duration:actualDuration];
with:
monster.position = CGPointMake(actualX, - monster.size.height/2);
&&
SKAction * actionMove = [SKAction moveTo:CGPointMake(actualX, self.frame.size.height + monster.size.height/2) duration:actualDuration];

Set sprite using background position

I am new to cocos2d so please help me if u can
I have background moving from right to left, and the background contains small windows with 3 rows of windows
_spaceDust1 = [CCSprite spriteWithFile:#"bg_front_spacedust.png"];
_spaceDust2 = [CCSprite spriteWithFile:#"bg_front_spacedust.png"];
CGPoint dustSpeed = ccp(0.1 , 0.1);
CGPoint bgSpeed = ccp(0.05 , 0.05);
[_backgroundNode addChild:_spaceDust1 z:0 parallaxRatio:dustSpeed positionOffset:ccp(0,winSize.height / 2)];
[_backgroundNode addChild:_spaceDust2 z:0 parallaxRatio:dustSpeed positionOffset:ccp(_spaceDust1.contentSize.width , winSize.height / 2)];
Now add enemies wich also move from right to left with same speed
_robbers = [[CCArray alloc] initWithCapacity:kNumAstroids];
for (int i = 0; i < kNumAstroids; ++i) {
CCSprite *asteroid = [CCSprite spriteWithSpriteFrameName:#"robber.png"];
asteroid.visible = NO;
[_batchNode addChild:asteroid];
[_robbers addObject:asteroid];
}
in update method:
double curTime = CACurrentMediaTime();
if (curTime > _nextRunemanSpawn) {
float randSecs = [self randomValueBetween:0.20 andValue:1.0];
_nextRunemanSpawn = randSecs + curTime;
float randY = 80.0;
float randY1 = 185.0;
float randY2 = 293.0;
float randDuration = [self randomValueBetween:5.2 andValue:5.2];
float randDuration1 = [self randomValueBetween:1.0 andValue:1.0];
CCSprite *asteroid = [_robbers objectAtIndex:_nextRobber];
_nextRobber++;
if (_nextRobber >= _robbers.count) {
_nextRobber = 0;
}
//[asteroid stopAllActions];
int winChoice = arc4random() % 3;
if (winChoice == 0) {
asteroid.position = ccp(winSize.width +asteroid.contentSize.width / 2 , randY);
asteroid.visible = YES;
}
else if(winChoice == 1){
asteroid.position = ccp(winSize.width +asteroid.contentSize.width / 2 , randY1);
asteroid.visible = YES;
}else {
asteroid.position = ccp(winSize.width +asteroid.contentSize.width / 2 , randY2);
asteroid.visible = YES;
}
[asteroid runAction:[CCSequence actions:[CCMoveBy actionWithDuration:randDuration position:ccp(-winSize.width-asteroid.contentSize.width, 0)],
[CCCallFuncN actionWithTarget:self selector:#selector(setInvisible:)],nil]];
All is going well but i want to set this enemies in to window and in random position
so how can i set x-argument of enemies so it can be fix in to window of the background?
For some reason I cannot comment so this is written as an answer. What exactly are you trying to do? I am a bit confused. It sounds like you want the X position to be set so that it is in one of 3 random positions, is that correct?

Resources