I was wondering if I am doing something wrong (most likely) or if there is an issue with LibGDX SkinLoader regarding additional resource dependencies.
According to the documentation of SkinLoader.SkinParameter you can pass an additional ObjectMap to define resources that the skin depends on.
I wanted to use that for bitmap fonts because I am creating them at runtime out of a .ttf file to be able to create the correct font sizes for the correct density/size of the target device's display.
Here is an example program that causes the issue:
public class Test extends Game {
private AssetManager assetManager;
private Skin skin;
private Batch batch;
#Override
public void create() {
this.assetManager = new AssetManager();
final FileHandleResolver resolver = new InternalFileHandleResolver();
assetManager.setLoader(FreeTypeFontGenerator.class, new FreeTypeFontGeneratorLoader(resolver));
assetManager.setLoader(BitmapFont.class, ".ttf", new FreetypeFontLoader(resolver));
final ObjectMap<String, Object> resources = new ObjectMap<String, Object>();
final FreetypeFontLoader.FreeTypeFontLoaderParameter fontParam = new FreetypeFontLoader.FreeTypeFontLoaderParameter();
fontParam.fontFileName = "ui/font.ttf";
fontParam.fontParameters.size = (int) (16 * Gdx.graphics.getDensity());
assetManager.load("font16.ttf", BitmapFont.class, fontParam);
assetManager.finishLoading();
resources.put("font_16", assetManager.get("font16.ttf", BitmapFont.class));
assetManager.load("ui/ui.json", Skin.class, new SkinLoader.SkinParameter(resources));
assetManager.finishLoading();
skin = assetManager.get("ui/ui.json", Skin.class);
batch = new SpriteBatch();
}
#Override
public void render() {
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.begin();
skin.get("font_16", BitmapFont.class).draw(batch, "Test123", 10, 10);
batch.end();
}
#Override
public void dispose() {
super.dispose();
// enabling the next line will get rid of the Pixmap already disposed exception
// skin.remove("font_16", BitmapFont.class);
assetManager.dispose();
batch.dispose();
}
}
Now when disposing the assetManager at the end of the program then I got an exception "Pixmap already disposed" because the bitmap fonts got disposed multiple times (once when the Skin gets disposed and once when the Bitmap font itself gets disposed).
To solve that for now I called skin.remove(fontName, BitMapFont.class) before assetManager.dispose() to release the bitmap fonts only once BUT imo this is not a nice way and I would expect that the dependency handling of the assetManager should take care of that.
I checked the code of the SkinLoader class and to me it seems like those passed resources are not added as a dependency of the Skin and that is why this error occurs.
My question now is:
Did I do something wrong or does anyone have a working code for that to show how the resourcesmap should be used in the correct way?
Found a topic which is related to this but seems like it never got a real answer
I have got the answer that I needed. Actually it is not an issue with the asset manager. It is an issue with the Skin class.
Look at the dispose method:
public void dispose () {
if (atlas != null) atlas.dispose();
for (ObjectMap<String, Object> entry : resources.values()) {
for (Object resource : entry.values())
if (resource instanceof Disposable) ((Disposable)resource).dispose();
}
}
It disposes any resource directly which is then not reflected in the asset manager of course and therefore the asset manager will also dispose the created bitmap fonts.
To solve this issue I wrote my own loader and Skin class. In case anyone is interested here is the code:
public class SkinLoader extends AsynchronousAssetLoader<Skin, SkinLoader.SkinParameter> {
public static class SkinParameter extends AssetLoaderParameters<Skin> {
private final String fontPath;
private final int[] fontSizesToCreate;
public SkinParameter(final String fontPath, final int... fontSizesToCreate) {
if (fontPath == null || fontPath.trim().isEmpty()) {
throw new GdxRuntimeException("fontPath cannot be null or empty");
}
if (fontSizesToCreate.length == 0) {
throw new GdxRuntimeException("fontSizesToCreate has to contain at least one value");
}
this.fontPath = fontPath;
this.fontSizesToCreate = fontSizesToCreate;
}
}
public SkinLoader(final FileHandleResolver resolver) {
super(resolver);
}
#Override
#SuppressWarnings("unchecked")
public Array<AssetDescriptor> getDependencies(final String fileName, final FileHandle file, final SkinParameter parameter) {
if (parameter == null) {
throw new GdxRuntimeException("SkinParameter cannot be null");
}
// texture atlas dependency
final Array<AssetDescriptor> dependencies = new Array<AssetDescriptor>();
dependencies.add(new AssetDescriptor(file.pathWithoutExtension() + ".atlas", TextureAtlas.class));
// bitmap font dependencies
for (int fontSize : parameter.fontSizesToCreate) {
final FreetypeFontLoader.FreeTypeFontLoaderParameter fontParam = new FreetypeFontLoader.FreeTypeFontLoaderParameter();
fontParam.fontFileName = parameter.fontPath;
// enable anti-aliasing
fontParam.fontParameters.minFilter = Texture.TextureFilter.Linear;
fontParam.fontParameters.magFilter = Texture.TextureFilter.Linear;
// create font according to density of target device display
fontParam.fontParameters.size = (int) (fontSize * Gdx.graphics.getDensity());
dependencies.add(new AssetDescriptor("font" + fontSize + ".ttf", BitmapFont.class, fontParam));
}
return dependencies;
}
#Override
public Skin loadSync(final AssetManager manager, final String fileName, final FileHandle file, final SkinParameter parameter) {
// load atlas and create skin
final String textureAtlasPath = file.pathWithoutExtension() + ".atlas";
final TextureAtlas atlas = manager.get(textureAtlasPath, TextureAtlas.class);
final Skin skin = new Skin(atlas);
// add bitmap fonts to skin
for (int fontSize : parameter.fontSizesToCreate) {
skin.add("font_" + fontSize, manager.get("font" + fontSize + ".ttf"));
}
// load skin now because the fonts in the json file are now available
skin.load(file);
return skin;
}
#Override
public void loadAsync(final AssetManager manager, final String fileName, final FileHandle file, final SkinParameter parameter) {
}}
public class Skin extends com.badlogic.gdx.scenes.scene2d.ui.Skin {
Skin(final TextureAtlas atlas) {
super(atlas);
}
#Override
public void dispose() {
for (String bitmapFontKey : this.getAll(BitmapFont.class).keys()) {
remove(bitmapFontKey, BitmapFont.class);
}
super.dispose();
}}
Related
How to check if compass is calibrated well in LibGDX (on Android). I've found how to do it on native Android:
In Android can I programmatically detect that the compass is not yet calibrated?
But couldn't find if that's implemented in LibGDX.
What you want is described in the wiki article Interfacing with platform specific code. LibGDX doesn't have any functionality for it because it isn't common and it doesn't make any sense for other backends.
In the core module you'll have something like this:
public interface GameListener {
void calibrateCompassIfNeeded()
}
public class Application extends ApplicationAdapter {
private GameListener listener;
public Application(GameListener listener) {
this.listener = listener;
}
#Override
public void create() {
// Call listener.calibrateCompassIfNeeded() whenever needed.
}
public void onCompassChanged(float[] values) {
// Do something...
}
}
And in the android module:
public class AndroidLauncher extends AndroidApplication implements GameListener, SensorEventListener {
private static final int COMPASS_ACCURACY_UNKNOWN = -1;
private Application app;
private int compassAccuracy = COMPASS_ACCURACY_UNKNOWN;
#Override
public void onCreate(Bundle state) {
super.onCreate(state);
SensorManager sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
Sensor compassSensor = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
sensorManager.registerListener(this, compassSensor, SensorManager.SENSOR_DELAY_GAME)
AndroidApplicationConfiguration config = new AndroidApplicationConfiguration();
app = new Application(this);
initialize(app, config);
}
#Override
public void calibrateCompassIfNeeded() {
if (compassAccuracy != COMPASS_ACCURACY_UNKNOWN && compassAccuracy < SENSOR_STATUS_ACCURACY_MEDIUM) {
// Calibrate only if accuracy is below medium.
// Show whatever is needed so user calibrates the compass.
}
}
#Override
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) {
app.onCompassChanged(event.values);
}
}
#Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
compassAccuracy = accuracy;
}
}
I haven't tried it and I have never used the compass before but I'm pretty sure this will work fine.
I'm working with JavaFX and my Idea was to have my own JavaFX Object which I can create like this:
public class Main {
public static void main(String[] args) throws Exception
TSS t = new TSS();
}
}
My JavaFX Main class looks like this:
public class TSS extends Application {
private Scene scene;
private Stage stage;
public void redrawGui() throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("tss.fxml"));
scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
#Override
public void start(Stage stage) throws Exception {
this.stage = stage;
this.stage = new Stage();
Parent root = FXMLLoader.load(getClass().getResource("tss.fxml"));
scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Normally the main method in TSS is called and its working but I want to create my own TSS-Object in its constructor, it creates the Gui.
Does anyone know how to do this?
In JavaFX, you should (typically) think of the Application subclass as the "main" class (i.e. the application entry point) and its start(...) method as the replacement for the main(...) method in a "traditional" Java Application.
If you want to factor your code out into a class that is separate from the Application subclass (which is generally a good idea), then you can do so, but you need to just reorganize things a little:
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
TSS t = new TSS();
Scene scene = new Scene(t.getView());
primaryStage.setScene(scene);
primaryStage.show();
}
// not really needed in Java 8, but some IDEs need this to execute this class:
public static void main(String[] args) { launch(args);}
}
And then you can define your own class as follows:
public class TSS {
private Parent view ;
private TssController controller ; // controller class specified in FXML
public TSS() throws Exception {
load();
}
private void load() throws Exception {
FXMLLoader loader = new FXMLLoader(getClass().getResource("tss.fxml"));
view = loader.load();
controller = loader.getController();
}
public Parent getView() {
return view ;
}
public void restartGui() throws Exception {
Scene scene = view.getScene();
Stage stage = null ;
if (scene != null) {
Window window = scene.getWindow();
if (window instanceof Stage) {
stage = (Stage) window ;
}
}
load();
if (stage != null) {
stage.setScene(new Scene(view));
}
}
public void doOtherStuff() {
controller.doSomething();
}
}
You could also consider implementing the TSS class above using the custom control pattern described in the FXML documentation. I marginally prefer the style I showed here, as it favors composition over inheritance, but it is a minimal difference.
I'm developping games with LIBGDX on ANDROID. Today, i've tried to generate one of my project in a HTML5 version. I put the content of the WAR folder on my server. All is fine except 2 things. I'll present you here just one of these 2 issues.
The problem : when a texture has to be disposed (by the call of its method dispose()), i get a BufferUnderflowException. It happens everytime.
Here is the sample code which is automatically generated when you create a new project :
public class TexDispose implements ApplicationListener
{
private OrthographicCamera camera;
private SpriteBatch batch;
private Texture texture;
#Override
public void create() {
float w = Gdx.graphics.getWidth();
float h = Gdx.graphics.getHeight();
camera = new OrthographicCamera(1, h/w);
batch = new SpriteBatch();
texture = new Texture(Gdx.files.internal("data/libgdx.png"));
Gdx.input.setInputProcessor(this);
}
#Override
public void dispose() {
batch.dispose();
texture.dispose(); // HERE IS THE ERROR
}
#Override
public void render() {
Gdx.gl.glClearColor(1, 1, 1, 1);
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
batch.setProjectionMatrix(camera.combined);
batch.begin();
batch.end();
}
#Override
public void resize(int width, int height) {
}
}
Has one of you already met this issue..? If yes, how can i avoid that (except by not disposing anything lol) ?
Thank you ! ;)
I am a new game developing with libgdx. I have a problem with Admob ads. When I call "adView.loadAd(adRequest);" my game is slowl, when I start game, FPS ~ 60 , when I call adView.loadAd(adRequest) my game is slowly FPS ~ 30.
Here is my
public class MainActivity extends AndroidApplication implements IActivityRequestHandler {
protected AdView adView;
AdRequest adRequest;
private final int SHOW_ADS = 1;
private final int HIDE_ADS = 0;
protected Handler handler = new Handler() {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case SHOW_ADS: {
System.out.println("SHOW ADVIEW");
adView.setVisibility(View.VISIBLE);
break;
}
case HIDE_ADS: {
adView.setVisibility(View.GONE);
break;
}
}
}
};
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Create the layout
RelativeLayout layout = new RelativeLayout(this);
// Do the stuff that initialize() would do for you
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
// Create the libgdx View
View gameView = initializeForView(new MyGdxGame(this), false);
// Create and setup the AdMob view`enter code here`
adView = new AdView(this, AdSize.BANNER, "XXXXXX"); // Put in your
// secret key
// here
adRequest = new AdRequest();
adView.loadAd(adRequest);
// adView.loadAd(new AdRequest());
// Add the libgdx view
layout.addView(gameView);
// Add the AdMob view
RelativeLayout.LayoutParams adParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT,
RelativeLayout.LayoutParams.WRAP_CONTENT);
adParams.addRule(RelativeLayout.ALIGN_PARENT_TOP);
adParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
layout.addView(adView, adParams);
// Hook it all up
setContentView(layout);
}
// This is the callback that posts a message for the handler
#Override
public void showAds(boolean show) {
handler.sendEmptyMessage(show ? SHOW_ADS : HIDE_ADS);
}
}
I read topic Using interstitials from Admob in a Libgdx game for Android, it's slow when dismissing it
but not solution
Please help me if you have a solution.
This is a known issue and at the moment you cant change it.
Post at the libgdx Forum
It has nothing todo with your code. I think
This one is driving me crazy for a couple hours. I try to call a method kill(); (in function takeDamage()) which is in the same class, yet it won't find it.
package classes.ship
{
imports ...
public class Ship extends MovieClip
{
var speed:Number;
var shootLimiter:Number;
public static var health:Number;
public static var maxHealth:Number;
public function initialize()
{
var stageReff:Stage = this.stage as Stage;
stage.addEventListener(KeyboardEvent.KEY_DOWN, reportKeyDown);
stage.addEventListener(KeyboardEvent.KEY_UP, reportKeyUp);
stage.addEventListener("enterFrame", move);
}
//code
public static function takeDamage(d):void
{
health -= d;
if(health <= 0)
{
health = 0;
kill();
}
Main.healthMeter.bar.scaleX = health/maxHealth;
}
public function kill():void
{
var boom = new Explosion();
stage.addChild(boom);
boom.x = this.x;
boom.y = this.y;
this.visible = false;
//Main.gameOver();
}
//code
}
}
Has it to do with var stageReff:Stage = this.stage as Stage; ?
Thanks in advance.
kill() is an instance method, but takeDamage is a static class method. You can't call instance methods from a static class method. You can only call instance methods when you have an instance reference to call it on.
nice simple one for early in the year!
You have declared the function 'takeDamage' as a static method - this means that it does not belong to a particular instance of the class Ship, instead it belongs to the Class itself. Static methods and properties can be a bit confusing if you are new to OOP, but are easily explained through a quick example:
Class Member Property
In this example we declare a new Class definition for a Ship where we can define the speed of the ship instance via setSpeed().
public class Ship {
private var speed : Number;
public function setSpeed(value : Number) : void {
this.speed = value;
}
public function getSpeed() : Number {
return this.speed;
}
}
Now we will create a couple of ships and set their speed:
var scoutShip : Ship = new Ship();
scoutShip.setSpeed(500); // Scout's are fast!
var cargoShip : Ship = new Ship();
cargoShip.setSpeed(10); // Cargo ships are sloooow.
trace("Scout Ship Speed: " + scoutShip.getSpeed()); // 500
trace("Cargo Ship Speed: " + cargoShip.getSpeed()); // 10
As you can see from the above, each new instance of Ship that we create can have its own Speed - this is a fundamental of Object Orientated Programming (where the Ship is the Object and it's speed is the data).
Static Property
Now we will create another class, this time called StaticShip which uses a static property instead, note the use of the static keyword:
public class StaticShip {
private static var speed : Number;
public function setSpeed(value : Number) : void {
this.speed = value;
}
public function getSpeed() : Number {
return this.speed;
}
}
Because the speed property is static it is shared across all instances of StaticShip; for example:
var scoutShip : StaticShip = new StaticShip();
scoutShip.setSpeed(500); // So the scout should move at 500
var cargoShip : StaticShip = new StaticShip();
cargoShip.setSpeed(10); // ... and the Cargo move at 10, as before
trace("Scout Ship Speed: " + scoutShip.getSpeed()); // 10
trace("Cargo Ship Speed: " + cargoShip.getSpeed()); // 10
Notice how both StaticShips move at 10 - this is because we set the Speed of the 'cargoShip' instance last - as the 'speed' property in StaticShip is declared static it is shared across all instances of that Class.
Now, just as you can have static properties in Classes, you can also have static functions. Usually, when you call a Class' method (ie: setSpeed()) you need to invoke that method on an instance (ie: scoutShip.setSpeed(500);), however, Static Methods allow you to interact with other static members of a given class, here's another example:
Static Method Example
public class StaticMethodShip {
private static var speed : Number;
// Note that setSpeed is now declared as static
public static function setSpeed(value : Number) : void {
this.speed = value;
}
public function getSpeed() : Number {
return this.speed;
}
}
Now, we can still create new instances of StaticMethodShip as before, but because we have now declared 'setSpeed' as static, we can't invoke setSpeed on an instance:
var scoutShip : StaticMethodShip = new StaticMethodShip();
// This call will trigger Error #1180 - Call to a possibly undefined Method because
// setSpeed was declared as static.
scoutShip.setSpeed(500);
Instead, we can now only invoke the setSpeed() method on the StaticMethodShip Class, ie:
// Set the speed of all StaticMethodShip instances.
StaticMethodShip.setSpeed(250); // all StaticMethodShips travel at 250.
// Proof!
var shipOne : StaticMethodShip = new StaticMethodShip();
var shipTwo : StaticMethodShip = new StaticMethodShip();
trace("ShipOne Speed: " + shipOne.getSpeed()); // 250
trace("ShipTwo Speed: " + shipTwo.getSpeed()); // 250
Static methods are useful when you want to define behaviour for all instances of a given Class (ie: all StaticMethodShips move at the specified speed, all fade out Tweens last for 0.25 seconds, etc); but they are also used in common design Patterns such as the Static Factory Method
Now, to the reason you are seeing your error - member level methods are able to invoke static methods, ie:
public class StaticExampleOne {
public static function getName() : String {
return "Robbe";
}
public function traceName() : void {
// traces 'Robbe'.
trace(getName());
}
}
In usage (new StaticExampleOne().traceName()) this works just fine - member methods can access static methods without problem, however this doesn't work the other way around:
public class StaticExampleTwo {
private var name : String = "Robbe";
public function getName() : void {
return this.name;
}
public static function traceName() : void {
// Throws Error #1180.
trace(getName());
}
}
This is because static methods have no scope (ie: They do not know which instance of the Class they are referring too because they can only reference other static members) and therefore can not access class level members (methods and properties).
To solve your problem you could introduce a new static property to Ship called 'STAGE' (typically static properties are written in ALL CAPS to differentiate them from member properties) and then make your kill() method static.
Hope this helps and good luck!
Jonny.