【SvelteでRPGゲーム開発】Svelteコンポーネントを使ったHTML中の表示画面のフレキシブル対応


※ 当ページには【広告/PR】を含む場合があります。
2023/02/18
【SvelteでRPGゲーム開発】コントローラーをコンポーネント化してスプライトを操作してみる
Svelteアプリでクリックイベントのスマホ対応〜preventDefaultでタッチイベントを適切にさばく
合同会社タコスキングダム|TacosKingdom,LLC.

この記事ではSvelteでHTML製ゲームアプリを作る過程をステップ・バイ・ステップでまったり解説していく連載企画です。

4回目の今回は、ブラウザの表示サイズに合わせてアプリの画面サイズを調整する機能を追加するまでを見ていきます。

Svelteゲームの作り方の基本は、あらかた前回の記事で説明した通り、
「コンポーネントを作成する→ゲームステージに追加する→...繰り返し」という地道な作業の繰り返しで開発を進めることになります。

合同会社タコスキングダム|タコキンのPスクール
【SvelteでRPGゲーム開発】コントローラーをコンポーネント化してスプライトを操作してみる

Svelteゲームを作る際にコントローラーからキャラクターを操作するためのコンポーネントを作成してスプライトを操作してみます。

HTMLゲーム開発としては少し脇道にそれた課題して、
ユーザーが使っているどのブラウザからでもできるだけ同じように表示させることも必要です。

例えば、前回までのアプリをスマホのブラウザから表示させたとしましょう。

合同会社タコスキングダム|タコキンのPスクール

このままだとゲーム画面の一部が見切れてますし、コントローラーも半分くらいしか表示されていません。

出来る限り多くの人に遊んでもらいたい場合、いまどきスマホ対応なしだとユーザーを半分以上失ってしまうようなものですので、
"フレキシブル対応"はとても重要になります。

この記事で目的としてかかげるポイントは以下の通りです。

            
            1. svelte:window要素のプロパティバインドから画面サイズを取得する
2. iframeを使う場合のフレキシブル対応の注意点
        
なお、最後まで実装していただくとゲームの画面は以下のようにブラウザに合わせて調整されることが確認できます。

説明


RPG風マップでネコ(?)を動かすだけの実験プログラムです。
画面下にあるコントローラーからの操作が有効になります。
現状は十字キーで上下左右に動くのみです。


JavaScriptとHTMLで「レトロ風RPG」を作ろう 全コード解説

【Svelte.js入門】ReactやVueに挫折した人でも大丈夫!Svelteとfirebaseでシンプルアプリ開発

合同会社タコスキングダム|タコキンのPスクール【Html&Cssアプリ】Html/JavascriptとSassを使った四択クイズゲームの作り方

svelte:window要素のプロパティバインド

一般的にブラウザは現在の表示状態に関連したいくつかの情報を裏側で保持しています。

この情報を取得して別の処理に利用するにはJavascriptでの実装が不可欠です。

SvelteでDOM要素のサイズ調整をおこないたい場合、
<svelte:window>要素と呼ばれる特別なHTML要素のプロパティバインドを利用することになります。

svelte:windowでバインドできるプロパティは以下の通りです。

            
            - innerWidth: 現在のページのinnerWidthを取得
- innerHeight: 現在のページのinnerHeightを取得
- outerWidth: 現在のページのouterWidthを取得
- outerHeight: 現在のページのouterHeightを取得
- scrollX: 現在の水平方向のスクロール位置を取得か設定
- scrollY: 現在の垂直方向のスクロール位置を取得か設定
- online: オンラインかオンラインの判定(window.navigator.onLine相当)
        
HTMLアプリにおいて特に重要になってくるのが、現在のユーザーがブラウザ上に表示しているページの「横幅」を考慮することです。

ブラウザに表示されているページの横幅が分かれば、それに合わせてアプリの画面を調整(=フレキシブル化)することができます。

では早速
前回のソースコードをベースに、svelte:windowを使って、フレキシブル対応させてみましょう。

Svelteプロジェクトの構成(のnodejs等の設定ファイルは除く)は前回から変わりませんが、今回は
GameStage.svelteだけの修正になります。

            
            .
├── index.html
└── src
     ├── App.svelte
     ├── app.scss
     ├── main.ts
     ├── components
     │   ├── player.svelte
     │   ├── title.svelte
     │   └── controller.svelte
     └── lib
          ├── models.ts
          └── GameStage.svelte○
#(その他のファイルは省略)
        
前回までのGameStage.svelteに以下のような追加を行いましょう。

            
            <script lang="ts">
    //...中略

    //👇innerWidthを保持する変数
    let innerWidth = 0;

    //👇拡大比率(=デフォルトの横幅480pxと現在の横幅の比率)
    let ratio = 1;
    //👇と、ついでにratioとinnerWidthをリアクティブ化して変化を監視・自動更新
    $: ratio = innerWidth/480;

    //...中略
</script>

//...中略

//👇svelte:window要素にinnerWidthをバインド
<svelte:window
    bind:innerWidth
    on:keydown|preventDefault={onKeyDown}
    on:keyup|preventDefault={onKeyUp}/>

