Let's say I have an actor foo and an actor bar which is the bigger version of the foo actor. How can I show bar next to foo while foo is tapped and then have have it disappear when foo is no longer being tapped (kind of like a tooltip)? Foo is in a stage2d table.
How can I show a bigger, more complete version of the actor while it is tapped?
Rough idea of what I want:
Actor foo = new Actor();
foo.addListener(new ActorGestureListener() {
public void touchDown (InputEvent event, float x, float y, int pointer, int button) {
// show bar next to foo (like a tooltip)
}
public void touchUp (InputEvent event, float x, float y, int pointer, int button) {
// make bar dissapear
}
});
Code that kind of does what I want:
In this case toolTipCard is the actor I want to display when I touch one of the Strings from selectedDeck.
Stage collectionStage;
List<String> selectedDeck;
TestCard tooltipCard;
Vector3 touch = new Vector3();
selectedDeck.addListener(new ActorGestureListener() {
public void touchDown(InputEvent event, float x, float y, int count, int button) {
if (selectedDeck.getItems().size > 0) {
tooltipCard = new TestCard(game.cardFont, cardNameToCard.get(selectedDeck.getSelected()));
tooltipCard.setWidth(Gdx.graphics.getWidth() / (7 * scale));
tooltipCard.setHeight(Gdx.graphics.getHeight() / (3.3f * scale));
touch = viewport.unproject(touch.set(Gdx.input.getX(), Gdx.input.getY(), 0));
tooltipCard.setPosition(touch.x - tooltipCard.getWidth(), touch.y - tooltipCard.getHeight());
collectionStage.addActor(tooltipCard);
}
}
public void touchUp(InputEvent event, float x, float y, int pointer, int button) {
tooltipCard.remove();
}
});
As described in the comments there are 2 options:
edit the draw method of an actor (if you need to show a very simple tooltip use this).
If you want to show a more advanced actor which can have it's own actions. You can get the foo's global coordinates and add an actor directly to the stage which you set visible/invisible (or fade in/out for a nicer look/feel).
To get the coordinates you can use: localToStageCoordinates
Vector2 v = foo.localToStageCoordinates(new Vector2());
or relative to its center:
Vector2 v = foo.localToStageCoordinates(new Vector2(foo.getWidth()/2,foo.getHeight()/2));
then use for instance: bar.addAction(Actions.fadeIn(0.5f)); and bar.addAction(Actions.fadeOut(0.5f)); to show/hide the tooltip
For optimization purposes you can reuse the bar actor for all foo instances on the screen and just repurpose it on every click. It is probably cheaper than creating a full actor for every one of them. However, if you can probably even create and destroy it on touch down/up without any problems.
Related
I have two textures drawn in my 2d scene. And I want to drag them each separately. When they are touched and dragged I want both textures to be dragged to each points. How it drag them individually, or is there any other method to implement. as I am a beginner to libgdx.
My code:
public class MyGdxGame implements ApplicationListener{
OrthographicCamera camera;
ShapeRenderer shapeRenderer;
float screenOffset=10,circleRadius=30;
Texture firstTexture;
Texture secondTexture;
float firstTextureX;
float firstTextureY;
float secondTextureX;
float secondTextureY;
float touchX;
float touchY;
SpriteBatch batch;
#Override
public void create()
{
firstTexture= new Texture("b1.jpg");
firstTextureX = 50;
firstTextureY = 50;
secondTexture = new Texture("b2.jpg");
secondTextureX = 250;
secondTextureY = 250;
batch = new SpriteBatch();
camera=new OrthographicCamera();
shapeRenderer=new ShapeRenderer();
shapeRenderer.setAutoShapeType(true);
}
#Override
public void render()
{
Gdx.gl.glClearColor(1,1,1,1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
shapeRenderer.setProjectionMatrix(camera.combined);
shapeRenderer.begin();
shapeRenderer.setColor(Color.RED);
shapeRenderer.circle(camera.viewportWidth/2,camera.viewportHeight/2,circleRadius);
shapeRenderer.rect(screenOffset,screenOffset,camera.viewportWidth-2*screenOffset,camera.viewportHeight-2*screenOffset);
shapeRenderer.line(screenOffset,screenOffset,camera.viewportWidth-screenOffset,camera.viewportHeight-screenOffset);
shapeRenderer.line(screenOffset,camera.viewportHeight-screenOffset,camera.viewportWidth-screenOffset,screenOffset);
shapeRenderer.line(screenOffset,camera.viewportHeight/2,camera.viewportWidth-screenOffset,camera.viewportHeight/2);
shapeRenderer.line(camera.viewportWidth/2,screenOffset,camera.viewportWidth/2,camera.viewportHeight-screenOffset);
shapeRenderer.end();
batch.begin();
batch.draw(firstTexture, firstTextureX, firstTextureY);
batch.draw(secondTexture, secondTextureX, secondTextureY);
batch.end();
}
Well, you should save the current position of the texture, and on the update, move the texture checking the movement of the mouse if the mouse right click is active. This is the general idea, valid for any framework or code.
I recommend you to move the texture to their own class, with a getter and a setter of the position and the texture, so it's easy to manage.
Using libdgx you can check if the mouse is clicked at any time with
Gdx.input.justTouched();
On every update you can check what was the last position of the mouse and calculate the difference with the new position on every update using
Gdx.input.getX()
Gdx.input.getY()
To syncronize the position inside your screen with the position on your camera you should use unproject on your camera, for example:
Vector3 mousePos = new Vector3();
mousePos.x = Gdx.input.getX();
mousePos.y = Gdx.input.getY();
mousePos.z = 0;
camera.unproject(mousePos); //this will convert the screen position to your camera position
TL;DR you need to check what was the last position of the mouse when it was clicked, and on the next update calculate the difference, and then you update the position of the texture.
BTW, although this is for when you get more experience, you can create a class that implements InputProcessor, alow the class to be processed by libgdx with, for example:
public class CameraControllerDesktop implements InputProcessor, ControllerListener {
public CameraControllerDesktop() {
Gdx.input.setInputProcessor(this);
}
and the you could use the function
#Override
public boolean touchDragged(int screenX, int screenY, int pointer) {
return false;
}
to let libgx calculate the position of what you are dragging.
Sorry about not writing the entire solution, but if whit this info you can solve your problem, I'm pretty sure that you sooner or later will be able to make the game you want.
I want to create custom methods to swipe my view up and down, for example 25% down/up, or any other cases.
I tried to override methods like this:
public static ViewAction swipeDown(){
return new GeneralSwipeAction(Swipe.FAST, GeneralLocation.CENTER,
GeneralLocation.TOP_CENTER, Press.FINGER);
}
But it's only from top to center and i need shorter ones. I wanted to use new CoordinatesProvider() :
public static ViewAction swipeDown(){
return new GeneralSwipeAction(Swipe.FAST, new CoordinatesProvider() {
#Override
public float[] calculateCoordinates(View view) {
return new float[0];
}
},
GeneralLocation.TOP_CENTER, Press.FINGER);
}
...and it might be an answer, but i don't really know how to calculate coordinates.
Robotium has already drag() function which is pretty similar to swipe[Direction] functions in Espresso, but it already uses defined coordinates.
* #param fromX X coordinate of the initial touch, in screen coordinates
* #param toX X coordinate of the drag destination, in screen coordinates
* #param fromY Y coordinate of the initial touch, in screen coordinates
* #param toY Y coordinate of the drag destination, in screen coordinates
* #param stepCount how many move steps to include in the drag. Less steps results in a faster drag
Check https://github.com/RobotiumTech/robotium/blob/b69dcf740baabec48c91999e523377faef79682e/robotium-solo/src/main/java/com/robotium/solo/Solo.java
You can use both frameworks along or try to rewrite Robotium's drag function in your own way.
This might be handful: https://github.com/piotrek1543/robotium-showcase
Objective: to rotate an image in the center of the screen with movement equal to left or right touchDragged event.
Right now I have a basic Stage that is created and adds an actor (centerMass.png) to the stage. it is created and rendered like this:
public class Application extends ApplicationAdapter {
Stage stageGamePlay;
#Override
public void create () {
//setup game stage variables
stageGamePlay = new Stage(new ScreenViewport());
stageGamePlay.addActor(new CenterMass(new Texture(Gdx.files.internal("centerMass.png"))));
Gdx.input.setInputProcessor(stageGamePlay);
}
#Override
public void render () {
Gdx.gl.glClearColor(255f/255, 249f/255, 236f/255, 1f);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
//before drawing, updating actions that have changed
stageGamePlay.act(Gdx.graphics.getDeltaTime());
stageGamePlay.draw();
}
}
I then have a separate class file that contains the CenterMass class, extending Image. I am familiar enough to know I could extend Actor, but I am not sure the benefit I would gain using Actor vs Image.
In the CenterMass class I create the texture, set bounds, set touchable and center it on the screen.
Inside CenterMass class I also have an InputListener listening for events. I have an override set for touchDragged where I am trying to get the X and Y of the drag, and use that to set the rotate actions accordingly. That class looks like this:
//extend Image vs Actor classes
public class CenterMass extends Image {
public CenterMass(Texture centerMassSprite) {
//let parent be aware
super(centerMassSprite);
setBounds(getX(), getY(), getWidth(), getHeight());
setTouchable(Touchable.enabled);
setPosition(Gdx.graphics.getWidth()/2, Gdx.graphics.getHeight()/2);
setRotation(90f);
addListener(new InputListener(){
private int dragX, dragY;
private float duration;
private float rotateBy = 30f;
#Override
public void touchDragged(InputEvent event, float x, float y, int pointer) {
//get
float dX = (float)(x-dragX)/(float)Gdx.graphics.getWidth();
float dY = (float)(dragY-y)/(float)Gdx.graphics.getHeight();
duration = 1.0f; // 1 second
Actions.sequence(
Actions.parallel(
Actions.rotateBy(rotateBy, duration),
Actions.moveBy( dX, dY, duration)
)
);
}
});
}
#Override
protected void positionChanged() {
//super.positionChanged();
}
#Override
public void draw(Batch batch, float parentAlpha) {
//draw needs to be available for changing color and rotation, I think
batch.setColor(this.getColor());
//cast back to texture because we use Image vs Actor and want to rotate and change color safely
((TextureRegionDrawable)getDrawable()).draw(batch, getX(), getY(),
getOriginX(), getOriginY(),
getWidth(), getHeight(),
getScaleX(), getScaleY(),
getRotation());
}
#Override
public void act(float delta) {
super.act(delta);
}
}
The Problem:
I have not been able to get it to rotate the way I would like. I have been able to get it to shift around in unpredictable ways. Any guidance would be much appreciated.
As from you code it seems everything is good. except you don't set any origin of the image. without setting the origin it is by default set to 0,0.(bottom left of your image)
So if yow want to rotate the image with origin to centre you have to set the origin to imageWidth/2. imageHeight/2.
setOrigin(imageWidth/2,imageHeight/2)// something like this
My application is made with openGL ES 2.0 on android and i'm having a rather serious problem with object position updates. My game loop is like this, Draw all objects --> update game object positions --> iterate. However, i have 2 different kind of position updates: one kind that is calculation based, that when a value is low or high enough, it will change direction. The other kind is touch based, when the user touch at any position and/or swipes the screen, the object will follow.
Now to the problem. When the user touches the screen and/or swipes, the objects that is supposed to only respond to touch also gets the changing x value of the calculation based objects, and i'm clueless as to why this is since they use entirely different position variables.
The code that follows here is an excerpt from the GLsurfaceView showing how the touch position values are passed into the GL renderer
public boolean onTouchEvent(MotionEvent e){
if(e != null){
if(e.getAction() == MotionEvent.ACTION_DOWN){
x = e.getX();
y = e.getY();
if(_renderer != null){
queueEvent(new Runnable(){
#Override
public void run() {
_renderer.touchInput(x, y);
}
});
return true;
}
}
if(e.getAction() == MotionEvent.ACTION_MOVE){
x = e.getX();
y = e.getY();
if(_renderer != null){
queueEvent(new Runnable(){
#Override
public void run() {
_renderer.touchInput(x, y);
}
});
}
}
The code that follows here is an excerpt from the GL renderer that shows how the touch values enters the renderer and is converted to world coordinates.
public void touchInput(float x, float y){
y = (float)_view[3] - y;
GLU.gluUnProject(x, y, -1.5f, _ModelMatrix, 0, _ProjectionMatrix, 0, _view, 0, touch_to_world_coords, 0);
_world_x = touch_to_world_coords[0] * touch_to_world_coords[3];
_world_y = touch_to_world_coords[1] * touch_to_world_coords[3];
}
Next up is the code that calculates the new position of the non-touch object
public void updateObjectCoords(){
_test_x += 0.05f;
_test_y += 0.05f;
}
and finally the onDrawFrame from the renderer which im using as a game loop.
public void onDrawFrame(GL10 glContext) {
//Tell OpenGL to use this program when rendering.
GLES20.glUseProgram(_current_shaderProgramHandle);
GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
//Sets the active texture unit to texture unit 0
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
//Bind the texture to current unit
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, _TextureDataHandle);
//Tells texture uniform sampler to use this texture in the shader by binding it to unit 0
GLES20.glUniform1i(_TextureUniformHandle, 0);
//Draw square, gets coordinates from gluUnproject method
Matrix.setIdentityM(_ModelMatrix, 0);
Matrix.translateM(_ModelMatrix, 0, _world_x, _world_y, -1.0f);
drawSquare();
//Draw square, gets coordinates from updateObjectCoords method
Matrix.setIdentityM(_ModelMatrix, 0);
Matrix.translateM(_ModelMatrix, 0, _test_x, _test_y, -1.0f);
drawSquare();
//GLES20.glUseProgram(_point_shaderProgramHandle);
//drawLight();
updateObjectCoords();
}
edit: noticed my bad explenations so im adding this instead to be more clear what happens when :)
(When cube 1 gets touch input and cube 2 gets coordinates from updateObjectCoords):
Application starts:
everything work as expected, cube 2(updateObjectCoords one) moves as expected.
User touches screen:
cube2 continue to move as its supposed to, cube1(touch controlled) coordinate data seem to get mixed with cube2 as its movements become seemingly twice as large and towards the same direction as cube2, you can still manipulate it a bit but its not very responding.
(Cube2 is made to be static, however updateObjectCoords are still active and is called on every frame to update the value of _test_x and _test_y):
Application starts:
everything is just as expected
User touches screen:
everything works perfectly
So it seems that it is specificly the continually relocating of cube2 thats interfering with the positioning of cube1.
hope this have made matters more clear! :)
Why does the coordinates of one object to be drawn effect all other translateM's? How come all other objects doesnt get effected by the touch coordinates?
I fixed this by just switching the drawing of cube1 with cube2. This seems to have solved everything. Assuming that drawing the touch controlled objects last will end the interference :D
I want an action to be performed when the view is touched. However, the touches do not respond. The app doesn't crash, it just seems to ignore it.
public class CustomDrawableView extends View implements OnTouchListener
{
static final int width = 100;
static final int height = 50;
public CustomDrawableView(Context context)
{
super(context);
setFocusable(true);
setOnTouchListener(mCustomDrawableView);
mDrawable = new ShapeDrawable(new OvalShape());
mDrawable.getPaint().setColor(0xff74AC23);
mDrawable.setBounds(x, y, x + width, y + height);
}
#Override
public boolean onTouch(View v, MotionEvent event) {
if(event.getAction()==MotionEvent.ACTION_DOWN )
{
x = 400;
return true;
}
else {
x = 300;
return false;
}
}
protected void onDraw(Canvas canvas)
{
int mCanvasHeight = canvas.getHeight();
int mCanvasWidth = canvas.getWidth();
canvas.save();
canvas.rotate(R,x,y);
if (y >= mCanvasHeight-100) {
y = 0;
}
RectF oval = new RectF(x, y, x + width, y
+ height); // set bounds of rectangle
Paint p = new Paint(); // set some paint options
p.setColor(Color.BLUE);
canvas.drawOval(oval, p);
canvas.restore();
invalidate();
}
}
I have tried a bunch of different code to fix it. None of it does anything except if I change setOnTouchListener(mCustomDrawable) to mCustomDrawableView.setOnTouchListener(this) the app crashes. There is a bunch more code in the activity that I did not put up.
I'm guessing you believe that the onTouch() isn't firing because you're not seeing your graphics change in response to changes in x or whatever. If that's the case, it looks like you're missing a call to invalidate() in your touch handler to cause the View to redraw itself again (via a call to onDraw()).
Also, you have an invalidate() actually inside onDraw() itself which really shouldn't be there. It would certainly cause your View to redraw itself over and over - I suppose - so actually, I guess you should be seeing updates because that's there. But that isn't the way you should make a View animate; you should instead use a Thread or Handler to schedule a regular, periodic invalidate() - or some other means to regularly schedule an update.
Also there should be no need to implement 'OnTouchListener' when you can just override onTouch() as you have done. There are a couple of ways you can detect touch events for a View: (1) Override onTouch() as you have done, to get touch events on that View. (2) Register a listener using setOnTouchListener(). This latter option enables you to have a listener that listens to touch events from one View or multiple Views, and it also 'sees' touch events before a registered View's own onTouch() sees them.
Another thing I see is that you're setting x to 400 when you get an ACTION_DOWN event but then you're setting it to 300 for any other kind of event such as an ACTION_MOVE. Considering it's actually quite difficult to keep your finger still enough to never cause a string of ACTION_MOVE events immediately after an ACTION_DOWN, perhaps you're just never seeing the graphics when x is at 400 or something.
You really need to post a complete example (for instance your code as you've posted has setOnTouchListener(mCustomDrawableView) where mCustomDrawableView has not been defined anywhere) together with specific errors from Logcat. Also, you should use the debugger to see if your onTouch ever executes (stick a breakpoint in it).
Also, you should accept some answers.