LibGDX如何阻止玩家动画的拉伸

时间:2015-01-24 14:56:20

标签: java animation libgdx sprite stretching

对于我在学校的A级计算项目,我一直在创建一个类似于Mega-Man游戏风格的2D平台。到目前为止,我已经获得了一些菜单和播放器方法,如碰撞检测,移动和动画。我的所有播放器动画都在单个精灵表中,大小不一,所以它们并不完全相同(79x136,118x128,88x136等)。

这是我的具体问题:

然而,当玩游戏并通过移动和跳跃观看动画时,似乎动画的尺寸被拉伸或压缩成特定尺寸,我认为是118x140

我这样说是因为其中一个静态动画,当播放器静止不动时,以正确的大小正确显示,并且符合此分辨率,似乎所有其他动画都试图适合相同的尺寸

我已经浏览了互联网和这个网站,看看是否有人有类似的问题,但似乎没有人直接与这个问题有关,因此我为什么要求解决这个问题。

这是我的Player.java文件中使用的代码,其中定义了播放器的所有方面:

package com.ultimate.robot.entities;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.InputProcessor;
import com.badlogic.gdx.graphics.g2d.Animation;
import com.badlogic.gdx.graphics.g2d.Batch;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.maps.tiled.TiledMapTileLayer;
import com.badlogic.gdx.maps.tiled.TiledMapTileLayer.Cell;
import com.badlogic.gdx.math.Vector2;

public class Player extends Sprite implements InputProcessor {

    /** the movement velocity */
    private Vector2 velocity = new Vector2();

    private float speed = 240 * 2, gravity = 180 * 1.8f, animationTime = 0;

    private boolean canJump;

    private Animation still, left, right, jump, fall;
    private TiledMapTileLayer collisionLayer;

    private String blockedKey = "blocked";

    public Player(Animation still, Animation left, Animation right, 
                  Animation jump, Animation fall, TiledMapTileLayer 
                  collisionLayer) {

        super(still.getKeyFrame(0));
        this.still = still;
        this.left = left;
        this.right = right;
        this.jump = jump;
        this.fall = fall;
        this.collisionLayer = collisionLayer;
    }

    public void draw(Batch spriteBatch) {
        update(Gdx.graphics.getDeltaTime());
        super.draw(spriteBatch);
    }

    public void update(float delta) {
        // apply gravity
        velocity.y -= gravity * delta;

        // clamp velocity
        if(velocity.y > speed)
            velocity.y = speed;
        else if(velocity.y < -speed)
            velocity.y = -speed;

        // save old position
        float oldX = getX(), oldY = getY();
        boolean collisionX = false, collisionY = false;

        // move on x
        setX(getX() + velocity.x * delta);

        if(velocity.x < 0) // going left
            collisionX = collidesLeft();
        else if(velocity.x > 0) // going right
            collisionX = collidesRight();

        // react to x collision
        if(collisionX) {
            setX(oldX);
            velocity.x = 0;
        }

        // move on y
        setY(getY() + velocity.y * delta * 5f);

        if(velocity.y < 0) // going down
            canJump = collisionY = collidesBottom();
        else if(velocity.y > 0) // going up
            collisionY = collidesTop();

        // react to y collision
        if(collisionY) {
            setY(oldY);
            velocity.y = 0;
        }

        // update animation
        animationTime += delta;
        setRegion(velocity.x < 0 ? left.getKeyFrame(animationTime) :
                  velocity.x > 0 ? right.getKeyFrame(animationTime) :
                  velocity.y > 0 ? jump.getKeyFrame(animationTime) : 
                  velocity.y < 0 ? fall.getKeyFrame(animationTime) : 
                  still.getKeyFrame(animationTime));
    }

    private boolean isCellBlocked(float x, float y) {
        Cell cell = collisionLayer.getCell(
              (int) (x / collisionLayer.getTileWidth()), 
              (int) (y / collisionLayer.getTileHeight()));

        return cell != null && cell.getTile() != null 
           && cell.getTile().getProperties().containsKey(blockedKey);
    }