<div id="wrapper" on:selectstart|preventDefault>
    //👇ゲームのメイン画面の要素にstyle属性からwidthとheightをリアルタイム修正
    <section style="width: {24*20*ratio}px; height: {24*20*ratio}px;">
        {#each _map as mapx}
            {#each mapx as mapxy}
                <Tile pattern={mapxy.tile_index} left={mapxy.x} top={mapxy.y} ratio={ratio}/>
            {/each}
        {/each}
        <Player left={player.L + slipX} top={player.T + slipY} ratio={ratio}></Player>
    </section>

    //...中略

</div>

<style lang="scss">
    div#wrapper {
        width: 480px;
        height: 740px;
        section {
            //👇スタイルファイルからはwidth/heightを設定しないようにコメントアウト
            // width: 24px * 20;
            // height: 24px * 20;
            box-sizing: border-box;
            border: solid rgb(0, 0, 0) 1px;
            margin: 0;
            position: relative;
            display: block;
        }
    }
</style>
        
ここでのポイントとして、svelte:window要素からinnerWidthをプロパティバインド(bind:innerWidth)して得たページの横幅から、描画に適切なコンポーネントの拡大比率(ratio)を計算し、その都度要素のstyle属性へ設定することでサイズを補正しています。

ちなみにプロパティバインドするプロパティ名と変数名が一致するときには以下のような略記形が利用できます。

            
            <svelte:window bind:innerWidth={innerWidth}/>
//👇略記形
<svelte:window bind:innerWidth/>
        
他のJSフレームワークと比較しても、Svelteならばスッキリとした実装テクニックになっている気がします。


JavaScriptとHTMLで「レトロ風RPG」を作ろう 全コード解説

【Svelte.js入門】ReactやVueに挫折した人でも大丈夫!Svelteとfirebaseでシンプルアプリ開発

合同会社タコスキングダム|タコキンのPスクール【Html&Cssアプリ】Html/JavascriptとSassを使った四択クイズゲームの作り方

iframeを使う場合のフレキシブル対応の注意点

必須ではありませんが、HTMLアプリを一般のブログのようなウェブサイトに埋め込む際には、iframe要素の中から提供することもあるでしょう。

iframeを使ったウェブページへのフレキシブル対応は、iframeの性質を良く理解されていないと、全然思う通りにならずに結構つまずきやすいポイントです。

というのも、
「iframeの内部は現在のページの親要素(bodyやdivなど)のサイズは考慮せず、リンク先のウェブページのサイズ設定を反映してありのまま表示する」ことがHTMLプログラマー初学者にとっては結構厄介なシロモノです。

iframe要素自体は当然現在のブラウザのコントロール下にあって、cssスタイル付けやjsスクリプトによる操作も受け付けます。

他方、iframeの内部は無法地帯の"別ブラウザ"ですので、その"別ブラウザ"の中身まで操作しようとするのは非常に困難なのです。

iframeのフレキシブル対応は下のリンク記事の解説が詳しいのでとことん理解したい方はそちらを一読されてみてください。

参考|アイフレームをスマホ対応させる方法

他のHTML系の技術記事でも
「iframeのスマホ対応」として紹介されている基本的なテクニックをSvelteコード的に解釈したものが以下です。

            
            <div class="wrapper">
    <iframe src="埋め込みたいアプリの外部URL" scrolling="no" frameborder="0" style="border:0" allowfullscreen></iframe>
</div>

<style lang="scss">
div#wrapper {
    position: relative;
    width: 100%;
    $targetWidth: [外部アプリの表示される幅];
    $targetHeight: [外部アプリの表示される高さ];
    padding: 0 0 calc($targetHeight / $targetWidth * 100%);
    iframe {
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
    }
}
</style>
        
理解のポイントは、iframe自体は制御可能でも、iframeの中は治外法権ということを意識してこのコードを眺めてみてください。

ここでのiframeの親要素である
div要素は少なくともiframeに対して横幅を与えることは可能です。

iframeに指定できるのは基本的に横幅だけで、
iframeの中身の高さは外部の読み込み先のウェブページが決めるので、現在のブラウザ側からはどのくらいの高さにするのがベストかは不明なままです。

何もしないとiframeは高さ0で設定されてしまい、何も表示してくれない...という初心者つまずきポイントになるわけです。

そこでこのテクニックでは、iframeの親要素に
paddingを利用した強制的な空白をtopかbottomに作って、iframeの中身を表示させるための隙間を作ることになります。

最適な隙間のサイズが、
[外部アプリの表示される高さ] / [外部アプリの表示される幅] * 100%ということで計算できるわけですが、これは読み込み先のアプリの画面アスペクト比率を計算しています。

アスペクト比といえば、一般的にYoutubeに標準アスペクト比は
16:9ですので、9 / 16 * 100%を計算していることになります。

ただしこれはYoutubeに限った話で、このテクニックを使う場合、iframeの読み込み先の外部アプリが提供しているアスペクト比が最低限分かっている必要があります。

逆に言うと、アスペクト比が分からない・決まったサイズの無いアプリでは上手く使えないことが多いです。(※例えばTwitterのように画像が遅延読み込みされて表示されるタイプのアプリ)

その場合には、MutationObserverのようなより高度なJavascriptによる実装が必要です。

参考|Google Adsenseで勝手にCSSスタイルに注入されてしまう「height: auto !important」をMutationObserverで抑える


JavaScriptとHTMLで「レトロ風RPG」を作ろう 全コード解説

【Svelte.js入門】ReactやVueに挫折した人でも大丈夫!Svelteとfirebaseでシンプルアプリ開発

合同会社タコスキングダム|タコキンのPスクール【Html&Cssアプリ】Html/JavascriptとSassを使った四択クイズゲームの作り方

まとめ

今回はSvelteアプリがブラウザ上に表示されるときにフレキシブルになるように実装を少しだけ改良する話を行っていきました。

            
            1. svelte:window要素のプロパティバインドから画面サイズを取得する
2. iframeを使う場合のフレキシブル対応の注意点
        
ゲームの内容とは直接関係はしないものの、ユーザーフレンドリーなアプリのHTML設計は重要な開発戦略の一つですので、ないがしろにせずにしっかり取り組んでみましょう。