のにっき

【VBA】自作汎用関数まとめ

今回は、書くことがないのでVBAのコードを公開したいと思います。
VBAでよく使う関数を一つのファイルにまとめて管理しています。
ファイル管理することで、インポートして呼ぶだけなので便利ですよ。

関数をまとめるときの注意!

ファイルにまとめる方法は、
とても便利ですが一つだけ大きな問題があります。
参照設定の追加ができない!ということです。
関数を呼んだ時に、参照の追加ができないとエラーが出てしまい、
対応が面倒くさいことになってしまいます。
対応策としては、

Set fso_Default = CreateObject("Scripting.FileSystemObject")

のように、関数上でオブジェクトを定義する方法です。
関数を組み込む際に、「参照の追加」が必要になった時は、
追加せず、関数上で定義するやり方で組み込みを行いましょう。
このやり方は、他人にマクロを配布する時にも有効なやり方です。

以上です。
GitHubとの連携もやってみたかったので、良い練習になりました。
VBAは組み方次第で処理時間がえげつないぐらい変わるので
処理の速い組み込み方を皆さまと共有できればと思います。

UnityとWindowsフォームアプリの意識の違い

今回は、つい最近Unityで組み込みやってて
同時並行でWindowsフォームアプリでツールを作っていた時に
ハッ!としたことについてお話します。
一週間前にハッとしてから今まで内容をまとめようと
業務をほっぽり出して考えたのですが、全く考えがまとまらなかったので
意味の分からない部分が多々あると思います。
お手数をお掛けして申し訳ありませんが、くみ取ってください。。。

シーンではなく、オブジェクトに対してスプリクトを作る!

さっそく意味不明なタイトルです・・・
Windowsフォームアプリの仕様としまして、

  • オブジェクトのイベント関数は一つのクラスファイル(【例】Form1.cs)にまとめる
  • オブジェクトを呼び出すとき、必ずフォームをベースとして呼び出さないといけない

上記2つの仕様があります。この仕様に慣れると、
Unityの組み込みの自由度に、無意識のうちに枷を付けてしまうことになります。
Unityに出来てWindowsフォームアプリでできないこと・・・
『 オブジェクトに対してクラスファイルを設定できる 』です!

・・・だからなんなのか?
例として、ボタンを押すとエラー画面が出る簡単なアプリを作るとします。

f:id:apuridasuo:20190523161123p:plain
図:仕様説明
この仕様を、「Windowsフォームアプリ的思考(個人的)」と
「Unity的思考(個人的)」の2パターンで組み込んでみます。

Windowsフォームアプリ的思考の組み込み

▼ Test01_Main.cs

    //==============================================
    // 変数
    //==============================================
    public static GameObject Obj_Err;
    public static Text Txt_Value;

    //==============================================
    // 起動時イベント
    //==============================================
    void Start()
    {
        Obj_Err = GameObject.Find("Canvas/Panel_Error").gameObject;
        Txt_Value = Obj_Err.transform.Find("Value").GetComponent<Text>();
        Obj_Err.SetActive(false);
    }

上記ソースを空オブジェクトに設定します。
シーン起動時に、
シーン内で変更を加えるオブジェクトを全て変数に定義することで
オブジェクトをまとめて管理できます。

▼ Test01_Tap.cs

    //=============================================
    // エラー画面表示イベント
    //=============================================
    public void Tap_ErrorWindow_Open()
    {
        Tst_Main.Obj_Err.SetActive(true);
        Tst_Main.Txt_Value.text = "エラーの内容!";
    }
    //=============================================
    // エラー画面閉じるイベント
    //=============================================
    public void Tap_ErrorWindow_Close()
    {
        Tst_Main.Obj_Err.SetActive(false);
    }

上記ソースが、「エラー画面の表示開始・終了」の処理です。
ボタン等のイベントで呼び出す関数をまとめて一つのファイルにしておく事で、
ボタンに設定するスプリクトファイルが共通化されます。
後は、各ボタンにInspectorウィンドウから設定します。

以上が、Windowsフォームアプリ的思考で組み込んでみたパターンです。
特徴としましては、

  • オブジェクトの管理を1シーンでまとめて行っている(Test01_Main.cs)
  • イベント関数の管理を1シーンでまとめて行っている(Test01_Tap.cs)

1シーンごとにまとめる方が見やすいし管理しやすい!。という考え方が
フォームアプリの組み込み方に毒された意固地な思考なんです。
※決して悪くはないとも思っています・・・

Unity的思考の組み込み

「Test02_Main.cs」は作りません
▼ Test02_Tap.cs

    //==============================================
    // エラー画面表示イベント
    //==============================================
    public void Tap_ErrorWindow_Open()
    {
        Test02_ErrCntrl.SetOpenErrMsg("エラーの内容!");
    }

エラー画面を出力するボタンの関数のみ記入しておきます。
共通オブジェクトとしてまとまらなかった
オブジェクトのイベントをこのファイルにまとめます。

▼ Test02_ErrCntrl.cs

    // ■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□
    //
    //			変数定義
    //
    // ■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□
    //===============================================
    // 変数
    //===============================================
    public static GameObject Obj_Err;
    public static Text Txt_Value;

    // ■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□
    //
    //			イベント関数
    //
    // ■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□
    //=============================================
    // 起動時
    //=============================================
    void Start ()
    {
        Obj_Err= gameObject;
        Txt_Value = transform.Find("Value").GetComponent<Text>();
        Button Btn_Ok = transform.Find("Btn_Ok").GetComponent<Button>();
        Btn_Ok.onClick.AddListener(Tap_OkButton);
        Obj_Err.SetActive(false);
    }

    // ■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□
    //
    //			サブルーチン関数
    //
    // ■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□
    /*********************************************************************
       メソッド       : Tap_OkButton
       概要           : OKボタンタップ
    **********************************************************************/
    public void Tap_OkButton()
    {
        Obj_Err.SetActive(false);
    }
    /*********************************************************************
       メソッド       : SetOpenErrMsg
       概要           : エラーパネル表示処理
    **********************************************************************/
    public static void SetOpenErrMsg( string str_Msg_Set)
    {
        Obj_Err.SetActive(true);
        Txt_Value.text = str_Msg_Set;
    }

上記ファイルは、エラー画面のパネルに設定します。
エラー画面のパネル以下のオブジェクトを1つにまとめて管理しています。
シーンごとにまとめるのではなく、同カテゴリのオブジェクトごとにまとめる!
これが、Windowsフォームアプリに出来なくてUnityにできる組み込み方だと思います。

Unity的思考の組み込み方の利点

ではこの組み込み方のなにが良いのでしょうか?
まとめて管理したほうが見やすそうでは?そんなことないです!
オブジェクトごとにまとめる利点を思いつくだけまとめました。

  • 1つの cs ファイルで完結させることができる

