Làm thế nào để xử lý các tỷ lệ khung hình khác nhau trong libGDX?


81

Tôi đã triển khai một số màn hình sử dụng libGDX rõ ràng sẽ sử dụng Screenlớp được cung cấp bởi khung libGDX. Tuy nhiên, việc triển khai cho các màn hình này chỉ hoạt động với các kích thước màn hình được xác định trước. Ví dụ: nếu sprite được dành cho màn hình có kích thước 640 x 480 (Tỷ lệ co 4: 3), thì nó sẽ không hoạt động như dự định trên các kích thước màn hình khác vì sprite đi ngang với ranh giới màn hình và không được chia tỷ lệ với kích thước màn hình ở tất cả. Hơn nữa, nếu libGDX cung cấp tỷ lệ đơn giản, thì vấn đề mà tôi đang gặp phải vẫn còn đó vì điều đó sẽ khiến tỷ lệ khung hình của màn hình trò chơi thay đổi.

Sau khi nghiên cứu trên internet, tôi tình cờ thấy một blog / diễn đàn đã thảo luận về vấn đề tương tự. Tôi đã thực hiện nó và cho đến nay nó đang hoạt động tốt. Nhưng tôi muốn xác nhận liệu đây có phải là lựa chọn tốt nhất để đạt được điều này hay liệu có những lựa chọn thay thế tốt hơn hay không. Dưới đây là mã để hiển thị cách tôi giải quyết vấn đề hợp pháp này.

LIÊN KẾT DIỄN ĐÀN: http://www.java-gaming.org/index.php?topic=25685.new

public class SplashScreen implements Screen {

    // Aspect Ratio maintenance
    private static final int VIRTUAL_WIDTH = 640;
    private static final int VIRTUAL_HEIGHT = 480;
    private static final float ASPECT_RATIO = (float) VIRTUAL_WIDTH / (float) VIRTUAL_HEIGHT;

    private Camera camera;
    private Rectangle viewport;
    // ------end------

    MainGame TempMainGame;

    public Texture splashScreen;
    public TextureRegion splashScreenRegion;
    public SpriteBatch splashScreenSprite;

    public SplashScreen(MainGame maingame) {
        TempMainGame = maingame;
    }

    @Override
    public void dispose() {
        splashScreenSprite.dispose();
        splashScreen.dispose();
    }

    @Override
    public void render(float arg0) {
        //----Aspect Ratio maintenance

        // update camera
        camera.update();
        camera.apply(Gdx.gl10);

        // set viewport
        Gdx.gl.glViewport((int) viewport.x, (int) viewport.y,
        (int) viewport.width, (int) viewport.height);

        // clear previous frame
        Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);

        // DRAW EVERYTHING
        //--maintenance end--

        splashScreenSprite.begin();
        splashScreenSprite.disableBlending();
        splashScreenSprite.draw(splashScreenRegion, 0, 0);
        splashScreenSprite.end();
    }

    @Override
    public void resize(int width, int height) {
        //--Aspect Ratio Maintenance--
        // calculate new viewport
        float aspectRatio = (float)width/(float)height;
        float scale = 1f;
        Vector2 crop = new Vector2(0f, 0f);

        if(aspectRatio > ASPECT_RATIO) {
            scale = (float) height / (float) VIRTUAL_HEIGHT;
            crop.x = (width - VIRTUAL_WIDTH * scale) / 2f;
        } else if(aspectRatio < ASPECT_RATIO) {
            scale = (float) width / (float) VIRTUAL_WIDTH;
            crop.y = (height - VIRTUAL_HEIGHT * scale) / 2f;
        } else {
            scale = (float) width / (float) VIRTUAL_WIDTH;
        }

        float w = (float) VIRTUAL_WIDTH * scale;
        float h = (float) VIRTUAL_HEIGHT * scale;
        viewport = new Rectangle(crop.x, crop.y, w, h);
        //Maintenance ends here--
    }

    @Override
    public void show() {
        camera = new OrthographicCamera(VIRTUAL_WIDTH, VIRTUAL_HEIGHT); //Aspect Ratio Maintenance

        splashScreen = new Texture(Gdx.files.internal("images/splashScreen.png"));
        splashScreenRegion = new TextureRegion(splashScreen, 0, 0, 640, 480);
        splashScreenSprite = new SpriteBatch();

        if(Assets.load()) {
            this.dispose();
            TempMainGame.setScreen(TempMainGame.mainmenu);
        }
    }
}

