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

【爆速メモ】unityやっているけれど、clusterやってみたいとき

爆速とはいえども、やはり公式最強。
ブラウザで、

公式 すぐ出来る!ワールド作りの始め方
https://creator.cluster.mu/2020/02/27/helloworld/

を開きつつ、
・unity(2019でバージョン指定あり!!)
・Git
をインストールすべし。(詳しくは上記の公式を見たほうが早いです。わかりやすいです)

cluster用のワールドを練習で作りたいとか、初めてアップロードしたいとかだったら、
クリエイターキットのパッケージをインストールしたり、
そもそもunityのインストールでアドオン多かったりするので、
作業時間は多めに見積もるべし。
(どのくらいか? オフ日に10時くらいにまったり始めて、1時間は環境設定であれこれしていると思ったほうが良い)

あとmacの場合は、結局はxcodeが必要なので、unityやっている上で
「xcodeは容量食うから普段消してるぜ!!」という方は、覚悟してください。

ワールドのアップロードは、ぼーっと待っているだけだと時間がかかるので、
switchでデッドセルズやったり、スイーツ食べたりしていてください。

公式サイト
https://cluster.mu/

公式 クリエイターズガイド
https://creator.cluster.mu/

公式 すぐ出来る!ワールド作りの始め方
https://creator.cluster.mu/2020/02/27/helloworld/

そんなこんなで、今日息子と作ったワールドがこちら。練習用です。
https://cluster.mu/w/0cec517b-b4ba-4246-9e7f-f39989cf3b53

スクリーンショット 2020-03-16 16.59.34.png

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

[Unity]PostEffectにUIのスプライトを利用する

PostEffectを掛ける際にUIのスプライト(テクスチャでもいい)を利用したかったメモ。
あまりにもニッチな内容だけど、なかなか苦労したのでメモ残しておきます。

何が問題なのか

ちょっとググれば出るけど、PostEffectを掛けるシェーダーを書く際はまずカメラに処理用のスクリプトを書く。
んで、処理中にRenderTextureを用意して、画面に映ったものをシェーダーで処理してRenderTextureに映す。
シェーダーで描画を実行するにはOnRenderImageで値を渡してGraphics.Blitで描画するんだけど、そのとき_MainTexとして画面のレンダリングイメージが送られる。

_MainTexのUVは画面全体を0-1として送られるので、UIの領域を参照するには画面全体からどうにかして算出しなければならない。

コード

とりあえず上手く行った(っぽい)コード。
説明の必要部分だけ抜粋します。
UIのポジション変換はこちらを参考にさせて頂きました。
http://karanokan.info/2019/02/03/post-2213/

UVは左下から0-1なので、そこに上手く合わせられるようにする。

スクリプト側

public GameObject imageobj = null;
public Texture AddTexture = null;
public Material mat = null;
private RectTransform trfTarget = null;
private RectTransform rectCanvas = null;
private Vector2 ressize = new Vector2(0f,0f);
private Vector2 piv = new Vector2(0f,0f);
private Vector4 rct;

void OnValidate(){
    if(imageobj != null){
        AddTexture = imageobj.GetComponent<Image>().sprite.texture;
        trfTarget = imageobj.GetComponent<RectTransform>();
        var top = imageobj.GetComponent<RectTransform>().root;
        rectCanvas = top.GetComponent<RectTransform>();
        ressize = new Vector2(rectCanvas.rect.width,rectCanvas.rect.height);
        piv = rectCanvas.pivot;
    }
}

private void OnRenderImage(RenderTexture source, RenderTexture dest){
    mat.SetTexture("_AddTex", AddTexture);
    mat.SetVector("_Rect", rct);
    Graphics.Blit(source, dest, mat);
}

