このプログラムはGitHub Pagesで公開しています。
放置しているのもどうかと思うので半年ぶりに書きます。
ウェブゲーム開発に入門 MDN web docs にある 「そのままのJavaScriptを使ったブロックくずしゲーム」
の教材でウェブゲーム開発に入門してみる。
PureScript+Halogen この教材は pure JavaScript(そのままのJavaScript)とあるけど, そのままのJavaScript(UIフレームワークなし)は個人的にしんどいので変更します。
PureScript ってのは簡単にいうと ビルドするとJavaScriptを得られる正格評価のHaskell。
開発環境 1
2
3
4
5
6
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
Copy 1
2
3
4
5
6
7
8
9
10
11
12
13
$ 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
Copy このWindows 11 + WSL2 + Ubuntuにhttps://github.com/purescript/documentation/blob/master/guides/Getting-Started.md を済ませた開発環境で始める。
Halogen example Halogen example の Basic を動かしてみる。
1
2
3
4
5
6
7
8
9
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/
Copy 1
2
3
4
5
6
7
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
Copy 1
2
3
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
Copy Webブラウザで上のパスを開いて index.htmlを開く。ボタンがあってOnOffできることを確認出来たらOK。
自分の書くコードの置き場 1
2
3
4
5
6
7
8
9
10
11
12
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.
🍝
Copy purescript-halogen/examples/basic/src/ にある Main.purs と Button.purs を src/ にコピーする。
1
2
3
4
aki:~/breakout/lesson01$ cp ~/purescript-halogen/examples/basic/src/* src/
aki:~/breakout/lesson01$
aki:~/breakout/lesson01$ ls src
Button.purs Main.purs
Copy vscodeを起動する 1
aki:~/breakout/lesson01$ code .
Copy
lesson01-1 赤線が出ている。そういえばVSCode拡張機能をいれていた。
これは halogen が無いというエラーだから, とりあえずターミナルから halogen を入れる。
1
2
aki:~/breakout/lesson01$ spago install halogen
aki:~/breakout/lesson01$ spago build
Copy Main.pursを保存すると赤線が消える。
1
2
3
aki:~/breakout/lesson01$ spago bundle-app
[ info] Build succeeded.
[ info] Bundle succeeded and output file to index.js
Copy これで index.js が出来た。
続いて purescript-halogen/examples/dists/index.html を ./ にコピーする。
1
2
3
4
5
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
Copy VSCode上で index.htmlを開いて<script src="example.js"></script>
と書かれているところを<script src="index.js"></script>
にして保存する。
Webブラウザでこの index.htmlを開く。 同じくボタンがあってOnOffできることを確認出来たらOK。
lesson01 準備が済んだので Canvasを作ってその上に描画する を始めます。
ゲームのHTML チュートリアル通りならこのとおりだけど
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!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 >
Copy 中身はプログラムの方で用意するので, index.htmlはこのくらいにする。
1
2
3
4
5
6
7
8
9
10
11
12
13
<!DOCTYPE html>
< html >
< head >
< meta charset = "UTF-8" >
< title > Gamedev Canvas Workshop</ title >
</ head >
< body >
< script src = "./index.js" ></ script >
</ body >
</ html >
Copy これをindex.htmlに保存する。
lesson01-3 ブロックくずしゲームのUIコンポーネント 今あるButton.purs は不要なので削除して Breakout.purs を新規作成する。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
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
Copy Main.purs をこう書き換える。
1
2
3
4
5
6
7
8
9
10
11
12
13
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
Copy spagoの指示通り CSS Halogen-CSS Canvas Math Aff Maybe を入れる。
1
aki:~/breakout/lesson01$ spago install css halogen-css canvas math aff maybe
Copy Canvasの基本 チュートリアルで説明されている「Canvasの基本部分」はここ。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
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
Copy ブラウザーで確認すると
lesson01-4 GitHubリポジトリ コードはGitHub上にあります。https://github.com/ak1211/breakout
つづく よてい