PlayCanvasのhtmlファイルなどをローカルのテキストエディタからuploadする [REST API]

~~**注意：このREST APIを使用するには有料のORGANIZATIONプランに加入する必要があります**(2019/06/07現在)~~
**REST APIが全プランで利用可能になりました！！！**

#PlayCanvas

GLSLやらwebGLやらをよくみるようになってから勉強するようになったんですが、
如何せん理解するのが難しいこの頃です。

もっと簡単にwebGLとか触れないかなと探して見つけたのが、[PlayCanvas](https://playcanvas.jp/)でした。
Unityみたいなエディター画面ですが、この画面からwebGLのコンテンツを作れるようで。すごいなー…

Flashが完全廃止され、HTML5ゲームとかが出てきている最中、こんなツール？が出てきたら開発の時間のコストも下がりそうですね。
そんな私はゲームは作ったことがないですが…これを機に作ってみたいですね。

ちなみにPlayCanvasはクラウドのサービスで、その中にコードエディターもあるのですが…
今まで使っていたコードエディターと比べるとsnippetが入っていないだけで結構打ちづらくなりまして…なんとか出来ないかなあ。

そこで、REST APIを使ってローカルのエディターからPlayCanvasのクラウド上になんとかuplaod出来ないかなと。

[gulp-playcanvas](https://github.com/yushimatenjin/gulp-playcanvas)なるREST APIの必要な情報を入れるだけでuploadが出来ちゃうものがありました。ステキ。
早速中身を見てみましょう！！

#gulp-playcanvas

##読んでみる
https://github.com/yushimatenjin/gulp-playcanvas

これを読んでみる。
*（上記githubのreadmeから以下のコードを引用 ※2019/06/07現在）*

###npmでインストールして

```bash
npm i -D gulp-playcanvas
```

###config.jsに必要な情報を入れて

```js
//config.js
module.exports = {
  accessToken: "accessToken",
  scenes: [scene],
  projectId: projectId,
  branchId: "branchId", 
  projectName: "projectName", 
  remotePath: "remotePath" //PlayCanvasエディター上で配置したフォルダ (例, dev, web...)
};
```

###gulpfile.jsに設置…

```js
//gulpfile.js
const gulp = require("gulp");
const playcanvas = require("gulp-playcanvas");
const pcOptions = require("./config");
const pug = require("gulp-pug");
const sass = require("gulp-sass");

gulp.task("pug", () => {
  return gulp
    .src(["src/**/*.pug", "!src/**/_*.pug"])
    .pipe(pug())
    .pipe(gulp.dest("dist/"))
    .pipe(playcanvas(pcOptions));
});

gulp.task("js", () => {
  return gulp
    .src(["src/**/*.js", "!src/**/_*.js"])
    .pipe(gulp.dest("dist/"))
    .pipe(playcanvas(pcOptions));
});

gulp.task("sass", () => {
  return gulp
    .src("src/**/*.+(scss|sass)")
    .pipe(sass())
    .pipe(gulp.dest("dist/"))
    .pipe(playcanvas(pcOptions));
});

gulp.task("watch", function() {
  gulp.watch(["src/**/*.pug", "!src/**/_*.pug"], gulp.task("pug"));
  gulp.watch(["src/**/*.js", "!src/**/_*.js"], gulp.task("js"));
  gulp.watch("src/**/*.+(scss|sass)", gulp.task("sass"));
});
gulp.task("default", gulp.parallel("watch"));
```

pugとsassとjsを更新するたびに`playcanvas(pcOptions)`でuploadするようですね。

早速自分のgulp環境でもやってみよう！

---

##自分のgulpfile.js
ちょっと突貫で作ったので結構汚いのですが許して…

```js
const gulp = require("gulp");
const pug = require("gulp-pug");
const sass = require("gulp-sass");
const postcss = require("gulp-postcss");
const autoprefixer = require("autoprefixer");
const cache = require("gulp-cache");
const terser = require("gulp-terser");
const rename = require("gulp-rename");
const notify = require("gulp-notify");
const changed = require("gulp-changed");
const plumber = require("gulp-plumber");

const browserSync = require("browser-sync");
const runSequence = require("run-sequence");

const playcanvas = require("gulp-playcanvas");
const pcOptions = require("./config");


const sassOptions = {
outputStyle: "expanded",
sourceMap: true,
sourceComments: false
};

const autoprefixerOptions = {
browsers: ["last 2 version", "ie >= 11", "Android >= 4.0"]
};

gulp.task('clear', function (done) {
  return cache.clearAll(done);
});

gulp.task("pug", () => {
  return gulp
    .src(["src/pug/**/*.pug", "!src/pug/**/_*.pug"])
    .pipe(plumber({errorHandler: notify.onError('<%= error.message %>')}))
    .pipe(pug({
      pretty: true,
      locals: {
        playcanvas: false
      }
    }))
    .pipe(changed("dist", {extension: '.html'}))
    .pipe(gulp.dest("dist/"))
    .pipe(browserSync.stream())
});
gulp.task("pug2", () => {
  return gulp
    .src(["src/pug/**/*.pug", "!src/pug/**/_*.pug"])
    .pipe(plumber({errorHandler: notify.onError('<%= error.message %>')}))
    .pipe(pug({
      pretty: true,
      locals: {
        playcanvas: false
      }
    }))
    .pipe(gulp.dest("dist/"))
    .pipe(browserSync.stream())
});
gulp.task("pug_pc", () => {
  return gulp
    .src(["src/pug/**/*.pug", "!src/pug/**/_*.pug"])
    .pipe(plumber({errorHandler: notify.onError('<%= error.message %>')}))
    .pipe(pug({
      pretty: true,
      locals: {
        playcanvas: true
      }
    }))
    .pipe(changed("pc", {extension: '.html'}))
    .pipe(gulp.dest("pc/"))
    .pipe(playcanvas(pcOptions));
});
gulp.task("pug2_pc", () => {
  return gulp
    .src(["src/pug/**/*.pug", "!src/pug/**/_*.pug"])
    .pipe(plumber({errorHandler: notify.onError('<%= error.message %>')}))
    .pipe(pug({
      pretty: true,
      locals: {
        playcanvas: true
      }
    }))
    .pipe(gulp.dest("pc/"))
    .pipe(playcanvas(pcOptions));
});



gulp.task("js", () => {
  return gulp
    .src(["src/js/*.js", "!src/js/_*.js"])
    .pipe(plumber({errorHandler: notify.onError('<%= error.message %>')}))
    .pipe(terser())
    .pipe(rename({extname: ".min.js"}))
    .pipe(gulp.dest("dist/js"))
    .pipe(browserSync.stream())
    .pipe(gulp.dest("pc/"))
    .pipe(playcanvas(pcOptions));
});

gulp.task("sass", () => {
  return gulp
    .src("src/sass/*.+(scss|sass)")
    .pipe(plumber({errorHandler: notify.onError('<%= error.message %>')}))
    .pipe(sass(sassOptions))
    .pipe(postcss([autoprefixer({autoprefixerOptions})]))
    .pipe(gulp.dest("dist/css"))
    .pipe(browserSync.stream())
    .pipe(sass({outputStyle: "compressed"}))
    .pipe(gulp.dest("pc/"))
    .pipe(playcanvas(pcOptions));
});

gulp.task("browser-sync", () => {
  browserSync.init({
    online: true,
    ui: false,
        server: {
            baseDir: "./dist",
        },
    port: 1234
  });
});
//監視開始
gulp.task("watch", () => {
  gulp.watch(['src/pug/**/*.pug','!src/pug/**/_*.pug'], gulp.task("pug"));
  gulp.watch(['src/pug/**/_*.pug'], gulp.task("pug2"));
  gulp.watch("src/sass/**/*.+(scss|sass)", gulp.task("sass"));
  gulp.watch(["src/js/*.js","!src/js/*.min.js"],gulp.task("js"));

  gulp.watch(['src/pug/**/*.pug','!src/pug/**/_*.pug'], gulp.task("pug_pc"));
  gulp.watch(['src/pug/**/_*.pug'], gulp.task("pug2_pc"));
});

gulp.task('default', gulp.parallel('clear', 'watch', "browser-sync"));

```

多分綺麗な書き方ではないのかもしれないですが、自分の環境に入れられました！

###変数

```js
const playcanvas = require("gulp-playcanvas");
const pcOptions = require("./config");
```
requireでREST APIの情報のあるconfig.jsも一緒に持ってて、

###コンパイル

```js
gulp.task("pug_pc", () => {
  return gulp
    .src(["src/pug/**/*.pug", "!src/pug/**/_*.pug"])
    .pipe(plumber({errorHandler: notify.onError('<%= error.message %>')}))
    .pipe(pug({
      pretty: true,
      locals: {
        playcanvas: true
      }
    }))
    .pipe(changed("pc", {extension: '.html'}))
    .pipe(gulp.dest("pc/"))
    .pipe(playcanvas(pcOptions));
```
コンパイル後のデータを`playcanvas(pcOptions))`でREST APIを叩きuploadします。

###ディレクトリを区分
ローカルでは`browser-sync`で確認したいファイルとuploadするファイルとで区別したかったので`gulp.task`を増やしています。
（もっと綺麗な書き方があったら教えてください…）

```js
/*-- ローカルで確認するディレクトリ --*/
.pipe(gulp.dest("dist/"))
/*-- PlayCanvasへuploadするディレクトリ --*/
.pipe(gulp.dest("pc/"))
```

###htmlのheadタグなどPlayCanvasでは不要な要素
PlayCanvasでhtmlファイルを使い時に、`<html>`とか`<head>`とかいらないタグが出てきまして。
これを捨てるためにgulpからpugに変数を送っています。

```js
    .pipe(pug({
      locals: {
        playcanvas: true
      }
    }))
```

`playcanvas`という変数で`true/false`を取り、pugファイル内でifを使って区分けしていきます。

```js
- if(!playcanvas) {
	doctype html
	html(lang='ja')
- }
	- if(!playcanvas) {
		head(prefix='og: http://ogp.me/ns# fb: http://ogp.me/ns/fb# article: http://ogp.me/ns/article#')
			meta(charset='utf-8')
			title #{_title_str}
			meta(http-equiv='X-UA-Compatible', content='IE=edge')
			meta(name='description', content='_desc_str')
			meta(name='keywords', content='_keys_str')
			meta(name='viewport', content='width=device-width,user-scalable=yes')
			link(rel='stylesheet', href=DIR_CSS + 'master.css')
			block link
	- }
	- if(!playcanvas) {
		body
	- }
		.wrapper
			main.main
				.container
					block body
	- if(!playcanvas) {
		//- 全体共通のjs
		script(src= DIR_JS + 'libs.min.js')
		script(src= DIR_JS + 'functions.min.js')
		block script
	- }

```

###動いた！

そんなこんなで動きました！
更新したら以下の画像のファイルたちがplaycanvasのアセットとしてuploadされていました〜
![スクリーンショット 2019-06-07 16.02.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109168/935d25a8-0503-98c9-3f5f-af9f4dc63be8.png)
![スクリーンショット 2019-06-07 16.04.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109168/97bdbe39-ffaa-0742-7836-53ab9be9b1ca.png)

更新するたびにREST APIが叩かれるので連打することがなければ問題なく使えそうです。
ただ、PlayCanvasにはディレクトリを作成するREST APIが無いようで…
そのため、今回uploadするためのディレクトリ`/pc`にコンパイルされるファイルたちは階層を保持しないようにする必要があります。
また、uploadする先のPlayCanvasのディレクトリも階層を持たないroot直下のディレクトリに限られるそうです。

このREST APIを使ったやり方もパワープレイではありますが、おかげで開発が前より捗るようになりました。
もっと綺麗な書き方ややり方があるとは思いますが、とりあえずはこれ…

今回のこのプロジェクトファイルと使用したい人がいたらgithubにあげているので確認してみてください。
このまま使用しても多分sassやらpugやら消し忘れているコードがあったりしてエラーが出るので、自分なりに書き換えて使用するよう注意してください。
https://github.com/sutobu000/playcanvas-codingSet

---
####参考・引用
- PlayCanvas --- https://playcanvas.jp/
- gulp-playcanvas --- https://github.com/yushimatenjin/gulp-playcanvas
- PlayCanvas REST API --- https://developer.playcanvas.com/en/user-manual/api/