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);
}
Related
I am rotating a shape in Java using g2D.rotate(Math.toRadians(rotationDegrees), x, y) where the x and the y are the axis that I want the shape to be rotating along. When I try to move the shape with a key listener, the shape still moves up, down, left, and right instead of moving according to the angle that it is facing. Is there any way that I can rotate a shape and have it move in the same direction that it is facing?
You need to do a little bit of trigonometry before you change your x and y values. Maintain a variable angle which stores the total amount that your shape has rotated from its initial position.
Now suppose you've got variables xChange and yChange that represent the desired change in x and y, but in the reference frame of the shape, not of the frame. Then instead of writing x += xChange; you'd write
x += (xChange * Math.cos(angle) + yChange * Math.sin(angle));
and instead of writing y += yChange; you'd write
y += (yChange * Math.cos(angle) - xChange * Math.sin(angle));
This assumes that angle is stored in radians, not degrees. You may also need to experiment with the + and - signs, depending on whether you're measuring y from top to bottom or bottom to top.
I have a Rectangle object that is placed on the screen and rendered using paintComponent.
I also have a rotation variable that determines the rotation of the object (using right and left keys to rotate) and repaints the object on the screen using Affine Transform.
In the keyPressed method, I have this which allows me to shoot bullets:
else if(key == KeyEvent.VK_SPACE) {
Bullet b = new Bullet(handler, player.x + 10, player.y - 10, ID.Bullet);
handler.addObject(b);
b.setDY(-3*Math.cos(player.rotation));
b.setDX(3*Math.sin(player.rotation));
}
If you see where I create a new bullet, in the second line, the second and third arguments in the new Bullet() are what determines where the bullets are created. Currently they just shoot from the same position on the rectangle regardless of the rotation.
I have failed at allowing the player to shoot a bullet from the direction they are facing, so if anyone has any suggestions that would be very helpful.
So basically you are trying to find the offset Vector from the player's position.
Let's assume, that you have the offset Vector, when you haven't rotated the object.
Then your task would be to rotate the given Vector by the rotation variable.
To do this we first need to look into geometry a bit. Let's think of a 2d-Vector as a triangle, that we can split into it's x and y components. We know, that the rotated Vector should have the same hypothenuse length as the original offset or in other terms the same magnitude.
This means that:
x₀² + y₀² = x₁² + y₁²
Since:
x² + y² = Vector-Magnitude
Your rotation variable keeps track of the inner angle of that "triangle vector" and hence we can describe the lengths as:
x = sin(rotation) * Vector Magnitude
y = cos(rotation) * Vector Magnitude
Now the only thing left to do is evalute those values. To do that, let us jump into the code, shall we?
float magnitude = offset.x*offset.x + offset.y*offset.y;
float x = Math.sin(rotation) * magnitude;
float y = Math.cos(rotation) * magnitude;
Bullet b = new Bullet(handler, player.x + x, player.y + y, ID.Bullet);
possible Errors and fixes
Make sure that you have the correct angle-format. The Java-Math class uses radiens for trigonometric methods.
There could be a constant rotation deviation, depending on what exactly you want your result to look like. This is a rather easy fix, as you just have to add this constant to the rotation variable. In unlikely instances you maybe also need to negate the rotation variable simply by using -rotation in the places I used rotation in my code.
If you know the rotation angle and the width of the rectangle then use this information to rotate the start position of the bullet too. For example to let the start position at the right side:
x = rectMiddleX + width/2 * cos(angle)
y = rectMiddleY + width/2 * sin(angle)
If angle is 0 it will start at the right side
else if(key == KeyEvent.VK_SPACE) {
Bullet b = new Bullet(handler, player.x + width/2 * cos(player.rotation), player.y - width/2 * sin(player.rotation), ID.Bullet);
//if player.x/y is in corner, add width/2 / height/2
handler.addObject(b);
b.setDY(-3*Math.cos(player.rotation));
b.setDX(3*Math.sin(player.rotation));
}
This is a bit more of a conceptual question. Lets say you have an array of squares, each 40 pixels by 40 pixels. Lets also say you clicked inside one of them. How would you get another object to appear in the center of the box that was clicked instead of where exactly the mouse was clicked?
Would you use an offset of some kind? I am really struggling to understand how to determine the center of the square in relation to the mouse clicked.
Assuming your could calculate the position of the box which was clicked (mousex / 40 for column and mousey / 40 for row), then you would simply need to calculate the center position for the object...
Normally something like...
int x = (parentWidth - childWidth) / 2;
int y = (parentWidth - childWidth) / 2;
would give you the center position for the child within the parent. You would then simply apply the offset of the box in question...
int x = xOffset + ((parentWidth - childWidth) / 2);
int y = yOffset + ((parentWidth - childWidth) / 2);
i'm fairly new to libgdx and java but am learning everything I can! I'm working on an orthogonal 2d tiled game. Basically, i've got my character walking around and the camera following him. I am trying to set it up so the camera stops when the character is near the edge of the map so no black space is seen. The camera should stop at the edge and the player should continue moving.
This is my code for moving the camera. It's a bit messy now as i've been trying everything. The map is 30x30.
if (mainPlayer.getPosition().x >= 15 && mainPlayer.getPosition().x <= 30) {
camera.position.x = mainPlayer.getPosition().x;
}
camera.position.y = mainPlayer.getPosition().y;
camera.update();
camera.apply(gl);
This is inside my render method. I've only messed with the x part, so ignore the y for now.
EDIT I think I need to reword that. I know how to make it stop..It works using my code, but I can't figure out how to determine where to stop it. I'm using 15 in my code above which is half the map..which of course doesn't work.
EDIT I think I need to reword that. I know how to make it stop..It
works using my code, but I can't figure out how to determine where to
stop it. I'm using 15 in my code above which is half the map..which of
course doesn't work.
The minimum x would need to be:
map.position.x + camera.viewportWidth/2;
And the maximum x would need to be:
map.position.x+mapwidth-camera.viewportWidth/2;
Assuming the map position is in (0,0) you can just ignore the map.position.x part.
You need to fix the bounds of the Camera rectangle to inside the world rectangle. A function such as below would help:
public void fixBounds() {
float scaledViewportWidthHalfExtent = viewportWidth * zoom * 0.5f;
float scaledViewportHeightHalfExtent = viewportHeight * zoom * 0.5f;
// Horizontal
if (position.x < scaledViewportWidthHalfExtent)
position.x = scaledViewportWidthHalfExtent;
else if (position.x > xmax - scaledViewportWidthHalfExtent)
position.x = xmax - scaledViewportWidthHalfExtent;
// Vertical
if (position.y < scaledViewportHeightHalfExtent)
position.y = scaledViewportHeightHalfExtent;
else if (position.y > ymax - scaledViewportHeightHalfExtent)
position.y = ymax - scaledViewportHeightHalfExtent;
}
I have a spaceship, that spaceship moves though space 360 Degrees.
The spaceship needs thrust animation in 2d. The trust animation needs to be at the bottom middle line of the spaceship. I have the following variables.
_Rotation
_Spaceship.width
_Spaceship.height
_Spaceship.Position(x,y)
I've uploaded an image of my problem too in-case people don't understand my bad explanation:
http://imgur.com/Lgchc
Both animation render like so:
this._itemAnimation.render(this.getPosition(), canvas, this._degrees);
this._thrustAnimation.render(this.thrustPosition(), canvas, this._degrees);
I have tried so far and failed:
_thurstPosition.set(((int)_object_x + Math.cos(_degrees) * _itemAnimation.getWidth() / 2) ,
((int)_object_y + Math.sin(_degrees) * _itemAnimation.getWidth() / 2));
I'm fail, somebody help me.
--- UPDATE ---
I've updated the code so it's better understood:
int SpaceshipCenterX = getPosition().x + (_SpaceshipAnimation.getWidth() / 2) - (_thrustAnimation.getWidth() / 2);
int SpaceshipCenterY = getPosition().y + ((_SpaceshipAnimation.getHeight() / 2) - (_thrustAnimation.getHeight() / 2));
double OffSetCos = Math.cos(_spaceshipDegrees);
double OffSetSin = Math.sin(_spaceshipDegrees);
_thurstPosition.set
(
(int)(SpaceshipCenterX + (SpaceshipAnimation.getWidth() / 2) * OffSetCos)
,
(int)(SpaceshipCenterY + (SpaceshipAnimation.getHeight() / 2) * OffSetSin)
);
I still can't get it too work. It's going around the spaceship but very fast and flashing everywhere.
--- UPDATE 2 ---
This is almost working but it's going too far out:
int xOffset = -1 * (_itemAnimation.getWidth() / 2);
double DegreeToRadien = Math.toRadians(_degrees);
_thurstPosition.set
(
(int)(((xOffset) * Math.cos(DegreeToRadien)) + getPosition().x),
(int)(((xOffset) * Math.sin(DegreeToRadien)) + getPosition().y)
);
Assuming that you are using this coordinate/angle system:
90 (pi/2) - Up
^
|
Left - 180 (pi) <----|----> 0 - Right
|
v
Down - 270 (3pi/2)
And that your spaceship is going to the right at 0 degrees
>[ } 0
Then for any direction you need to translate the thrust relatively from the centre of the spaceship, let's say we translate in the x direction by
offset = -1 * width/2;
Then rotate it by the angle of the spaceship and finally translate it by the position of the spaceship.
To compute this transformation, write out the 3 transformations as matrices in reverse order and multiply them out, transforming a point starting at (0,0)
[1 0 spaceshipX] [cos(dir) -sin(dir) 0] [1 0 -width/2] [0]
[0 1 spaceshipY] [sin(dir) cos(dir) 0] [0 1 0 ] [0]
[0 0 1 ] [ 0 0 1] [0 0 1 ] [1]
So that would give you the position of the thrust as
thrustX = (-width/2)cos(dir) + spaceshipX
thrustY = (-width/2)sin(dir) + spaceshipY
So I suppose you just missed the fact you need to subtract width/2, not add it.
I've edited this with a correct and more readable syntax. Using underscores everywhere really hurts readability. I am assuming you have a spaceship class, and the spaceship has a width, height, x position, y position and rotation. You have a thruster class which also has a width, height, x position, y position and rotation. (They could inherit from a Sprite abstract class). To set the position of a thruster object we call thruster.setPosition(x,y);
int xOffset = -1 * (spaceship.width / 2);
thruster.setPosition(
(int)(((xOffset) * Math.cos(spaceship.rotation)) + spaceship.x),
(int)(((xOffset) * Math.sin(spaceship.rotation)) + spaceship.y)
);
Hopefully this makes it obvious to you which values you need to be setting where. I can't decipher your code without seeing more of it, where these variables are declared and what they actually mean.
Update
Just to conclude, as I think you may have discovered. Math.cos and Math.sin require angles to be in Radians, not degrees. The solution I have given here is correct, and I have shown how you compute the position of any relatively positioned object by performing the matrix calculation. You just have to remember that spaceship.rotation must be in radians or you must translate it to radians from degrees before passing it to Math.cos() or Math.sin().
int xOffset = -1 * (spaceship.width / 2);
double radians = Math.toRadians(spaceship.rotation);
thruster.setPosition(
(int)(((xOffset) * Math.cos(radians)) + spaceship.x),
(int)(((xOffset) * Math.sin(radians)) + spaceship.y)
);
Your code looks close to me. You need to be careful about what _degrees means. If _degrees is the direction in which the rocket points (counterclockwise from the +x axis), then you'll need to put some minus signs in there because the thrust is at the back of the rocket, not the front. Also, I would think the thrust is on the height end of the rocket, so use getLength instead of getWidth. You may need to add some extra for the height of the thrust animation as well (depending on where its anchor is). So something like
_thurstPosition.set(((int)_object_x - Math.cos(_degrees) * (_itemAnimation.getHeight() / 2 + _thrustAnimation.getHeight() / 2)) ,
((int)_object_y - Math.sin(_degrees) * (_itemAnimation.getHeight() / 2 + _thrustAnimation.getHeight() / 2)));
You'll also need to set thrust orientation if it isn't a circle.
(I'm assuming _degrees == _Rotation)
double DegreeToRadien = Math.toRadians(_degrees);
int ObjectXCenter = (int) (_object_x + ((_itemAnimation.getWidth() / 2)) - _thrustAnimation.getWidth() / 2);
int ObjectYCenter = (int) (_object_y + ((_itemAnimation.getHeight() / 2)) - _thrustAnimation.getHeight() / 2);
int xOffset = -1 * (_itemAnimation.getWidth() / 2);
_thurstPosition.set
(
(int)(((xOffset) * Math.cos(DegreeToRadien)) + ObjectXCenter),
(int)(((xOffset) * Math.sin(DegreeToRadien)) + ObjectYCenter)
);