Sprite Kit turn node by Y - objective-c

I have 2 nodes: SKShapeNode coin and SKSpriteNode hero with delegate and didBeginContact method. Contact works fine, but after i reverse my hero texture, nodes are don't interact.
The logic is: hero is go on line, under line and above line positioned coins. When hero is go above line all good, but when i use changeHeroSide method, hero reversed and go under line, didBeginContact method don't response.
- (void)changeHeroSide
{
self.yScale = -fabs(self.yScale); // this part don't interact under line
/** BUT THIS PART WORKS WELL AND INTERACTS UNDER LINE
self.zRotation = M_PI;
self.xScale = fabs(self.xScale);
**/
self.position = CGPointMake(self.position.x, self.position.y - self.frame.size.height);
self.physicsBody.affectedByGravity = NO;
}
Creating of nodes
- (void)create
{
self.hero = [[VZHero alloc] initAtPosition:CGPointZero withPlayer:nil];
self.hero.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:self.hero.frame.size];
self.hero.xScale = -fabs(self.hero.xScale);
SKShapeNode *platform = [self getCurrentPlatform];
self.hero.position = CGPointMake([self getHeroEdgeXCoordinateForPlatform:platform], platform.frame.size.height + _hero.frame.size.height/2);
self.hero.physicsBody.dynamic = YES;
self.hero.physicsBody.categoryBitMask = monsterCategory;
self.hero.physicsBody.contactTestBitMask = projectileCategory;
self.hero.physicsBody.usesPreciseCollisionDetection = YES;
[self addChild:self.hero atWorldLayer:VZWorldLayerCharacter];
SKShapeNode *coin = [SKShapeNode shapeNodeWithCircleOfRadius:radius];
coin.name = #"coin";
coin.strokeColor = [SKColor blackColor];
coin.fillColor = [SKColor yellowColor];
coin.lineWidth = 1;
coin.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:radius];
coin.physicsBody.dynamic = NO;
coin.position = pos;
coin.physicsBody.categoryBitMask = projectileCategory;
coin.physicsBody.contactTestBitMask = monsterCategory;
// coin.physicsBody.collisionBitMask = 0;
coin.physicsBody.usesPreciseCollisionDetection = YES;
[self addChild:coin atWorldLayer:VZWorldLayerCharacter];
}

Mirroring the y scale that way is messing up the physics body properties. Looks like a bug in SpriteKit (at least on iOS 7). You can solve this by wrapping your sprite in a container node. This related question might be of some help.

Related

contactTestBitMask fails to detect contact?

I am detecting contacts from a ball and two edges of the screen (left and right one)
For that purpose I created:
– SKSpriteNode *ball
– SKNode *leftEdge
– SKNode *rightEdge
Here I am setting up the bit masks
static const uint32_t kCCBallCategory = 0x1 << 0;
static const uint32_t kCCEdgeCategory = 0x1 << 1;
Here I am adding the edges
leftEdge.physicsBody = [SKPhysicsBody bodyWithEdgeFromPoint:CGPointZero toPoint:CGPointMake(0.0, self.size.height)];
leftEdge.position = CGPointZero;
leftEdge.physicsBody.categoryBitMask = kCCEdgeCategory;
[self addChild:leftEdge];
Right edge is added exactly the same, except it has different name and position is set to
rightEdge.position = CGPointMake(self.size.width, 0.0);
Here I am configuring and adding the ball
ball.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:6.0];
ball.physicsBody.categoryBitMask = kCCBallCategory;
ball.physicsBody.collisionBitMask = kCCEdgeCategory;
ball.physicsBody.contactTestBitMask = kCCEdgeCategory;
[_mainLayer addChild:ball];
Later in didBeginContact I am checking if first body is the ball and the second body is the edge and adding some explosion to it
SKPhysicsBody *firstBody;
SKPhysicsBody *secondBody;
if (firstBody.categoryBitMask == kCCBallCategory && secondBody.categoryBitMask == kCCEdgeCategory) {
// Collision between ball and the edge
[self addExplosion:contact.contactPoint withName:#"BallEdgeBounce"];
}
And the strange thing is – when the ball hits the left edge - code works fine and explosion effect added to the scene. But the right edge wont do the same. It came by surprise for me, because collisions works just fine. So why does the right edge behave like that? How do I fix this?
You could look at the project on the github, settings is done in GameScene.m file https://github.com/Fenkins/Space-Cannon
I've looked into your Git project and this is happening because you have wrongly set category for rightEdge ( to be more precise, you haven't set it at all). You should do something like this:
SKNode *rightEdge = [[SKNode alloc]init];
rightEdge.physicsBody = [SKPhysicsBody bodyWithEdgeFromPoint:CGPointZero toPoint:CGPointMake(0.0, self.size.height)];
rightEdge.position = CGPointMake(self.size.width, 0.0);
// leftEdge.physicsBody.categoryBitMask = kCCEdgeCategory;//Error when copy/pasting ;-)
rightEdge.physicsBody.categoryBitMask = kCCEdgeCategory;
[self addChild:rightEdge];
You might have missed a simple practice carried out in the didBeginContact: method, of assigning the first and second bodies based on their categoryBitMask values.
-(void)didBeginContact:(SKPhysicsContact *)contact {
SKPhysicsBody *firstBody;
SKPhysicsBody *secondBody;
if (contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask) {
firstBody = contact.bodyA;
secondBody = contact.bodyB;
} else {
firstBody = contact.bodyB;
secondBody = contact.bodyA;
}
...
}
This needs to be done because there is no guarantee which body will be the first or the second.
This is a different direction than where you seem to be going with your app, but it works.
SKSpriteNode *edgeLeft = [SKSpriteNode spriteNodeWithColor:[SKColor clearColor] size:CGSizeMake(1, self.frame.size.height)];
edgeLeft.physicsBody.collisionBitMask = kCCEdgeCategory;
edgeLeft.position = CGPointMake(3, self.frame.size.height / 2);
[self addChild:edgeLeft];
Then do the same for the top edge.
Hope this helped.

