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.
Related
I'm using Lance for a game where the gameplay area is a tiled map. When a player presses the left-arrow key, their character should move one tile to the left, etc. I tried two approaches, see below, but got neither to work.
Could either approach be modified to work with tile-based movement? Or is a third approach needed? Or is Lance not suited to this kind of game?
Approach 1: Adjust the player's position directly when a key is pressed. From my GameEngine class:
if (inputData.input == 'left') {
player.position.x -= 32;
player.angle = 180;
}
While this works well for a single player, it doesn't in multiplayer. When player A moves, their position is not updated on player B's screen.
Approach 2: Set the player's state when a key is pressed:
if (inputData.input == 'left') {
player.state = 'walkLeft';
}
Then add a postStep handler in the common GameEngine class. (Adding it to Player didn't work). This code turns the player (over many steps) to face 180 degrees and then accelerates the player in that direction:
onPostStep(event) {
let players = this.world.queryObjects({instanceType: Player});
players.forEach(player => {
if (player.state == 'walkLeft') {
if (Math.abs(player.angle - 180) > 2)
player.turnLeft(2);
}
else {
player.accelerate(1);
player.state = '';
}
}
})
}
With this approach, if a player presses the left arrow key, their angle changes as expected at first, but the acceleration and movement is erratic. Also, Player A's position appears different on their screen vs the screen of Player B.
The Spaaace demo is the base for my project, so my project uses the same bending, physics engine, etc.
The first approach is better. The Brawler game in the sample collection does exactly what you describe. You can look at the BrawlerGameEngine.js code in https://github.com/lance-gg/tinygames/tree/master/brawler
Make sure that the action is processed in the method
GameEngine::processInput(inputData, playerId)
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.
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've got and XNA 2D game which I've been making, but I'm having problems with it.
I've got boxes scrolling across the screen which my sprite is jumping over. The sprite is being followed by a 2D camera, and I fear that the camera is causing issues, as it's causing the scrolling boxes to stop half way across the screen instead of continuing, and also the number of lives are decreasing rapidly rather than one life when one collision occurs.
This is the code in which my sprite collides with the moving boxes
Rectangle fairyRectangle = new Rectangle((int)position.X, (int)position.Y, texture.Width, texture.Height);
hit = false;
for (int i = 0; i < GameConstants.TotalBoxes; i++)
{
if (scrollingBlocks.boxArray[i].alive)
{
Rectangle blockRectangle = new Rectangle((int)scrollingBlocks.boxArray[i].position.X, (int)scrollingBlocks.boxArray[i].position.Y, scrollingBlocks.boxArray[i].texture.Width, scrollingBlocks.boxArray[i].texture.Height);
if (IntersectPixels(fairyRectangle, fairyTextureData, blockRectangle, blockTextureData))
{
scrollingBlocks.boxArray[i].alive = false;
hit = true;
lives--;
scrollingBlocks.boxArray[i].alive = true;
scrollingBlocks.boxArray[i].position.X = random.Next(GameConstants.ScreenWidth);
scrollingBlocks.boxArray[i].position.Y = 570;
}
}
}
And this is the update function in the scrolling boxes
public void Update(GameTime gameTime)
{
for (int i = 0; i < GameConstants.TotalBoxes; i++)
{
boxArray[i].position.X = boxArray[i].position.X - 5;
boxArray[i].position.Y = boxArray[i].position.Y;
if (boxArray[i].position.X < 0)
{
boxArray[i].position.X = randomno.Next(GameConstants.ScreenWidth) + 700;
boxArray[i].position.Y = 570;
}
Helper.WrapScreenPosition(ref boxArray[i].position);
}
}
I want them to start at the right hand screen and move all the way across to x = 0, but they're currently stopping around halfway at x = 400.
And finally this is where I'm drawing it all, with the sprite and the block
if (gameState == GameState.PLAYINGLEVEL3)
{
graphics.GraphicsDevice.Clear(Color.Aquamarine);
spriteBatch.Begin(SpriteBlendMode.AlphaBlend);
backgroundManager.Draw(spriteBatch);
//scrollingBlocks.Draw(spriteBatch);
spriteBatch.DrawString(lucidaConsole, "Score: " + score + " Level: " + level + " Time Remaining: " + ((int)timer / 1000).ToString() + " Lives Remaining: " + lives, scorePosition, Color.DarkOrchid);
spriteBatch.End();
spriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Deferred, SaveStateMode.None, camera.transform);
scrollingBlocks.Draw(spriteBatch);
fairyL3.Draw(spriteBatch);
spriteBatch.End();
}
Thanks for any help!
From the code provided I can't tell much about how your camera works but my guess is that when you start the level your camera picks a new center point(rather than the original xna screen location)
In other words, the center of the camera may have pushed it back a bit, so what you think is position 0 of the screen may have been pushed forward to the middle of the screen making the boxes stop. I'd suggest looking at these camera tutorials http://www.david-amador.com/2009/10/xna-camera-2d-with-zoom-and-rotation/ or http://www.youtube.com/watch?v=pin8_ZfBgq0
Cameras can be tricky so I'd suggest looking at a few tutorials to get the hang out matrices and view ports.
As for the lives taking away more than one, It's because your saying "If this box is colliding with my player, take away lives." the computer doesn't know that you only want one taken away as long as it's colliding with the player its taking away lives, it will keep decrementing.
Hopefully this will give you some ideas :D
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.