CẬP NHẬT: Gần đây tôi đã biết rằng libGDX có một số chức năng riêng để duy trì tỷ lệ khung hình mà tôi muốn thảo luận ở đây. Trong khi tìm kiếm vấn đề về tỷ lệ khung hình trên internet, tôi đã gặp một số diễn đàn / nhà phát triển có vấn đề này là "Làm thế nào để duy trì tỷ lệ khung hình trên các kích thước màn hình khác nhau?" Một trong những giải pháp thực sự hiệu quả với tôi đã được đăng ở trên.

Sau đó, khi tôi tiếp tục triển khai các touchDown()phương thức cho màn hình, tôi thấy rằng do thay đổi tỷ lệ khi thay đổi kích thước, các tọa độ mà tôi đã thực hiện touchDown()sẽ thay đổi rất nhiều. Sau khi làm việc với một số mã để dịch các tọa độ phù hợp với thay đổi kích thước màn hình, tôi đã giảm số lượng này xuống rất nhiều nhưng tôi đã không thành công trong việc duy trì chúng với độ chính xác của điểm pin. Ví dụ: nếu tôi đã triển khai touchDown()trên một kết cấu, việc thay đổi kích thước màn hình sẽ dịch chuyển TouchListener trên vùng kết cấu một số pixel sang phải hoặc trái, tùy thuộc vào việc thay đổi kích thước và điều này rõ ràng là không mong muốn.

Sau đó, tôi biết rằng lớp sân khấu có chức năng riêng để duy trì tỷ lệ khung hình ( boolean stretch = false). Bây giờ tôi đã triển khai màn hình của mình bằng cách sử dụng lớp sân khấu, tỷ lệ khung hình được duy trì tốt bởi nó. Tuy nhiên khi thay đổi kích thước hoặc các kích thước màn hình khác nhau, vùng màu đen được tạo ra luôn xuất hiện ở phía bên phải của màn hình; đó là màn hình không được căn giữa, điều này làm cho nó khá xấu nếu vùng đen về cơ bản là lớn.

Bất kỳ thành viên nào trong cộng đồng có thể giúp tôi giải quyết vấn đề này không?


Bạn có thể liên kết đến blog hoặc diễn đàn có cùng vấn đề không?
Steve Blackwell

@SteveBlackwell đây là liên kết: java-gaming.org/index.php?topic=25685.new
Rafay

@SteveBlackwell Vui lòng xem câu hỏi cập nhật và xem bạn có thể giúp gì về vấn đề này không.
Rafay

1
Tôi không biết nhiều về Sân khấu, nhưng nhìn vào đây , có vẻ như điều này đã được sửa chữa. Các tài liệu không giúp ích quá nhiều cho việc căn giữa. Có thể bạn có thể di chuyển máy ảnh qua một chút hoặc điều chỉnh chế độ xem.
Steve Blackwell

1
Vâng, vấn đề của tôi thực sự đã được giải quyết. Tôi đăng nó như một câu trả lời.
Rafay

Câu trả lời:


51

Làm thế nào để làm điều đó ngày nay:

Vì đây là một trong những câu hỏi nổi tiếng nhất trên libgdx ở đây, nên tôi sẽ cập nhật một chút :

LibGDX v1.0 được giới thiệu Viewportđể xử lý vấn đề này. Nó dễ sử dụng hơn rất nhiều và chiến lược mở rộng quy mô có thể cắm được, có nghĩa là một dòng duy nhất có thể thay đổi hành vi và bạn có thể chơi với nó và xem dòng nào phù hợp với trò chơi của bạn nhất.

Mọi thứ bạn cần biết về nó có thể được tìm thấy ở đây .


Tôi đã thử nó nhưng vẫn là TextureRegionStreches của tôi . Nếu để reszie(int width, int height)trống chức năng. Anh TextureRegionấy không cứng đầu.
Muhammad Babar,

