I just started with libgdx and so I created a simple test application where an image just moves. The problem is that even if its an extremly simple game the image is stuttering a lot while it moves. Here is the code:
public class Game extends ApplicationAdapter {
private Entity e;
private OrthographicCamera camera;
private SpriteBatch batch;
#Override
public void create () {
batch = new SpriteBatch();
e = new Entity(50,50,"badlogic.jpg");
camera = new OrthographicCamera();
camera.setToOrtho(false, 480, 800);
camera.update();
}
#Override
public void render () {
Gdx.gl.glClearColor(1, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
camera.update();
e.setX(e.getX()+100 * Gdx.graphics.getDeltaTime());
batch.setProjectionMatrix(camera.combined);
batch.begin();
e.draw(batch);
batch.end();
}
The entity class:
public class Entity {
private Texture texture;
private Rectangle hitbox;
private float x,y;
public Entity(float x,float y,String texture){
this.x=x;
this.y=y;
this.texture=new Texture(Gdx.files.internal(texture));
this.hitbox=new Rectangle(x,y,this.texture.getWidth(),this.texture.getHeight());
}
public void draw(SpriteBatch sb){
sb.draw(texture, x, y);
}
public boolean collidesWith(Entity e){
return this.hitbox.overlaps(e.getHitbox());
}
public Rectangle getHitbox() {
return hitbox;
}
public float getX() {
return x;
}
public void setX(float x) {
this.x = x;
}
public float getY() {
return y;
}
public void setY(float y) {
this.y = y;
}
Can someone explain me why this is stuttering?
getDeltaTime() is smoothed out over multiple frames so if a average frame takes .016 seconds and suddenly there is a frame taking .030 seconds then getDeltaTime() will just return .016. So in reality the game has progressed .030 seconds but you multiply your movement by .016.
getRawDeltaTime() gives you the real time passed since last frame. Try to use this as you multiplier. Otherwise try to log both and see if there are differences.
The benefit of using the smoothed getDeltaTime() is when frame drops happen the game will be still playable. While with getRawDeltaTime() if there would suddenly a second pass for the next frame then your velocity would get multiplied by 1 instead of .016 and could immediately go of screen or miss collision detection.
Related
I'm doing a Java Project that consists of making a "Space Invaders" clone. I'm starting with the ship movement, searching on stackOverflow I found this code:
if(Gdx.input.isKeyPressed(Input.Keys.LEFT) )
x -= Gdx.graphics.getDeltaTime() * PlayerSpeed;
if(Gdx.input.isKeyPressed(Input.Keys.RIGHT) )
x += Gdx.graphics.getDeltaTime() * PlayerSpeed;
I use it to the playerShip(the class below):
public class PlayerShip extends Ship {
private Animator animator;
private float PlayerSpeed = 20.0f;
private int x,y;
public PlayerShip(SpriteBatch batch){
this.animator=new Animator(batch,"ship.png", 5, 2);
}
public void create(){
animator.create();
}
public void render(){
this.animator.render(this.x,this.y);
if(Gdx.input.isKeyPressed(Input.Keys.LEFT) )
x -= Gdx.graphics.getDeltaTime() * PlayerSpeed;
if(Gdx.input.isKeyPressed(Input.Keys.RIGHT) )
x += Gdx.graphics.getDeltaTime() * PlayerSpeed;
}
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
}
Game(main):
public class Game extends ApplicationAdapter {
private SpriteBatch batch;
private BackgroundManagement backgroundManagement;
private BitmapFont font;
private PlayerShip player;
private SmallShip smallShip;
#Override
public void create() {
Gdx.graphics.setWindowedMode(600, 800);
batch = new SpriteBatch();
player = new PlayerShip(batch);
smallShip = new SmallShip(batch);
player.create();
player.setX(300);
player.setY(100);
smallShip.create();
smallShip.setX(200);
smallShip.setY(400);
font = new BitmapFont(Gdx.files.internal("gamefont.fnt"),
Gdx.files.internal("gamefont.png"), false);
backgroundManagement = new BackgroundManagement(batch);
}
#Override
public void render() {
Gdx.gl.glClearColor(0, 0, 0.2f, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.begin();
backgroundManagement.render();
player.render();
smallShip.render();
batch.end();
}
#Override
public void dispose() {
batch.dispose();
}
}
When trying on my code, the ship didn't move to the right, I had tried various solutions but i didn't found any, Any help is appreciated, thanks!
The position of the ship is an integer. Your increment is a float. You maybe have a decent graphics card which can render a simple game at e.g. 200+ fps continuous render (or more even could be crazy). In 200fps case the increment would be (1/200)*20 = 1/10 float.
integer += .1f
won't change the original integer.
Change your position to a float as well, then cast (or convert) to an integer when you need to actually render so x can increment very small values.
I am trying to draw a game character in my "MainGameScreen.java" class from the "germans.java" class when I touch the screen of my phone.Unfortunately my program does not draw the image nor does it give me a warning or an error.
MainGameScreen.java:
import com.daenni.trenchwarfare.mygdx.enteties.germans;
public class MainGameScreen implements Screen, InputProcessor {
Trench_Warfare game;
public SpriteBatch batch;
//Enemies
ArrayList<germans> german;
public MainGameScreen (Trench_Warfare game) {
this.game = game;
batch = new SpriteBatch();
//Enemies
//Initialise Array
german = new ArrayList<germans>();
}
#Override
public void render(float delta) {
//Colours
Gdx.gl.glClearColor(116/255f,102/255f,91/255f,1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
//Create Germans
if (Gdx.input.justTouched()){
german.add(new germans(300));
german.add(new germans(400));
}
//Update Germans
for (germans german : german) {
german.update(delta);
}
game.batch.begin();
//Render Germans
for (germans germans : german) {
germans.render(game.batch);
}
//Background
game.batch.draw(background,0,0);
game.batch.draw(background_links,-background_links.getWidth(),0);
game.batch.draw(background_rechts,background.getWidth(),0);
game.batch.end();
}
This is all of the code that I use to render it in the "MainGameScreen.java" file.
This is my class:
public class germans {
//Set speed
public static final int speed = 25;
//Constant
public static final int default_x = 300;
//Every german uses the same Texture
private static Texture texture;
//Position
float x, y;
public boolean remove = false;
//Create german
public germans(float y) {
this.x = default_x;
this.y = y;
y = 200;
if (texture == null) { //When texture is never loaded
//Set Texture
texture = new Texture("de_s1_default.png");
}
}
public void update (float deltaTime){
x += speed * deltaTime;
}
public void render (SpriteBatch batch) {
batch.draw(texture,x,y);
}
}
Although I am not keen on how libgdx exactly works I am pretty sure first drawing your "germans" and then the background is not what you want.
Try swapping it around:
//Background
game.batch.draw(background,0,0);
game.batch.draw(background_links,-background_links.getWidth(),0);
game.batch.draw(background_rechts,background.getWidth(),0);
//Render Germans
for (germans germans : german) {
germans.render(game.batch);
}
I am making a Pong game and when I click on the screen the paddle jump to my cursor point. I want that I need to drag the cursor to move him and without jumping like normal Pong game. how can I do this?
This is my Paddle class:
public class Paddle {
private Vector3 position;
private int width, height;
private Texture texture;
public Paddle(int x, int y, int width, int height){
this.width = width;
this.height = height;
createTexture(width,height);
position = new Vector3(x, y, 0);
}
private void createTexture(int width, int height) {
Pixmap pixmap = new Pixmap(width, height, Pixmap.Format.RGBA8888);
pixmap.setColor(Color.BLACK);
pixmap.fillRectangle(0, 0, width, height);
texture = new Texture(pixmap);
pixmap.dispose();
}
public void update(int y){
position.add(0, y - position.y,0);
position.y = y;
position.set(position.x, HeadGuns.HEIGHT - position.y, position.z);
}
public void draw(SpriteBatch sb){
sb.draw(texture, position.x, position.y, width,height);
}
This is my PlayState class:
public class PlayState extends State {
private Paddle myPaddle;
public PlayState(GameStateManager gsm) {
super(gsm);
myPaddle = new Paddle(25, HeadGuns.HEIGHT/2, 25, 150);
}
#Override
public void handleInput() {
if (Gdx.input.isTouched()){
//when I touched the screen
myPaddle.update(Gdx.input.getY());
}
}
#Override
public void update(float dt) {
handleInput();
}
#Override
public void render(SpriteBatch sb) {
sb.begin();
myPaddle.draw(sb);
sb.end();
}
#Override
public void dispose() {
}
You are reading touch position:
Gdx.input.getY()
and using it directly to set pad position - you can't do that.
You should use InputLister to get events.
First you should listen to touchDown and see is user touching your pad or not (compare touch coordinates with pad coordinates)
Then, for dragging you should use touchDragged() event...to update pad position when dragging happen, but only if touchDown detected that touch:
https://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/scenes/scene2d/InputListener.html#touchDragged-com.badlogic.gdx.scenes.scene2d.InputEvent-float-float-int-
In my game, I have my objects represented as a actors, thus all of the game objects would be on a Stage. For some reason when I try to move the Stage's camera around, it won't work, or actually it doesn't seem to work. I have added a game Actor to the location of 0,0. When I translate the camera's position around, the Actor still stays at the bottom left corner, despite when I log the camera's position, it shows that the camera has moved.
public class Striker extends Actor {
private Sprite img;
private World worldRef;
private Body body;
//constructor
public Striker(float size, float x, float y, World world) {
img = new Sprite(new Texture(Gdx.files.internal("Striker.png")));
//mains the aspect size ratio
img.setSize((275f / 300f) * size, size);
img.setPosition(x, y);
worldRef = world;
//set up the physics
BodyDef bodyDef = new BodyDef();
bodyDef.type = BodyDef.BodyType.DynamicBody;
bodyDef.position.set(x,y);
body = world.createBody(bodyDef);
}
#Override
public void draw(Batch batch, float parentAlpha) {
img.draw(batch);
}
#Override
public void act(float delta) {
super.act(delta);
}
#Override
public float getX() {
return body.getPosition().x;
}
#Override
public float getY() {
return body.getPosition().y;
}
#Override
public float getWidth() {
return img.getWidth();
}
#Override
public float getHeight() {
return img.getHeight();
}
}
The results of the 2 logs show that the camera's positions have moved, but it doesn't look like it.
public class StrikerScreen implements Screen {
public static float WIDTH = 1920;
public static float HEIGHT = 1080;
public static float PPM = 200;
private Launcher launcherRef;
private OrthographicCamera camera;
private FitViewport viewport;
private World world;
private Box2DDebugRenderer debugRenderer;
private Striker striker;
private Stage gameStage;
//constructor
public StrikerScreen(Launcher launcher) {
launcherRef = launcher;
world = new World(new Vector2(0, -9.8f), true);
debugRenderer = new Box2DDebugRenderer();
gameStage = new Stage();
camera = (OrthographicCamera) gameStage.getCamera();
viewport = new FitViewport(WIDTH / PPM, HEIGHT / PPM, gameStage.getCamera());
viewport.apply();
gameStage.setViewport(viewport);
striker = new Striker(160f / PPM, 0, 0, world);
gameStage.addActor(striker);
gameStage.getCamera().translate(viewport.getWorldWidth() / 2f, 500f, 0);
viewport.apply();
camera.update();
Gdx.app.log("StrikerScreen.java", "Camera position: " + gameStage.getCamera().position.toString());
Gdx.app.log("StrikerScreen.java", "Camera size: " + gameStage.getCamera().viewportWidth + ", " + gameStage.getCamera().viewportHeight);
}
#Override
public void show() {
}
public void update(float delta) {
world.step(1 / 30f, 6, 2);
gameStage.act(delta);
}
#Override
public void render(float delta) {
update(delta);
debugRenderer.render(world, camera.combined);
gameStage.draw();
}
#Override
public void resize(int width, int height) {
viewport.update(width, height, true);
}
#Override
public void pause() {
}
#Override
public void resume() {
}
#Override
public void hide() {
}
#Override
public void dispose() {
}
}
In the code you posted, the only times you move the camera are in the StrikerScreen constructor where you explicitly translate it, and in the resize method, where you have called viewport.update(width, height, true); Passing true to viewport.update tells it to move the camera to where (0, 0) is in the bottom left of corner of the viewport. Since resize is automatically called when you set your screen to this screen, that is the most recent position you have set the camera to.
I have a Stage with OrthographicCamera and also I have a Actor with InputListener setter by addListener (from actor class). The problem is that the actor doesn't proccess input, but if in my screen I delete OrthographicCamera the Actor proccess the input, so, with OrthographicCamera Actor doesn't proccess input but it works if I remove OrthographicCamera.
Any advice?
I have the following code
public class Test implements Screen {
private Game game;
private Stage stage;
private MemoryActor actor;
private AssetManager manager;
private boolean loaded = false;
float width, height;
private OrthographicCamera camera;
public Test(Game game){
this.game = game;
stage = new Stage();
manager = new AssetManager();
manager.load("img.png",Texture.class);
manager.load("img1.png",Texture.class);
InputMultiplexer im = new InputMultiplexer();
im.addProcessor(stage);
Gdx.input.setInputProcessor(im);
height = Gdx.graphics.getHeight();
width = Gdx.graphics.getWidth();
camera = new OrthographicCamera(width, height);
camera.position.set(((width / 2)), ((height / 2)), 0);
camera.update();
stage.setViewport(new ExtendViewport(300,300, camera));
}
public void createActor(){
Texture back = manager.get("img.png", Texture.class);
actor = new MemoryActor(manager.get("img1.png", Texture.class), back,0,0,50,50);
actor.setInputListener(new InputListener(){
#Override
public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
System.out.println("down");
return true;
}
#Override
public void touchUp(InputEvent event, float x, float y, int pointer, int button) {
System.out.println("up");
}
});
stage.addActor(actor);
}
#Override
public void render(float delta) {
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
if (manager.update()){
if (!loaded){
createActor();
loaded = true;
}
}
stage.draw();
}
#Override
public void resize(int width, int height) {
}
#Override
public void show() {
}
#Override
public void hide() {
}
#Override
public void pause() {
}
#Override
public void resume() {
}
#Override
public void dispose() {
}
}
and MemoryActor:
public class MemoryActor extends Actor {
...
public MemoryActor(){}
public MemoryActor(Texture texture, Texture texBack, float x, float y, float width, float height){
...
}
public void setInputListener(InputListener il){
addListener(il);
}
#Override
public void draw(Batch batch, float alpha){
...
}
}
Just registering the stage as an InputProcessor isn't enough. You also need to trigger the event processing of Stage via stage.act() in every frame.
Furthermore you need to properly update the stage's Viewport when a resize event occurs. This can be done via stage.getViewport().update(width, height, true). Otherwise the stage will process the events based on incorrect assumptions about the screen size and might also render your stage not the way you want it. The true is important because it will also center the camera on the new screen size, which is necessary in case of UIs.