private void Update(){
    //World座標をUI座標へ変換
    Vector2 spos = RectTransformUtility.WorldToScreenPoint(Camera.main, trfTarget.position);
    Vector2 pos;
    RectTransformUtility.ScreenPointToLocalPointInRectangle(rectCanvas, spos, Camera.main, out pos);
    //左下を計算上のpivotにする
    var imgsize = new Vector2(trfTarget.rect.width * trfTarget.localScale.x,trfTarget.rect.height * trfTarget.localScale.y);
    var imgpiv = trfTarget.pivot;
    var imgpos = new Vector2(pos.x - (imgsize.x * imgpiv.x),pos.y - (imgsize.y * imgpiv.y));
    //CanvasのRectTransformから解像度とピボットを取り、左下0にする
    var resetpos = new Vector2(imgpos.x + (ressize.x * piv.x),imgpos.y + (ressize.y * piv.y));
    //リニア化 (minx,miny,解像度xに対する横の割合,解像度yに対する高さの割合)
    rct = new Vector4(resetpos.x / ressize.x, resetpos.y / ressize.y, imgsize.x / ressize.x, imgsize.y / ressize.y);
}

シェーダー側

範囲が分かりやすいようにスプライト部分を黒で塗りつぶしてます。
取得したUIのRect情報でいかにしてUVをずらすか。

Shader "UIPostEffect"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _AddTex ("AddTexture", 2D) = "white" {}
    }
    SubShader
    {
        Cull Off ZWrite Off ZTest Always

        Pass
        {
            CGPROGRAM
            #pragma vertex vert_img
            #pragma fragment frag

            #include "UnityCG.cginc"

            sampler2D _MainTex;
            sampler2D _AddTex;
            float4 _Rect;

            fixed4 frag (v2f_img i) : SV_Target
            {
                fixed4 col = tex2D(_MainTex, i.uv);
                float2 uv = float2((i.uv.x-_Rect.x)/_Rect.z, (i.uv.y-_Rect.y)/_Rect.w);
                fixed4 addcol = tex2D(_AddTex, uv);
                return lerp(col,fixed4(0,0,0,1),addcol.a);
            }
            ENDCG
        }
    }
    Fallback Off
}

正直上手く説明できない

数学苦手なんでツッコミあったらよろしくお願いします。

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

強制横スクロールアクションゲームを作る_2 ~強制横スクロール、三段ジャンプ~

はじめに

ゲームを作る練習として初心者がUnityを使って、強制横スクロールアクションゲームを作るお話です。出来るだけ細かくどのような作業をしたか書いていくつもりです。今回は以下の続きです。

*強制横スクロールアクションゲームを作る_1 ~Unityで画像をObjectとして使う,Colliderをつける~

仕様

自転車に乗った人(=Player)が穴に落ちないようにジャンプして進むシンプルなゲームです。

  • Playerは右方向に等速で進んでジャンプは3段ジャンプまで
  • 床がランダムに生成されて、大きすぎる穴ができないように
  • Playerが床の横側にぶつかった時、画面下に落ちた時にゲームオーバー
  • 右上に進んだ距離を表示
  • ゲームオーバーになるとゲーム終了の画面でその距離を表示

開発

ものすごくシンプルな仕様を元にゲームを作り始めました。
今回使用したUnityのバージョンは2019.2.6f1で、2Dのプロジェクトにしました。

3.Playerの移動(強制右スクロール)

まずProjectタブの中にあるAssets内で
右クリック --> Create --> C# Script
とした後、PlayerControllerと名前をつけておきます。これをダブルクリックして編集します。

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

public class PlayerController : MonoBehaviour
{
    private float STEP = 5.0f;

    void Update()
    {
        //右向きに等速で進む
        this.transform.position += new Vector3(STEP * Time.deltaTime, 0, 0);
    }
}

このスクリプトをHierarchy内のPlayerにドラックでアタッチすることでPlayerが右向きに直進します。

続いて、カメラがPlayerを追跡するようにHierarchy内のMain Cameraに新しい以下のスクリプトをアタッチしておきます。
ついでにCameraのInspectorの中にあるBackgroundを編集して背景の色を好きな色にしておきましょう。

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

public class CameraController : MonoBehaviour
{
    private float px;

    void Update()
    {
        px = GameObject.Find("Player").transform.position.x;
        this.transform.position = new Vector3(px, 0, -20.0f);
    }

}