    public boolean collidesRight() {
        for(float step = 0; step < getHeight();
          step += collisionLayer.getTileHeight() / 2)
            if(isCellBlocked(getX() + getWidth(), getY() + step))
                return true;
        return false;
    }

    public boolean collidesLeft() {
        for(float step = 0; step < getHeight(); 
         step += collisionLayer.getTileHeight() / 2)
            if(isCellBlocked(getX(), getY() + step))
                return true;
        return false;
    }

    public boolean collidesTop() {
        for(float step = 0; step < getWidth();
          step += collisionLayer.getTileWidth() / 2)
            if(isCellBlocked(getX() + step, getY() + getHeight()))
                return true;
        return false;

    }

    public boolean collidesBottom() {
        for(float step = 0; step < getWidth(); 
          step += collisionLayer.getTileWidth() / 2)
            if(isCellBlocked(getX() + step, getY()))
                return true;
        return false;
    }

    public Vector2 getVelocity() {
        return velocity;
    }

    public void setVelocity(Vector2 velocity) {
        this.velocity = velocity;
    }

    public float getSpeed() {
        return speed;
    }

    public void setSpeed(float speed) {
        this.speed = speed;
    }

    public float getGravity() {
        return gravity;
    }

    public void setGravity(float gravity) {
        this.gravity = gravity;
    }

    public TiledMapTileLayer getCollisionLayer() {
        return collisionLayer;
    }

    public void setCollisionLayer(TiledMapTileLayer collisionLayer) {
        this.collisionLayer = collisionLayer;
    }

    @Override
    public boolean keyDown(int keycode) {
        switch(keycode) {
        case Keys.SPACE:
            if(canJump) {
                velocity.y = speed / 3f;
                canJump = false;
                animationTime = 0;
            }
            break;
        case Keys.LEFT:
            velocity.x = -speed;
            animationTime = 0;
            break;
        case Keys.RIGHT:
            velocity.x = speed;
            animationTime = 0;
        }
        return true;
    }

    @Override
    public boolean keyUp(int keycode) {
        switch(keycode) {
        case Keys.LEFT:
        case Keys.RIGHT:
            velocity.x = 0;
            animationTime = 0;
        }
        return true;
    }

    @Override
    public boolean keyTyped(char character) {
        return false;
    }

    @Override
    public boolean touchDown(int screenX, int screenY, 
                             int pointer, int button) {
        return false;
    }

    @Override
    public boolean touchUp(int screenX, int screenY, 
                           int pointer, int button) {
        return false;
    }

    @Override
    public boolean touchDragged(int screenX, int screenY, 
                                int pointer) {
        return false;
    }

    @Override
    public boolean mouseMoved(int screenX, int screenY) {
        return false;
    }

    @Override
    public boolean scrolled(int amount) {
        return false;
    }

}

这个下一个代码用在playersheet.sprites文件中,该文件充当玩家的纹理图集并包含区域:

playersheet.png
format: RGBA8888
filter: Nearest,Nearest
repeat: none
playerstill
  rotate: false
  xy: 363, 272
  size: 118, 140
  orig: 59, 70
  offset: 0, 0
  index: -1
playerstill
  rotate: false
  xy: 867, 152
  size: 118, 128
  orig: 59, 64
  offset: 0, 0
  index: -1
playerrun
  rotate: false
  xy: 688, 276
  size: 79, 136
  orig: 39, 68
  offset: 0, 0
  index: -1
playerrun
  rotate: false
  xy: 481, 272
  size: 92, 140
  orig: 46, 70
  offset: 0, 0
  index: -1
playerrun
  rotate: false
  xy: 767, 280
  size: 132, 132
  orig: 66, 66
  offset: 0, 0
  index: -1
