WebAssemblyで遊んでみるその2〜web_sysでブロック崩し〜

こんにちは。とらのあなラボ所属のY.Fです。
前回のWebAssemblyの記事ではチュートリアルを通して環境構築&JavaScriptとWebAssemblyを連携する方向でアプリケーションを作成しました。

(前回の記事はこちら) toranoana-lab.hatenablog.com

今回は、web_sysというCrateを使ってRustのみでMDNのCanvasチュートリアルで紹介されているブロック崩しを作ってみます。

(MDN) developer.mozilla.org

(web_sys) rustwasm.github.io

web_sysとは

前回の記事では説明していませんでしたが、中身では wasm_bindgen というCrateを使って、RustとJSの世界の結びつけを行っていました。
今回は更に web_sys というCrateを使って見ます。
これはRustからDOM APIを触るためのライブラリとなります。(以下のような感じ)

let document = web_sys::window().unwrap().document().unwrap();
let canvas = document.get_element_by_id("myCanvas2").unwrap();

今回はこれを使ってCanvasAPIを触ります。

続きを読む

技術書同人誌博覧会で購入した技術書の社内読書会を開催しました!

はじめまして!8月より虎の穴ラボに入社したHKです!
先日開催された技術書同人誌博覧会にてラボのメンバーが購入した技術書を持ち寄って読書会を開催しました!

技術書同人誌博覧会への出展レポートはこちら toranoana-lab.hatenablog.com

続きを読む

技術書同人誌博覧会に出展しました。

こんにちは!虎の穴ラボのNSSです。

2019/07/27(土)、大田区産業プラザPiOにて、第1回「技術書同人誌博覧会(技書博)」が開催されました。
虎の穴ラボは、こちらのイベントにスポンサー及びサークルとして参加させていただきました。

今回は、初開催となった「技書博」の模様をレポートしたいと思います。

レポート

当日

当日は台風の予報が出ており、ちゃんと開催されるか心配していましたが、
ギリギリで散ってくれたので、快晴の下での開催となりました。

京急蒲田駅東口を出ると、会場である大田区産業プラザPiOがあります。 f:id:toranoana-lab:20190802123110j:plain

入場するとノベルティとして、トートバッグ、公式パンフレットなどがもらえます。
f:id:toranoana-lab:20190802124647j:plain f:id:toranoana-lab:20190802122809j:plain 公式パンフレットの内容は、サークル紹介やスポンサー様が寄稿した記事などが中心です。
虎の穴ラボが寄稿した記事も掲載されていますので、手に入れた方はぜひ読んでみてください。

技書博の特徴

技書博の印象的な特徴を2点ご紹介します。

続きを読む

とらラボエンジニアの日常⑹とらラボ恒例!?誕生日プレゼント紹介〜おーばつーる編〜

f:id:toranoana-lab:20190730170601p:plain

こんにちは!とらのあなデザイナーのすずです。

先週おーばつーるさんがお誕生日を迎えられたということで、誕生日プレゼント紹介第3弾です!

お誕生日おめでとうございます♪

おーばつーるさんといえばプチラスカルと筋肉…(詳しくは休日編をチェック👀)やはりプレゼントはその2つにスポットが当てられているのでしょうか!?

それでは早速見ていきましょう🏃‍♀️

続きを読む

WebAssemblyで遊んでみる〜Rust+wasm-pack環境構築編〜

こんにちは。とらのあなラボ所属のY.Fです。

最近情報収集していると俄にWebAssemblyの盛り上がりを感じます。
私はフロントエンドベースにして、Web周りを何でもやるエンジニアとして働いているのですが、
フロントエンドやるにあたってWebAssemblyについていけないとまずいなと感じたのでRustで入門してみます。
ちなみにRustは少し前にプログラミング言語Rustを一通りやってみた程度にしか知りません。

そもそもWebAssemblyとは

