【SvelteでRPGゲーム開発】マップ背景を表示する〜基本編
※ 当ページには【広告/PR】を含む場合があります。
2023/01/22
Svelteコンポーネントに画像をBase64で表示する
.
├── index.html
└── src
├── App.svelte
├── app.scss
├── main.ts
├── components
│ └── tile.svelte
└── lib
└── GameStage.svelte
#(その他のファイルは省略)
index.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset='utf-8'>
<meta name='viewport' content='width=device-width,initial-scale=1'>
<title>Svelteで自作RPG!</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
main.ts
import './app.scss'
import App from './App.svelte'
const app = new App({target: document.getElementById('app')})
export default app
app.scss
body {
margin: 0;
display: flex;
place-items: center;
min-width: 320px;
min-height: 100vh;
#app {
margin: 0 auto;
padding: 12px;
}
}
App.svelte
<script lang="ts">
import GameStage from './lib/GameStage.svelte';
</script>
<div><GameStage/></div>
<style lang="scss">
div {display: block;}
</style>
GameStage.svelte
GameStage.svelte
<script lang="ts">
import Tile from '../components/tile.svelte';
//👇試しに与えるマップ配列
const _map = [...Array(20).keys()].map((x: number) => {
return [...Array(20).keys()].map((y: number) => {
return {x: 24*x, y: 24*y}
})
});
</script>
<section>
{#each _map as mapx}
{#each mapx as mapxy}
<Tile left={mapxy.x} top={mapxy.y}/>
{/each}
{/each}
</section>
<style lang="scss">
section {
width: 24px * 20;
height: 24px * 20;
box-sizing: border-box;
border: solid rgb(0, 0, 0) 1px;
margin: 10px auto;
position: relative;
display: block;
overflow: hidden;
span { display: block; }
}
</style>
base64形式
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAJTSURBVEhLjZXBbtNAEIbH66ROI0oOiEBBAXEqL8BTcOiJY0+9IyROSHAqghMSQjwBHDlWiFdA4gVyBCTSEtqiChSaxuuFf+LfnWxslU+ydj37zz87aztJdvdvBJ8HcWlHxBeSulOxHK1ek2zyWVZbTpxvSeESsfrMzyRvh1I9Z7w3lf56pnMnRZB2O5PD0bHMpKVByyTPJO9clVnR1vuDbyfy8/tUvA+qhzkMLTCfx3JxIfcSQtBgx02WxCtfh9KbHkn2TwOgu3x9TVbcVPVNdAd9CUkqybu9jdANzUKy9ehCOZvz9vnvhaOwMP7D3ZTk/WigW2OQHdhEmL+4f1jezXn46pIWsdiCnCevx/fCrfyjBmO469icsIg1jnGxOTs4z5xA12QOXDlWQFxnbk045xFRD3yR6siNagHegNg83h3urZ465qXO68g8LVCXZI05t6Ndjzs5Tvo6guqIkGBbBdbEYnXU2E56YaxzsPQM7O7vTB7raEGn0LAINLiQYztZeAYAweGTBwvmn7pPqyKjsK7j3d5OZYYcaAB1LEIfLcDd0JDm2MXtnZe6tvlnuzIBds4iFnbhaI7dEyQgzg+JBXnRnF0QxHHZLrQDmjPRGnAOIxoQu2ax8aWHDHgs1qwJbo45hPGlAhTaI4tpMovjQAtwockc8brY/6A/1/EZEpjWmYPzijI32R1tBHeaydrJFfl1cVgunz24OnMQd0BDy5tnhSQfvgzC/kH973lTZ4CvIoh1WOOXXP2jESzUFQN2jQZNWrLwFjWZ15lhHmupO0PkL0Q+bDUvpbHbAAAAAElFTkSuQmCC
data:image/png;base64,
tile.svelte
<script lang="ts">
export let top: number;
export let left: number;
const mountain = 'iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAJTSURBVEhLjZXBbtNAEIbH66ROI0oOiEBBAXEqL8BTcOiJY0+9IyROSHAqghMSQjwBHDlWiFdA4gVyBCTSEtqiChSaxuuFf+LfnWxslU+ydj37zz87aztJdvdvBJ8HcWlHxBeSulOxHK1ek2zyWVZbTpxvSeESsfrMzyRvh1I9Z7w3lf56pnMnRZB2O5PD0bHMpKVByyTPJO9clVnR1vuDbyfy8/tUvA+qhzkMLTCfx3JxIfcSQtBgx02WxCtfh9KbHkn2TwOgu3x9TVbcVPVNdAd9CUkqybu9jdANzUKy9ehCOZvz9vnvhaOwMP7D3ZTk/WigW2OQHdhEmL+4f1jezXn46pIWsdiCnCevx/fCrfyjBmO469icsIg1jnGxOTs4z5xA12QOXDlWQFxnbk045xFRD3yR6siNagHegNg83h3urZ465qXO68g8LVCXZI05t6Ndjzs5Tvo6guqIkGBbBdbEYnXU2E56YaxzsPQM7O7vTB7raEGn0LAINLiQYztZeAYAweGTBwvmn7pPqyKjsK7j3d5OZYYcaAB1LEIfLcDd0JDm2MXtnZe6tvlnuzIBds4iFnbhaI7dEyQgzg+JBXnRnF0QxHHZLrQDmjPRGnAOIxoQu2ax8aWHDHgs1qwJbo45hPGlAhTaI4tpMovjQAtwockc8brY/6A/1/EZEpjWmYPzijI32R1tBHeaydrJFfl1cVgunz24OnMQd0BDy5tnhSQfvgzC/kH973lTZ4CvIoh1WOOXXP2jESzUFQN2jQZNWrLwFjWZ15lhHmupO0PkL0Q+bDUvpbHbAAAAAElFTkSuQmCC';
</script>
<object title="" data="data:image/png;base64,{mountain}" type="image/png" style="left: {left}px; top: {top}px"></object>
<style lang="scss">
object {
display: block;
width: 24px;
height: 24px;
position: absolute;
}
</style>
yarn dev
他のタイル画像を組み合わせてマップを作る
マップ定義
GameStage.svelte
<script lang="ts">
...
const _map = [...Array(20).keys()].map((x: number) => {
return [...Array(20).keys()].map((y: number) => {
return {x: 24*x, y: 24*y}
})
});
...
</script>
...
<section>
{#each _map as mapx}
{#each mapx as mapxy}
<Tile left={mapxy.x} top={mapxy.y}/>
{/each}
{/each}
</section>
...
[...Array(20).keys()]
[0, 1, 2, ...., 19]
山のタイル ... 1
草原のタイル ... 2
...
const _arr = [
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
[1,1,1,2,1,2,2,1,1,2,2,2,1,1,2,1,2,1,1,1],
[1,1,2,2,1,2,2,2,1,2,2,2,1,2,2,1,2,2,1,1],
[1,1,2,2,2,2,2,2,2,2,2,2,1,2,2,2,2,2,2,1],
[1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1],
[1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1],
[1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1],
[1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1],
[1,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1],
[1,2,2,2,2,2,2,2,2,1,1,2,2,2,2,2,2,2,2,1],
[1,2,2,2,2,2,2,2,2,1,1,1,2,2,2,2,2,2,1,1],
[1,2,2,2,2,2,2,2,1,1,2,2,2,2,2,2,2,2,1,1],
[1,2,2,2,2,2,2,2,2,1,2,2,2,2,2,2,2,2,2,1],
[1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,1,2,1,2,1],
[1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1],
[1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,2,2,1],
[1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1],
[1,2,2,2,2,1,2,2,2,2,2,1,2,1,2,2,1,2,2,1],
[1,1,2,2,1,1,1,1,2,2,1,1,1,2,2,2,1,1,2,1],
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
];
...
配列の配列
2次元配列
tile.svelte
<script lang="ts">
export let top: number;
export let left: number;
//👇インプット変数(=インデックス番号)を新しく追加
export let pattern: number;
const mountain = 'iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAJTSURBVEhLjZXBbtNAEIbH66ROI0oOiEBBAXEqL8BTcOiJY0+9IyROSHAqghMSQjwBHDlWiFdA4gVyBCTSEtqiChSaxuuFf+LfnWxslU+ydj37zz87aztJdvdvBJ8HcWlHxBeSulOxHK1ek2zyWVZbTpxvSeESsfrMzyRvh1I9Z7w3lf56pnMnRZB2O5PD0bHMpKVByyTPJO9clVnR1vuDbyfy8/tUvA+qhzkMLTCfx3JxIfcSQtBgx02WxCtfh9KbHkn2TwOgu3x9TVbcVPVNdAd9CUkqybu9jdANzUKy9ehCOZvz9vnvhaOwMP7D3ZTk/WigW2OQHdhEmL+4f1jezXn46pIWsdiCnCevx/fCrfyjBmO469icsIg1jnGxOTs4z5xA12QOXDlWQFxnbk045xFRD3yR6siNagHegNg83h3urZ465qXO68g8LVCXZI05t6Ndjzs5Tvo6guqIkGBbBdbEYnXU2E56YaxzsPQM7O7vTB7raEGn0LAINLiQYztZeAYAweGTBwvmn7pPqyKjsK7j3d5OZYYcaAB1LEIfLcDd0JDm2MXtnZe6tvlnuzIBds4iFnbhaI7dEyQgzg+JBXnRnF0QxHHZLrQDmjPRGnAOIxoQu2ax8aWHDHgs1qwJbo45hPGlAhTaI4tpMovjQAtwockc8brY/6A/1/EZEpjWmYPzijI32R1tBHeaydrJFfl1cVgunz24OnMQd0BDy5tnhSQfvgzC/kH973lTZ4CvIoh1WOOXXP2jESzUFQN2jQZNWrLwFjWZ15lhHmupO0PkL0Q+bDUvpbHbAAAAAElFTkSuQmCC';
const grassland = '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';
//👇インデックス番号からタイル画像を選択
const image =
pattern == 1 ? mountain :
pattern == 2 ? grassland :
'';
</script>
<object title="" data="data:image/png;base64,{image}" type="image/png" style="left: {left}px; top: {top}px"></object>
<style lang="scss">
object {
display: block;
width: 24px;
height: 24px;
position: absolute;
}
</style>
GameStage.svelte
<script lang="ts">
import Tile from '../components/tile.svelte';
//👇マップ配列
const _arr = [
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
[1,1,1,2,1,2,2,1,1,2,2,2,1,1,2,1,2,1,1,1],
[1,1,2,2,1,2,2,2,1,2,2,2,1,2,2,1,2,2,1,1],
[1,1,2,2,2,2,2,2,2,2,2,2,1,2,2,2,2,2,2,1],
[1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1],
[1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1],
[1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1],
[1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1],
[1,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1],
[1,2,2,2,2,2,2,2,2,1,1,2,2,2,2,2,2,2,2,1],
[1,2,2,2,2,2,2,2,2,1,1,1,2,2,2,2,2,2,1,1],
[1,2,2,2,2,2,2,2,1,1,2,2,2,2,2,2,2,2,1,1],
[1,2,2,2,2,2,2,2,2,1,2,2,2,2,2,2,2,2,2,1],
[1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,1,2,1,2,1],
[1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1],
[1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,2,2,1],
[1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1],
[1,2,2,2,2,1,2,2,2,2,2,1,2,1,2,2,1,2,2,1],
[1,1,2,2,1,1,1,1,2,2,1,1,1,2,2,2,1,1,2,1],
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
];
//👇マップ配列の中身を{インデックス番号,x座標,y座標}になるように変換
const _map = _arr.map((r: number[], i: number) => {
return r.map((c: number, j: number) => {
return {tile_index: c, x: 24*i, y: 24*j}
})
});
</script>
<section>
{#each _map as mapx}
{#each mapx as mapxy}
<Tile pattern={mapxy.tile_index} left={mapxy.x} top={mapxy.y}/>
{/each}
{/each}
</section>
<style lang="scss">
section {
width: 24px * 20;
height: 24px * 20;
box-sizing: border-box;
border: solid rgb(0, 0, 0) 1px;
margin: 10px auto;
position: relative;
display: block;
overflow: hidden;
span { display: block; }
}
</style>
yarn dev
今後の課題
まとめ
記事を書いた人
ナンデモ系エンジニア
これからの"地方格差"なきプログラミング教育とは何かを考えながら、 地方密着型プログラミング学習関連テーマの記事を不定期で独自にブログ発信しています。
カテゴリー