Wall collisions in scene kit

I'm trying to write a game where a node can move about but is contained by a series of walls- basically a maze game. Just to get the basic thing going I pulled apart Apple's SceneKitVehicle project (https://developer.apple.com/library/ios/samplecode/SceneKitVehicle) to just the basic elements and put in a cube (the box node in the code below) instead of the physics vehicle in the original code.
Problem is, I programmatically move the box node until it reaches the wall and it just continues straight through the wall without stopping. Meanwhile, I can put the vehicle code back in and that DOES stop when it reaches the wall.
- (void)setupEnvironment:(SCNScene *)scene
{
//floor
SCNNode*floor = [SCNNode node];
floor.geometry = [SCNFloor floor];
floor.geometry.firstMaterial.diffuse.contents = #"wood.png";
SCNPhysicsBody *staticBody = [SCNPhysicsBody staticBody];
floor.physicsBody = staticBody;
[[scene rootNode] addChildNode:floor];
}
- (void)moveBox
{
SCNAction *moveByAction = [SCNAction moveByX:0 y:0 z:-3 duration:0.1];
SCNAction *repeatAction = [SCNAction repeatActionForever:moveByAction];
[_boxNode runAction:repeatAction];
}
- (void)setupSceneElements:(SCNScene *)scene
{
// add walls
SCNNode *wall = [SCNNode nodeWithGeometry:[SCNBox boxWithWidth:400 height:100 length:4 chamferRadius:0]];
wall.geometry.firstMaterial.diffuse.contents = #"wall.jpg";
wall.geometry.firstMaterial.diffuse.contentsTransform = SCNMatrix4Mult(SCNMatrix4MakeScale(24, 2, 1), SCNMatrix4MakeTranslation(0, 1, 0));
wall.geometry.firstMaterial.diffuse.wrapS = SCNWrapModeRepeat;
wall.geometry.firstMaterial.diffuse.wrapT = SCNWrapModeMirror;
wall.geometry.firstMaterial.doubleSided = NO;
wall.castsShadow = NO;
wall.geometry.firstMaterial.locksAmbientWithDiffuse = YES;
wall.position = SCNVector3Make(0, 50, -92);
wall.physicsBody = [SCNPhysicsBody staticBody];
[scene.rootNode addChildNode:wall];
}
- (SCNNode *)setupBox:(SCNScene *)scene
{
SCNBox *boxgeo = [[SCNBox alloc] init];
boxgeo.height = 5;
boxgeo.width = 5;
boxgeo.length = 5;
SCNNode *box = [SCNNode nodeWithGeometry:boxgeo];
box.position = SCNVector3Make(0, 0, -30);
boxPosition = box.position;
box.rotation = SCNVector4Make(0, 1, 0, M_PI);
box.physicsBody = [SCNPhysicsBody kinematicBody];
[scene.rootNode addChildNode:box];
return box;
}
- (SCNScene *)setupScene
{
// create a new scene
SCNScene *scene = [SCNScene scene];
//global environment
[self setupEnvironment:scene];
//add elements
[self setupSceneElements:scene];
//setup box
_boxNode = [self setupBox:scene];
[self moveBox];
//create a main camera
_cameraNode = [[SCNNode alloc] init];
_cameraNode.camera = [SCNCamera camera];
_cameraNode.camera.zFar = 500;
_cameraNode.position = SCNVector3Make(0, 60, 50);
_cameraNode.rotation = SCNVector4Make(1, 0, 0, -M_PI_4*0.75);
[scene.rootNode addChildNode:_cameraNode];
return scene;
}
- (void)viewDidLoad
{
[super viewDidLoad];
[[UIApplication sharedApplication] setStatusBarHidden:YES];
SCNView *scnView = (SCNView *) self.view;
scnView.backgroundColor = [SKColor blackColor];
SCNScene *scene = [self setupScene];
scnView.scene = scene;
scnView.scene.physicsWorld.speed = 4.0;
scnView.pointOfView = _cameraNode;
scnView.delegate = self;
[super viewDidLoad];
}
Any ideas why the box node doesn't get stopped by the wall?
Might the problem be that the box node needs to be propelled by a force like the vehicle is in order for the wall to stop it?
What you are trying to accomplish is a little tricky. Moving a box AND making it react to physics interaction is not possible in the way to envision it with a kinematicBody.
You can keep that, but you will need to fake the collision yourself based on position/bounding boxes, or listen for intersections in the Physics World.
You could also change to a dynamic body, that interacts with other dynamics objects. However, in the moveBox function, you are using SCNActions to move the Box. If you are using a dynamicBody, it cannot be moved that way. You'll have to use forces.
The last option is simply to use a SCNConstraint. Not the prettiest, but if your wall doesn't move it will do the trick.

Cocos2D v3 CCParallaxNode scrolling can't keep player in focus

I am very fresher to game development and I need to develop a game like NinjaJump.
I have created a CCParallaxNode to setup scrolling background and added CCPhysicsNode to setup Physics world. I have created player object as shown below.
// Add a sprite
_sprite = [CCSprite spriteWithImageNamed:#"Icon.png"];
_sprite.position = ccp(self.contentSize.width/2,100);
_sprite.physicsBody = [CCPhysicsBody bodyWithRect:(CGRect){CGPointZero, _sprite.contentSize} cornerRadius:0.0];
_sprite.physicsBody.friction = 0.0f;
_sprite.physicsBody.collisionGroup = #"player";
_sprite.physicsBody.collisionType = #"Player";
//_sprite.physicsBody.collisionMask = 0;
//[self addChild:_sprite];
[foreground addChild:_sprite];
foreground is just a node added into CCScene to easily manage player in-focus.
// code for physics world
_physicsWorld = [CCPhysicsNode node];
_physicsWorld.gravity = ccp(0,-100);
_physicsWorld.debugDraw = YES;
//_physicsWorld.collisionDelegate = self;
[self addChild:_physicsWorld];
_foreground = [CCNode node];
//[self addChild: _foreground];
[_physicsWorld addChild: _foreground];
To make player always visible we have implemented update method as
- (void) update:(CFTimeInterval)currentTime {
// Calculate player y offset
if (_player.position.y > 200.0f) {
//_midgroundNode.position = CGPointMake(0.0f, -((_player.position.y - 200.0f)/4));
_foreground.position = CGPointMake(0.0f, -(_player.position.y - 200.0f));
}
}
I can't understand but the player scrolls off screen anyhow. The code is written in Cocos2d v3.
I have also setup a demo project to show what I implemented: https://www.dropbox.com/s/5s55d00kk80wun4/HumptyJump-Example.zip?dl=0
Any kind of help is appreciated. Thanks in advance.
I could not run your sample code but one thing, I can tell you for sure that there is no need of Physics Engine here.
You just keep your player at a particular height and just move your object right to left.
Apply moving parallax background image and objects to give a feel that your character is moving upwards or downwards.
For reference you can see the games like Swing Drop, made on the same approach as above.
I have implemented 2 fixed views changing their positions while physics body goes down ;).
See the same in action
#interface TestScene () {
CCNode *_background;
CCNode *_foreground;
NSArray *grounds; // Keeps track of each of the 2 views
CCPhysicsNode *_physicsWorld;
}
#implementation TestScene
- (id)init {
self = [super init];
if (!self) return(nil);
// Enable touch handling on scene node
self.userInteractionEnabled = YES;
// Physics world setup
_physicsWorld = [CCPhysicsNode node];
_physicsWorld.gravity = ccp(0,-100);
_physicsWorld.debugDraw = YES;
_physicsWorld.collisionDelegate = self;
[self addChild:_physicsWorld];
// Foreground node in which platforms are there and moves downwards as player goes up
_foreground = [CCNode node];
// Adding background images
CCSprite *default1 = [CCSprite spriteWithImageNamed: #"Default.png"];
[default1 setAnchorPoint: ccp(0, 0)];
CCSprite *default2 = [CCSprite spriteWithImageNamed: #"Default.png"];
[default2 setAnchorPoint: ccp(0, 0)];
[default2 setPosition: ccp(0, default1.contentSize.height)];
[_foreground addChild: default1];
[_foreground addChild: default2];
// Adding into array
grounds = #[default1, default2];
// Adding into physics world
[_physicsWorld addChild: _foreground];
// creating player
_player = [CCSprite spriteWithImageNamed: #"Assets.atlas/Player.png"];
[_player setPosition: ccp(160.0f, 160.0f)];
_player.physicsBody = [CCPhysicsBody bodyWithRect:(CGRect){CGPointZero, _player.contentSize} cornerRadius:0]; // 1
_player.physicsBody.collisionGroup = #"playerGroup"; // 2
_player.physicsBody.collisionType = #"player";
//[_physicsWorld addChild:_player];
[_foreground addChild: _player];
// Multiple platforms can be added into body, they are static entities only
PlatformNode *platform = (PlatformNode *)[PlatformNode node];
[platform createPlatformAtPosition:CGPointMake(110, 50) ofType: PLATFORM_NORMAL];
[_foreground addChild: platform];
return self;
}
- (void) update:(CFTimeInterval)currentTime {
// Take background and physics world down, 163 was Y position of the player
_physicsWorld.position = ccp(_physicsWorld.position.x, 163 - _player.position.y);
// loop the ground
for (CCNode *ground in grounds) {
// Get the world position
CGPoint groundWorldPosition = [_physicsWorld convertToWorldSpace: ground.position];
// Get screen position
CGPoint groundScreenPosition = [self convertToNodeSpace: groundWorldPosition];
NSLog(#"Positioning ground ---> world: (%f, %f) & screen: (%f, %f)", groundWorldPosition.x, groundWorldPosition.y, groundScreenPosition.x, groundScreenPosition.y, nil);
if (groundScreenPosition.y <= -ground.contentSize.height) {
ground.position = ccp(ground.position.x, ground.position.y + 2 * ground.contentSize.height);
break;
}
}
}
-(void) touchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
// Take the player upside
[_player.physicsBody applyImpulse: ccp(0, 215)];
}
#end
This is how I coded the background :).

Detecting collision between objects in array in sprite kit

i'm trying to make a game where obstacles fall from the top of the screen and the player has to try to avoid them, but the didBeginContact method is not getting called and its driving me crazy. here are the parts of my code relevant to the node collisions...
//myScene.h
#interface MyScene : SKScene <SKPhysicsContactDelegate>
//myScene.m
#implementation MyScene
static const uint32_t playerCategory = 0x1 << 0;
static const uint32_t obstacleCategory = 0x1 << 1;
-(id)initWithSize:(CGSize)size
{
if (self = [super initWithSize:size])
{
//setup scene
self.backgroundColor = [SKColor whiteColor];
self.physicsWorld.gravity = CGVectorMake(0, 0);
self.physicsWorld.contactDelegate = self;
obstacles = [NSMutableArray array];
//add player node
player = [SKNode node];
SKSpriteNode *spritePlayer = [SKSpriteNode spriteNodeWithImageNamed:#"black.png"];
spritePlayer.size = CGSizeMake(50, 50);
spritePlayer.position = CGPointMake(self.frame.size.width/2, 100);
player.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:player.frame.size];
player.physicsBody.dynamic = NO;
player.physicsBody.mass = 0.02;
[player addChild:spritePlayer];
player.physicsBody.categoryBitMask = playerCategory;
player.physicsBody.contactTestBitMask = obstacleCategory;
player.physicsBody.collisionBitMask = 0;
player.physicsBody.usesPreciseCollisionDetection = YES;
[self addChild:player];
[self spawnNewObstacles];
}
return self;
}
- (void)spawnNewObstacles
{
//spawn obstacles
obstacle = [SKSpriteNode spriteNodeWithImageNamed:#"black.png"];
obstacle.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:obstacle.frame.size];
obstacle.physicsBody.dynamic = YES;
obstacle.physicsBody.categoryBitMask = obstacleCategory;
obstacle.physicsBody.contactTestBitMask = playerCategory;
obstacle.physicsBody.collisionBitMask = 0;
obstacle.physicsBody.usesPreciseCollisionDetection = YES;
}
-(void)didBeginContact:(SKPhysicsContact *)contact
{
NSLog(#"didBeginContact Called!");
}
the didBeginContact method is not getting called and i can't find what wrong with my code
all help is appreciated...
sknode size isn't set correctly because there is no image reference, and if you log its size if should be inf, and when I tested your code I returned an exception.
to fix this error change the following line
player.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:player.frame.size]; change that to
to the following and it should work
player.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:spritePlayer.size];
as I said below just ditch the player sknode, it is simply redundant unless there is a strong reason to use it.
///////previous answer
I couldn't help but to notice that you didn't add the obstacle node to any parent node, namely the scene, like what you did with the player node using this line
[self addChild:player];
you need to do the same thing with the obstacle node
[self addChild:obstacle];
, I am surprised that you didn't mention not seeing those nodes on the screen, unless you added them in a different section of your code that you did not include, then at this point I suggest you log the positions of both nodes to give an idea of how things are located on the screen.
as for the array you won't need it all you have to do is give a name for every obstacle node like #"obstacle", then you can use the following method to access all the nodes that has that name
obstacle.name = #"obstacle";
/////later when you want to check for node removal
[self enumerateChildNodesWithName:#"obstacle" usingBlock:^(SKNode *node, BOOL *stop) {
SKSpriteNode *child = (SKSpriteNode*)node;
if (child.position.y <0){ //or any arbitrary condition
[child removeFromParent];
}
}];
Lastly, I can't see the point from using the player as a sknode and then adding an skspritenode to it, unless I am missing something !!! :) . You can simply ditch the player sknode all together and simply use the spritePlayer node instead.
Hope this helps.

