20200123のC#に関する記事は7件です。

Unity3D/C#で簡単なリバーシ(オセロ)を作る!!~Part1~

Unity学習歴1ヶ月、そろそろ教本を手放して簡単なゲームを作ろうと思い、いろいろなものを参考にしながら挑戦してみました。
盤と石を作成し、ゲーム開始時に所定の位置に石を置くところまでが本Part内容です。

初投稿ですが、極力わかりやすい文になるよう努めます。

環境

・Macbook air
・Unity2019.2.17

盤と石を生成

・盤はCubeを座標(0,0.5,0)を起点に置き、xとzを1ずつ足して合計8×8個置きます。

・石はcylinderを2つ引っ付けたオブジェクトを1つ作り、盤に乗るようにCupsule Colliderを調節、座標をGame画面外に設定します。(PrefabにしてHiererchyから消すとなぜか後の処理で詰まりました)
626fc047f6d307fbaa9a9cd249e1967d.png

石の色をスクリプトで管理

    [SerializeField] Material material = null;

    Material topMaterial = null;
    Material backMaterial = null;

    [SerializeField] MeshRenderer topCylinder = null;
    [SerializeField] MeshRenderer backCylinder = null;
    public void SetState(StageManager.eStoneState state){
        bool isActive = (state != StageManager.eStoneState.EMPTY);
        {
            topCylinder.gameObject.SetActive(isActive);
            backCylinder.gameObject.SetActive(isActive);
        }
        SetColor(state == StageManager.eStoneState.WHITE);

    }
    public void SetColor(bool isWHITE)
    {
        if (topMaterial == null)
        {
            topMaterial = GameObject.Instantiate<Material>(material);
            backMaterial = GameObject.Instantiate<Material>(material);
            topCylinder.material = topMaterial;
            backCylinder.material = backMaterial;
        }
        topMaterial.color = isWHITE ? Color.white : Color.black;
        backMaterial.color = isWHITE ? Color.black : Color.white;
    }

スクリプトに適当な名前を付け(今回は"StoneManager")、上記のコードで石の色を管理します。

UnityのInspectorから"material","topCylinder","backCylinder"に先ほど作成した石のmaterialと、引っ付けてあった2つのcylinderを別々に貼り付けます。

7行目の"StageManager"は後述するスクリプトのクラス名なのでスルーしてください。

topMaterial.color = isWHITE ? Color.white : Color.black;
backMaterial.color = isWHITE ? Color.black : Color.white;

上の2行で、石の上が黒(白)のときに下が白(黒)になるように設定しています。

最初の石を生成

    public enum eStoneState//石の状態
    {
        EMPTY,//石が空
        WHITE,//石の上が白
        BLACK//石の上が黒
    };
    public GameObject firstStone;//置いた石
    private GameObject[,] firstStoneState = new GameObject[squareZ, squareX];//置いた石の座標
    private StoneManager[,] stoneManagers = new StoneManager[squareZ, squareX];//石のシリンダーとマテリアルの状態
    private eStoneState[,] stoneState = new eStoneState[squareZ, squareX];//石が空か白か黒か

    public Camera mainCamera;//カメラ取得用変数
    const int squareX = 8;//盤上のx(横)座標
    const int squareZ = 8;//盤上のz(縦)座標
    public int whiteScore;//白の枚数
    public int blackScore;//黒の枚数

    void Start()
    {
        mainCamera = GameObject.Find("Main Camera").GetComponent<Camera>();
        for (int i = 0; i < squareZ; i++)
        {
            for (int j = 0; j < squareX; j++)
            {
                // 石を64枚EMPTYで生成
                GameObject stone = GameObject.Instantiate<GameObject>(firstStone);
                StoneManager stoneManager = stone.GetComponent<StoneManager>();

                stone.transform.position = new Vector3(j, 1, i);
                firstStoneState[i, j] = stone;
                stoneManagers[i, j] = stoneManager;
                stoneState[i, j] = eStoneState.EMPTY;
            }
                stoneState[3, 3] = eStoneState.WHITE;
                stoneState[3, 4] = eStoneState.BLACK;
                stoneState[4, 3] = eStoneState.BLACK;
                stoneState[4, 4] = eStoneState.WHITE;
        }
        whiteScore = 2;
        blackScore = 2;
    }

