Codaybook

サーバサイドエンジニアが必死に作成した!発見した!を綴るブログ



UnityでジョイスティックのようなUIを作成したい!

Unityでモックを作成するときに仮想コントローラを作る機会がありました。仮想コントローラで少し大変なジョイスティックを実装した時にどのような感じで実装するかUIのみになりますが簡単に記述します。間違っている、もっといい方法があるかもしれないということを念頭に置いて参考にしてください( ´Ꙩωꙩ` )

以下画像のようにCanvas内でゲームオブジェクトを作成します。CanvasのImageのJostickFrameとButtonのJoystickButtonを追加します。

f:id:nisei275:20170720155450p:plain

JoystickButtonゲームオブジェクトにJoystick.csスクリプトEvent Triggerを追加します。Joystick.csの内容については以下に記述します。以下記述するスクリプトの今回の内容は、ゲームコントローラにあるようなジョイスティックのような挙動になるような制御になります。

using System.Collections;
using System.Collections.Generic;
using UnityEngine.Events;
using UnityEngine.EventSystems;
using UnityEngine;

public class Joystick : MonoBehaviour, IPointerUpHandler, IdragHandler {
    
    private GameObject joystickButtonObject;
    private Vector2 initialJoystickButtonPosition;
    
    void Start()
    {
        joystickButtonObject = this.gameObject;
        initialJoystickButtonPotision = joystickButtonObject.transform.position;
    }
    
    void Update()
    {
    }
    
    // ボタンをリリース
    public void OnPointerUp(PointerEventData ped)
    {
        joystickButtonObject.transform.position = initialJoystickButtonPosition;
    }
    
    // OnPointerDown実行後に移動した時
    public void OnDrag(PointerEventData ped)
    {
        // JoystickButtonが移動する前にtransformを取得
        Vector2 beforeMovePosition = joystickButtonObject.transform.position;
        
        // JoystickButtonゲームオブジェクトの移動
        joystickButtonObject.transform.position = new Vector2(
            joystickButtonObject.transform.position.x + ped.delta.x,
            joystickButtonObject.transform.position.y + ped.delta.y
        );
        
        // 初期位置からの移動距離を算出
        float moveX = joystickButtonObject.transform.position.x - initialJoystickButtonPosition.x;
        float moveY = joystickButtonObject.transform.position.y - initialJoystickButtonPosition.y;
        // 三平方の定理(a^2+b^2=c^2)で移動距離を算出
        float moveDistance = Mathf.Sqrt(Mathf.Pow(moveX, 2) + Mathf.Pow(moveY, 2));
        
        // 移動距離を超えている場合は移動前に戻す
        if (moveDistance >= MaxEnableDistance) {
            joystickButtonObject.transform.position = beforeMovePosition;
        }
    }
}

ゲームのジョイスティックはJoystickButtonの移動反映は円形になります。そこで、中心点から反映内の直線距離を算出するために、三平方の定理を使うことにします。
これを使用するとうまいことジョイスティックみたいになります( ´Ꙩωꙩ` )b

Jenkinsから対話式のシェルを実行

Jenkinsからあるサーバに置いてあるシェルスクリプトを実行しなくてはいけないことになりました。 実行するだけならジョブのシェル実行にssh $HOSTNAME $SHELL_PATHを記述すれば良いですが、 実行しなくてはいけないシェルは対話式になっていました(◞‸◟)
ということで、対話式のシェルを実行するためのシェルを作成してJenkinsからでも実行できるようにしました。

#!/bin/bash

SHELL_PATH="/usr/local/bin/deploy.sh"
TARGET_ENV="develop"
TARGET_BRANCH="master"

expect -c "
set timeout -1
spawn  $SHELL_PATH
expect -re \"Select Environment?.*\"
send   \"${TARGET_ENV}\n\"
expect -re \"Select Branch?.*\"
send   \"${TARGET_BRANCH}\n\"
expect eof
exit
"

対話式に対応させるためにexpectコマンドを使用して実現させました。細かく説明しますと、
expect -c "[コマンド群]"スクリプトを実行する前に、実行するコマンドを指示する。コマンドはシェルに壊されないようにクオートで囲むべきとされている。
set timeout -1タイムアウトにする時間を設定できる。無制限にしたい場合は-1を設定する
spawn [実行コマンド]:ここで実行するシェルを指定する。
expect -re "[対話の対象となる文字文字列]":対話の対象になる文字列を指定する。
send "[対象の応答]":対話に対する応答の文字列を指定する。
expect eof,exit:cronやjenkinsから実行する場合はexpect eofexitとする必要がある。

このシェルを使用すると対話式のシェルでもJenkinsから実行できるようになります。うむ、超絶便利!

Ruby on RailsにてRedisにマスタデータのModelをキャッシュ

とあるシュミレータをruby on rails上で作成することになり、ある処理を何万回、 何百万回と動作させなければいけなくなった。例えば以下の処理が本番環境で動作している。

def cards
  (1..1000).each do |index|
    index.to_s + ": " + Card.all[Random.rand(Card.all.size)].name
  end
end

