libgdx firing event from actor to call another actor - android

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

Related

libgdx: main menu doesn't show game class libgdx

I am a newbie in libgdx and I have a little problem with screens and main menu.
I have a mainmenu class and it work with click on actors. The code for click is:
pulsanteplay.addListener(
new InputListener() {
#Override
public boolean touchDown(InputEvent event, float x, `float y, int pointer, int button) {
System.out.println("click play");
game.setScreen(new GameClass());
return false;
}
});
and it works. If I call Gdx.app.exit(); it works. If I call the next class, works
public class SplashScreen implements Screen {
private SpriteBatch batch;
private Texture ttrSplash;
private Timer timer;
private MainMenu mainMenuScreen;
public SplashScreen() {
super();
batch = new SpriteBatch();
ttrSplash = new Texture("quadrato.png");
}
#Override
public void render(float delta) {
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.begin();
batch.draw(ttrSplash, 0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
batch.end();
}
#Override
public void hide() { }
#Override
public void pause() { }
#Override
public void resume() { }
#Override
public void show() { }
#Override
public void resize(int width, int height) { }
#Override
public void dispose() {
ttrSplash.dispose();
batch.dispose();
}
When I call a class that extends game and have a render method don't work. The screen don't change but the events of the game class are really generated.
Someone could explain me why this? How do I fix this bad behavior?
Thanks for your attention
Have that class that extends Game just implement Screen and put your code in it's corresponding methods. A screen offers everything a Game does so you should have no trouble converting. your onCreate code can be put in the show method or the constructor of your new Screen class. Now just call setScreen() to show this screen.

LIBGDX Camera doesn't move

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.

Screen does not save its progress when switched

So, this is too hard for me to explain this in short, so the title looks so creepy. Well, I am writing a small android application and i have like three screens: the main-menu one, and two in-game screens. I can switch between the last two ones with a switch button. The problem is, that when i switch back to the last screen I've been to, all its acting stuff starts again from the very beginning. I have also seen a similar question here, but i did not work for me(( So, is there any way to keep the screen rendering, while I am using the other one?
Thank you!
The first screen:
public class InGameScreen implements Screen {
TinyBattles game;
public static OrthographicCamera camera;
public static Button switchButton1;
public InGameScreen(TinyBattles game) {
this.game = game;
}
#Override
public void show() {
camera = new OrthographicCamera(Global.VIEWPORT_WIDTH, Global.VIEWPORT_WIDTH*Global.actualHeight/Global.actualWidth);
camera.position.set(camera.viewportWidth / 2, camera.viewportHeight / 2, 0);
camera.update();
switchButton1 = new Button();
switchButton1.create(10, 800, 150, 150);
Assets.loadSwitchButton();
}
#Override
public void render(float delta) {
Global.cleanScreen();
touchHandler(camera, Global.touch);
switchButton1.act();
switchButton1.render(Assets.buttonSwitchStateUpSprite, Assets.buttonSwitchStateDownSprite);
Global.debugRender();
localDebugRender();
leaveScreen();
Global.batch.getProjectionMatrix().setToOrtho2D(0, 0, Global.actualWidth, Global.actualHeight);
Global.batch.setProjectionMatrix(camera.combined);
Global.shapeRenderer.setProjectionMatrix(camera.combined);
}
public void localDebugRender(){
switchButton1.debugRender();
}
public void touchHandler(OrthographicCamera camera, Vector3 touch){
if (Gdx.input.isTouched()){
touch.set(Gdx.input.getX(), Gdx.input.getY(), 0);
camera.unproject(touch);
Global.finger.bounds.x = touch.x - 64;
Global.finger.bounds.y = touch.y - 64;
}else{
Global.finger.bounds.x = 10000;
Global.finger.bounds.y = 10000;
}
}
#Override
public void resize(int width, int height) {
}
#Override
public void pause() {
}
#Override
public void resume() {
}
#Override
public void hide() {
}
#Override
public void dispose() {
}
public void leaveScreen(){
if (switchButton1.isPressed == true){
game.setScreen(TinyBattles.inGameVirtualScreen);
}
}
}
The second screen:
public class InGameVirtualScreen implements Screen {
TinyBattles game;
public static World world;
public static OrthographicCamera camera;
public static Button switchButton2;
public static Balls balls;
public InGameVirtualScreen(TinyBattles game) {
this.game = game;
}
#Override
public void show() {
world = new World(new Vector2(0, 0), true);
camera = new OrthographicCamera(Global.VIEWPORT_WIDTH, Global.VIEWPORT_WIDTH*Global.actualHeight/Global.actualWidth);
camera.position.set(camera.viewportWidth / 2, camera.viewportHeight / 2, 0);
camera.update();
switchButton2 = new Button();
switchButton2.create(10, 800, 150, 150);
balls = new Balls();
Assets.loadBallSprites();
balls.create();
balls.setup();
}
#Override
public void render(float delta) {
Global.tweenManager.update(1 / 60f);
Global.cleanScreen();
world.step(1 / 60f, 10, 10);
touchHandler(camera, Global.touch);
switchButton2.act();
balls.act();
renderLayerOne();
renderInterfaceLayer();
renderDebugLayer();
leaveScreen();
Global.batch.getProjectionMatrix().setToOrtho2D(0, 0, Global.actualWidth, Global.actualHeight);
Global.batch.setProjectionMatrix(camera.combined);
Global.shapeRenderer.setProjectionMatrix(camera.combined);
}
public void renderLayerOne(){
balls.render();
}
public void renderInterfaceLayer(){
switchButton2.render(Assets.buttonSwitchStateUpSprite, Assets.buttonSwitchStateDownSprite);
}
public void renderDebugLayer(){
Global.debugRender();
localDebugRender();
}
public void localDebugRender(){
switchButton2.debugRender();
}
public void touchHandler(OrthographicCamera camera, Vector3 touch){
if (Gdx.input.isTouched()){
touch.set(Gdx.input.getX(), Gdx.input.getY(), 0);
camera.unproject(touch);
Global.finger.bounds.x = touch.x - 64;
Global.finger.bounds.y = touch.y - 64;
}else{
Global.finger.bounds.x = 10000;
Global.finger.bounds.y = 10000;
}
}
#Override
public void resize(int width, int height) {
}
#Override
public void pause() {
}
#Override
public void resume() {
}
#Override
public void hide() {
}
#Override
public void dispose() {
}
public void leaveScreen(){
if (switchButton2.isPressed){
game.setScreen(TinyBattles.inGameScreen);
}
}
}
There is a leaveScreen() method at the bottom of the each class
You should move all your code in each screen from show() to the constructor. show() is called every time the screen becomes the active screen, so you don't want to do initialization there.
By the way, you should never keep static references to anything that implements Disposable, such as SpriteBatch, or anything that keeps a reference to something that implements Disposable, such as Button or Stage. If you do, when the user backs out of your game activity and reopens it, there will be memory leaks and incorrect textures, etc.
Its been a while since I used libgdx, but it seems you are creating a new screen on each leaveScreen function.
See here for a better approach, where when you create the Game class you create the screens as well:
public class MyGame extends Game {
MainMenuScreen mainMenuScreen;
AnotherScreen anotherScreen;
#Override
public void create() {
mainMenuScreen = new MainMenuScreen(this);
anotherScreen = new AnotherScreen(this);
setScreen(mainMenuScreen);
}
}
Then, you change the screens like this:
public void leaveScreen(){
if (switchButton2.isPressed){
game.setScreen(game.anotherScreen);
}
}
As I said it's been a while, but give it a shot :)

Custom SurfaceView with thread crashes App when Activity is exited

I just made an Activity which uses at "setContentView" a view from a class which extends SurfaceView. The problem is:
It works fine, but when I exit it (BACK key) it crashes. Code:
package ro.etrandafir.mate.appCreator;
import android.app.Activity;
import android.os.Bundle;
import android.content.Context;
import android.view.View;
import android.view.SurfaceView;
import android.graphics.Canvas;
import android.view.MotionEvent;
import android.graphics.Color;
import android.graphics.Paint;
public class Sample2 extends Activity implements View.OnTouchListener {
float x = 0, y = 0;
SampleTwoView theView;
public boolean onTouch(View v, MotionEvent event) {
// TODO: Implement this method
x = event.getX();
y = event.getY();
return true;
}
#Override
protected void onPause() {
super.onPause();
finish();
}
#Override
protected void onCreate(Bundle b) {
super.onCreate(b);
theView = new SampleTwoView(this);
theView.setOnTouchListener(this);
setContentView(theView);
}
public class SampleTwoView extends SurfaceView implements Runnable {
Paint p = new Paint();
public SampleTwoView(Context context) {
super(context);
p.setColor(Color.RED);
Thread theThread = new Thread(this);
theThread.start();
}
public void run() {
while (true) {
if (!getHolder().getSurface().isValid()) continue;
Canvas canvas;
canvas = getHolder().lockCanvas();
canvas.drawColor(Color.BLUE);
if ((x != 0) && (y != 0)) canvas.drawCircle(x, y, 40, p);
getHolder().unlockCanvasAndPost(canvas);
}
}
}
}
What can I do? Should I add onDestroy or what?
Thanks in advance,
Matei
The issue you are getting is related to this code:
Canvas canvas;
canvas = getHolder().lockCanvas();
canvas.drawColor(Color.BLUE);
When your activity ends, your thread is still running, but your custom SurfaceView is no longer available, so you will get a null ptr exception. Your existing code can easily be patched by adding a boolean that gets set to false as soon as the onPause fn gets called:
public void run() {
while (booleanThatGetsSetToFalseWhenActivityPauses) {
if (!getHolder().getSurface().isValid()) continue;
Canvas canvas;
canvas = getHolder().lockCanvas();
canvas.drawColor(Color.BLUE);
if ((x != 0) && (y != 0)) canvas.drawCircle(x, y, 40, p);
getHolder().unlockCanvasAndPost(canvas);
}
}
However, I would suggest altering the structure of your application as a whole. This may just be for practice, but I think a more efficient and bug free way of accomplishing your goal would be to simply use a standard SurfaceView and to completely decouple your drawing logic from any custom view.
My redesigned activity is below, but it utilizes a Ball class that is used to maintain the ball's logic, which, in your current code is separately coupled with both the actvity (the coordinates) and the view (the Paint). In this new ball class, a ball has a location (specified by a PointF), a Paint, and a diameter. It also has methods to get most of these variables in addition to setting some.
public class Ball {
private Paint mPaint;
private PointF mCoordinates;
private int mDiameter;
public Ball (int color, int diameter) {
mPaint = new Paint();
mPaint.setColor(color);
mCoordinates = new PointF();
mCoordinates.x = 0;
mCoordinates.y = 0;
mDiameter = diameter;
}
public void setCoordinates (float x, float y) {
mCoordinates.x = x;
mCoordinates.y = y;
}
public PointF getCoordinates() {
return mCoordinates;
}
public Paint getPaint() {
return mPaint;
}
public int getDiameter() {
return mDiameter;
}
/* You did not want to draw the uninitialized ball, so this method checks that */
public boolean hasNonZeroLocation () {
return (mCoordinates.x != 0 && mCoordinates.y != 0);
}
}
I use the Ball class in the activity as shown below. Notice that the redrawing to the canvas now only occurs when a user touches the canvas as opposed to an infinite while loop. This is due to the utilization of the Handler class which posts actions to run to the UI thread. Additionally, now we do not need a custom view, and our ball's logic has been decoupled from the activity and the view.
public class RedBallActivity extends Activity {
Handler mDrawingHandler;
SurfaceView mDrawingSurfaceView;
Ball mBall;
private final Runnable drawRedBallOnBlueSurface = new Runnable() {
#Override
public void run() {
if (!mDrawingSurfaceView.getHolder().getSurface().isValid()) return;
Canvas canvas = mDrawingSurfaceView.getHolder().lockCanvas();
canvas.drawColor(Color.BLUE);
if (mBall.hasNonZeroLocation())
canvas.drawCircle(mBall.getCoordinates().x, mBall.getCoordinates().y, mBall.getDiameter(), mBall.getPaint());
mDrawingSurfaceView.getHolder().unlockCanvasAndPost(canvas);
}
};
private final OnTouchListener mCanvasTouchListener = new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
mBall.setCoordinates(event.getX(), event.getY());
mDrawingHandler.post(drawRedBallOnBlueSurface);
return true;
}
};
#Override
protected void onCreate(Bundle b) {
super.onCreate(b);
mDrawingSurfaceView = new SurfaceView(this);
mDrawingSurfaceView.setOnTouchListener(mCanvasTouchListener);
setContentView(mDrawingSurfaceView);
mBall = new Ball(Color.RED, 40);
mDrawingHandler = new Handler();
}
}
Now, if you actually run this code you will notice that initially the screen is not drawn with a blue background. You might be tempted to simply call mDrawingHandler.post(drawRedBallOnBlueSurface); at the end of the onCreate method, but it is not guaranteed that the SurfaceView will be ready to be drawn upon (see the documentation on this lockCanvas method). If you want the surface to initially be blue, you need to implement a [SurfaceHolder.Callback][2], which needs to be connected to the SurfaceView's SurfaceHolder, and on the surfaceCreated method being called, we know the surface is ready, so we can then call mDrawingHandler.post(drawRedBallOnBlueSurface);
Now, with this added, I change the Activity to implement [SurfaceHolder.Callback][2] as follows:
public class FriendManagerActivity extends Activity implements SurfaceHolder.Callback {
and add this line to the constructor:
mDrawingSurfaceView.getHolder().addCallback(this);
and implement the interface:
#Override
public void surfaceCreated(SurfaceHolder holder) {
mDrawingHandler.post(drawRedBallOnBlueSurface);
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
Feel free to ask any questions on my little redesign! While your problem could be easily patched, I felt like the way you were coupling logic with Views was a little bit flawed, and thought a little more info on SurfaceView coding would be helpful.
As someone mentioned it in the above, when your activity ends, your thread is still running, but your custom SurfaceView is no longer available, so you will get a Null Point Exception. Your existing code can easily be patched by adding a boolean that gets set to false as soon as the onPause fn gets called:I had the same problem. To solve it I added the following onPause() to your SampleTwoView class:
// pause method will destroy the Thread
public void pause() {
isRunning = false;
while (true) {
try {
myThread.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
break;
}
myThread = null;
}
Then call this onPause() method in your onPause() method of your Sample2 class as follows:
#Override
protected void onPause() {
super.onPause();
SampleTwoView.onPause();
finish();
}
So everytime the onPause() method of your main Activity class is called the Thread will be destroyed.
I hope this will help you.
Cheers!

ScaleTo not working in Libgdx

I'm trying to scale my Actor using actions.
But it does not work. I just want my actor to increase/decrease its size over a given time.
The Actor will just wait for the 2 seconds duration I have given the ScaleTo. It correctly moves based on the MoveTo action I gave it.
public class SpriteTest extends Actor {
private Sprite sprite;
private TextureAtlas atlas;
Rectangle boundsd = new Rectangle();
public SpriteTest(FirstGame game) {
//super(game);
Gdx.app.log( FirstGame.LOG, "spritetest's costructor" );
atlas = new TextureAtlas(Gdx.files.internal("pages-info.atlas"));
sprite = atlas.createSprite("Plus");
}
public void draw(SpriteBatch batch,float parentAlpha) {
batch.draw(sprite, x, y);
}
// We are adding the actor to the stage in another class
public class LevelScreen extends AbstractScreen {
private Jumper2D jumper2d;
SpriteTest obstacled = new SpriteTest(game);
public LevelScreen(FirstGame game) {
super(game);
}
#Override
protected boolean isGameScreen() {
return true;
}
#Override
public void show() {
super.show();
stage.addActor(obstacled);
obstacled.action
(Forever.$
(Sequence.$
(ScaleTo.$(1.4f, 1.4f, 2),(MoveTo.$(100,120, 3f) ))
));
jumper2d = Jumper2D.create(getAtlas());
stage.addActor(jumper2d);
stage.draw();
}
public void render () {
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
stage.act(Math.min(Gdx.graphics.getDeltaTime(), 1 / 30f));
stage.draw();
}
}
Any help is appreciated
Your draw() method for SpriteTest is ignoring any scale or rotation settings on the Actor. You need to draw the sprite scaled/rotated/sized appropriately. (You may also need to set the Actor's x, y, width, and height --- see
setBounds)
public void draw(SpriteBatch batch, float parentAlpha)
{
final Color c = getColor();
batch.setColor(c.r, c.g, c.b, c.a * parentAlpha);
batch.draw(sprite, getX(), getY(), getOriginX(), getOriginY(), getWidth(), getHeight(), getScaleX(), getScaleY(), getRotation());
}

Categories

Resources