新しくスクリプトを作成し(今回は"StageManager")、上記のコードを記述します。

startで繰り返し文を使い、各マスに石を置き、"StoneManager"の"SetState"で"SetActive()"をコントロールし、最初に置いておきたい石には予め"eStoneState"を"BLACK"か"WHITE"にしておくよう記述しておけば、ゲーム開始時に下記の画像のように石が配置されます。
d3c6fe70c0ff8058ec5539c5db8b45f5.png

Part2ではタップして石を置く処理とひっくり返す処理について記述します。

↓Part2↓
https://qiita.com/t-o2mt/items/7ec46c62107f965572c1

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Entity Framework Coreでクラスをメンバとして扱う方法

はじめに

日頃からEF Coreを多用しているじゅううんです。

ちょっとハマったことがあったので備忘程度に書いておきます。

コードファーストのマイグレーションが通らない

以下のようなクラスでコードファーストマイグレーションを行おうとしました。

public class Person
{
  public int Id { get; set; }
  public GeoCoordinate Position { get; set; }
}

public class GeoCoordinate
{
  public double Latitude { get; set; }
  public double Longitude { get; set; }
}

理想としてはこういうテーブルができてほしい

カラム名
Id int
Position_Latitude double
Position_Longitude double

> Add-Migration init
The entity type 'GeoCoordinate' requires a primary key to be defined.

できない

なぜ

  1. EFがGeoCoordinateを外部キーとして認識
  2. テーブルを作ろうとする
  3. [Key]属性がないので主キーが見つからないとエラーが出る

解決策

public class GeoCoordinate
{
  public double Latitude { get; set; }
  public double Longitude { get; set; }
}

これを

[Owned]
public class GeoCoordinate
{
  public double Latitude { get; set; }
  public double Longitude { get; set; }
}

とするだけです。

image.png

できました。

おわり。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Visual Studio2017でLivetがインストールできなくなっている件

仕事で使っているVisualStudio2017にLivetをインストールしようと
サイトhttps://www.nuget.org/packages/LivetCask/
からダウンロードしたファイルを実行したところ
「この拡張機能は、現在インストール済みの製品にはインストールできません。」と表示された。

ダウンロードサイトに飛ぶと「Work with」には「Visual Studio 2019」と書かれていて2017の記述がない。

あちこち調べたところサポート切れていた。

下記URLからダウンロードできる古いインストーラでインストールできた。
https://github.com/runceel/Livet/releases/tag/v2.2.0

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

unityでシーン間のフェードインフェードアウトを行う

初めに

このプログラムはTama-Labさんの記事を参考にして作りました、
>Unityでフェードイン/フェードアウトを実装する方法
完成した物のイメージ画像がこちらになります
以下gifあり
Image from Gyazo
完成したスクリプトがこちらになります

プログラム文

SceneFadeManager.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;