Nhưng ngay cả khi sử dụng Viewport, chúng ta phải sử dụng các kết cấu riêng biệt cho các tỷ lệ khung hình khác nhau, phải không? Có logic nào để đơn giản hóa phương pháp đó không? Hay thiết kế đồ họa nên được thực hiện bằng cách giữ cho nó độc lập với các độ phân giải?
WeirdElfB0y

Hướng dẫn hay về cách sử dụng Viewports: gamefromscratch.com/post/2014/12/09/…
ubzack 14/02/16

Thêm vào câu trả lời này và trả lời các câu hỏi trong nhận xét, bạn có thể vẽ thế giới của mình theo tỷ lệ 16: 9 và tăng thêm chiều cao cho kết cấu của mình để có thể tải ở 4: 3. Sau đó, khi khởi tạo khung nhìn, bạn có thể kiểm tra tỷ lệ khung hình thực tế của màn hình và trong khi vẫn giữ tiêu chuẩn chiều rộng, bạn có thể cung cấp chiều cao cho khung nhìn của mình phù hợp với tỷ lệ khung hình bạn đang chạy. Một nhược điểm là bạn sẽ có nhiều không gian "trống" trên 4: độ phân giải 3, nhưng nếu kết cấu của bạn có thể xử lý đó, mọi thứ sẽ hiển thị như rút ra và không kéo dài
Klitos G.

24

CHỈNH SỬA: libGDX đã phát triển. Câu trả lời tốt nhất bây giờ là câu trả lời này của người dùng. Đảm bảo kiểm tra liên kết đến Viewporttài liệu. .

Khi SteveBlack đăng liên kết của vấn đề đã được báo cáo trong lớp sân khấu, tôi đến đó chỉ để phát hiện ra rằng vấn đề (thực ra không phải là vấn đề của tôi) đã được giải quyết trong các đêm diễn gần nhất.

Sau khi nghiên cứu ở đây và ở đó trên internet về vấn đề tôi đang gặp phải, tôi không thể tìm thấy bất kỳ giải pháp nào nên quyết định liên hệ trực tiếp với người đã báo cáo lỗi. Sau đó, anh ấy đã trả lời tôi trên các diễn đàn libgdx và tôi biết ơn anh ấy vì đã giúp đỡ tôi. Đây là liên kết

Đó là một dòng mã duy nhất và tất cả những gì bạn phải làm là:

Trong thay đổi kích thước () methond:

stage.setViewport(640, 480, false);
stage.getCamera().position.set(640/2, 480/2, 0);

Trong đó 640 X 480 là độ phân giải của TextureRegion mô tả tỷ lệ khung hình dự kiến. Nếu kích thước TextureRegion của bạn là 320 X 240, thì cả hai đối số phải được thay đổi thành độ phân giải mới để thực hiện thủ thuật.

Liên kết hồ sơ của người ban đầu đã thực sự giải quyết vấn đề của tôi


6
Trong libGDX 0.9.6, stage.setViewport (640, 480, false) là đủ vì nó đã bao gồm một lệnh gọi tới stage.getCamera () .position.set (640/2, 480/2, 0);
Alexis Pautrot

1
setViewportvới 3 tham số hiện không có sẵn!
Muhammad Babar

Tôi đã thay đổi câu trả lời được chấp nhận thành câu trả lời do người dùng không đăng như Steve Blackwell đã chỉ ra vì nó có vẻ là câu mới nhất và chính xác.
Rafay

7

Các thanh màu đen ở bên trái / phải hoặc trên / dưới trông đẹp hơn là chỉ làm biến dạng toàn bộ cảnh của bạn để vừa với màn hình. Nếu bạn nhắm mục tiêu tỷ lệ khung hình ở giữa các phạm vi có thể (4: 3 có thể là cấp thấp, 16: 9 có thể là cấp cao), thì các thanh này sẽ ở mức nhỏ đối với hầu hết các thiết bị. Điều này cũng cho phép bạn sử dụng hầu hết màn hình ngay cả trên màn hình lớn hơn và bạn đã có mã của anh chàng đó nên việc này khá dễ dàng. Sẽ còn đẹp hơn nếu đây chỉ là một tùy chọn được tích hợp sẵn trong libgdx.

