如何在LibGDX开发的Facebook Instant Game中集成排行榜与应用内广告
Absolutely! You don’t need to dig through GWT’s obfuscated compiled JavaScript to add Facebook Instant Games features like ads and leaderboards to your GameOver screen. GWT provides two reliable ways to integrate custom JavaScript directly into your Java code—these methods will compile alongside your game logic seamlessly. Let’s break down both approaches with examples tailored to your use case:
Method 1: JSNI (JavaScript Native Interface) – Quick & Straightforward
JSNI lets you embed raw JavaScript directly in Java methods using a special syntax. It’s ideal for quick integrations where you just need to call existing FB Instant Games SDK functions.
Step 1: Create a Java Utility Class with JSNI Methods
Make a new class (e.g., FBInstantUtils.java) in your GWT project’s source folder, and add native methods for ads and leaderboards:
package com.yourgame.gwt; import com.google.gwt.user.client.Window; public class FBInstantUtils { // Show interstitial ad when GameOver screen loads public static native void showInterstitialAd() /*-{ if (!$wnd.FBInstant) { console.error("FBInstant SDK not loaded"); return; } // Load and display ad (replace YOUR_PLACEMENT_ID with your actual ID) $wnd.FBInstant.getInterstitialAdAsync('YOUR_PLACEMENT_ID') .then(interstitial => interstitial.showAsync()) .then(() => { console.log("Ad closed"); // Trigger Java logic after ad closes @com.yourgame.gwt.FBInstantUtils::onAdClosed()(); }) .catch(error => console.error("Ad failed: ", error)); }-*/; // Show leaderboard on button press public static native void showLeaderboard(String leaderboardId) /*-{ if (!$wnd.FBInstant) { console.error("FBInstant SDK not loaded"); return; } $wnd.FBInstant.getLeaderboardAsync(leaderboardId) .then(leaderboard => leaderboard.showAsync()) .catch(error => console.error("Leaderboard failed: ", error)); }-*/; // Java callback for post-ad actions (optional) public static void onAdClosed() { // Add game logic here, like returning to main menu Window.alert("Ad closed! Ready to play again?"); // Or call your LibGDX game's method: // YourGame.getInstance().switchToMainMenu(); } }
Step 2: Call These Methods from Your GameOver Screen
In your LibGDX GameOverScreen.java, trigger these methods when the screen loads (or on button presses):
import com.badlogic.gdx.ApplicationType; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.scenes.scene2d.ui.Button; import com.badlogic.gdx.scenes.scene2d.utils.ClickListener; public class GameOverScreen extends ScreenAdapter { private final YourGame game; public GameOverScreen(YourGame game) { this.game = game; } @Override public void show() { // Show ad when GameOver screen appears (only on web platform) if (Gdx.app.getType() == ApplicationType.WebGL) { FBInstantUtils.showInterstitialAd(); } // Example: Show leaderboard when a button is clicked Button leaderboardButton = new Button(game.skin); leaderboardButton.addListener(new ClickListener() { @Override public void clicked(InputEvent event, float x, float y) { if (Gdx.app.getType() == ApplicationType.WebGL) { FBInstantUtils.showLeaderboard("your_leaderboard_id"); } } }); // Add button to your screen's stage stage.addActor(leaderboardButton); } }
Method 2: JsInterop – Modern & Type-Safe
JsInterop is the recommended approach for GWT 2.8+. It provides type-safe access to JavaScript objects, making your code cleaner and easier to maintain.
Step 1: Map FBInstant SDK with JsInterop Annotations
Create interfaces to mirror the FBInstant SDK’s structure:
package com.yourgame.gwt; import jsinterop.annotations.JsMethod; import jsinterop.annotations.JsProperty; import jsinterop.annotations.JsType; import java.util.concurrent.CompletableFuture; // Map the global FBInstant object @JsType(isNative = true, namespace = JsType.GLOBAL) public class FBInstant { @JsProperty public static boolean isInitialized; @JsMethod public static native CompletableFuture<Void> initializeAsync(); @JsMethod public static native CompletableFuture<InterstitialAd> getInterstitialAdAsync(String placementId); @JsMethod public static native CompletableFuture<Leaderboard> getLeaderboardAsync(String leaderboardId); } // Map interstitial ad object @JsType(isNative = true) public interface InterstitialAd { CompletableFuture<Void> showAsync(); } // Map leaderboard object @JsType(isNative = true) public interface Leaderboard { CompletableFuture<Void> showAsync(); }
Step 2: Build a Type-Safe Utility Class
Use the mapped interfaces to create a clean utility class:
package com.yourgame.gwt; import java.util.concurrent.CompletableFuture; public class FBInstantJsUtils { public static void showInterstitialAd(String placementId) { if (!FBInstant.isInitialized) { System.err.println("FBInstant not initialized"); return; } FBInstant.getInterstitialAdAsync(placementId) .thenApply(InterstitialAd::showAsync) .thenRun(FBInstantJsUtils::onAdClosed) .exceptionally(error -> { System.err.println("Ad error: " + error.getMessage()); return null; }); } public static void showLeaderboard(String leaderboardId) { if (!FBInstant.isInitialized) { System.err.println("FBInstant not initialized"); return; } FBInstant.getLeaderboardAsync(leaderboardId) .thenApply(Leaderboard::showAsync) .exceptionally(error -> { System.err.println("Leaderboard error: " + error.getMessage()); return null; }); } private static void onAdClosed() { // Trigger game logic here System.out.println("Ad closed! Returning to main menu..."); YourGame.getInstance().switchToMainMenu(); } }
Step 3: Call from GameOver Screen
Just like with JSNI, call these methods with a platform check:
@Override public void show() { if (Gdx.app.getType() == ApplicationType.WebGL) { FBInstantJsUtils.showInterstitialAd("your_ad_placement_id"); } // Leaderboard button example Button leaderboardButton = new Button(game.skin); leaderboardButton.addListener(new ClickListener() { @Override public void clicked(InputEvent event, float x, float y) { if (Gdx.app.getType() == ApplicationType.WebGL) { FBInstantJsUtils.showLeaderboard("your_leaderboard_id"); } } }); stage.addActor(leaderboardButton); }
Key Tips for Success
- Platform Check: Always wrap web-specific calls in
Gdx.app.getType() == ApplicationType.WebGLto avoid errors on desktop/mobile builds. - Initialization Confirmation: Ensure
FBInstant.initializeAsync()andstartGameAsync()have completed before calling ad/leaderboard functions (your existingmain.jshandles this, but you can add a flag if needed). - Replace Placeholder IDs: Swap
YOUR_PLACEMENT_IDandyour_leaderboard_idwith your actual IDs from the Facebook Developer Dashboard.
内容的提问来源于stack exchange,提问作者Harry