public class SceneFadeManager : MonoBehaviour
{
    //フェードアウト処理の開始、完了を管理するフラグ
    private bool isFadeOut = false;
    //フェードイン処理の開始、完了を管理するフラグ
    private bool isFadeIn = true;
    //透明度が変わるスピード
    float fadeSpeed = 0.75f;
    //画面をフェードさせるための画像をパブリックで取得
    public Image fadeImage;
    float red, green, blue, alfa;
    //シーン遷移のための型
    string afterScene;
    // Start is called before the first frame update
    void Start()
    {
        DontDestroyOnLoad(this);
        SetRGBA(0, 0, 0, 1);
        //シーン遷移が完了した際にフェードインを開始するように設定
        SceneManager.sceneLoaded += fadeInStart;
    }
    //シーン遷移が完了した際にフェードインを開始するように設定
    void fadeInStart(Scene scene,LoadSceneMode mode)
    {
        isFadeIn = true;
    }
    /// <summary>
    /// フェードアウト開始時の画像のRGBA値と次のシーン名を指定
    /// </summary>
    /// <param name="red">画像の赤成分</param>
    /// <param name="green">画像の緑成分</param>
    /// <param name="blue">画像の青成分</param>
    /// <param name="alfa">画像の透明度</param>
    /// <param name="nextScene">遷移先のシーン名</param>
    public void fadeOutStart(int red,int green,int blue,int alfa,string nextScene)
    {
        SetRGBA(red, green, blue, alfa);
        SetColor();
        isFadeOut = true;
        afterScene = nextScene;
    }
    // Update is called once per frame
    void Update()
    {
        if (isFadeIn == true)
        {
            //不透明度を徐々に下げる
            alfa -= fadeSpeed * Time.deltaTime;
            //変更した透明度を画像に反映させる関数を呼ぶ
            SetColor();
            if (alfa <= 0)
                isFadeIn = false;
        }
        if (isFadeOut == true)
        {
            //不透明度を徐々に上げる
            alfa += fadeSpeed * Time.deltaTime;
            //変更した透明度を画像に反映させる関数を呼ぶ
            SetColor();
            if (alfa >= 1)
            {
                isFadeOut = false;
                SceneManager.LoadScene(afterScene);
            }
        }
    }
    //画像に色を代入する関数
    void SetColor()
    {
        fadeImage.color = new Color(red, green, blue, alfa);
    }
    //色の値を設定するための関数
    public void SetRGBA(int r, int g, int b, int a)
    {
        red = r;
        green = g;
        blue = b;
        alfa = a;
    }
}

注意

作成したイメージをSceneFadeManagerをアタッチしたオブジェクトの子要素に設定しないと、ロード先のシーンで画像が見つからないので、エラーが発生してしまいます。


ただこのプログラム単体ではシーン遷移することが出来ないので、別プログラムから呼び出す必要があります。

呼び出し例

SceneChange.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SceneChange : MonoBehaviour
{
    GameObject ManageObject;
    SceneFadeManager fadeManager;
    // Start is called before the first frame update
    void Start()
    {
        //SceneFadeManagerがアタッチされているオブジェクトを取得
        ManageObject = GameObject.Find("ManageObject");
        //オブジェクトの中のSceneFadeManagerを取得
        fadeManager = ManageObject.GetComponent<SceneFadeManager>();
    }

    // Update is called once per frame
    void Update()
    {
        if(Input.GetMouseButtonDown(0))
        {
            //SceneFadeManagerの中のフェードアウト開始関数を呼び出し
            fadeManager.fadeOutStart(0, 0, 0, 0, "Scene2");
        }
    }
}

この↑のプログラムを別のオブジェクトにアタッチして使用すれば
トップにあるgif画像のようにフェードイン、フェードアウトができると思います。

最後に

こうした方が分かりやすい等のアドバイス頂けると嬉しいです!:grin:

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

golangにusingステートメントのようなものを作ってみた

この記事の内容は以下のバージョンで作りました。

> go version
go version go1.13 windows/amd6

きっかけ

  • ちょっとしたgoのコードを書いているときに、「リソースの開放が面倒だなぁ」とおもった。
  • .NETだと、usingステートメントですごくすっきりかけるから、似たようなものを作りたい。

通常のgoの場合

// 何らかのリソースを取得する
resource, err := hogehoge.GetResource()
// エラーがあるかどうか調べてちょめちょめする
if err != nil {
  // ちょめちょめ
}
// 解放忘れがないように、deferを定義
defer resource.Close()

// ようやく本来やりたい処理

みたいな感じになる。本来やりたい処理を書き始めるまでいつまでかかるんだ・・・
という感じ

今回作ったもの

type IDispose interface {
    open() error
    dispose()
}

func Using(disposable IDispose, fn func(resource interface{}) error) (err error) {
    if err = disposable.open(); err != nil {
        return err
    }
    defer disposable.dispose()
    if err = fn(disposable); err != nil {
        return err
    }
    return nil
}

