i++

プログラム系のメモ書きなど

golang : コミット時に gofmt を実行する(pre-commit hook)

misc/git/pre-commit - The Go Programming Language を参考に作成しました。

オリジナルではフォーマットされていないコードがある場合にコミットを中断していますが、以下のスクリプトでは gofmt を実行して git add するところまでやっています。

#!/bin/sh

gofiles=$(git diff --cached --name-only --diff-filter=ACM | grep '.go$')
[ -z "$gofiles" ] && exit 0

unformatted=$(gofmt -l $gofiles)
[ -z "$unformatted" ] && exit 0

echo >&2 "Info: Running gofmt as some Go files are not formatted with gofmt."
for fn in $unformatted; do
    gofmt.exe -s -w $fn
    echo >&2 "gofmt -s -w $fn"
    git add $fn
done

exit 0

リンク先は、gofmt に加えて、gocyclo によるCyclomatic complexity のチェック(見るだけ)と goimports の実行も合わせたものになります。

github.com

Google App Engine : appcfg.py update 時の HTTP Error 403: Forbidden Unexpected HTTP status 403.

エラーの全文は以下の通り。

久々に GAE のプロジェクトを作成してデプロイしようと思ったら発生しました。

python appcfg.py -A YOUR_APP_ID -V v1 update src/
06:44 PM Application: YOUR_APP_ID (was: None); version: v1 (was: None)
06:44 PM Host: appengine.google.com
06:44 PM Starting update of app: YOUR_APP_ID, version: v1
06:44 PM Getting current resource limits.
2016-04-23 18:44:08,184 ERROR appcfg.py:2402 An error occurred processing file '': HTTP Error 403: Forbidden Unexpected HTTP status 403. Aborting.
Error 403: --- begin server output ---
You do not have permission to modify this app (app_id=u's~YOUR_APP_ID').
--- end server output ---

python - Google App Engine app deployment - Stack Overflow に書かれている通り、%ユーザディレクトリ%/.appcfg_oauth2_tokens を削除してから再実行することで、ブラウザで認証画面が開いてアップロードできました。

golang : URL短縮サービス goo.gl を使う

URL Shortener API の使い方はこちら → URL Shortener

ここでは API Key を使った方法を載せています。

type responseParam struct {
    Kind    string `json:"kind"`
    Id      string `json:"id"`
    LongUrl string `json:"longUrl"`
}

func GetShortenedUrlImpl(longUrl string) (string, error) {

    values := url.Values{}
    values.Add("key", GoogleApiKey) // パッケージの変数として定義し、この関数を呼ぶ前にセットしておきます。

    url := "https://www.googleapis.com/urlshortener/v1/url" + "?" + values.Encode()

    param := fmt.Sprintf(`{"longUrl":"%s"}`, longUrl);
    req, err := http.NewRequest("POST", url, bytes.NewBuffer([]byte(param)))
    if err != nil {
        return "", err
    }
    req.Header.Set("Content-Type", "application/json")

    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        return "", err
    }
    defer resp.Body.Close()
    respBody, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        return "", err
    }
    var respParam responseParam
    err = json.Unmarshal(respBody, &respParam)
    if err != nil {
        return "", err
    }
    return respParam.Id, nil
}

golang : 日本語(マルチバイト)文字を含む string の substring

go では string のスライスは byte として扱われるので、日本語のようにマルチバイト文字を含んだ文字列 str に対して str[start:start+length] のようなことをすると、思わぬ場所が切り取られてしまいます。

この問題を回避するために、一旦 rune にキャストしてから string に戻します。

func substring(str string, start, length int) string {
    if start < 0 || length <= 0 {
        // 明らかに不正な値なので、チェックせずに進んで panic を起こしても良さそうです。
        return str
    }
    r := []rune(str)
    if start + length > len(r) {
        return string(r[start:])
    } else {
        return string(r[start:start + length])
    }
}

TypeScript : typings による Chrome Extension 用型定義のインストール

typings で型定義ファイルの管理を行うので、未インストールの場合はインストールします。

npm install -g typings

拡張機能のプロジェクトのディレクトリに移動し、typings init コマンドで初期化します(typings.json ファイルが作成されます)。 その後、install コマンドで Chrome Extension 用の 型定義ファイル(.d.ts)をインストールします。

typings install chrome --ambient --save
typings install filesystem --ambient --save
typings install filewriter --ambient --save
typings install webrtc/mediastream --ambient --save
typings install es6-shim --ambient --save

chromeChrome Extension 用の型定義の本体ですが、これをインストールするだけ使えず、chrome が依存している他の型定義もインストールする必要があります。 typings でインストールしたときに、Stripped reference というメッセージが表示された場合、それらを手動でインストールする、と覚えておけば良さそうです。

> typings install chrome --ambient --save
typings INFO reference Stripped reference "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/7de6c3dd94feaeb21f20054b9f30d5dabc5efabd/webrtc/MediaStream.d.ts" during installation from "chrome"
typings INFO reference Stripped reference "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/7de6c3dd94feaeb21f20054b9f30d5dabc5efabd/filesystem/filesystem.d.ts" during installation from "chrome"
chrome
└── (No dependencies)

--ambient って何者?など typings についてより詳しく知りたい場合は TypeScriptの型定義管理ツールTypingsについて - Qiita が参考になります。

Git : コミットメッセージに自動でブランチ名を挿入する

