I'm developing a game using libGDX and I would like to know how I can drag and drop an Actor. I've made my stage and drawn the actor, but I don't know how to trigger that event.
Please try to help me using my own architecture.
public class MyGame implements ApplicationListener
{
Stage stage;
Texture texture;
Image actor;
#Override
public void create()
{
texture = new Texture(Gdx.files.internal("actor.png"));
Gdx.input.setInputProcessor(stage);
stage = new Stage(512f,512f,true);
actor = new Image(texture);
stage.addActor(actor);
}
#Override
public void render()
{
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
stage.draw();
}
}
If you don't want to use DragAndDrop class, you can use this:
actor.addListener(new DragListener() {
public void drag(InputEvent event, float x, float y, int pointer) {
actor.moveBy(x - actor.getWidth() / 2, y - actor.getHeight() / 2);
}
});
Edit: method drag instead touchDragged
Take a look at the Example in the libgdx examples. Here is the drag and drop test from the libgdx test classes: DragAndDropTest
If you just want to drag/slide your Actor around you need to add a GestureListener to it and pass your Stage to the Inputprocessor like this:Gdx.input.setInputProcessor(stage);.
Here is the GestureDetectorTest from libgdx.
For drag events its the Flinglistener.
In your main gamescreen class add a multiplexer so you can access events from different classes:
private InputMultiplexer inputMultiplexer = new InputMultiplexer(this);
After the gamescreen constructor add as an example:
inputMultiplexer = new InputMultiplexer(this);
inputMultiplexer.addProcessor(1, renderer3d.controller3d);
inputMultiplexer.addProcessor(2, renderer.controller2d);
inputMultiplexer.addProcessor(3, renderer3d.stage);
Gdx.input.setInputProcessor(inputMultiplexer);
In your class that is using the actors use a DragListener as and example:
Actor.addListener((new DragListener() {
public void touchDragged (InputEvent event, float x, float y, int pointer) {
// example code below for origin and position
Actor.setOrigin(Gdx.input.getX(), Gdx.input.getY());
Actor.setPosition(x, y);
System.out.println("touchdragged" + x + ", " + y);
}
}));
Related
Ive been playing around with libgdx for the past couple of days and I wanted to see if someone could give a simple example of how a main menu screen is created with a start button and exit button.
LibGDX suggests to use its "scene2d" component if you need to build a UI.
The simplest example can be found on LibGDX's Wiki right in the article about scene2d:
https://libgdx.com/wiki/graphics/2d/scene2d/scene2d
Actually, the first code example from the wiki will do - it is a bit modified by me and shoud be added to your implementation of Screen or Game:
private Stage stage;
#Override
public void create () {
stage = new Stage(new ScreenViewport());
Gdx.input.setInputProcessor(stage);
// Insert your UI elements here, for example:
Skin skin = new Skin(Gdx.files.internal("skin.json")); // https://libgdx.com/wiki/graphics/2d/scene2d/skin
Table menuContainer = new Table();
TextButton playButton = new TextButton("Play", skin);
playButton.addListener(new ClickListener() {
#Override
public void clicked (InputEvent event, float x, float y) {
// Called when player clicks on Play button
}
});
menuContainer.add(playButton);
TextButton exitButton = new TextButton("Exit", skin);
exitButton.addListener(new ClickListener() {
#Override
public void clicked (InputEvent event, float x, float y) {
// Called when player clicks on Exit button
}
});
menuContainer.add(exitButton);
}
#Override
public void resize (int width, int height) {
// See below for what true means.
stage.getViewport().update(width, height, true);
}
#Override
public void render () {
float delta = Gdx.graphics.getDeltaTime();
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
stage.act(delta);
stage.draw();
}
#Override
public void dispose () {
stage.dispose();
}
I want a camera to follow an Actor. I've watched a lot of tutorials on how to do this but I get a weird bug.
The Actor i created called "skeleton" seems to move away from the camera and not on the X axis.
The camera moves over static sprites fine.
I used 3 different types of moving the actor. None of them seem to work.
Sry for the bad code aswell.
Playscreen:
public class PlayScreen implements Screen {
OrthographicCamera camera;
Table table;
Stage stage;
Viewport viewport;
private MyGdxGame game;
skeleton skeleton;
public PlayScreen(MyGdxGame game){
this.game = game;
camera = new OrthographicCamera(1f, (Gdx.graphics.getHeight()/Gdx.graphics.getWidth()));
viewport =new FitViewport(Gdx.graphics.getWidth(), Gdx.graphics.getHeight(),camera);
skeleton = new skeleton();
stage = new Stage(new ScreenViewport());
skeleton.setPosition((Gdx.graphics.getWidth()/2)-(skeleton.getWidth()/2),((Gdx.graphics.getHeight()/1.75f)-(skeleton.getHeight()/1.4f)));
stage.addActor(skeleton);
Gdx.input.setInputProcessor(stage);
}
#Override
public void render(float delta) {
Gdx.gl.glClearColor(1,0,0,1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
skeleton.moveRight();
camera.position.x = skeleton.getX() + skeleton.getOriginX();
camera.position.y = skeleton.getY() + skeleton.getOriginY();
camera.update();
game.batch.setProjectionMatrix(camera.combined);
Gdx.input.setInputProcessor(stage);
stage.act(Gdx.graphics.getDeltaTime());
stage.draw();
}
#Override
public void resize(int width, int height) {
viewport.update(width,height);
}
Skeleton Actor:
public class skeleton extends Actor {
SpriteBatch batch;
Texture img;
int frame = 0;
int zeile = 0;
TextureRegion[][] regions;
public Sprite sprite;
public skeleton(){
batch = new SpriteBatch();
img = new Texture(Gdx.files.internal("Warrior Skeleton Animations/Walking/walk.png"));
//ANIMATION
regions = TextureRegion.split(img,572,953);
sprite = new Sprite(regions[0][0]);
code to make it an animation...
(works fine don't wont to enlarge the code here any further)
setBounds(sprite.getX(),sprite.getY(),sprite.getWidth(),sprite.getHeight());
}
public void moveRight(){
//First way of moving
MoveByAction mba = new MoveByAction();
mba.setAmount(10f,0f);
mba.setDuration(1/10f);
skeleton.this.addAction(mba);
//Second way of moving
//this.setPosition(this.getX()+10,this.getY());
//setBounds(sprite.getX(),sprite.getY(),sprite.getWidth(),sprite.getHeight());
//Third way of moving
//sprite.translateX(1);
//setBounds(sprite.getX(),sprite.getY(),sprite.getWidth(),sprite.getHeight());
}
#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);
}
}
I've tried moving the camera independently from the skeleton actor like this: camera.position.x = camera.position.x + 100; And the actor still moves away from the camera even if the camera moves faster than the actor.
I also tried moving the camera with the coordinates of the sprite itself from the skeleton actor. Same error though.
Thanks in advance.
I found a way to fix it for me.
If i move the camera itself and not the actor, I get the effect of the actor being moved. This proofs that my actor is bound to my Camera.
I would still love to know why that is and how to fix it though.
so I am doing what is suppose to be a simple game but I think I might be complicating things. I have a camera for the GameScreen and a viewPort which follow the position of the player and when it reaches to points on the sides, the camera stops following the player and stays in one point.
This by itself works fine, but then I wanted to add the pause menu and some other features in the game, creating a hud class with its own camera and viewport as well as a Stage and a shapeRenderer.
The problem comes when I create the instance of this hud inside my gameScreen, the camera that I am looking while I am playing looks like is the hudCam, which does not follow the player and basically does not let me see the player when it reaches the edges of the screen.
This is my GameScreen Class:
public class GameScreen implements Screen {
WowInvasion game;
ScrollingBackground background;
private OrthographicCamera gameCam;
private Viewport gameViewPort;
/*
Basically I wanna keep the same sprites running while in the menu, playing and till dead
therefore, I'll have a switch statement with cases on where the games at, inside the functions needed. That way I'll keep
the game has a background for the menu and there's no need for running a second screen.
*/
public static final int MAIN_MENU = 0;
public static final int GAME = 1;
private static int state = 1; //current state. starts with MAIN_MENU //DEBUGGING GAME SCREEN
//STAGES
private GameStage gameStage; //game ui
private menuStage mainMenu; //Main menu of the game
private Hud hud;
//Resources
private TextureAtlas atlas; //for the textures most
private Skin skin; //for the styles and fonts
//Sprites
private Player player;
//Shapes
private float progressPower; //for the power to build up
private final float POWER_CHARGED = 1000; //limit to get power
private final float DECREASING_POWER = 20; //limit to get power
public GameScreen(WowInvasion game){
this.game = game;
gameCam = new OrthographicCamera();
gameCam.setToOrtho(false, WowInvasion.WIDTH, WowInvasion.HEIGHT);
gameViewPort = new StretchViewport(WowInvasion.WIDTH, WowInvasion.HEIGHT, gameCam);
progressPower = 0f;
game.wowAssetManager.loadTexturesGameScreen(); // tells our asset manger that we want to load the images set in loadImages method
game.wowAssetManager.loadSkins(); //load the needed skins
game.wowAssetManager.manager.finishLoading(); // tells the asset manager to load the images and wait until finsihed loading.
skin = game.wowAssetManager.manager.get("ui/menuSkin.json");
}
#Override
public void show() {
game.batch.setProjectionMatrix(gameCam.combined);
background = new ScrollingBackground();
atlas = game.wowAssetManager.manager.get(WowAssetManager.GAME_ATLAS); //declaring atlas
mainMenu = new menuStage(gameViewPort, game.batch, skin, game); //pass it so that we only use one batch and one same viewport
gameStage = new GameStage(gameViewPort, game.batch, skin, game);
hud = new Hud(game.batch, skin);
player = new Player(atlas.findRegion("player"), (int) gameCam.viewportWidth / 2, (int) gameCam.viewportHeight / 2);
switch(state){
case MAIN_MENU:
Gdx.input.setInputProcessor(mainMenu);
break;
case GAME:
background.setFixedSpeed(false); //does not work in here
}
}
#Override
public void render(float delta) {
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
if(state == GAME) {
background.setFixedSpeed(false);
player.update(delta, gameCam.viewportWidth, gameCam.viewportHeight); //updating player for movement
//really cheap way to charge power with velocity
if(progressPower != POWER_CHARGED) {
progressPower += Math.abs(player.getVelocity().x) + Math.abs(player.getVelocity().y);
progressPower -= DECREASING_POWER;
}
else
progressPower = POWER_CHARGED / 4;
}
mainMenu.act(Math.min(Gdx.graphics.getDeltaTime(), 1 / 30f)); //updating while making sure delta won't be more than 1/30f.
gameStage.act(Math.min(Gdx.graphics.getDeltaTime(), 1 / 30f));
game.batch.begin();
background.updateAndRender(delta, game.batch); //updating scrolling background
player.draw(game.batch);
game.batch.end();
mainMenu.draw(); //draw the menu stage
gameStage.draw(); //draw the ui stage for the game
hud.getStage().draw();
hud.renderRotateMeter();
updateCamera(0, WowInvasion.WIDTH);
System.out.println(player.getPosition().x);
}
public void updateCamera(float startX, float endX){
Vector3 position = gameCam.position;
//linear interpolation : a + (b - a) * lerp
//b = player position
//a = current camera position
//lerp = interpolation factor
position.x = gameCam.position.x + (player.getPosition().x - gameCam.position.x) * .1f;
//making the camera stay when the player gets to close to the sides
if(position.x < startX) {
position.x = startX;
}
if(position.x > endX){
position.x = endX;
}
gameCam.position.set(position);
gameCam.update();
}
#Override
public void resize(int width, int height) {
gameViewPort.update(width, height);
//hud.getViewport().update(width, height);
}
#Override
public void pause() {
}
#Override
public void resume() {
}
#Override
public void hide() {
dispose();
}
#Override
public void dispose() {
mainMenu.dispose();
gameStage.dispose();
game.dispose();
hud.dispose();
}
public static void setState(int state) {
GameScreen.state = state;
}
}
And this is my HUD:
public class Hud implements Disposable{
private Stage stage;
private Viewport viewport;
Button buttonPause, buttonResume;
private OrthographicCamera hudCam;
private ShapeRenderer sp; //like a batch for shapes
public Hud(SpriteBatch sb, Skin skin){
hudCam = new OrthographicCamera();
hudCam.setToOrtho(false, WowInvasion.WIDTH, WowInvasion.HEIGHT);
viewport = new StretchViewport(WowInvasion.WIDTH, WowInvasion.HEIGHT, hudCam);
stage = new Stage(viewport, sb);
sp = new ShapeRenderer();
Table table = new Table();
table.top();
//this makes the table the size of the stage
table.setFillParent(true);
buttonPause = new Button(skin, "pause");
buttonPause.setTransform(true);
buttonPause.addListener(new ClickListener(){ //listener to handle event
#Override
public void clicked(InputEvent event, float x, float y) {
}
});
buttonResume = new Button(skin, "resume");
buttonResume.setTransform(true);
buttonResume.setScale(0.5f);
buttonResume.addListener(new ClickListener(){
#Override
public void clicked(InputEvent event, float x, float y) {
buttonResume.setVisible(false);
}
});
table.add(buttonPause);
table.row();
table.add(buttonResume);
stage.addActor(table);
}
public void renderRotateMeter(){
sp.setProjectionMatrix(hudCam.combined);
sp.begin(ShapeRenderer.ShapeType.Filled);
sp.setColor(Color.YELLOW);
sp.rect(hudCam.position.x,hudCam.position.y, WowInvasion.WIDTH / 2, 20);
sp.end();
}
public Viewport getViewport() {
return viewport;
}
public Stage getStage() {
return stage;
}
#Override
public void dispose() {
stage.dispose();
sp.dispose();
}
}
thanks in advance!
EDIT
so I tried passing the gameCam has a parameter to the hud and instead of making a new OrthographicCamera I used that one has the hudCamara as well and well, the movement with the player is perfect except now the thins from the Hud do not move at all..
It looks like you only set projectionMatrix to only HUD camera as seen in
sp.setProjectionMatrix(hudCam.combined);
Try to set it the same to other stuff outside of the HUD class prior to draw call too.
Another thing to keep in mind is that, when you involve using multiple Viewport and Camera in the game as most of the time it will be 1 Viewport matching with 1 Camera and work with another set as in your case. In draw call, you need to call apply() or apply(true) of Viewport class too to tell the system that you will draw based on which viewport thus in turn it will adhere to screen coordinate that was set up by viewport's attaching camera.
So assume you have 2 objects that needed to be called in different viewport consecutively, do it like the following code. The methods call is correct according to libgdx API but variable names are fictional.
// draw objA adhering to viewportA (thus cameraA) <-- assume it's player cam
sb.setProjectionMatrix(cameraA.combined);
viewportA.apply();
objA.draw();
// draw objB adhering to viewportB (thus cameraB) <-- assume it's HUD cam
sb.setProjectionMatrix(cameraB.combined);
viewportB.apply(true); // send in true as for HUD, we always want to center the screen
objB.draw();
In summary, 2 things to keep in mind when drawing objects that use multiple of camera and viewport in consecutive draw call.
Set projection matrix to either SpriteBatch or ShapeRenderer.
Call apply() or apply(true) of Viewport class to let it know you work with this viewport.
I'm trying to make a mini game with 2 classes: the menu and the play. I am trying to randomly change the image of an object in the play class by clicking a button in the menu class.
So, in the menu class I add a button and add input listener so that when I click
playbutton.addListener(new InputListener() {
public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
game.setScreen(new lessonclass (game));
lessonclass.currentlevel = MathUtils.random(2);
return true;
}
});
in the play class, I add static int level, an object
public grid ( Vector2 position, Vector2 size) {
texture0 = new Texture (Gdx.files.internal("zero.jpg"));
texture1 = new Texture (Gdx.files.internal("one.jpg"));
texture2 = new Texture (Gdx.files.internal("two.jpg"));
texture3 = new Texture (Gdx.files.internal("three.jpg"));
this.position = position;
this.size = size;
}
public void update(){
}
public void draw0(SpriteBatch batch) {
batch.draw(texture0, position.x, position.y, size.x, size.y);
}
public void draw1(SpriteBatch batch) {
batch.draw(texture1, position.x, position.y, size.x, size.y);
}
public void draw2(SpriteBatch batch) {
batch.draw(texture2, position.x, position.y, size.x, size.y);
}
if (TouchDown) {System.out.println ("" + level);}
When I run the program, the level value VARIES between 0 and 1 but the object always draw image batch.
If I change the code in the menu class so that it only brings me to play class and the in play class I change the level to
public static int level = MathUtils.random(1);
then my level value varies and the object randomly draws either batch image or batch1 image.
However, I want to do this from the menu class. Since the program runs and I cant think of the logic error, I am stuck now. Why can't I change my object image by clicking the button in the menu class?
I'm having trouble getting a MoveToAction to work on an Actor (menuBackground), when the Actor is in a separate class. I have attached relevant code below- which doesn't make the Actor move at all.
I have had success in apply other actions to the root stage in the MainMenuScreen class, and to a different Actor (Button) in the MainMenuScreen class, but have had no success applying actions to Actors in a separate class.
I've tried putting the MoveToAction in the act(float delta) method within the MenuBackground class, but that didn't work either. Neither did assigning the MoveToAction to the menuBackground from within the MainMenuScreen class.
Note that I am making a call to super.act(delta); within my MenuBackground class.
I would ultimately like to put the code for the MoveToAction within the Actor MenuBackground class, to make things neat and tidy.
Cheers.
Class containing stage:
public class MainMenuScreen implements Screen
{
private Stage stage;
public MainMenuScreen()
{
stage = new Stage(new FitViewport(800, 480));
Gdx.input.setInputProcessor(stage);
menuBackground = new MenuBackground();
MoveToAction moveToAction = new MoveToAction();
moveToAction.setPosition(242f, 276f);
moveToAction.setDuration(10f);
menuBackground.addAction(moveToAction);
stage.addActor(menuBackground);
#Override
public void render(float delta)
{
Gdx.gl.glClearColor(0, 0, 0, 0);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);
stage.act(Gdx.graphics.getDeltaTime());
stage.draw();
}
...
}
Actor Class:
public class MenuBackground extends Actor
{
private Texture menuBackgroundTexture;
private float actorX;
private float actorY;
public MenuBackground()
{
menuBackgroundTexture = new Texture(Gdx.files.internal("data/menuTitleTexture.png"));
actorX = 242f;
actorY = 350f;
setBounds(actorX,actorY,316,128);
}
#Override
public void draw(Batch batch, float alpha)
{
batch.draw(menuBackgroundTexture,actorX,actorY);
}
#Override
public void act(float delta)
{
super.act(delta);
}
...
}
The problem is inside your draw() method.
Look at code draws your texture, it uses actorX and actorY which are actually fields that don't change their values.
The proper way is:
batch.draw(menuBackgroundTexture, getX(), getY(), getWidth(), getHeight());
So, you should use actor's own fields and getters and don't manage yours.