IDisposeな構造体と関数を受け取り、Open/実行/Closeまでを自動的にやってくれるやつ。

使い方

IDisposeな構造体を用意

type DisposableStruct struct {
  hogeResouce *hoge.HogeResource
}

func (s *DisposableStruct) open() error {
  return nil
}

func (s *DisposableStruct) dispose() {
  // リソースを破棄する処理
  s.hogeResouce.Close()
}

実行部

err := Using(&DisposableStruct{hogeResouce: nil}, func(r interface{}) error {
  // castする必要がある。イケてない部分1
  hoge := r.(*DisposableStruct)
  // ここに本来やりたい処理を書く

  // 戻り値はエラーのみ。イケてない部分2
  return nil
})

最後に

  • ジェネリクスが使えれば、もっとかっこよくかけるんだけど・・・
  • golangでこんな無駄なことしてんじゃねぇよみたいに言われるかもしれないけど、気にしない。
  • もっといい感じの実装がある場合はコメント欄で指摘をお待ちしております!
  • 実際にはステートメントではなくただの関数です(タイトル詐欺ですまんな)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

cscの作法 その39

概要

cscの作法、調べてみた。
chartやってみた。

環境

.NETFramework4.8

写真

image.png

コンパイル

csc chart1.cs /r:System.Windows.Forms.DataVisualization.dll

サンプルコード

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Windows.Forms.DataVisualization.Charting;

namespace myapp {
    class ChartSample1 {
        public partial class form1: Form {
            private Chart chart;
            public form1() {
                this.Size = new Size(400, 400);
                this.Load += new System.EventHandler(this.form1_Load);
            }
            private void form1_Load(object sender, EventArgs e) {
                this.chart = new Chart();
                chart.ChartAreas.Clear();
                chart.Titles.Clear();
                chart.Series.Clear();
                ChartArea area = new ChartArea("area1");
                Title title = new Title("title1");
                title.DockedToChartArea = "area1";
                Series series1 = new Series();
                Series series2 = new Series();
                series1.ChartType = SeriesChartType.Line;
                series2.ChartType = SeriesChartType.Line;
                for (int i = 0; i < 360; i++)
                {
                    series1.Points.AddXY(i, Math.Sin(i * Math.PI / 180.0));
                    series2.Points.AddXY(i, Math.Cos(i * Math.PI / 180.0));
                }
                series1.ChartArea = "area1";
                series2.ChartArea = "area1";
                chart.ChartAreas.Add(area);
                chart.Titles.Add(title);
                chart.Series.Add(series1);
                chart.Series.Add(series2);
                this.Controls.Add(this.chart);
            }
        }
        static void Main() {
            Application.Run(new form1());
        }
    }
}




以上。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【UnityEditor拡張】AudioClipを司る内部ユーティリティ "UnityEditor.AudioUtil" のラッパーを作る

UnityEditor.AudioUtilとは

エディタ上でAudioClipを扱うための内部APIです。
リファレンスは存在しませんが、その全貌はここで見ることができます。
全貌と言ってもほぼexternなのでメソッド名や型しか分かりませんが。

使うと何ができるの?

AudioSourceを使わずに、Editor上でAudioClipを再生できます。
AudioClipを選択したときにInspector下部にプレビューとしてサウンドプレイヤーが表示されますが、あんな感じのGUIを自前定義のカスタムInspectorやEditorWindow上に構築できるわけです。

私の場合、AudioClip(のラッパークラス)にこんなカスタムPropertyDrawerを定義するのに使いました。
image.png

準備

内部APIなので、当然普通に呼ぶことはできません。Reflectionを使って無理やり呼び出します。

メソッド名列挙

ここを見てpublicメソッドの名前を全部列挙します。
列挙型管理なのは、コード補完が効いて扱いやすいため。タイポ怖い。

ついでに使うnamespaceも全て書いておきます。

using System;
using UnityEditor;
using UnityEngine;
using System.Reflection;
using System.Collections.Concurrent;
using System.Linq;
using System.Linq.Expressions;

