How to move a sprite relative to another sprite's rotation? - java

In my game I have a rocket sprite and at the bottom of the rocket there is an animated flame sprite , I want the flame sprite to align with the bottom of the rocket when the rocket rotates.
This is the rocket when the game starts, everything is OK:
But when i rotate the rocket the flame does not align with the rocket's bottom like this:
How can i make it align?
here is my code:
// draw rocket
rocketSprite.setRegion(AssetLoader.rocketTexture);
rocketSprite.setPosition(rocket.getX(), rocket.getY());
rocketSprite.setOrigin(rocket.getWidth() / 2, rocket.getHeight() / 2);
rocketSprite.setSize(rocket.getWidth(), rocket.getHeight());
rocketprite.setRotation(rocket.getRotation());
rocketSprite.setScale(1, 1);
rocketPodSprite.draw(game.batch);
//flame width and height
int w = 70;
int h = 20;
// randomly change scale of flame for effects
float sw = MathUtils.random(2f / 7f, 1);
float sh = MathUtils.random(0.5f, 1);
// draw flame
flameSprite.setRegion(AssetLoader.flameTexture);
flameSprite.setPosition(rocket.getX() + rocket.getWidth() / 2 - w, rocket.getY() - 15);
flameSprite.setOrigin(w, h / 2);
flameSprite.setSize(w, h);
flameSprite.setRotation(rocket.getRotation());
flameSprite.setScale(sw, sh);
flameSprite.draw(game.batch);

You need to change origin - point arround which your sprite will be rotated.
Your problem is that both rocket and flame are rotated arround their own centres however you would rather like to rotate them both arround rocket's one. Then you need to set the flame sprite origin with position of rocket center.
flameSprite.setOrigin(w, h / 2 + rocket.getHeight() / 2 + SPACE_BETWEEN_ROCKET_AND_FLAME);
As I see in your case the SPACE_BETWEEN_ROCKET_AND_FLAME has value of 15 - flame.height due to this position set:
flameSprite.setPosition(rocket.getX() + rocket.getWidth() / 2 - w, rocket.getY() - 15);

Related

Rotating Coordinates (Java and Geometry)

I am working on a 2D java game engine using AWT canvas as a basis. Part of this game engine is that it needs to have hitboxes with collision. Not just the built in rectangles (tried that system already) but I need my own Hitbox class because I need more functionality. So I made one, supports circular and 4-sided polygon shaped hitboxes. The way the hitbox class is setup is that it uses four coordinate points to serve as the 4 corner vertices that connect to form a rectangle. Lines are draw connecting the points and these are the lines that are used to detect intersections with other hitboxes. But I now have a problem: rotation.
There are two possibilities for a box hitbox, it can just be four coordinate points, or it can be 4 coordinate points attached to a gameobject. The difference is that the former is just 4 coordinates based on 0,0 as the ordin while the attached to gameobject stores offsets in the coordinates rather than raw location data, so (-100,-100) for example represents the location of the host gameobject but 100 pixels to the left, and 100 pixels up.
Online I found a formula for rotating points about the origin. Since Gameobject based hitboxes were centered around a particular point, I figured that would be the best option to try it on. This code runs each tick to update a player character's hitbox
//creates a rectangle hitbox around this gameobject
int width = getWidth();
int height = getHeight();
Coordinate[] verts = new Coordinate[4]; //corners of hitbox. topLeft, topRight, bottomLeft, bottomRight
verts[0] = new Coordinate(-width / 2, -height / 2);
verts[1] = new Coordinate(width / 2, -height / 2);
verts[2] = new Coordinate(-width / 2, height / 2);
verts[3] = new Coordinate(width / 2, height / 2);
//now go through each coordinate and adjust it for rotation
for(Coordinate c : verts){
if(!name.startsWith("Player"))return; //this is here so only the player character is tested
double theta = Math.toRadians(rotation);
c.x = (int)(c.x*Math.cos(theta)-c.y*Math.sin(theta));
c.y = (int)(c.x*Math.sin(theta)+c.y*Math.cos(theta));
}
getHitbox().vertices = verts;
I appologize for poor video quality but this is what the results of the above are: https://www.youtube.com/watch?v=dF5k-Yb4hvE
All related classes are found here: https://github.com/joey101937/2DTemplate/tree/master/src/Framework
edit: The desired effect is for the box outline to follow the character in a circle while maintaining aspect ratio as seen here: https://www.youtube.com/watch?v=HlvXQrfazhA . The current system uses the code above, the effect of which can be seen above in the previous video link. How should I modify the four 2D coordinates to maintain relative aspect ratio throughout a rotation about a point?
current rotation system is the following:
x = x*Cos(theta) - y *Sin(theta)
y = x*Sin(theta) + y *Cos(theta)
where theta is degree of rotation in raidians
You made classic mistake:
c.x = (int)(c.x*Math.cos(theta)-c.y*Math.sin(theta));
c.y = (int)(c.x*Math.sin(theta)+c.y*Math.cos(theta));
In the second line you use modified value of c.x. Just remember tempx = c.x
before calculations and use it.
tempx = c.x;
c.x = (int)(tempx*Math.cos(theta)-c.y*Math.sin(theta));
c.y = (int)(tempx*Math.sin(theta)+c.y*Math.cos(theta));
Another issue: rounding coordinates after each rotation causes distortions and shrinking after some rotations. It would be wise to store coordinates in floats and round them only for output, or remember starting values and apply rotation by accumulated angle to them.