MDNの解説に詳しく載っていますが、Webブラウザ上で動作するバイナリファイルです。
調べた限りではWebAssemblyをビルドできる言語には以下のような物があるようです。

今回はこの中でRustのwasm-packというツールでWebAssemblyの環境構築をしてみます。
また、ちょうどわかりやすいチュートリアルもあるのでやってみました。

rustwasm.github.io

Rustとツールのインストール

基本的にはこのチュートリアルのsetupの章に従えば完了です。

$ curl https://sh.rustup.rs -sSf | sh
$ curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
$ cargo install cargo-generate
$ npm install npm@latest -g

チュートリアルやってできるもの

何をするかは上記チュートリアルを見てもらえばいいので割愛します。
出来上がるのは以下のようなライフゲームです。

f:id:toranoana-lab:20190726121639g:plain

webpackと連携する

上記のチュートリアルをやると以下のようなフォルダ構成で最終的に出来上がると思います

.
├── Cargo.lock
├── Cargo.toml
├── LICENSE_APACHE
├── LICENSE_MIT
├── README.md
├── pkg
│   ├── README.md
│   ├── package.json
│   ├── wasm_game_of_life.d.ts
│   ├── wasm_game_of_life.js
│   ├── wasm_game_of_life_bg.d.ts
│   └── wasm_game_of_life_bg.wasm
├── src
│   ├── lib.rs
│   └── utils.rs
├── target
├── tests
│   └── web.rs
└── www
    ├── LICENSE-APACHE
    ├── LICENSE-MIT
    ├── README.md
    ├── bootstrap.js
    ├── index.html
    ├── index.js
    ├── package-lock.json
    ├── package.json
    └── webpack.config.js

これはwasm-packによってWebAssemblyでnpmモジュールを作るような構成になっているようです。
そのため、www以下に画面表示用のhtmlやjsファイルが置かれるようになっています。
一方で、フロントエンドの一部としてWebAssemblyを開発したいと思ったときに、package.jsonがあるディレクトリをメインに置いて、Rustのプロジェクトをサブディレクトリに置きたいと感じる方は多いと思います。

幸いなことにwebpackのプラグインとして wasm-pack-plugin が用意されています。これを使うことでwebpackとwasm-packを連携して、wasmを一括管理することができるようになります。
これでwebpack + wasm-packの環境に上記環境を移行していきます。 (参考 wasm-bindgen + wasm-pack + webpack で フロントエンド - Qiita)

webpack化することで以下のような恩恵が受けられます。

  • フロントエンドに慣れている人からするとフロントエンドのソースとRustのソースを同じような形で扱える
  • Rust側のソースを変更した場合webpack-dev-serverによって自動ビルドなどができる

webpackの環境準備

よくある一般的なwebpack環境のインストール手順です。

$ mkdir wasm-game-of-life-wabpack
$ cd wasm-game-of-life-wabpack
$ npm init
$ npm install --save-dev webpack webpack-cli webpack-dev-server typescript ts-loader html-webpack-plugin @wasm-tool/wasm-pack-plugin
$ mkdir src
$ touch src/index.ts
$ touch src/index.html
$ touch webpack.config.js
$ touch tsconfig.json

webpack.config.jsは以下の様な感じにしました。

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const WasmPackPlugin = require('@wasm-tool/wasm-pack-plugin');

module.exports = {
  resolve: {
    extensions: ['.ts', '.tsx', '.js', '.jsx', '.wasm']
  },
  module: {
    rules: [{
      test: /\.tsx?$/,
      loader: 'ts-loader',
      options: {
        transpileOnly: true
      }
    }]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: path.join(__dirname, 'src/index.html')
    })
  ]
};

ほかtsconfigなどの中身は代わり映えしないので割愛です。

Rustのプロジェクトを設置

以下コマンドでRustプロジェクトを作ります。

$ cargo new wasm-game-of-life --lib

