ダリアメモ@ブログ

日々思ったことや自分用のメモも含めプログラムの技術を淡々と書いていきます

【GAS】ウェブアプリーケーションとして使う場合にパラメーターを渡す方法

概要

よくWebのAPIを呼び出すときにパラメーターに応じて取得する内容を変えるのをGASでしたかったので、調べたことを忘備録の意味合いも含めて記事を書きます。

スクリプト準備

GASでHTTP通信が行われた際に、do◯◯系のコールバックが呼ばれます。

  • GETの場合はdoGet()、POSTの場合はdoPOST()

このdo◯◯コールバックには引数を入れることができ、この引数のparameterというプロパティの中に渡されたパラメータが格納されてます。

function doGet(e){
//e.parameterに渡されたパラメーターが入る
}

例としてuserNameという自身の名前を入れてもらうパラメーターを使用したい場合は、以下のようにすることで使用できます。

function doGet(e){
    var userName = e.parameter.userName;
   //以降適当な処理 
}

次に先程追加したパラメーターをURLに追記して渡せるようにします。

URL準備

まずはURLが必要になるので、GASをウェブアプリケーションとして実行できるようにします。
上のメニューから「公開」→「ウェブアプリケーションとして導入」を押すと設定のダイアログが出るので、よしなに設定します。

  • Project Version : 1.0
  • Execute the App as : Me
  • Who has access to the app : Only myself

※注意点として既にウェブアプリケーションとして公開している場合は、プロジェクトのバージョン情報を新しくしないと反映されないので、コンボボックスからNewを選択してバージョンを新しくします。

設定が終わったらOK、もしくは更新を押すとURLが発行されるので、このURLにパラメーターを追加して渡すようにします。
先程のuserNameを渡したい場合は、以下のようになります。

発行されたURL?userName=入れたい名前

後はこのURLをブラウザなりcurlなりで開いて、入れた名前がうまく実行できていれば成功です。

今回は単体でしたが、複数のパラメーターを入れたい場合は&で繋げるようにします。

発行されたURL?パラメーター名1=パラメーター1&パラメーター名2=パラメーター2

参考

GASでURLを踏んでスクリプトを動作させたときにパラメータを渡す方法

GASでGetパラメータを受け取ってスプレッドシートに書き込む方法

【UE4】DataTableに対してFill Data Table From JSON Stringをすると謎のメッセージが出る

現象

HTTP通信を用いてスプレッドシートjsonに変換して、そのjsonを「Fill Data Table From JSON String」を使ってDataTableアセットに保存しようとしました。 すると以下のメッセージが出て保存されずに処理が終了しました。

f:id:daria_sieben:20200120003122p:plain

データのフォーマットが違うのかエクスポートして確認してみましたが、フォーマットも同じだったので途方に暮れていましたが、メッセージの内容でぐぐったら暫定的にですが解決方法がわかったので、とりあえず解決したい人の参考になれば…と思い記事を書きました。

状況

環境は4.24.1です。
データの構造は以下のとおりです。

[
  {
    "Name": "hoge",
    "Description": "ほげ"
  },
  {
    "Name": "hogehoge",
    "Description": "ほげほげ"
  },
]
  • 確認方法

確認しやすいようにボタンを押したら「Fill Data Table From JSON String」をするように「Editor Utilities Widget」でBPを作ります。 f:id:daria_sieben:20200120011217p:plain

が、上記のメッセージが出てデータはうまく保存されずに終了します。

解決方法

出力したいDataTableアセットを右クリックして、以下の様にします。

  1. 出力対象のDataTableをJSONとしてエクスポートします。
  2. 1でエクスポートしたJSONを出力対象のDataTableにインポートします。

f:id:daria_sieben:20200120011912p:plain

これで再び上記BPで実行するとちゃんと保存されるようになりました。

注意点

上記の方法で解決したと思ったのですが、エディタを立ち上げ直すと再び上記のメッセージが出るようになるので、再度インポートしないといけません…。
これやれば解決するよなどがあればご教授いただけると幸いです。

参考

ExcelからのCSVのインポートに関する問題

GroupByの第2引数を使って、Selectで射影したインデックス付き配列を射影前の型だけ参照できるようにする

概要

以下の様に配列のインデックスによってGroupByでグループ分けしたいときがありました。 よくありそうな手法としてSelectでインデックス付き配列に変換してからグループ化するとします。

numbers = new int[]{0,1,2,3,4,5}; //数は適当です
gridNumbers = numbers
.Select((number,index) => new {Number = number, Index = index})
.GroupBy(numbers => numbers.Index / 2); //仮として2で割った値の数でグループを作ります
foreach(var gridNumber in gridNumbers)
{
    Consol.WriteLine(gridNumber);
} 
0
0
0
1
1
1