How to get coordinates of some points in my .TMX map? - LibGDX

I'm working on my game in LibGDX. I loaded my .TMX map to game, set a camera and a viewport.
gameCam = new OrthographicCamera();
gamePort = new FitViewport(Marina.V_WIDTH / Marina.PPM, Marina.V_HEIGHT / Marina.PPM, gameCam)
gameCam.position.set(gamePort.getWorldWidth() / 2, gamePort.getWorldHeight() / 2, 0);
gameCam.position.x = 2.08f;// Here you have the starting cords of my camera.
gameCam.position.y = 1.36f;
The problem is, when player touch one of the four egdes the camera is moving (4.16/-4.16 on X and 2.72/-2.72 on Y). For example, from 2.08/1.36 to 2.08+4.16/1.36 when the player touch the right edge.
My question is how to get cords of green points?
https://i.stack.imgur.com/w2Zjt.png
To get the edges of what the camera is seeing, you simply have to do this:
Left edge: camera.position.x - (viewport.getWorldWidth() / 2)
Right edge: camera.position.x + (viewport.getWorldWidth() / 2)
Top edge: camera.position.y - (viewport.getWorldHeight() / 2)
Bottom edge: camera.position.y + (viewport.getWorldHeight() / 2)
I think I have answered your question

Libgdx Box2D - Draw Sprite on Body - Heavy problems

