VKontakteからのコンテストと正しい情報を見つけるためのインターネットでの2週間のマラソンは終了しました。 LibGDXグラフィックエンジンで少し経験を共有したいと思います。 インターネットには多くの例がありますが、ほとんどは実践からはほど遠い(描かれたスプライトはゲームからほど遠い)か、すでに時代遅れです。
正直なところ、Android Studioと簡単に統合でき、使いやすい言語で記述されており、グラフィックタスクを完全に実行するため、気に入っています。
私が解決しなければならなかった質問:
組み込みロガーを使用するオプション
便宜上、私はTimberをよく使います。 コアモジュールでは使用できないため、LibGDXで使用可能なロガーを使用して単純なヘルパークラスを作成し、リリースバージョンのアプリケーションはログの書き込みを停止します
さらに、便宜上、1.23456789のさまざまな浮動小数点値は丸められます
import com.badlogic.gdx.Gdx; public class GdxLog { public static boolean DEBUG; @SuppressWarnings("all") public static void print(String tag, String message) { if (DEBUG) { Gdx.app.log(tag, message); } } @SuppressWarnings("all") public static void d(String tag, String message, Integer...values) { if (DEBUG) { Gdx.app.log(tag, String.format(message, values)); } } @SuppressWarnings("all") public static void f(String tag, String message, Float...values) { if (DEBUG) { Gdx.app.log(tag, String.format(message.replaceAll("%f", "%.0f"), values)); } } } //... GdxLog.d(TAG, "worldWidth: %d", worldWidth);
さらに、便宜上、1.23456789のさまざまな浮動小数点値は丸められます
E / libEGL:現在のコンテキストなしでOpenGL ES APIを呼び出します(スレッドごとに1回記録されます)
最初は、そのようなエラーが私を非常に動揺させ、何も理解できませんでした。 これは、GLSurfaceViewグラフィックが、外部からこのプロセスに干渉しようとすると、独自の個別のストリームでレンダリングされるために発生します。 修正方法:
原則として、view.postInvalidate()の場合のように
私は匿名クラスのファンではないので、コードを減らすための単純なメソッドを作成しました(そうしないと判読できなくなります)。 Java 8ではこれはもはや問題ではありませんが、追加の利点は、たとえばファイルが見つからない場合にInvocationTargetExceptionが処理されることです。このような小さなエラーが原因でアプリケーションがクラッシュすることはなくなります。
パラメータがプリミティブではなく、オブジェクトを継承することが重要です。 また、ここにnullパラメーターを使用した単純化があります(Stringクラスのみ)
Gdx.app.postRunnable(new Runnable() { @Override public void run() { // } });
原則として、view.postInvalidate()の場合のように
私は匿名クラスのファンではないので、コードを減らすための単純なメソッドを作成しました(そうしないと判読できなくなります)。 Java 8ではこれはもはや問題ではありませんが、追加の利点は、たとえばファイルが見つからない場合にInvocationTargetExceptionが処理されることです。このような小さなエラーが原因でアプリケーションがクラッシュすることはなくなります。
// null may be only String params public void postRunnable(final String name, final Object...params) { Gdx.app.postRunnable(new Runnable() { @Override public void run() { Method method = null; Class[] classes = new Class[params.length]; for (int i = 0; i < params.length; i++) { classes[i] = params[i] == null ? String.class : params[i].getClass(); } try { method = World.class.getMethod(name, classes); } catch (SecurityException e) { GdxLog.print(TAG, e.toString()); } catch (NoSuchMethodException e) { GdxLog.print(TAG, e.toString()); } if (method == null) { return; } try { method.invoke(WorldAdapter.this, params); } catch (IllegalArgumentException e) { GdxLog.print(TAG, e.toString()); } catch (IllegalAccessException e) { GdxLog.print(TAG, e.toString()); } catch (InvocationTargetException e) { GdxLog.print(TAG, e.toString()); } } }); }
パラメータがプリミティブではなく、オブジェクトを継承することが重要です。 また、ここにnullパラメーターを使用した単純化があります(Stringクラスのみ)
LibGDXを他のウィジェットで使用する方法
フラグメントを通して。 wikiの詳細情報
例:
そしてフラグメント自体:
例:
public class ActivityMain extends AppCompatActivity implements AndroidFragmentApplication.Callbacks { protected FragmentWorld fragmentWorld; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // ... getSupportFragmentManager() .beginTransaction() .add(R.id.world, fragmentWorld, FragmentWorld.class.getSimpleName()) .commitAllowingStateLoss(); } @Override public void exit() {}
そしてフラグメント自体:
public class FragmentWorld extends AndroidFragmentApplication { public World world; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { int worldWidth = getResources().getDimensionPixelSize(R.dimen.world_width); int worldHeight = getResources().getDimensionPixelSize(R.dimen.world_height); world = new World(BuildConfig.DEBUG, worldWidth, worldHeight); return initializeForView(world); } }
ビットマップで世界またはPixmapのレンダリングを描画する方法
pixmapをファイルに保存してから、Androidツールを使用してBitmapを引き出したくなかった
したがって、OutputStreamクラスを使用して、このようなライフハックを思いつきました。 正常に動作し、遅いr / w操作を必要としません
したがって、OutputStreamクラスを使用して、このようなライフハックを思いつきました。 正常に動作し、遅いr / w操作を必要としません
final Pixmap pixmap = getScreenshot(); Observable.fromCallable(new Callable <Boolean> () { @Override public Boolean call() throws Exception { PixmapIO.PNG writer = new PixmapIO.PNG((int)(pixmap.getWidth() * pixmap.getHeight() * 1.5 f)); writer.setFlipY(false); ByteArrayOutputStream output = new ByteArrayOutputStream(); try { writer.write(output, pixmap); } finally { StreamUtils.closeQuietly(output); writer.dispose(); pixmap.dispose(); } byte[] bytes = output.toByteArray(); Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length); return true; } }).subscribeOn(Schedulers.io()).subscribe();
Androidと組み合わせて色を使用する方法
実際、おそらくintが可能です(LibGDXのColorクラスは、すべてのグラフィックスと同じように非常に具体的です。つまり、ビットレベルを理解する必要があります)。
したがって、パーサーが必要でした:
パラメータ例「ffffff」
したがって、パーサーが必要でした:
protected Color parseColor(String hex) { String s1 = hex.substring(0, 2); int v1 = Integer.parseInt(s1, 16); float f1 = 1 f * v1 / 255 f; String s2 = hex.substring(2, 4); int v2 = Integer.parseInt(s2, 16); float f2 = 1 f * v2 / 255 f; String s3 = hex.substring(4, 6); int v3 = Integer.parseInt(s3, 16); float f3 = 1 f * v3 / 255 f; return new Color(f1, f2, f3, 1 f); }
パラメータ例「ffffff」
アクターのクリックを検出する方法 sticker.addListener()すべてがシンプルになりました。 シーンにヒットメソッドがあります
Sticker sticker = (Sticker) stickersStage.hit(coordinates.x, coordinates.y, false);
ピンチイベントでの数学のスケールと回転(2本の指)
これは最善の解決策ではないかもしれませんが、うまく機能します。 鋭いジャンプなしで回転、スムーズなズーム
このイベントの前に現在のズームと回転を覚えておくだけで十分です。
そして、ピンチイベントの間、俳優は動かない
@Override public boolean pinch(Vector2 initialPointer1, Vector2 initialPointer2, Vector2 pointer1, Vector2 pointer2) { // initialPointer doesn't change // all vectors contains device coordinates Sticker sticker = getCurrentSticker(); if (sticker == null) { return false; } Vector2 startVector = new Vector2(initialPointer1).sub(initialPointer2); Vector2 currentVector = new Vector2(pointer1).sub(pointer2); sticker.setScale(sticker.startScale * currentVector.len() / startVector.len()); float startAngle = (float) Math.toDegrees(Math.atan2(startVector.x, startVector.y)); float endAngle = (float) Math.toDegrees(Math.atan2(currentVector.x, currentVector.y)); sticker.setRotation(sticker.startRotation + endAngle - startAngle); return false; }
このイベントの前に現在のズームと回転を覚えておくだけで十分です。
@Override public boolean touchDown(float x, float y, int pointer, int button) { if (pointer == FIRST_FINGER) { Vector2 coordinates = stickersStage.screenToStageCoordinates(new Vector2(x, y)); Sticker sticker = (Sticker) stickersStage.hit(coordinates.x, coordinates.y, false); if (sticker != null) { // sticker.setPinchStarts(); currentSticker = sticker.index; } } return false; } @Override public void pinchStop() { Sticker sticker = getCurrentSticker(); if (sticker != null) { // sticker.setPinchStarts(); } }
そして、ピンチイベントの間、俳優は動かない
アニメーションは発生しないが、アクションがアクターに追加される理由
シーン内のキーアクトメソッド。 知らないときの痛み)
spriteBatch.begin(); stickersStage.act(); stickersStage.getRoot().draw(spriteBatch, 1); spriteBatch.end();
LibGDXの勾配
私が理解しているように、左上と右下の色のみを設定できます。 この場合、残り(透明)を設定する必要はなく、それらの間にスペースがあります。 すなわち 残りは、線形グラデーションになると、特定の距離におけるこれら2つの色の合計として定義されます。 その独特なことを言うこと、何も言わないこと
gradientTopLeftColor = parseColor(topLeftColor); gradientBottomRightColor = parseColor(bottomRightColor); gradientBlendedColor = new Color(gradientTopLeftColor).add(gradientBottomRightColor);
アクターの移動処理のトリック(パンイベント)
ここにハンドラーがあります
worldDensityは、画面座標で指を動かすこととゲーム内の俳優を動かすことの違いです。 このパラメーターがないと、アクターは指から離れます
また、sticker.addListenerを介してタッチ入力バインディングを行う場合、入力座標は現在の俳優の指の位置に相対的です。 小さいサイズの俳優(ズーム)で、彼は痙攣してシーンから飛び出します(私がしたように)。
@Override public boolean pan(float x, float y, float deltaX, float deltaY) { if (currentSticker != Sticker.INDEX_NONE) { Sticker sticker = getCurrentSticker(); if (sticker != null) { sticker.moveBy(deltaX * worldDensity, -deltaY * worldDensity); } } return false; }
worldDensityは、画面座標で指を動かすこととゲーム内の俳優を動かすことの違いです。 このパラメーターがないと、アクターは指から離れます
@Override public void resize(int width, int height) { if (height > width) { worldDensity = 1f * worldWidth / width; } else { worldDensity = 1f * worldHeight / height; } viewport.update(width, height, true); }
また、sticker.addListenerを介してタッチ入力バインディングを行う場合、入力座標は現在の俳優の指の位置に相対的です。 小さいサイズの俳優(ズーム)で、彼は痙攣してシーンから飛び出します(私がしたように)。
アクターアニメーションをより適切に操作する方法(アクション)
おそらくすべてが興味深い。 私自身、ビューポートを増やすという問題の解決策を見つけませんでした。 ズームカメラはシーンのアプローチにのみ役立ち、シーンが必要以上に縮小することがわかります(スコープは変更されません)。
別の問題は、世界のレンダリングの保存です。 出力では、画面のサイズと一致しますが、特定のサイズが必要です。 フレームバッファで試しましたが、そこからピックスマップを取得できませんでした(テクスチャクラスの初期化にはいくつかのバグがあります)
エンジンの別の欠点。たとえば、キーボード入力を完全に無効にすることはできません。 彼は別のウィジェットからフォーカスをインターセプトしたことが判明しました(しかし、彼はこのために設計されていませんでしたが、それは素晴らしいことですが。
しかし、全体的に、すべてが非常に優れています。 LibGDXをさらに開発する)
→ プロジェクトへのリンク