Scrolling Game Scene with SpriteKit - ios

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.

Related

infinite scrolling background - how to make objects move with ground

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..

2D physics / collision formula not working as intended

I have a 2D tile matrix (of walls for now) and for each wall I check if my player's next Rectangle (next as in where it will be in next frame) is colliding with that wall, and if it is, I want my player to stand right next to it, without going inside of it.
Here is the formula I came up with (this is just for left and right collision):
//*move entity
if (KState.Down(Keys.W)) en.AddForce(new Vector2(0, -10));
if (KState.Down(Keys.S)) en.AddForce(new Vector2(0, 10));
if (KState.Down(Keys.A)) en.AddForce(new Vector2(-10, 0));
if (KState.Down(Keys.D)) en.AddForce(new Vector2(10, 0));
foreach(Item i in map.Items)
{
//resolve left-right collision
if (en.NextBody(elapsedTime).Intersects(i.Body))
{
//check distance between top left corners
int distance = (int)Math.Abs(i.Position.X - en.Position.X);
//stop player in his horisontal movement
en.AddForce(new Vector2(-en.Force.X, 0));
//place player next to the wall his about to collide with
en.Position = new Vector2(en.Position.X - distance + i.Body.Width, en.Position.Y);
}
}
//stop player in place
if (KState.Clicked(Keys.Space)) en.AddForce(en.Force*-1);
en.Update(elapsedTime);
**
Which gave me this result (note that fps is not stable and that currently rectangles are squares 32x32):
Top line of text is players Rectangle, and bottom one is Rectangle of the wall that player collided with last.
Now, when the rocks (player) is moving to the left, I'm holding left key for some time, and when going to the right, I'm holding right key for some time.
Problem: As you can see in gif, when player's going left, he's moving 1 pixel to and from left wall. And when he's moving right, he bounces once, and then stays right next to the right wall. I don't want any bouncing but I don't know how to fix this in logic that I'm using.
Also, if you know good logic for how to resolve Rectangle collision so that they are not intersecting from any sides, I'd appriciate sharing it with me.
Edit 1: I managed to resolve collision to the right problem. Instead of the code above I've put
int sign = Math.Sign(i.Position.X - en.Position.X);
en.AddForce(new Vector2(-en.Force.X, 0));
//wall is right and player is left
if (sign > 0) en.Position = new Vector2(i.Body.Left - en.Body.Width, en.Position.Y);
//wall is left and player is right
if (sign < 0) en.Position = new Vector2(i.Body.Right, en.Position.Y);
but the problem with the left side collision is the same. I tried
if (sign < 0) en.Position = new Vector2(i.Body.Right + 1, en.Position.Y); and if (sign < 0) en.Position = new Vector2(i.Body.Right - 1, en.Position.Y); just in case, but the problem is the same (on "...-1..." case player gets stuck in wall).
Edit 2: As #paste requested, here is the code for NextBody(float elapsedTime) from player class.
public Rectangle NextBody(float elapsedTime)
{ return new Rectangle((int)(force.X * elapsedTime) + body.X, (int)(force.Y * elapsedTime) + body.Y, body.Width, body.Height); }
This is what's happening:
The first time the objects collide while the rocks are moving left, the algorithm works as it should. It sees that the objects are overlapping, it sets the force to Vector2.Zero, and it moves the en so that it is touching the item's right side. Good. Your object is stopped and in the right place.
What happens in the next frame is where things go wrong. You set up your force vector, and then call NextBody(). Let's just look at the X-coordinate. It gets set to:
(int)(force.X * elapsedTime) + body.X
What does this evaluate to? force.X is non-zero, and elapsedTime is also non-zero, but they're less than 1, so when they get converted to int, the result is zero. Therefore, your body is in the same place as it was before, which means the Rectangles do not overlap. This means your collision response code gets skipped and your force vector is still not zero!
Then, further down, you call your player's Update() method. I'm guessing that in that code you update the player's Position and Body. But since force is not zero, and you are using a Vector2 for Position, the x-coordinate will end up being something like 31.841304... instead of 32. If you use that number to calculate the new Body and use it in your Draw method, you'll end up drawing it at x = 31. The next time you check, it'll detect a collision, and the player will bounce out again.
There are a number of ways to fix this. What I do in my collision code is to actually move the objects first and keep a record of where they were in the previous frame. That way, the last adjustment to their position is when they are moved out of the collision area.

Centering Camera on Velocity Applied Node Not Working

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

Cocos2D - Simulating infinite scrolling

