I would like to write my own tips to make the layout adjustment that I started recently a little more efficient. There is no novelty, so thank you. If there is something better or better, I would be grateful if you could respond as a comment in this article or as a comment.
Development using the following
When using Scene2d, the coordinate position is roughly the local coordinates confined in the Group, so Due to the layout of the screen, the adjustment of the position does not deviate so much.
However, at the stage of fine position adjustment (for example, the stage of making a screen mock), it is necessary to change the coordinate position and size of the screen UI many times. So if you just want to do it, you'll have to recompile your Java classes each time. Although I'm no longer suffering from Java compilation speed these days (citation needed), compiling and relaunching every time for simple alignment is ridiculous and time consuming.
Use PropertiesUtils to have layout related values in the properties file. This Util has been summarized earlier. Notes on Utils class of libGDX
Hoge.java
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.Group;
import com.badlogic.gdx.utils.ObjectMap;
import com.badlogic.gdx.utils.PropertiesUtils;
import com.badlogic.gdx.utils.StreamUtils;
import java.io.IOException;
import java.io.InputStreamReader;
/**
* Created by yy_yank on 2017/02/01.
*/
public class Hoge extends Group {
ObjectMap<String, String> debugCache = new ObjectMap<>();
public Hoge() {
//Various processing
try{
InputStreamReader reader = new InputStreamReader(getClass().getClassLoader().getResourceAsStream("debugCache.properties"));
PropertiesUtils.load(debugCache, reader);
StreamUtils.closeQuietly(reader);
} catch (IOException e) {
//I'm squeezing it with all my strength, but don't imitate a good girl
}
init();
}
private void init() {
Actor a = new Actor();
a.setScale(Float.parseFloat(debugCache.get("a.scale")));
addActor(a);
a.setPosition(Float.parseFloat(debugCache.get("a.x")), Float.parseFloat(debugCache.get("a.y")));
Actor b = new Actor();
b.setPosition(Float.parseFloat(debugCache.get("b.x")), Float.parseFloat(debugCache.get("b.y")));
addActor(b);
Actor c = new Actor();
c.setPosition(Float.parseFloat(debugCache.get("c.x")), Float.parseFloat(debugCache.get("c.y")));
addActor(c);
Actor d = new Actor();
d.setPosition(Float.parseFloat(debugCache.get("d.x")), Float.parseFloat(debugCache.get("d.y")));
addActor(d);
}
}
And put the property file in a place like android / assets.
debugCache.properties
a.x=100
a.y=200
a.scale=1
b.x=100
b.y=300
c.x=100
c.y=400
d.x=100
d.y=500
It's kind of muddy, but this way you can adjust the layout using the values in the properties file.
Then launch the game app. It is supposed to start the desktop version.
We will adjust the position of the Actors such as a, b, c, and d in the source code we wrote earlier. Specifically, a file called debugCache.properties should have been generated under desktop / build / resources / main, so I will play with that. By doing so, the position can be adjusted each time an Actor of the Hoge class is instantiated. What a crap. But it's faster than compiling every time.
There is no trick just to parse this ```ObjectMap <String, String> `` `type every time and convert it to a numeric type. Assuming that the coordinates, scale, and font size have been decided, I will write the code to rewrite the source code so that the values in the property file are applied. Please close your eyes to see that it is equivalent to Java SE 7 with a little skipping.
import com.badlogic.gdx.utils.ObjectMap;
import com.badlogic.gdx.utils.PropertiesUtils;
import com.badlogic.gdx.utils.StreamUtils;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Arrays;
import java.util.List;
public class DebugPropertiesConverter implements InternalConverter{
private ObjectMap<String, String> debugCache;
CacheConfigurationFactory factory = new CacheConfigurationFactory() {
@Override
public String createCacheVariableName() {
return "debugCache";
}
@Override
public String createCacheFileName() {
return "debugCache.properties";
}
@Override
public List<Pair<String, String>> cretateTargetStringPair(String between, String value) {
return Arrays.asList(
new Pair<>("Integer.parseInt("+createCacheVariableName()+".get(\""+ between +"\"))", value),
new Pair<>("Double.parseDouble("+createCacheVariableName()+".get(\""+ between +"\"))", value),
new Pair<>("Float.parseFloat("+createCacheVariableName()+".get(\""+ between +"\"))", value + "f")
);
}
};
public static void main(String[] args) throws Exception {
new DebugPropertiesConverter().run();
}
public void run() throws Exception {
FileVisitor<Path> visitor = new InternalFileVisitor(this);
Files.walkFileTree(Paths.get("/Users/yy_yank/work/projects/hogehoge/core/src/com/github/yyYank"
,"your"
,"package"
,"dir"
), visitor);
}
@Override
public ObjectMap<String, String> readBetweenString() throws IOException {
if(debugCache != null) {
return debugCache;
}
InputStreamReader reader = new InputStreamReader(getClass().getClassLoader().getResourceAsStream(factory.createCacheFileName()));
this.debugCache = new ObjectMap<>();
PropertiesUtils.load(debugCache, reader);
System.out.println(debugCache);
StreamUtils.closeQuietly(reader);
return debugCache;
}
@Override
public void executeOnFile(Path file, BasicFileAttributes attrs) throws IOException {
ObjectMap<String, String> betweens = readBetweenString();
List<String> lines = Files.readAllLines(file, Charset.forName("UTF-8"));
boolean isRewrite = false;
for (ObjectMap.Entry<String, String> between : betweens) {
List<Pair<String, String>> targets = factory.cretateTargetStringPair(between.key, between.value);
for (Pair<String, String> target : targets) {
for (int i = 0; i < lines.size(); i++) {
String line = lines.get(i);
isRewrite = isRewrite ? isRewrite : line.contains(target.key);
lines.set(i, line.replace(target.key, target.value));
}
}
}
// rewrite
if(isRewrite) {
System.out.println("rewrite file.... -> " + file.getFileName());
Files.write(file, lines, Charset.forName("UTF-8"));
} else {
System.out.println("[SKIP]" + file.getFileName());
}
}
@Override
public void executeOnDir(Path dir, BasicFileAttributes attrs) {
// do nothing
}
@Override
public boolean filterExtension(Path file) {
return file.getFileName().toString().endsWith("java");
}
}
interface CacheConfigurationFactory {
String createCacheVariableName();
String createCacheFileName();
List<Pair<String, String>> cretateTargetStringPair(String between, String value);
}
interface InternalConverter {
ObjectMap<String, String> readBetweenString() throws IOException;
void executeOnFile(Path file, BasicFileAttributes attrs) throws IOException;
void executeOnDir(Path dir, BasicFileAttributes attrs);
boolean filterExtension(Path file);
}
class InternalFileVisitor implements FileVisitor<Path> {
private final DebugPropertiesConverter converter;
public InternalFileVisitor(DebugPropertiesConverter converter) {
this.converter = converter;
}
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
print("preVisitDirectory : " + dir.getFileName());
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
if(converter.filterExtension(file)) {
print("visitFile : " + file.getFileName());
converter.executeOnFile(file, attrs);
} else {
print("[SKIP]visitFile : " + file.getFileName());
}
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
String error = String.format(" [exception=%s, message=%s]", exc.getClass(), exc.getMessage());
print("visitFileFailed : " + file.getFileName() + error);
return FileVisitResult.CONTINUE;
}
protected void print(String message) {
System.out.println(message);
}
}
class Pair<K, V>{
public final K key;
public final V value;
Pair(K k, V v) {
this.key = k;
this.value = v;
}
}
When you do this, the source code will be rewritten to the values set in the properties file.
I'm grateful for static typing, but I also want to make it dynamic! I really want to feel smarter.
Recommended Posts