Есть ли эффективный способ загрузки текстур в виде изображений .PNG для простой 2D-игры - PullRequest
0 голосов
/ 15 октября 2019

Я пишу простую 2D-игру на основе блоков (похожую на Minecraft), чтобы начать работать с Java. У меня много игровой механики, такой как работа мирового поколения, но загрузка текстур громоздка и неэффективна. В основном все текстуры загружаются по отдельности с помощью функции загрузки (текстуры размером 16x16 .PNG). У каждого блока в игре есть имя (например, «камень», «грязь»), и когда игра рендерит плитку, она загружает текстуру из класса textureManager. Это работает, но я должен жестко закодировать каждую загрузку и возврат текстуры. То, что я хочу сделать, это заставить текстуры загружаться автоматически, но при этом иметь строковые индексы. Например, я хочу, чтобы игра загрузила все файлы .PNG в папку текстур, но сохранила их имя, чтобы я мог сказать что-то вроде loadTexture («камень»).

Вот мой текущий код менеджера текстур. Он просто загружает каждый файл при запуске игры и затемняет каждый для фоновых плиток. Добавлять новые текстуры неудобно и просто неэффективно.

package graphics;

import java.awt.image.BufferedImage;
import java.awt.image.RescaleOp;

public class textureManager {
    private static final BufferedImage darken(BufferedImage image) {
        return new RescaleOp(.7f, 0, null).filter(image, null);
    }

    private static final String path = "/assets/textures/";
    // textures 
    public static final BufferedImage unknown   = renderer.loadImage(path + "unknown.png");
    private static final BufferedImage stone    = renderer.loadImage(path + "stone.png");
    private static final BufferedImage grass    = renderer.loadImage(path + "grass.png");
    private static final BufferedImage dirt     = renderer.loadImage(path + "dirt.png");
    private static final BufferedImage iron_ore = renderer.loadImage(path + "iron_ore.png");
    private static final BufferedImage coal_ore = renderer.loadImage(path + "coal_ore.png");
    private static final BufferedImage diamond_ore = renderer.loadImage(path + "diamond_ore.png");
    private static final BufferedImage bedrock  = renderer.loadImage(path + "bedrock.png");
    private static final BufferedImage andesite = renderer.loadImage(path + "andesite.png");
    private static final BufferedImage granite  = renderer.loadImage(path + "granite.png");
    private static final BufferedImage marble   = renderer.loadImage(path + "marble.png");

    // darkened textures
    private static final BufferedImage dark_stone = darken(stone);
    private static final BufferedImage dark_grass = darken(grass);
    private static final BufferedImage dark_dirt = darken(dirt);
    private static final BufferedImage dark_iron_ore = darken(iron_ore);
    private static final BufferedImage dark_coal_ore = darken(coal_ore);
    private static final BufferedImage dark_diamond_ore = darken(diamond_ore);
    private static final BufferedImage dark_andesite = darken(andesite);
    private static final BufferedImage dark_granite = darken(granite);
    private static final BufferedImage dark_marble = darken(marble);

    // animations
    private static Animation player   = new Animation("player");
    private static Animation lava     = new Animation("lava");
    private static Animation plant_01 = new Animation("plant_01");

    /** returns the textures of the name provided */
    public static BufferedImage getTexture(String name, boolean darken) {
        if(name.equals("air")) return null;
        if(name.equals("stone"))    if(!darken) return stone;           else return dark_stone;
        if(name.equals("grass"))    if(!darken) return grass;           else return dark_grass;
        if(name.equals("dirt"))     if(!darken) return dirt;            else return dark_dirt;
        if(name.equals("iron_ore")) if(!darken) return iron_ore;        else return dark_iron_ore;
        if(name.equals("coal_ore")) if(!darken) return coal_ore;        else return dark_coal_ore;
        if(name.equals("diamond_ore")) if(!darken) return diamond_ore;  else return dark_diamond_ore;
        if(name.equals("bedrock"))  return bedrock;
        if(name.equals("andesite")) if(!darken) return andesite;        else return dark_andesite;
        if(name.equals("granite"))  if(!darken) return granite;         else return dark_granite;
        if(name.equals("marble"))   if(!darken) return marble;          else return dark_marble;

        return unknown;
    }

    /** returns the animation of the name provided */
    public static Animation getAnimation(String name) {
        if(name.equals("player"))   return player;
        if(name.equals("lava"))     return lava;
        if(name.equals("plant_01")) return plant_01;

        return null;
    }
}

Пока этот код работает, я хотел бы добавить более эффективный способ, но я не знаю как. Я думал об использовании списка массивов для хранения изображений, но тогда он не принял бы строковый индекс. Возможно, пользовательский класс, который может управлять списком массивов и индексом имен?

примечание: анимации - это пользовательский класс, который анимирует текстуры, загружаемые из папки. их можно игнорировать.

1 Ответ

0 голосов
/ 15 октября 2019

Требуется Карта .

Распространенной (вероятно, самой распространенной) реализацией Map является HashMap :

private static final Map<String, Map<Boolean, BufferedImage>> textures;

static {
    String[] baseNames = {
        "unknown",
        "stone",
        "grass",
        "dirt",
        "iron_ore",
        "coal_ore",
        "diamond_ore",
        "bedrock",
        "andesite",
        "granite",
        "marble",
    };

    Map<String, Map<Boolean, BufferedImage>> map = new HashMap<>();
    for (String baseName : baseNames) {
        BufferedImage regular =
            renderer.loadImage(path + baseName + ".png");
        BufferedImage dark = darken(regular);

        Map<Boolean, BufferedImage> regularAndDark = new HashMap<>(2);
        regularAndDark.put(false, regular);
        regularAndDark.put(true, dark);

        map.put(baseName, Collections.unmodifiableMap(regularAndDark));
    }

    textures = Collections.unmodifiableMap(map);
}

/** returns the textures of the name provided */
public static BufferedImage getTexture(String name, boolean darken) {
    Map<Boolean, BufferedImage>> matches = textures.get(name);
    assert matches != null : "No textures for name \"" + name + "\"";
    return matches.get(darken);
}

Однако… Есть возможности для улучшения:

  • Как видите, я добавил оператор assert, чтобы убедиться, что недопустимые строковые аргументы не предоставлены. Этого можно избежать, используя значение enum в качестве ключа изображения вместо строки, что исключает возможность неправильных написаний.
  • Map<Boolean, BufferedImage>> немного сложнее для понимания. Я бы сделал простой внутренний класс для хранения обоих, поэтому структура более очевидна.

Имея это в виду, это выглядит так:

public enum TextureType {
    UNKNOWN,
    STONE,
    GRASS,
    DIRT,
    IRON_ORE,
    COAL_ORE,
    DIAMOND_ORE,
    BEDROCK,
    ANDESITE,
    GRANITE,
    MARBLE,
}

private static class Textures {
    final BufferedImage regular;
    final BufferedImage dark;

    Textures(BufferedImage regular) {
        this.regular = Objects.requireNonNull(regular,
            "Image cannot be null");
        this.dark = darken(regular);
    }
}

private static final Map<TextureType, Textures> textures;

static {
    Map<TextureType, Textures> map = new EnumMap(TextureType.class);
    for (TextureType type : TextureType.values()) {
        map.put(type,
            new Textures(renderer.loadImage(path + baseName + ".png")));
    }

    textures = Collections.unmodifiableMap(map);
}

/** returns the textures of the name provided */
public static BufferedImage getTexture(TextureType type, boolean darken) {
    Objects.requireNonNull(type, "Texture type cannot be null");
    Textures match = textures.get(type);
    return darken ? match.dark : match.regular;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...