20210213のUnityに関する記事は7件です。

Unityの野球ゲームで簡単に変化球を実装する

Unityの野球ゲームで、投手に変化球を投げさせたい場合の簡易的な方法を書きました。

あくまで簡易的なので若干リアルさには欠けるかもしれません。

手順

PlaneとSphereを用意します。
Sphereとカメラの距離はある程度空けておきます。
653885a600eab7c7c73992f394624da7.png

ボールをど真ん中に投げてみる

まずはボールを真ん中に投げてみます。
SphereにRigidBodyコンポーネントを追加し、スクリプトは以下の通りで試してみます。
powerは1000ぐらい。

Ball.cs
public class BallTest : MonoBehaviour
{
    public Rigidbody _rb;
    public int power;
    Vector3 direction = Vector3.back;//カメラの方向に投げてみる

    void Start()
    {
        _rb.AddForce(direction * power);
    }
}

Image from Gyazo
powerは丁度いいですが、球が転がってしまったので力を与える方向(direction)を修正します。

    Vector3 direction = new Vector3(0,0.2f,-1.0f);

Image from Gyazo
これをど真ん中のストレートとします。

球に変化を与える

飛ばした球がColliderに当たった際に変化方向に力を加えるという方法でいきます。
以下のように空のオブジェクトを作成しBoxColliderを追加。ボールの軌道の被るように配置します。
isTrrigerはONにします。
Image from Gyazo

最初はスライダーをかけてみます。
Sphereオブジェクトのtagは"Ball"に変更しておきます。

ChangeCollider.cs
    int changePower = 100; //変化させる力
    Vector3 sliderDirection = new Vector3(1.0f, -1.0f, 0); //変化の方向

    private void OnTriggerEnter(Collider other)
    {
        if (other.gameObject.tag == "Ball")
        {
            Rigidbody ball_rb = other.gameObject.GetComponent<Rigidbody>();
            ball_rb.AddForce(sliderDirection * changePower);//ボールに力を加える
        }
    }


Image from Gyazo
なかなかいい感じですね。

Colliderの位置を前後させることで曲がり始めのタイミングをずらせます。
Z軸に力を加えるとブレーキをかけることもできます。

様々な変化をさせてみた

せっかくなので投手を準備して色々な変化球を試してみました。
directionをいじれば様々な変化球を実現できます。
ezgif.com-gif-maker.gif

投手のアセットはこちらを使用しました。

1d589c9b7008af5afc597af6df96509e.png

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

【Unity】SRPとBuilt-in Render Pipelineをランタイムで切り替える

はじめに

以前,Scriptable Render Pipelineを自作する記事を書きました.
【Unity】SRPを自作して独自の描画フローを構築する

この記事を書いている際に,Bilt-in Render Pipeline(以降Built-in)で作られている既存のプロジェクトの一部(インゲーム部分とか)にのみ自作SRPを適用することができれば便利だと思い方法を調べました.

方法

https://gist.github.com/togucchi/1b562b457482d4593c50f52ae75a84fa

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;

public class RuntimePipelineSwitcher : MonoBehaviour
{
    [SerializeField]
    private RenderPipelineAsset pipelineAsset;

    private RenderPipelineAsset renderPipelineAsset;

    void Update()
    {
        if(Input.GetKeyDown(KeyCode.S))
        {
            if (renderPipelineAsset == null)
            {
                renderPipelineAsset = pipelineAsset;
            }
            else
            {
                renderPipelineAsset = null;
            }

            GraphicsSettings.renderPipelineAsset = renderPipelineAsset;
        }
    }
}

結論, UnityEngine.Rendering 名前空間の GraphicsSettings.renderPipelineAsset を変更することで実現できます.
RenderPipelineAsset を継承した自作のPipelineAssetをセットします.また,nullを代入するとBuilt-in Render Pipelineに切り替わります.

// 独自のSRPに変更する
GraphicsSettings.renderPipelineAsset = renderPipelineAsset;

// Built-in に戻す
GraphicsSettings.renderPipelineAsset = null;

最後に

短くなりましたが以上です.簡単に描画フローを切り替えられるので,使い方によってはすごく便利かと思います.

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