playerrun
  rotate: false
  xy: 644, 140
  size: 88, 136
  orig: 44, 68
  offset: 0, 0
  index: -1
playerrun
  rotate: false
  xy: 422, 132
  size: 98, 140
  orig: 49, 70
  offset: 0, 0
  index: -1
playerrun
  rotate: false
  xy: 732, 144
  size: 135, 132
  orig: 67, 66
  offset: 0, 0
  index: -1
playershoot
  rotate: false
  xy: 592, 0
  size: 116, 136
  orig: 58, 68
  offset: 0, 0
  index: -1
playerjump
  rotate: false
  xy: 0, 44
  size: 74, 184
  orig: 74, 184
  offset: 0, 0
  index: -1
playerjumpshoot
  rotate: false
  xy: 0, 228
  size: 107, 184
  orig: 107, 184
  offset: 0, 0
  index: -1
playerfall
  rotate: false
  xy: 186, 76
  size: 108, 168
  orig: 108, 168
  offset: 0, 0
  index: -1
playerfallshoot
  rotate: false
  xy: 107, 244
  size: 124, 168
  orig: 124, 168
  offset: 0, 0
  index: -1
playerrunshoot
  rotate: false
  xy: 294, 132
  size: 128, 140
  orig: 128, 140
  offset: 0, 0
  index: -1
playerrunshoot
  rotate: false
  xy: 573, 276
  size: 115, 136
  orig: 115, 136
  offset: 0, 0
  index: -1
playerrunshoot
  rotate: false
  xy: 294, 0
  size: 150, 132
  orig: 150, 132
  offset: 0, 0
  index: -1
playerrunshoot
  rotate: false
  xy: 520, 136
  size: 124, 136
  orig: 124, 136
  offset: 0, 0
  index: -1
playerrunshoot
  rotate: false
  xy: 231, 272
  size: 132, 140
  orig: 132, 140
  offset: 0, 0
  index: -1
playerrunshoot
  rotate: false
  xy: 444, 0
  size: 148, 132
  orig: 148, 132
  offset: 0, 0
  index: -1
playerblock
  rotate: false
  xy: 708, 8
  size: 136, 132
  orig: 136, 132
  offset: 0, 0
  index: -1
playerduck
  rotate: false
  xy: 899, 304
  size: 96, 108
  orig: 96, 108
  offset: 0, 0
  index: -1
playerhurt
  rotate: false
  xy: 844, 48
  size: 136, 96
  orig: 136, 96
  offset: 0, 0
  index: -1
playervictory
  rotate: false
  xy: 74, 48
  size: 112, 180
  orig: 112, 180
  offset: 0, 0
  index: -1

最后这个代码用在我的LevelOne.java文件中,该文件包含关卡的所有元素并创建播放器:

package com.ultimate.robot.levels;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Screen;
import com.badlogic.gdx.audio.Music;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Animation;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
import com.badlogic.gdx.maps.tiled.TiledMap;
import com.badlogic.gdx.maps.tiled.TiledMapTileLayer;
import com.badlogic.gdx.maps.tiled.TmxMapLoader;
import com.badlogic.gdx.maps.tiled.renderers.OrthogonalTiledMapRenderer;
import com.ultimate.robot.entities.Player;

public class LevelOne implements Screen {

    private TiledMap map;
    private OrthogonalTiledMapRenderer renderer;
    private OrthographicCamera camera;
    private Music level1music;
    private Sprite desert, blueSky;
    private TextureAtlas playerAtlas;
    private Player player;

