I've reviewed similar questions and do believe I'm asking about different thing.
I have a player node which has it's own body and I want to check when it's been moved to some part of the level (exit node). Examples of this may be putting some dynamic item in a box, or parking a car at the specific place.
In my case player is SKSpriteNode with physicsBody created from similar texture (bodyWithTexture) and exit is SKNode with no visual and physicsBody created from polygon path (4 points, non rectangle).
I have a code which does something I wan't, but I do believe there maybe some cases it's doing something I don't want and there are better ways to do it.
int contactsCount = 0;
- (void)didBeginContact:(SKPhysicsContact *)contact{
if ((contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask) & exitCategory) {
contactsCount++;
if (contactsCount == 4) {
[player runAction:[SKAction colorizeWithColor:[UIColor greenColor] colorBlendFactor:1.0 duration:1.0] completion:^{
self.physicsWorld.speed = 0;
}];
}
return;
}
[player runAction:[SKAction colorizeWithColor:[UIColor redColor] colorBlendFactor:1.0 duration:1.0] completion:^{
self.physicsWorld.speed = 0;
}];
}
- (void)didEndContact:(SKPhysicsContact *)contact{
if ((contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask) & exitCategory) {
contactsCount--;
return;
}
}
Related
I am designing a game that changes scenes as the user is playing. The scenes change based on a NSTimer. I can ge everything to change accordingly in regards to graphics. Can even get background music to change.
Where I seem to be hitting a roadblock is changing the score sound and the sound for when the player collides with an object and dies.
As it it's right now I can cancel the sound for scene one but cannot get another one to play.
* on phone don't know how to hand code a code block on here*
If([_timer isValid]) {
//plays correct audio and does not continue when scene changes
}
Else if ([_timer == nil ]){
//suppose to play other sound but does nothing.
}
I would really prefer to call the different sounds based on if a method is called to change something along the lines of
Else if ([ _method1 == TRUE]) {
//play sound
}
Any advice in the right direction appreciated.
EDIT
- (void)didBeginContact:(SKPhysicsContact *)contact {
if( _moving.speed > 0 ) {
if( ( contact.bodyA.categoryBitMask & scoreCategory ) == scoreCategory || ( contact.bodyB.categoryBitMask & scoreCategory ) == scoreCategory ) {
// has contact with score entity
_score++;
_scoreLabelNode.text = [NSString stringWithFormat:#"%d", _score];
//Pick Up Egg
//Sound for Score
SKAction* teslaPoint = [SKAction playSoundFileNamed:#"Point1" waitForCompletion: YES];
[self runAction:teslaPoint withKey:#"teslaPoint"];
I have been trying to structure an IF statement based on the background image because that changes with the sounds.
EDIT
_scoreSound = [SKAudioNode node];
SKAudioNode* teslaPoint = [SKAudioNode nodeWithFileNamed:#"Point1"];
[_scoreSound addChild:teslaPoint];
EDIT
THIS IS ADDED IN initwithSize:
_scoreSound = [SKNode node];
[self addChild:_scoreSound];
Then its called in the contacts
EDIT
//Sound for Score
if([_timer isValid]){
SKAction* teslaPoint = [SKAction playSoundFileNamed:#"Point1" waitForCompletion:YES];
[self runAction:teslaPoint withKey:#"teslaPoint"];
}
if ([_bgImage isEqual: #"bgimage2.png"]){
SKAction* teslaPoint = [SKAction playSoundFileNamed:#"grassypoint" waitForCompletion:YES];
[self runAction:teslaPoint withKey:#"teslaPoint"];
}
Currently in my application I've been adopting a technique to remove/re-draw certain sprites.
in example, the app is a poker app. So when a call/raise/check is made, there is a chip that is placed in-front of the player with an SKLabelNode containing the bet,check etc.. However, removing the previous to then re-add the new is inconsistent and causes a lot of EXC_BAD_ACCESS errors. Now, I guess I could program it to nest search for that node and alter the value instead of redrawing. However, it's used in multiple occasions and will at some point rely on removing the child from it's parent.
What I'm asking is what is the best technique to achieve this without the possibility of inconsistent crashes..?
-(void)removePreviousChips:(NSString *)player {
NSString *string = [NSString stringWithFormat:#"chipLabel:%#", player];
SKNode *node = [tableSprite childNodeWithName:string];
[node removeFromParent];
}
-(void)updateChipStacksAndPlayerPositionsWith:(NSDictionary *)dict {
// [tableSprite removeAllChildren];
for (int i = 0; i < 8; i++) {
//loop through seats taken if value then draw.
if (![[dict valueForKey:_seatNames[i]] isEqualToString:#""]) {
[self drawAvatarWithPlayerName:[dict valueForKey:_seatNames[i]] withSeatPosition:[[seatPositions valueForKey:_seatNames[i]] CGPointValue] withChipStack:[[dict valueForKey:#"startingChips"] intValue]];
}
}
if ([self displayStartGameButton]) {
[startGameBG runAction:[SKAction fadeAlphaTo:1 duration:0.5]];
} else {
[startGameBG runAction:[SKAction fadeAlphaTo:0 duration:0.5]];
}
}
Them two examples are consistent ways to crash my app.
EDIT
for example, a better approach would be to detect whether the node is present before the requirement to remove it from it's parent and redraw it. However, something to detect it's presence is not working out for me
SKNode *node = [tableSprite childNodeWithName:[dict valueForKey:_seatNames[i]]];
if (node) {
NSLog(#"node for %# exists", [dict valueForKey:_seatNames[i]]);
} else {
NSLog(#"node for %# doesn't exist", [dict valueForKey:_seatNames[i]]);
}
my issue is, I've got a "2.5D" game where characters that come into contact with one another need to dynamically change their zPosition. I'm using physics bodies to achieve this.
It works OK with the characters, however, the shield they are holding (a child object) is not following suit even though I'm logging a correct change of zPosition attribute.
if (((firstNode.categoryBitMask & dudeCategory) != 0) && ((secondNode.categoryBitMask & shieldGuyCategory) !=0))
{
if ((firstNode.node.position.y < secondNode.node.parent.position.y))
{
[secondNode.node.parent enumerateChildNodesWithName:#"shieldNode" usingBlock:^(SKNode *node, BOOL *stop){
NSLog(#"Ooh I Say! Shield zPos = %f", node.zPosition);
SKAction *zPos = [SKAction runBlock:^{
node.zPosition = node.zPosition -200;
NSLog(#"And now, %# zPos = %f", node.name, node.zPosition);
NSLog(#"But static NPC zPos = %f", firstNode.node.zPosition);
NSLog(#"And finally, Shield-holder Guy zPos = %f", secondNode.node.parent.zPosition);
}];
[node runAction:zPos];
}];
secondNode.node.parent.zPosition = secondNode.node.parent.zPosition -200;
}
}
Halp!
Instead of setting the zPosition using SKAction runBlock:, set it directly.
[secondNode.node.parent enumerateChildNodesWithName:#"shieldNode" usingBlock:^(SKNode *node, BOOL *stop){
node.zPosition -= 200;
}];
So, it turns out I was setting the zPosition for the shield in its method. Removing that restores normal behaviour when the parent changes zPosition, having it declared explicitly seems to override the nested behaviour.
I'm trying to experiment with Sprite Kit's joint system. So far, I've had difficulty making even a single strand of "string"/"rope". The end goal is to produce something like a basketball net where there will be 1 left rope (vertical), 1 right rope (vertical) & at least 2 ropes in-between connecting the left & right rope (horizontally) - 4 ropes in total. Afterwards, figuring out how to make the 2 horizontal ropes not collide with an object (cannonball?) when it passes through them. Does anyone know how to do something like this? I've been trying this for 2 weeks and my eyes & fingers are sore.
You can connect several physics bodies together with limit joints to create a rope like structure which is affected by collisions with other bodies. My code is quite messy but here is the Scene class for a sprite kit project. It has a basketball net and when you tap the screen it spawns a ball.
with screenshot here https://dl.dropboxusercontent.com/s/flixgbhfk2yysr8/screenshot.jpg?dl=0
EDIT: sorry about that, code is now in obj-C
EDIT: paste these methods into your SKScene subclass:
-(void)addBasketAtPos:(CGPoint)pos WithSize:(CGSize)size HoopThickness:(CGFloat)hoopThickness RopeThickness:(CGFloat)ropeThickness GapSize:(CGFloat)gapSize {
int nSect=ceil(size.height/(ropeThickness*4));
SKSpriteNode *hoop=[SKSpriteNode spriteNodeWithColor:[SKColor redColor] size:CGSizeMake(size.width+hoopThickness, hoopThickness)];
hoop.position=pos;
//[self addChild:hoop];
SKNode *lHoopEdge=[SKNode node];
lHoopEdge.position=CGPointMake(pos.x-size.width/2, pos.y);
lHoopEdge.physicsBody=[SKPhysicsBody bodyWithCircleOfRadius:hoopThickness/2];
lHoopEdge.physicsBody.dynamic=false;
lHoopEdge.physicsBody.restitution=0.9;
[self addChild:lHoopEdge];
SKNode *rHoopEdge=[SKNode node];
rHoopEdge.position=CGPointMake(pos.x+size.width/2, pos.y);
rHoopEdge.physicsBody=[SKPhysicsBody bodyWithCircleOfRadius:hoopThickness/2];
rHoopEdge.physicsBody.dynamic=false;
rHoopEdge.physicsBody.restitution=0.9;
[self addChild:rHoopEdge];
NSMutableArray *rope1=[self makeRopeAtPos:lHoopEdge.position node:lHoopEdge num: nSect gap:size.height/nSect width:ropeThickness];
NSMutableArray *rope2=[self makeRopeAtPos:rHoopEdge.position node:lHoopEdge num: nSect gap:size.height/nSect width:ropeThickness];
CGFloat insetStep=(size.width-gapSize)/2/nSect;
for (int i=0;i<rope1.count;++i) {
SKNode *a=[rope1 objectAtIndex:i];
SKNode *b=[rope2 objectAtIndex:i];
a.position=CGPointMake(a.position.x+i*insetStep, a.position.y);
b.position=CGPointMake(b.position.x-i*insetStep, b.position.y);
}
for (int i=0;i<rope1.count;++i) {
/*
SKNode *n1=[rope1 objectAtIndex:i];
SKNode *n2=[rope2 objectAtIndex:i];
SKPhysicsJointLimit* joint=[self joinBodyA:n1.physicsBody bodyB:n2.physicsBody ropeThickness:ropeThickness];
[self.physicsWorld addJoint:joint];
*/
if (i>0) {
[self.physicsWorld addJoint:[self joinBodyA:((SKNode*)[rope1 objectAtIndex:i]).physicsBody
bodyB:((SKNode*)[rope2 objectAtIndex:i-1]).physicsBody
ropeThickness:ropeThickness]];
[self.physicsWorld addJoint:[self joinBodyA:((SKNode*)[rope2 objectAtIndex:i]).physicsBody
bodyB:((SKNode*)[rope1 objectAtIndex:i-1]).physicsBody
ropeThickness:ropeThickness]];
}
if (i==rope1.count-1) {
[self.physicsWorld addJoint:[self joinBodyA:((SKNode*)[rope1 objectAtIndex:i]).physicsBody
bodyB:((SKNode*)[rope2 objectAtIndex:i]).physicsBody
ropeThickness:ropeThickness]];
}
}
[self addChild:hoop];
}
-(NSMutableArray*)makeRopeAtPos:(CGPoint)p node:(SKNode*)node num:(int)num gap:(CGFloat)gap width:(CGFloat)width {
NSMutableArray *array=[[NSMutableArray alloc] init];
SKNode *last=node;
CGPoint pos=p;
CGPoint anchor=pos;
for (int i=0; i<num; ++i) {
pos=CGPointMake(pos.x, pos.y-gap);
last=[self makeRopeSegAtPos:pos prev:last anchor:anchor first:(i==0) width:width];
anchor=pos;
[array addObject:last];
}
return array;
}
-(SKNode*)makeRopeSegAtPos:(CGPoint)pos prev:(SKNode*)prev anchor:(CGPoint)anchor first:(BOOL)first width:(CGFloat)width {
//SKNode *r=[SKNode node];
SKSpriteNode *r=[SKSpriteNode spriteNodeWithColor:[SKColor whiteColor] size:CGSizeMake(anchor.y-pos.y, width)];
r.position=pos;
r.anchorPoint=CGPointMake(0, 0.5);
if (first) {
//r.size=CGSizeMake(10, 20);
r.constraints=#[[SKConstraint orientToPoint:anchor inNode:self offset:nil]];
}else {
r.constraints=#[[SKConstraint orientToNode:prev offset:nil]];
}
r.physicsBody=[SKPhysicsBody bodyWithCircleOfRadius:width];
r.physicsBody.allowsRotation=NO;
//r.physicsBody.density=0.2;
r.physicsBody.linearDamping=0.8;
[self addChild:r];
SKPhysicsJointLimit *j=[SKPhysicsJointLimit jointWithBodyA:r.physicsBody bodyB:prev.physicsBody anchorA:r.position anchorB:anchor];
[self.physicsWorld addJoint:j];
return r;
}
-(SKPhysicsJoint*)joinBodyA:(SKPhysicsBody*)bodyA bodyB:(SKPhysicsBody*)bodyB ropeThickness:(CGFloat)ropeThickness {
SKPhysicsJointLimit *j=[SKPhysicsJointLimit jointWithBodyA:bodyA bodyB:bodyB anchorA:bodyA.node.position anchorB:bodyB.node.position];
SKSpriteNode *rope=[SKSpriteNode spriteNodeWithColor:[SKColor whiteColor] size:CGSizeMake(j.maxLength, ropeThickness)];
rope.anchorPoint=CGPointMake(0, 0.5);
rope.constraints=#[[SKConstraint distance:[SKRange rangeWithUpperLimit:1] toNode:bodyA.node],[SKConstraint orientToNode:bodyB.node offset:nil]];
SKAction *keepLength=[SKAction repeatActionForever:[SKAction sequence:#[[SKAction waitForDuration:1/60],[SKAction runBlock:^{
rope.size=CGSizeMake(sqrtf(powf(bodyA.node.position.x-bodyB.node.position.x, 2)+powf(bodyA.node.position.y-bodyB.node.position.y, 2)), rope.size.height);
}]]]];
[rope runAction:keepLength];
[self addChild:rope];
return j;
}
use
[self addBasketAtPos: CGPointMake(self.size.width/2, self.size.height*0.7)
WithSize: CGSizeMake(100, 100)
HoopThickness: 10
RopeThickness: 5
GapSize: 80
];
to add a basket
see new screenshot here: https://dl.dropboxusercontent.com/s/xe07ri70rgpxp47/screenshot%202.jpg?dl=0
I am using SpriteKit.
The code below basically makes a lattice of dots on the screen. However, I want to call each 'dot' a different name based on its position, so that I can access each dot individually in another method. I'm struggling a little on this, so would really appreciate if someone could point me in the right direction.
#define kRowCount 8
#define kColCount 6
#define kDotGridSpacing CGSizeMake (50,-50)
#import "BBMyScene.h"
#implementation BBMyScene
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
/* Setup your scene here */
// Background
self.backgroundColor = [SKColor colorWithRed:0.957 green:0.957 blue:0.957 alpha:1]; /*#f4f4f4*/
CGPoint baseOrigin = CGPointMake(35, 385);
for (NSUInteger row = 0; row < kRowCount; ++row) {
CGPoint dotPosition = CGPointMake(baseOrigin.x, row * (kDotGridSpacing.height) + baseOrigin.y);
for (NSUInteger col = 0; col < kColCount; ++col) {
SKSpriteNode *dot = [SKSpriteNode spriteNodeWithImageNamed:#"dot"];
dot.position = dotPosition;
[self addChild:dot];
//6
dotPosition.x += kDotGridSpacing.width;
}
}
}
return self;
}
Here is an image of what appears on screen when I run the above code...
http://cl.ly/image/3q2j3E0p1S1h/Image1.jpg
I simply want to be able to call an individual dot to do something when there is some form of user interaction, and I'm not sure how I would do that without each dot having a different name.
If anyone could help I would really appreciate it.
Thanks,
Ben
- (void)update:(NSTimeInterval)currentTime {
for(SKNode *node in self.children){
if ([node.name containsString:#"sampleNodeName"]) {
[node removeFromParent];
}
}
}
Hope this one helps!
You can set the name property of each node inside the loop.
Then you can access them with self.children[index].
If you want to find a specific node in your children, you have to enumerate through the array.
Update:
To clarify how to search for an item by iterating, here is a helper method:
- (SKNode *)findNodeNamed:(NSString *)nodeName
{
SKNode *nodeToFind = nil;
for(SKNode *node in self.children){
if([node.name isEqualToString:nodeName]){
nodeToFind = node;
break;
}
}];
return nodeToFind;
}