LibGDX + Scene2d(Kotlinでプログラム)。 パート1

こんにちは。 最後の出版物の結果によると、私は再び間違いを犯すという結論に達しました。 出版のペースが速いことは、私とあなたにとって不便です。 そして、理論を短くしようとしますが、より多くのコード例を示します。



少し叙情的な余談。 ほとんどの場合、LibGDXはOpenGLの単純なラッパーです。 テクスチャを操作するだけです。 テクスチャのレンダリングの順序と方法を指定するだけです。 テクスチャを描画するための基本的なツールはDrawableです。



ドローアブル



Drawable。これは、Scene2dで文字通りすべてのステップで見つかるものです。 写真、ボタン、要素の背景、スライダーのあらゆる種類の要素、スクロールバーなど -それらはすべてDrawableを使用して画面に表示されます。 実用的な観点から、内部にどのように配置されるかについてはあまり心配していません。 Drawableの3つの特定の実装で作業するためです。 これらは、TextureRegionDrawable、TiledDrawable、およびNinePatchDrawableです。 画面に描画するテクスチャは次のとおりです。









そして、このテクスチャに基づいたDrawableの3つのオプションがあります




最初のオプションはTextureRegionDrawableです。 テクスチャを指定された座標に引き伸ばすだけです。 2番目のオプションはTiledDrawableです。 テクスチャは何度も繰り返されますが、スケールは変更されません。 3番目のオプションは9ボックスまたは9パッチです。 それは何のために、いつ使用されるべきですか?



9パッチ




9-Patchは、中央オブジェクトのサイズに関係なく、定義されている形式で外部要素を保存します。 ボタン、ダイアログ、パネルなどに広く使用されています。 あるパネルの外側のフレームの厚さが、隣のパネルの2倍厚いか薄いかを想像してください。



テーブルレイアウト



昨日述べたように、シーンは要素の階層セットです(Actorクラスの子孫)。 すべてのアクターは、WidgetとWidgetGroupの2つのグループに分けられます。 ウィジェットは、子要素を含むことができないツリーリーフです。 WidgetGroupはノードです。 つまり、それらの違いはすべて、WidgetGroupが特定の順序で子を「レイアウト」できるという事実にあります。 すべてのScene2dトレーニングは、これらのオブジェクトを結合する能力に帰着します。 たとえば、LibGDXのボタンは、Tableの子孫であるWidgetGroupです。 テキストと画像の両方を含めることができます。 まあ、他のテーブルのような他のレイアウト。



画像




コトリン
class TableStage : Stage() { init { val stageLayout = Table() addActor(stageLayout.apply { debugAll() setFillParent(true) pad(AppConstants.PADDING) defaults().expand().space(AppConstants.PADDING) row().let { add(Image(uiSkin.getDrawable("sample"))) add(Image(uiSkin.getDrawable("sample"))).top().right() add(Image(uiSkin.getDrawable("sample"))).fill() } row().let { add(Image(uiSkin.getTiledDrawable("sample"))).fillY().left().colspan(2) add(Image(uiSkin.getTiledDrawable("sample"))).width(64f).height(64f).right().bottom() } row().let { add(Image(uiSkin.getDrawable("sample"))) add(Image(uiSkin.getTiledDrawable("sample"))).fill().pad(AppConstants.PADDING) add(Image(uiSkin.getDrawable("sample"))).width(64f).height(64f) } }) } }
      
      







コードは、読みやすさを向上させるためにテクスチャ/スキンのアトラスを使用しています。 構成方法は、リポジトリで確認することをお勧めします。 彼らの仕事の原則を説明することは全く別の記事です。



