MDN ブロックくずしゲームでウェブゲーム開発に入門してみた。その1

このプログラムはGitHub Pagesで公開しています。

放置しているのもどうかと思うので半年ぶりに書きます。

ウェブゲーム開発に入門

MDN web docs にある 「そのままのJavaScriptを使ったブロックくずしゲーム」 の教材でウェブゲーム開発に入門してみる。

PureScript+Halogen

この教材は pure JavaScript(そのままのJavaScript)とあるけど, そのままのJavaScript(UIフレームワークなし)は個人的にしんどいので変更します。

OutIn
プログラミング言語JavaScriptPureScript
UIフレームワーク無しHalogen

PureScript ってのは簡単にいうと
ビルドするとJavaScriptを得られる正格評価のHaskell。

開発環境

TEXT
Microsoft Windows [Version 10.0.22000.613]
(c) Microsoft Corporation. All rights reserved.

C:\Users\aki>wsl -l -v
  NAME            STATE           VERSION
* Ubuntu-20.04    Running         2
クリックして展開し、詳細を表示
BASH
$ cat /etc/os-release
PRETTY_NAME="Ubuntu 22.04 LTS"
NAME="Ubuntu"
VERSION_ID="22.04"
VERSION="22.04 (Jammy Jellyfish)"
VERSION_CODENAME=jammy
ID=ubuntu
ID_LIKE=debian
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
UBUNTU_CODENAME=jammy
クリックして展開し、詳細を表示

このWindows 11 + WSL2 + Ubuntuに
https://github.com/purescript/documentation/blob/master/guides/Getting-Started.md
を済ませた開発環境で始める。

Halogen example

Halogen example の Basicを動かしてみる。

BASH
aki: $ git clone https://github.com/purescript-halogen/purescript-halogen.git
Cloning into 'purescript-halogen'...
remote: Enumerating objects: 9892, done.
remote: Counting objects: 100% (385/385), done.
remote: Compressing objects: 100% (208/208), done.
remote: Total 9892 (delta 195), reused 325 (delta 167), pack-reused 9507
Receiving objects: 100% (9892/9892), 3.91 MiB | 5.70 MiB/s, done.
Resolving deltas: 100% (6005/6005), done.
aki:~$ cd purescript-halogen/examples/basic/
クリックして展開し、詳細を表示
BASH
aki:~/purescript-halogen/examples/basic$ npm install
aki:~/purescript-halogen/examples/basic$ npm run example-basic

省略...

[info] Build succeeded.
[info] Bundle succeeded and output file to examples/basic/dist/example.js
クリックして展開し、詳細を表示
TEXT
aki:~/purescript-halogen/examples/basic$ cd dist/
aki:~/purescript-halogen/examples/basic/dist$ wslpath -w .
\\wsl.localhost\Ubuntu-20.04\home\aki\purescript-halogen\examples\basic\dist
クリックして展開し、詳細を表示

Webブラウザで上のパスを開いて index.htmlを開く。ボタンがあってOnOffできることを確認出来たらOK。

自分の書くコードの置き場

BASH
aki:~/purescript-halogen/examples/basic/dist$ cd
aki:~$ mkdir breakout
aki:~$ cd breakout
aki:~/breakout$ mkdir lesson01
aki:~/breakout$ cd lesson01/
aki:~/breakout/lesson01$ spago init
aki:~/breakout/lesson01$ spago run

省略...

[info] Build succeeded.
🍝
クリックして展開し、詳細を表示

purescript-halogen/examples/basic/src/ にある Main.purs と Button.purs を src/ にコピーする。