I'm heaving heavy problems with drawing a Sprite on a Box2D body.
I'm creating a platformer and I did draw a sprite on a body before but then realized that my gravity is really floaty. After googling I found out that I should work with meters when using Box2D and I changed my code to work with a pixel to meter conversion ratio of 25.
Since then I can't get everything to work though, my sprite just won't draw on my body.
Camera:
float width = Gdx.graphics.getWidth() * PIXELS_TO_METERS;
float height = Gdx.graphics.getHeight() * PIXELS_TO_METERS;
camera = new OrthographicCamera(width / 2, height / 2);
camera.position.set(camera.viewportWidth / 2, camera.viewportHeight / 2, 0);
camera.update();
Here is the code for my body:
idleRegion = new TextureRegion(xeonTexture, 20, 13, 50, 65);
xeonSprite = new Sprite(idleRegion);
//Physics
bodyDef = new BodyDef();
bodyDef.type = BodyDef.BodyType.DynamicBody;
bodyDef.position.set(100 * PIXELS_TO_METERS, 100 * PIXELS_TO_METERS);
bodyDef.fixedRotation = true;
body = world.createBody(bodyDef);
PolygonShape shape = new PolygonShape();
shape.setAsBox((xeonSprite.getWidth() / 2) * PIXELS_TO_METERS, (xeonSprite.getHeight() / 2) * PIXELS_TO_METERS);
FixtureDef fixtureDef = new FixtureDef();
fixtureDef.shape = shape;
fixtureDef.density = 1f;
fixtureDef.friction = 1f;
fixtureDef.restitution = 0f;
fixtureDef.isSensor = false;
physicsFixture = body.createFixture(fixtureDef);
Here is how I set the position of my sprite:
final float widthD2 = (xeonSprite.getWidth() / 2);
final float heightD2 = (xeonSprite.getHeight() / 2);
final float angle = this.getBodyAngle();
xeonSprite.setOrigin(widthD2, heightD2);
xeonSprite.setPosition(body.getPosition().x - xeonSprite.getWidth() / 2, body.getPosition().y - xeonSprite.getHeight() / 2);
xeonSprite.setRotation((float) Math.toRadians(angle));
I also tried the following:
xeonSprite.setPosition(body.getPosition().x - xeonSprite.getWidth() / 2 * METERS_TO_PIXELS, body.getPosition().y - xeonSprite.getHeight() / 2 * METERS_TO_PIXELS);
And here is how I draw my Sprite:
penguinBatch.begin();
xeon.getPenguinSprite(stateTime, Gdx.graphics.getDeltaTime()).draw(penguinBatch);
penguinBatch.end();
This is yet another case of "Pixels do not exist in your game world", they are only there to represent you game world to your client. A camera man for TV need to know as much about your TV as you need to know about your clients screens. Just capture what you want to show and let LibGDX do the rest.
So, you should never, ever work with pixels. In the case you want pixel perfect drawing you might want to setup your camera to the amount of pixels of the screen you are targeting but after that you treat these "pixels" as units.
Box2D does not work with pixel per meter. It works with 1 unit == 1 meter and you should stick to that. But how much meters a pixel should represent is still up to you.
If you want to draw a 1m x 1m box you should not multiply by some number you create based of screen pixels like you do now, you should just give it 1 x 1 to represent the box.
shape.setAsBox(1, 1);
Your sprite should be exactly the same
xeonSprite.setSize(1, 1);
And you should position them on the same position. It's really that simple, 1 UNIT == 1m and there is nothing more to it, the camera does the rest. If you want to show 9m x 16m of your game world you setup your camera like this.
camera = new OrthographicCamera(9, 16);
If you want to represent the empire state building you would give the body and sprite a size of 57, 443 and then your camera needs to represent a much larger area, if you don't want to render a small portion of it. If you want to fit the height exactly on screen without stretching you need your own conversion.
float camHeight = 443;
float aspectRatio = Gdx.graphics.width / Gdx.graphics.height
float camWidth = aspectRatio * 443;
Although those Gdx calls give the pixels your clients are running you should still not treat these as pixels about these since you don't know at what screen I will be playing your game.
Now forget about the empire state building example and your camera's center is positioned at 0, 0 world space so bottom left is at -4.5, -8 so you might want to translate it and don't forget to .update the camera. You are currently doing this in your code.
If you start drawing your sprite and updating Box2D you will see a ball drop from the center of your screen. Of course you need to keep updating your sprites position to match the body position for it to move along with the Box2D body.
Just forget pixels, unless you want to code your own camera or viewports, which you probably do not want because you chose LibGDX. The rest of your game code does not need to work with pixels in any way. The OrthographicCamera can calculate world position to screen position for you by camera.unproject(touchVector).

Android Screen coordinates to canvas view coordinates

I am trying to turn my screen x and y coordinates to the ones that's used to draw on screen.
So I get my screen X and Y coordinates from MotionEvent thats fired by my touch listener.
I thought it should be as easy as multiplying those by the matrix that's used to draw on canvas so I creatad Matrix instance at creation of view
matrix = new Matrix();
when void onDraw(Canvas canvas) gets called, I set the canvas' matrix to be the matrix I created on the constructor and apply all my transformations to the matrix
matrix.reset();
canvas.setMatrix(matrix);
canvas.translate(getWidth() / 2, getHeight() / 2);
canvas.scale(mScaleFactor, mScaleFactor);
canvas.translate(-getWidth() / 2, -getHeight() / 2);
canvas.translate(-x, -y);
The view looks as it should but when on my touch listener I try to turn my screen coordinates to view coordinates with that matrix using mapPoints(float[] points) the values it gives aren't right, I draw cross on 0, 0 in onDraw
canvas.drawLine(0f, -100f, 0f, 100f, viewportPaint);
canvas.drawLine(-100f, 0f, 100, 0f, viewportPaint);
and when I click where it appears to be after scaling and what not the values I receive aren't even close to 0, 0
float[] array = new float[]{e.getX(), e.getY()};
matrix.mapPoints(array);
Log.v(TAG, "points transformed are " + array[0] + ", " + array[1]);
When I took this picture where I am clearly clicking the 0, 0 mark I received the following logging:
03-09 14:08:48.803: V/XXXX(22181): points transformed are 403.43967, 628.47
Ps. I am not touching matrix anywhere else than in my onDraw code
I had the question in my mind inverted, when I thought about the question again after having a break from it(after 8 hours..) I got it, I had to invert the matrix in order to perform this operation.
Also the matrix wasn't transformed when I was transforming canvas so i had to canvas.get(matrix)
in order to get the same matrix that the canvas was using..
This might help others:
In onDraw()
canvasMatrix.postScale(mScaleFactor, mScaleFactor);
canvasMatrix.postTranslate(-getWidth() / 2, -getHeight() / 2);
canvas.setMatrix(canvasMatrix);
Somewhere else where you need the mapped coordinates:
Matrix m = new Matrix();
canvasMatrix.invert(m);
float[] touch = new float[] { X, Y };
m.mapPoints(touch);
X = touch[0];
Y = touch[1];
there you go.

