I'm a little bit confused. I'm using libgdx for the first time and i have some problems with the coordinate systems. When i'm creating a texture and want to set the position, I do:
texture = new Texture("myGraphic.png", 0, 0);
and my picture will be positioned at the left bottom corner.
But when I try to get the touch position with:
if(Gdx.input.isTouched())
{
Vector3 tmp = new Vector3(Gdx.input.getX(),Gdx.input.getY(),0);
System.out.println("Coord:" + " + " + tmp.x + " + " + tmp.y);
}
I recognized that (0,0) is in the left top corner.
So I tried camera.unproject(tmp) before my output, but then i will get only values between -1 and 1.
How do i get the same coordinate system for all elements?
In Libgdx coordinate system for touch is y-down but for Screen or Image it's y-up.
Take a look of LibGDX Coordinate systems
If can use camera, set y-axis pointing up by camera.setToOrtho(false); and get world point by camera.unproject(vector3); method.
public class TouchSystem extends ApplicationAdapter {
SpriteBatch batch;
Texture texture;
Vector3 vector3;
OrthographicCamera camera;
#Override
public void create() {
batch=new SpriteBatch();
texture=new Texture("badlogic.jpg");
vector3=new Vector3();
camera=new OrthographicCamera();
camera.setToOrtho(false); // this will set screen resolution as viewport
}
#Override
public void render() {
Gdx.gl.glClearColor(0,0,0,1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.begin();
batch.draw(texture,0,0);
batch.end();
if(Gdx.input.justTouched()) {
vector3.set(Gdx.input.getX(),Gdx.input.getY(),0);
camera.unproject(vector3);
System.out.println("Coord:" + " + " + vector3.x + " + " + vector3.y);
}
}
#Override
public void dispose() {
texture.dispose();
batch.dispose();
}
}
Related
I have two line graphs that show complementary data over the same time period. Is there any way to send any touch events received by one graph to the other? I essentially want it so that the graphs always show the same viewing rectangle (at least on horizontally). So if a user swipes left 'n' units on the top graph, the bottom graph will automatically scroll left 'n' units to match.
Zooms can be done with the current MPAndroidChart release, for scrolling check out or wait for my extension to be merged:
https://github.com/PhilJay/MPAndroidChart/pull/545
You need to set up an OnChartGestureListener on the master chart that copies the translation matrix values to the slave charts:
public class CoupleChartGestureListener implements OnChartGestureListener {
private Chart srcChart;
private Chart[] dstCharts;
public CoupleChartGestureListener(Chart srcChart, Chart[] dstCharts) {
this.srcChart = srcChart;
this.dstCharts = dstCharts;
}
[...other overrides...]
#Override
public void onChartScale(MotionEvent me, float scaleX, float scaleY) {
//Log.d(TAG, "onChartScale " + scaleX + "/" + scaleY + " X=" + me.getX() + "Y=" + me.getY());
syncCharts();
}
#Override
public void onChartTranslate(MotionEvent me, float dX, float dY) {
//Log.d(TAG, "onChartTranslate " + dX + "/" + dY + " X=" + me.getX() + "Y=" + me.getY());
syncCharts();
}
public void syncCharts() {
Matrix srcMatrix;
float[] srcVals = new float[9];
Matrix dstMatrix;
float[] dstVals = new float[9];
// get src chart translation matrix:
srcMatrix = srcChart.getViewPortHandler().getMatrixTouch();
srcMatrix.getValues(srcVals);
// apply X axis scaling and position to dst charts:
for (Chart dstChart : dstCharts) {
if (dstChart.getVisibility() == View.VISIBLE) {
dstMatrix = dstChart.getViewPortHandler().getMatrixTouch();
dstMatrix.getValues(dstVals);
dstVals[Matrix.MSCALE_X] = srcVals[Matrix.MSCALE_X];
dstVals[Matrix.MTRANS_X] = srcVals[Matrix.MTRANS_X];
dstMatrix.setValues(dstVals);
dstChart.getViewPortHandler().refresh(dstMatrix, dstChart, true);
}
}
}
}
Then set up your master/slave connections for example like this:
//
// Couple chart viewports:
//
tripChart.setOnChartGestureListener(new CoupleChartGestureListener(
tripChart, new Chart[] { powerChart, energyChart }));
powerChart.setOnChartGestureListener(new CoupleChartGestureListener(
powerChart, new Chart[] { tripChart, energyChart }));
energyChart.setOnChartGestureListener(new CoupleChartGestureListener(
energyChart, new Chart[] { tripChart, powerChart }));
You need add this to CoupleChartGestureListener
#Override
public void onChartGestureStart(MotionEvent me, ChartTouchListener.ChartGesture lastPerformedGesture) {
((BarLineChartTouchListener) srcChart.getOnTouchListener()).stopDeceleration();
for (Chart dstChart : dstCharts) {
if (dstChart.getVisibility() == View.VISIBLE) {
((BarLineChartTouchListener) dstChart.getOnTouchListener()).stopDeceleration();
}
}
}
when setting up the sprite buttons in my libgdx project, i get displaced their touching area if i give a screen size that is bigger than my device one.
This is a simple example of the code i wrote. The main class extends Game...
public class Application extends Game
{
public SpriteBatch batch;
public OrthographicCamera camera;
public Vector3 touchPosition;
protected TitleScreen screen_title;
protected SettingsScreen screen_settings;
public PlayScreen screen_play;
#Override
public void create()
{
batch = new SpriteBatch();
camera = new OrthographicCamera(Constants.VIEWPORT_WIDTH, Constants.VIEWPORT_HEIGHT);
camera.setToOrtho(false,Constants.VIEWPORT_WIDTH, Constants.VIEWPORT_HEIGHT);
touchPosition = new Vector3();
//SCREENS
screen_title = new TitleScreen(this);
screen_settings = new SettingsScreen(this);
screen_play = new PlayScreen(this);
this.setScreen(screen_title);
}
}
And a simple screen with an image to detect if is touched...
public TitleScreen(final Application game)
{
this.game = game;
game.camera = new OrthographicCamera(Constants.VIEWPORT_WIDTH, Constants.VIEWPORT_HEIGHT);
game.camera.position.set(Constants.VIEWPORT_WIDTH / 2, Constants.VIEWPORT_HEIGHT / 2, 0);
background = new Sprite(new Texture(Gdx.files.internal(Constants.PATH_BACKGROUND + "bg.png")));
img = new Sprite(new Texture(Gdx.files.internal("badlogic.jpg")));
}
#Override
public void render(float delta)
{
Gdx.gl.glClearColor(0.7f, 0.7f, 0.9f, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
game.camera.update();
game.batch.setProjectionMatrix(game.camera.combined);
game.batch.begin();
background.draw(game.batch);
img.setPosition( Constants.VIEWPORT_WIDTH / 2, Constants.VIEWPORT_HEIGHT /2);
img.draw(game.batch);
game.batch.end();
//detect touch position
if(Gdx.input.justTouched())
{
game.touchPosition.set(Gdx.input.getX(), Gdx.input.getY(), 0);
game.camera.unproject(game.touchPosition);
if ((Gdx.input.getX() > img.getX()) &&
Gdx.input.getX() < img.getX() + img.getWidth() &&
Gdx.input.getY() < Gdx.graphics.getHeight() - img.getY() &&
Gdx.input.getY() > Gdx.graphics.getHeight() - (img.getY() + img.getHeight()))
{
System.out.println("TOUCHED BUTTON!!!");
System.out.println("X: " + Gdx.input.getX() + "/ Y: " + Gdx.input.getY());
}
}
}
As mentioned, this code sample works fine in desktop (any size) and my phone (if it has same or smaller screen size), but not when i use a bigger viewport size, and don't really understand why :)
Thanks a lot for the help :)
Presently I'm trying to implement simple game using libgdx for Android Platform. I have implemented the game but don't know how to pause and resume game based on user input.Kindly suggest idea as well as some practical code to implement the same.I am using simple game code demonstrated in libgdx library. Thank you.
Here is the code :
public class Drop implements ApplicationListener {
Texture dropImage;
Texture bucketImage;
Sound dropSound;
Music rainMusic;
SpriteBatch batch;
OrthographicCamera camera;
Rectangle bucket;
Array<Rectangle> raindrops;
long lastDropTime;
#Override
public void create() {
// load the images for the droplet and the bucket, 64x64 pixels each
dropImage = new Texture(Gdx.files.internal("droplet.png"));
bucketImage = new Texture(Gdx.files.internal("bucket.png"));
// load the drop sound effect and the rain background "music"
dropSound = Gdx.audio.newSound(Gdx.files.internal("drop.wav"));
rainMusic = Gdx.audio.newMusic(Gdx.files.internal("rain.mp3"));
// start the playback of the background music immediately
rainMusic.setLooping(true);
rainMusic.play();
// create the camera and the SpriteBatch
camera = new OrthographicCamera();
camera.setToOrtho(false, 800, 480);
batch = new SpriteBatch();
// create a Rectangle to logically represent the bucket
bucket = new Rectangle();
bucket.x = 800 / 2 - 64 / 2; // center the bucket horizontally
bucket.y = 20; // bottom left corner of the bucket is 20 pixels above the bottom screen edge
bucket.width = 64;
bucket.height = 64;
// create the raindrops array and spawn the first raindrop
raindrops = new Array<Rectangle>();
spawnRaindrop();
}
private void spawnRaindrop() {
Rectangle raindrop = new Rectangle();
raindrop.x = MathUtils.random(0, 800-64);
raindrop.y = 480;
raindrop.width = 64;
raindrop.height = 64;
raindrops.add(raindrop);
lastDropTime = TimeUtils.nanoTime();
}
#Override
public void render() {
// clear the screen with a dark blue color. The
// arguments to glClearColor are the red, green
// blue and alpha component in the range [0,1]
// of the color to be used to clear the screen.
Gdx.gl.glClearColor(0, 0, 0.2f, 1);
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
// tell the camera to update its matrices.
camera.update();
// tell the SpriteBatch to render in the
// coordinate system specified by the camera.
batch.setProjectionMatrix(camera.combined);
// begin a new batch and draw the bucket and
// all drops
batch.begin();
batch.draw(bucketImage, bucket.x, bucket.y);
for(Rectangle raindrop: raindrops) {
batch.draw(dropImage, raindrop.x, raindrop.y);
}
batch.end();
// process user input
if(Gdx.input.isTouched()) {
Vector3 touchPos = new Vector3();
touchPos.set(Gdx.input.getX(), Gdx.input.getY(), 0);
camera.unproject(touchPos);
bucket.x = touchPos.x - 64 / 2;
}
if(Gdx.input.isKeyPressed(Keys.LEFT)) bucket.x -= 200 * Gdx.graphics.getDeltaTime();
if(Gdx.input.isKeyPressed(Keys.RIGHT)) bucket.x += 200 * Gdx.graphics.getDeltaTime();
// make sure the bucket stays within the screen bounds
if(bucket.x < 0) bucket.x = 0;
if(bucket.x > 800 - 64) bucket.x = 800 - 64;
// check if we need to create a new raindrop
if(TimeUtils.nanoTime() - lastDropTime > 1000000000) spawnRaindrop();
// move the raindrops, remove any that are beneath the bottom edge of
// the screen or that hit the bucket. In the later case we play back
// a sound effect as well.
Iterator<Rectangle> iter = raindrops.iterator();
while(iter.hasNext()) {
Rectangle raindrop = iter.next();
raindrop.y -= 200 * Gdx.graphics.getDeltaTime();
if(raindrop.y + 64 < 0) iter.remove();
if(raindrop.overlaps(bucket)) {
dropSound.play();
iter.remove();
}
}
}
#Override
public void dispose() {
// dispose of all the native resources
dropImage.dispose();
bucketImage.dispose();
dropSound.dispose();
rainMusic.dispose();
batch.dispose();
}
#Override
public void resize(int width, int height) {
}
#Override
public void pause() {
}
#Override
public void resume() {
}
}
The most simple thing is, that you add an Enum:
public enum State
{
PAUSE,
RUN,
RESUME,
STOPPED
}
And modify your rendermethod
private State state = State.RUN;
#Override
public void render()
{
switch (state)
{
case RUN:
//do suff here
break;
case PAUSE:
//do stuff here
break;
case RESUME:
break;
default:
break;
}
}
Use the callback from android too. For example use the callbacks from libgdx to change the state:
#Override
public void pause()
{
this.state = State.PAUSE;
}
#Override
public void resume()
{
this.state = State.RESUME;
}
Also add something like a setMethod for the state so you can change it on userevent.
public void setGameState(State s){
this.state = s;
}
You can use states, with enums or constants. and only update in the Running state. Something like this:
public enum State{
Running, Paused
}
State state = State.Running;
#Override
public void render(){
switch(state){
case Running:
update();
break;
case Paused:
//don't update
break;
}
draw();
}
public void update(){
//your update code
}
public void draw(){
//your render code
}
I don't have the rep to reply to comments, but the flickering of items is something I ran into before.
You are no longer clearing the screen and redrawing the sprites in your render when its paused right? If I am not mistaken if you glClear and redraw the srpites in every render i think it will stop
Even more simple, see here: https://github.com/libgdx/libgdx/wiki/Continuous-&-non-continuous-rendering
In your ApplicationListener's create method, just put
Gdx.graphics.setContinuousRendering(false);
Gdx.graphics.requestRendering();
Set up a boolean
public boolean paused = false;
Then for example a button listener to pause/resume on a button press
buttonPause.addListener(new ChangeListener() {
public void changed (ChangeEvent event, Actor actor) {
// set paused true or false here
}
});
Then right at the end of your render method, just add:
if (!paused) {
Gdx.graphics.requestRendering();
}
The render method is now paused/resumed.
I tried using this approach:
I made GAME_PAUSED a boolean , If it is true don't update your screen or camera . Otherwise update Camera .
boolean GAME_PAUSED = false;
if (GAME_PAUSED) {
//Do not update camera
batch.begin();
resumeButton.draw(batch);
batch.end();
} else {
//Update Camera
Gdx.gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
world.step(1/60f, 8, 3);
camera.update();
debugRenderer.render(world, camera.combined);
//Do your game running
}
I'm having a hard time making the chase camera follow the a car body. I am tinkering the Racer Game example project. The Tile Map is 1024 x 786 and the Camera is set to chase the Car Body. Here's the code:
#Override
public Scene onCreateScene() {
this.mEngine.registerUpdateHandler(new FPSLogger());
this.mScene = new Scene();
//this.mScene.setBackground(new Background(0, 0, 0));
/** Tiled Map Test **/
try {
final TMXLoader tmxLoader = new TMXLoader(this.getAssets(), this.mEngine.getTextureManager(), TextureOptions.BILINEAR_PREMULTIPLYALPHA,
this.getVertexBufferObjectManager(), new ITMXTilePropertiesListener() {
#Override
public void onTMXTileWithPropertiesCreated(final TMXTiledMap pTMXTiledMap, final TMXLayer pTMXLayer, final TMXTile pTMXTile,
final TMXProperties<TMXTileProperty> pTMXTileProperties) {
/* We are going to count the tiles that have the property "box=true" or "boxBool=true" set. */
if(pTMXTileProperties.containsTMXProperty("box", "true")) {
SpeedsterGameActivity.this.numBoxes++;
}
}
});
// Load the TMX file into an Object
this.mTMXTiledMap = tmxLoader.loadFromAsset("tmx/level3.tmx");
this.runOnUiThread(new Runnable() {
#Override
public void run() {
Toast.makeText( SpeedsterGameActivity.this, "Box count in this TMXTiledMap: " + SpeedsterGameActivity.this.numBoxes, Toast.LENGTH_LONG).show();
}
});
} catch (final TMXLoadException e) {
Debug.e(e);
}
// Get the first TMX Layer and add it to the scene
final TMXLayer tmxLayer = this.mTMXTiledMap.getTMXLayers().get(0);
this.mScene.attachChild(tmxLayer);
/* Make the camera not exceed the bounds of the TMXEntity. */
this.mBoundChaseCamera.setBounds(0, 0, tmxLayer.getHeight(), tmxLayer.getWidth());
this.mBoundChaseCamera.setBoundsEnabled(true);
/* Debugging stuff */
Debug.i( "Game Info", "Height & Width: " + tmxLayer.getHeight() + " x " + tmxLayer.getWidth() );
int[] maxTextureSize = new int[1];
GLES20.glGetIntegerv( GLES20.GL_MAX_TEXTURE_SIZE, maxTextureSize, 0);
Debug.i("Game Info", "Max texture size = " + maxTextureSize[0]);
/**********/
/* Calculate the coordinates for the face, so its centered on the camera. */
final float centerX = (CAMERA_WIDTH - this.mVehiclesTextureRegion.getWidth()) / 2;
final float centerY = (CAMERA_HEIGHT - this.mVehiclesTextureRegion.getHeight()) / 2;
/* Create the sprite and add it to the scene. */
final AnimatedSprite player = new AnimatedSprite(centerX, centerY, this.mVehiclesTextureRegion, this.getVertexBufferObjectManager());
this.mBoundChaseCamera.setChaseEntity(player);
/********************/
this.mPhysicsWorld = new FixedStepPhysicsWorld(30, new Vector2(0, 0), false, 8, 1);
//this.initRacetrack();
//this.initRacetrackBorders();
this.initCar();
this.initObstacles();
this.initOnScreenControls();
this.mScene.registerUpdateHandler(this.mPhysicsWorld);
}
A possible cause of the problem is that your camera size is 1024x786 too, therefore the full camera rectangle is shown and since you enabled bounds, the camera does not follow the car.
Omit the line this.mBoundChaseCamera.setBoundsEnabled(true);.
Another problem is - the camera follows the player object to which you lose the reference once onCreateScene finishes executing. You are not connecting the player object to a physics body using PhysicsConnector class, so it has no reason to move.
Otherwise if the car body & entity are created in initCarmethod, you are not setting the car as the chase entity.
I'm creating an android app using Andengine. One part of the app requires users to select a few sprites from a group of sprites on the screen, which causes the selected sprites to turn a different color (ie, moving to the next tile). I declared them all as animated sprites and I'm using the same texture for each one. The problem is that once I select a sprite, every sprite moves to the next tile, not just the one I selected. How do I make just the one sprite change?
Here's where I setup the textures and whatnot:
private Texture mGreenTextureAtlas;
private TiledTextureRegion mGreenBallFaceTextureRegion;
#Override
public void onLoadResources() {
/* Textures. */
...
this.mGreenTextureAtlas = new Texture(32, 32, TextureOptions.BILINEAR_PREMULTIPLYALPHA);
...
TextureRegionFactory.setAssetBasePath("gfx/");
/* TextureRegions. */
...
this.mGreenBallFaceTextureRegion = TextureRegionFactory.createTiledFromAsset(this.mGreenTextureAtlas, this, "green_ball.png", 0, 16, 2, 1); // 64x32
this.mEngine.getTextureManager().loadTextures(this.mCueTextureAtlas, this.mGreenTextureAtlas , this.mBackgroundTexture, this.mPocketTexture);
}
Here's where I actually create the sprites and apply the textures:
face = new AnimatedSprite(pX, pY, this.mGreenBallFaceTextureRegion);
body = PhysicsFactory.createCircleBody(this.mPhysicsWorld, face, BodyType.DynamicBody, FIXTURE_DEF);
encapsed = new Encapsulator(body, face, Encapsulator.AVOID_BALL, mFaceCount);
ballsList.add(encapsed);
I encapsulate each sprite, it's body, and some other data into an object that I made, and then add that object into an ArrayList.
Here is the onTouch event handler.
#Override
public boolean onAreaTouched( final TouchEvent pSceneTouchEvent, final ITouchArea pTouchArea,final float pTouchAreaLocalX, final float pTouchAreaLocalY) {
if(pSceneTouchEvent.isActionDown()) {
final AnimatedSprite face = (AnimatedSprite) pTouchArea;
for(int i=0; i<ballsList.size(); i++)
{
if(face.equals(ballsList.get(i).animatedFace))
{
ballsList.get(i).toggleType(face);
System.out.println("Ball " + ballsList.get(i).id + " is now " + ballsList.get(i).type);
}
}
return true;
}
return false;
}
Finally, here is the toggleType method in the Encapsulator class that's responsible for moving to the next tile:
public void toggleType(AnimatedSprite face)
{
if(this.type == AVOID_BALL)
{
this.type = HIT_BALL;
face.nextTile();
}
else if(this.type == HIT_BALL)
{
this.type = AVOID_BALL;
face.setCurrentTileIndex(0);
}
}
Sorry if this is a bit long-winded. Any help is appreciated.
I did some more googling and came across a solution. I had to use the textureregion.clone() method when creating the sprites. I found the solution at this link:
http://www.andengine.org/forums/development/two-sprites-sharing-the-same-tiledtextureregion-t4339.html