public static class InternalAudioUtil
{
    enum Method
    {
        PlayClip,
        StopClip,
        PauseClip,
        ResumeClip,
        LoopClip,
        IsClipPlaying,
        StopAllClips,
        GetClipPosition,
        GetClipSamplePosition,
        SetClipSamplePosition,
        GetSampleCount,
        GetChannelCount,
        GetBitRate,
        GetBitsPerSample,
        GetFrequency,
        GetSoundSize,
        GetSoundCompressionFormat,
        GetTargetPlatformSoundCompressionFormat,
        GetAmbisonicDecoderPluginNames,
        HasPreview,
        GetImporterFromClip,
        GetMinMaxData,
        GetDuration,
        GetFMODMemoryAllocated,
        GetFMODCPUUsage,
        IsTrackerFile,
        GetMusicChannelCount,
        GetLowpassCurve,
        GetListenerPos,
        UpdateAudio,
        SetListenerTransform,
        HasAudioCallback,
        GetCustomFilterChannelCount,
        GetCustomFilterProcessTime,
        GetCustomFilterMaxIn,
        GetCustomFilterMaxOut,
    }
}

以降のコードは全てこのInternalAudioUtilクラス内に記述します。

メソッド取得・コンパイル・キャッシュ

ReflectionとExpressionをこねくり回します。
全部public staticだし、オーバーロードの曖昧性がないので楽でいいですね。
Expression構築については、neuecc先生の手法を参考にしています。

    //AudioUtil型
    static readonly Type tAudioUtil = typeof(Editor).Assembly.GetType("UnityEditor.AudioUtil");
    //コンパイル済みメソッドのキャッシュ
    static readonly ConcurrentDictionary<Method, Func<object[], object>> 
        compiled = new ConcurrentDictionary<Method, Func<object[], object>>();

    //キャッシュからメソッドを取得する。コンパイル済みでなければコンパイルしてキャッシュし、それを返す。
    static Func<object[], object> GetOrCompile(Method method)
    {
        return compiled.GetOrAdd(method, _m =>
        {
            //キャッシュが存在しなければここに来る

            //MethodInfo取得
            var m = tAudioUtil.GetMethod(_m.ToString(), BindingFlags.Static | BindingFlags.Public);

            //voidメソッドのためのreturn先ラベルを定義
            var voidTarget = Expression.Label(typeof(object));

            //引数はobject[]
            var args = Expression.Parameter(typeof(object[]), "args");
            //MethodInfoのパラメータの型に引数をキャストするExpressionの束
            var parameters = m.GetParameters()
                .Select((x, index) =>
                    Expression.Convert(
                        Expression.ArrayIndex(args, Expression.Constant(index)),
                    x.ParameterType))
                .ToArray();
            //式木構築
            var lambda = Expression.Lambda<Func<object[], object>>(
                m.ReturnType == typeof(void)
                    //voidメソッドの場合、ブロックにしてreturn default(object)する必要がある
                    ? (Expression)Expression.Block(
                        Expression.Call(null, m, parameters),
                        Expression.Return(voidTarget, Expression.Default(typeof(object))),
                        Expression.Label(voidTarget, Expression.Constant(null))
                    )
                    //返り値がある場合はCallして結果をobjectにキャストするだけ
                    : Expression.Convert(
                        Expression.Call(null, m, parameters),
                        typeof(object)),
                args);

            //コンパイルしてキャッシュしつつ返す
            return lambda.Compile();
        });
    }

呼ぶ

