I tried to bind the camera on my player, with this code:
-(void)didSimulatePhysics {
[self centerOnNode: player];
}
-(void)centerOnNode: (SKNode *) node {
CGPoint cameraPositionInScene = [node.scene convertPoint:node.position fromNode:node.parent];
node.parent.position = CGPointMake(node.parent.position.x - cameraPositionInScene.x, node.parent.position.y - camerapositionInScene.y);
}
but nothing happens except of getting output like this:
Setting the position of a SKScene has no effect.
2014-03-22 16:55:35.604 Jelly Jumper [5801:90b] SKScene:
Setting the position of a SKScene has no effect.
2014-03-22 16:55:35.753 Jelly Jumper [5801:90b] SKScene:
Setting the position of a SKScene has no effect.
2014-03-22 16:55:35.907 Jelly Jumper [5801:90b] SKScene:
You will need to create a node that holds everything but for player and user interface in it.
For example, call it worldNode. Add it as child to scene and everything on screen but for player and ui to it. Add player and ui as children to your scene directly.
Now when you need to move the player, move worldNode instead. You can calculate the amount you need to move player in update method and apply negative of this to world node. This way it will work as you want.
Related
I have a rolling marble and would like my camera to follow it. My problem is that my camera is also rotating around my object as it is rotating.
Here's my code.
-(void) setupCameraOnMarble:(SCNNode*)marble
{
[cameraNode removeFromParentNode];
[marble addChildNode:cameraNode];
SCNLookAtConstraint *marbleStare = [SCNLookAtConstraint lookAtConstraintWithTarget:marble];
marbleStare.gimbalLockEnabled = YES;
cameraNode.constraints = #[marbleStare];
}
I'm thinking that a transform constraint should work but I don't understand how it works.
Are you sure you need to make cameraNode a child of marble?
Because according to SKNode docs: when a node’s coordinate system is scaled or rotated, this transformation is applied both to the node’s own content and to that of its descendants see SKNode
So i assume you need to un-child camera from marble. And to achive that followint the object behavior from camera, you may update camera coordinates somewhere in the code, that moves the marble
I have un-childed my camera to my sphere and made my class a SCNSceneRendererDelegate protocol and added this to the rendering loop:
-(void) renderer:(id)renderer updateAtTime:(NSTimeInterval)time
{
[cameraNode setPosition: SCNVector3Make(marbleNode.presentationNode.position.x, marbleNode.presentationNode.position.y + 5, marbleNode.presentationNode.position.z + 10)];
}
If I log the coordinates of the marble.presentationNode they are changing but my camera still doesn't move!
I am writing a game in which the user must prevent falling objects (rocks) to pass the button of the screen using some plates, each click make a plate at that position but he can only have 5 plates at each time and clicking on sixth point vanished the first plate while creating a new one.
That was a brief on how my game mechanics work.
Here is the part of my code that counts falling objects(sprites) using physics and if they became more than 10 a game over message will be shown and the code restarts the game, a new game. to have a new game I need to
-(void)didSimulatePhysics
{
[self enumerateChildNodesWithName:#"rock" usingBlock:^(SKNode *node, BOOL *stop) {
if (node.position.y < 0)
{
ScoreNum++;
[node removeFromParent];
score.text = [NSString stringWithFormat:#"%# %li",ScoreMSG,(long)ScoreNum];
if (ScoreNum > 10)
{
score.fontSize = 40;
score.position = CGPointMake(CGRectGetMidX(self.frame),CGRectGetMidY(self.frame));
score.text = #"Game Over!";
SKScene *spaceshipScene = [[GameScene alloc] initWithSize:CGRectMake(0, 0,CGRectGetMaxY(self.frame),CGRectGetMaxX(self.frame)).size];
printf("%f %f",CGRectGetMidX(self.frame),CGRectGetMidX(self.frame));
SKTransition *doors = [SKTransition doorsOpenVerticalWithDuration:0.25];
[self.view presentScene:spaceshipScene transition:doors];
ScoreNum = 0;
}
}
}];
}
GameScene is subclassed from SKScene like this
#import <SpriteKit/SpriteKit.h>
#interface GameScene : SKScene
#end
The code works find but the plates (all of the sprites on the screen) gets squeezed from sides
The following picture shows the results, first time, second time and third time, I needed 10 reputation to include 3 pictures so I had to merge them all into one picture, Here it is.
https://www.imageupload.co.uk/images/2015/08/30/123.png
Sorry, looks like I can not embed an image since I have not enough reputation!
Why each my scene squeezes?
Looks like you set a scaleMode in your viewController.viewDidLoad that handles your game scene, nothing wrong with your physics code (in your question).
look for something like this scene.scaleMode = SKSceneScaleModeAspectFill usually SKScenes are automatically scaled based on the screen size of the device and it is upto SKScene to provide you a scene.
Your scenes in your pictures shows that scenes have a complete and full cover but they are scaled.
It is kind of hard to handle sprite size while do not controling scale. I suggest to get ride of every Scale Mode and remark them all and control the scale your self.
if you are not going to zoom in/out as I believe it is the case in your game you just need to reset the scenes scale to 1 each time after each scene transition.
all.
I am now developing an ios game using sprite kit.The game mechanics include: infinitely scroll sprites from top to bottom.
My game scene holds just 3 sprites,top,middle,bottom.
I have dozens of sprite images,so my solution is dynamic create 3 sprites ( so the sprites can fill the whole screen),when the bottom sprite is off screen,then destroy it;when the top sprite
goes down the top frame,create a new sprite node,then exchange the bottom,middle,top sprite.
The pseudo code:
in the interface file:
- GameScene:SKScene
{
...
SKSpriteNode *_topSprite;
SKSpriteNode *_middleSprite;
SKSpriteNode *_bottomSprite;
...
}
in the implementation file:
- (void)update:(CFTimeInterval)currentTime
{
// 1 compute time interval
// 2 update sprite node
_topSprite.position move down 100*timeInterval
_middleSprite.position move down 100*timeInterval
_bottomSprite.position move down 100*timeInterval
// 3 crop the off-screen bottom sprite node
if (_bottomSprite is offScreen)
{
[_bottomSprite removeFromParent]
}
// 4 check the highest sprite position visible
// if the highest position is below the frame top,then create new sprite
_bottomSprite = _middleSprite;
_middleSprite = _topSprite;
_topSprite = [self createNewNode]; // random a sprite image
}
the game fps is 60,and the nodes counts is not increasing.
All seems good,but i found sometimes the game just suddenly has a very shot choke causing the moving frame not continuous.
In my instinct,I thought the main reason is caused by step 4.
But I don't have any idea to solve it.
Any suggestions will be appreciated.
Here is the createNewNode method:
- (SKSpriteNode *)createNewNode {
NSString *blockName = [self randomImageName];
SKSpriteNode *sprite = [[SKSpriteNode alloc] initWithImageNamed:blockName];
sprite.position = _highestPosition;
sprite.anchorPoint = CGPointMake(0.5, 0);
NSLog(#"putAnotherNodeOnTop %#", blockName);
[self addChild:sprite];
return sprite;
}
A couple of suggestions.
Since you are using the same three images, you don't need to re-create the sprites each time. It should be less expensive to set the hidden value to YES, move the sprite somewhere, set the texture and then show it again with hidden to NO. SpriteKit doesn't draw hidden nodes, or ones off screen.
Your code basically asks for a PNG on disk every frame in update when createNewNode: is called. It should be more performant to load the textures in memory and assign them to sprites as needed. If you have just a few, making them SKTexture instance variables would be an easy way to do it.
Use spritesheets (texture atlases), it is much faster to draw an image from memory at mapped coordinates than to load it from disk each time. For example with three images in a spritesheet this can be a single draw call. From disk this is three separate draw calls.
You are returning a sprite and adding it to the scene in the createNewNode: method. Maybe that is what you want for some reason. It stood out as possible duplicate effort.
Every example I see on how to use transitions is with CCScene like this:
[[CCDirector sharedDirector] replaceScene:[CCTransitionCrossFade transitionWithDuration:0.5f scene:[NewScene scene]]];
My app is setup with a GameScene, MenuScene ..etc. Within the GameScene I setup the DPad and the HUD and under that I have the hero moving to different levels which are of type CCNode. I want to use the native transitions to move from one level to the next.
I found a question similar to what I need here but that user was essentially told to build the transitions himself.
The confusing part to me is that CCNode contains a method called:
- (void) onEnterTransitionDidFinish
So, is there a way to use transitions with CCNode that I'm missing? Is my app setup incorrectly and the levels should be CCScene? If so how to I keep the HUD and Dpad over the transition?
Thanks in advance for any help.
CCTransitions are specifically for scenes. CCNodes contain onEnterTransitionDidFinish, as it gets called on every CCNode child of a CCScene. Fortunately, you can just check the CCTransition code and see how they are implemented and then roll your own for layers (many have done and shared this).
For example, I wrote a simple little method which will use blocks to give the same fade-in, fade-out effect as a scene transition, but for an individual layer (along with some added features). Just pass in the layer you want to cover up, the speed of the fade out and fade in, the color to fade to, and the block you wish to execute while the layer is hidden.
-(void)fadeLayer:(CCLayer*)layer withOutDuration:(float)outTime inDuration:(float)inTime color:(ccColor3B)color withBlock: (void(^)())block
{
CGSize winSize = [[CCDirector sharedDirector] winSize];
CCLayerColor *toplayer = [CCLayerColor layerWithColor:ccc4(color.r, color.g, color.b, 0) width:winSize.width height:winSize.height];
[layer addChild:toplayer z:INT_MAX];
[toplayer runAction:
[CCSequence actions:
[CCFadeIn actionWithDuration:outTime],
[CCCallBlock actionWithBlock:block],
[CCFadeOut actionWithDuration:inTime],
[CCCallBlockN actionWithBlock:^(CCNode *node) {
[node removeFromParentAndCleanup:YES];
}],
nil]
];
}
I try to add a CCParticleSystemQuad as child to LHSprite:
player = [loader spriteWithUniqueName:#"player"];
NSAssert(player != nil, #"Couldn't find the player!");
// Particles
smokeParticles = [CCParticleSystemQuad particleWithFile:#"smoke.plist"];
smokeParticles.position = ccp(-30.0, 0.0);
[player addChild:smokeParticles];
But I keep getting this error message:
2012-12-29 22:51:44.373 MyProject[15396:15203]
*** Assertion failure in -[LHSprite addChild:z:tag:],
/MyPath/MyProject/libs/cocos2d/CCSprite.m:567
Adding the CCParticleSystemQuad to the CCLayer
[self addChild:smokeParticles];
works just fine.
CCSprite.m: Line 567
NSAssert([child isKindOfClass:[CCSprite class]],
#"CCSprite only supports CCSprites as children when using CCSpriteBatchNode");
Can anyone tell me why this happens?
The reason is given in assert message. It happens because CCSprite only supports CCSprites as children when using CCSpriteBatchNode. If you use CCSpriteBatchNode to draw a sprite, you can only add CCSprites as children to this sprite.
Inside LH in the Level Navigator - drag your sprite on top of the MAIN_LAYER - this will remove the sprite from the batch node and make it self rendered - now you will be able to add children to it via code. If you use a batch sprite, you can only add children to the sprite that have the same texture as the batch node (that is, uses the same image file)
for the LHParallaxNode - this does not add the particle/sprite to the node - it just moves it. So you should also add your particle to the layer
[self addChild:particle z:100];
then add it to the parallax node.
Bogdan Vladu the creator of LevelHelper says so..