これで右向きにPlayerが等速で移動できます。床を長くして確かめてみてください。
強制スクロールプレイヤー圧縮.gif

4.Playerのジャンプ

先ほど作ったPlayerControllerにこのスクリプトを追加することで、以下の実装ができます。

  • クリックでジャンプ
  • ジャンプは空中でも可能だが、三段ジャンプまで
  • ジャンプ力は1回目2回目3回目が同様になるように
public class PlayerController : MonoBehaviour
{
    Rigidbody2D rigid2D;
    private Vector2 velocity;
    public float JUMP_POWER;
    private int MAX_JUMP_COUNT = 3;
    private int jumpCount = 0;
    private bool jump = false;
    [SerializeField] ContactFilter2D filter2d;

    void Update()
    {
        //クリックでJumpをtrueにしてジャンプ、
        //空中で3回以上ジャンプしてたらジャンプできない
        if (jumpCount < MAX_JUMP_COUNT && Input.GetMouseButtonDown(0))
        {
            jump = true;
        }
        if (jump)
        {
            // 速度をゼロにして、2回目のジャンプも1回目と同じ挙動にする
            this.rigid2D = GetComponent<Rigidbody2D>();
            rigid2D.velocity = Vector2.zero;

            //ジャンプさせる
            this.rigid2D.AddForce(transform.up * JUMP_POWER);

            //ジャンプ回数をカウント
            jumpCount++;

            jump = false;
        }
        // 床に着地したか判定し、床に接地したら3段ジャンプできるようにする
        if (GetComponent<Rigidbody2D>().IsTouching(filter2d))
        {
            jumpCount = 0;

        }
    }
}

InspectorでPlayerControllerのスクリプトを開いて、publicにしておいたJUMP_POWERの大きさを調整します。合わせてPlayerのRigidbody 2Dの中にあるMass(重さ)を調整します。

ちなみに速度を0にする部分は非常に大事です。
AddForceというのは力を加える関数なので、物理法則で速度を決定してしまいます。Playerがどんな速度で空中にいても同じ空中ジャンプをキメるためには、AddForceをする前にPlayerの速度を0にしておく必要があります。

また上向きのジャンプを安定させるためにPlayerオブジェクトを選択して、Inspector内のRigidbody 2DコンポーネントにあるConstraintsというところで、Freeze RotaionのZにチェックを入れました。こうすることでPlayerの上向きの力は常に画面に対して上向きの力になり、ジャンプが安定します。

接地判定は以下を参考にしました。

ジャンプ圧縮.gif

まとめ

自転車に乗った人が右向きに強制スクロールで進んで三段ジャンプできるようにしました。

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

UniRxでボタンから離したときに発火したいが、押している間にボタンの領域から離れたら無効にしたい時

要件

ボタン押したタイミングではまだ発火せず、離したタイミングでOnNextを送ってほしい。

ただし、押しながらボタンの領域からスワイプのようにずらしていって、最終的にボタンを離した場所がボタンの上でなければ、OnNextは送らないでほしい。

解決

var btnPressStream = btn.OnPointerClickAsObservable();

P.S.

もし最終的に離れた場所がボタン上でなくてもよければ、 OnPointerUpAsObservable() を使う。

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

[Unity] 不要なパーミッションを削除する話

アプリをアップロードしようとしたら,こんな警告が出てしまった.
image.png

意図してないところで,READ_PHONE_STATEの権限がついてしまっていたらしい.警告なので無視して,ストアにアップロードすることはできけど,不必要なので削除することにします.

削除方法

今回はAndroidManifest.xmlで上書きして削除する方法をとりたいと思います.
書き換えるAndroidManifest.xmlは, プロジェクト/Assets/Plugins/Android/にあるものを書き換えます.

ここにないよ!って方は, いったんアプリをビルドした後に,プロジェクト/Temp/StagingArea/AndroidManifest.xmlの中身をコピーして置いてください.

次に削除する記述を書きます.

AndroidManifest.xml
<uses-permission android:name="android.permission.READ_PHONE_STATE" tools:node="remove" />

これを記述する位置は,

