I currently have an animation which runs as soon as I launch the app. Now I want to select another animation which plays in response to user input. e.g If I press left, the animation for walking left replaces the current sprite's animation. I've tried adding a new textureAtlas in the keyDown function but that doesn't work.
In other words I want the app to display the idle animation of the sprite when no Input is present. Then If I hit a button (left for example) It should change to walking animation.
package com.mygdx.game;
import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.InputProcessor;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.g2d.Animation;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
public class MyGdxGame extends ApplicationAdapter implements InputProcessor {
private SpriteBatch batch;
private TextureAtlas textureAtlas;
private Animation animation;
private float elapsedTime = 0;
#Override
public void create() {
batch = new SpriteBatch();
textureAtlas = new TextureAtlas(Gdx.files.internal("spritesheet.atlas"));
animation = new Animation(1/7f, textureAtlas.getRegions());
}
#Override
public void dispose() {
batch.dispose();
textureAtlas.dispose();
}
#Override
public void render() {
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.begin();
//sprite.draw(batch);
elapsedTime += Gdx.graphics.getDeltaTime();
batch.draw(animation.getKeyFrame(elapsedTime, true), 0, 0);
batch.end();
}
#Override
public void resize(int width, int height) {
}
#Override
public void pause() {
}
#Override
public void resume() {
}
#Override
public boolean keyDown(int keycode) {
if (keycode == Input.Keys.LEFT) {
textureAtlas = new TextureAtlas(Gdx.files.internal("spritesheet2.atlas"));
}
return true;
}
#Override
public boolean keyUp(int keycode) {
return false;
}
#Override
public boolean keyTyped(char character) {
return false;
}
#Override
public boolean touchDown(int screenX, int screenY, int pointer, int button) {
return false;
}
#Override
public boolean touchUp(int screenX, int screenY, int pointer, int button) {
return false;
}
#Override
public boolean touchDragged(int screenX, int screenY, int pointer) {
return false;
}
#Override
public boolean mouseMoved(int screenX, int screenY) {
return false;
}
#Override
public boolean scrolled(int amount) {
return false;
}
}
Depending on how many different animations you have (maybe it is just idle, walk_right, walk_left, maybe a couple of others...), you could just load all the textureAtlases and create various animations - like you did in your create() method
//simple example ...
animationIdle = new Animation(1/7f, textureAtlas1.getRegions());
animationLeft = new Animation(1/7f, textureAtlas2.getRegions());
//etc
Then have code somewhere (not in the render method), determine what the "current animation" should be. Using your code as an example:
#Override
public boolean keyDown(int keycode) {
if (keycode == Input.Keys.LEFT) {
// Maybe check to make sure the currentAnimation is not already the
// left animation, if it isn't, swap it:
currentAnimation = animationLeft;
//set elapsedTime to 0 to "Restart the new animation" at its beginning
elapsedTime = 0;
}
Then your render() method just draws the current animation, and the elapsedTime was reset as needed(if you changed the animation you wanted):
batch.draw(currentAnimation.getKeyFrame(elapsedTime, true), 0, 0);
//increment elapsedTime after drawing animation at least once??
elapsedTime += Gdx.graphics.getDeltaTime();
The above code is rough, not necessarily syntax free or structured the best, but gives you an idea of what might work.
Related
I wrote a little STT-functionality, with a floating button that is pulsating after being clicked on to notify that the app is listening. This works quite well so far with the one annoying behavior that my floating button does not return to its original size in some cases.
The animation increases and decreases the size of the button, and I guess it gets stuck in the increased state, hence the randomness of this behavior. I just can't figure out how to catch that and set the size to the original one.
Action Listener of my Button:
private View.OnTouchListener setVoiceButtonOnClick()
{
return new View.OnTouchListener()
{
#Override
public boolean onTouch(View v, MotionEvent event)
{
if (event.getAction() == MotionEvent.ACTION_DOWN)
{
if(!voiceButton.isInitialized())
voiceButton.initAnimationValues();
voiceButton.setPressed(true);
listen();
}
return true;
}
};
}
My Button extends FloatingActionButton, and does the following:
public class FloatingVoiceButton extends FloatingActionButton
{
public static final float DEFAULT_ANIMATION_FACTOR = 1.2f;
private boolean isInitialized = false;
private int originalHeight;
private int originalWidth;
private boolean isAnimationRunning;
private ObjectAnimator animator;
public FloatingVoiceButton(Context context)
{
super(context);
}
public void initAnimationValues()
{
isInitialized = true;
isAnimationRunning = false;
originalHeight = getMeasuredHeight();
originalWidth = getMeasuredWidth();
animator = ObjectAnimator.ofPropertyValuesHolder(
this,
PropertyValuesHolder.ofFloat("scaleX", DEFAULT_ANIMATION_FACTOR),
PropertyValuesHolder.ofFloat("scaleY", DEFAULT_ANIMATION_FACTOR));
animator.setDuration(200);
animator.setRepeatCount(ObjectAnimator.INFINITE);
animator.setRepeatMode(ObjectAnimator.REVERSE);
}
public boolean isInitialized()
{
return isInitialized;
}
public void resetButtonSize()
{
setMeasuredDimension(originalWidth, originalHeight);
}
public boolean isAnimationRunning()
{
return isAnimationRunning;
}
public void animate(boolean doAnimation)
{
isAnimationRunning = doAnimation;
if(doAnimation)
animator.start();
else
{
animator.end();
setPressed(false);
resetButtonSize();
//destroyDrawingCache(); tried these without success
//postInvalidate();
}
}
}
Finally I am controlling the button the start and end of the animation with my RecognitionListener:
public class InputVoiceRecognitionListener implements RecognitionListener
{
private EditText targetEditText;
private String originalContent;
private final String DELIMITER = "\n\n";
private FloatingVoiceButton button;
public InputVoiceRecognitionListener(EditText editText, FloatingVoiceButton button)
{
targetEditText = editText;
originalContent = editText.getText().toString();
this.button = button;
}
#Override
public void onReadyForSpeech(Bundle params)
{
button.animate(true);
}
#Override
public void onBeginningOfSpeech()
{
originalContent = targetEditText.getText().toString();
}
#Override
public void onRmsChanged(float rmsdB)
{}
#Override
public void onBufferReceived(byte[] buffer)
{}
#Override
public void onEndOfSpeech()
{
if(button.isAnimationRunning())
button.animate(false);
}
#Override
public void onError(int error)
{
if(button.isAnimationRunning())
button.animate(false);
}
#Override
public void onResults(Bundle results)
{
setRecognizedText(results.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION));
}
#Override
public void onPartialResults(Bundle partialResults)
{
setRecognizedText(partialResults.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION));
}
#Override
public void onEvent(int eventType, Bundle params)
{
}
private void setRecognizedText(ArrayList<String> matches)
{
String result = "";
if(matches != null)
result = matches.get(0);
if((originalContent.trim()).length() > 0)
{
if(!originalContent.endsWith("\n\n"))
result = originalContent + DELIMITER + result;
else result = originalContent + result;
}
targetEditText.setText(result);
targetEditText.setSelection(result.length());
}
}
EDIT
This did it for me:
resettingAnimator = ObjectAnimator.ofPropertyValuesHolder(
this,
PropertyValuesHolder.ofFloat("scaleX", 1.0f),
PropertyValuesHolder.ofFloat("scaleY", 1.0f));
resettingAnimator.setDuration(0);
resettingAnimator.setRepeatCount(1);
and calling resettingAnimator.start(); when I finish my main animation.
Simple solution to this problem is that you define another animation after stopping your repeating one.
I just can't figure out how to catch that and set the size to the original one.
You, that is View, does know what is the "original" size, its the size of the scale factor 1f. So after stopping repeating animation just make another animations to set scale to 1f
PropertyValuesHolder.ofFloat("scaleX", 1f)
PropertyValuesHolder.ofFloat("scaleY", 1f))
This animation will run always, but will not be visible if your button is already at "normal" size.
With this in mind I would recommend that you use some other flag than isAnimationRunning(), either by some state (ex. selected) of your Fab, or some manually set arbitrary boolean.
I'm using Scene2D and have 2 actors. When MyActor1 is touched, it should fire an event which should be handled by MyActor2. I've tried several different variants on how to fire and even from MyActor1, but it is still not being handled by MyActor2. The full code is below.
Game class which just forwards to a Screen
public class MyGdxGame extends Game {
#Override
public void create() {
setScreen(new MyGdxScreen());
}
}
Screen class (empty methods ommited)
public class MyGdxScreen implements Screen {
private Stage stage;
#Override
public void show() {
stage = new Stage(new ScreenViewport());
stage.addActor(new MyActor1());
stage.addActor(new MyActor2());
Gdx.input.setInputProcessor(stage);
}
#Override
public void render(float delta) {
Gdx.gl.glClearColor(1, 1, 1, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
stage.act(delta);
stage.draw();
}
#Override
public void dispose() {
stage.dispose();
}
}
First Actor (fires custom event when touched)
public class MyActor1 extends Actor {
private Texture image;
public MyActor1() {
this.image = new Texture("badlogic.jpg");
setPosition(0, 0);
setBounds(0, 0, image.getWidth(), image.getHeight());
setTouchable(Touchable.enabled);
addListener(new InputListener() {
#Override
public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
Gdx.app.log("touched", "First actor touched!");
MyActor1.this.fire(new ActorTouchedListener.ActorTouchedEvent());
MyActor1.this.getParent().fire(new ActorTouchedListener.ActorTouchedEvent());
return true;
}
});
}
#Override
public void draw(Batch batch, float parentAlpha) {
batch.draw(image, 0, 0, image.getWidth(), image.getHeight());
}
}
Second Actor (should handle an Event fired by Actor1)
public class MyActor2 extends Actor {
public MyActor2() {
addListener(new ActorTouchedListener());
}
}
Event listener
public class ActorTouchedListener implements EventListener {
#Override
public boolean handle(Event event) {
if (event instanceof ActorTouchedEvent) {
Gdx.app.log("fired", "Second actor got event!");
return true;
}
return false;
}
public static class ActorTouchedEvent extends Event {
public ActorTouchedEvent() {
}
}
}
These two lines do nothing.
MyActor1.this.fire(new ActorTouchedListener.ActorTouchedEvent());
MyActor1.this.getParent().fire(new ActorTouchedListener.ActorTouchedEvent());
To call Actor from another Actor you should create instance of it.
public class MyActor1 extends Actor {
private Texture image;
private Actor actor2; // instance of MyActor2
public MyActor1(actor2: Actor) {
this.image = new Texture("badlogic.jpg");
this.actor2 = actor2;
setPosition(0, 0);
setBounds(0, 0, image.getWidth(), image.getHeight());
setTouchable(Touchable.enabled);
addListener(new InputListener() {
#Override
public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
Gdx.app.log("touched", "First actor touched!");
actor2.fire(event); // Here we fire event on MyActor2
return true;
}
});
}
#Override
public void draw(Batch batch, float parentAlpha) {
batch.draw(image, 0, 0, image.getWidth(), image.getHeight());
}
}
Intercomponent events
LibGDX does not provide embeded intercomponent event system, but you can use additional frameworks. For example LibGDX Autumn. You can use two type events:
text event - to notify components about changing of some state
object event - this mechanism deliver your event object to observers
This framework have documentaion and examples, it will help to do what you want
Delayed actor update
For actor's update purposes LibGDX have actions mechanism. In your case I suggest to attach DelayAction to MyActor2 with wrapped MyRedrawAction
DelayAction delayAction = new DelayAction();
// set wrapped action dellay (in sec.)
delayAction.setDuration(1F);
// set wrapped action. It will be invoked after 1 sec. of stage drawing
delayAction.setAction(new MyRedrawAction());
actor2.addAction(delayAction);
You can do this in touchDown() method as #icarumbas suggest
As the question is clearly stated, I want to slow down a TranslateAnimation while it is being executed when a user clicks on a certain button.
This is how I instantiate the TranslateAnim where -textViewHeight and layoutHeight are just some values I already instantiated in earlier time:
It goes from up to bottom.
TranslateAnim translateAnim = new TranslateAnim(0, 0, -textViewHeight, layoutHeight);
translateAnim.setDuration(20000);
translateAnim.setInterpolator(new LinearInterpolator());
textView.startAnimation(translateAnim);
This is the method where I want to slow the TranslateAnim down:
It didn't work as I expected though.
public void slowDown() {
translateAnim.setStartTime(translateAnim.getStartTime() - 20000);
translateAnim.setDuration((20000 - translateAnim.getElapsedTime()) * 2);
}
I also tried doing that but still no luck:
translateAnim.setInterpolator(new DecelerateInterpolator());
My custom TranslateAnim class extending TranslateAnimation:
public class TranslateAnim extends TranslateAnimation {
private long mElapsedAtPause, elapsedTime;
private boolean mPaused = false;
public TranslateAnim(float fromXDelta, float toXDelta, float fromYDelta, float toYDelta) {
super(fromXDelta, toXDelta, fromYDelta, toYDelta);
}
#Override
public boolean getTransformation(long currentTime, Transformation outTransformation) {
if (mPaused && mElapsedAtPause == 0) {
mElapsedAtPause = currentTime - getStartTime();
}
if (mPaused) {
setStartTime(currentTime - mElapsedAtPause);
}
elapsedTime = currentTime - getStartTime();
return super.getTransformation(currentTime, outTransformation);
}
public long getElapsedTime() {
return elapsedTime;
}
public void slowDown() {
translateAnim.setStartTime(translateAnim.getStartTime() - 20000);
translateAnim.setDuration((20000 - translateAnim.getElapsedTime()) * 2);
}
public void pause() {
mElapsedAtPause = 0;
mPaused = true;
}
public void resume() {
mPaused = false;
}
#Override
public void cancel() {
super.cancel();
elapsedTime = 0;
mElapsedAtPause = 0;
mPaused = false;
}
}
Is there any workaround for this?
Use a custom Interpolator instead of a linear one. A linear one causes it to have equal time slices. A custom one can have the time slices in the middle be longer.
For example:
public class CustomInterpolator implements Interpolator {
public boolean slowMode;
float lastInput;
float lastInputBeforeSlowed;
#Override
public float getInterpolation(float input) {
if (!slowMode) {
//Should be edited
lastInput = input;
return input;
} else {
return (input - lastInputBeforeSlowed) * .5f + lastInputBeforeSlowed;
}
}
public void enterSlowMode() {
slowMode = true;
lastInputBeforeSlowed = lastInput;
}
public void endSlowMode() {
slowMode = false;
//Should be edited
}
}
My game sets this screen and I see the actor that I create.
However can anyone tell me why the camera doesn't seem to be moving? I would expect the actor to move to the right as I pressed the Left key.
The Gdx.input.isKeyPressed(Input.Keys.LEFT) does resolve to true and the translate function does get called.
public class MainScreen implements Screen {
private Stage mainStage;
private Camera orthCamera;
#Override
public void show() {
orthCamera = new OrthographicCamera();
mainStage = new Stage(new ScreenViewport());
mainStage.getViewport().setCamera(orthCamera);
MyGameActorObject s = new MyGameActorObject();
s.setPosition(100, 100);
mainStage.addActor(s);
}
#Override
public void render(float delta) {
Gdx.gl.glClearColor(0,0,0,1); //sets clear
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
processKeyboardInput();
mainStage.act(delta);
mainStage.draw();
}
private void processKeyboardInput()
{
if (Gdx.input.isKeyPressed(Input.Keys.LEFT)) {
orthCamera.translate(-30, 0, 0);
orthCamera.update();
}
}
#Override
public void resize(int width, int height) {
}
#Override
public void pause() {
}
#Override
public void resume() {
}
#Override
public void hide() {
}
#Override
public void dispose() {
mainStage.dispose();
}
}
I am trying to acheieve being able to pan around the 2d game world using keyboard input.
This is the contents of the custom Actor object:
public class MyGameActorObject extends Actor
{
#Override
public void act(float delta) {
super.act(delta);
}
#Override
public void draw(Batch batch, float parentAlpha) {
super.draw(batch, parentAlpha);
ShapeRenderer r = new ShapeRenderer();
r.begin(ShapeRenderer.ShapeType.Filled);
r.setColor(Color.RED);
r.circle(this.getX() - 50.0f, this.getY() - 50.0f, 100.0f);
r.end();
}
}
I believe I have to add something like
r.setProjectionMatrix(batch.getProjectionMatrix());
in my custom actor object. So the draw function on my object would now be.
#Override
public void draw(Batch batch, float parentAlpha) {
super.draw(batch, parentAlpha);
ShapeRenderer r = new ShapeRenderer();
r.setProjectionMatrix(batch.getProjectionMatrix());
r.begin(ShapeRenderer.ShapeType.Filled);
r.setColor(Color.RED);
r.circle(this.getX() - 50.0f, this.getY() - 50.0f, 100.0f);
r.end();
}
I think your problem is that you haven't set the InputProcessor.
Gdx.input.setInputProcessor(...) is the method you're looking for. Put it in your show() method and pass it an InputProcessor reference. InputProcessor is an interface that goes through all the different types of click-based input and allows you to determine what happens in them. So for instance:
#Override
public boolean keyDown(int keycode)
{
switch(keycode)
{
case Keys.D:
screen.universe.entity_handler.player.moving_right = true;
break;
case Keys.A:
screen.universe.entity_handler.player.moving_left = true;
break;
case Keys.SPACE:
screen.universe.entity_handler.player.jumping = true;
break;
...
}
...
}
The above code is part of an InputProcessor-implementing class for a game I'm making. When the input in-question is pressed (keyDown in this case), the game looks at your input button (the keycode) and sends it through the switch statement.
Keep in mind that you must return true for all the input events that you want to use. That's what the boolean return value for this method is.
I'm stuck on implementing touchDragged listener using libgdx.
Here is my code and can anyone suggest how to drag image when user touches it and than moves finger?
I use stage and actor and I want to catch touchDragged event on actor.
Thanks.
public void create () {
Texture.setEnforcePotImages(false);
stage = new Stage();
Gdx.input.setInputProcessor(stage);
// create a SpriteBatch with which to render the sprite
batch = new SpriteBatch();
// load the sprite's texture. note: usually you have more than
// one sprite in a texture, see {#see TextureAtlas} and {#see TextureRegion}.
texture = new Texture(Gdx.files.internal("ball3.png"));
Skin skin = new Skin();
skin.add("default", new Label.LabelStyle(new BitmapFont(), Color.WHITE));
skin.add("ball", texture);
Image sourceImage = new Image(skin, "ball");
sourceImage.setBounds(50, 125, 100, 100);
stage.addActor(sourceImage);
// create an {#link OrthographicCamera} which is used to transform
// touch coordinates to world coordinates.
camera = new OrthographicCamera();
// we want the camera to setup a viewport with pixels as units, with the
// y-axis pointing upwards. The origin will be in the lower left corner
// of the screen.
camera.setToOrtho(false);
}
public void render () {
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
stage.act(Gdx.graphics.getDeltaTime());
stage.draw();
Table.drawDebug(stage);
// if a finger is down, set the sprite's x/y coordinate.
if (Gdx.input.isTouched()) {
// the unproject method takes a Vector3 in window coordinates (origin in
// upper left corner, y-axis pointing down) and transforms it to world
// coordinates.
camera.unproject(spritePosition.set(Gdx.input.getX(), Gdx.input.getY(), 0));
}
}
Here are the steps:
You need to check if the finger is touching the object that you want to move. Here are some useful methods to get your finger's position:
Gdx.input.getX();
Gdx.input.getY();
You need to use a variable to track whether the finger is moving, when touching.
If the variable is true, you change the object's position to your finger's position.
You disable your variable when the finger is no longer touching the screen.
I suggest you to use InputProcessor:
public class MyInputProcessor implements InputProcessor{
#Override
public boolean keyDown(int keycode){
return false;
}
#Override
public boolean keyUp(int keycode){
return false;
}
#Override
public boolean touchDown(int screenX, int screenY, int pointer, int button){
return false;
}
#Override
public boolean touchUp(int screenX, int screenY, int pointer, int button){
return false;
}
#Override
public boolean keyTyped(char character){
return false;
}
#Override
public boolean touchDragged(int screenX, int screenY, int pointer){
return false;
}
#Override
public boolean mouseMoved(int screenX, int screenY){
return false;
}
#Override
public boolean scrolled(int amount) {
return false;
}
}
Make it a Field:
MyInputProcessor inputProcessor;
And in onCreate():
inputProcessor = new MyInputProcessor();
Gdx.input.setInputProcessor(inputProcessor);
That way you can just implement your code in the touchDragged callback.