Unity ゲーム実行中に任意のMeshFilterからNavMeshを生成する

目的

Unity実行中にシーン内に存在するMeshFilterからNavMeshを動的に生成し、NavMeshAgentに経路探索させます

とにかくコードが見たい人はこちら
https://github.com/lisearcheleeds/SimpleNavMesh/tree/master/Assets/Scripts/SimpleNavMesh

動機

UnityではNavMeshという経路探索AIが機能として実装されていますが、あまり使い勝手が良くないと思われてます。
シーンにBakeする必要があったり、GameObjectをstaticにする必要があったり、NavMeshAgentは速度と回転率を設定する必要があったり、デフォルトで勝手に移動し始めたり、まぁやっぱりなんか使い勝手悪い気がしてきました。使い勝手悪いです。
ただ、今回はその中の大きな要素の一つである「動的にNavMeshを生成するのがめんどくさい」という思い込みを取り除けると思います。

ちなみに、テラシュールブログ公式のリファレンスなどで「高レベルのコンポーネントを入手する」と称して同コンポーネントが紹介されているので、
「このUnity公式の拡張コンポーネントがないと実行時にベイク出来ないんだ」とちょっと前まで思ってましたがそんなことは無かったです。
煩雑な上にstatic変数で探索対象を管理していて、宗教上私には使えなかったので倦厭してましたが、ちゃんと読み解けばシンプルな使い勝手で良い感じになります。

Unity-Technologies/NavMeshComponents
https://github.com/Unity-Technologies/NavMeshComponents

解説

UnityEngine.AI

  • NavMeshBuildSource
    • ナビメッシュを生成する最小単位
    • shapeとかsizeとかtransformとか渡す。 MeshFilter と1:1
  • NavMeshData
    • 複数の NavMeshBuildSource を持つデータ
    • NavMeshAddNavMeshData() で渡す
      • ナビメッシュを実際に生成する
    • NavMeshBuilderUpdateNavMeshDataAsync()で渡す
      • ナビメッシュを実際に生成する
      • 前回の AddNavMeshData() もしくは UpdateNavMeshData() からの差分だけ更新する
  • NavMeshDataInstance
    • NavMeshData のInstance。そのナビメッシュが不要になったときにRemove()を呼ぶ
    • NavMeshAddNavMeshData() の返り値で渡される

SimpleNavMesh

https://github.com/lisearcheleeds/SimpleNavMesh/tree/master/Assets/Scripts/SimpleNavMesh
NavMeshController コンポーネントの IEnumerator GenerateNavMesh() を実行すると、
navMeshTargetParent 以下の全ての INavMeshTarget に対してNavMeshを生成します

  • INavMeshTarget
    • NavMeshTargetDataUpdateNavMeshTargetData() を持つインターフェイス
  • MeshFilterTarget
    • MeshFilter から NavMeshBuildSource を作るコンポーネント
    • INavMeshTarget を実装する
  • NavMeshController
    • 指定されたGameObjectのHierarchy配下の INavMeshTarget を集めてBoundsを計算して NavMeshBuilder.UpdateNavMeshDataAsync() とか叩くコンポーネント
  • NavMeshTargetData
    • NavMeshBuildSource とBoundsを持つデータ

出来なかったもの

高台から落下してショートカットするためにOffmeshLinkも動的に生成したかったんですがどうやるんですかね?
まぁそもそもあまり使わないほうが良い気がする

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

頭部伝達関数を使ったデモアプリをUnity WebGLで無理やり作った

何を作ったか

頭部伝達関数を使って音にエフェクトをかけ、ヘッドホンから出た音が頭の外側で鳴っているような感じで再生されるアプリを作りました。
また、手っ取り早く試せるようにWebGLで作成しました。
イヤホン・ヘッドホンを着けて聞いてみてください。
感じ方には個人差がありますが、頭の周りで音が鳴っているような感じがすると思います。

WebGLにるデモアプリ

作ったものはこちら。
HRTF-Demo-WebGL

上のリンクを開くと、下のような画面が表示されます。
image.png

