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();.
Related
I have a bug object that I want to move across the screen as soon as the game starts. The bug starts from the bottom left of the screen and is supposed to move to the top right and stop. What I have is the bug never really gets to the top right because the game screen(X and Y) size are not equal. How do I make the bug move to that position?
This is what I have.
public void create() {
spriteBatch = new SpriteBatch();
bug = new Sprite(new Texture("EnemyBug.png"));
bug.setSize(50, 85);
bug.setPosition(0,0);
}
public void render() {
xdeg++;
ydeg++;
Gdx.gl.glClearColor(0.7f, 0.7f, 0.2f, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
spriteBatch.begin();
bug.translate(xdeg, ydeg);
bug.draw(spriteBatch);
spriteBatch.end();
}
I'll assume that you know your window width (W) and height (H). First find the W / H ratio:
float ratio = screenWidth / screenHeight;
Then update your bug position accordingly:
bug.translate(ratio, 1);
This will make the sprite move through the screen diagonal.
I'm working at a android game using LIBGDX.
#Override
public boolean touchDown(int x, int y, int pointer, int button) {
// TODO Auto-generated method stub
return false;
}
Here, the x and y returns the position of the touch of the device screen, and the values are between 0 and the device screen width and height.
My game resolution is 800x480, and it will keep its aspect ratio on every device.
I want to find out a way to get the touch position, related to the game rectangle, this image can explain exactly:
Is there a way to do it?
I want to get the touch position related to my viewport..
I use this to keep the aspect ratio
http://www.java-gaming.org/index.php?topic=25685.0
Unproject your touch.
Make a Vector3 object for user touch:
Vector3 touch = new Vector3();
And use the camera to convert the screen touch coordinates, to camera coordinates:
#Override
public boolean touchDown(int x, int y, int pointer, int button){
camera.unproject(touch.set(x, y, 0)); //<---
//use touch.x and touch.y as your new touch point
return false;
}
In the newer version of LibGDX you can achieve it with the built in viewports.
Firstly choose your preferred viewport the one you want here is FitViewport.
You can read about them here:
https://github.com/libgdx/libgdx/wiki/Viewports
Next, you declare and initialize the viewport and pass your resolution and camera:
viewport = new FitViewport(800, 480, cam);
Then edit your "resize" method of the screen class to be like that:
#Override
public void resize(int width, int height) {
viewport.update(width, height);
}
Now wherever you want to get touch points you need to transfer them to new points according to the new resolution. Fortunately, the viewport class does it automatically.
Just write this:
Vector2 newPoints = new Vector2(x,y);
newPoints = game.mmScreen.viewport.unproject(newPoints);
Where x and y are the touch points on the screen and in the second line "newPoints" gets the transformed coordinates. Now you can pass them wherever you want.
After 1-2 paintful hours I finnaly found the solution..
if (Gdx.input.isTouched()) {
float x = Gdx.input.getX();
float y = Gdx.input.getY();
float yR = viewport.height / (y - viewport.y); // the y ratio
y = 480 / yR;
float xR = viewport.width / (x - viewport.x); // the x ratio
x = 800 / xR;
bubbles.add(new Bubble(x, 480 - y));
}
Edit: this is an old deprecared way to do it, so don't.
I am trying to create a method which returns a texture modified by an overlay using libgdx and PixMap.
Assuming I have 2 images:
A Base Image in FileHandle textureInput
And an overlay image in FileHandle overLay
It should produce this texture:
So it should use the RGB values from the textureInput and the alpha values from the overLay and create the final image. I believe I can do this using the Pixmap class but I just can't seem to find exactly how.
Here is what I gather should be the structure of the method:
public Texture getOverlayTexture(FileHandle overLay, FileHandle textureInput){
Pixmap inputPix = new Pixmap(textureInput);
Pixmap overlayPix = new Pixmap(overLay);
Pixmap outputPix = new Pixmap(inputPix.getWidth(), inputPix.getHeight(), Format.RGBA8888);
// go over the inputPix and add each byte to the outputPix
// but only where the same byte is not alpha in the overlayPix
Texture outputTexture = new Texture(outputPix, Format.RGBA8888, false);
inputPix.dispose();
outputPix.dispose();
overlayPix.dispose();
return outputTexture;
}
I am just looking for a bit of direction as to where to go from here. Any help is really appreciated. I apologize if this question is too vague or if my approach is entirely off.
Thanks!
I finally found the way to do this.
How my game is setup is that each item draws itself. They are handed a spritebatch and can do stuff with it. I did it that way various reasons. There is an item manager containing a list of items. Each item has various attributes. Each item has it's own render method along with other independent methods. Here is what finally worked:
A normal item's render method which does not use any alpha masking:
public void render(SpriteBatch batch, int renderLayer) {
if(renderLayer == Integer.parseInt(render_layer)){ // be in the correct render layer
batch.draw(item.region,
item.position.x, // position.x
item.position.y, // position.y
0, //origin x
0, //origin y
item.region.getRegionWidth() , //w
item.region.getRegionHeight(), //h
item.t_scale, //scale x
item.t_scale, //scale y
item.manager.radiansToDegrees(item.rotation)); //angle
}
}
So it is handed a spritebatch that it draws to with the correct image, location, scale, and rotation, and that is that.
After playing around with what I found here: https://gist.github.com/mattdesl/6076846 for a while, this finally worked for an item who needs to use alpha masking:
public void render(SpriteBatch batch, int renderLayer) {
if(renderLayer == Integer.parseInt(render_layer)){
batch.enableBlending();
//draw the alpha mask
drawAlphaMask(batch, item.position.x, item.position.y, item.region.getRegionWidth(), item.region.getRegionHeight());
//draw our foreground elements
drawForeground(batch, item.position.x, item.position.y, item.region.getRegionWidth(), item.region.getRegionHeight());
batch.disableBlending();
}
}
There is a TextureRegion named alphaMask which contains a black shape.
It can be any image, but let's say in this instance its this shape / image:
Here is the function called above that uses that image:
private void drawAlphaMask(SpriteBatch batch, float x, float y, float width, float height) {
//disable RGB color, only enable ALPHA to the frame buffer
Gdx.gl.glColorMask(false, false, false, true);
// Get these values so I can be sure I set them back to how it was
dst = batch.getBlendDstFunc();
src = batch.getBlendSrcFunc();
//change the blending function for our alpha map
batch.setBlendFunction(GL10.GL_SRC_ALPHA, GL10.GL_ZERO);
//draw alpha mask sprite
batch.draw(alphaRegion,
x, // position.x
y, // position.y
0, // origin x
0, // origin y
alphaRegion.getRegionWidth(), // w
alphaRegion.getRegionHeight(), // h
item.t_scale, // scale x
item.t_scale, // scale y
item.manager.radiansToDegrees(item.rotation)); // angle
//flush the batch to the GPU
batch.flush();
}
There are a variety of "materials" to apply to any shape. In any instance one of them is assigned to the spriteRegion variable. Let's say right now it is this:
So the drawForeground method called above uses that image like this:
private void drawForeground(SpriteBatch batch, float clipX, float clipY, float clipWidth, float clipHeight) {
//now that the buffer has our alpha, we simply draw the sprite with the mask applied
Gdx.gl.glColorMask(true, true, true, true);
batch.setBlendFunction(GL10.GL_DST_ALPHA, GL10.GL_ONE_MINUS_DST_ALPHA);
batch.draw(spriteRegion,
clipX, // corrected center position.x
clipY, // corrected center position.y
0, //origin x
0, //origin y
spriteRegion.getRegionWidth() , //w
spriteRegion.getRegionHeight(), //h
item.t_scale, //scale x
item.t_scale, //scale y
item.manager.radiansToDegrees(item.rotation)); //angle
//remember to flush before changing GL states again
batch.flush();
// set it back to however it was before
batch.setBlendFunction(src, dst);
}
That all worked right away in the desktop build, and can produce "Brick Beams" (or whatever) in the game nicely:
However in Android and GWT builds (because after all, I am using libgdx) it did not incorporate the alpha mask, and instead rendered the full brick square.
After a lot of looking around I found this: https://github.com/libgdx/libgdx/wiki/Integrating-libgdx-and-the-device-camera
And so to fix this in Android I modified the MainActivity.java onCreate method like this:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
AndroidApplicationConfiguration cfg = new AndroidApplicationConfiguration();
cfg.useGL20 = false;
cfg.r = 8;
cfg.g = 8;
cfg.b = 8;
cfg.a = 8;
initialize(new SuperContraption("android"), cfg);
if (graphics.getView() instanceof SurfaceView) {
SurfaceView glView = (SurfaceView) graphics.getView();
// force alpha channel - I'm not sure we need this as the GL surface
// is already using alpha channel
glView.getHolder().setFormat(PixelFormat.TRANSLUCENT);
}
}
And that fixes it for Android.
I still cannot figure out how to make it work properly in gwt, as I cannot figure out how to tell libgdx to tell GWT to tell webGl to go ahead and pay attention to the alpha channel. I'm interested in how to do something like this in an easier or less expensive way (though this seems to work fine).
If anyone knows how to make this work with GWT, please post as another answer.
Here is the non-working GWT build if you want to see the texture issue:
https://supercontraption.com/assets/play/index.html
I have a player setup with box2d and I am trying to draw a sprite over the player. The player spawns in the middle of the screen, while the sprite spawns in the lower left hand corner of the screen but does move along with the player entity, just starting at a different location.
GameScreen snippet:
#Override
public void render(float delta) {
super.render(delta);
player.update();
world.step(TIMESTEP, VELOCITY_ITERATIONS, POSITION_ITERATIONS);
}
#Override
public void show() {
player = new Player(world, 0, 0);
}
Player class snippet:
public Player(World world, float x, float y) {
texture = new Texture(Gdx.files.internal("sprites/Player.png"));
sprite = new Sprite(texture);
}
public void update() {
batch = new SpriteBatch();
batch.begin();
sprite.draw();
sprite.setPosition(body.getPosition().x, body.getPosition().y);
batch.end();
body.setLinearVelocity(impulse);
}
I tried setting the position of the sprite in the constructor based on the body's coordinates but it doesn't seem to be working. I have removed body & fixture code. Any push in the right direction is appreciated.
If you enable box2d debug render, you'll probably find that both the texture and the body are starting at the left corner of the screen. Actually, 0, 0 are supposed to be the coordinates for the left bottom corner. In order to set your body in the center of the screen, you should set something like
(SCREEN_WIDTH / 2) / PTM_RATIO, (SCREEN_HEIGHT / 2) / PTM_RATIO
as your body initial position.
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));