I want to make a button clickable, but it isn't working - it seems like I need to use unproject() but I can't figure out how. The code in question is:
Texture playButtonImage;
SpriteBatch batch;
ClickListener clickListener;
Rectangle playButtonRectangle;
Vector2 touchPos;
OrthographicCamera camera;
#Override
public void show() {
playButtonImage = new Texture(Gdx.files.internal("PlayButton.png"));
camera = new OrthographicCamera();
camera.setToOrtho(false, 800, 480);
batch = new SpriteBatch();
playButtonRectangle = new Rectangle();
playButtonRectangle.x = 400;
playButtonRectangle.y = 250;
playButtonRectangle.width = 128;
playButtonRectangle.height = 64;
}
#Override
public void render(float delta) {
Gdx.gl.glClearColor(0, 0, 0.2f, 1);
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
camera.update();
batch.setProjectionMatrix(camera.combined);
batch.begin();
batch.draw(playButtonImage, playButtonRectangle.x, playButtonRectangle.y);
batch.end();
if (Gdx.input.isTouched()) {
Vector2 touchPos = new Vector2();
touchPos.set(Gdx.input.getX(), Gdx.input.getY());
if (playButtonRectangle.contains(touchPos)) {
batch.begin();
batch.draw(playButtonImage, 1, 1);
batch.end();
}
}
}
In general you use camera.unproject(Vector) to transform your screen coordinates from a click or touch to your gameworld. This is needed because the origin is not necessarily the same and using the camera you can also zoom in and out, move around, rotate and so on. Unprojecting will take care of all of that and give you your game world coordinate matching the pointers position.
In your example it would go like this:
Vector3 touchPos = new Vector3(Gdx.input.getX(), Gdx.input.getY(), 0);
camera.unproject(touchPos);
Having this said you should actually not do this UI task manually. Libgdx has also some shipped UI functionality which is called a Stage (see this). There are lots of widgets already available (see this). They use skins (you can get a basic one from here, you need all the uiskin.* files). They automatically forward inputevents to the socalled Actors, e.g. a button, and you just need to implement handling of those events.
with camera.unproject(Vector3); you can translate screen coordinates to game world coordinates.
Vector3 tmpCoords = new Vector3(Gdx.input.getX(), Gdx.input.getY(), 0);
camera.unproject(tmpCoords);
tmpCoords.x //is now the touched X coordinate in the game world coordinate system
tmpCoords.y //is now the touched Y coordinate in the game world coordinate system.
In addition to that it is best practice to define a tmpVector as a field an Instantiate a Vector3 object only once. You can then do the same with the .set() method.
tmpCoords.set(Gdx.input.getX(), Gdx.input.getY(), 0);
camera.unproject(tmpCoords);
tmpCoords.x //is now the touched X coordinate in the game world coordinate system
tmpCoords.y //is now the touched Y coordinate in the game world coordinate system.
Therefore you reduce object creation and remove unnecessary GC calls which lead to microlag.
#Override
public void render(float delta) {
Gdx.gl.glClearColor(0, 0, 0.2f, 1);
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
camera.update();
batch.setProjectionMatrix(camera.combined);
batch.begin();
batch.draw(playButtonImage, playButtonRectangle.x, playButtonRectangle.y);
batch.end();
if (Gdx.input.isTouched()) {
Vector3 touchPos = new Vector3();
touchPos.set(Gdx.input.getX(), Gdx.input.getY(),0);
camera.unproject(touchPos);
if (playButtonRectangle.contains(touchPos.x, touchPos.y)) {
batch.begin();
batch.draw(playButtonImage, 1, 1);
batch.end();
}
}
}
when ever u need a touch point from user and to convert those touchpoints to camera u need to unproject them to camera coordinates by camera coordinates
camera.unproject(touchPos.set(Gdx.input.getX(), Gdx.input.getY(), 0));
Related
I'm using a GestureListener to try and create a panning menu, but even though the camera's coordinates are being changed and it's being updated, nothing moves. I think it might be because the stage is drawing relative to the camera but I'm not sure how I would change that.
How I'm going about it:
camera = new OrthographicCamera(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
...
gesture = new GestureDetector(this);
InputMultiplexer im = new InputMultiplexer();
im.addProcessor(gesture);
im.addProcessor(stage);
Gdx.input.setInputProcessor(im);
} //end initialization of CustomizeScreen
public void render(float delta) {
Gdx.gl.glClearColor(0.2f, 0.2f, 0.2f, 1);
Gdx.gl.glClear(GL30.GL_COLOR_BUFFER_BIT);
stage.act(Math.min(Gdx.graphics.getDeltaTime(), 1 / 60f)); //0, 18, 31 for layer style
stage.draw();
Table.drawDebug(stage);
batch.setProjectionMatrix(camera.combined);
g.batch.begin();
g.font.setScale(2);
g.font.setColor(Color.WHITE);
g.font.draw(g.batch, "Coins: " + Filer.getCoins(), 15, Gdx.graphics.getHeight() - 15);
g.font.draw(g.batch, message, 0, 150);
g.batch.end();
camera.update();
}
...
#Override
public boolean pan(float x, float y, float deltaX, float deltaY) {
message = "Pan, delta:" + Float.toString(deltaX) +
"," + Float.toString(deltaY);
camera.translate(deltaX, 0, 0);
camera.update();
return false;
}
I've checked for camera coordinates (camera.position.x) in the update method, so I know it's actually changing something. But all my buttons remain the same. They are placed in thirds of the screen width (Gdx.graphics.getWidth() / 3).
Am I missing something, or doing it wrong? I'd think I'm doing it correctly based on everything I'm found on Google.
Thanks :)
The Stage class has different constructors. Some of those constructors take a Viewport, which contains a Camera. If you use a constructor without Viewport, the Stage creates a new Viewport with a new Camera.
In that case it will use the created Viewport (and Camera) for all it's drawing, unless you switch the Viewport by calling setViewport.
As you don't show, how you create the Stage i am not sure if thats the porblem in your case, but if that is the problem, you can solve it by creating a Viewport with your Camera and passing it to the Stage or by changing the Camera of the Viewport (stage.getViewport().setCamera(myCamera)).
Hope it helps.
pretty much i want my texture to move to a random position on the screen when its touched and when its missed i want to system.out("missed"). I cant figure out how to see if its touched. right now i can only get if the screen is touch and it records about 10 touches for every one touch because it renders so fast.
public void render(float delta) {
Gdx.gl.glClearColor(0,1,0,1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
camera.update();
game.batch.setProjectionMatrix(camera.combined);
game.batch.begin();
if(Gdx.input.isTouched()) {
int randomX2 = (int)MathUtils.random(100,500);
int randomY2 = (int)MathUtils.random(100,500);
game.batch.draw(boxImage, randomX2, randomY2);
}
game.batch.end();
}
If randomX2 and randomY2 are your coordinates for the texture, you can check with this code:
Rectangle bounds = new Rectangle(randomX2, randomY2, boxImage.getWidth(), boxImage.getHeight());
Vector3 tmp = new Vector3(Gdx.input.getX(), Gdx.input.getY(), 0);
camera.unproject(tmp);
if (bounds.contains(tmp.x, tmp.y)) {
System.out.println("Is touched");
}
I could solve this without using Vector3.
var upArrowCircle = Circle(randomX, randomY, radius)
if(upArrowCircle.contains(Gdx.input.getX().toFloat(), Gdx.input.getY().toFloat()))
{
Gdx.app.log("upArrow", "contains")
}
For me it works perfectly.
I'm creating a 3D game but I don't know how to translate the camera according to my fingers. I'm creating a map(x = -30 to +30;y = -30 to 30;z = -1 to 1) where every coordinate is used for a model using .g3db files from my assets and put in place using model translation. This works so far, the map looks good and is viewed in a 67% angle. The map is so large it can't be viewed in the camera at once(no I don't want to zoom out). Whenever I'm touching the screen of my android phone it's just rotating around the center, but instead I want the gamemap to stay in place from my point of view and instead change the position of the perspective camera on the x and y axis. There's no game object that can be used for position tracking, everything should be depending on my finger actions. This way I want to move to visible screen to each x and y direction(think of it visualy like a 2D camera moving up, down and to the sides in front of a picture). Can you help me?
This is my code so far:
public class MyGdxGame extends ApplicationAdapter implements ApplicationListener{
//define variables
...
#Override
public void create() {
environment = new Environment();
environment.set(new ColorAttribute(ColorAttribute.AmbientLight, 0.4f, 0.4f, 0.4f, 1f));
environment.add(new DirectionalLight().set(0.8f, 0.8f, 0.8f, -1f, -0.8f, -0.2f));
modelBatch = new ModelBatch();
cam = new PerspectiveCamera(67, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
cam.position.set(10f, 10f, 10f);
cam.lookAt(0,0,0);
cam.near = 1f;
cam.far = 300f;
cam.update();
assets = new AssetManager();
//load assets
...
loading = true;
camController = new CameraInputController(cam);
camController.forwardTarget = true;
Gdx.input.setInputProcessor(camController);
}
private void doneLoading() {
//load models and add them to instances
loading = false
}
#Override
public void render() {
if (loading && assets.update())
doneLoading();
camController.update();
Gdx.gl.glViewport(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);
modelBatch.begin(cam);
modelBatch.render(instances, environment);
modelBatch.end();
}
I'm using the spring tool suite 3.5.1 as IDE and libgdx 1.0.1
Dont use a CameraInputController, but instead implement InputProcessor and use that class in the call to Gdx.input.setInputProcessor.
For example:
public class MyGdxGame extends ApplicationAdapter implements InputProcessor {
And in the create method:
Gdx.input.setInputProcessor(this);
You'll have to implement all methods as shown in this link, except for the touchDown and touchDragged method which require additional code:
private int dragX, dragY;
#Override
public boolean touchDown (int x, int y, int pointer, int button) {
dragX = x;
dragY = y;
return true;
}
#Override
public boolean touchDragged (int x, int y, int pointer) {
float dX = (float)(x-dragX)/(float)Gdx.graphics.getWidth();
float dY = (float)(dragY-y)/(float)Gdx.graphics.getHeight();
dragX = x;
dragY = y;
cam.position.add(dX * 10f, dY * 10f, 0f);
cam.update();
return true;
}
Here the dX and dY values is the amount that user has dragged on the screen within the range between -1f and +1f. E.g. for dX a value of 0.5f means that the user dragged half the screen size to the right. Note that these are delta values since the last time the method was called. The method is likely to be called many times per drag operation. Therefor, the dX and dY will be small values.
The call to cam.position.add(...) will actually move the camera and the call to cam.update(); will recalculate the values needed for rendering. I've used a multiplier of 10f for both the X and Y translation of the camera. This means that if the user fully drags from the left edge to the right edge of the screen, that the camera will move a total amount of 10 units to the right. You can adjust the value as needed.
Note that this moves the camera on XY plane. If you need to move on e.g. the XZ plane, you need to adjust the call the cam.position.add(...) accordingly. Also, this doesn't change the direction of the camera. If you want to move the camera, while looking at the same location, you'll need to add cam.lookAt(0,0,0); just before cam.update();.
I've been trying to create a game where there's a person falling. The size of my background texture is 480x3200, and I'm trying to create the game so that the camera keeps the person in the middle of the screen while falling down, and then stops at the bottom. But I can't get my background is extend beyond the screen it starts in, and then be able to see the rest of the image.
All of my code does right now is scale down the 480x3200 image down to fit onto my current screen (which I set to be 480x800), and then as the person falls, the background doesn't change.
Here's my WorldRenderer class where I've tried to do different things but every time, I can never get the person to see a different part of the image when it starts moving down.
public WorldRenderer(SpriteBatch b, World w) {
this.world = w;
this.cam = new OrthographicCamera(CAMERA_WIDTH, CAMERA_HEIGHT);
this.cam.position.set(CAMERA_WIDTH / 2, Person.position.y,0);
this.cam.setToOrtho(false, CAMERA_WIDTH, CAMERA_HEIGHT);
this.cam.update();
spriteBatch = b;
loadTextures();
}
public void render(float delta) {
person = world.getPerson();
moveCamera();
cam.update();
spriteBatch.setProjectionMatrix(cam.combined);
spriteBatch.disableBlending();
spriteBatch.begin();
renderBackground();
spriteBatch.end();
spriteBatch.enableBlending();
spriteBatch.begin();
renderObjects();
spriteBatch.end();
}
private void moveCamera() {
cam.position.set(cam.position.x, Person.position.y, 0);
cam.update();
}
private void renderObjects() {
renderPerson();
renderBlocks();
renderPlatforms();
}
private void renderBackground() {
spriteBatch.draw(backgroundTexture, cam.position.x - CAMERA_WIDTH / 2, cam.position.y - CAMERA_HEIGHT / 2, CAMERA_WIDTH, CAMERA_HEIGHT);
}
}
Does anyone have any suggestions?
Edit:
Thanks, I changed the draw in renderBackground to
spriteBatch.draw(backgroundTexture,0,0, CAMERA_WIDTH, CAMERA_HEIGHT * 4); and it works now.
spriteBatch.draw(backgroundTexture, cam.position.x - CAMERA_WIDTH / 2,
cam.position.y - CAMERA_HEIGHT / 2, CAMERA_WIDTH, CAMERA_HEIGHT);
This code is drawing the background image relative to the camera's position. That's why changing the camera's position has no effect on the background image's position. Change it to something like this:
spriteBatch.draw(backgroundTexture,0,0);
Or u can simply use ParrallaxBackground and ParrallaxLayer class
This way u dont have to manage ur camera
Its done in an optimized manner in the mentioned class.
I am new and I just start my journey with libgdx. I would like to know how I can do image.png in resolution 960x640 as background in my game? This is possible? Thx for advices and forbearance. Maybe you hava a simply tutorial?
This is my render class:
public void render() {
texture = new Texture(Gdx.files.internal("E:/background.png"));
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
batch.begin();
batch.draw(texture, 0, 0);
batch.end();
}
Second question. I need to insert two active images, active means when I click on that image, the next image show me on the screen. I want to implement action when I click on that picture.
In your create() method, create a new Texture referencing your image.png, and then use your existing SpriteBatch to render it in the render() loop. Immediately after your GL.clear() call, go your batch.draw(backgroundTexture, 0. 0) and make sure you're in OrthographicProjection mode for your camera.
first you have to set the view port
do this in your create method
`float scrw = 960;
float scrh = 640;
camera = new OrthographicCamera();
camera.viewportHeight = scrh;
camera.viewportWidth = scrw;
camera.position.set(camera.viewportWidth * .5f,
camera.viewportHeight * .5f, 0f);
camera.update();`
create a texture
texture = new Texture("data/background.png");
put this texture in a sprite like this
sprite=new sprite(texture);
and then set the size like this
sprite.setsize(960,640);
and draw it in your render methods between batch.begin
and batch.end
sprite.draw(batch);