I'm trying to transfer my bitmap from using rectF translation to using matrix rotation and translation. I feel like I've tried every combination of pre,set,post for rotate and translate. This is a sample of my code
Matrix m;
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap(bitmap, m, null);
}
onTouchEvent(MotionEvent event)
{
m.setRotate(rotation, bitmap.getWidth()/2, bitmap.getHeight()/2); //rotation points to your finger
if (fingerx1 > bitmapX) {
xspeed = xspeed + accel;
}
if (fingerx1 < bitmapX) {
xspeed = xspeed - accel;
}
if (fingery1 < bitmapY) {
yspeed = yspeed - accel;
}
if (fingery1 > bitmapY) {
yspeed = yspeed + accel;
}
}
private Runnable runny = new Runnable() {
public void run() {
m.postTranslate(xspeed, yspeed);
handler.postDelay(this, 50);
}
};
Not the final code since I don't know which pre,set,post to use in these situations. I feel like this would be the solution though (setRotate, postTranslate).
With this combination, the bitmap looks like it tries to but can't translate.
When I've tried setTranslate(bitmapX + xspeed, bitmapY + yspeed) and postRotate it gives me the right translation but crazy rotation. Using both setRotate, setTranslate gives me crazy glitches. Adding pretranslate and prerotate to the mix just gave me a headache.
TL;DR: Ugh.
EDIT:
bitmapX = playerValues[Matrix.MTRANS_X];
bitmapY = playerValues[Matrix.MTRANS_Y];
After trying postRotate and postTranslate together, I see my bitmap rotate around it's center at first and then it moves out of the 0,0 position and continue to rotate around the 0,0 center.x,center.y position.
Basically I need to fix my rotate's center x,y to make them dynamically change with my bitmap's location.
Thanks pskink, I was having a very bad case of matricides.
Related
There is something I am missing inhere so I hope you can share some light on me.
I am drawing some text inside canvas. For this I have a class Word
public class Word {
private int x;
private int y;
private String text;
}
The app allows the user to rotate the text, and I handle the rotation withing onDraw
protected void onDraw(Canvas canvas) {
canvas.save(Canvas.MATRIX_SAVE_FLAG);
canvas.rotate(angle, centerX, centerY)
...
canvas.drawText(word.getText(), word.getX(), word.getY())
....
canvas.restore();
}
The problem I get is when the user drags the canvas and there is a rotation set. When angle=0 the movement is going as expected.
#Override
public boolean onTouchEvent(MotionEvent event) {
case MotionEvent.ACTION_DOWN:
initialX = (int) event.getX();
initialY = (int) event.getY();
break;
case MotionEvent.ACTION_MOVE:
int currentX = (int) event.getX();
int currentY = (int) event.getY();
int xMovement = currentX - initialX;
int yMovement = currentY - initialY;
dragWords(xMovement, yMovement);
.....
and on dragWords for each word I do:
private void dragText(int xMovement, int yMovement){
for (Word word : words) {
word.setX(word.getX() + xMovement);
word.setY(word.getY() + yMovement);
}
invalidate();
}
When rotation angle is 0, moving up/down/left/right makes the words move by the same distance. As angle gets bigger, the words start to move in different dirrections, for instance at 60, it is starting to go diagonally up, when 180 it only moves up/down and not left/right.
I think I need to calculate some sort of a difference based on angle and add it to xMovement/yMovement... but how should I do this ?
LE: Here is an image on how it behaves:
The blue lines is how the text is moving on drag while the orange is the finger dragging on the screen. When angle is 0 it works quite well, when angle increases, it starts to move diagonally on left/right, while when angle is even bigger, it only moves up and down and does not respond to left/right
If I understand correctly, the issue is that Canvas.rotate() does not only rotate the text direction, but rather the whole canvas. Therefore, the x-y coordinates of the words are also rotated from the specified pivot point.
In order to match the dragging movement, you can use a Matrix for this, more specifically the inverse matrix of the one you're using to rotate the canvas. It will be used to convert the x-y coordinates of the words to their original, pre-rotate locations.
For example, calculate this once, and update it whenever angle, centerX, or centerY changes.
// rotMatrix is the same operation applied on the canvas.
Matrix rotMatrix = new Matrix();
rotMatrix.postRotate(mAngle, centerX, centerY);
// Invert it to convert x, y to their transformed positions.
Matrix matrix = new Matrix();
rotMatrix.invert(matrix);
Then, when drawing each word:
int wordX = ...
int wordY = ...
String text = ...
float[] coords = new float[] { wordX, wordY };
matrix.mapPoints(coords);
canvas.drawText(text, coords[0], coords[1], paint);
In the ellipses part in the following code:
dragWords(xMovement, yMovement);
..... <<<--------------------- I hope you are updating initialX and initialY
initialX = currentX;
initialY = currentY;
Otherwise, your x and y values will not correspond correctly with the amount of distance moved during the touch gesture.
As user matiash indicated, you should use Matrix#mapPoints(float[]) to transform your x and y values. Declare and initialize a Matrix:
Matrix correctionMatrix;
// Your view's constructor
public MyView() {
....
correctionMatrix = new Matrix();
}
Here's how your onDraw(Canvas) should look like:
#Override
protected void onDraw(Canvas canvas) {
canvas.save(Canvas.MATRIX_SAVE_FLAG);
canvas.rotate(angle, centerX, centerY);
...
// Neutralize the rotation
correctionMatrix.setRotate(-angle, centerX, centerY);
// Initialize a float array that holds the original coordinates
float[] src = {word.getX(), word.getY()};
// Load transformed values into `src` array
correctionMatrix.mapPoints(src);
// `src[0]` and `src[1]` hold the transformed `X` and `Y` coordinates
canvas.drawText(word.text, src[0], src[1], somePaint);
....
canvas.restore();
}
This should give you the desired results - movement in the X and Y axis irrespective of canvas rotation.
You can obviously move the call to setRotate(float, float, float) to a better place. You only need to call it once after changing the angle value.
I am new to LWJGL but am slowly learning. I was wanting to make a square that rotated when you pressed the key. Like d rotates it 90 degrees as you can tell below, but when I use glRotatef(); it gives me an error and I don't know why. There error tells me I need to create a method for it, I know I don't need to though. Anything helps!
public class MainPlayer {
private Draw draw;
private int rotation;
private float WIDTH = (float) (Display.getWidth() * 0.1);
private float HEIGHT = (float) (WIDTH / 2);
private float x = Display.getWidth() / 2 - WIDTH / 2;
private float y = Display.getHeight() / 2 - HEIGHT / 2;
public MainPlayer(){
draw = new Draw(1.0f, 1.0f, 1.0f, WIDTH, HEIGHT);
}
public void update(){
}
public void render(){
glTranslatef(x, y, 0);
glRotatef(rotation,0,0,1);
draw.render();
}
public void getInput(){
if(Keyboard.isKeyDown(Keyboard.KEY_W)){
rotation = 0;
}
if(Keyboard.isKeyDown(Keyboard.KEY_S)){
rotation = 180;
}
if(Keyboard.isKeyDown(Keyboard.KEY_A)){
rotation = 270;
}
if(Keyboard.isKeyDown(Keyboard.KEY_D)){
rotation = 90;
}
}
}
You create an int rotation and I assume your render() loops the whole time, and you only set rotation in getInput().
So I am assuming that you should declare it as int rotation = 0.
glRotatef() is a OpenGL call to rotate objects, just like glTranslatef() moves them. glRotatef() does it in the same way.
glRotatef(AngleOfRotationf, 0, 1, 0) would rotate it horisontally, like in this video i just made: http://www.youtube.com/watch?v=SHsssrj9qr8& uses that line to rotate a ship.
Also in that video i demonstrated moving it with glTranslatef().
To use it you must use GL11.glRotatef(), or import static org.lwjgl.opengl.GL11.*;
That probably means that you haven't statically imported glRotatef
Either use
GL11.glRotatef(rotation, 0, 0, 1);
or import it at the beginning of your program with
import static org.lwjgl.opengl.GL11.glRotatef
I am making a game in Libgdx, in which I want to fall a ball from up word to down word and move background towards up word and update camera according to it. My code is given below...
public WorldRenderer(SpriteBatch spriteBatch, World world){
this.world = world;
this.camera = new OrthographicCamera(FRUSTUM_WIDTH, FRUSTUM_HEIGHT);
this.camera.position.set(FRUSTUM_WIDTH/2, FRUSTUM_HEIGHT/2, 0);
this.spriteBatch = spriteBatch;
positionBGY1 = 0;
positionBGY2 = 0;
}
public void render(World world, float deltaTime){
if(world.ball.position.y > - camera.position.y){
camera.position.y = world.ball.position.y;
}
if(camera.position.y<0)
camera.position.y=world.ball.position.y;
camera.update();
spriteBatch.setProjectionMatrix(camera.combined);
renderBackground();
renderObjects(world, deltaTime);
}
private void calculateBGPosition(){
positionBGY2 = positionBGY1 + (int)FRUSTUM_HEIGHT;
if(camera.position.y >= positionBGY2){
positionBGY1 = positionBGY2;
}
}
private void renderBackground() {
spriteBatch.disableBlending();
spriteBatch.begin();
spriteBatch.draw(Assets.gamebackgroundRegion, camera.position.x - FRUSTUM_WIDTH / 2, positionBGY1 - FRUSTUM_HEIGHT / 2, FRUSTUM_WIDTH, 1.5f * FRUSTUM_HEIGHT);
spriteBatch.draw(Assets.gamebackgroundRegion2, camera.position.x - FRUSTUM_WIDTH / 2, positionBGY2 - FRUSTUM_HEIGHT / 2, FRUSTUM_WIDTH, 1.5f * FRUSTUM_HEIGHT);
calculateBGPosition();
spriteBatch.end();
}
Problem with my code is background screen is not moving and camera is not updated with ball movement and ball fall down out of screen.
Well, first thing you need to do is change one of the bg variables, so in render somehwere do something like:
float speed = 1;
positionBGY1+=deltaTime*speed;
positionBGY2+=deltaTime*speed;
As far as the camera moving with the ball I would just do this:
camera.position.y = Math.min(ball.y,0);
And I would in turn omit all of this:
if(world.ball.position.y > - camera.position.y){
camera.position.y = world.ball.position.y;
}
if(camera.position.y<0)
camera.position.y=world.ball.position.y;
Best way to achieve this is using ParrallaxLayer and ParrallaxBackground classes
it optimised the background operation so that u dont have to worry about the performance
I am trying to rotate a custom shape around its center, but can not get the result as expected.
what i want is
*shape should be rotated around its center without moving itself.*
what my solution is currently doing is
rotating a whole shape around its center , by every rotation its changing its position.
I have multiple shapes so i have created a class to encapsulate a shape with its transform in following class
public abstract class Shoe implements Shape, ShoeShape {
// variable declaration
/**
*
*/
public Shoe() {
position = new Point();
lastPosition = new Point();
}
public void draw(Graphics2D g2, AffineTransform transform, boolean firstTime) {
AffineTransform af = firstTime ? getInitTransform()
: getCompositeTransform();
if (af != null) {
Shape s = af.createTransformedShape(this);
if (getFillColor() != null) {
g2.setColor(getFillColor());
g2.fill(s);
} else {
g2.draw(s);
}
}
}
}
public AffineTransform getCompositeTransform() {
AffineTransform af = new AffineTransform();
af.setToIdentity();
af.translate(position.getX(), position.getY());
Point2D centerP = calculateShapeCenter();
af.rotate(orientation, centerP.getX(), centerP.getY());
return af;
}
public void onMouseDrag(MouseEvent me, Rectangle2D canvasBoundary,
int selectionOperation) {
// shape operation can be either resize , rotate , translate ,
switch (selectionOperation) {
case MmgShoeViewer.SHAPE_OPERATION_MOVE:
// MOVEMENT
break;
case MmgShoeViewer.SHAPE_OPERATION_ROTATE:
Point2D origin = calculateShapeCenter();
Point2D.Double starting = new Point2D.Double(me.getX(), me.getY());
currentAngle = RotationHelper.getAngle(origin, starting);
rotationAngle = currentAngle - startingAngle;
rotate(rotationAngle);
break;
case MmgShoeViewer.SHAPE_OPERATION_RESIZE:
break;
default:
System.out.println(" invalid select operation");
}
}
public void onMousePress(MouseEvent me, Rectangle2D canvasBoundary,
int selectionOperation) {
// shape operation can be either resize , rotate , translate ,
switch (selectionOperation) {
case MmgShoeViewer.SHAPE_OPERATION_MOVE:
break;
case MmgShoeViewer.SHAPE_OPERATION_ROTATE:
Point2D origin = calculateShapeCenter();
Point2D.Double starting = new Point2D.Double(me.getX(), me.getY());
startingAngle = RotationHelper.getAngle(origin, starting);
setShapeOperation(selectionOperation);
break;
case MmgShoeViewer.SHAPE_OPERATION_RESIZE:
break;
default:
System.out.println(" invalid select operation");
}
}
public void onMouseRelease(MouseEvent me, Rectangle2D canvasBoundary,
int selectionOperation) {
// shape operation can be either resize , rotate , translate ,
switch (selectionOperation) {
case MmgShoeViewer.SHAPE_OPERATION_MOVE:
break;
case MmgShoeViewer.SHAPE_OPERATION_ROTATE:
// FIXME rotation angle computation
setShapeOperation(-1);
break;
case MmgShoeViewer.SHAPE_OPERATION_RESIZE:
break;
default:
System.out.println(" invalid select operation");
}
}
public void rotate(double angle) {
orientation = (float) angle;
}
public void translate(double deltaX, double deltaY) {
position.setLocation(deltaX, deltaY);
lastPosition.setLocation(deltaX, deltaY);
}
// another getter and setter
I am calculating angle of rotation using following method
public static double getAngle(Point2D origin, Point2D other) {
double dy = other.getY() - origin.getY();
double dx = other.getX() - origin.getX();
double angle;
if (dx == 0) {// special case
angle = dy >= 0 ? Math.PI / 2 : -Math.PI / 2;
} else {
angle = Math.atan(dy / dx);
if (dx < 0) // hemisphere correction
angle += Math.PI;
}
// all between 0 and 2PI
if (angle < 0) // between -PI/2 and 0
angle += 2 * Math.PI;
return angle;
}
in mouse press event of the canvas mouse listener
selectedShape.onMousePress(me, canvasBoundary, shoeViewer
.getShapeOperation());
i am just calling selected shape's onMousePress method
and in my mouse drag method of the canvas mouse listener , i am just calling the selected shape's onMouseDrag method which updates the rotation angle as you can see from the very first class
selectedShape.onMouseDrag(me, canvasBoundary, shoeViewer
.getShapeOperation());
and you can see the draw method of the individual shape , to draw the shape according to current transform , i am calling from paintComponent like
Iterator<Shoe> shoeIter = shoeShapeMap.values().iterator();
while (shoeIter.hasNext()) {
Shoe shoe = shoeIter.next();
shoe.draw(g2, firstTime);
}
where shoeShapeMap contains all of the custom shapes currently on the canvas.
is i am doing mistake in calculating angle or determining anchor point ? my current solution rotates shape 360 degree by checking all the conditions[90 degree etc.] as you can see in the above mentioned method.
i want the shape should be rotated around its center without resizing its positions ?
in the word it is difficult to explain , so please suggest me any better way to show here what i want to accomplish ?
i think i have mentioned all the things related to this issue. if you have any doubts please feel free to ask me.
i found 2 related posts here but i could not find much information from them.
I think that the solution may be to (either/and):
invert the order of operations on your AffineTransform, put translate after rotate
use -x and -y for your translation values
In order to build a tic-tac-toe game for testing, I have following routine. But problem is that I am getting too many events for just one touch. I suspect isTouched() returns all of down, up, and move. Is there any way to just get up event?
UPDATE: Resolved the issue by employing justTouched() instead.
#Override
public void render() {
// we update the game state so things move.
updateGame();
// First we clear the screen
GL10 gl = Gdx.graphics.getGL10();
gl.glViewport(0, 0, width, height);
gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
// Next we update the camera and set the camera matrix
camera.update();
camera.apply(Gdx.gl10);
...
}
private void updateGame() {
// the delta time so we can do frame independant time based movement
float deltaTime = Gdx.graphics.getDeltaTime();
// Has the user touched the screen? then position the paddle
if (Gdx.input.isTouched() && !isProcess) {
// get the touch coordinates and translate them
// to the game coordinate system.
isProcess=true;
int width = Gdx.graphics.getWidth();
int height = Gdx.graphics.getHeight();
int offx=-width/2;
int offy=-height/2;
float x = Gdx.input.getX();
float y = Gdx.input.getY();
float touchX = 480 * (x
/ (float) width - 0.5f);
float touchY = 320 * (0.5f - y
/ (float) height);
for(int i=0;i<3;i++) {
for(int j=0;j<3;j++)
{
if(touchX >= offx+i*width/3 && touchX < offx+(i+1)*width/3 &&
touchY >= offy+j*height/3 && touchY < offy+(j+1)*height/3)
{
if(isCurrentO)
data[i][j]=CellStatus.O;
else
data[i][j]=CellStatus.X;
isCurrentO=!isCurrentO;
break;
}
}
}
isProcess=false;
}
}
An alternative to using justTouched is to implement the InputProcessor interface, as it has a touchUp(x,y,pointer,button) which gives you greater control over the input. There are several classes that implement this or you can have your class implement it.
You can create a board for example (with hash map) and each object in your game wants to be clickable add itself to that board if an object was touched and was in board it will catch the event. If not it will not catch the event. So easy! :)