BASH
aki:~/breakout/lesson01$ cp ~/purescript-halogen/examples/basic/src/* src/
aki:~/breakout/lesson01$
aki:~/breakout/lesson01$ ls src
Button.purs  Main.purs
クリックして展開し、詳細を表示

vscodeを起動する

BASH
aki:~/breakout/lesson01$ code .
クリックして展開し、詳細を表示

lesson01-1

赤線が出ている。そういえばVSCode拡張機能をいれていた。

lesson01-2

これは halogen が無いというエラーだから, とりあえずターミナルから halogen を入れる。

BASH
aki:~/breakout/lesson01$ spago install halogen
aki:~/breakout/lesson01$ spago build
クリックして展開し、詳細を表示

Main.pursを保存すると赤線が消える。

BASH
aki:~/breakout/lesson01$ spago bundle-app
[info] Build succeeded.
[info] Bundle succeeded and output file to index.js
クリックして展開し、詳細を表示

これで index.js が出来た。

続いて purescript-halogen/examples/dists/index.html を ./ にコピーする。

BASH
aki:~/breakout/lesson01$ ls
index.js  output  packages.dhall  spago.dhall  src  test
aki:~/breakout/lesson01$ cp ~/purescript-halogen/examples/basic/dist/index.html ./
aki:~/breakout/lesson01$ ls
index.html  index.js  output  packages.dhall  spago.dhall  src  test
クリックして展開し、詳細を表示

VSCode上で index.htmlを開いて
<script src="example.js"></script>
と書かれているところを
<script src="index.js"></script>
にして保存する。

Webブラウザでこの index.htmlを開く。
同じくボタンがあってOnOffできることを確認出来たらOK。

lesson01

準備が済んだので Canvasを作ってその上に描画する を始めます。

ゲームのHTML

チュートリアル通りならこのとおりだけど

HTML
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>Gamedev Canvas Workshop</title>
    <style>
    	* { padding: 0; margin: 0; }
    	canvas { background: #eee; display: block; margin: 0 auto; }
    </style>
</head>
<body>

<canvas id="myCanvas" width="480" height="320"></canvas>

<script>
	// JavaScriptのコードがここに入ります
</script>

</body>
</html>
クリックして展開し、詳細を表示

中身はプログラムの方で用意するので, index.htmlはこのくらいにする。

HTML
<!DOCTYPE html>
<html>

<head>
  <meta charset="UTF-8">
  <title>Gamedev Canvas Workshop</title>
</head>

<body>
  <script src="./index.js"></script>
</body>

</html>
クリックして展開し、詳細を表示

これをindex.htmlに保存する。

lesson01-3

ブロックくずしゲームのUIコンポーネント

今あるButton.purs は不要なので削除して Breakout.purs を新規作成する。

HASKELL
module Breakout (component) where

import Prelude
import CSS (background, block, display, margin, marginBottom, marginLeft, marginRight, marginTop, padding, px, rgb)
import CSS.Common (auto)
import Data.Maybe (Maybe(..))
import Effect (Effect)
import Effect.Aff.Class (class MonadAff)
import Graphics.Canvas (CanvasElement)
import Graphics.Canvas as Canvas
import Halogen as H
import Halogen.HTML as HH
import Halogen.HTML.CSS (style)
import Halogen.HTML.Properties as HP
import Math as Math

canvasId :: String
canvasId = "myCanvas"

type State
  = {}

data Action
  = Initialize

component :: forall query input output m. MonadAff m => H.Component query input output m
component =
  H.mkComponent
    { initialState
    , render
    , eval:
        H.mkEval
          $ H.defaultEval
              { handleAction = handleAction
              , initialize = Just Initialize
              }
    }

initialState :: forall i. i -> State
initialState _ = {}

render :: forall m. State -> H.ComponentHTML Action () m
render _ =
  HH.main
    [ style do
        margin (px 0.0) (px 0.0) (px 0.0) (px 0.0)
        padding (px 0.0) (px 0.0) (px 0.0) (px 0.0)
    ]
    [ HH.canvas
        [ style do
            background (rgb 238 238 238)
            display block
            marginTop (px 0.0)
            marginRight auto
            marginBottom (px 0.0)
            marginLeft auto
        , HP.id canvasId
        , HP.width 480
        , HP.height 320
        ]
    ]

handleAction :: forall output m. MonadAff m => Action -> H.HalogenM State Action () output m Unit
handleAction = case _ of
  Initialize -> do
    maybeCanvas <- H.liftEffect $ Canvas.getCanvasElementById canvasId
    H.liftEffect
      $ case maybeCanvas of
          Nothing -> pure unit
          Just canvas -> draw canvas

draw :: CanvasElement -> Effect Unit
draw canvas = do
  ctx <- Canvas.getContext2D canvas
  --
  Canvas.beginPath ctx
  Canvas.rect ctx
    $ { x: 20.0
      , y: 40.0
      , width: 50.0
      , height: 50.0
      }
  Canvas.setFillStyle ctx "#FF0000"
  Canvas.fill ctx
  Canvas.closePath ctx
  --
  Canvas.beginPath ctx
  Canvas.arc ctx
    $ { x: 240.0
      , y: 160.0
      , radius: 20.0
      , start: 0.0
      , end: Math.pi * 2.0
      }
  Canvas.setFillStyle ctx "green"
  Canvas.fill ctx
  Canvas.closePath ctx
  --
  Canvas.beginPath ctx
  Canvas.rect ctx
    $ { x: 160.0
      , y: 10.0
      , width: 100.0
      , height: 40.0
      }
  Canvas.setStrokeStyle ctx "rgba(0, 0, 255, 0.5)"
  Canvas.stroke ctx
  Canvas.closePath ctx
クリックして展開し、詳細を表示

Main.purs をこう書き換える。

HASKELL
module Main where

import Prelude
import Effect (Effect)
import Breakout as Breakout
import Halogen.Aff as HA
import Halogen.VDom.Driver (runUI)

main :: Effect Unit
main =
  HA.runHalogenAff do
    body <- HA.awaitBody
    runUI Breakout.component unit body
クリックして展開し、詳細を表示

spagoの指示通り CSS Halogen-CSS Canvas Math Aff Maybe を入れる。

BASH
aki:~/breakout/lesson01$ spago install css halogen-css canvas math aff maybe
クリックして展開し、詳細を表示

Canvasの基本

チュートリアルで説明されている「Canvasの基本部分」はここ。

HASKELL
draw :: CanvasElement -> Effect Unit
draw canvas = do
  ctx <- Canvas.getContext2D canvas
  --
  Canvas.beginPath ctx
  Canvas.rect ctx
    $ { x: 20.0
      , y: 40.0
      , width: 50.0
      , height: 50.0
      }
  Canvas.setFillStyle ctx "#FF0000"
  Canvas.fill ctx
  Canvas.closePath ctx
  --
  Canvas.beginPath ctx
  Canvas.arc ctx
    $ { x: 240.0
      , y: 160.0
      , radius: 20.0
      , start: 0.0
      , end: Math.pi * 2.0
      }
  Canvas.setFillStyle ctx "green"
  Canvas.fill ctx
  Canvas.closePath ctx
  --
  Canvas.beginPath ctx
  Canvas.rect ctx
    $ { x: 160.0
      , y: 10.0
      , width: 100.0
      , height: 40.0
      }
  Canvas.setStrokeStyle ctx "rgba(0, 0, 255, 0.5)"
  Canvas.stroke ctx
  Canvas.closePath ctx
クリックして展開し、詳細を表示

ブラウザーで確認すると

lesson01-4

GitHubリポジトリ

コードはGitHub上にあります。
https://github.com/ak1211/breakout

つづく

よてい

著作権表示

著者: Akihiro Yamamoto

リンク: https://ak1211.com/posts/7716/

ライセンス: CC BY-NC-SA 4.0

This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. Please attribute the source, use non-commercially, and maintain the same license.

コメント

検索を開始

キーワードを入力して記事を検索

↑↓
ESC
⌘K ショートカット