I have two SKSpriteNode first Hero
+(id)hero
{
NSMutableArray *walkFrames = [NSMutableArray array];
SKTextureAtlas *heroAnimatedAtlas = [SKTextureAtlas atlasNamed:#"HeroImages"];
int numImages = (int)heroAnimatedAtlas.textureNames.count;
for (int i=1; i <= numImages; i++) {
NSString *textureName = [NSString stringWithFormat:#"hero%d", i];
SKTexture *temp = [heroAnimatedAtlas textureNamed:textureName];
[walkFrames addObject:temp];
}
Hero *hero = [Hero spriteNodeWithTexture:walkFrames[0]];
hero.heroWalkingFrames = walkFrames;
hero.name =#"Hero";
hero.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:hero.size];
hero.physicsBody.categoryBitMask = heroCategory;
hero.physicsBody.categoryBitMask = obstacleCategory | groundCategory | homeCategory | ~goodiesCategory;
return hero;
}
and second is Coin
SKSpriteNode *coin = [SKSpriteNode spriteNodeWithImageNamed:#"coin"];
coin.size = CGSizeMake(10,10);
coin.position = CGPointMake(100,100);
coin.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:coin.size];
coin.physicsBody.contactTestBitMask = coinCategory;
coin.physicsBody.dynamic=NO;
coin.name = #"coin";
[self.world addChild:coin];
And I am able to get collision detection by
if([contact.bodyA.node.name isEqual: #"coin"] || [contact.bodyB.node.name isEqual: #"coin"])
{
//[self LevelComplete];
SKNode* coinNode ;
if ([contact.bodyA.node.name isEqual: #"coin"]) {
coinNode=contact.bodyA.node;
}
else{
coinNode=contact.bodyB.node;
}
[coinNode removeFromParent];
NSLog(#"Coin touched");
}
Now my problem is every time hero jump and touch the coin it will go down to ground instead to continue jump and reach the height that it should, I know I am missing something here but don't know what it is, So anyone can please show me the right direction to correct this effect .
Create an extra "nilCategory" and set the collisionBitMask of your coin..
coin.physicsBody.collisionBitMask = nilCategory;
Related
I am new to the iOS native game development. I am trying to create animation using some frames for Ideal state for the character. I am following tutorial from the Ray's Website. Look like I am doing everything fine but I can't see animation in working. Only first frame (default) is visible all the time. I debug the code & it's indeed visiting blinking player method where all 3 frames are also present, but nothing happening on screen.
It will be great if someone can guide/help me identify the issue.
#implementation GameScene
{
NSArray *_playerBlinkFrames;
}
// Player
SKNode *_player;
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
[self createPlayer];
}
return self;
}
- (void) createPlayer
{
SKTextureAtlas *playerAnimatedAtlas = [SKTextureAtlas atlasNamed:#"Assets"];
_player = [SKNode node];
SKSpriteNode *sprite = [SKSpriteNode spriteNodeWithImageNamed:#"Idle"];
[_player addChild:sprite];
[sprite setName:#"Ball"];
NSMutableArray *blinkFrames = [NSMutableArray array];
SKTexture *temp = [playerAnimatedAtlas textureNamed:#"Idle.png"];
SKTexture *temp2 = [playerAnimatedAtlas textureNamed:#"Blink.png"];
SKTexture *temp3 = [playerAnimatedAtlas textureNamed:#"LookRight.png"];
[blinkFrames addObject:temp];
[blinkFrames addObject:temp2];
[blinkFrames addObject:temp3];
_playerBlinkFrames = blinkFrames;
_player.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame));
[self addChild:_player];
[self blinkingPlayer];
}
-(void)blinkingPlayer
{
[_player runAction:[SKAction repeatActionForever:
[SKAction animateWithTextures:_playerBlinkFrames
timePerFrame:0.3f
resize:NO
restore:YES]] withKey:#"blinkingInPlacePlayer"];
return;
}
Thanks.
Try this:
// Player
SKSpriteNode *_player;
//..//
SKTextureAtlas *playerAnimatedAtlas = [SKTextureAtlas atlasNamed:#"Assets"];
NSMutableArray *blinkFrames = [NSMutableArray array];
SKTexture *temp = [playerAnimatedAtlas textureNamed:#"Idle.png"];
SKTexture *temp2 = [playerAnimatedAtlas textureNamed:#"Blink.png"];
SKTexture *temp3 = [playerAnimatedAtlas textureNamed:#"LookRight.png"];
[blinkFrames addObject:temp];
[blinkFrames addObject:temp2];
[blinkFrames addObject:temp3];
_playerBlinkFrames = blinkFrames;
SKTexture *tempSprite = _playerBlinkFrames[0];
_player = [SKSpriteNode spriteNodeWithTexture:tempSprite];
I've not tested it, but i think you have create a SKNode player covered by a spritenode.
let me know
I have an array of names of sksprite nodes. i need to make a new array and put the name from the last array but the sprite node that is closest to (tilt - name of another node) will be first in the array And so on.
if I didn't explain my self clear enough tell me. please if anyone know how to do it. write here.
thanks
As I said previously, your question lacked some specifics in exactly how this is to be implemented. I have made my code somewhat generic to be adaptable to pretty much any scenario you might have in mind.
I used the touch location coordinates as a starting point to calculating distances to the 4 nodes on screen. Click anywhere on the screen and a list will print with the 4 node names in order of distance.
There's probably a better way of sorting this but I decided on the quick and dirty way.
#import "GameScene.h"
#implementation GameScene {
SKSpriteNode *node0;
SKSpriteNode *node1;
SKSpriteNode *node2;
SKSpriteNode *node3;
NSMutableArray *nodeArray;
}
-(void)didMoveToView:(SKView *)view {
self.backgroundColor = [SKColor blackColor];
nodeArray = [[NSMutableArray alloc] init];
node0 = [SKSpriteNode spriteNodeWithColor:[SKColor redColor] size:CGSizeMake(10, 10)];
node0.name = #"red";
node0.position = CGPointMake(300, 300);
[self addChild:node0];
[nodeArray addObject:node0];
node1 = [SKSpriteNode spriteNodeWithColor:[SKColor blueColor] size:CGSizeMake(10, 10)];
node1.name = #"blue";
node1.position = CGPointMake(450, 500);
[self addChild:node1];
[nodeArray addObject:node1];
node2 = [SKSpriteNode spriteNodeWithColor:[SKColor yellowColor] size:CGSizeMake(10, 10)];
node2.name = #"yellow";
node2.position = CGPointMake(100, 400);
[self addChild:node2];
[nodeArray addObject:node2];
node3 = [SKSpriteNode spriteNodeWithColor:[SKColor greenColor] size:CGSizeMake(10, 10)];
node3.name = #"green";
node3.position = CGPointMake(200, 200);
[self addChild:node3];
[nodeArray addObject:node3];
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
for (UITouch *touch in touches) {
CGPoint touchLocation = [touch locationInNode:self];
NSMutableArray *myArray = [[NSMutableArray alloc] init];
NSMutableDictionary *myDict = [[NSMutableDictionary alloc] init];
for(SKSpriteNode *object in nodeArray) {
float xDistance = fabsf(touchLocation.x - object.position.x);
float yDistance = fabsf(touchLocation.y - object.position.y);
NSString *totalDistance = [NSString stringWithFormat:#"%f",(xDistance+yDistance)];
[myDict setValue:totalDistance forKey:object.name];
}
float lowestValue = 999999.0;
NSMutableString *lowestName = [[NSMutableString alloc] init];
for (int i=0; i<[nodeArray count]; i++) {
for (NSString *key in myDict) {
if([[myDict objectForKey:key] floatValue] < lowestValue) {
lowestValue = [[myDict objectForKey:key] floatValue];
[lowestName setString:key];
}
}
[myArray addObject:[NSString stringWithString:lowestName]];
[myDict removeObjectForKey:lowestName];
lowestValue = 999999.0;
}
NSLog(#"values: %#",myArray);
}
}
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)
}
I am trying to add a few seconds to countdown timer each time when enemy and player node contacts.
Also when the player reach to some certain point I want player to pass to another level. I am sending part of the code . Could anybody help me to solve this problem. By the way because I am new to programming my code is a little hard to read . Sorry for that.
Thanks
-(void)update:(CFTimeInterval)currentTime{
if (startGamePlay){
startTime = currentTime;
startGamePlay = NO;
}
countDownInt = 10.0 - (int)(currentTime-startTime);
if(countDownInt > 0 ){ //if counting down to 0 show counter
countDown.text = [NSString stringWithFormat:#"%i", countDownInt];
}
else if (countDownInt == 0){
countDown.text =#"0";
Level2 *level2 = [Level2 sceneWithSize:self.frame.size];
SKTransition *transition = [SKTransition fadeWithDuration:0.5];
[self.view presentScene:level2 transition:transition];
}
}
- (void) didBeginContact:(SKPhysicsContact *)contact {
SKPhysicsBody *firstBody, *secondBody;
if (contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask) {
firstBody = contact.bodyA;
secondBody = contact.bodyB;
} else {
firstBody = contact.bodyB;
secondBody = contact.bodyA;
}
if ( firstBody.categoryBitMask == CollisionCategoryBrick && secondBody.categoryBitMask == CollisionCategoryShark ) {
NSLog(#"BRICK");
[contact.bodyB.node removeFromParent];
} else if ( firstBody.categoryBitMask == CollisionCategoryWaterBall && secondBody.categoryBitMask == CollisionCategoryShark ) {
NSLog(#"BALL");
countDownInt = [countDown.text intValue];
[contact.bodyA.node removeFromParent];
[self addPoints:PointsPerHit];
if (![ self childNodeWithName:#"WATERBALLTL"]) {
[self addWaterBallTopLeft];
}
if (![ self childNodeWithName:#"WATERBALLTR"]) {
[self addWaterBallTopRight];
}
if (![ self childNodeWithName:#"WATERBALLBL"]) {
[self addWaterBallBottomLeft];
}
if (![ self childNodeWithName:#"WATERBALLBR"]) {
[self addWaterBallBottomRight];
}
}
NSLog(#"%lu", (unsigned long)[self.children count]);
}
#import "HudNode.h"
#implementation HudNode
{
SKLabelNode *countDown;
BOOL startGamePlay;
NSTimeInterval startTime;
}
+ (instancetype) hudAtPosition:(CGPoint)position inFrame:(CGRect)frame {
HudNode *hud = [self node];
hud.position = position;
hud.zPosition = 10;
hud.name = #"HUD";
SKLabelNode *scoreLabel = [SKLabelNode labelNodeWithFontNamed:#"Futura-CondensedExtraBold"];
scoreLabel.name = #"Score";
scoreLabel.text = #"0";
scoreLabel.fontSize = 24;
scoreLabel.horizontalAlignmentMode = SKLabelHorizontalAlignmentModeRight;
scoreLabel.position = CGPointMake(frame.size.width-20, -10);
scoreLabel.zPosition = 12;
[hud addChild:scoreLabel];
SKLabelNode *countDown = [SKLabelNode labelNodeWithFontNamed:#"Futura-Medium"];
countDown.fontSize = 50;
countDown.position = CGPointMake(30, -10);
countDown.verticalAlignmentMode = SKLabelVerticalAlignmentModeCenter; //good for positioning with other sprites
countDown.fontColor = [SKColor whiteColor ];
countDown.name = #"countDown";
countDown.zPosition = 100;
[hud addChild:countDown];
return hud;
}
- (void) addPoints:(NSInteger)points {
self.score += points;
SKLabelNode *scoreLabel = (SKLabelNode *) [self childNodeWithName:#"Score"];
scoreLabel.text = [NSString stringWithFormat:#"Score:%li", (long)self.score];
}
Create a boolean flag that the update method checks to see if it should add seconds.
Add the following variable and set self.addSeconds = NO; initially:
#property (nonatomic) BOOL addSeconds;
Set self.addSeconds =YES; in the method where your player hits your enemy:
Add the following to the very beginning of the Update:
if(self.addSeconds == YES)
{
CountDownInt+= 5 //Change this to add as many seconds as you want
self.addSeconds=NO;
}
This should do the trick.
I am trying to get the value from my custom class SKNode object. The problem is when I touch on the object no matter what is gives me the same value regardless of the object I touch on.
(blue button) and I cannot get the buttonID,buttonType that I set earlier.
Everything works well, except when I need to get the buttonType, buttonID of the object I touch or drag over.
I am not sure where I am going wrong, any help or push in the right direction would be great. Thanks.
here is my custom class .h file.
#import <SpriteKit/SpriteKit.h>
#interface ButtonNode : SKNode {
SKNode *buttonCustom;
}
#property int buttonType, buttonColumn, buttonID, xPos, yPos;
#property NSString *buttonName;
-(id)initWithButtonType:(int)buttonType;
#end
Here is my custom class .m file
#import "ButtonNode.h"
#define kBoxSize CGSizeMake(40, 40)
#implementation ButtonNode
#synthesize buttonColumn,buttonType,buttonID,xPos,yPos,buttonName;
static const uint32_t blueCategory = 1 << 0;
static const uint32_t redCategory = 1 << 1;
static const uint32_t greenCategory = 1 << 2;
static const uint32_t yellowCategory = 1 << 3;
-(id)initWithButtonType:(int)buttonType {
self = [super init];
if (buttonType == 1) {
NSLog(#"BLUE BUTTON CREATE");
[self addButtonBlue];
}
if (buttonType == 2) {
NSLog(#"RED BUTTON CREATE");
[self addButtonRed];
}
if (buttonType == 3) {
NSLog(#"Green BUTTON CREATE");
[self addButtonGreen];
}
if (buttonType == 4) {
NSLog(#"Yellow BUTTON CREATE");
[self addButtonYellow];
}
return self;
}
- (void) addButtonBlue {
SKSpriteNode *rect;
//button type 1
rect = [SKSpriteNode spriteNodeWithColor:[UIColor blueColor] size:kBoxSize];
int tmpInt = [[NSDate date] timeIntervalSince1970];
NSString *tmpName = [NSString stringWithFormat:#"%i", tmpInt];
rect.name = tmpName; //unique name.
rect.name = #"1";
rect.physicsBody.categoryBitMask = blueCategory;
rect.physicsBody.contactTestBitMask = blueCategory;
rect.physicsBody.collisionBitMask = blueCategory | redCategory | yellowCategory | greenCategory;
rect.position = CGPointMake(xPos , yPos );
[self addChild:rect];
}
- (void) addButtonRed {
SKSpriteNode *rect;
//button type 2
rect = [SKSpriteNode spriteNodeWithColor:[UIColor redColor] size:kBoxSize];
int tmpInt = [[NSDate date] timeIntervalSince1970];
NSString *tmpName = [NSString stringWithFormat:#"%i", tmpInt];
rect.name = tmpName; //unique name.
rect.name = #"2";
rect.physicsBody.categoryBitMask = redCategory;
rect.physicsBody.contactTestBitMask = redCategory;
rect.physicsBody.collisionBitMask = blueCategory | redCategory | yellowCategory | greenCategory;
rect.position = CGPointMake(xPos , yPos );
[self addChild:rect];
}
- (void) addButtonGreen {
SKSpriteNode *rect;
//button type 3
rect = [SKSpriteNode spriteNodeWithColor:[UIColor greenColor] size:kBoxSize];
int tmpInt = [[NSDate date] timeIntervalSince1970];
NSString *tmpName = [NSString stringWithFormat:#"%i", tmpInt];
rect.name = tmpName; //unique name.
rect.name = #"3";
rect.physicsBody.categoryBitMask = greenCategory;
rect.physicsBody.contactTestBitMask = greenCategory;
rect.physicsBody.collisionBitMask = blueCategory | redCategory | yellowCategory | greenCategory;
rect.position = CGPointMake(xPos , yPos );
[self addChild:rect];
}
- (void) addButtonYellow {
SKSpriteNode *rect;
//button type 4
rect = [SKSpriteNode spriteNodeWithColor:[UIColor yellowColor] size:kBoxSize];
int tmpInt = [[NSDate date] timeIntervalSince1970];
NSString *tmpName = [NSString stringWithFormat:#"%i", tmpInt];
rect.name = tmpName; //unique name.
rect.name = #"4";
rect.physicsBody.mass = 1;
rect.physicsBody.categoryBitMask = yellowCategory;
rect.physicsBody.contactTestBitMask = yellowCategory;
rect.physicsBody.collisionBitMask = blueCategory | redCategory | yellowCategory | greenCategory;
rect.position = CGPointMake(xPos , yPos );
[self addChild:rect];
}
#end
Here is where I create the buttons.
(at top of file with rest of global ivar )
ButtonNode * newButton;
for (int i = 1; i <= 6; i++) {
//create random Int
int tmpInt = arc4random() %3;
NSLog(#"tmp %i" ,tmpInt);
column1.position = CGPointMake(100, self.frame.size.height - 40);
if (tmpInt == 0) {
//button type 1
newButton = [[ButtonNode alloc] initWithButtonType:1];
newButton.xPos = column1.position.x;
newButton.yPos = column1.position.y *i;
newButton.buttonID = 344224351; //unique name
newButton.buttonColumn = 2;
newButton.buttonType = 1;
[column1 addChild:newButton];
blueTotal++;
totalButtons++;
column1Total++;
}
if (tmpInt == 1) {
//button type 2
newButton = [[ButtonNode alloc] initWithButtonType:2];
newButton.xPos = column1.position.x;
newButton.yPos = column1.position.y *i;
newButton.buttonID = 344224351; //unique name
newButton.buttonColumn = 2;
newButton.buttonType = 1;
[column1 addChild:newButton];
redTotal++;
totalButtons++;
column1Total++;
}
}
Here is the Part that is not working correctly.
- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
for (UITouch *touch in touches) {
UITouch* touch = [touches anyObject];
CGPoint loc = [touch locationInNode:self];
NSArray *nodes = [self nodesAtPoint:loc];
for (SKNode *nod in nodes) {
NSString *tmp = nod.name;
if (tmp.length !=0) {
NSString * tmpType = nod.name;
if ([tmpType isEqualToString:#"1"]) {
NSLog(#"Node Type: %#", nod.name);
previousButton = #"1";
NSLog (#"%d",newButton.buttonType);
}
if ([tmpType isEqualToString:#"2"]) {
NSLog(#"Node Type: %#", nod.name);
previousButton = #"2";
NSLog (#"%d",newButton.buttonType);
}
if ([tmpType isEqualToString:#"3"]) {
NSLog(#"Node Type: %#", nod.name);
previousButton = #"3";
NSLog (#"%d",newButton.buttonType);
}
if ([tmpType isEqualToString:#"4"]) {
NSLog(#"Node Type: %#", nod.name);
previousButton = #"4";
NSLog (#"%d",newButton.buttonType);
}
}
}
}
}
A SKNode does not have those properties.
Try this just inside your for loop :
ButtonNode *myButton = (ButtonNode *)nod;
That will cast nod correctly as a ButtonNode, and you can use myButton like this :
NSLog(#"%d",myButton.buttonType);
Then you should be able to access the properties you have defined in the ButtonNode class.
You might only want to do that cast if you are sure it's a ButtonNode, but was just trying to help you understand why those properties would NEVER be accessible given your current code.
Also, your usage of newButton in that loop in touchesBegan is not what I think you 'think' it is. It's going to be the last button created, not the current node being stored in nod in the loop.