I have a BIG problem!! I want to make something like old space shooter games like “Asteroids”, where the ship, when going out of the screen, is reappearing at the other side. For example, when the ship go out at the top of the screen, it come back at the bottom. But in my game, there’s a camera following the player, showing only a quarter of the world, and I want to simulate an infinite world this way! Here’s a picture showing what I mean :
What I thought doing was simulate the scroll by only moving game objects, stored in an array, but not the player, and calculating at every frame if the objects are out of the world boundary and re-adding them at the other end of the world (i.e going out at the left would add it back at the right).
But I don’t really like that way of doing… I’d like something more… intuitive..?
Do you guys have any idea of how doing it? Like, any tutorial on the web or just the right words to explain what I mean so I could do a bright research on google (I’m french, so I had a really hard time writing that question)!
Thank you in advance!
Knowing the players position, x,y check to see if the player is within the bounds of the area. If the player has left those bounds, set the player's position to the opposite bounds, possibly adding width/height. I'll do it here for the Y coordinate only:
const float minWarpAreaY( 0 );
const float maxWarpAreaY( 400 );
//if (player.y < minWarpAreaY) { player.y = maxWarpAreaY - player.height; }
//if (player.y > maxWarpAreaY) { player.y = minWarpAreaY + player.height; }
if (player.y < minWarpAreaY) { WarpPlayer(0.0f, (maxWarpAreaY - minWarpAreaY)); }
if (player.y > maxWarpAreaY) { WarpPlayer(0.0f, -(maxWarpAreaY - minWarpAreaY)); }
void WarpPlayer(float amountX, float amountY)
{
player.x += amountX;
player.y += amountY;
for (eachObject in World)
{
eachObject.x += amountX;
eachObject.y += amountY;
}
}
Something along those lines should help.

Jumping effect in games

I'm currently trying to make a basic platformer with XNA and I'm wondering how to create a "jumping effect." I currently have basic keyboard input which allows for sideways movement, but I would like my sprite to slowly progress into a jump rather than instantly teleporting there (right now I have something like Rectangle.Y += 40 every time I jump, making the sprite instantly appear there). Does anyone have any insight?
I'm not totally across how to implement this in XNA/C#, but in Flash games I've made I just added a vertical velocity property. I'll try write everything as C# as I can..
Example; create the velocity property:
float verticalVelocity = 0;
Vertical velocity should be constantly reduced (by gravity). Set up a gravity property somewhere accessible from your player:
float Gravity = 2.5;
And in your update() method for the player, increment the verticalVelocity by Gravity. Also increment the Y position of your player by the verticalVelocity. This will simulate falling:
verticalVelocity += Gravity;
Position.Y += verticalVelocity; // this may be -= in XNA, not sure where the y axis beings
When you hit a surface, the velocity should be reset to 0.
And finally, to jump, simply subtract a given value from verticalVelocity:
public void Jump(float height)
{
// Only jump if standing on a surface.
if(verticalVelocity == 0)
verticalVelocity -= height;
}
You'll eventually want to add gravity and possibly other forces to your game, so I highly recommend you save yourself a lot of pain and implement some kind of basic force system. This can be done using Vector2s, as you can just add them to the speed of your character. Then just apply an instantaneous force to your character to push it up.
If you really don't want to use a physics engine, you can make a Vector2 with the high point of the jump for the Y and the characters X, and then use the Vector2.Lerp method to interpolate between the characters position and the end point of the jump.
This is generally a very bad system to use, and I highly recommend you either use an existing physics engine, or make your own simple one.
use a sinusoidcode should look something like this:
float ground = 0.0f;
float angle = 330.0f;
jump(){
if(ground == 0.0f)ground = Rectangle.Y;
if(Rectangle.Y <= ground)
{
Rectangle.Y+=Math.Sin(angle/(Math.Pi*180));
angle++;
}
}
You can accurately create a gravity effect if you modify the ySpeed dynamically, as opposed to just adding 40.
You want to declare a ySpeed
ySpeed = 0;
Then you want to use an acceleration variable
acceleration = 0.25;
Okay, now that we've done that, let's add gravity, provided that our player isn't touching the floor.
if(playerLocationY + playerHeight > floorLocationY)
{
gravity = false;
}
else
{
gravity = true;
}
if(gravity)
{
ySpeed += acceleration;
}
Now that we've got that down, we want to include something that allows us to jump.
if(KeyPressed == UP)
{
ySpeed -= acceleration;
}
This will move our player in the upward direction
We now want to make sure we actually move, so let's add one last line and we're done.
playerLocationY += ySpeed;
Congratulations, you made it.

Resources