new object[]{...}やらキャストやらを毎回書くのは嫌なので、適当に中間メソッドを作っておいて、

    static TRet Call<TRet>(Method method) 
        => (TRet)GetOrCompile(method).Invoke(null);
    static TRet Call<T0, TRet>(Method method, T0 arg0) 
        => (TRet)GetOrCompile(method).Invoke(new object[] { arg0 });
    static TRet Call<T0, T1, TRet>(Method method, T0 arg0, T1 arg1)
        => (TRet)GetOrCompile(method).Invoke(new object[] { arg0, arg1 });
    static TRet Call<T0, T1, T2, TRet>(Method method, T0 arg0, T1 arg1, T2 arg2)
        => (TRet)GetOrCompile(method).Invoke(new object[] { arg0, arg1, arg2 });
    static TRet Call<T0, T1, T2, T3, TRet>(Method method, T0 arg0, T1 arg1, T2 arg2, T3 arg3)
        => (TRet)GetOrCompile(method).Invoke(new object[] { arg0, arg1, arg2, arg3 });

    static void Call(Method method)
        => GetOrCompile(method).Invoke(null);
    static void Call<T0>(Method method, T0 arg0) 
        => GetOrCompile(method).Invoke(new object[] { arg0 });  
    static void Call<T0, T1>(Method method, T0 arg0, T1 arg1)
        => GetOrCompile(method).Invoke(new object[] { arg0, arg1 });
    static void Call<T0, T1, T2>(Method method, T0 arg0, T1 arg1, T2 arg2)
        => GetOrCompile(method).Invoke(new object[] { arg0, arg1, arg2 });
    static void Call<T0, T1, T2, T3>(Method method, T0 arg0, T1 arg1, T2 arg2, T3 arg3)
        => GetOrCompile(method).Invoke(new object[] { arg0, arg1, arg2, arg3 });

以下のようにそれぞれのメソッドに対応する公開APIを定義します(これが一番疲れました)。
[Obsolete]を付けているPlayClipについては後述します。

    public static void PlayClip(AudioClip clip) => Call(Method.PlayClip, clip, 0, false);
    [Obsolete("The parameters <startSample> and <loop> are not working")]
    public static void PlayClip(AudioClip clip, int startSample, bool loop) => Call(Method.PlayClip, clip, startSample, loop);
    public static void StopClip(AudioClip clip) => Call(Method.StopClip, clip);
    public static void PauseClip(AudioClip clip) => Call(Method.PauseClip, clip);
    public static void ResumeClip(AudioClip clip) => Call(Method.ResumeClip, clip);
    public static void LoopClip(AudioClip clip) => Call(Method.LoopClip, clip);
    public static bool IsClipPlaying(AudioClip clip) => Call<AudioClip, bool>(Method.IsClipPlaying, clip);
    public static void StopAllClips() => Call(Method.StopAllClips);
    public static float GetClipPosition(AudioClip clip) => Call<AudioClip,float>(Method.GetClipPosition, clip);
    public static int GetClipSamplePosition(AudioClip clip) => Call<AudioClip, int>(Method.GetClipSamplePosition, clip);
    public static void SetClipSamplePosition(AudioClip clip, int iSamplePosition) => Call(Method.SetClipSamplePosition, clip, iSamplePosition);
    public static int GetSampleCount(AudioClip clip) => Call<AudioClip, int>(Method.GetSampleCount, clip);
    public static int GetChannelCount(AudioClip clip) => Call<AudioClip, int>(Method.GetChannelCount, clip);
    public static int GetBitRate(AudioClip clip) => Call<AudioClip, int>(Method.GetBitRate, clip);
    public static int GetBitsPerSample(AudioClip clip) => Call<AudioClip, int>(Method.GetBitsPerSample, clip);
    public static int GetFrequency(AudioClip clip) => Call<AudioClip, int>(Method.GetFrequency, clip);
    public static int GetSoundSize(AudioClip clip) => Call<AudioClip, int>(Method.GetSoundSize, clip);
    public static AudioCompressionFormat GetSoundCompressionFormat(AudioClip clip) => Call<AudioClip, AudioCompressionFormat>(Method.GetSoundCompressionFormat, clip);
    public static AudioCompressionFormat GetTargetPlatformSoundCompressionFormat(AudioClip clip) => Call<AudioClip, AudioCompressionFormat>(Method.GetTargetPlatformSoundCompressionFormat, clip);
    public static string[] GetAmbisonicDecoderPluginNames() => Call<string[]>(Method.GetAmbisonicDecoderPluginNames);
    public static bool HasPreview(AudioClip clip) => Call<AudioClip, bool>(Method.HasPreview, clip);
    public static AudioImporter GetImporterFromClip(AudioClip clip) => Call<AudioClip, AudioImporter>(Method.GetImporterFromClip, clip);
    public static float[] GetMinMaxData(AudioImporter importer) => Call<AudioImporter, float[]>(Method.GetMinMaxData, importer);
    public static double GetDuration(AudioClip clip) => Call<AudioClip, double>(Method.GetDuration, clip);
    public static int GetFMODMemoryAllocated() => Call<int>(Method.GetFMODMemoryAllocated);
    public static float GetFMODCPUUsage() => Call<float>(Method.GetFMODCPUUsage);
    public static bool IsTrackerFile(AudioClip clip) => Call<AudioClip, bool>(Method.IsTrackerFile, clip);
    public static int GetMusicChannelCount(AudioClip clip) => Call<AudioClip, int>(Method.GetMusicChannelCount, clip);
    public static AnimationCurve GetLowpassCurve(AudioLowPassFilter lowPassFilter) => Call<AudioLowPassFilter, AnimationCurve>(Method.GetLowpassCurve, lowPassFilter);
    public static Vector3 GetListenerPos() => Call<Vector3>(Method.GetListenerPos);
    public static void UpdateAudio() => Call(Method.UpdateAudio);
    public static void SetListenerTransform(Transform t) => Call(Method.SetListenerTransform, t);
    public static bool HasAudioCallback(MonoBehaviour behaviour) => Call<MonoBehaviour, bool>(Method.HasAudioCallback, behaviour);
    public static int GetCustomFilterChannelCount(MonoBehaviour behaviour) => Call<MonoBehaviour, int>(Method.GetCustomFilterChannelCount, behaviour);
    public static int GetCustomFilterProcessTime(MonoBehaviour behaviour) => Call<MonoBehaviour, int>(Method.GetCustomFilterProcessTime, behaviour);
    public static float GetCustomFilterMaxIn(MonoBehaviour behaviour, int channel) => Call<MonoBehaviour, int, float>(Method.GetCustomFilterMaxIn, behaviour, channel);
    public static float GetCustomFilterMaxOut(MonoBehaviour behaviour, int channel) => Call<MonoBehaviour, int, float>(Method.GetCustomFilterMaxOut, behaviour, channel);