使い方

  1. ヘッドホンを用意する
  2. 画面上をタッチする。タッチと同時に音が鳴る。(大きい音がするので注意)
  3. 緑の円がタッチしたところに表示される。この円はドラッグで動かせる。
  4. 緑の円の動きに少し遅れて赤い円が動く。
  5. 赤い円の方向に対応して鳴っている音の方向が変化する。
  6. 中央黄色い円を押すとサウンドがエフェクトなしで再生される
  7. 画面下側のボタンを押すと再生される音が変わる

(再生中にノイズが発生しますが仕様です。)

ソースコード

https://github.com/morishift/HRTF-Demo
Unity 2019.4.11f1

実装

音に対して頭部伝達関数と呼ばれるデータ(実際には方向に対応するインパルス応答)を使って、選択した方向から音が鳴っているようなエフェクトをかけています。以下の記事がわかりやすいです。
立体音響を動的に生成する

サウンドファイル

wavファイル、44100Hz,16bitモノラル
(サンプル曲はGaragebandで作成)

畳み込み計算

サウンドデータとインパルス応答との畳み込み計算に重畳加算法を使います。

頭部伝達関数データ

頭部伝達関数のデータとして、以下を使用しました。
元データには頭に対して上下方向の情報も含まれています。このアプリでは頭と同じ高さのデータのみを利用しています。
Bill Gardner and Keith Martin
MIT Media Lab
HRTF Measurements of a KEMAR Dummy-Head Microphone
https://sound.media.mit.edu/resources/KEMAR.html

無理やりなところ

Unity WebGLのサウンドに関する機能に制限があり、無理やりな事をやっています。
WebGL でオーディオの使用
Unity公式でアナウンスされているように、UnityWebGLではサウンドについては一部機能しか使えません。単純に音を再生するだけなら問題ありませんが、それ以上のことをやろうとしてもあんまり上手く行きません。(別途プラグインを導入すれば解決できるのかも)

AudioClipから波形データが取得できない

Unityではサウンドの波形データはAudioClipオブジェクトから取得できます。
しかしWebGLではそれができません。(AudioClip.GetData()が使えない。SetData()は使える。)
そのため、Unityプロジェクト内にバイナリデータとしてwavファイルを配置しておき、実行時に直接読んで波形データを取得しています。

AudioClipのストリーミング再生ができない

AudioClipでストリーミング再生ができれば音にエフェクトを加えながら再生させることもやりやすくなります。しかし、WebGLではサポートされていません。
そのため、1秒程度のAudioClipを連続的に生成・再生しています。
一つのAudioClipで再生するサウンドデータは32分割し、その分割した断片に方向に対応するインパルス応答を畳み込むようにしました。

音の方向を選択した直後に鳴っている音が変更されず、遅れて反映されるのはこれに起因します。また、AudioClipとAudioClipのつなぎ目のところでかなり大きいノイズが発生します。

既存の立体音響SDK

試したことはありませんが、Unityで使える立体音響SDKはすでにあります。
本気で立体音響の実装を考えるならこれらを試すのが良いと思います。
Audio Spatializer SDK
https://docs.unity3d.com/ja/current/Manual/AudioSpatializerSDK.html
Resonance Audio
https://resonance-audio.github.io/resonance-audio/develop/unity/getting-started

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

頭部伝達関数を使ったデモをUnity WebGLで無理やり作った

作ったもの

頭部伝達関数を使って音にエフェクトをかけ、ヘッドホンから出た音が頭の外側で鳴っているような感じで再生されるデモを作りました。
また、手っ取り早く試せるようにWebGLで作成しました。
イヤホン・ヘッドホンを着けて聞いてみると、頭の周りで音が鳴っているような感じがすると思います。

WebGLにるデモ

作ったものはこちら。
HRTF-Demo-WebGL

上のリンクを開くと、下のような画面が表示されます。
image.png

使い方

  1. 画面上をタッチする。タッチと同時に音が鳴る。(大きい音がするので注意)
  2. 緑の円がタッチしたところに表示される。この円はドラッグで動かせる。
  3. 緑の円の動きに少し遅れて赤い円が動く。
  4. 赤い円の方向に対応して鳴っている音の方向が変化する。
  5. 中央黄色い円を押すとサウンドがエフェクトなしで再生される
  6. 画面下側のボタンを押すと再生される音が変わる

(再生中にノイズが発生しますが仕様です。)