参考:How to add Git's branch name to the commit message?

  1. .git/hooks/ 下にある prepare-commit-msg.sample を prepare-commit-msg に変更する(.sample を外す)
  2. prepare-commit-msg に以下のようなスクリプトを追記する
branchPath=$(git symbolic-ref -q HEAD) # branchPath は refs/heads/feature/XXXX_YYYY のような文字列に
branchName=${branchPath##*/} # 最後の / 以下を取得し、branchName は XXXX_YYYY のような文字列に
issueNumber=$(echo $branchName | cut -d "_" -f 1) # "_" を delimiter として cut し、issueNumber は XXXX に
firstLine=$(head -n1 $1)

if [ -z "$firstLine"  ] ;then # 最初の行が空行かチェックして、amend でないことを確認
    sed -i "1s/^/($issueNumber) \n/" $1 # コミットメッセージの先頭に (XXXX) という文字列でブランチの情報を追加。
fi

参考先に少し修正を加え、branchName を直接使うのではなく、cut を使ってその一部を使うようにしています。 例えば feature/XXXX_YYYY というブランチ名だった場合、コミットメッセージには (XXXX) という形でブランチ名の一部が入るようになります。

これは、自分は XXXX の部分にイシューやタスクの番号を、YYYY の部分に少し説明的な文章を入れる形でブランチ名を作っているためです。

Unreal Engine : Timeline を C++ コードで作成・実行する

手順

準備
  1. Timeline で使用する Curve を「Content Browser を右クリック」→「Miscellaneous」→「Curve」(もしくは「Float Curve」)で作成する
  2. 作成した Curve を右クリックして「Copy Reference」する
  3. C++ のコード上で ConstructorHelpers::FObjectFinder を使い、先ほど作成した Curve を取得する
  4. FTimeline オブジェクトを作成する
  5. FTimeline オブジェクトに追加する FOnTimeline* (FOnTimelineVector や FOnTimelineFloat など Curve の種類による)を作成する
  6. FOnTimeline* に、Timeline が更新される毎に呼び出す関数を設定する
  7. FTimeline に、Curve と FOnTimeline* を追加する

Timeline 完了時のイベントを追加する場合のために SetTimelineFinishedFunc などもあるので、適宜使用する。

実行
  1. FTimeLine の PlayFromStart 関数を呼ぶ
    • 途中から再生する場合は Play、逆再生する場合は Reverse など再生開始方法は任意
  2. Tick で FTimeline の IsPlaying() が真であれば、TickTimeline(DeltaTime) を呼ぶ
    • 更新頻度が低くて良い場合は Tick ではなく TickTimeline を呼ぶだけの関数を設定した Timer を作成したほうが負荷が軽くなって良いかも

サンプルコード

void MyActor::MyActor()
{
    // 他の初期化処理...

    // メンバー変数として FTimeline* MyTimeline を定義している
    MyTimeline = new FTimeline();
    MyTimeline->SetTimelineLength(1.0f);

    // 作成した Timeline の取得。Copy Reference でコピーしたテキストを TEXT の中に貼り付ける。
    // <> の中身は Curve の種類によって変更する。今回は Vector。 
    const ConstructorHelpers::FObjectFinder<UCurveVector> StepCurve(TEXT("CurveVector'/Game/Timeline/TimelineCurve.TimelineCurve'"));

    // Timeline 更新時に呼ばれる関数の設定。このクラスに定義している void TimelineStep(FVector v) を呼ぶ。
    FOnTimelineVector MyTimelineStepFunction;
    MyTimelineStepFunction.BindUFunction(this, "TimelineStep");
    MyTimeline->AddInterpVector(StepCurve.Object, MyTimelineStepFunction);

    // Timeline 終了時に呼ばれる関数の設定。このクラスに定義している void TimelineFinished(FVector v) を呼ぶ。
    FOnTimelineEvent MyTimelineFinishedFunc;
    MyTimelineFinishedFunc.BindUFunction(this, "TimelineFinished");
    MyTimeline->SetTimelineFinishedFunc(MyTimelineFinishedFunc);
}

void MyActor::BeginPlay()
{
    if (MyTimeline != nullptr) {
        MyTimeline->PlayFromStart();
    }
}

void MyActor::Tick( float DeltaTime )
{
    Super::Tick( DeltaTime );

    // Timeline 再生中であれば DeltaTime 進めて実行
    if (MyTimeline != nullptr && MyTimeline->IsPlaying())
    {
        MyTimeline->TickTimeline(DeltaTime);
    }
}

// TickTimeline 毎に、進んだ Time の合計と対応した Timeline から得られる値でこの関数が呼ばれる
// .h で定義する際に UFUNCTION() を付けておくこと
void MyActor::TimelineStep(FVector value)
{
    UE_LOG(LogTemp, Warning, TEXT("TimelineStep : (%.2f, %.2f, %.2f)"), value.X, value.Y, value.Z);
}

// Timeline 完了時に呼ばれる関数
// .h で定義する際に UFUNCTION() を付けておくこと
void MyActor::TimelineFinished()
{
    UE_LOG(LogTemp, Warning, TEXT("TimelineStep Finished"));
}

参考


Blueprint も良くできていると思いますが、メインは C++ で書いたほうが楽そうです。

Unreal Engine 4で極めるゲーム開発:サンプルデータと動画で学ぶUE4ゲーム制作プロジェクト

Unreal Engine 4で極めるゲーム開発:サンプルデータと動画で学ぶUE4ゲーム制作プロジェクト