I've been trying to learn scene2d and I'm having trouble getting a sprite/Actor to move when a key is held down. I've tried a few different methods but I can't seem to figure it out. This is the code I currently have, which moves the actor everytime i press the key but not when it is held down.
class Player extends Actor {
Sprite sprite = new Sprite(new Texture (Gdx.files.internal("character.png")));
public Player() {
setBounds(sprite.getX(), sprite.getY(), sprite.getWidth(), sprite.getHeight());
setTouchable(Touchable.enabled);
addListener( new InputListener() {
#Override
public boolean keyDown(InputEvent event, int keycode) {
if (keycode == Input.Keys.RIGHT) {
MoveByAction mba = new MoveByAction();
mba.setAmount(1f, 0f);
Player.this.addAction(mba);
}
return true;
}
});
}
#Override
protected void positionChanged() {
sprite.setPosition(getX(), getY());
super.positionChanged();
}
#Override
public void draw(Batch batch, float parentAlpha) {
sprite.draw(batch);
}
#Override
public void act(float delta) {
super.act(delta);
}
}
You can use a state mechanism for doing that. This can be a simple enum like that :
public enum PlayerMoveState {
RIGHT,
LEFT,
IDLE
}
Define a field below to your sprite typed PlayerMoveState like this :
PlayerMoveState moveState;
Set it's state to proper one according to Input.Keys.RIGHT, Input.Keys.LEFT in your keyDown method. Reset the state by setting it to IDLE in keyUp method which you need to override and implement.
#Override
public boolean keyUp(InputEvent event, int keycode) {
moveState = PlayerMoveState.IDLE;
}
Finally, write a simple switch case block in act method of your player.
#Override
public void act(float delta) {
super.act(delta);
switch(moveState) {
case RIGHT :
// Move right by using whatever method you want.
// Directly increasing x
// Increase x according to velocity you have defined.
// Use actions, little bit dangerous.
break;
case LEFT :
// Move left by using whatever method you want.
break;
case IDLE :
// Don't change x coordinate of your player.
break;
default :
break;
}
}
I'm not really sure if actor has a focus on itself to handle KeyUp/Down events and that's why I would avoid using listeners to move player.
Also changing the actor's position does not need actions - there is a simple setPosition() method in Actor class. You have also getX() and getY() methods to get current position of actor.
What would i do would be to check if any Key is down (by using Gdx.input not lisetners) and set position modified by some defined step like:
//Actor class
final float STEP = 1f;
...
#Override
public void act(float delta) {
super.act(delta);
if(Gdx.input.isKeyPressed(Keys.W))
this.setPosition(getX(), getY() + STEP);
if(Gdx.input.isKeyPressed(Keys.S))
this.setPosition(getX(), getY() - STEP);
if(Gdx.input.isKeyPressed(Keys.A))
this.setPosition(getX() - STEP, getY());
if(Gdx.input.isKeyPressed(Keys.D))
this.setPosition(getX() + STEP, getY());
}
the act() method is called in every render (if you are calling stage's act of course) so the movement will be continuous.
Related
I am trying to simulate tap and joystick movement on screen using AccessibilityService.
In addition i'm getting my input from gamepad controller device. doing tap is ok and done. my problem is simulating joystick movement on screen.
I don't know how can i do continuous touch with GestureDescription, because of time duration that this function requires.
i have used this code for tap:
public void virtual_touch(int posX, int posY)
{
Path path = new Path();
path.moveTo(posX, posY);
GestureDescription.Builder gestureBuilder = new GestureDescription.Builder();
gestureBuilder.addStroke(new GestureDescription.StrokeDescription(path, 10, 10));
//gestureBuilder.build();
boolean isDispatched = dispatchGesture(gestureBuilder.build(), new AccessibilityService.GestureResultCallback()
{
#Override
public void onCompleted(GestureDescription gestureDescription)
{
super.onCompleted(gestureDescription);
MyUtils.Log("onCompleted");
}
#Override
public void onCancelled(GestureDescription gestureDescription)
{
super.onCancelled(gestureDescription);
MyUtils.Log("onCancelled");
}
}, null);
MyUtils.Log("virtual_touch isDispatched : " + isDispatched);
}
For Continue Stroke Use this method may be this will help to you.
willContinue --
continueStroke
public GestureDescription.StrokeDescription continueStroke (Path path,
long startTime,
long duration,
boolean willContinue)
boolean: true if this stroke will be continued by one in the next gesture false otherwise. Continued strokes keep their pointers down when the gesture completes.
Im trying to implement a pause option for my following game. By pressing the back button my game should go to the updatePause() method. But instead it renders the latest 2-3 frames repeatedly. Making it look like it's "flickering".
By trying to prevent this I've added (in the comments) non-continously rendering which prevented the flickering. But then Im facing the problem that pressing the back-button more than once will render another frame. (one of those flickering frames). This is my code structure in short:
public class GameScreen implements Screen {
private static final int GAME_RUNNING = 0;
private static final int GAME_PAUSED = 1;
private static final int GAME_OVER = 2;
private static final int GAME_LEVEL_END = 3;
private int gameState;
private GdxGame game;
//create()
public GameScreen(final GdxGame game) {
//create all the stuff
}
#Override
public void render(float deltaTime) {
switch (gameState) {
case GAME_RUNNING:
updateRunning(deltaTime);
break;
case GAME_PAUSED:
updatePause(deltaTime);
break;
case GAME_OVER:
updateGameOver();
break;
}
}
public void updateRunning(float deltaTime) {
//draw section
if (gameState == GAME_RUNNING) {
game.batch.begin();
//Draw some stuff
game.batch.end();
//move section
handleInput();
}
}
public void updatePause(float deltaTime) {
//Gdx.graphics.setContinuousRendering(false);
//resume
if (Gdx.input.isTouched()) {
gameState = GAME_RUNNING;
//Gdx.graphics.setContinuousRendering(true);
return;
}
}
private void handleInput() {
//catch back button
Gdx.input.setCatchBackKey(true);
if (Gdx.input.isKeyJustPressed(Input.Keys.BACK)) {
gameState = GAME_PAUSED;
return;
}
}
}
Any tips to prevent the flickering?
The render() method is supposed to draw something to the screen. If it doesn't, then the contents of the back buffer will be undefined (so may contain a previous frame) and you get this flicker.
Disabling continuous rendering sounds like a good approach. Alternatively, continue drawing even if the game is paused.
As Thomas points out in his answer. It should be drawing every frame.
I haven't see you draw anything on screen yet, so backbuffer might just contain junk information. One way to solve this is to always clear the screen at the top of render(). This means when you switch immediately to another game state, it will make sure that screen is clear and no flickering result as you experienced.
public void render(float deltaTime) {
// clear screen
Gdx.gl.glClearColor(0f, 0f, 0f, 1.0f);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
...
}
As Thomas said: continue drawing is a way to fix it. So I drew the latest frame and disabled continous rendering after that. The result is having no bug at all and giving the CPU a rest.
So i have an actor which is a sprite, set on a screenviewport stage. What i want to do is be able to touch the actor, then touch a spot on the screen it will move that fluently. Currently when i touch the actor it just jumps seemingly to random spots. Here is some of the code in my actor class,
public MyActor(){
setBounds(sprite.getX(),sprite.getY(),
sprite.getWidth(),sprite.getHeight());
setTouchable(Touchable.enabled);
addListener(new InputListener(){
#Override
public boolean touchDown(InputEvent event, float x, float y,
int pointer, int button) {
MoveByAction mba = new MoveByAction();
mba.setAmount(x,y);
MyActor.this.addAction(mba);
return true;
}
});
}
#Override
protected void positionChanged() {
sprite.setPosition(getX(),getY());
super.positionChanged();
}
#Override
public void draw(Batch batch, float parentAlpha) {
sprite.draw(batch);
}
#Override
public void act(float delta){
super.act(delta);
}
Few things to ask here. First of all, your touch is only on touching your Actor. That's going to be exactly where the actor is. You need to implement at your Scene level some basic state machine to know "on first tap, this actor is selected", then it has to be in a "select where this actor goes" state, and finally when you select a position it has to send that XY to the selected actor to do the move.
Now, as Tenfour04 mentioned, you have to figure out the conversion of the screen's XY of the touch point into the game world's coordinates. Tenfour04 made a good comment about using the projection method on the viewport camera to achieve this. Once you do that, you can send the coordinates to your actor to do the previously mentioned stuff.
To achieve the movement, I'd use the Action framework, like below:
actor.addAction(moveTo(x, y, 0.4f, Interpolation.circle));
This page shows you all the nice actions available for Scene2d: https://github.com/libgdx/libgdx/wiki/Scene2d#actions
Hope that's what you needed. :)
I am building a simple game with LIBGDX, and I have come across this irritating problem.
I have a MenuScreen.java class, which looks like this:
Here's the full class on pastebin
The important part is the imageButton's inputListener:
button.addListener(new InputListener(){
#Override
public boolean touchDown(InputEvent event, float x, float y, int pointer, int button){
return true;
}
#Override
public void touchUp(InputEvent event, float x, float y, int pointer, int button){
MineFinder.Log.debug("xpos: " + x + ", ypos: " + y);
game.setScreen(new GameScreen(game));
}
});
My problem is, that if this class is present (and GameScreen gets called from this) for some reason if I click to the ImageButton's position in GameScreen, it still handles the input. This is really irritating, because if a player clicks on that part of the screen the game gets reseted.
If i remove references to my MenuScreen.java file, and skip it alltogether the problem isn't present, so I am pretty sure that the problem is that the InputListener doesn't get "deleted"
Any ideas how to fix this?
Thanks!
The problem is that in your resize method. This line:
Gdx.input.setInputProcessor(stage);
sets the global input processor to be the stage. The input handling code doesn't really care about what is being rendered on the screen.
I think the easiest fix is to set the input processor in show and clear it in hide. Something like this:
#Override
public void show() {
Gdx.input.setInputProcessor(stage);
}
#Override
public void hide() {
Gdx.input.setInputProcessor(null);
}
Alternatively, you could have your callbacks check to see if the button/stage/screen they are in is active, or unregister/re-register the callbacks in show/hide, depending on how you are using your Screens.
I want to execute a function only when the actor on my stage is touched..
But the problem is the function gets executed irrespective of where its touched on the stage..
Even if its touched in some random place on stage the function is executed..
I want the function to execute only when the actor is touched..I have setbounds...Still its not working..
public Restart()
{
atlas = new TextureAtlas(Gdx.files.internal("pages-info.atlas"));
sprite = atlas.createSprite("restart");
this.touchable = true;
sprite.setBounds(x, y, sprite.getWidth(), sprite.getHeight());
}
public void draw(SpriteBatch batch,float parentAlpha)
{
batch.draw(sprite, x, y , width, height );
}
#Override
public Actor hit(float x, float y)
{
// TODO Auto-generated method stub
Gdx.app.log( FirstGame.LOG, " restart working " );
return null;
}
The hit method is not doing what you think it does. The hit method is for testing if the current actor intersects the given x,y or not, so its always called. (Its useful if you want to throw away "hits" because your actor is not rectangular.)
Use addListener to add an event listener to receive touch events and react to them.