ソースコード

https://github.com/morishift/HRTF-Demo
Unity 2019.4.11f1

実装

音に対して頭部伝達関数と呼ばれるデータ(実際には方向に対応するインパルス応答)を使って、選択した方向から音が鳴っているようなエフェクトをかけています。以下の記事がわかりやすいです。
立体音響を動的に生成する

サウンドファイル

wavファイル、44100Hz,16bitモノラル
(サンプル曲はGaragebandで作成)

畳み込み計算

サウンドデータとインパルス応答との畳み込み計算をするわけですが、
定義通りの畳み込み計算をそのまま実行すると時間がかかりすぎます。
なので、重畳加算法を使います。

頭部伝達関数データ

頭部伝達関数のデータとして、以下を使用しました。
元データには頭に対して上下方向の情報も含まれています。このアプリでは頭と同じ高さのデータのみを利用しています。
Bill Gardner and Keith Martin
MIT Media Lab
HRTF Measurements of a KEMAR Dummy-Head Microphone
https://sound.media.mit.edu/resources/KEMAR.html

無理やりなところ

Unity WebGLのサウンドに関する機能に制限があり、無理やりな事をやっています。
WebGL でオーディオの使用
Unity公式でアナウンスされているように、UnityWebGLではサウンドについては一部機能しか使えません。単純に音を再生するだけなら問題ありませんが、それ以上のことをやろうとしてもあんまり上手く行きません。(別途プラグインを導入すれば解決できるのかも)

AudioClipから波形データが取得できない

Unityではサウンドの波形データはAudioClipオブジェクトから取得できます。
しかしWebGLではそれができません。(AudioClip.GetData()が使えない。SetData()は使える。)
そのため、Unityプロジェクト内にバイナリデータとしてwavファイルを配置しておき、実行時に直接読んで波形データを取得しています。

AudioClipのストリーミング再生ができない

AudioClipでストリーミング再生ができれば音にエフェクトを加えながら再生させることもやりやすくなります。しかし、WebGLではサポートされていません。
そのため、1秒程度のAudioClipを連続的に生成・再生しています。
一つのAudioClipで再生するサウンドデータは32分割し、その分割した断片に方向に対応するインパルス応答を畳み込むようにしました。

音の方向を選択した直後に鳴っている音が変更されず、遅れて反映されるのはこれに起因します。また、AudioClipとAudioClipのつなぎ目のところでかなり大きいノイズが発生します。

既存の立体音響SDK

Unityで使える立体音響SDKはすでにあります。
本気で立体音響の実装を考えるならこれらを試すのが良いと思います。
Audio Spatializer SDK
https://docs.unity3d.com/ja/current/Manual/AudioSpatializerSDK.html
Resonance Audio
https://resonance-audio.github.io/resonance-audio/develop/unity/getting-started

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

Unity + Arborで敵AIデータを使い回す方法 〜ビヘイビアツリー編〜

一つ前の記事ではArborのビヘイビアツリーを使うときに大事なポイントを紹介しました。
この記事では、ビヘイビアツリーにて敵AIロジックを使い回すための方法を解説します!

環境
Unity 2020.2.1f1
Arbor 3.7.8

どういうときに役立つの?

普通ゲームで作成する敵のロジックは複数になります。
Arborでは作成したビヘイビアツリーは、コンポーネントをコピー・ペーストすることで、グラフのコピーができます。
が、こうやってコピーされた敵AI達は、全てに影響する変更をしたくなった際に、コピペしたグラフを一つ一つ開いて修整せねばなりません。
これはツライ…
old_graph.png

そこで、今回紹介する方法を使えば、敵AIの行動単位で部品化して使い回せるようになります。
こうすると、一つの行動部品を修整すれば参照している敵全ての行動を修正することができます。
工数削減です!
new_graph.png

使い回すために使用するArborの機能