    @Override
    public void show() {
        map = new TmxMapLoader().load("maps/levelone.tmx");

        playerAtlas = new TextureAtlas("Sprites/playersheet.sprites");
        Animation still, left, right, jump, fall;
        still = new Animation(1/2f, playerAtlas.findRegions("playerstill"));
        left = new Animation(1 /6f, playerAtlas.findRegions("playerrun"));
        right = new Animation(1 /6f, playerAtlas.findRegions("playerrun"));
        jump = new Animation(1 /6f, playerAtlas.findRegions("playerjump"));
        fall = new Animation(1 /6f, playerAtlas.findRegions("playerfall"));
        still.setPlayMode(Animation.PlayMode.LOOP);
        left.setPlayMode(Animation.PlayMode.LOOP);
        right.setPlayMode(Animation.PlayMode.LOOP);
        jump.setPlayMode(Animation.PlayMode.NORMAL);
        fall.setPlayMode(Animation.PlayMode.NORMAL);

        player = new Player(still, left, right, jump, fall, 
                            (TiledMapTileLayer) map.getLayers().get(0));

        player.setPosition(5 * player.getCollisionLayer().getTileWidth(),
                         4 * player.getCollisionLayer().getTileHeight());

        // setting camera
        camera = new OrthographicCamera();

        // creating background
        desert = new Sprite(new Texture(
               Gdx.files.internal("Backgrounds/desert.png")));
        blueSky = new Sprite(new Texture(
               Gdx.files.internal("Backgrounds/bluesky.png")));

        // creating music
        level1music = Gdx.audio.newMusic(
               Gdx.files.internal("Music/level1.ogg"));

        // playing music
        level1music.setLooping(true);
        level1music.play();

        Gdx.input.setInputProcessor(player);

        renderer = new OrthogonalTiledMapRenderer(map);
    }

    @Override
    public void render(float delta) {
        Gdx.gl.glClearColor(0, 0, 0, 1);
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

        camera.position.set(player.getX() + player.getWidth() / 2, 
                        player.getY() + player.getHeight() / 2, 0);

        camera.update();

        renderer.setView(camera);

        renderer.getBatch().begin();
        renderer.getBatch().draw(blueSky, 0, 0);
        renderer.getBatch().draw(desert, 0, 0);
        player.draw(renderer.getBatch());
        renderer.getBatch().end();

        renderer.render();
    }

    @Override
    public void resize(int width, int height) {
        camera.viewportWidth = width;
        camera.viewportHeight = height;
    }

    @Override
    public void pause() {

    }

    @Override
    public void resume() {

    }

    @Override
    public void hide() {
        dispose();
    }

    @Override
    public void dispose() {
        map.dispose();
        renderer.dispose();
        level1music.dispose();
        playerAtlas.dispose();
        desert.getTexture().dispose();
        blueSky.getTexture().dispose();
    }

}

我希望这有助于找到解决方案。

2 个答案:

答案 0 :(得分:0)

你可以做几件事情,如果只有alpha通道“会消耗更多的内存”以避免变量大小,你可以做同样的图像,另一方面你可以检查动画是否有某些决议和设施yourSprite.size (w, h);后者, 虽然看起来比较容易,但是你必须创建一个控制sprite中你拥有的图像的函数,如果这个改变分辨率然后设置精灵的大小,那么只有在它们变得必要时才改变,如果不改变则不进行设置。在尺寸上,我希望我理解我的意思。

您的评论示例:

打开Gimp,例如创建尺寸为118x140的imagen,使用alpha通道,创建一个新图层并将图像粘贴到其中,然后将其导出为您想要的格式,例如png“您可以将图层向下合并以加入它们”但如果你有可见和导出,则没有必要。

inicial size before Gimp           size base 118x140 
 texture size 79x136        texture size 118x140 after export gimp 
 texture size 118x128       texture size 118x140 after export gimp 
 texture size 88x136        texture size 118x140 after export gimp 

yourSprite.size(118, 140);

答案 1 :(得分:0)

不用担心,我已经设法使用自定义动画类解决它,而在另一个教程中,它似乎修复了拉伸和压缩的动画。它创建了一个动画必须适合的纹理区域,但我将大小设置为最大的动画,因此它们都适合该区域。我不得不重写我的很多代码但是我对最终结果很好。