Isometric Tile Selection

I'm not all that good with Maths, so i was hoping some of you guys could help?
I'm trying to make a function to convert mouse coordiantes into a particular tile in an isometric view.
It won't let me post images for a stupid reason, so ill just link the image:
Link
All of the algorithms i have seen so far work with the X & Y axes going diagonal, my game is currently set up like this, and i would like to keep it so.
Is there an algorithm so that if the mouse was at the red dot, it would return the coordinates of the tile that it is sitting on? (6,2)
Thanks in advance!
There is a good start : http://www.java-gaming.org/index.php?topic=23656.0
Enjoy :)
EDIT
Full-trusted "DrDobb's" website, full article on this : http://www.drdobbs.com/parallel/designing-isometric-game-environments/184410055
<0;4>
x <0;3> <1;4>
<0;2> <1;3> <2;4>
<0;1> <1;2> <2;3> <3;4>
<0;0> <1;1> <2;2> <3;3> <4;4>
<1;0> <2;1> <3;2> <4;3>
<2;0> <3;1> <4;2>
y <3;0> <4;1>
<4;0>
I rendered the tiles like above.
the sollution is VERY simple!
first thing:
my Tile width and height are both = 32 this means that in isometric view, the width = 32 and height = 16! Mapheight in this case is 5 (max. Y value)
y_iso & x_iso == 0 when y_mouse=MapHeight/tilewidth/2 and x_mouse = 0
when x_mouse +=1, y_iso -=1
so first of all I calculate the "per-pixel transformation"
TileY = ((y_mouse*2)-((MapHeight*tilewidth)/2)+x_mouse/2;
TileX = x_mouse-TileY;
to find the tile coordinates I just devide both by tilewidth
TileY = TileY/32; TileX = TileX/32;
DONE!! never had any problems!
It's quite easy actually once you get your head wrapped around it. All you do is find out where your mouse is relative to the map and then reverse to how you are drawing the tiles.
I draw my map in the double "for" loop like this:
For x coord: x * (TileWidth / 2) - (y * (TileWidth / 2))
For y coord: x * (TileHeight / 2) + (y * (TileHeight / 2))
So my x goes from top left to bottom right and my y goes from top right to bottom left. Mind though, like for the first tile the world coord will be 0,0 but the top pixel starts at x=0 + (tilewidth / 2) so we have to compensate for that when we are looking to find which tile the mouse is over. (or we could do that for the whole world itself by giving it a offset).
Now first we have to find the mouse position in relation to the world since you probably want a moving camera. My camera's centre starts as 0,0 so i have to compensate the mouse by half the screen width like so:
mouseWorldPosX = mouse.x + cam.x - (screen.width / 2)
mouseWorldPosY = mouse.y + cam.y - (screen.height / 2)
This is all we need to calculate the mouse position back to tile position.
For X:
tileX = (mouseWorldPosX + (2 * mouseWorldPosY) - (tileWidth / 2)) / tileWidth
As you can see we divide the whole thing by the tilewidth since we multiplied it in the draw method. The (tileWidth / 2) is just there to compensate for the offset i mentioned earlier.
For Y:
tileY = (mouseWorldPosX - (2 * mouseWorldPosY) - (tileHeight / 2) / -tileWidth
It's practically the same but the other way around. We subtract the Y world position since the Y axis runs the other way around. This time we compensate the offset for the height of the tile and we divide the whole thing by negative tilewidth, again since it runs the other way.
I hope this helps below is a working example of a method i looked up, it returns a vector with the tile coordinates:
public Vector2 MouseTilePosition(Camera cam, GraphicsDevice device)
{
float mPosX = newMouseState.X + (cam.Position.X - (device.Viewport.Width / 2));
float mPosY = newMouseState.Y + (cam.Position.Y - (device.Viewport.Height / 2));
float posx = (mPosX + (2 * mPosY) - (Map.TileWidth / 2)) / Map.TileWidth;
float posy = (mPosX - (2 * mPosY) - (Map.TileHeight / 2)) / -Map.TileWidth;
return new Vector2((int)posx, (int)posy);
}

Categories

Resources