使い回す方法を説明する前に、必要なArborの機能を紹介します。
ざっとこんなところでしょうか。
(アクションとはみたいな基本的なことは説明しません。前回の記事を参照してください

  • 外部参照のビヘイビアツリーを再生するためのアクション:Sub Behaviour Tree Reference
  • 参照先のビヘイビアツリーに情報を渡すためのパラメーター

Sub Behaviour Tree Reference アクション

このアクションが使い回しのポイントです。
Sub Behaviour Tree Reference アクションは、外部のビヘイビアツリーを再生するためのアクションです。
「外部のビヘイビアツリー」とは言っていますが、ほとんどプレハブ化したビヘイビアツリーを使用するのが一般的かなと思います。
スクリーンショット 2021-02-10 19.37.41.png
これは、「SoloTargetCastAttack」というビヘイビアツリーがアタッチされたプレハブを読み込ませているSub Behaviour Tree Reference アクションです。
こちらの例ではプレハブを読み込ませていますが、他の方法(階層から取得など)を選択することもできるようです。
ちなみに、下にずらずら並んでいるのはパラメーターです。
こちらは後で触れるので覚えておくとよいです!

パラメーター

Arborにはパラメーターをとる手段がいくつか用意されていますが、今回扱うのはグラフに紐づくパラメーターです。
今回のパラメーターの用途は、使い回しする行動にパラメーターを与えて、挙動をある程度変える目的で使用します(例えば、攻撃力を変えるなど)。
スクリーンショット 2021-02-10 19.46.58.png
グラフのパラメーターは、Arbor Editorの左側で設定することができます。
+マークを押して型を選択、型名の横にパラメーター名を定義して、その下に値を設定できます。
上の例だと、Stringが型名、attack_nameがパラメーター名、突撃が設定された値ですね。

敵AIデータを使い回す方法

では実際に敵AIデータを使い回す方法を見ていきましょう。
手順はこんな感じです。

1. 部品化させたいAI行動を作成、プレハブ化する(必要であればパラメータを定義)
2. 敵A本体のビヘイビアツリーを作成する
3. 2.のビヘイビアツリー内にSub Behaviour Tree Reference アクションを組み込む
4. 3.に1.で作成したプレハブを設定する
5. 必要であれば3.にパラメータの設定を追加する

順番に見ていきましょう。

1. 部品化させたいAI行動を作成、プレハブ化する

まずはこんな感じで、BehaviourTreeコンポーネントがアタッチされただけのGameObjectを作成、プレハブ化しましょう。
スクリーンショット 2021-02-10 19.59.32.png
(画像だとPlay On StartとかUpdate Settingsとかいじってありますが、普通に使う分にはそのままの設定で良いと思います)

次に、このプレハブに設定されているビヘイビアツリーの中身を作成しましょう。
これが使いまわせる部品になります。
つまり、「SoloTargetCastAttack」という行動が敵A、敵B、敵Cにも参照させて使いまわせるようになります。
スクリーンショット 2021-02-10 20.04.51.png
今回はこんな感じにして、Sequencerに複数のアクションが紐づく構成にしています。
再利用しやすいように、分岐処理などは避けてシンプルに組みました。
メンテナンスのしやすさも考えて、あんまり複雑にしないほうがいいかなと思います。

ワンポイント:パラメータを追加する

パラメータは必要なければ特に追加しなくてもOKですが、再利用性を高めるためにもパラメータを使用して各値を外部から変更できるようにすると使いやすくなります。
スクリーンショット 2021-02-10 21.29.09.png
先ほどのグラフの一部です。
パラメータを定義して、アクションの各設定値に割り当てることができます。
今回は、「Enemy Action Cast Start」のSkillNameにパラメーターのattack_name(名前がよくないかも)、CastTimeにパラメーターのcast_timeが割り当たっています。

パラメーター名の右隣にある色のついた場所からドラッグ&ドロップで、アクションの設定値に割り当てることができます。
こうすることで、外部からこれらのパラメーターが設定された時に値が各アクションに反映されるようになり、例えば敵キャラによって攻撃力を変えるなどができるようになります。

ちなみに、このような使い方をしたときパラメーターに設定されている具体的な値は、デフォルト値扱いになり、外部からパラメーターとして渡されない場合に使用される値になります。

2. 敵A本体のビヘイビアツリーを作成する

この手順は単純にいつも通りビヘイビアツリーを対象のオブジェクト(敵)に対して作成すればOKです。

3. 手順2.のビヘイビアツリー内にSub Behaviour Tree Reference アクションを組み込む

とりあえずこんな感じにしました。
スクリーンショット 2021-02-10 21.44.03.png
(自作デコレーターをつけて、HP条件を満たしていないときは通常攻撃。みたいなニュアンスの設定にしてみましたよ

4. 手順3.に手順1.で作成したプレハブを設定する

最初の手順で作成したプレハブをSub Behaviour Tree Reference アクションに設定します。
ExternalBTにプレハブをドラッグ&ドロップで設定できます!
スクリーンショット 2021-02-10 21.49.12.png
こんな感じ。
パラメーターが必要なければこれで終わりです。
ゲームを実行して挙動を確かめてみましょう。
条件を満たしていれば、Sub Behaviour Tree Reference アクションに処理が流れて、設定したビヘイビアツリーに処理が移っていくことがわかると思います!

5. パラメータの設定を追加する

Sub Behaviour Tree Reference アクションの下の方の+マークから、パラメーターを設定することができます。
スクリーンショット 2021-02-10 21.53.01.png
最終的にこんな感じになりました。

これでAI行動を部品化して、さらにパラメータによって挙動を変えることができるようになりました!
異なる敵に同じ行動をさせたい場合は、2~5の手順を繰り返してSub Behaviour Tree Reference アクションにプレハブ化したビヘイビアツリーを設定しましょう。

今回部品化した行動に修正を加えたいときは、手順1.で作成したビヘイビアツリー一つを修正すれば全ての参照している敵に反映されます。

まとめ

敵AIデータを使い回すために必要なArborの機能、手順を解説しました。
これで悲しいコピペ地獄は避けられそうです。
自分はまだ試していませんが、ステートマシンの方でもおそらく同じような使い方ができると思うので、興味がある方は試してみると良さそうです。

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

原神やゼルダBoWのあの雰囲気を手っ取り早く出す方法

今更ながら原神を遊び始めました。
クオリティがエグくてビックリ!
グラフィックスがすごいすごいと言われていますが、そこよりも実装されてる仕様の量と質がすごい。
このクオリティを出そうと思ったらいったい何人月必要なのか想像するだけで目眩がします。

いつかこんなゲームが作りたい、でも過酷なデスマーチと、すごい勢いで増えていく開発費を想像したら作りたくない。
そんな人(自分)の為に手っ取り早く、見た目の雰囲気だけ、なんとなく原神やBoWに近づけるアプローチを置いておきます。

↓こんな感じを目指します。
スクリーンショット 2021-02-13 0.16.38.png

1. はじめに

まずはTerrainなどを使ったりアセットストアで買ったりして、木と草が生えてるフィールドを用意します。
この時点ではまだPS2クオリティ。

スクリーンショット 2021-02-13 0.06.23.png

2. ライトをそれっぽくする

DirectionalLightを黄色っぽくして、intensityを少し上げます。
スクリーンショット 2021-02-13 0.08.07.png

スクリーンショット 2021-02-13 0.08.14.png

3.影をつける

シャドウマップを適用して影をつけます。ソフトシャドウでもハードシャドウでもいいです。

スクリーンショット 2021-02-13 0.09.15.png

4.Ambientを調整

↑の時点では影が真っ暗でいかにも一昔前のPCゲームって感じです。
全体的に柔らかい雰囲気にするため、アンビエントライトの調整をします。
ここでAmbient ColorをHDRにすることで全体的に発光した雰囲気を作ることができます。
スクリーンショット 2021-02-13 0.11.38.png
スクリーンショット 2021-02-13 0.11.17.png
一気にそれっぽくなりました。

5.Fogで遠近感をかもしだす

スクリーンショット 2021-02-13 0.15.11.png

スクリーンショット 2021-02-13 0.14.46.png

遠くの木にフォグがかかりました。
静止画だとそんなに差がないですが、実機で動いている状態だとゲーム世界の空気感が変わります。

6. ゴッドレイとか置いてみる

雰囲気ものでゴッドレイとか置いてみるとなお良いです。

スクリーンショット 2021-02-13 0.16.38.png

この他、UnityのVolumeでビネットかけてみたり、風のエフェクト(白い線みたいなやつ)を飛ばしてみたり、色々工夫するとより雰囲気が出そうです。

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