I'm trying to implement an autorotation method for a SpriteAnimation class (trying to port SpriteAnimation).
Everything is working, the sprite draws correctly (it uses the correct animations) it just doesn't rotate properly if at all, normally it seems to point to the origin (0, 0) only after its target matches its position while I want its rotation to update as it moves, i've tried both degrees and radians, neither of which work. It should be rotating to match the current direction it's going. I've been struggling with this for about a week now, but still have not gotten the desired result.
Relevant code:
From SpriteAnimation
// The x position of the sprite's upper left corner pixel.
public int getX() { return (int)position.x; }
public void setX(int value)
{
prevPosition.x = position.x;
position.x = value;
updateRotation();
}
// The y position of the sprite's upper left corner pixel.
public int getY() { return (int)position.y; }
public void setY(int value)
{
prevPosition.y = position.y;
position.y = value;
updateRotation();
}
void updateRotation()
{
if (rotateByPosition)
{
Vector2 rotationVector = new Vector2(position.x - prevPosition.x, position.y - prevPosition.y);
rotationRad = rotationVector.angle();
rotationDeg = rotationRad * MathUtils.radiansToDegrees;
}
}
public void MoveBy(int x, int y)
{
prevPosition = new Vector2(position);
position.x += x;
position.y += y;
updateRotation();
}
public void Update(float deltaTime)
{
// Don't do anything if the sprite is not animating
if (animating)
{
// If there is not a currently active animation
if (getCurrentFrameAnimation() == null)
{
// Make sure we have an animation associated with this sprite
if (animations.size() > 0)
{
// Set the active animation to the first animation
// associated with this sprite
String[] sKeys = new String[animations.size()];
int index = 0;
for (Entry<String, FrameAnimation> mapEntry : animations.entrySet()) {
sKeys[index] = mapEntry.getKey();
index++;
}
setCurrentAnimation(sKeys[0]);
}
else
{
return;
}
}
// Run the Animation's update method
getCurrentFrameAnimation().Update(deltaTime);
// Check to see if there is a "follow-up" animation named for this animation
if (getCurrentFrameAnimation().getNextAnimation() != null && !getCurrentFrameAnimation().getNextAnimation().isEmpty())
{
// If there is, see if the currently playing animation has
// completed a full animation loop
if (getCurrentFrameAnimation().getPlayCount() > 0)
{
// If it has, set up the next animation
setCurrentAnimation(getCurrentFrameAnimation().getNextAnimation());
}
}
}
}
public void Draw(SpriteBatch spriteBatch, int xOffset, int yOffset)
{
updateRotation(); // Calling this while testing to make sure that it is being called
spriteBatch.draw(getCurrentTextureRegion(), getPosition().x + xOffset - center.x, getPosition().y + yOffset - center.y, center.x, center.y, getCurrentFrameAnimation().getFrameWidth(), getCurrentFrameAnimation().getFrameHeight(), 1.0f, 1.0f, rotationRad);
}
TestScreen class
package com.darkstudio.darkisle.screens;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Screen;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.math.Vector3;
import com.darkstudio.darkisle.DarkIsle;
import com.delib.engine.sprite.MobileSprite;
/**
* Created by DarkEnder on 2017/06/27.
*/
public class TestScreen implements Screen {
final DarkIsle game;
OrthographicCamera camera;
Texture tankTexture;
MobileSprite mouseTank;
public TestScreen(final DarkIsle game)
{
this.game = game;
camera = new OrthographicCamera();
configureCamera();
tankTexture = new Texture(Gdx.files.internal("MulticolorTanks.png"));
mouseTank = new MobileSprite(tankTexture, 32, 32);
mouseTank.getSprite().AddAnimation("red", 0, 32, 32, 32, 8, 0.1f);
mouseTank.getSprite().AddAnimation("purple", 0, 128, 32, 32, 8, 0.1f, "red");
mouseTank.getSprite().AddAnimation("yellow", 0, 64, 32, 32, 8, 0.1f);
mouseTank.getSprite().setAutoRotate(true);
mouseTank.setPosition(new Vector2(100, 100));
mouseTank.setTarget(new Vector2(mouseTank.getPosition()));
mouseTank.setIsPathing(true);
mouseTank.setEndPathAnimation("yellow");
mouseTank.setLoopPath(false);
mouseTank.setSpeed(2);
}
@Override
public void show() {
}
@Override
public void render(float delta) {
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);
update();
game.batch.begin();
mouseTank.draw(game.batch);
game.batch.end();
}
@Override
public void resize(int width, int height) {
}
@Override
public void pause() {
}
@Override
public void resume() {
}
@Override
public void hide() {
}
@Override
public void dispose() {
}
private void configureCamera()
{
Vector3 camPos = new Vector3(camera.position);
float size = 800;
float cameraWidth = 0;
float cameraHeight = 0;
if (Gdx.graphics.getHeight() < Gdx.graphics.getWidth())
{
cameraWidth = size;
cameraHeight = size * Gdx.graphics.getHeight() / Gdx.graphics.getWidth();
}
else
{
cameraWidth = size * Gdx.graphics.getWidth() / Gdx.graphics.getHeight();
cameraHeight = size;
}
camera.setToOrtho(true, cameraWidth, cameraHeight);
camera.position.set(camPos.x + camera.viewportWidth / 2f, camPos.y + camera.viewportHeight / 2f, 0);
}
private void update() {
int xTouch = Gdx.input.getX(0);
int yTouch = Gdx.input.getY(0);
mouseTank.setTarget(xTouch, yTouch);
mouseTank.update(Gdx.graphics.getDeltaTime());
}
}
Edit: Just saying now that I've already tried using atan2 in MathUtils and it gave the same results.
The MobileSprite update method
public void update(float deltaTime)
{
if (active && movingTowardsTarget)
{
if ((target != null))
{
// Get a vector pointing from the current location of the sprite
// to the destination.
Vector2 Delta = new Vector2(target.x - sprite.getX(), target.y - sprite.getY());
if (Delta.len() > getSpeed())
{
Delta.nor();
Delta.scl(getSpeed());
getPosition().add(Delta);
}
else
{
if (target == sprite.getPosition())
{
if (pathing)
{
if (queuePath.size() > 0)
{
target = queuePath.remove();
if (loopPath)
{
queuePath.remove(target);
}
}
else
{
if (!(endPathAnimation == null))
{
if (!(getSprite().getCurrentAnimation() == endPathAnimation))
{
getSprite().setCurrentAnimation(endPathAnimation);
}
}
if (deactivateAtEndOfPath)
{
setIsActive(false);
}
if (hideAtEndOfPath)
{
setIsVisible(false);
}
}
}
}
else
{
sprite.setPosition(target);
}
}
}
}
if (active)
sprite.Update(deltaTime);
}
To get the radians angle:
void updateRotation()
{
if (rotateByPosition)
{
Vector2 rotationVector = new Vector2(position.x - prevPosition.x, position.y - prevPosition.y);
rotationRad = rotationVector.angleRad();
rotationDeg = rotationRad * MathUtils.radiansToDegrees;
}
}
and the draw method needs a degrees angle:
spriteBatch.draw(
getCurrentTextureRegion(),
getPosition().x + xOffset - center.x,
getPosition().y + yOffset - center.y,
center.x, center.y,
getCurrentFrameAnimation().getFrameWidth(), getCurrentFrameAnimation().getFrameHeight(),
1.0f, 1.0f,
rotationDeg
);
The screen mouse position is from the upper left corner the renderer needs from the bottom left corner:
int xTouch = Gdx.input.getX(0);
int yTouch = Gdx.graphics.getHeight() - Gdx.input.getY(0);