Updating a node's position while adding new ones? - ios

I have a enemy node that moves by updating its position in the update method, I add new nodes in a set interval, when two of the same nodes appear, the most recent one updates its position and the last node doesn't update its position, how do I write my code accordingly to update all the positioning of my enemy nodes when I add more?
The Enemy Method
-(void)Enemies {
SKTexture *pM1 = [SKTexture textureWithImageNamed:#"enemy-1"];
enemy = [SKSpriteNode spriteNodeWithTexture:pM1];
enemy.zPosition = 8;
enemy.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:enemy.size];
enemy.physicsBody.categoryBitMask = fEnemyCategory;
enemy.physicsBody.contactTestBitMask = fPlatformCategory | fMainPlatformCategory | fPlayerCategory | fPitOfCertainDoomCategory;
enemy.physicsBody.collisionBitMask = fPlatformCategory | fPlayerCategory | fEnemyCategory;
enemy.physicsBody.allowsRotation = NO;
enemy.physicsBody.friction = 0.3;
enemy.physicsBody.linearDamping = 0.7;
enemy.physicsBody.dynamic = YES;
enemy.physicsBody.affectedByGravity = YES;
enemy.physicsBody.usesPreciseCollisionDetection = YES;
enemy.position = CGPointMake(CGRectGetMidX(self.frame) + 30, CGRectGetMidY(self.frame));
enemy.position = CGPointMake(730, enemy.position.y - 129);
NSLog(#"Spawned");
[self addChild:enemy];
[enemy runAction:runAnimation];
}
Update Method
-(void)update:(CFTimeInterval)currentTime {
if (gameStart == YES & gameOver == NO) {
if (enemyExists == YES) {
if (switchMovement == NO) {
enemy.position = CGPointMake(enemy.position.x - 3.66, enemy.position.y);
}
else if (switchMovement == YES) {
enemy.position = CGPointMake(enemy.position.x + 2.83, enemy.position.y);
}
}

This is how you would implement what #LearnCocos2D has mentioned. I make no promises on casting the object as a spritenode properly ;)
Define Array:
NSMutableArray *array = [NSMutableArray alloc] init];
Add Enemy to Array:
[array addObject:myObject]; //Directly after [self addChild:enemy];
New Update:
-(void)update:(CFTimeInterval)currentTime {
if (gameStart == YES & gameOver == NO)
{
for (NSObject* o in array1)
{
SKSpriteNode enemyTemp = (SKSpriteNode*)o;
if (switchMovement == NO)
{
enemyTemp.position = CGPointMake(enemyTemp.position.x - 3.66, enemy.position.y);
}
else if (switchMovement == YES)
{
enemyTemp.position = CGPointMake(enemyTemp.position.x + 2.83, enemy.position.y);
}
}
}

Related

Efficient SpriteKit Spatial Hashing Collision Detection For 2D Game

I've been looking around for tutorials on spatial hashing for Objective C or spriteKit. I've found examples in other languages, but I can't Seem to figure it out. I've gotten this far but I'm stuck.
This Loads Enemy Sprites To An NSMutableDictionary, I divide Their x,y positions by 64 to get their cellID (I use cellID as a key for the enemy in NSMutableDictionary).
- (void)loadEnemies {
TMXObjectGroup *enemiesGroup = [self.map groupNamed:#"enemies"];
for (NSDictionary *enemyDict in enemiesGroup.objects) {
NSString *enemyType = enemyDict[#"type"];
NSString *firstFrameName =
[NSString stringWithFormat:#"%#1.png",enemyType];
CGPoint enemyPosition =
CGPointMake([enemyDict[#"x"] floatValue], [enemyDict[#"y"] floatValue]);
CGPoint cellNumber =
CGPointMake(enemyPosition.x / 64, enemyPosition.y / 64);
NSString *CellID =
[NSString stringWithFormat:#"%#",NSStringFromCGPoint(cellNumber)];
SMCEnemy *enemy = [[NSClassFromString(enemyType)alloc]
initEnemyWithImageNamed:firstFrameName Position:enemyPosition];
enemy.position = enemyPosition;
enemy.player = self.cat;
enemy.map = self.map;
enemy.delegate = self;
[self.map addChild:enemy];
enemy.zPosition = 900;
//enemies added to NSMutableDictionary
[self.enemeyDictionary setObject:enemy forKey:CellID];
}
}
Ive added enemies to an NSMutableDictionary using CellID as a key.
This Checks For Collisions with the player, I first check the distance between the player and each enemy; if the distance is less than 1000, then enemy becomes active.
- (void)checkForEnemyCollisions:(SMCEnemy *)enemy {
CGFloat distance = CGPointDistance(enemy.posiiton, self.player.position);
if (distance < 1000){
enemy.isActive = YES;
} else {
enemy.isActive = NO;
}
if (enemy.isActive && enemy.life > 0) {
//cat hurt
if ((CGRectIntersectsRect(self.cat.collisionBoundingBox, enemy.collisionBoundingBox) || CGRectIntersectsRect(self.cat.collisionBoundingBox, enemy.attackBox)) && self.cat.isActive && !enemy.hidden && enemy.canHurtPlayer){
enemy.attackBox = CGRectNull;
if (enemy.position.x < self.cat.position.x) {
[self.cat bounceWithDirection:1];
}else{
[self.cat bounceWithDirection:-1];
}
[self.cat tookHit:self.cat];
[self movementCoolDownForCharacter:self.cat coolDownTime:.5];
}
// enemy hurt
if (CGRectIntersectsRect(enemy.collisionBoundingBox, self.cat.attackBox)&& !enemy.isHurt && enemy.isStun && !enemy.hidden) {
CGRect intersection = CGRectIntersection(enemy.collisionBoundingBox,self.cat.attackBox);
_hit.position = CGPointMake(intersection.origin.x, intersection.origin.y);
_hit.hidden = NO;
SKAction *DoneAction = [SKAction runBlock:(dispatch_block_t)^() {
_hit.hidden = YES;
}];
SKAction *moveLaserActionWithDone = [SKAction sequence:#[enemy.hitAnim,DoneAction]];
[_hit runAction:moveLaserActionWithDone];
enemy.isHurt = YES;
[enemy tookHit:self.cat];
[self movementCoolDownForCharacter:enemy coolDownTime:.5];
}
}
The Update Method
- (void)update:(NSTimeInterval)currentTime {
NSTimeInterval delta = currentTime - self.previousUpdateTime;
self.previousUpdateTime = currentTime;
if (delta > 0.1) {
delta = 0.1;
}
for (SMCEnemy *enemy in self.enemies) {
[self checkForEnemyCollisions:enemy];
}
}
I want to use spatial hashing instead of going through every enemy item in the enemyArray to improve performance.
Im stuck Any Help would be really awesome! :D

How to make this building blocks code more flexible / efficient?

The code below stacks bricks on top of each other. When TouchesBegin is called a new brick is added.
I am new to coding. I want the code to be more flexible, so different kind of shapes of bricks can be added too.
Thanks for helping.
CODE
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
/* Called when a touch begins */
if (integer == 0) {
[_previousBrick removeAllActions];
[self spawnNextBrick];
}
if (integer == 1) {
[self spawnPrevBrick];
}
}
-(void)spawnPrevBrick
{
_previousBrick = [SKSpriteNode spriteNodeWithImageNamed:#"TestBridgePiece"];
_previousBrick.anchorPoint = CGPointMake(0.5, 0.0);
_previousBrick.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:_previousBrick.frame.size];
_previousBrick.position = CGPointMake(0.0, _nextBrick.position.y + _previousBrick.size.height);
_previousBrick.physicsBody.categoryBitMask = bridgeCategory;
_previousBrick.physicsBody.contactTestBitMask = leftEdgeCategory | rightEdgeCategory | newBrickNotificator;
_previousBrick.physicsBody.collisionBitMask = bridgeCategory;
_isPreviousBrick = YES;
[_previousBrickLayer addChild:_previousBrick];
}
-(void)spawnNextBrick
{
_nextBrick = [SKSpriteNode spriteNodeWithImageNamed:#"TestBridgePiece"];
_nextBrick.anchorPoint = CGPointMake(0.5, 0.0);
_nextBrick.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:_nextBrick.frame.size];
_nextBrick.position = CGPointMake(self.size.width, _previousBrick.position.y + _nextBrick.size.height);
_nextBrick.physicsBody.contactTestBitMask = leftEdgeCategory | rightEdgeCategory | newBrickNotificator;
_nextBrick.physicsBody.collisionBitMask = bridgeCategory;
_isPreviousBrick = NO;
[_previousBrickLayer addChild:_nextBrick];
}
-(void)update:(CFTimeInterval)currentTime {
if (_isPreviousBrick == NO) {
integer = 1;
}
if (_isPreviousBrick == YES) {
integer = 0;
}
NSLog(#"integer = %i", integer);
}

Why isn't the physics working the way I want it to?

Okay, so in my game I have a block of code that spawns a sprite ('enemy') every x amount of time. It spawns the sprite up high and the sprite falls down.
What should happen is this: The sprite falls down, it touches the ground or another enemy, then it changes to static so it can't be moved.
What happens is this: Sometimes it works, but sometimes (especially when the x amount of time is smaller and the sprites spawn more often) the sprite suddenly changes to static while it is still in the air.
Why is this happening?
Here's some code:
in GameScene.m, in createSceneContents, a method that is called by didMoveToView:
self.physicsWorld.contactDelegate = self;
SKSpriteNode *bottom = [SKSpriteNode spriteNodeWithColor:[SKColor whiteColor] size:CGSizeMake(self.frame.size.width, 10)];
bottom.position = CGPointMake(self.frame.size.width/2, 0);
bottom.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:bottom.size];
bottom.physicsBody.dynamic = NO;
bottom.physicsBody.restitution = 0;
bottom.physicsBody.categoryBitMask = self.bottomCategory;
bottom.physicsBody.contactTestBitMask = self.enemyCategory;
[self spawnObject];
[self addChild:self.world];
[self.world addChild:bottom];
in spawnObject, a method that gets called by createSceneContents:
if (self.isPaused == NO){
self.spawningSpeed = 1;
self.enemyData = [[Enemy alloc]init];
SKAction *wait = [SKAction waitForDuration:self.spawningSpeed];
SKAction *run = [SKAction runBlock:^{
SKSpriteNode *aNewEnemy = [self.enemyData createEnemyWithSize:self.customUnit andWidth:self.frame.size.width andHeight:self.frame.size.height + self.player.position.y andPlayerPosition:self.player.position.x];
aNewEnemy.physicsBody.allowsRotation = NO;
aNewEnemy.physicsBody.categoryBitMask = self.enemyCategory;
aNewEnemy.physicsBody.collisionBitMask = self.enemyCategory | self.bottomCategory;
aNewEnemy.physicsBody.contactTestBitMask = self.enemyCategory | self.bottomCategory;
[self.world addChild:aNewEnemy];
}];
SKAction *action = [SKAction repeatActionForever:[SKAction sequence:#[wait,run]]];
[self runAction:action withKey:#"action"];
in createEnemyWithSize:andWidth:andHeight:andPlayerPosition:, in Enemy.m:
self.enemy = [SKSpriteNode spriteNodeWithImageNamed:#"block.png"];
self.enemy.size = CGSizeMake(size - 5, size - 5);
self.enemy.name = #"fallingEnemy";
self.enemy.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(size - 1, size - 1)];
self.enemy.physicsBody.restitution = 0;
self.enemy.physicsBody.allowsRotation = NO;
int column1 = width/7;
int column2 = column1 * 2;
int column3 = column1 * 3;
int column4 = column1 * 4;
int column5 = column1 * 5;
int column6 = column1 * 6;
int halfAColumn = column1/2;
if (position > 0 && position < column1) {
self.enemy.position = CGPointMake(halfAColumn, height - size);
}else if (position > column1 && position < column2) {
self.enemy.position = CGPointMake((column2-halfAColumn), height - size);
}else if (position > column2 && position < column3) {
self.enemy.position = CGPointMake((column3-halfAColumn), height - size);
}else if (position > column3 && position < column4) {
self.enemy.position = CGPointMake((column4-halfAColumn), height - size);
}else if (position > column4 && position < column5) {
self.enemy.position = CGPointMake((column5-halfAColumn), height - size);
}else if (position > column5 && position < column6) {
self.enemy.position = CGPointMake((column6-halfAColumn), height - size);
}else if (position > column6 && position < width) {
self.enemy.position = CGPointMake((width-halfAColumn), height - size);
}
return self.enemy;
in didBeginContact, in GameScene.m:
SKPhysicsBody *enemyBodyA, *bottomBodyA, *enemyBodyB, *bottomBodyB;
if (contact.bodyA.categoryBitMask == self.enemyCategory) {
enemyBodyA = contact.bodyA;
}else if (contact.bodyA.categoryBitMask == self.bottomCategory) {
bottomBodyA = contact.bodyA;
}
if (contact.bodyB.categoryBitMask == self.enemyCategory) {
enemyBodyB = contact.bodyB;
}else if (contact.bodyB.categoryBitMask == self.bottomCategory) {
bottomBodyB = contact.bodyB;
}
if (enemyBodyA == contact.bodyA && enemyBodyB == contact.bodyB) {
[self.enemyData changeBlock];
NSLog(#"Change1");
}
if (contact.bodyA == enemyBodyA && contact.bodyB == bottomBodyB) {
NSLog(#"Change2");
[self.enemyData changeBlock];
}
if (contact.bodyB == enemyBodyB && contact.bodyA == bottomBodyA) {
NSLog(#"Change3");
[self.enemyData changeBlock];
}
in changeBlock, in Enemy.m:
-(void)changeBlock {
self.enemy.physicsBody.dynamic = NO;
self.enemy.name = #"staticEnemy";
}
Bitmask values in createSceneContents:
self.playerCategory = 1;
self.enemyCategory = 2;
self.edgeCategory = 4;
self.bottomCategory = 8;
You might be losing a reference to the enemy which has not received a contact before another is spawned. Hence a few changes are needed. You do not need the self.enemy property in the enemy class at all.
First of all, modify the changeBlock method so it can be performed on any block which is passed as a parameter.
-(void)changeBlock:(SKSpriteNode*)block {
block.physicsBody.dynamic = NO;
block.name = #"staticEnemy";
}
And then clean up the contact delegate method.
-(void)didBeginContact:(SKPhysicsContact *)contact
{
SKPhysicsBody *firstBody, *secondBody;
//This is a useful technique for organising bodies in the contact delegate
if (contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask) {
firstBody = contact.bodyA;
secondBody = contact.bodyB;
} else {
firstBody = contact.bodyB;
secondBody = contact.bodyA;
}
//Since enemyCategory < bottomCategory, the firstBody is an enemy node, and second either an enemy or bottom node
if (firstBody.categoryBitMask == self.enemyCategory && secondBody.categoryBitMask == self.bottomCategory) {
[self.enemyData changeBlock: (SKSpriteNode*)firstBody.node];
} else if (firstBody.categoryBitMask == self.enemyCategory && secondBody.categoryBitMask == self.enemyCategory) {
[self.enemyData changeBlock: (SKSpriteNode*)firstBody.node];
[self.enemyData changeBlock: (SKSpriteNode*)secondBody.node];
}
}
I am not sure what your game is about so change is as you wish. But I hope you get the idea.

Spritekit drastic frame rate drop

I have tried my best to boil this question down as simple as possible. I have a coin object in my game:
#implementation
-(CollectableCoin*)initWithLocation:(CGPoint) Location andValue: (int) val
{
self = [super initWithImageNamed:#"coin"];
[self setScale:.35];
_value = val;
_collected = false;
self.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:self.size];
self.physicsBody.categoryBitMask = APAColliderTypeCoin;
self.physicsBody.collisionBitMask = APAColliderTypeBall;
self.physicsBody.mass = 0.00009;
self.physicsBody.restitution = .35;
self.position = Location;
self.name = #"collectableCoin";
return self;
}
#end
I also have a shelf object:
#implementation Shelf
-(Shelf*)initWithLocation:(CGPoint) location andWidth:(NSInteger) width
{
self = [super initWithImageNamed:#"shelf"];
if(self)
{
self.size = CGSizeMake(width, HEIGHT);
self.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:self.size];
self.physicsBody.dynamic = false;
self.physicsBody.restitution = 0;
self.position = location;
self.name = #"shelf";
SKSpriteNode* topOfShelf;
if(width > 5)
topOfShelf = [[SKSpriteNode alloc] initWithColor:[UIColor yellowColor] size:CGSizeMake(width-2, 1)];
else
topOfShelf = [[SKSpriteNode alloc] initWithColor:[UIColor yellowColor] size:CGSizeMake(width, 1)];
topOfShelf.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:topOfShelf.size];
topOfShelf.physicsBody.restitution = 1;
topOfShelf.physicsBody.dynamic = false;
topOfShelf.position = CGPointMake(0, location.y + self.size.height/2);
NSLog([NSString stringWithFormat:#"%f", location.y + self.size.height/2]);
NSLog([NSString stringWithFormat:#"%f", location.y]);
topOfShelf.name = #"shelf";
[self addChild:topOfShelf];
}
return self;
}
#end
I create a scene like so:
-(id)initWithSizeTest:(CGSize)size
{
self.physicsWorld.gravity = CGVectorMake(0, 0);
_gameState = READYTOSTART;
self.physicsWorld.contactDelegate = self;
if (self = [super initWithSize:size])
{
self.physicsWorld.gravity = CGVectorMake(0, 0);
for(int i = 0; i < 25; i++)
{
CollectableCoin* orb = [[CollectableCoin alloc] initWithLocation:CGPointMake(i*10, self.size.height*.75) andValue:1];
[self addChild:orb];
}
Shelf* shelf = [[Shelf alloc] initWithLocation:CGPointMake(self.size.width/2, self.size.height/2) andWidth:self.size.width];
[self addChild:shelf];
}
return self;
}
Here is the touchesBegan method:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
/* Called when a touch begins */
if(_gameState == READYTOSTART)
{
self.physicsWorld.gravity = CGVectorMake(0, -2.0);
_gameState = PLAYING;
[[self childNodeWithName:#"taptostart"] removeFromParent];
}
When the scene starts, I have a row of coins hovering above a shelf, gravity is disabled, and I have a solid 60fps. When I tap the screen, the touchesbegan function enables gravity and the coins fall on the shelf, and the frame rate drops to 5fps. The didBeginContact function is not being called because the shelf object is not dynamic nor does it have contact or collision bitmasks, so I am fairly sure that it is not being overloaded by extraneous calls to didBeginContact. This happens on an iPad mini and an iPhone 4s, but not in any of the simulators. This is a very simple example of the actual problem that I am having. Anyone have any insights?

SpriteKit - FPS dropping after changing texture

I am trying to change a SKSpriteNode's texture while it is animated , but I faced with FPS dropping ! it goes down from 60 to 30 ! on both device and simulator ! the node is a parallel scrolling background , and here is the codes :
- (void)createBricksEdge {
brickEdges = [[SKSpriteNode alloc]initWithImageNamed:#"edge.png"];
brickEdges.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame));;
brickEdges.name = #"edge";
[self addChild:brickEdges];
bEdge1 = [[SKSpriteNode alloc]initWithImageNamed:#"edge.png"];
bEdge1.position = CGPointMake(brickEdges.position.x,brickEdges.position.y+(brickEdges.size.height));
bEdge1.name = #"edge";
[self addChild:bEdge1];
}
moving background :
- (void)moveBg
{
[self enumerateChildNodesWithName:#"edge" usingBlock: ^(SKNode *node, BOOL *stop)
{
SKSpriteNode * bg = (SKSpriteNode *) node;
if (isSpeedBonus == YES) {
SPEED = 200;
} else {
switch (speedCriteria) {
case 0:
SPEED = 7.0;
break;
case 1:
SPEED = 11;
NSLog(#"FPS drops");
[bg runAction:[SKAction setTexture:[SKTexture textureWithImage:[UIImage imageNamed:#"edgeSpeed.png"]]]];
//I also tried : [bg setTexture:textre];
break;
.
.
.
.
.
}
}
bg.position = CGPointMake(bg.position.x , bg.position.y - SPEED);
if (bg.position.y <= -bg.size.width)
{ bg.position = CGPointMake(bg.position.x ,bg.position.y + bg.size.height*2); }} ];
}
Updating frames :
- (void)speedOMeter
{
int i = (int)[scoreLabel.text integerValue];
if (i <= 1000) {
speedCriteria = 0;
} else if (i <= 1500) {
speedCriteria = 1;
} else if (i <=2000) {
speedCriteria = 2;
} else if (i <= 2500) {
.
.
.
.
}
- (void)update:(CFTimeInterval)currentTime {
if (_lastUpdateTime)
{
_dt = currentTime - _lastUpdateTime;
}
else
{
_dt = 0;
}
_lastUpdateTime = currentTime;
[self moveBg]; [self speedOMeter];
}
You're bypassing the Sprite Kit caching mechanisms by creating the image first as a UIImage. This will load the image from disk, possibly every time:
[bg runAction:[SKAction setTexture:[SKTexture textureWithImage:[UIImage imageNamed:#"edgeSpeed.png"]]]];
Besides that you're overdoing it with the action. Try again with the simplified version that'll give you the same result by simply assigning the texture:
bg.texture = [SKTexture textureWithImageNamed:#"edgeSpeed.png"];

Resources