I wanted to create a simple 30x30 isometric tiled map and add a listener to be able to click on the tiles. I looked up a lot of the articles and posts here but none of them helped me.
My issue is, i have a viewport, 30x17, a camera, a stage and a tiled map with tileWidth = 32 and tileHeight = 16 pixels.
Now when i render the tiled map it looks fine.
When i click on stage and try to get the world coordinates i see some really weird coordinates.
This is the code:
private static final float TILE_WIDTH = 32;
private static final float TILE_HEIGHT = 16;
private OrthographicCamera camera;
private Viewport viewport;
private Stage stage;
private IsometricTiledMapRenderer isometricTiledMapRenderer;
private Matrix4 isoTransform;
private Matrix4 invIsotransform;
public void load(AssetManagerLoaderV2 assetManagerLoader) {
assetManagerLoader.load();
init();
camera = new OrthographicCamera();
viewport = new FitViewport(30, 17, camera);
stage = new Stage(viewport);
TiledMap tiledMap = new TiledMapGenerator(assetManagerLoader).generate(30, 30);
isometricTiledMapRenderer = new IsometricTiledMapRenderer(tiledMap, 1/32f);
stage.addListener(new InputListener() {
#Override
public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
System.out.println(screenToCell(x, y));
return true;
}
});
}
#Override
public void show() {
Gdx.input.setInputProcessor(stage);
}
#Override
public void render(float delta) {
DrawUtils.clearScreen();
viewport.apply();
isometricTiledMapRenderer.setView(camera);
isometricTiledMapRenderer.render();
}
#Override
public void resize(int width, int height) {
stage.getViewport().update(width, height, true);
}
#Override
public void pause() {
}
#Override
public void resume() {
}
#Override
public void hide() {
}
#Override
public void dispose() {
isometricTiledMapRenderer.dispose();
stage.dispose();
}
public void init () {
//create the isometric transform
isoTransform = new Matrix4();
isoTransform.idt();
isoTransform.translate(0.0f, 0.25f, 0.0f);
isoTransform.scale((float)(Math.sqrt(2.0) / 2.0), (float)(Math.sqrt(2.0) / 4.0), 1.0f);
isoTransform.rotate(0.0f, 0.0f, 1.0f, -45.0f);
//... and the inverse matrix
invIsotransform = new Matrix4(isoTransform);
invIsotransform.inv();
}
public Vector2 worldToCell(float x, float y) {
float halfTileWidth = TILE_WIDTH * 0.5f;
float halfTileHeight = TILE_HEIGHT * 0.5f;
float row = (1.0f/2) * (x/halfTileWidth + y/halfTileHeight);
float col = (1.0f/2) * (x/halfTileWidth - y/halfTileHeight);
return new Vector2((int)col,(int)row);
}
public Vector2 screenToWorld(float x, float y){
Vector3 touch = new Vector3(x,y,0);
camera.unproject(touch);
touch.mul(invIsotransform);
touch.mul(isoTransform);
return new Vector2(touch.x,touch.y);
}
public Vector2 screenToCell(float x, float y) {
Vector2 world = screenToWorld(x,y);
world.y -= TILE_HEIGHT *0.5f;
return worldToCell(world.x,world.y);
}
Does anyone have an idea how to write worldToCell to get the proper coordinates?
I found the issue, i was missing one step.
This is what touchDown should look like:
#Override
public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
Vector2 newCoords = stage.stageToScreenCoordinates(new Vector2(x, y));
System.out.println(screenToCell(newCoords.x, newCoords.y));
return true;
}
I need to convert stage coordinates to screen coordinates, now this code is working.
Related
I have a difficult situation.
I'm making a 2D game and I'm on the step
of integrating box2Dlights part.
But when i'm rendering my RayHandler, I have a black screen.
Here is another image, where I don't render my lights. There is no lights now.
I need to have collision of Box2D and Box2Dlights mixed together.
Please note:
All my boxes are made in units, equal to 1, so that one room is 16 squares length.
All entire world is zoomed by setToOrtho((16 - 1.875f) / Gdx.graphics.getHeight()), where 16 is tile size.
If I scaled my tile size, I would like to have this stuff work.
All code is complicated so I send the only GameScreen and GameMap classes for now:
#Override
public void show() {
InstanceVars inst = InstanceVars.getInstance();
inst.world = new World(new Vector2(0, 0f), true);
inst.engine = new Engine();
inst.handler = new RayHandler(inst.world);
LevelCreator creator = new LevelCreator("level_def.cfg");
try {
gameWorld = creator.createNewLevel();
gameWorld.start();
} catch (FileCorruptedException e) {
e.printStackTrace();
}
}
#Override
public void render(float delta) {
gameWorld.render();
}
#Override
public void resize(int width, int height) {
gameWorld.resize(width, height);
}
#Override
public void dispose() {
gameWorld.dispose();
InstanceVars.getInstance().dispose();
}
for the GameScreen.java
and
private World physWorld = InstanceVars.getInstance().world;
private Box2DDebugRenderer physRender = new Box2DDebugRenderer();
private OrthographicCamera cam = new OrthographicCamera();
private RayHandler lights = new RayHandler(physWorld);
private PlayerController controls;
private float STD_ZOOM = ((float) CELL_SIZE + 16) / Gdx.graphics.getHeight();
private Body playerBody;
public GameMap() {
cam.zoom = STD_ZOOM;
}
public void start() {
GameObject player = (GameObject) getProperties().get("ent_player");
playerBody = player.getComponent(PhysicsComponent.class).getBody();
controls = new PlayerController();
Gdx.input.setInputProcessor(controls);
physWorld.setContactListener(new CollisionInteractor(InstanceVars.getInstance().engine));
PointLight light = new PointLight(lights, 1000, Color.GOLDENROD, 500, playerBody.getPosition().x, playerBody.getPosition().y);
}
public void render() {
updatePlayer();
physWorld.step(1/60f, 8, 2);
updateCamera();
physRender.render(physWorld, cam.combined);
// lights.updateAndRender();
}
private void updatePlayer() {
if (controls.up) playerBody.setLinearVelocity(playerBody.getLinearVelocity().x, -controls.speed);
else playerBody.setLinearVelocity(playerBody.getLinearVelocity().x, controls.down ? controls.speed : 0);
if (controls.down) playerBody.setLinearVelocity(playerBody.getLinearVelocity().x, controls.speed);
else playerBody.setLinearVelocity(playerBody.getLinearVelocity().x, controls.up ? -controls.speed : 0);
if (controls.left) playerBody.setLinearVelocity(-controls.speed, playerBody.getLinearVelocity().y);
else playerBody.setLinearVelocity(controls.right ? controls.speed : 0, playerBody.getLinearVelocity().y);
if (controls.right) playerBody.setLinearVelocity(controls.speed, playerBody.getLinearVelocity().y);
else playerBody.setLinearVelocity(controls.left ? -controls.speed : 0, playerBody.getLinearVelocity().y);
}
public void resize(float width, float height) {
STD_ZOOM = ((float) CELL_SIZE - 1.875f) / Gdx.graphics.getHeight();
cam.setToOrtho(true, width, height);
}
private void updateCamera() {
cam.position.set(playerBody.getPosition(), 0);
cam.update();
}
public void dispose() {
super.dispose();
physWorld.dispose();
physRender.dispose();
}
for GameMap.java.
If you need more details, don't be afraid to ask.
I cannot solve the problem on my own.
I am trying to animate an actor which contain a simple message written with bitmapfont using fadeIn action but it doesn't work.
Here is my code.
public class Message extends Actor{
BitmapFont font;
String message;
float x, y;
public Message(BitmapFont font, String message, float x, float y) {
this.font = font;
this.message = message;
this.x = x;
this.y = y;
}
#Override
public void draw(Batch batch, float parentAlpha) {
Color color = getColor();
batch.setColor(color.r, color.g, color.b, color.a * parentAlpha);
font.setColor(color.r, color.g, color.b, color.a * parentAlpha);
font.draw(batch, message, x, y);
}
}
Then when clicking a button the message should appear but it is only appear without animation.
button.addListener(new ClickListener() {
#Override
public void clicked(InputEvent event, float x, float y) {
BitmapFont font = new BitmapFont(Gdx.files.internal("myfont.font"));
Message message = new Message(font, "Tap Here", 100, 20);
stage.addActor(message);
message.addAction(Actions.fadeIn(5));
}});
Other actors animations on the same stage work fine as I'm calling:
stage.act(delta);
stage.draw();
You should set message actor's alpha to zero before adding the fadeIn action:
button.addListener(new ClickListener() {
#Override
public void clicked(InputEvent event, float x, float y) {
BitmapFont font = new
BitmapFont(Gdx.files.internal("myfont.font"));
Message message = new Message(font, "Tap Here", 100, 20);
message.getColor().a = 0f; // add this line
stage.addActor(message);
message.addAction(Actions.fadeIn(5));
}});
I am developing a top-down 2D game with a TiledMap.
Currently i want to create a minimap of my TiledMap, but i´m really confused how to do this.
I´ve read something that i should create another camera, zoom out and render the map again, but how should i draw the zoomed out map in the upper right corner?
Am i overthinking this whole thing?
My Map:
public class LiquidMap {
private TiledMap map;
private OrthogonalTiledMapRenderer renderer;
private OrthographicCamera camera = new OrthographicCamera();
private MiniMap miniMap;
public LiquidMap(String filePath) {
map = new TmxMapLoader().load(filePath);
renderer = new OrthogonalTiledMapRenderer(map, 1 / 32f);
camera.setToOrtho(false, 30, 20);
miniMap = new MiniMap(map);
}
public void update(float x, float y){
camera.position.x = x;
camera.position.y = y;
camera.update();
//renderer.setView(camera.combined, x - 10, y - 10, 20, 20);
renderer.setView(camera);
miniMap.update(x, y);
}
public void update(HostPlayer player){
this.update(player.position.x + (player.skin.getWidth()/2f)/32f, player.position.y + (player.skin.getHeight()/2f)/32f);
}
public void render(HostPlayer player){
renderer.render();
renderer.getBatch().begin();
renderer.getBatch().draw(player.skin, player.position.x, player.position.y, 1/32f * player.skin.getWidth(), 1/32f * player.skin.getHeight());
renderer.getBatch().end();
miniMap.render();
}
And my MiniMap:
public class MiniMap {
private OrthogonalTiledMapRenderer renderer;
private OrthographicCamera camera = new OrthographicCamera();
public MiniMap(TiledMap map) {
renderer = new OrthogonalTiledMapRenderer(map, 1 / 32f);
camera.setToOrtho(false, 30, 20);
camera.zoom = 10;
}
public void update(){
}
public void update(float x, float y){
//Pixventure.instance.gameScreen.getCamera()
camera.position.x = x;
camera.position.y = y;
camera.update();
renderer.setView(camera.combined, x-15, y-15, 30, 30);
//renderer.setView(camera);
}
public void render(){
renderer.render();
}
}
The whole situation is looking like this:
Minimap's camera position is updated by player position. Place your minimap at the corner and don't change his position by your update() method.
Hello I've been stuck on an issue for a while trying to create a simple game using the Libgdx framework.
I have a stage with a background Image actor and a stick figure image actor that is placed in the center of the stage.
I cant seem to understand, after reading many posts on stackoverflow and the libgdx wiki on event handling, how events such as fling and pan are passed to the actor to simply move it my stickman around the screen.
I would greatly appreciate any help or explanations on how exactly this works.
I just don't know how to get the integer values from pan and the velocity values from fling into my actor's Vector2 position variable so that it can move around every time the render method is called.
Here is some of my code:
My GestureDetection Class:
public class GestureDetection implements GestureListener{
public GestureDetection(){
}
#Override
public boolean touchDown(float x, float y, int pointer, int button) {
// TODO Auto-generated method stub
return false;
}
#Override
public boolean tap(float x, float y, int count, int button) {
System.out.println("tapped");
return false;
}
#Override
public boolean longPress(float x, float y) {
// TODO Auto-generated method stub
return false;
}
#Override
public boolean fling(float velocityX, float velocityY, int button) {
// TODO Auto-generated method stub
return false;
}
#Override
public boolean pan(float x, float y, float deltaX, float deltaY) {
// TODO Auto-generated method stub
return false;
}
#Override
public boolean panStop(float x, float y, int pointer, int button) {
// TODO Auto-generated method stub
return false;
}
#Override
public boolean zoom(float initialDistance, float distance) {
// TODO Auto-generated method stub
return false;
}
#Override
public boolean pinch(Vector2 initialPointer1, Vector2 initialPointer2,
Vector2 pointer1, Vector2 pointer2) {
// TODO Auto-generated method stub
return false;
}
}
My Game class
public class Game implements Screen{
StickFlick game;
SpriteBatch batch;
Texture gameBackground;
Stage stage;
GestureDetector gd;
InputMultiplexer im;
WalkingEnemy testEnemy;
public Game(StickFlick game){
this.game = game;
testEnemy = new WalkingEnemy("basic", 100, Gdx.graphics.getWidth() / 2, Gdx.graphics.getHeight() / 2);
}
#Override
public void render(float delta) {
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
//enemy1.update(delta);
System.out.println("");
stage.act(Gdx.graphics.getDeltaTime());
batch.begin();
stage.draw();
batch.end();
}
#Override
public void resize(int width, int height) {
stage = new Stage(width, height, true);
stage.clear();
gd = new GestureDetector(new GestureDetection());
im = new InputMultiplexer(gd, stage);
Gdx.input.setInputProcessor(im);
Texture gameBackground = new Texture("data/gameBackground.png");
Image backgroundImage = new Image(gameBackground);
backgroundImage.setWidth(Gdx.graphics.getWidth());
backgroundImage.setHeight(Gdx.graphics.getHeight());
stage.addActor(backgroundImage);
stage.addActor(testEnemy.getImage());
stage.addAction(Actions.sequence(Actions.alpha(0), Actions.fadeIn(1)));
}
#Override
public void show() {
batch = new SpriteBatch();
}
#Override
public void hide() {
}
#Override
public void pause() {
}
#Override
public void resume() {
}
#Override
public void dispose() {
}
}
Ok, so at start you have to make some variables where you will keep X and Y coords of your stick man. Set some initial values to them, i.e. x should be half of screen width and y half of screen height to have your stick man at the center of the screen.
Then you have to use information you are getting in your GestureListener implementation to move your stick man, that is to change it's coordinates. Your current implementation does absolutely nothing - methods are empty, so when some gesture happens your code is not reacting to it.
So, you can i.e. inside of your pan method use deltaX and deltaY to change stick man coordinates for those values. Something like:
X+=deltaX;
y+=deltaY;
Or you can multiply deltaX and deltaY with some constant if you want faster or slower movement and/or multiply deltaY with -1 if movement is in opposite way from what you want it to be.
Or if you want to use fling use those variables that method provides - velocityX and velocityY - add them to you stick man x and y to move it.
So basically, you are always drawing stick man at x,y coords and from some method of GestureListener interface you implemented you should change stick man coords and make him move.
I've written an app that custom draws everything inside paint() based on fixed pixel positions. Then I disabled resize of the frame so its always visible.
However, now I would like to be able to resize it but I dont want to change my drawling code. I was hoping I could grab the 300x300 square of the Graphics g object and resize it to the JFrame current size after all of my drawling code, but I have no idea what I'm doing.
Here sample code. In this I want the 100x100 square to remain in the middle, proportionate to the resized JFrame:
package DrawAndScale;
import java.awt.Color;
import java.awt.Graphics;
public class DASFrame extends javax.swing.JFrame {
public DASFrame() {
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
this.setSize(300, 300);
}
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new DASFrame().setVisible(true);
}
});
}
#Override
public void paint(Graphics g) {
g.setColor(Color.BLACK);
g.fill3DRect(100, 100, 100, 100, true);
}
}
Thanks.
Assuming you rename your method that paints for 300x300 as paint300, define a buffered image:
#Override public void paint(Graphics g) {
Image bufferImage = createImage(300, 300); // empty image
paint300(bufferImage.getGraphics()); // fill the image
g.drawImage(bufferImage, 0, 0, null); // send the image to graphics device
}
Above is when you want to draw at full size (300x300).
If your window is resized:
#Override public void paint(Graphics g) {
Image bufferImage = createImage(300, 300);
paint300(bufferImage.getGraphics());
int width = getWidth();
int height = getHeight();
CropImageFilter crop =
new CropImageFilter((300 - width)/2, (300 - height)/2 , width, height);
FilteredImageSource fis = new FilteredImageSource(bufferImage, crop);
Image croppedImage = createImage(fis);
g.drawImage(croppedImage, 0, 0, null);
}
The new classes are from from java.awt.image.*.
I didn't test this code. It's just to send you in the right direction.
if you want to painting Custom paint then look for JLabel or JPanel and including Icon/ImageIcon inside, simple example about that
import java.awt.*;
import javax.swing.*;
public class MainComponentPaint extends JFrame {
private static final long serialVersionUID = 1L;
public MainComponentPaint() {
setTitle("Customize Preffered Size Test");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public void display() {
add(new CustomComponent());
pack();
setMinimumSize(getSize());
setPreferredSize(getSize());
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
setVisible(true);
}
});
}
public static void main(String[] args) {
MainComponentPaint main = new MainComponentPaint();
main.display();
}
}
class CustomComponent extends JComponent {
private static final long serialVersionUID = 1L;
#Override
public Dimension getPreferredSize() {
return new Dimension(50, 50);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
int w = getWidth();
int h = getHeight();
for (int i = 0; i < Math.max(w, h); i += 20) {
g.drawLine(i, 0, i, h);
g.drawLine(0, i, w, i);
}
}
}
Not an expert, but you could just scale the Graphics2D object (the passed Graphics is in fact a Graphics2D instance), where the x and y ratios are the ratios of the fixed size you chose to draw and the actual size of the frame.
See http://download.oracle.com/javase/6/docs/api/java/awt/Graphics2D.html#scale%28double,%20double%29
You could do this with some math.
public void paint(Graphics g){
int height = 100;
int width = 100;
int x = (this.getWidth() / 2) - (width / 2);
int y = (this.getHeight() / 2) - (height / 2);
g.setColor(Color.BLACK);
g.fill3DRect(x, y, width, height, true);
}
Or if you wanted to keep the width and height of the box with the same proportion, use int width = this.getWidth() / 3; and int height = this.getHeight() / 3.
The other option is to use Graphics2D.scale(), as JB pointed out, the passed Graphics object is actually a Graphics2D object.