I have a subclass of SKNode which consists of a few sprites that make up a player. I would like the "camera" to center on this node (Always have the player in the center). Now before you down vote this for it being a duplicate, hear me out. The Apple documents suggest making the player node completely static, and instead moving around a camera node. However in my case I'm applying multiple properties of physics to my character, including velocity impulses. My first thought would be to just apply these impulses to the camera node itself, however this has become impossible due to the fact that the character has a small soft-body physics engine on it. I'm applying velocity to it like so:
player.primaryCircle.physicsBody!.velocity = CGVector(dx: player.primaryCircle.physicsBody!.velocity.dx+relVel.dx*rate, dy: player.primaryCircle.physicsBody!.velocity.dy+relVel.dy*rate)
I managed to get it to partially work with the following code:
override func didSimulatePhysics() {
self.player.position = player.primaryCircle.position
self.camera.position = player.position
centerOnNode(camera)
}
func centerOnNode(node: SKNode) {
let cameraPositionInScene: CGPoint = node.scene!.convertPoint(node.position, fromNode: node.parent!)
node.parent!.position = CGPoint(x:node.parent!.position.x - cameraPositionInScene.x, y:node.parent!.position.y - cameraPositionInScene.y)
}
However that didn't 100% work, as seen here: (It should be focused on the red circle)
http://gyazo.com/b78950e6cc15b60f390cd8bfd407ab56
As you can see, the world/map is moving, however it doesn't seem to be moving fast enough to center the player in the middle. (And note that the "Unamed" text is at a fixed spot on the screen -- That's why it seems to always be in the center)
I think this should still work with physics unless I am not truly understanding the question. We did something similar with our SKATiledMap with that Auto Follow Feature. What you need to do is make sure the player is added to a node you can move (usually a map) as a child and then in the update function you do something like this...(sorry it isn't in swift)
-(void)update
{
if (self.autoFollowNode)
{
self.position = CGPointMake(-self.autoFollowNode.position.x+self.scene.size.width/2, -self.autoFollowNode.position.y+self.scene.size.height/2);
//keep map from going off screen
CGPoint position = self.position;
if (position.x > 0)
position.x = 0;
if (position.y > 0)
position.y = 0;
if (position.y < -self.mapHeight*self.tileWidth+self.scene.size.height)
position.y = -self.mapHeight*self.tileWidth+self.scene.size.height;
if (position.x < -self.mapWidth*self.tileWidth+self.scene.size.width)
position.x = -self.mapWidth*self.tileWidth+self.scene.size.width;
self.position = CGPointMake((int)(position.x), (int)(position.y));
}
}
Map being the node that the player is added to. Hopefully that helps. Also here is the link to the git hub project we have been working on. https://github.com/SpriteKitAlliance/SKAToolKit
Related
I have an infinitely scrolling background and a character 'walking' on the ground plane.
I want to drop objects from above and have them land on the ground - so far so good. But the objects don't move with the ground.
Can't find any examples where this is taken care of.
my 'ground' is just an edge at the correct height with regard to my graphics background.
let Edge = SKNode()
Edge.physicsBody = SKPhysicsBody(edgeFromPoint: CGPointZero, toPoint: CGPointMake(self.frame.width + 1, 0))
Edge.position = CGPointMake(0, bottomShelf_ItemStartPosition.y)
self.addChild(Edge)
I've tried giving my ground SKSriteNode physical properties, but this didn't make any difference.
What would be the best approach for this? Must be a simple way to make the moving ground effect objects.
adding scroll routine:
func backgroudScrollUpdate1(){
back.position = CGPoint(x: back.position.x - scrollPerFrameAmount, y: back.position.y)
back2.position = CGPoint(x: back2.position.x - scrollPerFrameAmount, y: back2.position.y)
if (back.position.x < -(back.size.width / 2)) {
back.position = CGPointMake(back2.position.x + back.size.width, back.position.y)
}
if (back2.position.x < -(back.size.width / 2)) {
back2.position = CGPointMake(back.position.x + back2.size.width, back2.position.y)
}
}
update - some progress
i have set up a physics world, where my ground is infinitely scrolling. I've got some items that fall out of the 'sky' and land on the ground. I can move the things around by applying impulse directly or one thing hitting another.
But, what i expected is that the scrolling ground would 'pull' things along with it if they are on the ground?
I have played around with different friction levels of both the ground at the items touching the ground, but it doesn't seem to matter.
one thing that did sort of work is if i set the phsyics body of an item to a circle, then the ground does influence the item, turning it around in the opposite direction of the scrolling - but when it hits an edge it just stops, rather than spinning at the edge.
If that part is working, then why wouldn't a rectangle be dragged along by the ground ?
would love to see example code of a phsyics world that does have the ground effecting other nodes..
In short: How do I make a scrolling gameScene which is NOT infinite?
I'll try to explain what I want to achieve with an example: Hill Climb Race
In this game you drive a car (or any sort of crazy vehicle, actually ;)) up a hill.
Now there is one particular thing about the game that I can't get my head around:
It's pretty obvious that the tracks of each individual stage are NOT laid out randomly. i.e. the course of the track is always the same each time you play it.
What I want to learn is:
How do you create a scrolling game scene like that? Is it a huge background node which gets scrolled or is there some sort of fancy tiling involved?
My game needs to scroll both axis (x,y). The players node starts in the center of the game area and can be moved around. There are some obstacles spread around in the area, some of which are not visible initially, because they lie at the edges of the 'game world'.
I suppose the easiest solution would be to use a big background node, but how will that affect the memory consumption of the game?Thanks for your help!
We built something like this into our SKATiledMap. The trick is to add the object you want to follow to the background you want to scroll. This will also keep the background on screen.
-(void)update
{
if (self.autoFollowNode)
{
self.position = CGPointMake(-self.autoFollowNode.position.x+self.scene.size.width/2, -self.autoFollowNode.position.y+self.scene.size.height/2);
//keep map from going off screen
CGPoint position = self.position;
if (position.x > 0)
position.x = 0;
if (position.y > 0)
position.y = 0;
//self.mapHeight*self.tileWidth gives you the size of the map in points
if (position.y < -self.mapHeight*self.tileWidth+self.scene.size.height)
position.y = -self.mapHeight*self.tileWidth+self.scene.size.height;
if (position.x < -self.mapWidth*self.tileWidth+self.scene.size.width)
position.x = -self.mapWidth*self.tileWidth+self.scene.size.width;
self.position = CGPointMake((int)(position.x), (int)(position.y));
}
}
self in this case is the background and autoFollowNode is the players. You could just use self.size.width instead of self.mapHeight*self.tileWidth Hopefully that makes sense and his helpful.
I'm looking for the proper SpriteKit way to handle something of a scrollable world. Consider the following image:
In this contrived example, the world boundary is the dashed line and the blue dot can move anywhere within these boundaries. However, at any given point, a portion of this world can exist off-screen as indicated by the image. I would like to know how I can move the blue dot anywhere around the "world" while keeping the camera stationary on the blue dot.
This is Adventure, a sprite kit game by apple to demonstrate the point I made below. Read through the docs, they explain everything
Theres a good answer to this that I can't find at the moment. The basic idea is this:
Add a 'world' node to your scene. You can give it a width/height that is larger than the screen size.
When you 'move' the character around (or blue dot), you actually move your world node instead, but in the opposite direction, and that gives the impression that you're moving.
This way the screen is always centered on the blue dot, yet the world around you moves
below is an example from when I was experimenting a while ago:
override func didMoveToView(view: SKView) {
self.anchorPoint = CGPointMake(0.5, 0.5)
//self.size = CGSizeMake(600, 600)
// Add world
world = SKShapeNode(rectOfSize: CGSize(width: 500, height: 500))
world.fillColor = SKColor.whiteColor()
world.position = CGPoint(x: size.width * 0.5, y: size.height * 0.5)
world.physicsBody?.usesPreciseCollisionDetection = true
self.addChild(world)
}
override func update(currentTime: CFTimeInterval) {
world.position.x = -player.position.x
world.position.y = -player.position.y
}
override func didSimulatePhysics() {
self.centerOnNode(self.camera)
}
func centerOnNode(node: SKNode) {
if let parent = node.parent {
let nodePositionInScene: CGPoint = node.scene!.convertPoint(node.position, fromNode: parent)
parent.position = CGPoint(
x: parent.position.x - nodePositionInScene.x,
y: parent.position.y - nodePositionInScene.y)
}}
If you create a "camera" node which you add to your "world" node, a couple of simple functions (above) allow you to "follow" this camera node as it travels through the world, though actually you are moving the world around similar to Abdul Ahmad's answer.
This method allows you to use SpriteKit functionality on the camera. You can apply physics to it, run actions on it, put constraints on it, allowing effects like:
camera shaking (an action),
collision (a physics body, or matching the position of another node with a physics body),
a lagging follow (place a constraint on the camera that keeps it a certain distance from a character, for example)
The constraint especially adds a nice touch to a moving world as it allows the "main character" to move around freely somewhat while only moving the world when close to the edges of the screen.
I setup a hero and some platforms that are moving from the top downwards.
With these I have collisionBitMasks that detect when the hero lands on a platform if the hero comes from above (to let the hero jump through the platforms)
if (_hero.physicsBody.velocity.dy > 0) {
_hero.physicsBody.collisionBitMask = 0;
}
else {_hero.physicsBody.collisionBitMask = platformCategory;
}
Everything works fine, except that the hero keeps bouncing on the platform.
Is there a way to let him sit on it, while the platform is moving down?
I tried using physicsBody.resting and physicsBody.friction, but without any success.
Thanks for help Guys
Had the same issue just a minute ago. It works with setting the restitution but you need to set both restitutions. The one of the hero AND the one of the platform (or in my case the scene boundaries).
so:
hero.physicsBody.collisionBitMask = platformCategory
hero.physicsBody.restitution = 0.0
platform.physicsBody.restitution = 0.0
any_other_object_that_should_still_bounce.physicsBody.restitution = 1.0
will do it. All other objects on the screen still bounce on the platform as long as you set their restitution > 0
The trick is to avoid any physics behavior while the hero is on the platform by resetting the hero body's velocity and setting the hero's position to a fixed vertical offset from the platform's position.
In semi-pseudo-code:
-(void) didSimulatePhysics
{
if (<hero on platform>)
{
hero.physicsBody.velocity = CGPointZero;
hero.position = CGPointMake(hero.position.x,
platform.position.y + <offset as needed>);
}
}
Have you tried altering the restitution property of the nodes' physics bodies?
_hero.physicsbody.restitution = 0;
It controls the bounciness...
You can use the Collision category:
When the velocity.dy is < 0 the hero is falling.
func collisionWithPlayer(player: SKNode) {
// 1
if player.physicsBody?.velocity.dy < 0 && (player.position.y - player.frame.size.height / 2.0) >= (self.position.y){
// 2
player.physicsBody?.collisionBitMask = CollisionCategoryBitMask.Platform
}
I'm fiddling around with cocos2d v3 in combination with sprite builder.
The game is a simple catch and avoid game, objects coming down an the hero is standing in the bottom of the screen and you can move him around with the accelerometer.
I have a level file (cclayer) with some objects (ccnode) in it, the have physics enabled.
In my update function I move the layer slowly down.
If the objects have physics enabled, they just drop down. If I switch it to statics physics, they stay in place.
The only way I can find to move the object along with the layer is to turn off the physics completely. But then the collisions won't work anymore...
This kept me buys for the past 4 hours or so.
What is the best approach for this situation?
Thanks in advance guys!
this is my update function:
- (void)update:(CCTime)delta {
float maxX = winSize.width - _hero.contentSize.width/2;
float minX = _hero.contentSize.width/2;
CMAccelerometerData *accelerometerData = _motionManager.accelerometerData;
CMAcceleration acceleration = accelerometerData.acceleration;
CGFloat newXPosition = _hero.position.x + acceleration.x * 1000 * delta;
newXPosition = clampf(newXPosition, minX, maxX);
_hero.position = CGPointMake(newXPosition, _hero.position.y);
//level position
CGPoint oldLayerPosition = _levelNode.position;
float xNew = oldLayerPosition.x;
float yNew = oldLayerPosition.y -1.4f;
_levelNode.position = ccp(xNew, yNew);
}
This is a design decision made within the physics engine. Physics bodies do not move with their parents. See: https://github.com/cocos2d/cocos2d-iphone/issues/570
You need to change the design of your game so that your hero moves, and the obstacles stay in the same position. Then you implement a camera like mechanism to follow your moving hero.
We have used the same approach in our flappy bird tutorial.