はじめまして, 都内でCTO(Chotto Technology Ojisan)やCFO(Chotto Frontend Ojisan)をしている @sadnessOjisan です. この投稿はクソアプリカレンダー2の13日目の投稿です.
先人たちはすごいクソの山 1 を作っていたので, ぼくも負けじとクソを作ろうと思い, 気合を入れました. そこで玉もバーもmarquee
タグで実装されたブロック崩し Marquee Breakout を開発しました. (残念ながらPCのみでの対応です. 小さい画面だとプレイできないようになっています. )
ソースコードは記事の末尾にあります. 初めての投稿なのですが, よろしくお願いいたします.
ゲームはこちら: https://inspiring-galileo-db9414.netlify.com/
はじめに
みなさん, marquee
タグってご存知でしょうか??こんなやつです.
marquee
タグって昔のHPにあった, 要素をスライドさせられるタグのことです.
<marquee>あなたは100人目のお客様です</marquee>
実はこのmarquee
タグは, 入れ子にすることができます.
<marquee direction='up' height='400px'>
<marquee>入れ子だよ〜〜〜〜</marquee>
</marquee>
入れ子にすると何が起きるでしょうか. なんと, 片方のmarquee
の移動方向を上向きにしていると, 斜めに動くようになります .
そして, 入れ子にするとなんとブロック崩しを作れます(困惑).
バーは普通のmarqueeで作れます.
<marquee behavior='alternate'>ーーーーーーーーーーーーーーー</marquee>
behavior='alternate'
は跳ね返りを指定するためのプロパティです.
玉は斜めに動かす + 跳ね返り指定で作れます. (入れ子 + alternate
指定)
<marquee direction='up' height='400px' behavior='alternate'>
<marquee behavior='alternate'>●</marquee>
</marquee>
今日はmarquee
タグ実装したブロック崩しゲームをクソアプリとして持ってきました!それが, marquee
タグの挙動に詳しくないとなかなか高得点を取れないゲーム, Marquee Breakoutです.
この記事で学べること
- Marquee Breakoutの遊び方
-
marquee
タグが魅せる未来 -
marquee
タグでブロック崩しを作る方法 - ログインとスコア保存機能をもったサービスを, 無料で開発・ホスティングできる方法
Marquee Breakout
遊び方
- 全国ランキングに挑戦したい人はサインアップする.
- マーキーのパラメータを入力してスタートする.
- 球が下に落ちないようにパラメータを調節する.
ぜひとも, marquee
に変わって台頭してきた今時の子たちに球をぶつけてください. 2
どれを壊しても1点です.
marquee
タグが魅せる未来
Marqueeタグは今のHTML5の仕様では廃止されている
マーキー要素は様々な理由からHTML5から廃止されたタグです. 明確な理由は私が探す限りでは見つけられなかったのですが, アクセシビリティが悪い, セマンティックなタグではないといった批判がされていました. 3
Marqueeタグの使い道
そんなmarqueeタグですがこの子は多種多様なことができます.
その面白い挙動は marqueeのいろいろな移動にまとめられていたので, 再現してみました.
文字を流す
<marquee>あなたは100人目のお客様です</marquee>
折り返す
<marquee behavior='alternate' scrollAmount='50'>折り返すよー</marquee>
上にも流せる
<marquee direction='up' scrollAmount='50' height='500px'>上上上上上</marquee>
波を作る
marqueeのいろいろな移動 さんから拝借しています.
<marquee bgcolor="#f0f0ff" width="500">
<marquee bgcolor="#f0f0ff" height="50" width="40" direction="up" behavior="alternate">
波~
</marquee>
</marquee>
斜めに流す
これも先ほどのものです.
マーキーの入れ子でブロック崩しを作れる
marqueeのいろいろな移動 さんから拝借しています.
<marquee bgcolor="#f0f0ff" width="300" behavior="alternate" scrollamount="11">
<marquee bgcolor="#f0f0ff" width="16" height="270" direction="up" behavior="alternate" scrollamount="4">●</marquee>
</marquee>
<BR>
<marquee bgcolor="#f0f0ff" width="300" behavior="alternate" scrollamount="12">
</marquee>
ブロック崩しを作るにあたっての課題
このように入れ子にして多様な動きを出せました. しかしブロック崩しの動きであって, ブロック崩しではありません. ブロック崩しをするためにはブロックとの衝突判定, バーとの衝突判定や跳ね返りが必要です. またゲームのスコアやゲームオーバーしているかの状態の管理も必要になってきます. それらはどのようにして実装すればよいのでしょうか. それらを次の章にまとめてみました.
技術的なおはなし
フロントエンド側
衝突判定を行うためにJavaScript
を利用してmarquee
タグの要素を監視・制御しました.
Marqueeの挙動
プロパティを設定することで動き方を制御できます. 方向は direction
, 跳ね返りの挙動は behavior
, スピードはscrollamount
で指定できます. どのようなプロパティを利用できるかは: マーキー要素 (廃止)を参照すると良いでしょう. しかしアニメーション中にプロパティを書き換えても, アニメーション終了後にしか反映されないため注意が必要です.
ブロックやバーにぶつかると跳ね返るというのは, behavior='alternate'
とすれば実現できます.
プロパティ名 | 制御できる箇所 |
---|---|
behavior | スクロール方法 |
bgcolor | 背景色 |
direction | スクロール方向 |
height | スクロールの高さ |
hspace | 高さのpadding |
loop | 繰り返し回数 |
scrollamount | 1インターバルにおける要素の移動ピクセル数 |
scrolldelay | 1インターバルとして扱う時間 |
truespeed | scrolldelayに足切りを設けるかのフラグ |
vspace | 垂直方向のpadding |
width | スクロールの幅 |
marqueeのプロパティの数値調整
背に腹は代えられません. React
を使いました. 反則かもしれませんが, 便利なものはどんどん使って行きましょう. marquee
はJSX
の中でも動きます. marquee
のプロパティにprops
を渡してプログラマブルなmarquee
タグを作ることができます. 4
<GameCanvas>
<Score>your score is {score}. </Score>
<Blocks />
<marquee
behavior={ballYBehavior}
scrollamount={ballYSpeed}
height={GAME_SCREEN_HEIGHT}
style={{ position: "absolute", top: `${bounceBorder}px` }}
direction={vBallDirection}
>
<marquee
behavior={ballXBehavior}
scrollamount={ballXSpeed}
direction={hBallDirection}
width={`${width}%`}
>
<Ball ref={this.ball}>●</Ball>
</marquee>
</marquee>
<marquee
behavior={barBehavior}
scrollamount={barSpeed}
style={{ position: "absolute", top: GAME_SCREEN_HEIGHT }}
>
<span ref={this.text}>--------------------</span>
</marquee>
</GameCanvas>
ちなみにReact
でMarquee
タグ (ReMarquee) を作ったこともあります.
FYI. Marqueeタグでブロック崩しを作りたい
marquee内の要素の位置を取得する
ref
を取ってDOMの位置を取得します. marquee
移動中の位置はsetTimeout
で毎sec取得し, state
を更新していきましょう. そのstate
は球, バー, ブロックにpropsとして渡して生きましょう. 「なんかやばいことしていない?」だって?クソアプリなので実装はクソで良いんですよ. 5
private text = React.createRef<HTMLParagraphElement>();
private ball = React.createRef<HTMLParagraphElement>();
setInterval(() => {
if (this.text.current && this.ball.current) {
const barPosition = this.text.current.getBoundingClientRect();
const barLeftPosition = barPosition.left;
const barTopPosition = barPosition.top;
const barBottomPosition = barPosition.bottom;
const ballPosition = this.ball.current.getBoundingClientRect();
const ballLeftPosition = ballPosition.left;
const ballWidth = ballPosition.width;
const ballRightPosition = ballLeftPosition + ballWidth;
const ballTopPosition = ballPosition.top;
const ballBottomPosition = ballPosition.bottom;
const barWidth = this.text.current.getBoundingClientRect().width;
const barRightPosition = barLeftPosition + barWidth;
this.setState({
barPositon: {
left: barLeftPosition,
right: barRightPosition,
top: barTopPosition,
bottom: barBottomPosition
},
ballPosition: {
top: ballTopPosition,
left: ballLeftPosition,
right: ballRightPosition,
bottom: ballBottomPosition
}
});
}
}, 1);
marqueeタグ要素とブロックの衝突判定を行う
ここが僕の一番の工夫ポイントです. 実はblock
一つ一つをコンポーネントとして作り, その中で, block
の位置のstate
とprops
経由で知られせるバーと球の一から, ブロックにぶつかったkどうかの衝突判定を行なっていました.
玉の位置はms間隔でprops
から流し込まれます. あとはprops
が変更されるたびに static getDerivedStateFromProps
でstateを衝突判定とstate
の更新を行うと良いです. 6
static getDerivedStateFromProps(nextProps: Props, prevState: State) {
const blockState = prevState;
const { onCollide } = nextProps;
const { left, right, top, bottom } = nextProps.ballPosition;
if (left && right && top && !blockState.isCollapsed) {
if (
top <= blockState.bottom &&
right >= blockState.left &&
left <= blockState.right &&
bottom >= blockState.top
) {
onCollide(bottom);
return { isCollapsed: true };
}
}
}
最後にお型付け
とりあえずany
って書いておきましょう. クソアプリカレンダーなので大丈夫です. ここには上司もレビュアーもいません. 7
ちなみにstyled-components
で使うpropsの型付けの方法が分からなくてて苦労しました.
サーバー&インフラ側
@kt3kさんのNetlify+Node.js+MongoDBでJAMstackな草を生やすサービスをOSSで公開しました!という記事を参考にしました. この記事では, スケーラブルで高機能なWebアプリケーションを0円で開発・公開するための方法を紹介しており, 当記事でもその構成を参考にしました. また記事内には,
Web 2.0 になってフロントエンド JavaScript が複雑化する中で、これが正解と言える Web プログラミングのスタイルがなかなか分かりづらい時期が続いていたように思います。また、上から下までの動的な Web サイトを作るのに必要な知識が相当に増えてしまったように感じました。そのせいか、(統計がある訳ではありませんが)自分の観測範囲では、誰かが趣味で作った変な Web サービスを見かける機会が減ってしまったように感じました。
...
この記事を読んで、変な思いつきの/独創的な Web サービスを作る人が出てくれたら良いなと思います。
とあり, 変な思いつきなWebサービス作りのために参考にさせていただきました. ソースコードも公開されているため, 興味がある方は一読されることをオススメします.
NodeJS
NodeJSは, サーバーサイドで動くJSです. express
というライブラリを利用することで, ルーティングの設定やリクエストのparse
を簡単に行え, 手間がかかる設定も少なくWebアプリを動かせます.
Auth0
Auth0は, 認証機能を提供するサービス(IDaaS)です. このサービスを使うことで, ログインやユーザーを管理する機能を自前で作ることなく, それらの機能を利用できます. 実装もとても簡単で, フロントエンド側のJavaScriptにはauth-0.js
というライブラリを入れてそのメソッドを呼ぶだけでよく, サーバー側もSDKとを入れて, 認証したいルーティングに少し設定を書くだけで認証ができます.
これにより, ログインユーザーでしかAPIにアクセスできなくしたり, ログインしたユーザーの特定などができます. Marquee Breakoutではスコアを登録するときの機能をログインユーザーしか行えないようにしています.
mongodb + mongoose + mlab
mongodb は, 「JSON
をそのまま保存できて, そのまま取り出せるかのように扱うことができてしまうDB」です. スキーマ的なものは, アプリケーションのコードにそのまま書けて, どんどん破壊的な変更を加えても動きはするので, スピード重視開発だとすごい楽です. 操作やデータの構造も直感的なのでそれもスピーディな開発を行う上で非常に助かりました.
そして, mlabはそのDBをクラウド上にホスティングしてくれるサービスで500MBまでならタダで使えます. 同様のサービスにmongodb
が提供しているMongoDB Atlasというものがあります. 本当はこちらを採用したかったのですが, プランの設定, NodeJS
のバージョン, mongodb
のバージョン, mongoose (mongodb
のODM)のバージョンが合わないと使えなさそうだったので, 少し格闘した末に諦めてmlab
を採用しました.
Now
これらの 機能を呼び出せるサーバーは Now というサービスにデプロイしています. Nowも無料で利用できます. package.json
やDockerfile
を書いて
$ now
ってすれば, もうそれだけでホスティングできます. 無料プランでは発行されるURLはランダムなのですが, 固定化できたり, デプロイ前に発行されるURLも知れるので, クライアント側にあらかじめ向き先を登録することができます.
Netlify
クライアントサイドのコードは Netlifyにデプロイしています. 僕はZeit教なのでこちらもNow
にあげたかったのですが, クソアプリカレンダーは失敗しても良さそうな雰囲気を感じ, 新しいことに挑戦してもバチが当たらないと思い, Netlify
を使ってみました. これはGithubと連携し, あるブランチにコードがpushされたらそのコードを対応する向き先にデプロイしてくれるサービスです. CDっぽいことをしてくれるだけでなく, デプロイ先も提供してくれるのに無料で利用できます. 学習コストがかからなかったのでとても助かりました.
終わりに
後半は真面目な話になってしまいましたが, このクソアプリはいかがでしたでしょうか. 是非, 全国TOP目指して頑張ってください!!!
ブロック崩しコード: https://github.com/sadnessOjisan/MarqueeBreakout
(P.S.) Qiitaの投稿楽しいですね!!いつも見てばっかりだったのですが, これからもがんばって投稿していきたいです!
-
なにぃ!?「そこは
browselify
じゃなくてWebpack
だろ」だと!? 画像を差し替えるPRをください(涙) ↩ -
他にも, HTML5では
center
タグやblink
タグといったものも削除されています. ↩ -
(注)
styled-components
からはmarquee
タグをスタイリングできないため,スタイルは直接設定しましょう. ↩ -
職場で私は非常に真面目です. 今日だけです. ↩
-
static getDerivedStateFromProps
はReact
v16.3で導入されたAPIです.React
v17から,props
が更新されたときに発火される組み込み関数のcomponentWillReceiveProps
やcomponentWillUpdate
の利用は非推奨になります. ↩ -
普段のお仕事ではきちんと型を書いています. こんなことをしているのは今日だけですよ. 今日だけ. ↩