AndroidManifest.xml
<manifest ...>
  ...ここの位置
  <uses-permission android:name="android.permission.READ_PHONE_STATE" tools:node="remove" />
  <application ...> 

になります.もうすでにほかの権限を与えているときは,<uses-permission android:name=...>があると思うので,その近くに書いてあげるといいと思います.

これでビルドしたときに,XmlException: 'tools' is undeclared namespace. みたいな toolsってやつがないよっていうエラーが出た場合は,以下のものを追記してください.

AndroidManifest.xml
xmlns:tools="http://schemas.android.com/tools"

追記する位置は<manifest>の中に書きます.

これでビルドするとREAD_PHONE_STATEの権限が外れた状態でビルドができていると思います.

READ_PHONE_STATE以外の権限も消したいときにも消したい権限を書き換えればできます.

参考

https://forum.unity.com/threads/sudden-error-unable-to-merge-android-manifests.490096/

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

【cluster】ワールドをアップロードするとき、Sceneにゴミがあって怒られる場合

練習ワールドをアップロードする際によく起こる事例かと。
アップロードするとき、「Scene上にMainCameraを配置しないでください」とか怒られると思います。
んなわけねぇ、置いた記憶にございません、という場合は、ヒエラルキーウィンドウで検索しましょう。

a01.png

下図:検索したら、いた
a02.png

あとは、怒られるオブジェクトを使用不可にしていけばOK。

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

ant+ 心拍計を用いて、Unity上でVRMの表情やアニメーションを制御する。

これがやりたい

心拍.png

※追記※心拍ゾーンとは何ですか? | Garmin サポートセンター

誰しも心拍によって顔が赤くなったり元に戻ったりしたい筈。したいですね?
(心拍数センサーの入力によって、VRMモデルのブレンドシェイプの数値を変動させたい)

この記事は知識のない者が感覚でばーっとやって、びゃーとした感想を書いています。
分からないなりに頑張って書いたから誉めてくださいお願いします。間違ってたら指摘をお願い致します。


この記事を書こうと思ったきっかけ

私はサイクリストバーチャルyoutuber仁志乃ちゃんの監督です。

https://twitter.com/Nishino_cyclist
普段はかわいいカンガルー?の女の子にYoutubeで雑談させたり自転車させたりしています。
やっぱスポーツ頑張ってたら汗かいたり暑くて顔が赤くなったりするよね。
スポーツ自転車乗ってる人なら結構持ってるant+ の心拍計と連動して
VRMモデルの顔色を変えるみたいなことをやりたくなりました。


用意するもの


環境

Uniy 2018.4.10f1

※私の環境なので他の環境でも出来ると思います。

Univrm v0.53.0

今回はあくまでも心拍とVRMに関して書くのでアバターとして動かしたい場合は別途バーチャルモーションキャプチャーなどを使用してください。無料です!

AdvancedAnt+

有料アセットです。$60です!プログラミングわかんないのにこれ使ったら秒ですよ最高。
このアセットでant+のデータを取得できるようになります。今回心拍数だけですが、ケイデンス(自転車のクランクの回転数)やスピード、パワー(ワット数)なんかも取れます。

赤面表情差分があるVRMモデル

今回は冒頭画像のようにゾーン2~4では頬付近だけをだんだん赤らめ、4~5では顔のメッシュ全体を赤くしていきたいので、
半透明のテクスチャを乗せたメッシュを仁志乃ちゃんに追加しました。
image.png


AdvancedAnt+を使うには開発者登録が必要

AdvancedAnt+っていうUnityのアセットについて
AdvancedAnt+アセットの導入について書かれています。

サイトにて開発者登録が必要です(無料)

とありますので、登録します。登録時のことに関して補足すると、サイトにアクセスしたら、右上のregister now(赤丸)をクリックしてCREATE A USER ACCOUNTに行き、登録を行いましょう。
image.png

ACTIVATION IS NOT AUTOMATIC AND WILL TAKE UP TO 1 BUSINESS DAY, NOT INCLUDING STATUTORY HOLIDAYS