出来上がったフォルダ内のCargo.tomlに追記します。チュートリアルで使ったものをそのままコピペでも動くと思いますが、余計なものが入るので以下のようにしました。

[package]
name = "wasm-crate"
version = "0.1.0"
authors = ["y-fujiwara"]
edition = "2018"

[dependencies]
wasm-bindgen = "^0.2"
console_error_panic_hook = { version = "0.1.1", optional = true }
wee_alloc = { version = "0.4.2", optional = true }

[lib]
crate-type = ["cdylib", "rlib"]

[features]
default = ["console_error_panic_hook", "wee_alloc"]

[dev-dependencies]
wasm-bindgen-test = "0.2"

[profile.release]
opt-level = "s"

[dependencies.web-sys]
version = "0.3"
features = [
    "console",
]

また、srcディレクトリが出来ているので、その中にチュートリアルで作成した lib.rs 及び utils.rs をコピペします。
コピペしたらビルドのみしておきます。

$ cargo build
$ wasm-pack build

この段階で以下の様なフォルダ構成になるはずです。

.
├── package-lock.json
├── package.json
├── node_modules
├── src
│   ├── index.html
│   └── index.ts
├── tsconfig.json
├── wasm-game-of-life
│   ├── Cargo.lock
│   ├── Cargo.toml
│   ├── pkg
│   │   ├── package.json
│   │   ├── wasm_crate.d.ts
│   │   ├── wasm_crate.js
│   │   ├── wasm_crate_bg.d.ts
│   │   └── wasm_crate_bg.wasm
│   ├── src
│   │   ├── lib.rs
│   │   └── utils.rs
│   └── target
└── webpack.config.js

wasm-pack-pluginを使ってwebpackとwasm-packを連携する

webpack.config.jsに以下を追記します。

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const WasmPackPlugin = require('@wasm-tool/wasm-pack-plugin');

module.exports = {
  resolve: {
    extensions: ['.ts', '.tsx', '.js', '.jsx', '.wasm'],
  },
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        loader: 'ts-loader',
        options: {
          transpileOnly: true,
        },
      },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: path.join(__dirname, 'src/index.html'),
    }),
    // 追加
    new WasmPackPlugin({
      crateDirectory: path.join(__dirname, 'wasm-game-of-life'),
      outName: 'wasm_game_of_life',
    }),
  ],
};

wasmを読み込むファイルを作っておきます。

$ touch lifegame.ts

中身はチュートリアルで作ったindex.jsと基本は同じなのですが、package.jsonに "wasm-game-of-life": "file:./wasm-game-of-life/pkg" の記載がない場合は以下のようにwasmを読み込みます。

import {Universe, Cell} from '../wasm-game-of-life/pkg';
import {memory} from '../wasm-game-of-life/pkg/wasm_crate_bg';

// 以下略

次に、index.tsを実装します。注意点としてはwasmの読み込みは非同期である必要があるのでlifegame.tsを非同期読み込みするように書く必要があります。

import('./lifegame')
  .then(mod => mod.run())
  .catch(e => console.error('Error importing `lifegame.js`:', e));

実行

npm start して http://localhost:8080 でライフゲームが動いていることを確認できれば完了です!

動かない場合はルートディレクトリのpkgフォルダを消してみたり、wasm-game-of-life/pkgを消して再ビルドしてみたりすると良いかと思います。

まとめ

今回は環境構築をしてチュートリアルで作ったアプリをwebpackに連携するまで実施しました。
次回はcanvasを使ってもう少し高度なゲームをなどを実装してみたいと思います。 また、個人的にはAssemblyScriptに注目しているのでなにかあればまたブログ書きたいと思います。

P.S

虎の穴では一緒に働く仲間を絶賛募集中です! この記事を読んで、興味を持っていただけた方はぜひ弊社の採用情報をご覧下さい。 yumenosora.co.jp

また、今月7/30には会社説明会を開催いたします。ご興味のある方は是非ご応募ください! yumenosora.connpass.com