VSCode のExtensionを開発してみる

こんにちは。
Visual Studio Code、使っていますか?
ぼくはこのエディタが大好きです。

今回は、このエディタの Extension(=プラグイン)の開発をしていきたいと思います。

※ node.js が必要です!

開発方法

  • TypeScript + Node.js でいけます。
  • ジェネレータは Yeoman でできています。

手順1: Yeomanを入れる。

npm install -g yo

また、VSCode Extension generatorも入れます。

npm install -g generator-code

手順2: ジェネレータを実行

> yo code
? ==========================================================================
We're constantly looking for ways to make yo better!
May we anonymously report usage statistics to improve the tool over time?
More info: https://github.com/yeoman/insight & http://yeoman.io
========================================================================== Yes

     _-----_
    |       |    .--------------------------.
    |--(o)--|    |   Welcome to the Visual  |
   `---------´   |   Studio Code Extension  |
    ( _´U`_ )    |        generator!        |
    /___A___\    '--------------------------'
     |  ~  |
   __'.___.'__
 ´   `  |° ´ Y `

? What type of extension do you want to create? New Extension (TypeScript)
? What's the name of your extension? first-ext
? What's the identifier of your extension? first-ext
? What's the description of your extension? first-ext
? What's your publisher name? xxx
? Initialize a git repository? No
   create first-ext\.vscode\launch.json
   create first-ext\.vscode\settings.json
   create first-ext\.vscode\tasks.json
   create first-ext\typings\node.d.ts
   create first-ext\typings\vscode-typings.d.ts
   create first-ext\test\extension.test.ts
   create first-ext\test\index.ts
   create first-ext\.vscodeignore
   create first-ext\.gitignore
   create first-ext\README.md
   create first-ext\vsc-extension-quickstart.md
   create first-ext\tsconfig.json
   create first-ext\src\extension.ts
   create first-ext\package.json


I'm all done. Running npm install for you to install the required dependencies. If this fails, try running the command yourself.

Extension の形式は、TypeScript がおすすめです。
開発するとき、インテリセンス君が強力に働いてくれて便利だからです。

その後、名前、識別子、説明、開発者名、Git と聞かれます。
今回は、Git の設定は n にして置きました。

ずらーとモジュールがインストールされます。

手順3: Extension を実行

> cd first-ext
> code .

code . がうまくいかない時は、
first-ext フォルダを VSCode で開いてください。

ジェネレータを実行した状態で、
Hello world が試せるようになっているので、
作った Extension を実行してみましょう。