GroupByで分けてみたものの、グループ分けのためだけに作ったIndexをこれ以降使わないとしたら、インデックスだけなくして元に戻したくなりますよね。 そんなときに、GroupByの第2引数を使えばもとに戻せることを知ったので、上記のコードを改善してみようと思います。

改善

先程のGroupByのところを以下のように直してみます。

.GroupBy(numbers => numbers.Index / 2, numbers => numbers.number);  //仮として2で割った値の数でグループを作って、第2引数で入れたnumberだけを抽出する

こうすることでインデックスは今後インテリジェンスで参照できなくなったので、スッキリしました。 小ネタ程度の記事ですが、ぐぐっても出てこなかったのでまとめてみました。

Hierarchyのコンテキストメニューから自作のゲームオブジェクトを生成する方法

やりたいこと

こんな感じに自作のゲームオブジェクトをHierarchyのコンテキストメニューから生成できるように作りたかったのでそれについてまとめます。 f:id:daria_sieben:20190817171459p:plain

環境はUnity2018.4.4f1です

実装

必要な処理はMenuItemです。

 [MenuItem("GameObject/TestGameObject", false, 0)]

・第1引数はHierarchyのコンテキストメニューに表示したいので「GameObject/」にし、表示したい名前をその後ろに入れます。
・第2引数は押せないようにチェックを入れるかのフラグですが、常に選択できるようにしたいのでtrueにします。
・第3引数は表示したい場所を指定します。0が一番上になり、Create Emptyが一番前に来るのでそれに下になります。 今回はわかりやすく一番上にします。

今回は生成するのが目的のため、他の処理はUndoだけできるようにしておけば元に戻せるのでそうします。

public class TestGameObjectMenuItem
{
    [MenuItem("GameObject/TestGameObject", false, 0)]
    public static void CreateGameObject()
    {
        var go = new GameObject("TestGameObject");
       //作成したゲームオブジェクトを選択状態にする
        Selection.activeObject = go;
       //Undoに登録
        Undo.RegisterCreatedObjectUndo(go,"TestGameObject");
    }
}

これでベースが出来上がったので、後は特定のコンポーネントをAddComponentしたり、選択しているオブジェクトと組み合わせて親子構造を作ったり自由にカスタマイズしてみてください。

参考

answers.unity.com

anchan828.github.io

選択したアセットを参照しているアセットを検索するエディタ拡張

概要

Unityの標準の機能にアセットを選択した状態で右クリックのメニューで「Select Dependencies」を選択すると、選択しているアセットが参照しているアセットの情報がインスペクターに表示されます。

今回はこれの逆のことがしたく、選択したアセットがどのアセットで参照されているのかが知れるように拡張してみました。

実装