登録ページにこう書かれてある通り、自動返信で即返事が来るわけではないので注意。
私は土日を挟んで月曜には登録完了メールが届きました。
早速ログインして、
https://www.thisisant.com/developer/ant-plus/ant-plus-basics/network-keys
のページにアクセスし、下段のこの部分、赤丸のところのネットワークキーを控えます。
(後ほど使用します。)

image.png


別の手段で取得?

私はアセットを使いましたが、こちらの記事では御自身で心拍を取れるようにされているみたいです。
ANT+で脈拍数を取得してUnityで作ったVR空間に表示させる


機材

GARMIN(ガーミン) ハートレートセンサー HRM-Dual

私と仁志乃は胸に巻くタイプの心拍計を使用しています。
上記はant+とBluetooth両方使えるタイプですが、必ずしもこれでなくても大丈夫。
リストウォッチタイプの心拍転送モードでもできるはず(検証します)

USB ANTスティック mini

パソコンにぶっさしてant+のデータを受け取る方。


UnityにAdvancedAnt+とUnivrmを入れる

二つともUnitypackageファイルをインポートするだけでOK


ネットワークキーを有効にする。

AntManager
Assets/AdvancedAnt/Plugins/Ant/AntManager.csを開き、

42行目に先ほど控えたネットワークキーをコピペします。

AntManager.cs
readonly byte[] NETWORK_KEY = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };// COPY THE CORRECT NETWORK KEY HERE

このへんのところAssets/AdvancedAnt/PC_MAC_readme.txt にも書かれています。


demowithPrefabsシーンを試す

それではUSB ANTスティックをPCに刺して認識させてましょう。
そして心拍計を体に装着します。機材の準備が整ったら、Unityに戻って
Assets/AdvancedAnt/demowithPrefabs/demowithPrefabsを開きます
hierarchyのHeartRateDisplayをアクティブにしてからシーンを再生。
心拍の情報がUnity届いていることを確認しましょう。届いているなら再生前は
Enable the gameObjects you would like to use
と表示されている部分が変化し、心拍数(HR = XX)が表示されます。
HeartRateDisplay

うまくいったところで、このシーンを別名保存してVRMを読み込みましょう。


VRMのblendshape

UniVRMのブレンドシェイプの設定を見てください。
[オプション]表情を追加する という項目がありますのでちょっと読んでみてください。メッシュだけではなく
テクスチャを変化させられるのが分かります。
_colorのTarget Valueをピンクに変えましょう。これで心拍ゾーン4~5の、顔全体が真っ赤にする方法が何となくわかるかと思います。(汗は思い付きで入れましたがこれは普通のブレンドシェイプ)
image.png

心拍ゾーン3~4では頬のテクスチャをNEUTRAL表情の時は_colorのアルファ0にしておき、赤ら顔表情の時にアルファ100になるようにしています
//image.png
コメント 2020-03-16 214929.png


顔色を操作するスクリプトをVRMにアタッチする

以下のスクリプトをVRMにアタッチしてください。先程作ったオプションの表情のBlend Shape nameを直接指定してください。

kaoiro.cs
using UnityEngine;
using System.Collections;
using ANT_Managed_Library;
using VRM;

public class kaoiro: MonoBehaviour
{

    GameObject HeartRateDisplay; //GameobjerctとしてのFitnessEquipmentDisplayが入る変数
    HeartRateDisplay script; //FitnessEquipmentDisplayScriptが入る変数
    BlendShapePreset currentFace;

    private VRMBlendShapeProxy proxy;
    private CharacterController characterController;
    private Vector3 velocity;
    public float maxheart = 200f;//最大心拍数
    public float minheart = 60f;//安静時心拍数


    [SerializeField]
    void Start()
    {

        HeartRateDisplay = GameObject.Find("HeartRateDisplay"); //FitnessEquipmentDisplay をオブジェクトの名前から取得して変数に格納する
        script = HeartRateDisplay.GetComponent<HeartRateDisplay>(); //FitnessEquipmentDisplay の中にあるFitnessEquipmentDisplay を取得して変数に格納する
        var proxy = GetComponent<VRMBlendShapeProxy>();

        // Blend Shape Nameを指定して適用量をセット
        proxy.SetValue("赤ら顔", 0f);

    }