左側のデバッグボタン(虫)をクリック、緑色の再生ボタンをクリックします。
(ちなみに F5 キーでもいけますー
すると、デバッグ用の VSCode がもう一つ起動します。

vscode-ext-debug

この状態で、Extension をテスト(=実行)できるわけです。

デバッグ用に開いた VSCode で、Ctrl + Shift + P をしてコマンドパレットを開きます。
Hello world と入力しEnter キーを押してみましょう。

vscode-ext-command

Hello world というメッセージが表示されます。

vscode-ext-info-message

この動きは、すべて今作った Extension で動いています。

では、どういう仕組みで動いているのか見てみましょう。

手順4: 動く仕組みは・・?

vscode-ext-nagare
↑ ざっくりとした流れの画像

いまデバッグしていたウインドウは閉じて、
先ほどのプラグインを開いたウインドウに切り替えましょう。

まず、コマンドパレットで「Hello World」と入力しましたね。
これはpackage.json の、contributes.commands から取得されています。

vscode-ext-package.json

ここでは、コマンドパレットでHello Worldが実行されたら、
extension.sayHelloを実行すると書いてあります。

では、この extension.sayHello はどこにあるのでしょう。
src/extension.ts を開いてみましょう。

vscode-ext-extension.ts

activate 関数の中に、
vscode.commands.registerCommand(...)を呼び出している行があります。(17行目)
ここでは、extension.sayHello コマンドが呼び出されたら、
第二引数のコールバック関数を実行する、となっています。

コールバック関数には、
vscode.window.showInformationMessage('Hello World!')
と書かれています。
これは、指定された文字列を先ほどのようなメッセージで表示する関数です。

手順5: 少し変えてみる

extension.ts を、少し変更してみましょう。

let disposable = vscode.commands.registerCommand('extension.sayHello', () => {
  // The code you place here will be executed every time your command is executed

  // Display a message box to the user
  vscode.window.showErrorMessage('Hello Error Message!');
});

似た関数で、showErrorMessage という物があります。
これは、エラーを表示する関数です。

実行してみましょう。手順は先程と同じです。
(F5キーでもいけるよ)

同じように、Ctrl+Shift+P から Hello World を選ぶと、

vscode-ext-show-err-msg

エラーメッセージ出現!

まとめ

このように、Extension は、
TypeScript + Node.js で開発できます。

また、公式サイトにも英語ですが、
Extension の作成方法が載っているので、
見てみてください。

TypeScriptのES6 Moduleでexport varしたものに書き込む

TypeScriptで、次のようにしたい時があります。

export var field1 = "foo";
import {field1} from "./export";
// エラーになる
field1 = "bar";

しかし、エラーになります。

Invalid left-hand side of assignment expression.

方法1:namespace で囲む

これは、変数を namespace(旧 module )で
囲ってあげることで解決できます。

export namespace fields {
  export var field1 = "foo";
}
import {fields} from "./export";
fields.field1 = "bar";

前は、namespace を、module と定義していましたが、
ES6 module と混同してしまうので、
これからは、namespace を使います。

方法2:setter となる関数を使う

var field1 = "foo";
export function setField1(text:string) {
  field1 = text;
}
import {setField1} from "./export";
setField1("bar");

まとめ

仕様がムズカシイ。
どうやら export default var とかもエラーになるから、
直接フィールドを export するのは良くないのかもしれないです。

AzureでGithubからデプロイするとき、jsonやwoffなどを読み込む

こんにちはー!
Azure の App Service では、Github から、
直接デプロイすることができて便利です。

Githubとの連携は、下のサイトが便利です。

Azure と GitHub で継続的インテグレーション
以前に Azure と Visual Studio Online で継続的インテグレーションというエントリを書…

Json を読み込められるようにする

Web アプリケーションで、JSON が使われていることは多いと思います。
しかし、初期状態では、JSON をレスポンスとして返すことはできないため、
ajax とかができなくなってしまいます。
これは、App Services で使っている IIS で、
Json の MIME が読み込まれていないためです。

Git のリポジトリのルートディレクトリに、
web.config というファイルを作って、次のようにします。

<?xml version="1.0"?>
<configuration>
  <system.webServer>
    <staticContent>
      <mimeMap fileExtension=".json" mimeType="application/json" />
    </staticContent>
  </system.webServer>
</configuration>

これでコミット&プッシュすると、 json ファイルが読み込まれるようになりました。

Woff、Woff2 形式のフォントを読み込む

上の staticContent タグ内に、次のように追加します。

<mimeMap fileExtension=".woff" mimeType="application/x-font-woff" />
<mimeMap fileExtension=".woff2" mimeType="font/x-woff2" />

他の形式を追加したい場合、次のようにします。

<mimeMap fileExtension=".拡張子" mimeType="MIME タイプ" />

これで woff、woff2 形式のフォントも読み込められるようになりました。

Gitで非公開のリポジトリが使いたかったらBitbucket!!

こんにちはー!!

Gitいいですよね~~!!
バックアップもしっかり取れるし、間違えてもすぐに戻せます。

ぼくはリモートでGithubを使っていましたが、
非公開のリポジトリが使いたくなりました。

Githubで非公開のリポジトリを作るには、
お金を払うか、学生認証するかしかありません。

ぼくの場合、「学生」という文字に一文字余計についていて、
学生なのか学生じゃないのかよくわからないし、
認証とかたぶんできないし、
審査英語だし・・

というわけで、Bitbucketを使うことにしました。

https://bitbucket.org
bitbucket
画像はBitbucket 公式サイトから

https://bitbucket.org/account/signup/で、
アカウントを作成します。

bitbucket-private

上のメニューから、「リポジトリ」、「リポジトリの作成」を選択し、
Repository name に適当な名前を指定、
アクセスレベルを非公開にして、リポジトリタイプは Git のままで
リポジトリを作成します。

これで外からアクセスしても見えないリポジトリが作成できました。

しかも日本語というのもいいです!!

UnityのSceneManagerで、シーンが存在するか確認する

Unity 5.3 から追加された、Multi Scene Editingで、
シーンを追加する時、
同じシーンを何個も追加したくない!ってこと、ありますよね。

「SceneManager.Contains()」的なメソッドがあれば最高なのですが、
アクティブなシーンしか
取れなかったりとかするんですよねー(GetActiveScene 関数)。

そこで、次のようにしました。
しかし、Unity 5.3.1 くらいではOKだったのですが、
5.3.2にしたら非推奨になっちゃってw・・

using UnityEngine.SceneManagement;
using System.Linq;

void LoadMyScene()
{
    // MyScene1 というシーンが存在しなければ
    if (SceneManager.GetAllScenes().Count((scene) => scene.name == "MyScene1") == 0)
    {
        SceneManager.LoadScene("MyScene1", LoadSceneMode.Additive);
    }
}

Linqで、すべてのシーンの中から、指定された名前のシーンが 1 以上の場合は存在するとしました。
しかし、すべてのシーンを取得する、「GetAllScenes 関数」が非推奨になってしまいました。
1行で書けるのがよかったのでしたが、しょうがない。for文をつかって地道にやろう。

using UnityEngine.SceneManagement;

void LoadMyScene()
{
    if (!ContainsScene("MyScene1"))
    {
        SceneManager.LoadScene("MyScene1", LoadSceneMode.Additive);
    }
}

bool ContainsScene(string sceneName)
{
    for (int i = 0; i < SceneManager.sceneCount; i++)
    {
        if (SceneManager.GetSceneAt(i).name == sceneName)
        {
            return true;
        }
    }
    return false;
}

「using UnityEngine.SceneManagement」を追加するのを忘れずに。
これでハマったこともあります。

もっといい方法ないんですかねー・・。

【感動】UnityでC# 6.0 が使えた!!

追記:朗報
Unity 2017 からは、デフォルトで C# 6.0 および .NET 4.6 が使用可能なようです!

C#6.0時代のUnity - Qiita
## はじめにUnity2017のベータが公開されましたね!(これを書いている4月13日現在 Unity 2017.1.0b1 https://unity3d.com/jp/unity/beta#downloads)なんといっても...

この記事では Unity 5 での場合を紹介しています。

こんにちはー!!

Unityでも、最新の C# 6.0 を使って、
async、await とかカッコよくコードを書きたいんだ ლ(´ڡ`ლ)

そんなことできないかなーと思って、調べてみました。

そしたら、なんと、ありました!!ので紹介します。

1. リポジトリをダウンロード

C# 6.0関係のいろいろが入ったリポジトリをBitbucketからダウンロードします。

https://bitbucket.org/alexzzzz/unity-c-5.0-and-6.0-integration/downloads

ダウンロードしたファイルは、Zip形式で圧縮されているので、展開します。

2. プロジェクトにインポート

Unityのプロジェクトのフォルダーを開き、
「CSharp60Support」をコピーします。

unity-cs6-copy

CSharp60Support内の、「CSharp60Support for Unity 5.unitypackage」
(Unity 4だったらUnity 4の方)を開き、
プロジェクトにインポートします。
import-cs6-unity

unity-cs6-many-errs
大量にエラーが出ますが、気にせず、
「CSharp60Support」ディレクトリを右クリックし、「Reimport All」をクリック。
unity-cs6-reimport
ダイアログが出てくるので、「Reimport」を選択すると、Unityが再起動し、エラーがすべて消えます。

3. 試す

新しいスクリプトを適当に作って、C# 6.0を試してみましょう。
試す機能は、String interpolationです。


// 略

void Start()
{
    string s1 = "world";
    string s2 = $"Hello, {s1}";
    Debug.Log(s2);
}

// 略

新しく空のゲームオブジェクト(Hierarchy右クリック「Create Empty」とか)を作成し、
作ったスクリプトをアタッチしましょう。

実行すると・・・・!
unity-cs6-success

できました~~!!

4. Visual Studio、MonoBehaviourでエラーが出るときは

Unityではエラーが出なくても、
「◯◯という機能はC# 6.0で使えて、C# 4では使えないよー」的なエラーが、
Visual Studio、MonoBehaviour側で出るときは、IDEを再起動しましょう。

まとめ

これでのびのびとUnityが使える!

自分的には、C# 6.0で一番便利な機能は、↑の、$"{変数}"で、string.Formatっぽいのが使える機能です(名前忘れた -> 追記: String interpolation らしい)
これが使えるのは本当に嬉しい!

(え、async 、await 使ってみないの)→試してみます!

UnityのLoadSceneAsyncでの非同期処理

こんにちはー!!

Unity 5.3 から、Multi Scene Editing が追加されましたね。

そこで、シーンを読み込んでから、GameObject.Find をする、
次のようなコードを動かしたのですが、うまくいきません。

TestGameObject は、追加するシーン Scene2 に入っているものとします。

void LoadScene2()
{
    SceneManager.LoadScene("Scene2", LoadSceneMode.Additive);
    Debug.Log(GameObject.Find("TestGameObject"));
}

実行してみると、Null が返ってきます。

unity-loadsceneasync-ok

なぜ?

これは、Scene2 が読み込まれるまで待たないで、
GameObject.Find を呼び出してしまっているからです!

LoadSceneAsync を使おう!

お、LoadSceneAsync とかいう、それらしい名前のがあるではないか。
そこで、LoadSceneAsync を使おうとしましたが、ハマりました。
C# の、async / await でするものだと間違えていて、
あれ?Unityの C# のバージョンじゃ、async とか使えなくねー・・

(使う方法もあります)

【感動】UnityでC# 6.0 が使えた!!
追記:朗報Unity 2017 からは、デフォルトで C# 6.0 および .NET 4.6 が使用可能なようです!この記事では Unity 5 での場合を紹介しています。こんにちはー!!Unityで...

しかし、LoadSceneAsync の戻り値は、「UnityEngine.AsyncOperation」でした・・。
アシンクオペレーションってなんだっけ?(検索中)
そう、コルーチンを使うんだ。(`・ω・´)ゞ

void LoadScene2()
{
    StartCoroutine("LoadScene2Coroutine");
}
IEnumerator LoadScene2Coroutine()
{
    yield return SceneManager.LoadSceneAsync("Scene2", LoadSceneMode.Additive);
    Debug.Log(GameObject.Find("TestGameObject"));
}

unity-loadscene-null

これでシーンが読み込まれるまで待機してくれるようになりました。

Cities: Skylines がセールだったから買ってみた

やっとクリスマスプレゼントが買えました!!
Cities: Skylines です。

Steam で、旧正月セールが行われています。
Cities: Skylinesも、60%Off で売られていたので、買いました。

なぜクリスマスプレゼントがこんなに遅いのかというと、
クリスマスに、何を買うか迷っていて、やっと「よし、買おう!」と決心したところ、
なんと、セールが終わってしまいました・・。

そのあと、価格が再び下がるのを待っていたら、ついにきました!!

cities-skylines-sale
これはポチるしかない。

というわけで、早速プレイしてみました。

重い!!

僕の環境では、初期設定では非常に重い状態でした。

とりあえずよくわからないので、適当に全部「Disabled」「Low」にしておきます。
2016-02-06_12h24_24
これでサクサク動きます!
Display Modeは、フルスクリーンより、ウィンドウで表示したほうが便利だよね、ってことで設定しました。

日本語化

いきなり、「contaminated」とかの単語が出てきても、よくわからないので、
ここは日本語に頼りましょう。
http://citiesskylines.wiki.fc2.com/wiki/%E6%97%A5%E6%9C%AC%E8%AA%9E%E5%8C%96を参考に、日本語にします。

マップの操作

  • カメラの移動 : WASDキー。Simcityと違うなあ。
  • 拡大・縮小 : マウスホイール。これはSimcityと同じ。
  • 回転 : マウスホイールをクリックするやつ。これもSimcityと同じ。

まとめ

まだ全然始めたばかりですけど、Simcityと同じくらい面白そうです!
セールは 2016年2月13日(UTCかな)までなので、買いたい人はお早めにどうぞ。

TypeScriptの複雑な型

こんにちはー!!
TypeScriptには、名前の通り、「型」があります。
その中で、複雑な型もあるので、紹介したいと思います。

配列

2つの定義の方法があります。

var numArray: number[] = [0, 1, 3, 5, 10, 100, 42];
var numArray2: Array<number> = [0, 42];

// これも上と同じ
console.log(typeof numArray); // object
console.log(numArray instanceof Array); // true

連想配列

知らなかった!いままでは Object 型とか、any とか使っていました・・

var hash1: {[key: string]: string} = { "a1": "123", "b2": "def" };
var hash2: {[key: number]: HTMLElement} = { 1: document.getElementById("elem"), 42: document.getElementById("42") };

構文は、次のようになります。

{[key: インデックスの型]: 値の型}

応用: 2次元配列

次のような配列は、どんな型にしたらよいでしょう。

[["apple", "banana", "tomato"], ["fire", "water", "tree"]]

文字列の2次元配列です。
まず配列の中に配列があって、その中に文字列ということなので、次のように書きます。

var ar: Array<Array<string>>;

こんな書き方もできます。

var ar2: string[][];

応用: 配列内の連想配列

では、次のような場合はどうでしょう。

[{"name": "taro", "like": "apple"}, {"name": "hoge", "like": "pc"}]

配列の中に連想配列があるので、こんなのはどうでしょう。

var list = Array<{[key: string]: string}>;

こんな書き方もできます。

var list2 = {[key: string]: string}[];

型の別名を指定する

いちいち、{[key: string]: string} とか、入力するのきついので、
別名をつけちゃいましょー!!

type type1 = {[key: string]: string}; // 別名を指定
var list: type1 = { "first": "abc", "second": "def" };

type 型の別名 = もととなる型 で定義できます!

まとめ

TypeScriptには、numberとかの簡単な型もあるけれど、
連想配列とかは、Objectとするのではなく、インデックス、値の型まで指定できる!

さっきから「型」が「方」って変換されて困る!!