Nhưng, tôi nghĩ cách tốt nhất là sử dụng toàn bộ màn hình. Tôi vẫn chưa làm điều này, nhưng nó sẽ là cách tiếp cận mà tôi thực hiện cho dự án tiếp theo của mình. Ý tưởng là nếu ai đó có màn hình rộng hơn, thì họ nên xem nhiều hơn ở các cạnh. Chris Pruett nói về cách thực hiện điều này trong một buổi nói chuyện của anh ấy ( liên kết đến điểm trong cuộc nói chuyện - thực tế, toàn bộ sự việc thực sự khá tốt). Ý tưởng là chia tỷ lệ chiều cao, sau đó đặt khung nhìn của bạn đủ rộng để vừa với màn hình. OpenGL nên quan tâm đến việc hiển thị phần còn lại. Cách anh ấy làm là ở đây .

Đối với libgdx, có lẽ có một cách dễ dàng để thực hiện điều này với scene2d bằng cách di chuyển camera qua sân khấu, nhưng tôi chưa bao giờ thực sự làm việc với scene2d. Trong mọi trường hợp, chạy ứng dụng dưới dạng một cửa sổ có thể thay đổi kích thước gốc là một cách dễ dàng hơn nhiều để kiểm tra nhiều kích thước màn hình so với việc tạo một loạt AVD.


"Trong mọi trường hợp, chạy ứng dụng dưới dạng một cửa sổ có thể thay đổi kích thước gốc là một cách dễ dàng hơn nhiều để kiểm tra nhiều kích thước màn hình so với việc tạo một loạt AVD." Nói hay lắm. thực sự tôi đang cố gắng giải quyết hiện tượng này để đảm bảo tính tương thích tối đa thay vì nhắm mục tiêu các kích thước màn hình khác nhau với các kích thước kết cấu khác nhau.
Rafay


3

Lớp ResolutionFileResolver cho phép bạn phân giải các tên tệp thành độ phân giải tốt nhất. Tôi tin rằng điều đó cũng sẽ hoạt động với các tỷ lệ khung hình khác nhau, miễn là bạn đã tạo các sprite cho các tỷ lệ khung hình đó. Có một ví dụ sử dụng trong AssetManagerTest .


0

Tôi đang sử dụng phương pháp đó từ http://blog.acamara.es/2012/02/05/keep-screen-aspect-ratio-with-dierence-resolutions-using-libgdx/

Tôi đã thực hiện chức năng này để trả về điểm trên màn hình được chạm, thu nhỏ đến vị trí trò chơi bạn muốn.

public Vector2 getScaledPos(float x, float y) {
    float yR = viewport.height / (y - viewport.y);
    y = CAMERA_HEIGHT / yR;

    float xR = viewport.width / (x - viewport.x);
    x = CAMERA_WIDTH / xR;

    return new Vector2(x, CAMERA_HEIGHT - y);
}

Sử dụng tính năng này trong thao tác chạm xuống / lên / kéo và bạn có thể kiểm tra tính năng chạm trong trò chơi của mình.


Điều này có vẻ giống với phương pháp không dự án của máy ảnh. Nó có làm điều tương tự không?
milosmns

@milosmns đây là cách tôi đã làm như 1 năm trước, tốt tôi phát hiện ra điều unproject .. là tốt khi máy ảnh của bạn luôn ở vị trí tương đương ..
Boldijar Paul

0

Mã này phù hợp với tôi với bản cập nhật mới nhất: * OrthographicCamera là cam, điều này không giúp cắt xén, chỉ thay đổi chế độ xem để chiều rộng vẫn lớn hơn "nhiều" lần so với cửa sổ / thiết bị thực tế

public void resize(int width, int height) {
    int newW = width, newH = height;
    if (cam.viewportWidth > width) {
        float scale = (float) cam.viewportWidth / (float) width;
        newW *= scale;
        newH *= scale;
    }

    // true here to flip the Y-axis
    cam.setToOrtho(true, newW, newH);
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.