1つのシーンには、様々な役割を持つオブジェクトがたくさんあります。
まとめて管理しようとすると、管理変数が膨大な数になってしまい、
どれがどれか分かりずらくなってしまいます。
1つの役割ごとにcsファイルをまとめることで
変数の中身も分かりやすく、
ほかのcsファイルとのつながりが最小限のやり取りで済みます!
※この辺の説明が上手くまとまらなかったんです・・・すみません((´;ω;`)

  • 役割でまとめることで、別シーン・別プロジェクトでも使えるプレハブが作れる

1シーンでまとめると、似たような役割のオブジェクトを
別シーンで作りたいとき、スクリプト部分が移行できないです。
しかし、1つの cs ファイルで完結できていればそのcsファイルは
別のシーンでも問題なく動くのです!
より自由度の高いプレハブを作ることができるのです!

以上が、今回ハッ!としたことです。
これに気付いた時、洗脳が解けた感覚がしましたー
無意識のうちに組み込み方にダメな癖がついていて、
頭の固い自分の脳みそをぶんなぐってやりたいです。。。

スワイプの向きに弾を発射【Android用】【ロックマン風】

今回は、横スクロールアクション等で使えそうな
フリックした方向に弾を発射する仕様を
解説用に1から作ってみたいと思います。
エミュレータ・実機で動作できるように弾発射以外の部分もたくさん書いたので、
 発射部分のみ見たい方は「 目次:弾発射に関するコードの解説 」に飛んでください。

追記

【Unity】Android用フリック対応ボタン作った - のにっき
上記サイトで、フリック角度や方向を簡単に取得できるボタンの
ユニティパッケージ公開してます!この記事と合わせて見てみてください。

画像準備

  • キャラ画像

適当にパワポで書きました
発射に合わせてアニメーションを付けようと思ったので
待機・発射用の画像を用意しました。

  • ボタン画像

下記サイトの素材をお借りしました。
落とした画像をボタンっぽくして1枚の画像にしてます。
銃弾のフリーアイコン4 | アイコン素材ダウンロードサイト「icooon-mono」 | 商用利用可能なアイコン素材が無料(フリー)ダウンロードできるサイト

  • 弾画像

下記サイトの「メテオ」の素材をお借りしました。
落とした画像をパワポでまとめて1枚の画像にしてます。
フリーエフェクト素材 - ゲーム素材 | ランスタのフリーBGM、効果音、ゲーム素材、3DCG

f:id:apuridasuo:20190514140040p:plain
図:作成画像(キャラ、弾、ボタンで3枚の画像になります)

オブジェクト作成

  • オブジェクト作成・配置

弾を発射するように適当にステージ・キャラ・ボタンを作成していきます
下図で注意が必要なことは、画面枠外にオレンジ色の枠を作っていることです。
これは、発射した弾が消えないと画面外で無限に動き続けるので、
弾の消える範囲を作っています。
画面外に作ったのは、画面外の敵にもある程度影響を与えたいためです。

f:id:apuridasuo:20190514145133p:plain
図:作成したオブジェクト配置

  • 弾丸オブジェクトについて

今回は、弾丸のプレハブを事前に10個作っています。
この理由は、発射時に「Resources.Load」で弾丸プレハブを作成するより、
オブジェクトを作っておいて、表示の切り替えでやりくりする方が処理が早いからです。
あと、見やすくて管理が楽です。

  • Tagの追加

各オブジェクトがぶつかった際の種類を判別する「Tag」
このTagを追加していきます。※テスト用なので大雑把に追加してます。
Tag 0:Ground・・・・・地面
Tag 1:Wall・・・・・・壁
Tag 2:Bullet・・・・・・弾
Tag 3:Chara_Main・・・メインキャラ
Tag 4:OutLine・・・・・外枠

f:id:apuridasuo:20190514152726p:plain
図:Tag追加内容

  • 各オブジェクトの設定内容

オブジェクトに中身を設定していきます。
※赤枠の部分が追加した内容になります。

f:id:apuridasuo:20190514153219p:plain
図:各オブジェクトのInspector内容

  • キャラ、弾丸のアニメーション管理

キャラ:基本は待機アニメがループで流れて、発射時に発射アニメが流れる
弾丸:弾丸アニメが無限ループで流れる

f:id:apuridasuo:20190515101500p:plain
図:Animatorの内容

スクリプト作成【ファイル作成】

いよいよ、弾を発射させる処理の部分を解説していきます。
まずは、スクリプトファイルを作成していきましょう。
作成するファイル名と、設定するオブジェクト名
Ply_Bullet_Cntrl・・・「Nd_Bullet_0~9」に設定
 →弾丸の衝突処理を記入
Ply_Main・・・「ScriptCntrl」に設定
 →エディター上で設定する変数、割り込みイベント等を記入
Ply_TapEv・・・「Btn_Shot」に設定
 →ユーザー操作(イベント)で呼び出す関数を記入
Ply_Cns・・・設定しない
 →全スクリプトで共通に使われる「変数・定数・クラス・関数」を記入
Ply_Timer・・・設定しない
 →タイマー系のイベントで呼ばれる関数を記入

スクリプト作成【コード記入】

説明がいる部分と要らない部分があってややこしいので、
とりあえず説明なしでソースコードを全部記入します。
必要な説明は後ろで行います。
Ply_Bullet_Cntrl

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using static Nm_Play.Ply_Cns;

public class Ply_Bullet_Cntrl : MonoBehaviour
{
    //*******************************************************************************
    // 衝突時に呼ばれるイベント
    //*******************************************************************************
    private void OnTriggerEnter2D(Collider2D col_Get)
    {
        //============================================================
        // 衝突判定 ※衝突オブジェクトのタグで判定している
        //============================================================
        if ((col_Get.gameObject.tag == Df_TagName[Df_Tag_No]) ||
            (col_Get.gameObject.tag == Df_TagName[Df_Tag_MainC]))
        {
            return;
        }

        //============================================================
        // 変数定義
        //============================================================
        SceneOneIf SceneIf = Cls_SceneOneIf;
        Obj_All ObjIf = Cls_ObjAll;

        //============================================================
        // 衝突した弾丸オブジェクトを非表示
        //============================================================
        int i_SetId = 0;
        i_SetId = int.Parse(this.name.Replace(Df_Bullet_Name, ""));
        ObjIf.BulletIf.SetBulletNd_End(i_SetId);
    }
}

Ply_Main

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using static Nm_Play.Ply_Cns;
using static Nm_Play.Ply_Timer;

public class Ply_Main : MonoBehaviour
{
    // ■□■□■□■□■□■□■□■□■□■□■□■□■□
    //
    //			変数定義
    //
    // ■□■□■□■□■□■□■□■□■□■□■□■□■□
    public GameObject Obj_Canvas_UI;
    public GameObject Obj_Canvas_Back;
    public GameObject Obj_Back;
    public Sprite[] Spr_Btn_On;
    public Sprite[] Spr_Btn_Off;

    // ■□■□■□■□■□■□■□■□■□■□■□■□■□
    //
    //			【イベント】関数一覧
    //
    // ■□■□■□■□■□■□■□■□■□■□■□■□■□
    //*****************************************************
    // 開始時
    //*****************************************************
    void Start()
    {
        //=================================================
        // オブジェクトクラス初期化
        //=================================================
        // クラス初期化
        Cls_ObjAll = 
        new Obj_All(Obj_Canvas_UI, Obj_Canvas_Back, Obj_Back, Spr_Btn_On, Spr_Btn_Off);
        Cls_SceneOneIf = new SceneOneIf();
    }

    //****************************************************
    // 割り込み関数
    //****************************************************
    void FixedUpdate()
    {
        //------------------------------------------------
        // 弾丸管理
        //------------------------------------------------
        if (Cls_SceneOneIf.cls_BtnIf[Df_BtnId_Shot] != null)
        {
            SetBullet_Shot();
        }
    }
}

Ply_TapEv

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using static Nm_Play.Ply_Cns;
using static Nm_Play.Ply_Timer;

public class Ply_TapEv : MonoBehaviour
{
    /*****************************************************************************
       モジュール名: Tap_Shot_
       概要        : ショットボタン押下・離す処理
    *****************************************************************************/
    public void Tap_Shot_On()
    {
        //==================================================================
        // 変数定義
        //==================================================================
        SceneOneIf SceneIf = Cls_SceneOneIf;
        Obj_All ObjIf = Cls_ObjAll;

        //==================================================================
        // ボタン押下処理
        //==================================================================
        // ボタン変数を設定
        ObjIf.BulletIf.i_ShotWait = 0;
        SceneIf.cls_BtnIf[Df_BtnId_Shot] = new BtnTapIf(Df_BtnId_Shot);
        // 画像設定
        ObjIf.SetBtnImage(true, Df_BtnId_Shot);

    }
    public void Tap_Shot_Off()
    {
        //==================================================================
        // 変数定義
        //==================================================================
        SceneOneIf SceneIf = Cls_SceneOneIf;

        //==================================================================
        // ボタン離す処理
        //==================================================================
        // ボタン情報を変数に格納
        SceneIf.cls_BtnIf[Df_BtnId_Shot] = null;
        // 画像設定
        Cls_ObjAll.SetBtnImage(false, Df_BtnId_Shot);
    }
}

Ply_Cns

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

namespace Nm_Play
{
    public class Ply_Cns : MonoBehaviour
    {
        // ■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□
        //
        //			変数定義
        //
        // ■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□
        //=======================================================================
        // 変数
        //=======================================================================
        public static Obj_All Cls_ObjAll;
        public static SceneOneIf Cls_SceneOneIf;

        //=======================================================================
        // 定数
        //=======================================================================
        //-----------------------------------------------------------------
        // ボタン管理系
        //-----------------------------------------------------------------
        // ボタン管理用ID
        public static int Df_BtnId_Shot = 0;
        public static int Df_BtnId_Max = 1;
        //-----------------------------------------------------------------
        // 弾丸管理系
        //-----------------------------------------------------------------
        // 弾丸プレハブ名
        public static string Df_Bullet_Name = "Nd_Bullet_";
        // 弾プレハブ最大数
        public static int Df_Bullet_Max = 10;
        // 弾発射ウェイト
        public static int Df_Bullet_Wait = 10;
        public static int Df_Bullet_Wait2 = 20;
        //-----------------------------------------------------------------
        // アニメータートリガー管理系
        //-----------------------------------------------------------------
        // メインキャラ
        public static string Df_Trg_Main_Gun = "Trg_Shot";
        //-----------------------------------------------------------------
        // 管理
        //-----------------------------------------------------------------
        public static int Df_Tag_No = 0;
        public static int Df_Tag_Grnd = 1;
        public static int Df_Tag_Wall = 2;
        public static int Df_Tag_Blet = 3;
        public static int Df_Tag_MainC = 4;
        public static int Df_Tag_OutL = 5;
        public static string[] Df_TagName = new string[]
        {
            "Untagged",
            "Ground",
            "Wall",
            "Bullet",
            "Chara_Main",
            "OutLine"
        };


        // ■□■□■□■□■□■□■□■□■□■□■□■□■□■□■■□■□■□
        //
        //			クラス定義
        //
        // ■□■□■□■□■□■□■□■□■□■□■□■□■□■□■■□■□■□
        /**************************************************************************
         * 
            クラス名    : Obj_All
            概要        : 全オブジェクト情報

        **************************************************************************/
        public class Obj_All
        {
            //=====================================================================
            // 変数定義
            //=====================================================================
            //---------------------------------------------------------------
            // ホーム画面
            //---------------------------------------------------------------
            public GameObject MainChara_Obj;
            public Transform MainChara_Posi;
            public Animator MainChara_Ani;
            public Image[] Btn_Sprite_Set;
            public Sprite[,] Btn_Sprite_Get;
            public BulletIf BulletIf;

            /**************************************************************************
               コントラクタ   
            **************************************************************************/
            public Obj_All
            ( GameObject Canvas_UI, GameObject Canvas_Back, GameObject Obj_Back, 
              Sprite[] Spr_Btn_On, Sprite[] Spr_Btn_Off )
            {
                //==================================================================
                // オブジェクト設定
                //==================================================================
                //------------------------------------------------------------
                // キャラ関係
                //------------------------------------------------------------
                MainChara_Obj = 
                    Obj_Back.transform.Find("Material/Chara_Main").gameObject;
                MainChara_Posi = MainChara_Obj.GetComponent<Transform>();
                MainChara_Ani = MainChara_Obj.GetComponent<Animator>();
                //------------------------------------------------------------
                // バレット関係
                //------------------------------------------------------------
                BulletIf = new BulletIf(Obj_Back);
                //------------------------------------------------------------
                // ボタン関係
                //------------------------------------------------------------
                // オブジェクト変数
                Btn_Sprite_Set = new Image[Df_BtnId_Max];
                Btn_Sprite_Set[Df_BtnId_Shot] = 
                    Canvas_UI.transform.Find("Panel_Btn/Btn_Shot").GetComponent<Image>();
                // 貼り付け用ボタン画像
                Btn_Sprite_Get = new Sprite[2, Df_BtnId_Max];
                for (int i_Flg = 0; i_Flg < 2; i_Flg++)
                {
                    // 貼り付ける画像配列取得
                    Sprite[] Spr_Set;
                    if (i_Flg == 0)
                    {
                        Spr_Set = Spr_Btn_On;
                    }
                    else
                    {
                        Spr_Set = Spr_Btn_Off;
                    }
                    // 画像を変数に格納
                    for (int i_Type = 0; i_Type < Df_BtnId_Max; i_Type++)
                    {
                        Btn_Sprite_Get[i_Flg, i_Type] = Spr_Set[i_Type];
                    }
                }
            }

            /********************************************************************
                メソッド名  : SetBtnImage
                概要        : ボタン画像設定
            ********************************************************************/
            public void SetBtnImage(bool bl_IsOn, int i_BtnId)
            {
                int i_Flg = 0;
                if (bl_IsOn == false)
                {
                    i_Flg = 1;
                }
                Btn_Sprite_Set[i_BtnId].sprite = Btn_Sprite_Get[i_Flg, i_BtnId];
            }
        }


        /********************************************************************
         * 
            クラス名    : BulletIf
            概要        : 弾丸情報

        ********************************************************************/
        public class BulletIf
        {
            //===============================================================
            // 変数定義
            //===============================================================
            public int i_SetId = 0;
            public int i_BltCnt = 0;
            public int i_ShotWait = 0;
            public Dictionary<string, int> Dic_NameId = new Dictionary<string, int>();
            public List<GameObject> l_Obj_Object;
            public List<Rigidbody2D> l_R2d_Vctl;

            /*********************************************************************
               コントラクタ   
            *********************************************************************/
            public BulletIf(GameObject Obj_Base)
            {
                // 変数初期化
                i_SetId = 0;
                i_BltCnt = 0;
                i_ShotWait = 0;
                Dic_NameId = new Dictionary<string, int>();
                l_Obj_Object = new List<GameObject>();
                l_R2d_Vctl = new List<Rigidbody2D>();
                // 変数設定
                for (int i_LpCnt = 0; i_LpCnt < Df_Bullet_Max; i_LpCnt++)
                {
                    // 事前に作成していた10個のプレハブオブジェクトを変数に格納
                    string str_Name = 
                        "Material/Bullets/" + Df_Bullet_Name + i_LpCnt.ToString();
                    Dic_NameId.Add(str_Name, i_LpCnt);
                    l_Obj_Object.Add(Obj_Base.transform.Find(str_Name).gameObject);
                    l_R2d_Vctl.Add
                        (l_Obj_Object[i_LpCnt].transform.GetComponent<Rigidbody2D>());
                    // 発射までは非表示
                    l_Obj_Object[i_LpCnt].SetActive(false);
                }
            }

            /***********************************************************************
               メソッド       : SetBulletNd_Stat
               概要           : 弾丸発射処理
            ***********************************************************************/
            public void SetBulletNd_Stat()
            {
                //==================================================================
                // 変数定義
                //==================================================================
                Obj_All ObjIf = Cls_ObjAll;

                //==================================================================
                // 表示開始処理
                //==================================================================
                // 弾数カウンタ加算
                i_BltCnt = i_BltCnt + 1;
                // 表示ID設定
                i_SetId = i_SetId + 1;
                if (i_SetId >= 10)
                {
                    i_SetId = 0;
                }
                // 位置をキャラに合わせて表示ON
                l_Obj_Object[i_SetId].transform.position = 
                    ObjIf.MainChara_Obj.transform.position;
                l_Obj_Object[i_SetId].SetActive(true);
                // 速度追加
                SetBulletSpeed(l_Obj_Object[i_SetId], l_R2d_Vctl[i_SetId]);
            }

            /************************************************************************
               メソッド       : SetBulletNd_End
               概要           : 弾丸消去処理
            ************************************************************************/
            public void SetBulletNd_End(int i_EndId)
            {
                // ぶつかったオブジェクトを非表示にする
                l_Obj_Object[i_EndId].SetActive(false);
            }

            /************************************************************************
              メソッド       : SetBulletSpeed
              概要           : 弾丸速度設定
           ************************************************************************/
            public void SetBulletSpeed(GameObject Obj_Set, Rigidbody2D R2d_Set)
            {
                //=====================================================================
                // 変数定義
                //=====================================================================
                float f_Speed = 10f;
                float f_Kakudo = 0f;
                Vector2 Vct2_Stat, Vct2_End;
                Vector2 Vct2_Cal_S, Vct2_Cal_E, Vct2_Cal_Set;
                SceneOneIf SceneIf = Cls_SceneOneIf;
                Obj_All ObjIf = Cls_ObjAll;

                //=====================================================================
                // 速度追加
                //=====================================================================
                //---------------------------------------------------------------
                // 角度設定
                //---------------------------------------------------------------
                // 開始・終点座標から角度計算
                Vct2_Stat = SceneIf.cls_BtnIf[Df_BtnId_Shot].GetNowTapPoint();
                Vct2_End = SceneIf.cls_BtnIf[Df_BtnId_Shot].vct3_StatPosi;
                Vct2_Cal_S = new Vector2(1, 0);
                Vct2_Cal_E = Vct2_Stat - Vct2_End;
                f_Kakudo = Vector2.Angle(Vct2_Cal_S, Vct2_Cal_E);
                // 座標から向き設定
                if (Vct2_Stat.y - Vct2_End.y < 0f)
                {
                    f_Kakudo = f_Kakudo * (-1f);
                }
                // 座標が同じなら前に発射書き換え
                if (Vct2_Stat == Vct2_End)
                {
                    f_Kakudo = 0;
                }
                //---------------------------------------------------------------
                // 速度追加
                //---------------------------------------------------------------
                Vct2_Cal_Set.x = Mathf.Cos(Mathf.Deg2Rad * f_Kakudo) * f_Speed;
                Vct2_Cal_Set.y = Mathf.Sin(Mathf.Deg2Rad * f_Kakudo) * f_Speed;
                R2d_Set.velocity = Vct2_Cal_Set;

                //=========================================================
                // オブジェクト設定
                //=========================================================
                //---------------------------------------------------
                // キャラ角度、アニメ設定
                //---------------------------------------------------
                ObjIf.MainChara_Ani.SetTrigger(Df_Trg_Main_Gun);
                if ((-90 <= f_Kakudo) && (f_Kakudo <= 90))
                {
                    ObjIf.MainChara_Posi.eulerAngles = new Vector3(0, 0, 0);
                }
                else
                {
                    ObjIf.MainChara_Posi.eulerAngles = new Vector3(0, 180, 0);
                }
                //----------------------------------------------------------------
                // バレット角度
                //----------------------------------------------------------------
                Quaternion Quat_Set = Quaternion.identity;
                Quat_Set.eulerAngles = new Vector3(0, 0, f_Kakudo + 90);
                Obj_Set.transform.rotation = Quat_Set;
            }
        }

        /***********************************************************************
         * 
            クラス名    : SceneOneIf
            概要        : シーン内情報

        ***********************************************************************/
        public class SceneOneIf
        {
            //==================================================================
            // 変数定義
            //==================================================================
            public BtnTapIf[] cls_BtnIf;

            /***********************************************************************
               コントラクタ   
            ***********************************************************************/
            public SceneOneIf()
            {
                cls_BtnIf = new BtnTapIf[2];
            }
        }

        /***********************************************************************
            クラス名    : BtnTapIf
            概要        : ボタンタップ情報
        ***********************************************************************/
        public class BtnTapIf
        {
            //==================================================================
            // 変数定義
            //==================================================================
            public int i_BtnType = 0;
            public Vector3 vct3_StatPosi;

            /***********************************************************************
               コントラクタ   
            ***********************************************************************/
            public BtnTapIf(int i_TypeSet)
            {
                // タップID設定
                i_BtnType = i_TypeSet;
                // 現在のタップ位置取得
                vct3_StatPosi = GetNowTapPoint();
            }

            /***********************************************************************
               モジュール名: GetNowTapPoint
               概要        : タップ座標取得
               引数        : なし
               戻り値      : Vector2:タップ中の座標
            ***********************************************************************/
            public Vector2 GetNowTapPoint()
            {
                //============================================================
                // エディター・実機ごとにタップ位置取得
                //============================================================
                Vector2 Vct2_Ret = new Vector2();
                // エディター
                if (AppConst.IsEditor == true)
                {
                    Vct2_Ret = Camera.main.ScreenToWorldPoint(Input.mousePosition);
                }
                // Android実機
                else
                {
                    for (int i_LpCnt = 0; i_LpCnt < Input.touchCount; i_LpCnt++)
                    {
                        Touch touch_One = Input.GetTouch(i_LpCnt);
                        Vct2_Ret = Camera.main.ScreenToWorldPoint(touch_One.position);
                        // 複数タップされる可能性があるので、適切なタップ位置を取得
                        // ※ここの判定は機種固有のものだと思われます
                        if ((i_BtnType == Df_BtnId_Shot) && (Vct2_Ret.x > 0))
                        {
                            break;
                        }
                    }
                }

                //================================================================
                // 2828 デバッグ用テキスト ※後で消す!
                //================================================================
                Text Txt_Set = 
                GameObject.Find("Canvas_Back/Text").GetComponent<Text>();
                Txt_Set.text = 
                "開始:「 " + vct3_StatPosi.x + " , " + vct3_StatPosi.y + " 」\r\n";
                Txt_Set.text = 
                Txt_Set.text + "終了:「 " + Vct2_Ret.x + " , " + Vct2_Ret.y + " 」\r\n";
                Txt_Set.text = 
                Txt_Set.text + "弾数:" + Cls_ObjAll.BulletIf.i_BltCnt + "";

                //=================================================================
                // 戻り値設定
                //=================================================================
                return Vct2_Ret;
            }
        }

        /****************************************************************************
         * 
            クラス名    : AppConst
            概要        : App作成前提の定義

        ****************************************************************************/
        public class AppConst
        {
            //=======================================================================
            // エディター用コンパイルかどうか
            //=======================================================================
#if UNITY_EDITOR
            public static bool IsEditor = true;
#else
    public static bool  IsEditor = false;
#endif
        }
    }
}

Ply_Timer

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using static Nm_Play.Ply_Cns;

namespace Nm_Play
{
    public class Ply_Timer : MonoBehaviour
    {
        // ■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□
        //
        //			【発射系】関数定義
        //
        // ■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□
        /***************************************************************************
           メソッド       : SetBullet_Shot
           概要           : バレット発射処理
        ***************************************************************************/
        public static void SetBullet_Shot()
        {
            //======================================================================
            // 変数定義
            //======================================================================
            SceneOneIf SceneIf = Cls_SceneOneIf;
            Obj_All ObjIf = Cls_ObjAll;

            //======================================================================
            // ショットボタン押下中判定
            //======================================================================
            if (SceneIf.cls_BtnIf[Df_BtnId_Shot] == null)
            {
                return;
            }

            //======================================================================
            // 弾丸発射処理
            //======================================================================
            //----------------------------------------------------------------
            // タイマー完了判定
            //----------------------------------------------------------------
            ObjIf.BulletIf.i_ShotWait--;
            if (ObjIf.BulletIf.i_ShotWait > 0)
            {
                return;
            }
            //----------------------------------------------------------------
            // 弾丸発射
            //----------------------------------------------------------------
            ObjIf.BulletIf.SetBulletNd_Stat();
            //----------------------------------------------------------------
            // タイマー値初期化
            //----------------------------------------------------------------
            if (ObjIf.BulletIf.i_BltCnt >= 3)
            {
                ObjIf.BulletIf.i_BltCnt = 0;
                ObjIf.BulletIf.i_ShotWait = Df_Bullet_Wait2;
            }
            else
            {
                ObjIf.BulletIf.i_ShotWait = Df_Bullet_Wait;
            }
        }
    }
}

以上が、今回組み込んだソースコードになります。
別プロジェクトでゲームを作ってるものからブログ用に抜粋したものなので、
余計なコードが入っているかもしれませんが気にしないでください。

スクリプト作成【オブジェクトへの設定内容】

スクリプトをオブジェクトに設定した後、Inspector上で設定が必要なものがあります。
設定内容を下記に図で解説します。 ※変更点は赤枠の部分です

f:id:apuridasuo:20190515114028p:plain
図:各オブジェクトの変更内容

弾発射に関するコードの解説

上記のコードは実行できるように弾以外の部分が
弾を発射する部分について掘り下げていきます。
弾発射時の仕様フロー
1.「発射」ボタンを押す

2.ボタンをタップされている間、一定間隔で弾を発射する

3.フリックの方向に弾プレハブを発射

4.弾が何かとぶつかったら消える

仕様解説
1.「発射」ボタンを押す
「Ply_TapEv」ファイルの「Tap_Shot_On()」関数
この関数で、ボタン押下開始時の座標を保持します。

            /***********************************************************************
               モジュール名: GetNowTapPoint
               概要        : タップ座標取得
               引数        : なし
               戻り値      : Vector2:タップ中の座標
            ***********************************************************************/
            public Vector2 GetNowTapPoint()
            {
                //============================================================
                // エディター・実機ごとにタップ位置取得
                //============================================================
                Vector2 Vct2_Ret = new Vector2();
                // エディター
                if (AppConst.IsEditor == true)
                {
                    Vct2_Ret = Camera.main.ScreenToWorldPoint(Input.mousePosition);
                }
                // Android実機
                else
                {
                    for (int i_LpCnt = 0; i_LpCnt < Input.touchCount; i_LpCnt++)
                    {
                        Touch touch_One = Input.GetTouch(i_LpCnt);
                        Vct2_Ret = Camera.main.ScreenToWorldPoint(touch_One.position);
                        // 【A】複数タップされる可能性があるので、適切なタップ位置を取得
                        // ※ここの判定は機種固有のものだと思われます
                        if ((i_BtnType == Df_BtnId_Shot) && (Vct2_Ret.x > 0))
                        {
                            break;
                        }
                    }
                }

                //=================================================================
                // 戻り値設定
                //=================================================================
                return Vct2_Ret;
            }

このコードで、現在タップしている座標を取得できます。
エディターではマウスのクリック位置、
Android実機ではTouchクラスの「position」という要素を取得しています。
この時、「Input.GetTouch(番号)」で取得できるTouch情報は、
ボタンのタップ情報に限らず、
5か所タップされていた場合は5つのタップ情報が格納されています

なので、機種仕様に合わせて判定で適切なTouchクラスを取得してください
※ソースの【A】部分を編集する
例としては、「タップ座標がボタンの画像範囲内の座標なら取得」とかだといいと思います。

2.ボタンをタップされている間、一定間隔で弾を発射する
割り込み処理でタイマー値を減算させることで
一定時間で弾を発射する仕様になります。

    //****************************************************
    // 割り込み関数
    //****************************************************
    void FixedUpdate()
    {
        //------------------------------------------------
        // 弾丸管理
        //------------------------------------------------
        if (Cls_SceneOneIf.cls_BtnIf[Df_BtnId_Shot] != null)
        {
            SetBullet_Shot();
        }
    }
        /***************************************************************************
           メソッド       : SetBullet_Shot
           概要           : バレット発射処理
        ***************************************************************************/
        public static void SetBullet_Shot()
        {
            //======================================================================
            // 変数定義
            //======================================================================
            SceneOneIf SceneIf = Cls_SceneOneIf;
            Obj_All ObjIf = Cls_ObjAll;

            //======================================================================
            // ショットボタン押下中判定
            //======================================================================
            if (SceneIf.cls_BtnIf[Df_BtnId_Shot] == null)
            {
                return;
            }

            //======================================================================
            // 弾丸発射処理
            //======================================================================
            //----------------------------------------------------------------
            // タイマー完了判定
            //----------------------------------------------------------------
            ObjIf.BulletIf.i_ShotWait--;
            if (ObjIf.BulletIf.i_ShotWait > 0)
            {
                return;
            }
            //----------------------------------------------------------------
            // 弾丸発射
            //----------------------------------------------------------------
            ObjIf.BulletIf.SetBulletNd_Stat();
            //----------------------------------------------------------------
            // タイマー値初期化
            //----------------------------------------------------------------
            if (ObjIf.BulletIf.i_BltCnt >= 3)
            {
                ObjIf.BulletIf.i_BltCnt = 0;
                ObjIf.BulletIf.i_ShotWait = Df_Bullet_Wait2;
            }
            else
            {
                ObjIf.BulletIf.i_ShotWait = Df_Bullet_Wait;
            }
        }

毎割り込みでタイマー変数(ObjIf.BulletIf.i_ShotWait)を減算して、
0になったら弾発射関数を呼び出しています。
関数呼び出し後、変数にウェイト定数(Df_Bullet_Wait、_Wait2)を再設定します。
発射した弾数(ObjIf.BulletIf.i_BltCnt)が3個になったら大ウェイト(Df_Bullet_Wait2)
を設定する仕様になっています。

3.フリックの方向に弾プレハブを発射

                 //---------------------------------------------------------------
                // 角度設定
                //---------------------------------------------------------------
                // 開始・終点座標から角度計算
                Vct2_Stat = SceneIf.cls_BtnIf[Df_BtnId_Shot].GetNowTapPoint();
                Vct2_End = SceneIf.cls_BtnIf[Df_BtnId_Shot].vct3_StatPosi;
                Vct2_Cal_S = new Vector2(1, 0);
                Vct2_Cal_E = Vct2_Stat - Vct2_End;
                f_Kakudo = Vector2.Angle(Vct2_Cal_S, Vct2_Cal_E);
                // 座標から向き設定
                if (Vct2_Stat.y - Vct2_End.y < 0f)
                {
                    f_Kakudo = f_Kakudo * (-1f);
                }
                // 座標が同じなら前に発射書き換え
                if (Vct2_Stat == Vct2_End)
                {
                    f_Kakudo = 0;
                }

                //=========================================================
                // 弾オブジェクトを設定
                //=========================================================
                //---------------------------------------------------------------
                // 速度ベクトルを設定
                //---------------------------------------------------------------
                Vct2_Cal_Set.x = Mathf.Cos(Mathf.Deg2Rad * f_Kakudo) * f_Speed;
                Vct2_Cal_Set.y = Mathf.Sin(Mathf.Deg2Rad * f_Kakudo) * f_Speed;
                R2d_Set.velocity = Vct2_Cal_Set;
                //---------------------------------------------------------------
                // 画像の角度を合わせる
                //---------------------------------------------------------------
                Quaternion Quat_Set = Quaternion.identity;
                Quat_Set.eulerAngles = new Vector3(0, 0, f_Kakudo + 90);
                Obj_Set.transform.rotation = Quat_Set;
            }

上記のソースでタップの方向に弾を発射できます。
・変数概要
Vct2_Stat:現在タップしている座標
Vct2_End:ボタンタップ開始時の座標
f_Kakudo:計算後の角度
f_Speed:弾の速さ
R2d_Set.velocity:設定する弾のRigidbody2Dクラス変数
Obj_Set.transform.rotation:設定する弾の画像の角度
・処理概要
「// 角度設定」
 フリック角度を計算
「// 速度ベクトルを設定」
 弾画像に計算した角度に向かって進む力を設定
 ※.タップ座標が開始・現在どちらも同じなら前に飛ばす仕様
「// 画像の角度を合わせる」
 画像が進行方向に向くように設定

4.弾が何かとぶつかったら消える

    //***************************************************************************
    // 衝突時に呼ばれるイベント
    //***************************************************************************
    private void OnTriggerEnter2D(Collider2D col_Get)
    {
        //=========================================================================
        // 衝突判定 ※衝突オブジェクトのタグで判定している
        //=========================================================================
        if ( (col_Get.gameObject.tag == Df_TagName[Df_Tag_No]) || 
             (col_Get.gameObject.tag == Df_TagName[Df_Tag_MainC]) )
        {
            return;
        }

        //=========================================================================
        // 変数定義
        //=========================================================================
        SceneOneIf SceneIf = Cls_SceneOneIf;
        Obj_All ObjIf = Cls_ObjAll;

        //=========================================================================
        // 衝突した弾丸オブジェクトを非表示
        //=========================================================================
        int i_SetId = 0;
        i_SetId = int.Parse(this.name.Replace(Df_Bullet_Name, ""));
        ObjIf.BulletIf.SetBulletNd_End(i_SetId);
    }

    /************************************************************************
       メソッド       : SetBulletNd_End
       概要           : 弾丸消去処理
    ************************************************************************/
    public void SetBulletNd_End(int i_EndId)
    {
        // ぶつかったオブジェクトを非表示にする
        l_Obj_Object[i_EndId].SetActive(false);
    }

上記ソースで弾がぶつかった時に消去されるようになります。
「// 衝突判定」部分は、「未定義、メインキャラ」のタグでは消えない判定を行っています。
理由は、弾発射時に必ずキャラにぶつかってしまうからです。
このTAG判定部分を変更することで、弾が貫通するオブジェクトを作ったりできます。
「// 衝突した弾丸オブジェクトを非表示」では、
オブジェクト名から、何番目のプレハブを非表示にするのか判別する仕様です。


以上が、フリック角度に合わせて弾を発射する仕様です。
言いたいこと以外の部分がめちゃくちゃ多かったので異常な長さに・・・
昨日書き始めたのですが予想以上に書くことが多すぎて2日かかりました。
相手がどこまで知っている前提で書くのか。
この要素ってものすごい大事ですね・・・

【C#6】別スクリプトの関数・定数を簡潔に呼び出す方法

今回は、スプリクトファイルを跨いで定数などを呼び出すときの
コードを簡潔に書けるようになる方法を書いていきます。

目次

参照部分の簡潔化

例えば、「Test_A.cs」と「Test_B.cs」の2つがあり、
Test_Aに定義された定数を取得する時にどうするでしょうか?

  • Test_A.csの中身
    public class Test_A: MonoBehaviour
    {
        public const int Df_Prm = 10;
    }
  • Test_B.csの中身
    public class Test_B: MonoBehaviour
    {
        public void SetCaliculateText()
        {
            string str_Set="";
            //        ↓この部分に注目!!!
            str_Set = Test_A.Df_Prm ; 
        }
    }

このように取得すると思います。
今回の方法を使えば、「この部分に注目」と書かれている部分。
スプリクトをまたいだ時に記入する必要がある
参照部分を書かずに定数を取得することができます!
例でいえば、

            str_Set = Test_A.Df_Prm ; 

            str_Set = Df_Prm ; 

このように簡潔化できます。

いちいち全ての定数や関数に参照部分を書くのは
とても面倒くさくて見づらくなるので、とってもすっきりすると思います。

事前準備【C#6.0導入手順】

今回の方法を行うには、C#6.0のバージョンをUnityで使用できるようにしないといけません。
なので、たぶん「Unity2017以降」にしか実行できません・・・

設定方法を下図に記入しておきます。
「Fileタブ」→「Build Settings...項目クリック」→下図参照

f:id:apuridasuo:20190426184705p:plain
図:C#6.0導入方法
上記手順で準備完了です。
ちょっと古いと余計なエラーを生んでしまうので、
できれば最新のUnityに更新してから行ってください。

参照簡潔化方法

まずは呼ばれる側のスプリクトです。※上記の例でいえば「Test_A.cs」

f:id:apuridasuo:20190426185240p:plain
図:呼ばれる側スプリクト組み込み方法

// *-*-*-*-**-****-*-*-*-*-*-*-*-*-*-**-**-***-
//	NameSpace定義
// *-*-*-*-**-****-*-*-*-*-*-*-*-*-*-**-**-***-
namespace Common
{
    public class Common_Function : MonoBehaviour
    {
        // ■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□
        //
        //			関数
        //
        // ■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□
        /*************************************************************
           モジュール名: GetCaliculateTest
           概要        : 10加算して文字列にして返す
           引数        : int i_GetVal:計算する値
           戻り値      : string:計算後、文字列型にした値
        **************************************************************/
        public static string GetCaliculateTest(int i_GetVal)
        {
            //========================================================
            // 引数に10加算して文字列に変換
            //========================================================
            string str_Ret = "";
            str_Ret = (i_GetVal+10).ToString();
            return str_Ret;
        }
    }
}
// *-*-*-*-**-****-*-*-*-*-*-*-*-*-*-**-**-***-
//	NameSpace定義
// *-*-*-*-**-****-*-*-*-*-*-*-*-*-*-**-**-***-
namespace Common
{
    public class Common_Value : MonoBehaviour
    {
        // ■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□
        //
        //			変数定義
        //
        // ■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□
        //=====================================
        // 定数
        //=====================================
        public const string Df_Msg = "「Common_Function」で定義してます";
        public const int Df_Prm = 10;
    }
}

今回は、定数・変数をまとめるファイルと関数をまとめるファイルの
2種類のファイルを用意しました。
手順としては、class全体を「namespace 名前区間名」で囲むだけです。


次に、呼ぶ側の組み込み方法になります。

f:id:apuridasuo:20190426185842p:plain
図:呼ぶ側のスプリクトの記入方法

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using static Common.Common_Function;
using static Common.Common_Value;

public class SetText : MonoBehaviour
{
    //**********************************************
    // テキストオブジェクト
    //**********************************************
    public static Text Obj_Text;

    //**********************************************
    // シーン起動時
    //**********************************************
    void Start ()
    {
        //=========================================
        // オブジェクト初期設定
        //=========================================
        Obj_Text = GameObject.Find("Panel/Text").GetComponent<Text>();
        Obj_Text.text = Df_Msg; // 余分な文字を書かずに定数を取得できてる!
    }

    //**********************************************
    // ボタン押下時
    //**********************************************
    public void SetCaliculateText()
    {
        //=========================================
        // 別スクリプトの関数で計算!
        //=========================================
        Obj_Text.text = GetCaliculateTest(Df_Prm); 
        // ↑ 余分な文字を書かずに定数、関数を取得できてる!
    }
}

手順は簡単。
using static 名前空間名.クラス名(スプリクトファイル名);
この定義を記入するだけです。
これで、呼ばれる側のファイルで定義した関数や定数は
参照部分を簡略化して記入することができます!

通化したクラスを取得する時など、
参照部分を書かないといけないからクラス名を短めの名前にしよう!
なんて心配をしなくてもいいようになります。

UnityでC#6.0が使えるなんて知らなかった・・・

ぜひご活用ください!

【初心者向け】Spriteの管理・分割について

今回は、画像の管理方法です。
対象は、
1枚の画像を分割するやつやりたいけど、
参考サイトとかみてもピンとこないなぁ~

と思っている方限定です!
docs.unity3d.com
上記のサイトの意味がよく分からない方(私)が、
そういうことか!と思った時のお話になります。

Sprite=画像じゃない!

皆さんは、画像をオブジェクトに設定する時にどうしているでしょうか?
私は取り込んだ画像をそのままD&Dで設定することが多いです。
そのせいで、Sprite型に設定しているものは
取り込んだ画像を設定していると思ってました。
違いました!

f:id:apuridasuo:20190425143553p:plain
図:オブジェクトに画像設定時
実際は、上図の画像データを展開した先にあるやつが設定されています。
ではその中身は何なのか?
f:id:apuridasuo:20190425144106p:plain
図:画像データ詳細
画像をクリック→上図の「Sprite Editor」ボタンをクリック
すると、画像が新しいウィンドウで表示されます。
その画像の枠に線があることに注目してください!
その枠こそがSprite型の中身です!!
Spriteタイプの画像データは、
取り込んだ画像の、指定の範囲(青線で囲んだ部分)のみを切り取った画像なんです!
つまり、1枚の画像を分割するということは
画像内の青線の枠をいっぱい作って、下図のように
オブジェクトに設定するデータをたくさん作ろう!ということです
f:id:apuridasuo:20190425150401p:plain
図:画像を4分割に分割した場合

f:id:apuridasuo:20190425151326p:plain
図:分割された画像の設定例

ここまでの内容が、僕が画像分割を学んだ時にしっくりこなかった部分です。
分割しても画像データ1つだからどーなってんの?って思ってました・・・

画像の分割方法

実際の分割手順をご説明します。
やったことない人向けですので簡単な分割方法をそのまま載せます

  • 取り込んだ画像の初期設定

f:id:apuridasuo:20190425153621p:plain
図:画像分割方法①

  • 「Sprite Editor」での分割処理と結果

f:id:apuridasuo:20190425153456p:plain
図:画像分割方法②

以上になります。
初めに話した部分が、独学で適当に学んでいる私には
気づくのに時間がかかってしまいました。。。

分かるとめちゃくちゃ簡単。
かつ画像管理がやりやすいのでぜひご活用ください。

【VBA】ショートカットキー設定時の注意

こんにちは。
今回は、Excelの自作関数にショートカットキーを設定する際、
適当にやって躓いてしまった事を書いていきます。

ショートカットキー設定方法は2つある
  • その①:「開発」のタブから設定

f:id:apuridasuo:20190424172922p:plain
図:開発タブからのショートカットキー設定方法

上記の方法で、組み込みなしでショートカットキーを設定できます。
注意点は、
・キーを登録できるのは、引数・戻り値がないPublicな関数のみ
・モジュールファイルのファイル名(オブジェクト名)と、
 そのファイルの中の関数名を同じにすると「図中.③」の
 関数名の部分がめちゃくちゃ長くなる

  • その②:Workbookイベントを捕まえてマクロで設定

ブックを開いた時のイベント内で、
ショートカットキーを設定するマクロを組み込みます。
注意点は、ブックを閉じるときに
設定したショートカットキーを初期化させないと、
全てのExcelを閉じないとショートカットキーが残るということです。

' ブック起動時イベント
Private Sub Workbook_Open()
    ' ショートカットキー設定
    Application.OnKey "指定のキー", "指定の関数名"
End Sub
' ブック閉じる時イベント
Private Sub Workbook_BeforeClose(Cancel As Boolean)
    ' ショートカットキー解放
    Application.OnKey "指定のキー"
End Sub

※「指定のキー」部分に入力する定数は下記リンク参照
https://docs.microsoft.com/ja-jp/office/vba/api/excel.application.onkey

※「指定の関数名」部分は
”オブジェクト名.関数名”の書き方で記入

キー設定時の注意!

ここからが今日躓いたことなのですが、
その①の方法でキーを設定した場合、
エクスポートした後もショートカットキー情報が残る!!

ということです。。。

つまり、
モジュールファイルをエクスポートして別のブックにインポートした時
エクスポート前に「その①」の方法でキー設定を行っていると、
インポートしたブックの方にも自動的にキー設定されています。

なので、ショートカットキーを設定した覚えがないのに
勝手に関数が呼び出されてしまいます・・・

私は今回、マクロ形式ブックでその①方法でキー設定して、
アドイン形式のブックにインポートしてその②方法でキー設定した為
意図しないショートカットキーが潜んでいて気づくのに時間がかかりました・・・

その①の方法でキー設定を管理するのは簡単ですが、
エクスポートすることを考えるならその②で設定する方が
キー設定の管理ができるので断然おススメです!

Excelで出力したCSVファイルをUnityで読み込むまで

今回は、Excelで出力したファイルをUnityで読み込む方法です

Unityで読み込めるのは文字コードが「UTF-8」のファイル!

エクセルから、通常の方法でテキストファイルを出力すると、
文字コードは「UTF-8」にはなりません
したがって、Unityで読み込めないファイルとなってしまいます

Excelで、文字コードを指定してテキストファイルを
出力する方法をご紹介します。

ExcelからCSVファイル出力
  • 事前準備

ExcelVisualBasicからエディターを開く
「ツール」→「参照設定」をクリック
下記の画像を参考に「Microsoft ActiveX Data Objects 6.1 Library」にチェック

f:id:apuridasuo:20190409183759p:plain
図:参照追加方法

'*******************************************************************

'モジュール名   :OutFileData
'概要           :テキストファイル出力
'引数           :st_filename As String  ファイル名
'引数           :st_DefPath As String   パス
'引数           :st_Kaku As String        拡張子
'引数           :st_outdata As String   出力データ
'戻り値         :なし

'*******************************************************************
Public Sub OutFileData(st_filename As String, st_DefPath As String, st_Kaku As String, st_outdata As String)
    '==================================================================
    ' データ定義
    '==================================================================
    Dim st_PJName As String
    Dim st_filepass As String
    Dim st_foldaname As String
    Dim i_cnt_file As Integer
    Dim i_cnt_teigi As Integer
    Dim i_cnt_youso As Integer

    '==================================================================
    ' 保存先フォルダ設定
    '==================================================================
    If st_DefPath = "" Then
        st_filepass = ThisWorkbook.Path & "\"
    Else
        st_filepass = st_DefPath & "\"
    End If
    
    '==================================================================
    ' ファイル存在判定 ※既に存在してたら削除
    '==================================================================
    st_filename = st_filepass & st_filename & "." & st_Kaku
    If Dir(st_filename) <> "" Then
        Kill st_filename
    Else
    End If

    '==================================================================
    ' ファイル出力
    '==================================================================
    ' 変数定義
    Dim obj_OutFile As New ADODB.Stream
    Dim maxRow As Integer
    ' ファイル出力
    With obj_OutFile
        ' 書き込みモード設定
        .Type = adTypeText
        .Charset = "UTF-8"
        .LineSeparator = adCRLF
        .Open
        ' データ記入
        .WriteText st_outdata, adWriteChar
        ' 閉じる
        .SaveToFile st_filename, adSaveCreateNotExist
        .Close
    End With   
    Set obj_OutFile = Nothing    
End Sub
  • 呼び方例

Call OutFileData("ファイル名" , "C:\ファイルいれるフォルダ名", ".csv" , "ファイルの中身" )
上記の呼び方で、作成したデータを
文字コードを「UTF-8」のファイルとして出力することができます。

UnityでCSVファイル読み込み
  • Unityにファイルを設定

下図のように、「Resources」フォルダにExcelで出力したファイルを設定する

f:id:apuridasuo:20190409185625p:plain
図:出力したファイルを設定する場所

  • 取得コード
    /*************************************************************************
        クラス名    : ReadTempFile
        概要        : 中間ファイル読み込み
    *************************************************************************/
    public void ReadTempFile()
    {
        //====================================================================
        // 変数定義
        //====================================================================
        string str_Path = "QuizList"; // 設定したファイル名
        string str_SetOne = "";
        TextAsset fl_Read = new TextAsset();

        //====================================================================
        // ファイル読み込み
        //====================================================================
        // csvファイルを取得
        fl_Read = Resources.Load(str_Path, typeof(TextAsset)) as TextAsset;
        StringReader reader = new StringReader(fl_Read.text);
        // 1行ごとに処理
        while (reader.Peek() > -1)
        {
            // 1行データを変数に格納
            str_SetOne = reader.ReadLine();
            //====================================================================
            // ※以下で、機種固有の処理を行って1行データを取得
            //====================================================================
        }
    }

以上です。
Excelで中間ファイルを作成して、Unityで読み込むまでができる様になります。

今回僕はマッチ棒で計算式を作るアプリを作ったのですが、
計算式の数字をエクセルで管理することで、
出題する問題の管理がとても楽にできました。

Unityでゲームを作る方には必須の知識だと思います!