1000回ほど、カードというマスタデータのnameカラムをランダムに1件ずつ取得している処理だが、

Completed 200 OK in 17243ms (Views: 29.3ms | ActiveRecord: 2451.8ms)

ぐらいの処理時間になってしまう。特にActiveRecord: 2451.8msは1回の処理でmysqlに アクセスしてしまうためにモデルデータをなんとかキャッシュできないかと考えなければいけない....!
というわけでruby on railsredisを導入し、モデルデータをキャッシュしてしまおうと対応にでた。

def cards
  (1..1000).each do |index|
    cache = Rails.cache.fetch("card_data", expires_in: 1.hour) do
      Card.all.to_a
    end
    index.to_s + ": " + cache[Random.rand(cache.size)].name
  end
end

card_dataというkeyのvalueにCardモデルの内容を全てキャッシュするように記述してみました。 (expires_inに関してはTTL時間で1時間だけキャッシングするという記述)

これで処理してみると

Completed 200 OK in 16713ms (Views: 94.6ms | ActiveRecord: 0.4ms)

という結果を得ることができました。なんとActiveRecord: 0.4msという速度になり、logを確認すると mysqlログもなくなりキャッシュからデータを取得できていることも確認できました。 (Views: 94.6msになってるのはなぜ・・)

ちなみに実際、どのようにデータがキャッシュされているかは以下コマンドで確認することができます。

$ redis-cli                   // redisコンソールの起動
127.0.0.1:6379> keys *        // キャッシュされているkey一覧
1) "card_data"
127.0.0.1:6379> get card_data // valueの取得

コマンドは大変!クライアントソフトないの!?っという人もいると思います。(自分のことです...) そういう方はRedis Desktop Managerを使用してみてはいかがでしょうか。 以下のような感じでredisを確認することができます。これは便利!

f:id:nisei275:20160809115244p:plain

Modelファイルにスキーマ情報を簡単記述「annotate」

業務でruby on railsに触れて半年目。いろいろな便利なgemに触れてきました。 ということで今回、あったら便利だなぁというgemを紹介いたします。
今回紹介するgemはannotateです。

github.com

導入の仕方はGemfileに以下1文を記述して$ bundle installいます。

gem "annotate"

annotateをインストールした後に以下コマンドを入力します。

$ bundle exec annotate

そうすると、Annotated (1): Playerみたいな文言がmodelの分出力され、 app/models/以下にあるmodelのファイルにスキーマ情報が以下のようにコメントとして追記されます。

# == Schema Information
#
# Table name: cards
#
#  id                :integer          not null, primary key
#  character_id      :integer          not null
#  name              :string(255)      not null

いちいちテーブルの情報を見なくても、このmodelにはこういうカラムが存在しているんだなぁというのが 一目瞭然になります。非常に便利なgemですので是非入れてみてください。

crystal-langのファイルをAtomで開く準備

最近、crystal-langに触れています。rubyのように書けて実行速度が爆速なのはありがたいですね。
さて、crystal-langAtomエディタで主にプログラミングしているのですが、カラーシンタックスPlain Textを選ばれてわざわざrubyに変更するのが面倒くさいと思いました。crという拡張子rubyのカラーシンタックスになるように以下パッケージを導入しました。

github.com

設定はメニューバーから[Atom]->[Open Your Config]を選択して以下のように追記をします。

"*":
  "file-types":
    cr: "source.ruby"
    tx: "text.html.basic"

この設定により、crrubyのカラーシンタックスになります。(ついでににtxhtmlに設定しています。)
Atomはバージョン1.5.3に書いた記事です。

Unity5.3にしたときのシーン切り替えで暗くなる

Unity5.3がリリースしてすんなり動くかと思った第2弾。今度はSceneManager.LoadSceneでシーン切り替えを実施した際に、急に画面が暗くなります。 それを解決するためには、以下手順を踏んで設定を変更する。

  1. メニューバーからWindow=>Lightingを選択し、Lightmapsを3つのタブから選択する。
  2. Autoのチェックを外して、Buildをクリックする。

この手順を踏むとSceneManager.LoadSceneした際に画面が暗くなりません。情報を提供していただいたクライアントエンジニアさんに感謝。

Unity5.3にしたときのシーンの切り替えで警告文

12月上旬にUnity5.3がリリースされました。新しい2DツールJSONの公式サポート、カスタムコルーチン等、魅力的な機能が 多く追加されたのですぐにアップデートしました。5.2から5.3のアップデートなのですんなり動くだろう(根拠なし)と思っていましたが、 以下エラーが発生しました。

"UnityEngine.Application.LoadLevel(int)' is obsolete: `Use SceneManager.LoadScene'".

シーンファイルの切り替えはApplication.LoadLevel([シーンナンバー])で実施していましたが、なんとこれはもう時代遅れだ!SceneManager.LoadSceneを使えや!というUnityの警告が出力されました。ということで以下のように書き換え。

using UnityEngine.SceneManager;
// Application.LoadLevel([シーンIDまたはシーン名]);
SceneManager.LoadScene([シーンIDまたはシーン名]);

これでUnityに怒られなくなります。