    void Update()
    {
        if (proxy == null)
        {
            proxy = GetComponent<VRMBlendShapeProxy>();
        }
        else
        {
            float faceCollor1 =  maxheart - minheart;//計算の順序わからなくて無理やり
            float faceCollor = 1f / faceCollor1;//やりたいのは平常時心拍と最大時心拍の間を0~1で変化させたかった。
            float heartRate = script.heartRate;//scriptで持ってきた心拍数を代入


            if (heartRate < maxheart * 0.6f)//心拍ゾーン1(~60%)
            {
                proxy.SetValue("赤ら顔", faceCollor *heartRate *0.5f);
                proxy.SetValue("顔真っ赤", faceCollor * heartRate * 0f);

            }
            else if (heartRate < maxheart * 0.8f)//心拍ゾーン2~3(~80%)
            {
                proxy.SetValue("赤ら顔", faceCollor * heartRate);
                proxy.SetValue("顔真っ赤", faceCollor * heartRate * 0.5f);
            }
            else if (heartRate < maxheart * 0.9f)//心拍ゾーン4~5(90%~)
            {
                 proxy.SetValue("赤ら顔", faceCollor * heartRate);
                proxy.SetValue("顔真っ赤", faceCollor * heartRate *1.1f);
            }

        }
    }
}

そしたらもう表情変わりますよ!!ぴゅんですよ!!やったね
(画像でセンサーが取得出来てないのはわざとです。即座に自分の心拍を上昇できなかったのでHeartRateDisplayを直接数字いじりました。)
image.png


ハートの鼓動アニメーションを連動させたい

書き忘れました。
冒頭のツイッターの動画右上に鼓動するハートのアニメがあります。これは取得している心拍とシンクロしています。
image.png

これを実装しましょう。
[Unity] Canvasに Image 画像を配置しScriptで変更
【Unity】uGUIでスプライトアニメーションするには
こちらの記事がとても参考になりました。

1秒間に1回ドクンとするAnimationClipを作成し、ドキドキさせたいGUIのimageにアタッチします。
image.png

次に、下のスクリプトをアタッチします。

kokoro_Move_speed.cs
using UnityEngine;
using System.Collections;
using ANT_Managed_Library;

public class kokoro_Move_speed : MonoBehaviour
{
    GameObject HeartRateDisplay; //GameobjerctとしてのFitnessEquipmentDisplayが入る変数
    HeartRateDisplay script; //FitnessEquipmentDisplayScriptが入る変数

    private CharacterController characterController;
    private Vector3 velocity;
    [SerializeField]
    private float speed;
    private Animator animator;
    void Start()
    {

        HeartRateDisplay = GameObject.Find("HeartRateDisplay"); //FitnessEquipmentDisplay をオブジェクトの名前から取得して変数に格納する
            script = HeartRateDisplay.GetComponent<HeartRateDisplay>(); //FitnessEquipmentDisplay の中にあるFitnessEquipmentDisplay を取得して変数に格納する

        characterController = GetComponent<CharacterController>();
        animator = GetComponent<Animator>();
        animator.speed = 1.0f; //1秒に1回鼓動するアニメーションの場合
    }

    void Update()
    {
        float heartRate = script.heartRate;//scriptで持ってきた心拍数を代入
        //Debug.Log("心拍数は" + heartRate);

        characterController = GetComponent<CharacterController>();
        animator = GetComponent<Animator>();
        animator.speed = 1.0f / 60.0f * heartRate; //アニメーションの速度を心拍数に同期させてる

    }
}

シーン再生するともう完成です。


最後に

だれか既にやってるはずと思って探したらあんまり見当たらなかったのでメモです。
リングフィットアドベンチャーやビートセイバーを顔真っ赤にしながらやっていただければ嬉しいです。
もっとこうしたらいいよとかあったら是非指摘たりしてください。使わせてください。

以上です。あ、チャンネル登録してくれると喜んじゃいます
仁志乃チャンネル


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