I want to program a game that is like the old "Warfare 1917" browser games. I am stuck on the scrolling part.I'll try to explain what I want to with a gif:
I have searched and tried everything the whole day but I am not able to find any real solutions to this. I hope you can understand what I am trying to do.
Since moving camera is a bad practice, better moving a Layer (the container of your sprites) or you can try ScrollPane, the implementation of the WidgetGroup
See https://libgdx.badlogicgames.com/ci/nightlies/docs/api/com/badlogic/gdx/scenes/scene2d/ui/ScrollPane.html
p.s. tested on v. 1.9.11
I think this should do the trick
public class MovingCamera extends InputAdapter {
OrthographicCamera camera; // The camera to be moved
float pivotX; // The pivot for the movement
public MovingCamera() {
camera = new OrthographicCamera(); // Initialize camera
}
// Create a pivot
#Override
public boolean touchDown(int screenX, int screenY, int pointer, int button) {
Vector3 unprojected = camera.unproject(new Vector3(screenX, screenY, 0)); // Convert from pixel to world coordinates
pivotX = unprojected.x; // Save first finger touch on screen (Will serve as a pivot)
return true; // Input has been processed
}
// Move the camera
#Override
public boolean touchDragged(int screenX, int screenY, int pointer) {
Vector3 unprojected = camera.unproject(new Vector3(screenX, screenY, 0)); // Convert from pixel to world coordinates
camera.position.x += unprojected.x - pivotX; // Change camera position
camera.update(); // Apply changes
return true; // Input has been processed
}
}
And in your render method:
public void render(SpriteBatch spriteBatch) {
spriteBatch.setProjectionMatrix(camera.combined); // Let the Sprite Batch use the camera
spriteBatch.begin();
// [Draw your Textures, TextureRegions, Sprites, etc...]
spriteBatch.end();
}
Related
I'm building a mobile Box2D body/joint editor in libGDX, but I'm getting inconsistent results between my desktop and android builds, the android build producing incorrect position Y-values. In summary, the editor consists of a HUD with its own FitViewport for consistent screen presence, and the actual build area grid with its own FitViewport that can be navigated and zoomed, much like any run-of-the-mill image editor. The user touches the screen, and a target appears above that position that can be dragged around to select points on the grid, connecting points that serve as the vertices of the shapes that will become Box2D bodies. This target-selector exists on the HUD viewport so that it can remain a static size if the user were to zoom in on the grid area. So, in order to select a point on this grid, I have to convert the target's position in the HUD viewport to screen coordinates, then convert it to that point on the build area grid. Next to the target, I have a BitmapFont that renders text of the targets current position.
So here's an example of my problem. Let's say the target is at the arbitrary point (0,0) on the grid. The text would say "0,0". In my desktop build it does this accurately. In my Android build, the Y-value is offset by varying values, based on camera zoom, even though that point is without a doubt (0, 0) in that space(The bottom-left of the grid is coded at 0,0). I don't understand how this could happen, since both builds share the core module where this code resides. I knew this would be hard to explain, so I took some screen shots.
These images show the desktop build, no issues, correct values:
desktop app image, default zoom, y-value is correct
desktop app image, camera zoomed in, y-value is still correct
These images show the android build, y-value is incorrect but becomes more correct as the camera zooms in:
android app image, default zoom, y-value is way off
android app image, zoomed in, y-value is closer to correct but still off
I really want to show some code, but I have no idea which parts are relevant to this issue. Essentially what is happening is that the user touches the screen, the target pops up 200px (HUD stage local) above that point, the targets position is converted to screen coordinates, then unprojected to build area grid coordinates. What gets me is that the Y-values are correct in the desktop build, so why would they be different in the android build. Here is my TargetSelector class where everything goes down:
public class TargetSelector extends Group {
private ShapeRenderer shapeRenderer;
private BitmapFont font;
private Vector2 position;
private float yOffset;
private boolean targetActive;
private Vector2 unprojectedPosition;
private OrthographicCamera linkedCamera;
private boolean hasLink = false;
public TargetSelector(){
initShapeRenderer();
initTargetFields();
initFont();
initInputListener();
}
private void initShapeRenderer(){
shapeRenderer = new ShapeRenderer();
shapeRenderer.setAutoShapeType(true);
shapeRenderer.setColor(Color.RED);
}
private void initTargetFields(){
unprojectedPosition = new Vector2();
position = new Vector2();
yOffset = 200;
targetActive = false;
}
private void initFont(){
font = new BitmapFont();
font.getData().scale(1.0f);
font.setColor(Color.WHITE);
}
private void initInputListener(){
setBounds(0, 0, Info.SCREEN_WIDTH, Info.SCREEN_HEIGHT);
addListener(new InputListener(){
#Override
public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
targetActive = true;
position.set(x, y).add(0, yOffset);
return true;
}
#Override
public void touchDragged(InputEvent event, float x, float y, int pointer) {
position.set(x, y).add(0, yOffset);
}
#Override
public void touchUp(InputEvent event, float x, float y, int pointer, int button) {
targetActive = false;
}
#Override
public void exit(InputEvent event, float x, float y, int pointer, Actor toActor) {
targetActive = false;
}
});
}
#Override
public void act(float delta) {
super.act(delta);
if(targetActive && hasLink){
updateUnprojectedPosition();
}
}
#Override
public void draw(Batch batch, float parentAlpha) {
if(targetActive){
batch.end();
Gdx.gl.glEnable(GL20.GL_BLEND);
Gdx.gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);
shapeRenderer.setProjectionMatrix(getStage().getCamera().combined);
shapeRenderer.begin();
// Render target
Gdx.gl20.glLineWidth(3);
shapeRenderer.set(ShapeRenderer.ShapeType.Line);
shapeRenderer.setColor(Color.RED);
shapeRenderer.circle(position.x, position.y, 32);
shapeRenderer.circle(position.x, position.y, 1);
// Render position text background
Vector2 fontPosition = position.cpy().add(64, 64);
shapeRenderer.set(ShapeRenderer.ShapeType.Filled);
shapeRenderer.setColor(0, 0, 0, 0.5f);
shapeRenderer.rect(fontPosition.x - 8, fontPosition.y - 32, 172, 40);
shapeRenderer.end();
Gdx.gl.glDisable(GL20.GL_BLEND);
Gdx.gl20.glLineWidth(1);
batch.setProjectionMatrix(getStage().getCamera().combined);
batch.begin();
// Render position text
String text;
if(hasLink)
text = "(" + (int) unprojectedPosition.x + " , " + (int) unprojectedPosition.y + ")";
else
text = "No Link";
font.draw(batch, text, fontPosition.x, fontPosition.y, 100, 10, false);
}
super.draw(batch, parentAlpha);
}
public void updateUnprojectedPosition(){
Vector2 screenPosV2 = localToScreenCoordinates(position.cpy());
Vector3 posV3 = new Vector3(screenPosV2.x, screenPosV2.y, 0);
linkedCamera.unproject(posV3);
unprojectedPosition.set(MathUtils.round(posV3.x), MathUtils.round(posV3.y));
}
public void linkToCamera(OrthographicCamera camera){
if(camera != null){
linkedCamera = camera;
hasLink = true;
}
}
}
The linkedCamera is assigned in a manager class that handles the relationship between all HUD actions and the build area grid.
This is my first time ever asking a question here, I'm sorry if I'm being too verbose but the issue is complicated to explain, at least it was for me.
My code moves an object by clicking on it and on the touch, I want to just touch on the moving object. How to disable the ability to navigate the click event object
Vector3 touchPos;
touchPos = new Vector3();
Gdx.input.setInputProcessor(this);
#Override
public boolean touchDragged(int screenX, int screenY, int pointer) {
touchPos.set(Gdx.input.getX(), Gdx.input.getY(), 0);
camera.unproject(touchPos);
bucket.x = touchPos.x - 64 / 2;
return true;
}
Isn't your touchDragged method missing a parameter?
public void touchDragged( InputEvent event, float x, float y, int pointer )
The event object has 2 methods for affecting the propagation of an event,setBubbles(boolean) and cancel().
Also, the methods touchDown and touchUp should have the same event parameter.
I try drawing stuff using InputProcessor class of LibGDX. But nothing is drawn! Can I draw textures from any other place rather than render () class in LibGDX?
And yes, if I draw in render () class it is drawn ok, but can I draw from somewhere else, like InputProcessor touchDragged?
here is my code
public class mm_imput implements InputProcessor {
SpriteBatch batch=new SpriteBatch();
Texture pixel=new Texture("something.png");
#Override
public boolean touchDragged (int x, int y, int pointer) {
drawSomething();
}
void drawSomething() {
batch.begin();
batch.draw(pixel, 100, 100, 100, 100);
batch.end();
}
}
It should be showing something every time I drag mouse.. How to achieve this?
Your Batch has to be in the Render method of a Screen class.
At this link you'll see what I'm talking about: https://github.com/littletinman/Hype/blob/master/Hype/core/src/com/philiproyer/hype/screens/Details.java
I have a main screen class, with a Render method. I'm implementing the InputProcessor interface.
What I recommend is having the Render method in a condition for when the touch is down.
public void render(float delta) {
Gdx.gl.glClearColor( 0, 0, 0, 1); // Clear the screen with a solid color
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
if(isTouching == true) { // check for touching
batch.begin();
batch.draw(pixel, 100, 100, 100, 100);
batch.end();
}
}
Then, in the touchDown method add something like this
public boolean touchDown(int screenX, int screenY, int pointer, int button) {
isTouching = true; // Set boolean to true
return false;
}
To make sure you have it reset when you stop touching do the following in your touchUp method
public boolean touchUp(int screenX, int screenY, int pointer, int button) {
isTouching = false; // Set boolean to false
return false;
}
Let me know if anything isn't quite clear. Best of luck!
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 trying to implement touch scrolling in a libgdx game. I have a wide image that is a panorama of a room. I want to be able to scroll the image so the user can see around the room. I have it so that I can scroll a certain distance but when a new touchDragged event is registered the image is moved back to the original position.
This is how I'm implementing it
public class AttackGame implements ApplicationListener {
AttackInputProcessor inputProcessor;
Texture backgroundTexture;
TextureRegion region;
OrthographicCamera cam;
SpriteBatch batch;
float width;
float height;
float posX;
float posY;
#Override
public void create() {
posX = 0;
posY = 0;
width = Gdx.graphics.getWidth();
height = Gdx.graphics.getHeight();
backgroundTexture = new Texture("data/pancellar.jpg");
region = new TextureRegion(backgroundTexture, 0, 0, width, height);
batch = new SpriteBatch();
}
#Override
public void resize(int width, int height) {
cam = new OrthographicCamera();
cam.setToOrtho(false, width, height);
cam.translate(width / 2, height / 2, 0);
inputProcessor = new AttackInputProcessor(width, height, cam);
Gdx.input.setInputProcessor(inputProcessor);
}
#Override
public void render() {
Gdx.gl.glClearColor(0,0,0,1);
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
batch.setProjectionMatrix(cam.combined);
batch.begin();
batch.draw(backgroundTexture, 0, 0, 2400, 460);
batch.end();
}
#Override
public void pause() {
// TODO Auto-generated method stub
}
#Override
public void resume() {
// TODO Auto-generated method stub
}
#Override
public void dispose() {
backgroundTexture.dispose();
}
}
And in the InputProcessor
#Override
public boolean touchDragged(int screenX, int screenY, int pointer) {
cam.position.set(screenX, posY / 2, 0);
cam.update();
return false;
}
I got this far with help from this question LibGdx How to Scroll using OrthographicCamera?. However it doesn't really solve my problem.
I think the problem is with the touchDragged corodinates not being world coordinates but I have tried unprojecting the camera with no effect.
I have been struggling with this for a few weeks and I would really appreciate some help on this.
Thanks in advance.
I recently did something as what you want. This is my Input class that I use for move the map, you only need to change my 'stage.getCamera()' for your 'cam':
public class MapInputProcessor implements InputProcessor {
Vector3 last_touch_down = new Vector3();
...
public boolean touchDragged(int x, int y, int pointer) {
moveCamera( x, y );
return false;
}
private void moveCamera( int touch_x, int touch_y ) {
Vector3 new_position = getNewCameraPosition( touch_x, touch_y );
if( !cameraOutOfLimit( new_position ) )
stage.getCamera().translate( new_position.sub( stage.getCamera().position ) );
last_touch_down.set( touch_x, touch_y, 0);
}
private Vector3 getNewCameraPosition( int x, int y ) {
Vector3 new_position = last_touch_down;
new_position.sub(x, y, 0);
new_position.y = -new_position.y;
new_position.add( stage.getCamera().position );
return new_position;
}
private boolean cameraOutOfLimit( Vector3 position ) {
int x_left_limit = WINDOW_WIDHT / 2;
int x_right_limit = terrain.getWidth() - WINDOW_WIDTH / 2;
int y_bottom_limit = WINDOW_HEIGHT / 2;
int y_top_limit = terrain.getHeight() - WINDOW_HEIGHT / 2;
if( position.x < x_left_limit || position.x > x_right_limit )
return true;
else if( position.y < y_bottom_limit || position.y > y_top_limit )
return true;
else
return false;
}
...
}
This is the result: http://www.youtube.com/watch?feature=player_embedded&v=g1od3YLZpww
You need to do a lot more computation in the touchDragged callback, you can't just pass whatever screen coordinates were touched on to the camera. You need to figure out how far the user has dragged their finger, and in what direction. The absolute coordinates are not immediately useful.
Consider dragging down from the top-right or dragging down from the top-left. In both cases you want (I presume) to move the camera the same distance, but the absolute values of the screen coordinates will be very different in the two cases.
I think the simplest thing is to just track previousX and previousY (initialize them in the touchDown method. Then invoke cam.translate() with the delta (deltaX = screenX - previousX, for example), during touchDragged. And also update the previous* in touchDragged.
Alternatively, you can look at some of the fancier InputProcessor wrappers libgdx provides (see https://code.google.com/p/libgdx/wiki/InputGestureDetection).
Simple answer:
Declare 2 fields to hold the new and old drag location:
Vector2 dragOld, dragNew;
When just touched you set both of these equal to the touched location or your cam will jump.
if (Gdx.input.justTouched())
{
dragNew = new Vector2(Gdx.input.getX(), Gdx.input.getY());
dragOld = dragNew;
}
Update dragNew each frame and simply subtract the vectors from each other to get the x and y for translating the camera.
if (Gdx.input.isTouched())
{
dragNew = new Vector2(Gdx.input.getX(), Gdx.input.getY());
if (!dragNew.equals(dragOld))
{
cam.translate(dragOld.x - dragNew.x, dragNew.y - dragOld.y); //Translate by subtracting the vectors
cam.update();
dragOld = dragNew; //Drag old becomes drag new.
}
}
This is all I use to drag my ortho cam around, simple and effective.
Used drinor's answer but added the line on "touchDown) function so it doesn't reset the camera every time you start dragging again:
#Override
public boolean touchDown(int screenX, int screenY, int pointer, int button) {
last_touch_down.set( screenX, screenY, 0);
return false;
}
I have not used it myself, but I would start by looking at the code of:
Have you looked at the code of http://libgdx.l33tlabs.org/docs/api/com/badlogic/gdx/scenes/scene2d/ui/FlickScrollPane.html
See: touchDragged