環境はUnity2018.4.6f1
Script Runtime Versionは.NET 4.x Equivalentです。(文字連結を+でしたくないという我儘のため.NET3.5でも行けると思います`)

using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;

public class ReverseSelectAssetsLookupFinder
{
    [MenuItem("Assets/ReverseSelectAssetsLookupFinder")]
    private static void ReverseSelectAssetLookup()
    {
        //検索対象は全てのアセットにする
        var allAssetsPath = AssetDatabase.GetAllAssetPaths();
        
        //今回は複数選択していた場合も検索できるようにループさせる
        var selectAssets = Selection.objects;
        foreach (var selectAsset in selectAssets)
        {
            var reverseSelectAssetsLookupList = new List<string>();
            
            var targetAssetPath = AssetDatabase.GetAssetPath(selectAsset);
            for (var i = 0; i < allAssetsPath.Length; ++i)
            {
                //何も出ないのも寂しいのでプログレスバーで進捗を出す
                EditorUtility.DisplayProgressBar("reverse lookup",
                    $"{selectAsset} is Search...{i}/{allAssetsPath.Length}", (float) i / allAssetsPath.Length);

                //アセットの参照対象に選択したアセットが含まれていたら逆引き対象としてみなす
                var assetPath = allAssetsPath[i];
                var dependenciesAssets = AssetDatabase.GetDependencies(assetPath, false);
                if (dependenciesAssets.Contains(targetAssetPath))
                {
                    reverseSelectAssetsLookupList.Add(assetPath);
                }
            }
            EditorUtility.ClearProgressBar();
            //とりあえず結果が知りたいのでDebug.Logに出す
            if (reverseSelectAssetsLookupList.Count == 0)
            {
                Debug.Log($"reverse {selectAsset} lookup result : no Dependencies");
            }
            else
            {
                Debug.Log($"reverse {selectAsset} lookup result : {string.Join(",\n", reverseSelectAssetsLookupList)}");
            }
        }
    }

    [MenuItem("Assets/ReverseSelectAssetsLookupFinder", true)]
    private static bool IsValidate()
    {
        //一つ以上アセットが選択されていたら選択可能にする
        return Selection.objects != null && Selection.objects.Length > 0;
    }
}

あとがき

今回はやりたいこと優先のため簡易的なコードになりましたが、 機能性を考えると同じようにインスペクターで表示したくなりますね…努力目標で追加してみたい…。
MenuItemからインスペクターの表示ってスクリプト上でできるのかな…お詳しい方がいればお力をお借りしたい…。

参考

qiita.com

RectTransformを使ってButtonの当たり判定を複数持ちたかった話

概要

uGuiのButtonを特定の範囲だけ選択したら押せるようにしたかったため調べたことをまとめて記事にしようと思いました。

f:id:daria_sieben:20190805000606p:plain

調べてみた所、大きさを変えたり範囲外にしたりするにはICanvasRaycastFilterを用いるのが良さそうなのがわかりました。

tsubakit1.hateblo.jp

今回のやりたいことを実現するために考えられる方法として

1.Rectを使う
→GameObjectが一つで済むので処理負荷があまりなさそう
→複数解像度対応などCanvasScalerでやっている処理を自前で書かないといけない
→シーン上に表示されないのでエディタ拡張を用いてHandleなどシーン上で調整しやすいような環境を整えると作業が楽になる
Handleを用いたRectクラスをシーン上で視覚的に確認しやすくなっている記事を参考にしました。 light11.hatenadiary.com

2.RectTransformを使う
→GameObjectを必要な数分用意しないといけないので、処理負荷が1に比べて高そう
→複数解像度対応をCanvasScalerでやってくれるので気にしなくていい
→RectTransformで範囲を設定できるのでエディタ拡張をしなくて済む

今回は簡単に実装がしたかったため、2の方法で実装してみようと思います

実装

やることは、UIのワールド座標=スクリーン座標に変換して、変換した位置にタッチ座標が重なっていれば選択できるようにすることです。

using System.Collections.Generic;
using System.Linq;
using UnityEngine;

public class RaycastRectangleFilter : MonoBehaviour, ICanvasRaycastFilter
{
  //子オブジェクトからも設定したいと考えた場合のためにSerializedFieldで設定する
    [SerializeField] private List<RectTransform> _rectTransforms = new List<RectTransform>();    
    public bool IsRaycastLocationValid(Vector2 sp, Camera eventCamera)
    {
    //各RectTransformのタッチ判定が一つでも成功したら許可する
        return _rectTransforms.Any(rectTransform =>
        {
       //スクリーン座標が欲しいのでUI座標からワールド座標に変換する
            var worldCorners = new Vector3[4];
            rectTransform.GetWorldCorners(worldCorners);
            var result = new Rect(
                worldCorners[0].x,
                worldCorners[0].y,
                worldCorners[2].x - worldCorners[0].x,
                worldCorners[2].y - worldCorners[0].y);
       //変換したスクリーン座標にタッチ座標が重なっていれば押せることを許可
            return result.Contains(sp);
        });
    }
}

参考

stackoverflow.com

アセットに設定されてあるアセットバンドル名をコンテキストメニューで取得できるようにしてみた

概要

プログラムでアセットバンドルからロードする際に、アセットに設定したAssetBundle名を取得したい時がありました。 f:id:daria_sieben:20190725105613p:plain

Inspectorで設定できるのでそこから名前もコピーできるのかなと思っていましたが、 コピーみたいな項目がありませんでした。 f:id:daria_sieben:20190725091014p:plain

なのでコンテキストメニューを利用して選択しているアセットのアセットバンドル名をコピーできるようにしました。

環境はUnity2018.4.4.f1です。

実装

コードを見たほうが早いと思うのでコード載せます。

using System.Linq;
using UnityEditor;
using UnityEngine;

public class SelectObjectAssetBundleNamesCopyToClipBoard
{
    [MenuItem("Assets/SelectObjectAssetBundleNamesCopyToClipBoard")]
    private static void CopyToClipBoard()
    {
        //バンドル名はAssetImporterで取得できるので、選択しているアセットのパスを渡す
        var selectAssetsPath = Selection.objects.Select(AssetDatabase.GetAssetPath);
        var assetImporters = selectAssetsPath.Select(AssetImporter.GetAtPath);

         //スクリプトで使えるように引用符(')で囲んでおく
                //.NET 4.xだったらこの書き方で行けますが、それより下のバージョンなら+などで連結してください
        var assetBundleNames = assetImporters.Select(importer => $"\"{importer.assetBundleName}\"");

        //クリップボードにコピー
        GUIUtility.systemCopyBuffer = string.Join(",\n", assetBundleNames);
    }

    [MenuItem("Assets/SelectObjectAssetBundleNamesCopyToClipBoard", true)]
    private static bool IsValidate()
    {
        //選択しているオブジェクトがあればコピーできるようにする
        return Selection.objects != null && Selection.objects.Length > 0;
    }
}

最後に

今は選択しているアセットのみですが、フォルダだった場合はフォルダ以下のアセットバンドル名も取得できれば便利になりそうですね。 機会があったら改良したいと思います。