【SvelteでRPGゲーム開発】コントローラーをコンポーネント化してスプライトを操作してみる
※ 当ページには【広告/PR】を含む場合があります。
2023/02/12
2023/02/14
requestAnimationFrame
1. 親子関係にあるSvelteコンポーネント間のイベントの渡し方
2. ゲーム画面上のコンポーネントを右クリック禁止にする
3. ゲーム画面上の文字テキストの選択を禁止にする
4. iframeのリソースキャッシュを無効化するメタタグの書き方
説明
RPG風マップでネコ(?)を動かすだけの実験プログラムです。
画面下にあるコントローラーからの操作が有効になります。
現状は十字キーで上下左右に動くのみです。
Svelteでコントローラー風コンポーネントを準備する
☆
○
.
├── index.html
└── src
├── App.svelte
├── app.scss
├── main.ts
├── components
│ ├── player.svelte
│ ├── title.svelte
│ └── controller.svelte☆
└── lib
├── models.ts
└── GameStage.svelte○
#(その他のファイルは省略)
controller.svelte
<script lang="ts">
export let cursorHandler: (dir: number, state: number) => void;
export let btnAHandler: (state: number) => void;
export let btnBHandler: (state: number) => void;
export let btnXHandler: (state: number) => void;
export let btnYHandler: (state: number) => void;
export let btnStartHandler: (state: number) => void;
export let btnSelectHandler: (state: number) => void;
</script>
<div class="spf-controller" on:contextmenu|preventDefault>
<div class="controller-left">
<div class="cross-layout">
<button class="position-top btn cross-key-btn"
on:mousedown={() => cursorHandler(1,0)}
on:mouseup={() => cursorHandler(1,1)}><span class="top-mark">▲</span></button>
<button class="position-left btn cross-key-btn"
on:mousedown={() => cursorHandler(2,0)}
on:mouseup={() => cursorHandler(2,1)}><span class="left-mark">▲</span></button>
<button class="position-center btn cross-key-btn"><span class="center-mark">●</span></button>
<button class="position-right btn cross-key-btn"
on:mousedown={() => cursorHandler(3,0)}
on:mouseup={() => cursorHandler(3,1)}><span class="right-mark">▲</span></button>
<button class="position-bottom btn cross-key-btn"
on:mousedown={() => cursorHandler(4,0)}
on:mouseup={() => cursorHandler(4,1)}><span class="bottom-mark">▲</span></button>
</div>
</div>
<div class="controller-center">
<div class="selectstart-btn-set">
<button class="btn selectstart-btn"
on:mousedown={() => btnSelectHandler(0)}
on:mouseup={() => btnSelectHandler(1)}></button>
<button class="btn selectstart-btn"
on:mousedown={() => btnStartHandler(0)}
on:mouseup={() => btnStartHandler(1)}></button>
</div>
</div>
<div class="controller-right">
<div class="abxy-btn-set">
<div class="cross-layout">
<button class="btn abxy-btn position-top btn-x"
on:mousedown={() => btnXHandler(0)}
on:mouseup={() => btnXHandler(1)}>X</button>
<button class="btn abxy-btn position-left btn-y"
on:mousedown={() => btnYHandler(0)}
on:mouseup={() => btnYHandler(1)}>Y</button>
<button class="btn abxy-btn position-right btn-a"
on:mousedown={() => btnAHandler(0)}
on:mouseup={() => btnAHandler(1)}>A</button>
<button class="btn abxy-btn position-bottom btn-b"
on:mousedown={() => btnBHandler(0)}
on:mouseup={() => btnBHandler(1)}>B</button>
</div>
</div>
</div>
</div>
<style lang="scss">
.spf-controller {
display: flex;
flex-direction: row;
justify-content: center;
.btn {
border-style: none;
cursor: pointer;
}
.cross-layout {
display: grid;
grid-template-columns: 30px 30px 30px;
grid-template-rows: 30px 30px 30px;
.position-top {
grid-row: 1 / 2;
grid-column: 2 / 3;
}
.position-left {
grid-row: 2 / 3;
grid-column: 1 / 2;
}
.position-center {
grid-row: 2 / 3;
grid-column: 2 / 3;
}
.position-right {
grid-row: 2 / 3;
grid-column: 3/4;
}
.position-bottom {
grid-row: 3 / 4;
grid-column: 2/3;
}
.abxy-btn {
border-radius: 50%;
width: 30px;
height: 30px;
color: white;
&.btn-x {
background-color: blue;
&:active {
color: black;
background-color: rgb(125, 208, 255);
}
}
&.btn-y {
background-color: green;
&:active {
color: black;
background-color: rgb(185, 243, 185);
}
}
&.btn-a {
background-color: red;
&:active {
color: black;
background-color: rgb(255, 179, 179);
}
}
&.btn-b {
background-color: yellow;
&:active {
color: black;
background-color: rgb(255, 255, 154);
}
}
}
.cross-key-btn {
width: 30px;
height: 30px;
background-color: rgba(66, 86, 123, 0.5);
&:active {
color: aliceblue;
background-color: red;
}
.left-mark {
display: block;
transform: rotate(-90deg);
}
.right-mark {
display: block;
transform: rotate(90deg);
}
.bottom-mark {
display: block;
transform: rotate(180deg);
}
}
}
.controller-right {
z-index: 2;
display: inline-block;
background-color: rgb(229, 227, 250);
padding: 20px;
border-radius: 50%;
.abxy-btn-set {
display: inline-block;
padding: 10px;
background-color: rgba(66, 86, 123, 0.5);
border-radius: 50%;
}
}
.controller-left {
z-index: 2;
display: inline-block;
background-color: rgb(229, 227, 250);
padding: 30px;
border-radius: 50%;
}
.controller-center {
z-index:1;
width: 220px;
height: 110px;
background-color: rgb(229, 227, 250);
display: block;
margin: auto;
margin-top: 0;
margin-left: -80px;
margin-right: -80px;
padding-top: 20px;
text-align: center;
font-size: 0.8em;
color: gray;
.selectstart-btn-set {
display: block;
width: 140px;
text-align: center;
margin: auto;
margin-top: 50px;
.selectstart-btn {
width: 10px;
height: 35px;
border-radius: 15%;
background-color: rgba(66, 86, 123, 0.5);
transform: rotate(30deg);
&:first-child {
margin-right: 20px;
}
&:active {
background-color: rgb(255, 115, 0);
}
}
}
}
}
</style>
<script lang="ts">
export let cursorHandler: (dir: number, state: number) => void;
//...中略
</script>
//...中略
<button class="position-top btn cross-key-btn"
on:mousedown={() => cursorHandler(1,0)}
on:mouseup={() => cursorHandler(1,1)}><span class="top-mark">▲</span></button>
//...以降略
GameStage.svelte
controller.svelte
export let cursorHandler ...
cursorHandler
GameStage.svelte
<script lang="ts">
//...中略
//👇Controllerコンポーネントを追加
import Controller from '../components/controller.svelte';
//...中略
//👇各ボタンに対応したハンドラを追加
function onClickCrossCursor(direct: number, state: number) {
if (state === 0) {
if (movingLock) {return;}
switch(direct) {
case 1:
key = 'u';
break;
case 4:
key = 'd';
break;
case 2:
key = 'l';
break;
case 3:
key = 'r';
break;
default:
break;
}
if (key != oldKey) {
movingFlg = key;
movePlayer();
}
oldKey = key;
}
else if (state === 1) {
key = 'n';
if (!movingLock) {movePlayer();}
oldKey = key;
}
}
function onClickABtn(state: number) {
console.log('A', state);
}
function onClickBBtn(state: number) {
console.log('B', state);
}
function onClickXBtn(state: number) {
console.log('X', state);
}
function onClickYBtn(state: number) {
console.log('Y', state);
}
function onClickStartBtn(state: number) {
console.log('START', state);
}
function onClickSelectBtn(state: number) {
console.log('SELECT', state);
}
//...中略
</script>
//...中略
//👇HTML部にControllerコンポーネントを追加
// ... 各ボタンに対応したイベントハンドラも設定
<Controller
cursorHandler={onClickCrossCursor}
btnAHandler={onClickABtn}
btnBHandler={onClickBBtn}
btnXHandler={onClickXBtn}
btnYHandler={onClickYBtn}
btnStartHandler={onClickStartBtn}
btnSelectHandler={onClickSelectBtn}></Controller>
//...以下略
bind:this
document.getElementById
on:{イベント}属性
Svelteコンポーネントを右クリック禁止にする
<div class="hoge" on:contextmenu|preventDefault>
...
</div>
on:contextmenu|preventDefault
Svelteコンポーネントでマウス操作中のテキスト選択を無効にする
onselectstart
div
「on:selectstart|preventDefault」
<div id="wrapper" on:selectstart|preventDefault>
...
</div>
onselectstart
app.css
app.scss
//...
#app {
margin: 0 auto;
padding: 12px;
//👇追加
user-select: none;
}
onselectstart
user-select: none;
iframeのリソースキャッシュを無効化するメタタグ
iframe
index.html
<!DOCTYPE html>
<html lang="ja">
<head>
...略
<!-- 👇以下の3つのタグでブラウザキャッシュを無効化 -->
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Cache-Control" content="no-cache">
<meta http-equiv="Expires" content="0">
...略
</head>
...略
まとめ
1. 親子関係にあるSvelteコンポーネント間のイベントの渡し方
2. ゲーム画面上のコンポーネントを右クリック禁止にする
3. ゲーム画面上の文字テキストの選択を禁止にする
4. iframeのリソースキャッシュを無効化するメタタグの書き方
記事を書いた人
ナンデモ系エンジニア
これからの"地方格差"なきプログラミング教育とは何かを考えながら、 地方密着型プログラミング学習関連テーマの記事を不定期で独自にブログ発信しています。
カテゴリー