Moving SKNodes and SKSpriteNodes

I have the following setup in my game:
An SKNode with the name _backgroundLayer
A repeating texture which I have added to the _backgroundLayer 9 times to make a larger background
An SKSprite with the name levelButton, which is added to the _backgroundLayer ([_backgroundLayer addChild:levelButton];).
I use levelButton.anchorPoint = CGPointMake(0.5, 0.5); to make the levelButton have an anchor point in the middle.
Now, when I make levelButton.position = CGPointMake(0, 0); and _backgroundLayer.position = CGPointMake(0, 0); the levelButton's middle is correctly situated in (0, 0) with its middle being the in lower left corner of the screen. So that's fine.
However, if I move the levelButton to be levelButton.position = CGPointMake(100, 0); and _backgroundLayer.position = CGPointMake(-100, 0);, as I show below, the levelButton should still have its middle in (0,0) aka the lower left corner of the screen. However, that is not the case, and the levelButton is more to the right, its something like 50 pixels to the right. But it shouldn't be, since I am moving the _backgroundLayer 100 to the left (-100) and _levelButton 100 to the right (100). It should have stayed in place.
These are basic stuff which I do not understand why they are not working as they should. I am probably doing something wrong but I can not find it even though I've read through the iOS Games by Tutorials and a bunch of tutorials and tips.
Please help.
Now, my code is the following:
#implementation LevelSelectScene
{
SKNode *_backgroundLayer;
}
-(id)initWithSize:(CGSize)size {
/* Setup your scene here */
_backgroundLayer = [SKNode node];
_backgroundLayer.name = #"backgroundLayer";
SKTexture *backgroundTexture = [SKTexture textureWithImageNamed:#"levelSelect"];
int textureID = 0;
for (int i = 0; i<3; i++) {
for (int j = 0; j<3; j++) {
SKSpriteNode *background = [SKSpriteNode spriteNodeWithTexture:backgroundTexture];
background.anchorPoint = CGPointZero;
background.position = CGPointMake((background.size.width)*i,
(background.size.height)*j);
background.zPosition = 0;
background.name = [NSString stringWithFormat:#"background%d", textureID];
textureID++;
[_backgroundLayer addChild:background];
}
}
[self addChild:_backgroundLayer];
SKSpriteNode * levelButton = [SKSpriteNode spriteNodeWithImageNamed:#"lock"];
levelButton.anchorPoint = CGPointMake(0.5, 0.5);
levelButton.position = CGPointMake(100, 0); //IMPORTANT
levelButton.zPosition = 150;
levelButton.name = #"test";
[_backgroundLayer addChild:levelButton];
_backgroundLayer.position = CGPointMake(-100, 0); //IMPORTANT
}
return self;
}
Found it!
I was changing the scale of the _backgroundLayer SKnode so the coordinates were different.