I'm trying to make this thing:
When user press a key once, the sprite smoothly moves on some pixels. But it just "teleporting" to the position. Here is the code:
int co = 0;
Vector2 ppos=new Vector2(x,y);
if (Gdx.input.isKeyJustPressed(Keys.A)){
while (co < 33) {
batch.begin();
ppos.y += Gdx.graphics.getDeltaTime()*5;
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.draw(Splayer, ppos.x, ppos.y); //Splayer is a sprite
batch.end();
co++;
out(co+"");
}
}
What am I doing wrong?
I will deconstuct your code:
while (co < 33) {
So this will loop 33 times since you have co = 0 and increment co each loop.
ppos.y += Gdx.graphics.getDeltaTime()*5;
You increment the y position by your framerate * 5. so something like 5 * 0.02 * 33 is happening which makes 3.3. Nothing wrong with that but it is kinda unconventional to use a loop for that. Since doing y = 5 * framerate * 33 would be the same, easier and faster.
It depends on what you want to end up with but basically "we" do something like this.
//Have position variable
private Vector2 position;
//Have a speed variable
private float speed;
//direction variable
private Vector2 direction;
//have a velocity variable (direction * speed)
private Vector2 velocity;
velocity should be direction * speed and the velocity can then be added each frame to the position. let's say we want to move up. The direction would be (0,1) (the direction should never exceed the length of 1, if it does then normalize the vector direction.nor(). This will make sure it is 1 long so multiplying this will results in the same speed in any direction.
direction = new Vector2(0,1);
//an easy way to make it go 45 degree up/right would be
direction = new Vector2(1,1);
direction.nor(); //normalized to 1 long.
//now we can make the velocity
velocity = direction.cpy().scl(speed); //we copy the vector first so we are not changing the direction vector.
//If you want to have this framerate independent
velocity = direction.cpy().scl(speed * Gdx.graphics.getDeltatime);
Now we just add velocity to position. Basic math (1, 1) + (0, 1) = (1 ,2). Yes that is how simple Vectors are. original pos (0, 0)plus direction multiplied by speed+ (0 * 10, 1 * 10) = (0, 10)`. So to add velocity to position in code:
position.add(velocity);
batch.draw(textures, position.x, position.y);
This would be my way of doing it, I find this very easy.
What you are doing wrong is generating a new Vector each game loop when you press "A". You should think twice about having the new keyword in your loop. It is better the change you vector or reset it since it the old one will be lost in memory and needs to be collected. One Vector will not get you into trouble but 1 Texture that needs manual disposing will, learn it the right way.
Other then that, why have a variable named ppos? Why not just position or patientPosition or palaeoanthropologyPosition or whatever the "p" stands for. You are only required to type it once in most IDE because intellisense will pick it up. So make your and others life easier by clearly defining variables.
you should use Scene2D for smooth movement.
Related
I have a problem with the correct vector alignment. I want to get a vector pointing in the same direction as the player, but with a constant Y value of 0. The point is, whatever the player's vertical and horizontal rotation, the vector's Y value was 0. The vector is always supposed to point horizontally (value 0), but keeping the direction of the player's rotation.
This picture shows the situation from the side. The red line represents an example of the player's viewing direction (up - down), and the green one the effect I want to achieve. Regardless of the direction in which the player is looking, up or down, the green line remains unchanged:
Here, in turn, I have presented this situation from the top. The red line is the player's viewing direction (left - right) and the green is the effect I want to achieve. As you can see, the player's rotation on this axis sets my vector exactly the same.
I was able to write a piece of code, but it doesn't behave correctly: the Y axis gets higher and higher as the player turns up or down. I don't know why:
Vector playerDirection = player.getLocation().getDirection();
Vector vector = new Vector(playerDirection.getX(), 0, playerDirection.getZ()).normalize().multiply(3);
How to do it correctly?
tl;dr:
Vector vector = new Vector(-1 * Math.sin(Math.toRadians(player.getLocation().getYaw())), 0, Math.cos(Math.toRadians(player.getLocation().getYaw())));
You are missing a fundamental principal of creating a new Vector based on where a player is looking. I don't know the math of it very well, but I can mess around with the math of people who are better than I at Geometry.
As such, let's try to reduce the number of Vector variables you have defined. Taking a quick peek at the source for Location, we can actually create your Vector directly to avoid having multiple defined.
public Vector getDirection() {
Vector vector = new Vector();
double rotX = this.getYaw();
double rotY = this.getPitch();
vector.setY(-Math.sin(Math.toRadians(rotY)));
double xz = Math.cos(Math.toRadians(rotY));
vector.setX(-xz * Math.sin(Math.toRadians(rotX)));
vector.setZ(xz * Math.cos(Math.toRadians(rotX)));
return vector;
}
As you can see, the pitch and yaw of a player are not a 1:1 relationship. No idea why, but let's repurpose their logic.
Here's how we'll do that:
public Vector getVectorForAdixe(Location playerLoc) {
Vector vector = new Vector();
double rotX = playerLoc.getYaw();
double rotY = 0; // this is the important change from above
// Original Code:
// vector.setY(-Math.sin(Math.toRadians(rotY)));
// Always resolves to 0, so just do that
vector.setY(0);
// Original Code:
// double xz = Math.cos(Math.toRadians(rotY));
// Always resolves to 1, so just do that
double xz = 1;
vector.setX(-xz * Math.sin(Math.toRadians(rotX)));
vector.setZ(xz * Math.cos(Math.toRadians(rotX)));
return vector;
Nice! Now, cleaning it up a bit to remove those comments and unnecessary variables:
public Vector getVectorForAdixe(Location playerLoc) {
Vector vector = new Vector();
double rotX = playerLoc.getYaw();
vector.setY(0);
vector.setX(-1 * Math.sin(Math.toRadians(rotX)));
vector.setZ(Math.cos(Math.toRadians(rotX)));
return vector;
Why does this math work like that? No idea! But this should almost certainly work for you. Could even inline it if you really wanted to keep it how you had it originally:
Vector vector = new Vector(-1 * Math.sin(Math.toRadians(player.getLocation().getYaw())), 0, Math.cos(Math.toRadians(player.getLocation().getYaw())));
Closing note, if you want to be able to get the pitch/yaw FROM the vector, that code is here: https://hub.spigotmc.org/stash/projects/SPIGOT/repos/bukkit/browse/src/main/java/org/bukkit/Location.java#310
I have a rectangle array holding multiple objects, moving back and forth on X axis.
Iterator<Rectangle> iter = array.iterator();
while ( iter.hasNext() ) {
Rectangle obj = iter.next();
array.get(i).x += speed * Gdx.graphics.getDeltaTime() ;
if (obj.x + obj.width > 800 || obj.x < 0) {
speed = -speed;
}
}
When the speed gets bigger, you'll start noticing the first object in the array overlapping with the other objects and pushing them apart. How to fix that?
Basically each object has
Rectangle obj = new Rectangle();
obj.x = xpos;
obj.y = ypos;
obj.width = width;
obj.height = height;
xpos += width + 4;
And has a texture, image, a sqaure, a rectange, a triangle... And each object is generated at an X position xpos different than the other. All they do is keep moving on the X axis, from x=0 till 800 and back.
What happens is that when the first object gets to 0, it tries to increase its speed again and overlapping with other objects, and then time after time, all objects keep overlapping and get further apart from each other. I want the distance between the objects to stay constant at any speed.
From what you've commented, the questions appears to be "How can I make all these blocks move together, bouncing from one edge to another". The issue being that you're getting bouncing, but they stop acting as a group.
Firstly, if you want to treat them as a group - the simplest way is to consider them as one large bounding box containing lots of smaller (inconsequential) objects. Moving that as a single object from side to side will give you the behaviour you need.
That aside, the direct answer to your question is "you're changing the direction mid-way through iteration". So in any single tick, some objects have moved left and some have moved right - meaning they stop acting as a group.
How you organise it is up to you, but this is the basic idea you need:
// assume "speedForThisFrame" is a float defined outside this function
float speedForNextFrame = speedForThisFrame
// iterate through however you want
Iterator<Rectangle> iter = array.iterator();
while ( iter.hasNext() ) {
Rectangle obj = iter.next();
obj.x += speedForThisFrame * Gdx.graphics.getDeltaTime() ;
// if it's moved out of bounds, we will change direction NEXT fame
if (obj.x + obj.width > 800 || obj.x < 0) {
speedForNextFrame = -speedForThisFrame;
}
}
// now that all movement has finished, we update the speed
speedForThisFrame = speedForNextFrame
The key thing is everything must move by the same amount, in the same direction, every frame. Changing the speed mid-update will cause them to act independently.
Note, you will still have issues when your group is larger than the bounds - or when they go over the bounds in one frame and don't fully get back the next frame. These are separate issues though and can be asked in a separate question.
I think your problem is that, caused by variations in Gdx.graphics.getDeltaTime(), the rectangles exceed your 0/800 borders by different distances.
An example:
First step:
Rect #1 x=790
Rect #2 x=780
Speed=100, DeltaTime=0.11 => DeltaX=11
After this step, Rect#1 would be at 801, Rect#2 at 791, their distance is 10.
Next step:
DeltaTime=0.12 => DeltaX=12
After this step, Rect#1 is at 789, Rect#2 at 803, their distance is 14.
Your rectangles vary their distance because they travel different distances. A possible solution would be to really bounce at the borders. So you should not only invert the speed but also take the distance a rectangle exceeded the border and let it travel this distance in the opposite direction:
So Rect#1 at 790, moving 11 pixels rightwards, should not be at 801 in the end of the step but at 799 (moving 10 pixels to the right and one to the left).
I am trying to shoot an object(a spell) depending on the rotation of the players arm. The spell is supposed to come out of the hand and shoot towards where the mouse cicked(the arm rotates and points to where the mouse is). This is how the arm rotates in game.
public boolean mouseMoved(int screenX, int screenY) {
tmp.x = screenX;
tmp.y = screenY;
tmp.z = 0;
cam.unproject(tmp);
rot = MathUtils.radiansToDegrees * MathUtils.atan2((float)tmp.y - (float)player.getArmSprite().getY() - player.getArmSprite().getHeight(),
tmp.x -player.getArmSprite().getX() - player.getArmSprite().getWidth());
if (rot < 0) rot += 360;
//Last right or left means if hes looking left or right
if(player.lastRight)
player.setObjectRotation(rot + 80);
if(player.lastLeft)
player.setObjectRotation(-rot - 80);
And this is how the spell is supposed to shoot based off rotation
//destination is a vector of where on screen the mouse was clicked
if(position.y < destination.y){
position.y += vel.y * Gdx.graphics.getDeltaTime();
}
if(position.x < destination.x){
position.x += vel.x * Gdx.graphics.getDeltaTime();
}
However this is very wonky and never really reacts the way it supposed to with a few exceptions. It fires from the hand and then if the y axis is equal it completely evens out and goes till it reaches the x position, I want it to fire from the hand to the position clicks perfectly straight from point a to point b, this is clearly a rotation problem that I just can't seem to figure out how to tackle.
Here is an image of what is happening
Example image
The red indicates where I clicked, as you can see it reached the x pos first and now is traveling to the y when it should have reached the x and y pos of where I clicked first
Any help with this problem is extremely appreciated!
I'm pretty bad at radians and tangents but luckily we have vectors.
Since you have the rot ation in degrees of the arm. I advice to use Vectors to use for any Vector related math now.
//A vector pointing up
Vector2 direction = new Vector2(0, 1);
//Let's rotate that by the rotation of the arm
direction.rotate(rot);
Now direction is the direction the arm is pointing. If your rotation is calculated where up = 0. So you might need to rotate it 180, 90 or -90 degrees. Or in the case you did something silly any degrees.
Your spell should have a Vector too for it's position. Set that to the hand or wherever you want to start from. Now all you need to do is scale that direction since it's currently has a length of 1. If you want to move 5 units each frame you can do direction.scl(5) now it is of length 5. But technically speaking it's no direction anymore now everybody calls it velocity so let's do.
//when you need to fire
float speed = 5;
Vector2 velocity = direction.cpy().scl(speed);
//update
position.add(velocity);
draw(fireballImage, position.x, position.y);
I copied direction first, otherwise it would also be scaled. Then I just added the velocity to the position and draw using that Vector.
And to show Vectors are awesome you should see this awesome badlogic vs mouse program I created. https://github.com/madmenyo/FollowMouse there are just a view lines of my own code. It just takes a little bit of vector knowledge and it's very readable.
I'm making a game similar to mario and I've got this map generated by arrays and images. But my problem is that I don't know how to make a hitbox system for all the tiles. I've tried to have a position based collision system based on your position on the map
like this
if(xpos > 10*mapX && xpos < 14 * mapX){
ypos -= 1;
}
But I don't want to that for every wall or hole.
So is there a way to check in front, below and above the character to see if there is a hitbox there and if there is you cant move that direction or fall?
Thank you
If it's a simple 2D game, I'd suggest dividing the map into square tiles. You could store the map in the memory as a two dimensional array and during each frame check tiles adjacent to the player. Of course he can occupy as much as 4 tiles during movement, but it makes you check only up to 12 positions, which can be easily done.
Further collision checking can be done easily using image position and dimension.
Remember that there is no need to check if a static object (environment) is colliding with something, you just need to check objects that have made a move since last frame, i.e. the player and sprites.
EDIT:
Let's say you've got the following section of map (variable map):
...
.pe
ooo
where
. = nothing
p = player
o = floor
e = enemy
you also have the pair (x, y) representing tile indices (not exact position) of the player. In this case you have to do something like this:
if ("o".equals(map[y + 1, x + 1]))
//floor is under
if ("e".equals(map[y, x + 1]))
//enemy is on the right
if ("o".equals(map[y - 1, x]))
//floor is above us
If any of these conditions are met, you have to check image positions and handle collisions.
Note: clicked submit way after the last post was made...
As Mateusz says a 2D array is best for this type of game:
e.g. using chars:
0123456789012
0 ==
1 * ===
2===== =======
So in this case tileMap[8][1] == '*'. You'd probably be best using an enumeration instead of chars though e.g. Tile.SPRING for a Sonic style spring board.
If your map was made up of regular sized tiles you could say:
int xInFrontOfPlayer = playerX + PLAYER_WIDTH;
int xBehindPlayer = playerX - PLAYER_WIDTH;
Tile tileInFrontOfPlayer = getTileAtWorldCoord(xInFrontOfPlayer, playerY);
Tile tileBehindPlayer = getTileAtWorldCoord(xBehindPlayer, playerY);
...
public Tile getTileAtWorldCoord(int worldX, worldY) {
return tileMap[worldX / TILE_WIDTH][worldY / TILE_HEIGHT];
}
Where TILE_WIDTH and TILE_HEIGHT are the dimensions of your tiles in pixels. Then use similar math for yAbovePlayer and yBelowPlayer.
You might then have some logic in your game loop:
if user is pressing the "go right" key:
if the tile to the right is Tile.SPACE:
move player right
else if the tile to the right is Tile.WALL:
don't do anything
if the tile below is Tile.SPACE:
fall
Having the following display() -
float tranX , tranY , tranZ ;
public void display(GLAutoDrawable gLDrawable) {
final GL gl = gLDrawable.getGL();
gl.glLoadIdentity();
gl.glPushMatrix();
gl.glTranslatef(tranX ,tranY ,tranZ);
gl.glCallList(i);
gl.glPopMatrix();
tranX += 0.05;
tranY += 0.05;
tranZ += 0.05;
}
As you can see each display() calling the matrix of the object in gl.glCallList(i) saved and get coordinates change by gl.glTranslatef(tranX ,tranY ,tranZ) .
Suppose that at some stage I want to save this object at its current position (after gl.glTranslatef calling ) and start to translate it to another tranX , tranY , tranZ values .
How could I save this object position considering the above gl.glPushMatrix() and gl.glPopMatrix() ?
Push/pop matrices are there to accumulate complex matrix transformations that would otherwise be painful to do by hand. For storing and moving object positions, keeping variables as you have done is correct. To expand on that and, as you say start moving in another, add a directionX/y/Z. Eg, tranX += directionX etc. Then when you want to change direction, simply set directionX/Y/Z to a different value.
The speed will change depending on how fast your computer is though. You'll want to find the time since the last frame (or last call to display) and do something like this: transX += velocityX * deltaTime etc.
If you want to move an object from one point to another specific point, you want to look into key-framed interpolation. For example position = pointA * (1.0 - x) + pointB * x and make x move from 0 to 1 (x += speed * deltaTime). When x is above one, pointA becomes pointB and pointB is set to the next position in the list. Then subtract 1.0 from x and continue.
Assuming you're translating from the origin (and even if you're not) - it should be quite possible to save the position of the object relative to the origin in this case. You might use an object that stores the data in three fields (xPosition, yPosition, zPosition).
To translate the object later on, you would first translate to this position and then translate from there as needed.