これで準備OKです。

使用上の注意

大体メソッド名と引数名から予測できる通りの挙動をしますが、いくつかかなりヤバめの注意点があります。

PlayClip()の第2引数以降は指定しても何も起きない

PlayClip(AudioClip clip, int startSample, bool loop)
見るからに再生開始位置とループ有無を指定できそうですが、できません。
一応全て引数に取れるメソッドも定義していますが、この理由により[Obsolete]を付けています。

なお、代わりにSetClipSamplePosition()LoopClip()を同時に使うことで所望の挙動が得られます。

複数のAudioClipを再生すると、最後に再生したもの以外停止不能になる

そんなバカな、StopAllClips()があるじゃないか。私もそう思いました。

StopAllClips()は、最後に再生したclipを停止します。

clipを明示的に指定しなくてもいいのでとても便利ですね!!!!!

この状態になると、再生が終了するか、Unityを再起動するまで他のAudioClipは停止できません。
そのため、クロスフェードのプレビューとかは無理です。残念。

全てのメソッドの動作確認はしていない

きっとまだ罠があるので、ぜひ踏み抜いて教えてください。

参考リンク

AudioUtilクラス
https://github.com/Unity-Technologies/UnityCsReference/blob/master/Editor/Mono/Audio/Bindings/AudioUtil.bindings.cs

neuecc先生によるReflectionの高速化手法紹介
http://neue.cc/2014/01/27_446.html

Rtyper氏が作った旧AudioUtilのラッパー(とその問題点(上述したものと同じ))
https://forum.unity.com/threads/reflected-audioutil-class-for-making-audio-based-editor-extensions.308133/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む