コードにあるもの:



  ... val stageLayout = Table() addActor(stageLayout.apply { //     debugAll() //       setFillParent(true) //       pad(AppConstants.PADDING) defaults().expand().space(AppConstants.PADDING) row().let { add(Image(uiSkin.getDrawable("sample"))) add(Image(uiSkin.getDrawable("sample"))).top().right() add(Image(uiSkin.getDrawable("sample"))).fill() }
      
      





.apply内のすべては、applyが呼び出されたオブジェクトに適用されます。 setFillParent(true)メソッドは、ルート要素をシーンに追加するときに1回だけ正しく使用されます。 非常にまれにしか使用されないため、私は常にそれを忘れてしまい、私のシーンが空である理由をすぐには理解しません。



最も一般的な間違い:setFillParent(true)をルート要素に追加することを忘れる



Javaの同じ例



  ... Table stageLayout = new Table(); stageLayout.debugAll(); stageLayout.setFillParent(true); stageLayout.pad(AppConstants.PADDING); stageLayout.defaults().expand().space(AppConstants.PADDING); stageLayout.row(); stageLayout.add(Image(uiSkin.getDrawable("sample"))); stageLayout.add(Image(uiSkin.getDrawable("sample"))).top().right(); stageLayout.add(Image(uiSkin.getDrawable("sample"))).fill(); addActor(stageLayout);
      
      





最も重要な違いは、埋め込みロジックによるコードの書式設定の欠如です。 要素のフットクロス全体が左に揃えられているため、混乱しやすい ほとんどのメソッドは、Widget / WidgetGroupレベルで一般的です。



Kotlinでは、.let可視性非表示関数をrow()に適用しました。 最も一般的な使用例は、nullチェックです。 let内では、フィールドはそのままアクセス可能であり、ゼロ以外であることが保証されます。



 var name: String? = ... name?.let { if (it == "Alex") ... }
      
      





テーブルのレイアウト方法







add-行にセルを追加します。 修飾子を適用できるセルを返します

row-行を追加します。 行のデフォルトのセルを返します。 デフォルトのセルに適用される修飾子は、この行のすべてのセルに自動的に適用されます。



expand / expandX / expandY-「スプリング」。 セルのサイズを変更します(内容は変更しません)。 デフォルトでは、セルのコンテンツは中央に配置されます



width / height-セルサイズを固定またはパーセンテージで設定します。



 .width(40f) .width(Value.percentWidth(.4f, stageLayout)
      
      





fill / fillX / fillY-セルのコンテンツがセルのサイズを受け入れるように強制する



左/右/上/下-セルの内容がサイズより小さい場合、位置合わせ方法を示します



最初の画面のレイアウトを作成します。







セルに適用された修飾子を説明するアイコンのセットを作成しました

スプリング-展開/ expandX / expandY(セルを展開)

矢印-fill / fillX / fillY(セルの内容がセルを塗りつぶします)

チャンネル-固定幅/高さサイズ(幅/高さのセル寸法を修正)



コンテナ<>レイアウト



コンテナは1つのウィジェットのみを持つことができます。 描画可能な背景があります。 したがって、これを使用して、ヘッダーとフッター(リソースパネル/コマンドパネル)を画面に描画します。



 val stageLayout = Table() addActor(stageLayout.apply { ... row().let { val headerContainer = Container<WidgetGroup>() add(headerContainer.apply { background = TextureRegionDrawable(TextureRegion(Texture("images/status-bar-background.png"))) //         }).height(100f).expandX() }
      
      





完全なメインシーンコード
 val stageLayout = Table() addActor(stageLayout.apply { setFillParent(true) defaults().fill() row().let { val headerContainer = Container<WidgetGroup>() add(headerContainer.apply { background = TextureRegionDrawable(TextureRegion(Texture("images/status-bar-background.png"))) }).height(100f).expandX() } row().let { add(Image(Texture("backgrounds/main-screen-background.png")).apply { setScaling(Scaling.fill) }).expand() } row().let { val footerContainer = Container<WidgetGroup>() add(footerContainer.apply { background = TextureRegionDrawable(TextureRegion(Texture("images/status-bar-background.png"))) fill() actor = CommandPanel() }).height(160f).expandX() } })
      
      







レイアウト読み込み画面



同じ方法でブート画面のレイアウトにアプローチしてみましょう:



レイアウトプロトタイプ




コード例:



 val stageLayout = Table() addActor(stageLayout.apply { setFillParent(true) background = TextureRegionDrawable(TextureRegion(Texture("backgrounds/loading-logo.png"))) })
      
      





うまくいくようです。 ただし、希望どおりには機能しません。 問題は、異なるアスペクト比を持つデバイスがテクスチャを平坦化または引き伸ばすことです。 どうでしょう?



 val stageLayout = Table() val backgroundImage = Image(Texture("backgrounds/loading-logo.png")) addActor(backgroundImage.apply { setFillParent(true) setScaling(Scaling.fill) })
      
      





そのようなオプションを考えてみましょう。 画像を使用し、小さい側がエッジに接するまで比率を維持しながら拡大縮小する必要があると言います。 この場合、大きな側面は切り取られます。 別のオプションはScaling.fitです。 ほとんどの部分がエッジに接するまでスケーリングが行われ、小さな部分には空白のセクションがあります(レターボックス)。



しかし、たとえば、プログレスバーを下のスペースの20%のどこかに配置し、画面の60%を占有する場合はどうでしょう。 いくつかのトップレベルの俳優をシーンに追加することを禁止する人はいません。 次のようになります。



スクリーン




コード
 init { val backgroundImage = Image(Texture("backgrounds/loading-logo.png")) addActor(backgroundImage.apply { setFillParent(true) setScaling(Scaling.fill) }) val stageLayout = Table() addActor(stageLayout.apply { setFillParent(true) row().let { add().width(Value.percentWidth(.6f, stageLayout)).height(Value.percentHeight(.8f, stageLayout)) } row().let { add(progressBar).height(40f).fill() //  progressBar     } }) }
      
      







今日は以上です。 コメントを残して、資料の表示を改善する方法をさらに詳しく確認したり、提案したりしてください。



PS最終画面には4つのボタンがあるコマンドパネルがあります。 この記事の資料を使用して、独自に実装できます。 答えはリポジトリにあります 。 週の次の記事。



パート1の結果





All Articles