20200228のUnityに関する記事は14件です。

Scratcher のための Scratch と Unity のコード対応リスト【動き編】

自分用メモ

動き

( 10 )歩動かす

transform.Translate(Vector3.forward * Time.deltaTime * 10f);

( 15 )度回す

transform.Rotate(new Vector3(0, 0, 15f));

(どこかの場所 |v)へ行く

どこかの場所のブロックはあとでやる。

//マウスのポインターへ行く
Vector3 pos = Input.mousePosition;
pos.z = 10f;
transform.position = Camera.main.ScreenToWorldPoint(pos);
//任意のスプライトへ行く
GameObject sprite1 = GameObject.Find("スプライト名");
transform.position = sprite1.getComponent<Transform>.position;

x座標を(10)、y座標を(10)にする

transform.position = new Vector3(10,10,0);

(1)秒で(どこかの場所 |v)へ行く

未対応

(1)秒でx座標を(10)に、y座標を(10)に変える

未対応

(90)度に向ける

transform.rotation = Quaternion.Euler(new Vector3(0,0,90));

マウスのポインターへ向ける

//マウスのポインターへ向ける
Vector3 pos = Input.mousePosition;
pos.z = 10f;
transform.LookAt(Camera.main.ScreenToWorldPoint(pos));
//任意のスプライトへ向ける
Vector3 pos = GameObject.Find("スプライト名").transform.position;
transform.LookAt(pos);

x座標を(10)ずつ変える

transform.Translate(10f,0,0);

x座標を(10)にする

var pos = transform.position;
transform.position = new Vector3(10f, pos.y, pos.z);

y座標を(10)ずつ変える

transform.Translate(0,10f,0);

y座標を(10)にする

var pos = transform.position;
transform.position = new Vector3(pos.x, 10, pos.z);

もし端についたら、跳ね返る

未対応

回転方法を[左右のみ]にする

未対応

x座標

transform.position.x

y座標

transform.position.y

向き

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

BlenderのモデルをUnityに入れるには

◆ はじめに

Unityに3Dゲーム作る為には、モデルは必要不可欠。
なので、今回は自作モデルをUnityに入れる方法とCollider機能について書きます。

◆ 開発環境

macOS Mojave バージョン 10.14.6
Unity 2018.4.12f1
Android SDK
Blender 2.81

◆ モデルの準備

今回のメインはモデルをUnityに入れる方法なので、
モデルの制作なとは説明しません。
01.pngスクリーンショット 2020-02-28 20.31.57.png

とりあえず、適度なモデル(コップ)を作って、保存します。
(名前は初期値のままでした…)

◆ Unityに入れて設置する

今回は
【UnityでOculus Quest向けのアプリを作る】APPをビルドして、動作確認するのSceneを使います。
(無論、普通の新規3D Objectも同じ方法で実現できます。)
03.png
やり方は非常に簡単、
「Project」に右クリック「Import New Asset」選択し、先ほどのファイルを読み込むだけです。
04.png
この中に必要なのは「Cricle」のみです。
05.png
Hierachyを右クリック「3D Object」→「Cube」選択「Cube」を作ります。
08.png
続いて、
「Cube」を選択しInspectorの「Circle」の「Mesh」の中に先程の「Cricle」入れます。
一番下のAdd Componentボタンをクリクし「Mesh Collider」を追加し設置します。
09.png
追加後、元にある「Box Collider」削除します。

ちなみに、Colliderの種類は色々あるんですが、よく使われているのは
Box Collider :立方体
Sphere Collider :球体
Capsule Collider :円柱
今回使っているのは、Mesh Collider :メッシュに基づくコライダーです。
10.png
これで設置完了です。
物理、色などの設置はご自由にやってください。

◆ ビルドして動作確認

スクリーンショット 2020-02-28 20.45.08.png

このようになりますね。
Blenderに関しては、まだ別の機会で。

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

AdobeXDでUnity用UIを作成する(XdUnityUI)

XdUnityUI

introduction

概要

  • AdobeXD のアートボードを Unity 用 UI Prefab にコンバートします。

クイックスタート

  1. インストール
    • ダウンロードする場合
      1. https://github.com/itouh2-i0plus/XdUnityUI/releases
      2. 最新バージョンの 「▶Assets」をクリックし XdUnityUI.unitypackage をダウンロードします。
      3. XdUnityUI.unitypackage を Unityにインポートしてください。
      4. Assets/I0plus/XdUnityUI フォルダが作成されます
    • Gitリポジトリからクローンする場合
      1. Git リポジトリをクローン
      2. (クローンフォルダ)/UnityProject を Unity で開きます
        • Assets/I0plus/XdUnityUI 以下が、プラグインフォルダになっています
        • 現在 Unity2019.3、UniversalRenderPipeline のプロジェクトとなっています。
  2. AdobeXD サンプルを 開く
    • /Assets/I0plus/XdUnityUI/ForAdobeXD/sample.xd にあります。
  3. AdobeXD プラグイン起動

    1. プラグインをインストールします。
      • /Assets/I0plus/XdUnityUI/ForAdobeXD/XdUnityUIExport.xdxをダブルクリックします。
    2. アートボード TestButton 内、ルート直下のレイヤー(例えば yellow-button)を選択状態にします。
    3. プラグインメニューから、「XdUnityUI export plugin」をクリック、起動します。
    4. 「Folder」の項目が出力フォルダ先指定です。(クローンしたフォルダ)/UnityProject/I0plus/XdUnityUI/Import フォルダを選択。
    5. 「Export」をクリック。
      • 出力時にエラーで止まるケースについて、当記事「問題が起こったとき」を参考にしてください。
  4. Unity コンバート

    • Unity ウィンドウをアクティブにするとコンバートが開始されます。
    • 作成された Prefab は Assets/I0plus/CreatedPrefabs に配置されます。
    • 作成された UI 画像は Assets/I0plus/CreatedSprites に配置されます。
      • Slice 処理されています。
    • できた Prefab を Canvas 以下に配置します。

動作条件

  • Windows で開発しています。
    • Mac での動作確認は現在不十分です。
  • Unity2019.3 で開発しています。
  • AdobeXD は最新版でテストしています。
    • バージョン:27.1.12.4

コンバートについて

概要

  • AdobeXD レイヤー名に対して、コンバートルールが適応されます。
    • コンバートルールは CSS の記述によって定義されています。
    • json ファイルと画像ファイルが出力されます。
  • 出力ファイルを Unity プロジェクト、XdUnityUI/Import フォルダに書き込むことで Unity でのコンバート処理が行われます。
  • 指定されたフォルダに Prefab と Sprite が出力されます。

ルール説明

  • AdobeXD レイヤー名で、Unity 上での機能が決まります。

image

  • image
    window-image
    icon-image
    
  • 説明

    • レイヤー、グループレイヤーに上記のような名前が付いていた場合、そのレイヤーと子レイヤーを合成した画像を生成し、Unity 上で Image コンポーネントが付与されます。
  • 注意

    • 子レイヤーも画像としてしまうため、子レイヤーのコンバート処理はされません。

button

  • button
    start-button
    back-button
    
  • 説明

    • Unity 上で button コンポーネントが付与されます。
  • 注意

    • 子レイヤーに image レイヤーが必要です。

text

  • text
    title-text
    name-text
    
  • 説明

    • テキストレイヤーに上記のような名前をつけることで Unity 上でも Text コンポーネントが 付与 されます。
  • 注意

    • AdobeXD で使用したフォントが Unity プロジェクト内、Assets/I0plus/XdUnityUI/Fonts/以下、.ttf か.otf で存在する必要があります。
    • AdobeXD と Unity では、デザイン上の差異があります。(例:カーニング)

textmp

  • textmp
    title-textmp
    name-textmp
    
  • 説明

    • テキストレイヤーに上記のような名前をつけることで Unity 上でも TextMeshPro コンポーネントが 付与 されます。
  • 注意

    • AdobeXD で使用したフォントが Unity プロジェクト内、Assets/I0plus/XdUnityUI/Fonts/以下、.ttf か.otf で存在する必要があります。
    • AdobeXD と Unity では、デザイン上の差異があります。(例:カーニング)
  • 追記予定

    • layout
    • viewport
    • scrollbar
    • toggle

問題が起こったとき

Xd プラグイン

コンバート中に失敗・中断され、再度エクスポートしたが、コンバート処理が実行されない

  • 原因

    • 失敗したファイルへの上書きでは、Unity 側がファイルの更新を検知できないため。
  • 対応

    • XdUnityUI/Import 内の_XdUnityUIImport、_XdUnityUIImport.meta ファイル以外を削除する。
    • もう一度エキスポートする。

画像の書き出しに失敗する

  • 原因
    • AdobeXD 上の問題かもしれません。調査中です。
  • 対策
    1. レイヤーを選択し画像出力の操作をする。
    2. 出力先に XdUnityUI/Import フォルダを選択すると、出力不可になっているか確認。
    3. フォルダを変えて画像出力。
    4. 再度 Import フォルダに出力する。
    5. 上記が成功した場合、プラグインからの出力も成功するようになります。

レスポンシブパラメータが正確にコンバートされない

  • 原因
    • XD プラグイン実行時、アートボードのサイズを変更しレイヤーのサイズの変化をみてレスポンシブパラメータを取得しています。その際、リピードグリッド内レイヤー等、サイズが変わらないものはレスポンシブパラメータが確定できません。
  • 対策
    • margin-fix プロパティをつかい、明示的に指定する。
      • 例: start-button {margin-fix: t b l r}
    • AdobeXD Plugin API で、レスポンシブパラメータが取得できるようになりましたら対応します。

Unity コンバータ

文字(Text、TextMeshPro)を扱おうとするとコンバートに失敗する

  • 原因
    • フォントが無い可能性があります。
  • 対策
    • Console に探そうとして見つからなかったフォントファイル名が出力されます
    • フォントファイルを場合によってはリネームして、XdUnityUI/Fonts ディレクトリ(_XdUnityUIFonts ファイルがおいてあるディレクトリ)にコピーしてください。

より使いこなすために

オリジナル変換ルール

  • 変換ルールCSSの変更
    1. XdUnityUIExport.xdxをXdUnityUIExport.zipとリネーム
    2. 解凍しxd-unity.cssファイルを変更
    3. 再びZIP圧縮、拡張子をxdxに変更
    4. プラグイン再インストール
  • 変換ルールCSSの説明
    • 追記予定
  • アートボード毎の変換ルール
    • 追記予定

コンバート時に自作コンポーネントを追加する

  • 追記予定

9Slice

  • 追記予定

今後進む方向について

  • 目標
    • リリースまで AdobeXD で UI デザインできるようにする。
  • メリット
    • デザイナの手に最終版をもたせる。
    • CCLibrary を使い、各種ツールとの連携ができる。
  • 課題
    • コンバートでの Prefab 上書きによって、Prefab に対して行った作業(Unity 上で行ったコンポーネント追加、パラメータ調整)が消えてしまう
      • 対応策
        • コピーして使用する。
        • Prefab Variant で、追加作業の退避。
          • 名前の変更で作業が消えてしまう。
          • Variant ファイル内には作業分残っている模様(これを復帰できないか調査中)。
    • 同じ Sprite 画像が大量にできてしまう。
      • 対応中
        • Sprite 画像を比較して、同じであれば結合するツールを開発中。
    • 9Slice
      • 対応中
    • コンポーネントのステート
  • その他
    • 調査中
      • UXML へコンバートできないか。
      • DOTS モード用 UI が作成できないか。

謝辞

お世話になっております ありがとうございます

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

OpenCV For Unity VideoWriter.write() 出来ない 【未解決】

今朝(https://qiita.com/wirsind55/items/98328600991fcf6218ce)の続編である。

やりたいことはUnity上でCameraの動画キャプチャを取ってavi保存すること。

有料のもの(OpenCV for Unity)使えばいけんじゃないかとたかを括っていたが、いけない。。

コードは最小限にしてみて下記のとおり。

ScreenCapture.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using OpenCVForUnity;
using OpenCVForUnity.VideoioModule;
using OpenCVForUnity.VideoModule;
using OpenCVForUnity.CoreModule;

public class ScreenCapture : MonoBehaviour
{
    public Camera _targetCamera;

    private VideoWriter _videoWriter;
    private int cnt = 0;

    // Start is called before the first frame update
    void Start()
    {
        _videoWriter = new VideoWriter(@"output.avi",  VideoWriter.fourcc('D', 'I', 'V', 'X'), 30.0, new Size(448, 448), true);
    }

    // Update is called once per frame
    void Update()
    {
        CaptchaScreen();
        cnt.ToString() + ".png");
        cnt++;
        if(cnt >= 500)
            _videoWriter.Dispose();
    }

    public void CaptchaScreen()
    {

        Mat srcMat = new Mat (new Size(448, 448), CvType.CV_8UC4);

        _videoWriter.write(srcMat);
    }
}

結果として、new VideoWriterでoutput.aviは作成されているけど、
その後のwriteが効いておらず作成されたaviファイルのファイルサイズは0のままで破損状態という扱いになる。

それまでに何か間違っているのでしょうか。。。
いかんせんUnity、OpenCVの記事はそこまで多くないし分からない。
誰か教えて下さい。

だいぶハマってしまったので、この方法は一旦あきらめ、別の手段に移ります(画像を大量生産して、その後でOpenCVで画像を動画にする)。

以上、しゅん。

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

OpenCV For Unity VideoWriter.write() 出来ない 【解決済み】

今朝(https://qiita.com/wirsind55/items/98328600991fcf6218ce)の続編である。

やりたいことはUnity上でCameraの動画キャプチャを取ってavi保存すること。

有料のもの(OpenCV for Unity)使えばいけんじゃないかとたかを括っていたが、いけない。。

コードは最小限にしてみて下記のとおり。

ScreenCapture.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using OpenCVForUnity;
using OpenCVForUnity.VideoioModule;
using OpenCVForUnity.VideoModule;
using OpenCVForUnity.CoreModule;

public class ScreenCapture : MonoBehaviour
{
    public Camera _targetCamera;

    private VideoWriter _videoWriter;
    private int cnt = 0;

    // Start is called before the first frame update
    void Start()
    {
        _videoWriter = new VideoWriter(@"output.avi",  VideoWriter.fourcc('D', 'I', 'V', 'X'), 30.0, new Size(448, 448), true);
    }

    // Update is called once per frame
    void Update()
    {
        CaptchaScreen();
        cnt.ToString() + ".png");
        cnt++;
        if(cnt >= 500)
            _videoWriter.Dispose();
    }

    public void CaptchaScreen()
    {

        Mat srcMat = new Mat (new Size(448, 448), CvType.CV_8UC4);

        _videoWriter.write(srcMat);
    }
}

結果として、new VideoWriterでoutput.aviは作成されているけど、
その後のwriteが効いておらず作成されたaviファイルのファイルサイズは0のままで破損状態という扱いになる。

それまでに何か間違っているのでしょうか。。。
いかんせんUnity、OpenCVの記事はそこまで多くないし分からない。
誰か教えて下さい。

だいぶハマってしまったので、この方法は一旦あきらめ、別の手段に移ります(画像を大量生産して、その後でOpenCVで画像を動画にする)。

以上、しゅん。

⇒ コメント欄にて解決されました:sob:

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

何もしない

if () {
;
}

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

【用Unity开发Oculus Quest用的APP】用Hand tracking功能抓住物体

◆ 写在开头

上一回、写了关于Oculus Quest 中Hand tracking功能的使用的内容。
所以这次的内容上,关于如何使用Hand tracking实现抓住物体的方法。

关于手的动作、手指状态的取得等,这里就不详细写了。
具体方法等,请自行查询。

如果制作中用什么问题或不明点,
有需要的话可以参考下面两条内容。
用Unity开发Oculus Quest用的APP 导入APP、进行运行状态确认
抓住物体吧(物体的设置与制作)
Hand tracking功能的使用

关于这次的内容,抓住物体的实行方法
完全属于自己想当然的做法,参考性比较低……请谅解。

◆ 开发环境

macOS Mojave 版本 10.14.6
Unity 2018.4.12f1
Android SDK
为了能够使用Hand tracking的功能,
Oculus的版本必须得是(Ver12)或以上。(需要更新的请即时更新)

◆ 制作顺序

  • 准备阶段的说明
  • 实现【抓住】功能
  • 手指动作的判断
  • 理论关系和代码
  • 动作确认

准备阶段的说明

スクリーンショット 2020-02-17 18.43.04.png
首先准备必要的Object,
在这里我们意见实装了Hand tracking功能、准备好了自带物理的几个测试用方块。
方块的尺寸颜色等请自由设置(方便看清就行)。

为了更直观的显示代码部分是否有正确实行等、
这里制作了观测用Canvas,当然这个只是起到了方便看的作用并不是必须要制作的东西,
只是单纯的有了会更直观而已。
当然桌子也不是必须品。

实现【抓住】功能

以前在抓住物体吧(物体的设置与制作)中,只要成功的在画面中表示手,便可以直接实行抓取,
但Hand tracking好像没有办法直接抓取的样子。
02.png
所以,
这次要做的事情就是,
当手触碰到物体且作出了抓住物体的动作时,把被触碰物体放入手的Object中,
也就是说,成为手的子级。

当然,除了成为子级以外,
当发生抓住判定时,被抓物体跟随手一起移动也是实现的方法之一。

手指动作的判断

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

public class ovrHand : MonoBehaviour
{
    private OVRHand _ovrHand;
    // 抓住状态的取得
    public bool _catching;

    // Start is called before the first frame update
    void Start()
    {
        _catching = false;
        _ovrHand = this.gameObject.GetComponent<OVRHand>();
    }
    // Update is called once per frame
    void Update()
    {
        if (_ovrHand.GetFingerIsPinching(OVRHand.HandFinger.Index))
        {
            _catching = true;
        }
        else
        {
            _catching = false;
        }
    }
}

将上述内容直接拖入「OVRHandPrefab」中。
关于手动作的取得、变数「_ catching」的制作与「_ catching」的说明,之后再说。

GetFingerIsPinching可以判断手指尖是否接触
OVRHand.HandFinger可以判断某手指是否与大拇指接触,这次接触手指的设置是食指。
当食指尖与大拇指触碰时,「_catching」变为true。

将「_catching」的状态在Canvas上表示
(和上面讲到的一样Canvas并不是必需品,下面的内容里讲省略全部关于Canvas的内容)

理论关系和代码

cubeTouch.cs
    private ovrHand _ovrHand;
    private bool _touchIN;      //触碰
    private bool _catch;        //抓住动作
    private bool _hold;         //是否能抓住

    // Start is called before the first frame update
    void Start()
    {
        _ovrHand = FindObjectOfType<ovrHand>();
    }
void Update()
    {
        // 取得手的动作 判断是否抓住
        _catch = _ovrHand._catching;
        touchCube();
        CatchCube();
    }
void touchCube()
    {
        if (_touchIN == true && _catch == false)
        {
            _hold = true;
        }
    }

    void CatchCube()
    {
        if (_hold == true && _catch == true)
        {
            _touchIN = false;
        }
        else if (_touchIN = false && _touchIN == false)
        {
            _hold = false;
        }
    }

首先,需要分析关于抓住物体这一动作。

当手是抓住动作状态的时候触碰物体,是不能抓住物体的。
而在手触碰物体后,实行抓住动作便可抓住物体。

将上述内容用代码表示。

cubeTouch.cs
void OnCollisionStay(Collision other)
    {
        if (other.gameObject.name == "Hand_Index3_CapsuleRigidBody" ||
            other.gameObject.name == "Hand_Index2_CapsuleRigidBody" ||
            other.gameObject.name == "Hand_Thumb3_CapsuleRigidBody" ||
            other.gameObject.name == "Hand_Thumb2_CapsuleRigidBody" 
            )
        {
            _touchIN = true;
            // 固定位置且消除物理す
            gameObject.GetComponent<Rigidbody>().constraints = RigidbodyConstraints.FreezeAll;
            gameObject.GetComponent<Rigidbody>().isKinematic = true;
        }
    }

参考上述内容,
想要实行抓住动作,首先需要从触碰物体开始。

关于手指各个部位的名字请参考最上面的连接。
发生触碰时,「 _touchIN」的状态变为true。

cubeTouch.cs
void CatchCube()
    {
        if (_hold == true && _catch == true)
        {
            // 变为右手子级
            gameObject.transform.SetParent(_rightHandAnchor.gameObject.transform);
            // 消除重力与触碰判定
            gameObject.GetComponent<Rigidbody>().useGravity = false;
            gameObject.GetComponent<BoxCollider>().isTrigger = true;
            _touchIN = false;
        }
        else if (_touchIN = false && _touchIN == false)
        {
            // 解除父级关系
            gameObject.transform.parent = null;
            // 恢复重力 触碰
            gameObject.GetComponent<Rigidbody>().useGravity = true;
            gameObject.GetComponent<BoxCollider>().isTrigger = false;
            // 恢复位置移动与物理
            gameObject.GetComponent<Rigidbody>().constraints = RigidbodyConstraints.None;
            gameObject.GetComponent<Rigidbody>().isKinematic = false;
            _hold = false;
        }
    }

这次准备的方块中因为加入了物理效果,所以当发生抓住判定时,需要将其去掉。
(没放入物理的情况下就没有写的必要了)
请根据自己的状况判断后,将必要的内容写入「CatchCube()」中。
(代码的意思,在上面有写请参考)

最后,把「cubeTouch.cs」拖入到方块中,完成必要的设置就算完成了。

动作确认

com.oculus.vrshell-20200218-204038_1_1.gif

最后导入Oculus,确认是否能够正常运行吧。

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

脱出ゲームスクリプトメモ

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;//書き加えないとToggleが使えない

public class tutorialScript : MonoBehaviour {

public GameObject wall1;
public GameObject wall2;
public GameObject wall3;
public GameObject wall4;

public GameObject close_door;//閉じた状態のドア
public GameObject open_door;//開いたドア
public GameObject close_treasurebox1;//閉じた宝箱1
public GameObject open_treasurebox1;//開いた宝箱1
public GameObject close_treasurebox2;//閉じた宝箱2
public GameObject open_treasurebox2;//開いた宝箱2

public GameObject key1;//カギ1を格納
public GameObject key2;//カギ2を格納

public Toggle key1_toggle;//カギ1を格納、アイテム選択の状態を取得するのに必要
public Toggle key2_toggle;//カギ2を格納、アイテム選択の状態を取得するのに必要

public GameObject calender_kakudai;//カレンダーの拡大画面
public GameObject treasurebox2_kakudai;//宝箱2の数字入力画面

public GameObject itemgetandclear;//下3つを表示するための全画面の透明な土台
public GameObject key1_get;//カギ1入手時に表示
public GameObject key2_get;//カギ2入手時に表示
public GameObject clear_text;//ドアを開けた時に表示

public GameObject left0;
public GameObject left1;
public GameObject left2;
public GameObject left3;
public GameObject left4;
public GameObject left5;
public GameObject left6;
public GameObject left7;
public GameObject left8;
public GameObject left9;

public GameObject center0;
public GameObject center1;
public GameObject center2;
public GameObject center3;
public GameObject center4;
public GameObject center5;
public GameObject center6;
public GameObject center7;
public GameObject center8;
public GameObject center9;

public GameObject right0;
public GameObject right1;
public GameObject right2;
public GameObject right3;
public GameObject right4;
public GameObject right5;
public GameObject right6;
public GameObject right7;
public GameObject right8;
public GameObject right9;

void Update()
{
    //613と入力し、かつ宝箱2が閉じているとき
    if (left6.activeInHierarchy == true && center1.activeInHierarchy == true &&
        right3.activeInHierarchy == true&& close_treasurebox2.activeInHierarchy == true)
    {
        close_treasurebox2.SetActive(false);
        open_treasurebox2.SetActive(true);
        treasurebox2_kakudai.SetActive(false);
        itemgetandclear.SetActive(true);
        key1_get.SetActive(true);
    }
}

public void gotowall1()
{
    wall1.SetActive(true);
    wall2.SetActive(false);
    wall3.SetActive(false);
    wall4.SetActive(false);
}

public void gotowall2()
{
    wall1.SetActive(false);
    wall2.SetActive(true);
    wall3.SetActive(false);
    wall4.SetActive(false);
}

public void gotowall3()
{
    wall1.SetActive(false);
    wall2.SetActive(false);
    wall3.SetActive(true);
    wall4.SetActive(false);
}

public void gotowall4()
{
    wall1.SetActive(false);
    wall2.SetActive(false);
    wall3.SetActive(false);
    wall4.SetActive(true);
}

//ヒエラルキーの閉じたドアに取り付ける
public void doortap()
{
    if (key2.activeInHierarchy == true && key2_toggle.isOn == true)
    {
        key2.SetActive(false);
        close_door.SetActive(false);
        open_door.SetActive(true);
        itemgetandclear.SetActive(true);
        clear_text.SetActive(true);
    }
}

//ヒエラルキーのカレンダーに取り付ける
public void calendertap()
{
    calender_kakudai.SetActive(true);
}
//ヒエラルキーのカレンダー拡大画像のボタンに取り付ける
public void calender_returntap()
{
    calender_kakudai.SetActive(false);
}

//閉じた宝箱1に取り付ける
public void treasurebox1tap()
{
    if (key1.activeInHierarchy == true && key1_toggle.isOn == true)
    {
        key1.SetActive(false);
        close_treasurebox1.SetActive(false);
        open_treasurebox1.SetActive(true);
        itemgetandclear.SetActive(true);
        key2_get.SetActive(true);
    }
}

//閉じた宝箱2に取り付ける
public void treasurebox2tap()
{
    treasurebox2_kakudai.SetActive(true);
}

//数字入力画面の下部の戻るボタンに取り付ける
public void treasurebox2_returntap()
{
    treasurebox2_kakudai.SetActive(false);
}

public void leftbuttontap()
{
    if (left0.activeInHierarchy == true)
    {
        left0.SetActive(false);
        left1.SetActive(true);
    }
    else if (left1.activeInHierarchy == true)
    {
        left1.SetActive(false);
        left2.SetActive(true);
    }
    else if (left2.activeInHierarchy == true)
    {
        left2.SetActive(false);
        left3.SetActive(true);
    }
    else if (left3.activeInHierarchy == true)
    {
        left3.SetActive(false);
        left4.SetActive(true);
    }
    else if (left4.activeInHierarchy == true)
    {
        left4.SetActive(false);
        left5.SetActive(true);
    }
    else if (left5.activeInHierarchy == true)
    {
        left5.SetActive(false);
        left6.SetActive(true);
    }
    else if (left6.activeInHierarchy == true)
    {
        left6.SetActive(false);
        left7.SetActive(true);
    }
    else if (left7.activeInHierarchy == true)
    {
        left7.SetActive(false);
        left8.SetActive(true);
    }
    else if (left8.activeInHierarchy == true)
    {
        left8.SetActive(false);
        left9.SetActive(true);
    }
    else if (left9.activeInHierarchy == true)
    {
        left9.SetActive(false);
        left0.SetActive(true);
    }
}

public void centerbuttontap()
{
    if (center0.activeInHierarchy == true)
    {
        center0.SetActive(false);
        center1.SetActive(true);
    }
    else if (center1.activeInHierarchy == true)
    {
        center1.SetActive(false);
        center2.SetActive(true);
    }
    else if (center2.activeInHierarchy == true)
    {
        center2.SetActive(false);
        center3.SetActive(true);
    }
    else if (center3.activeInHierarchy == true)
    {
        center3.SetActive(false);
        center4.SetActive(true);
    }
    else if (center4.activeInHierarchy == true)
    {
        center4.SetActive(false);
        center5.SetActive(true);
    }
    else if (center5.activeInHierarchy == true)
    {
        center5.SetActive(false);
        center6.SetActive(true);
    }
    else if (center6.activeInHierarchy == true)
    {
        center6.SetActive(false);
        center7.SetActive(true);
    }
    else if (center7.activeInHierarchy == true)
    {
        center7.SetActive(false);
        center8.SetActive(true);
    }
    else if (center8.activeInHierarchy == true)
    {
        center8.SetActive(false);
        center9.SetActive(true);
    }
    else if (center9.activeInHierarchy == true)
    {
        center9.SetActive(false);
        center0.SetActive(true);
    }
}

public void rightbuttontap()
{
    if (right0.activeInHierarchy == true)
    {
        right0.SetActive(false);
        right1.SetActive(true);
    }
    else if (right1.activeInHierarchy == true)
    {
        right1.SetActive(false);
        right2.SetActive(true);
    }
    else if (right2.activeInHierarchy == true)
    {
        right2.SetActive(false);
        right3.SetActive(true);
    }
    else if (right3.activeInHierarchy == true)
    {
        right3.SetActive(false);
        right4.SetActive(true);
    }
    else if (right4.activeInHierarchy == true)
    {
        right4.SetActive(false);
        right5.SetActive(true);
    }
    else if (right5.activeInHierarchy == true)
    {
        right5.SetActive(false);
        right6.SetActive(true);
    }
    else if (right6.activeInHierarchy == true)
    {
        right6.SetActive(false);
        right7.SetActive(true);
    }
    else if (right7.activeInHierarchy == true)
    {
        right7.SetActive(false);
        right8.SetActive(true);
    }
    else if (right8.activeInHierarchy == true)
    {
        right8.SetActive(false);
        right9.SetActive(true);
    }
    else if (right9.activeInHierarchy == true)
    {
        right9.SetActive(false);
        right0.SetActive(true);
    }
}

//ヒエラルキーのアイテム入手、クリアの土台パネルに取り付ける
public void itemgetandcleartap()
{
    if (key1_get.activeInHierarchy == true)
    {
        itemgetandclear.SetActive(false);
        key1_get.SetActive(false);
        key1.SetActive(true);
    }
    if (key2_get.activeInHierarchy == true)
    {
        itemgetandclear.SetActive(false);
        key2_get.SetActive(false);
        key2.SetActive(true);
    }
}

}

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

Unity Nuget OpenCvSharp3 VideoWriterが初期化出来ん ><

先日の続き・・
(Unity Nuget OpenCV DLLNotFoundError対応【解決済み】https://qiita.com/wirsind55/items/4e7130a458d0cca246dd)

先日の対応で環境は無事に整えた(ハズナン)だが、
VideoWriterがまともに動かない。。
具体的には下記コードでvideo wirterがopen出来ない。

videoWriter = new VideoWriter(videoOutputPath, VideoWriter.FourCC('X', 'V', 'I', 'D') , 29.97, new Size(width, height), true);

パス、コーデック、サイズいろいろ確認したり試したがどうしてもダメだ。
つまりダメということなのか。

そういうときは有料版へ流れよう。
https://assetstore.unity.com/packages/tools/integration/opencv-for-unity-21088

次回、「お世話になります、OpenCV For Unity」。

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

photonnetwork.instantiateを使用せずデータの同期をする方法#ゲーム制作03

どうも、わたたかです。
半ベソをかきながらもTwitterでアドバイスを頂き前回の問題を解決したので
その方法について書いていきます。

前回
前回の補足

上リンクを見て頂けると分かる通り自身が作成したCardControllerでは
photonnetwork.instantiateを使用することが出来ずどうにか他の方法は無いかと模索していました。

そこでRPCと言うものを使って生成と同期の処理を行うことにしました。

参考リンク
https://connect.unity.com/p/pun2deshi-meruonraingemukai-fa-ru-men-sono3

訂正箇所

前回

CardController.cs
    public void Init(int cardID)
    {
        model = new CardModel(cardID);// 生成
        view.Show(model);// 反映
    }

訂正後

CardController.cs
using Photon.Pun

    [PunRPC]
    public void Init(int cardID)
    {
        model = new CardModel(cardID);// 生成
        view.Show(model);// 反映
    }

    [PunRPC]
    public void OnlineInit(int cardID)
    {
        photonView.RPC(nameof(Init), RpcTarget.All,cardID);
    }

こうすることによってphotonnetwork.instantiateを使用しなくてもオブジェクトを生成することが出来ました。空のオブジェクトにPhoronView.csをつけるのを忘れないように。

あとは追加で

GameManager.cs
    // ルーム入室時に呼ばれる関数
    public override void OnJoinedRoom()
    {
        Debug.Log("ルームへ入室しました!");
        StartGame();
    }

ルームに入室してからゲームを始める処理を開始することでよく分からないサーバーエラーから逃れることが出来ました。

まとめとこれから

無事カードを生成することが出来ましたがそれも束の間。
次はIllegal view ID:0 method: Init GO:PlayerCard (PlayerCardは空のカードPrefabの名前)
と言ったエラーが出て今度は頭を捻らせてます。
一難去ってまた一難はこういうことなんですね。

オブジェクトにPhoton viewID?と言うのが付いていないから出ているエラーっぽいですが
スクリプトのPhotonView.csを外すと生成すらされなくなるので外せないです。

解決策が分かる方がいましたらコメント、Twtter(@wttk05)のDMお待ちしてます。

3月までに人に遊んでもらえる状態まで持っていきたいなぁ...(遠い目)
それではさようなら。

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

ノートPC+webカメラだけでモーションキャプチャぽいことをして3Dモデル動かしてみた

概要

こちらの記事を見かけて,処理がそこそこ軽い姿勢推定モデルを使ったらノートPCとwebカメラだけでモーションキャプチャぽいことができそうなのでやってみました.流れとして元記事とほとんど同じです.

必要なもの

  • Unity2018.1.9 f2
  • Python実行環境

手順

Python側

1. 姿勢推定モデルの用意

以下のものをcloneしてください
https://github.com/ildoonet/tf-pose-estimation/tree/master
これを使用することで2次元画像に写る人物の姿勢を推定できます.

2. 3次元情報の復元

モーションキャプチャぽいことをするためには3次元情報が必要になってきます.
1.のモデルは2次元情報しか得られません.
そこで先ほどのリポジトリのdevelブランチにある処理を使用して3次元情報を取得します.
(元々masterにあったぽいけどなくなってた)

以下のものをcloneしてください
https://github.com/ildoonet/tf-pose-estimation/tree/devel
そして devel/src/lifting フォルダを masterに移動させます

3. WebSocketサーバの用意

今回のシステムではPython側で人物の姿勢推定などの処理を行い,Unity側では3Dモデルの表示のみを行います.
PythonとUnityの情報通信部分を今回はWebSocketを用いて実装します.
以下のコマンドを実行
pip install git+https://github.com/Pithikos/python-websocket-server

PythonServer

server.py
import logging

import cv2
import json
import numpy as np
import common

from tf_pose.estimator import TfPoseEstimator
from tf_pose.networks import get_graph_path, model_wh
from websocket_server import WebsocketServer
from lifting.prob_model import Prob3dPose

PORT = 5000
HOST = '127.0.0.1'

# logger_setup
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
handler = logging.StreamHandler()
handler.setFormatter(logging.Formatter(' %(module)s -  %(asctime)s - %(levelname)s - %(message)s'))
logger.addHandler(handler)


def create_json(pose3d):
    global old_data

    data = {'body_parts': []}

    """
    // 0 :Hip
    // 1 :RHip
    // 2 :RKnee
    // 3 :RFoot
    // 4 :LHip
    // 5 :LKnee
    // 6 :LFoot
    // 7 :Spine
    // 8 :Thorax
    // 9 :Neck/Nose
    // 10:Head
    // 11:LShoulder
    // 12:LElbow
    // 13:LWrist
    // 14:RShoulder
    // 15:RElbow
    // 16:RWrist
    """

    for i in range(17):
        data['body_parts'].append({'id': i, 'x': pose3d[0][0][i], 'y': pose3d[0][2][i], 'z': pose3d[0][1][i]})

    old_data = data
    return data


def new_client(client, server):
    logger.info('NewClient {}:{} has left.'.format(client['address'][0], client['address'][1]))


def client_left(client, server):
    logger.info('Client {}:{} has left.'.format(client['address'][0], client['address'][1]))


def message_received(client, server, message):
    _, image = cam.read()

    humans = e.inference(image, resize_to_default=(w > 0 and h > 0), upsample_size=4.0)

    pose_2d_mpiis = []
    visibilities = []

    standard_w = 640
    standard_h = 480

    try:
        pose_2d_mpii, visibility = common.MPIIPart.from_coco(humans[0])
        pose_2d_mpiis.append([(int(x * standard_w + 0.5), int(y * standard_h + 0.5)) for x, y in pose_2d_mpii])
        visibilities.append(visibility)
        pose_2d_mpiis = np.array(pose_2d_mpiis)
        visibilities = np.array(visibilities)
        transformed_pose2d, weights = poseLifting.transform_joints(pose_2d_mpiis, visibilities)
        pose_3d = poseLifting.compute_3d(transformed_pose2d, weights)
        print(pose_3d)
        server.send_message(client, json.dumps(create_json(pose_3d)))

    except :
        server.send_message(client, json.dumps(old_data))


if __name__ == '__main__':
    # main
    w, h = model_wh("432x368")
    e = TfPoseEstimator(get_graph_path("mobilenet_thin"), target_size=(432, 368), trt_bool=False)
    poseLifting = Prob3dPose('lifting/models/prob_model_params.mat')

    cam = cv2.VideoCapture(0)

    old_data = {}

    server = WebsocketServer(port=PORT, host=HOST)
    server.set_fn_new_client(new_client)
    server.set_fn_client_left(client_left)
    server.set_fn_message_received(message_received)
    server.run_forever()

これでPython側の準備がおっけーです

Unity側実装

1. 3Dモデルの用意

まず初めに,動かしたい3Dモデルを用意しましょう.
今回はAssetStoreから"Unity-Chan!" Modelを使用させていただきました.

2. 必要なライブラリのインストール

SAFullBodyIKをcloneしてAssetsフォルダに移動させてください.
また,WebSocketの受信ができるように https://github.com/sta/websocket-sharp をcloneしビルドします.ビルド方法は以下のものがわかりやすいです
https://qiita.com/oishihiroaki/items/bb2977c72052f5dd5bd9

2. Unity側コード

本記事の参考元のコードを拝借させていただきました.

IKSetting.cs
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using WebSocketSharp;
using WebSocketSharp.Net;

public class IKSetting : MonoBehaviour {
    private BodyParts bodyParts;
    private string receivedJson;
    private WebSocket ws;

    [SerializeField, Range(10, 120)]
    float FrameRate;
    public List<Transform> BoneList = new List<Transform>();
    GameObject FullbodyIK;
    Vector3[] points = new Vector3[17];
    Vector3[] NormalizeBone = new Vector3[12];
    float[] BoneDistance = new float[12];
    float Timer;
    int[,] joints = new int[,] { { 0, 1 }, { 1, 2 }, { 2, 3 }, { 0, 4 }, { 4, 5 }, { 5, 6 }, { 0, 7 }, { 7, 8 }, { 8, 9 }, { 9, 10 }, { 8, 11 }, { 11, 12 }, { 12, 13 }, { 8, 14 }, { 14, 15 }, { 15, 16 } };
    int[,] BoneJoint = new int[,] { { 0, 2 }, { 2, 3 }, { 0, 5 }, { 5, 6 }, { 0, 9 }, { 9, 10 }, { 9, 11 }, { 11, 12 }, { 12, 13 }, { 9, 14 }, { 14, 15 }, { 15, 16 } };
    int[,] NormalizeJoint = new int[,] { { 0, 1 }, { 1, 2 }, { 0, 3 }, { 3, 4 }, { 0, 5 }, { 5, 6 }, { 5, 7 }, { 7, 8 }, { 8, 9 }, { 5, 10 }, { 10, 11 }, { 11, 12 } };
    int NowFrame = 0;

    float[] x = new float[17];
    float[] y = new float[17];
    float[] z = new float[17];

    bool isReceived = false;

    // Use this for initialization
    void Start () {

        ws = new WebSocket("ws://localhost:5000/");
        ws.OnOpen += (sender, e) =>
        {
            Debug.Log("WebSocket Open");
        };
        ws.OnMessage += (sender, e) =>
        {
            receivedJson = e.Data;
            Debug.Log("Data: " + e.Data);
            isReceived = true;
        };
        ws.OnError += (sender, e) =>
        {
            Debug.Log("WebSocket Error Message: " + e.Message);
        };
        ws.OnClose += (sender, e) =>
        {
            Debug.Log("WebSocket Close");
        };
        ws.Connect();

        ws.Send("");
    }

    // Update is called once per frame
    void Update () {

        Timer += Time.deltaTime;
        ws.Send("");

        if (Timer > (1 / FrameRate))
        {
            Timer = 0;
            PointUpdate();
        }
        if (!FullbodyIK)
        {
            IKFind();
        }
        else
        {
            IKSet();
        }
    }

    void OnDestroy()
    {
        ws.Close();
        ws = null;
    }

    void PointUpdate()
    {
        if (NowFrame < 600)
        {
            NowFrame++;
            if (isReceived)
            {
                bodyParts = JsonUtility.FromJson<BodyParts>(receivedJson);
                for (int i = 0; i < 17; i++)
                {
                    x[i] = bodyParts.body_parts[i].x;
                    y[i] = bodyParts.body_parts[i].y;
                    z[i] = bodyParts.body_parts[i].z;
                }

                isReceived = false;
            }

            for (int i = 0; i < 17; i++)
            {
                points[i] = new Vector3(x[i], y[i], -z[i]);
                Debug.Log(points[i]);
            }

            for (int i = 0; i < 12; i++)
            {
                NormalizeBone[i] = (points[BoneJoint[i, 1]] - points[BoneJoint[i, 0]]).normalized;
            }
        }
    }

    void IKFind()
    {
        FullbodyIK = GameObject.Find("FullBodyIK");
        if (FullbodyIK)
        {
            for (int i = 0; i < Enum.GetNames(typeof(OpenPoseRef)).Length; i++)
            {
                Transform obj = GameObject.Find(Enum.GetName(typeof(OpenPoseRef), i)).transform;
                if (obj)
                {
                    BoneList.Add(obj);
                }
            }
            for (int i = 0; i < Enum.GetNames(typeof(NormalizeBoneRef)).Length; i++)
            {
                BoneDistance[i] = Vector3.Distance(BoneList[NormalizeJoint[i, 0]].position, BoneList[NormalizeJoint[i, 1]].position);
            }
        }
    }

    void IKSet()
    {
        if (Math.Abs(points[0].x) < 1000 && Math.Abs(points[0].y) < 1000 && Math.Abs(points[0].z) < 1000)
        {
            BoneList[0].position = points[0] * 0.001f + Vector3.up * 0.8f;
        }
        for (int i = 0; i < 12; i++)
        {
            BoneList[NormalizeJoint[i, 1]].position = Vector3.Lerp(
                BoneList[NormalizeJoint[i, 1]].position,
                BoneList[NormalizeJoint[i, 0]].position + BoneDistance[i] * NormalizeBone[i]
                , 0.05f
            );
            DrawLine(BoneList[NormalizeJoint[i, 0]].position, BoneList[NormalizeJoint[i, 1]].position, Color.red);
        }
        for (int i = 0; i < joints.Length / 2; i++)
        {
            DrawLine(points[joints[i, 0]] * 0.001f + new Vector3(-1, 0.8f, 0), points[joints[i, 1]] * 0.001f + new Vector3(-1, 0.8f, 0), Color.blue);
        }
    }

    void DrawLine(Vector3 s, Vector3 e, Color c)
    {
        Debug.DrawLine(s, e, c);
    }
}

enum OpenPoseRef
{
    Hips,
    LeftKnee, LeftFoot,
    RightKnee, RightFoot,
    Neck, Head,
    RightArm, RightElbow, RightWrist,
    LeftArm, LeftElbow, LeftWrist
};

enum NormalizeBoneRef
{
    Hip2LeftKnee, LeftKnee2LeftFoot,
    Hip2RightKnee, RightKnee2RightFoot,
    Hip2Neck, Neck2Head,
    Neck2RightArm, RightArm2RightElbow, RightElbow2RightWrist,
    Neck2LeftArm, LeftArm2LeftElbow, LeftElbow2LeftWrist
};

[System.Serializable]
public class BodyParts
{
    public Position[] body_parts;
}

[System.Serializable]
public class Position
{
    public int id;
    public float x;
    public float y;
    public float z;
}

以上で準備完了です.

実行

server.pyを実行したあとにUnity側のPlayボタンを押してください.

まとめ

多少ラグはあるものの,予想より速く動きました.もう少し軽いモデルも探したら見つかりそうなのでまだ改良の予知はありそうです.

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

ノートPC+webカメラだけでモーションキャプチャぽいことをして3Dモデルを動かしてみた

概要

こちらの記事を見かけて,処理がそこそこ軽い姿勢推定モデルを使ったらノートPCとwebカメラ(内蔵カメラ)だけでモーションキャプチャぽいことができそうなのでやってみました.
流れとして元記事とほとんど同じです.

必要なもの

  • Unity2018.1.9 f2
  • Python実行環境

手順

Python側

1. 姿勢推定モデルの用意

以下のものをcloneしてください
https://github.com/ildoonet/tf-pose-estimation/tree/master
これを使用することで2次元画像に写る人物の姿勢を推定できます.

2. 3次元情報の復元

モーションキャプチャぽいことをするためには3次元情報が必要になってきます.
1.のモデルは2次元情報しか得られません.
そこで先ほどのリポジトリのdevelブランチにある処理を使用して3次元情報を取得します.
(元々masterにあったぽいけどなくなってた)

以下のものをcloneしてください
https://github.com/ildoonet/tf-pose-estimation/tree/devel
そして devel/src/lifting フォルダを masterに移動させます

3. WebSocketサーバの用意

今回のシステムではPython側で人物の姿勢推定などの処理を行い,Unity側では3Dモデルの表示のみを行います.
PythonとUnityの情報通信部分を今回はWebSocketを用いて実装します.

以下のコマンドを実行
pip install git+https://github.com/Pithikos/python-websocket-server

server.py
import logging

import cv2
import json
import numpy as np
import common

from tf_pose.estimator import TfPoseEstimator
from tf_pose.networks import get_graph_path, model_wh
from websocket_server import WebsocketServer
from lifting.prob_model import Prob3dPose

PORT = 5000
HOST = '127.0.0.1'

# logger_setup
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
handler = logging.StreamHandler()
handler.setFormatter(logging.Formatter(' %(module)s -  %(asctime)s - %(levelname)s - %(message)s'))
logger.addHandler(handler)


def create_json(pose3d):
    global old_data

    data = {'body_parts': []}

    """
    // 0 :Hip
    // 1 :RHip
    // 2 :RKnee
    // 3 :RFoot
    // 4 :LHip
    // 5 :LKnee
    // 6 :LFoot
    // 7 :Spine
    // 8 :Thorax
    // 9 :Neck/Nose
    // 10:Head
    // 11:LShoulder
    // 12:LElbow
    // 13:LWrist
    // 14:RShoulder
    // 15:RElbow
    // 16:RWrist
    """

    for i in range(17):
        data['body_parts'].append({'id': i, 'x': pose3d[0][0][i], 'y': pose3d[0][2][i], 'z': pose3d[0][1][i]})

    old_data = data
    return data


def new_client(client, server):
    logger.info('NewClient {}:{} has left.'.format(client['address'][0], client['address'][1]))


def client_left(client, server):
    logger.info('Client {}:{} has left.'.format(client['address'][0], client['address'][1]))


def message_received(client, server, message):
    _, image = cam.read()

    humans = e.inference(image, resize_to_default=(w > 0 and h > 0), upsample_size=4.0)

    pose_2d_mpiis = []
    visibilities = []

    standard_w = 640
    standard_h = 480

    try:
        pose_2d_mpii, visibility = common.MPIIPart.from_coco(humans[0])
        pose_2d_mpiis.append([(int(x * standard_w + 0.5), int(y * standard_h + 0.5)) for x, y in pose_2d_mpii])
        visibilities.append(visibility)
        pose_2d_mpiis = np.array(pose_2d_mpiis)
        visibilities = np.array(visibilities)
        transformed_pose2d, weights = poseLifting.transform_joints(pose_2d_mpiis, visibilities)
        pose_3d = poseLifting.compute_3d(transformed_pose2d, weights)
        print(pose_3d)
        server.send_message(client, json.dumps(create_json(pose_3d)))

    except :
        server.send_message(client, json.dumps(old_data))


if __name__ == '__main__':
    # main
    w, h = model_wh("432x368")
    e = TfPoseEstimator(get_graph_path("mobilenet_thin"), target_size=(432, 368), trt_bool=False)
    poseLifting = Prob3dPose('lifting/models/prob_model_params.mat')

    cam = cv2.VideoCapture(0)

    old_data = {}

    server = WebsocketServer(port=PORT, host=HOST)
    server.set_fn_new_client(new_client)
    server.set_fn_client_left(client_left)
    server.set_fn_message_received(message_received)
    server.run_forever()

これでPython側の準備がおっけーです

Unity側

1. 3Dモデルの用意

まず初めに,動かしたい3Dモデルを用意しましょう.
今回はAssetStoreから"Unity-Chan!" Modelを使用させていただきました.

2. 必要なライブラリのインストール

SAFullBodyIKをcloneしてAssetsフォルダに移動させてください.
また,WebSocketの受信ができるように https://github.com/sta/websocket-sharp をcloneしビルドします.ビルド方法は以下のものがわかりやすいです
https://qiita.com/oishihiroaki/items/bb2977c72052f5dd5bd9

2. Unity側コード

本記事の参考元のコードを拝借させていただきました.

IKSetting.cs
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using WebSocketSharp;
using WebSocketSharp.Net;

public class IKSetting : MonoBehaviour {
    private BodyParts bodyParts;
    private string receivedJson;
    private WebSocket ws;

    [SerializeField, Range(10, 120)]
    float FrameRate;
    public List<Transform> BoneList = new List<Transform>();
    GameObject FullbodyIK;
    Vector3[] points = new Vector3[17];
    Vector3[] NormalizeBone = new Vector3[12];
    float[] BoneDistance = new float[12];
    float Timer;
    int[,] joints = new int[,] { { 0, 1 }, { 1, 2 }, { 2, 3 }, { 0, 4 }, { 4, 5 }, { 5, 6 }, { 0, 7 }, { 7, 8 }, { 8, 9 }, { 9, 10 }, { 8, 11 }, { 11, 12 }, { 12, 13 }, { 8, 14 }, { 14, 15 }, { 15, 16 } };
    int[,] BoneJoint = new int[,] { { 0, 2 }, { 2, 3 }, { 0, 5 }, { 5, 6 }, { 0, 9 }, { 9, 10 }, { 9, 11 }, { 11, 12 }, { 12, 13 }, { 9, 14 }, { 14, 15 }, { 15, 16 } };
    int[,] NormalizeJoint = new int[,] { { 0, 1 }, { 1, 2 }, { 0, 3 }, { 3, 4 }, { 0, 5 }, { 5, 6 }, { 5, 7 }, { 7, 8 }, { 8, 9 }, { 5, 10 }, { 10, 11 }, { 11, 12 } };
    int NowFrame = 0;

    float[] x = new float[17];
    float[] y = new float[17];
    float[] z = new float[17];

    bool isReceived = false;

    // Use this for initialization
    void Start () {

        ws = new WebSocket("ws://localhost:5000/");
        ws.OnOpen += (sender, e) =>
        {
            Debug.Log("WebSocket Open");
        };
        ws.OnMessage += (sender, e) =>
        {
            receivedJson = e.Data;
            Debug.Log("Data: " + e.Data);
            isReceived = true;
        };
        ws.OnError += (sender, e) =>
        {
            Debug.Log("WebSocket Error Message: " + e.Message);
        };
        ws.OnClose += (sender, e) =>
        {
            Debug.Log("WebSocket Close");
        };
        ws.Connect();

        ws.Send("");
    }

    // Update is called once per frame
    void Update () {

        Timer += Time.deltaTime;
        ws.Send("");

        if (Timer > (1 / FrameRate))
        {
            Timer = 0;
            PointUpdate();
        }
        if (!FullbodyIK)
        {
            IKFind();
        }
        else
        {
            IKSet();
        }
    }

    void OnDestroy()
    {
        ws.Close();
        ws = null;
    }

    void PointUpdate()
    {
        if (NowFrame < 600)
        {
            NowFrame++;
            if (isReceived)
            {
                bodyParts = JsonUtility.FromJson<BodyParts>(receivedJson);
                for (int i = 0; i < 17; i++)
                {
                    x[i] = bodyParts.body_parts[i].x;
                    y[i] = bodyParts.body_parts[i].y;
                    z[i] = bodyParts.body_parts[i].z;
                }

                isReceived = false;
            }

            for (int i = 0; i < 17; i++)
            {
                points[i] = new Vector3(x[i], y[i], -z[i]);
                Debug.Log(points[i]);
            }

            for (int i = 0; i < 12; i++)
            {
                NormalizeBone[i] = (points[BoneJoint[i, 1]] - points[BoneJoint[i, 0]]).normalized;
            }
        }
    }

    void IKFind()
    {
        FullbodyIK = GameObject.Find("FullBodyIK");
        if (FullbodyIK)
        {
            for (int i = 0; i < Enum.GetNames(typeof(OpenPoseRef)).Length; i++)
            {
                Transform obj = GameObject.Find(Enum.GetName(typeof(OpenPoseRef), i)).transform;
                if (obj)
                {
                    BoneList.Add(obj);
                }
            }
            for (int i = 0; i < Enum.GetNames(typeof(NormalizeBoneRef)).Length; i++)
            {
                BoneDistance[i] = Vector3.Distance(BoneList[NormalizeJoint[i, 0]].position, BoneList[NormalizeJoint[i, 1]].position);
            }
        }
    }

    void IKSet()
    {
        if (Math.Abs(points[0].x) < 1000 && Math.Abs(points[0].y) < 1000 && Math.Abs(points[0].z) < 1000)
        {
            BoneList[0].position = points[0] * 0.001f + Vector3.up * 0.8f;
        }
        for (int i = 0; i < 12; i++)
        {
            BoneList[NormalizeJoint[i, 1]].position = Vector3.Lerp(
                BoneList[NormalizeJoint[i, 1]].position,
                BoneList[NormalizeJoint[i, 0]].position + BoneDistance[i] * NormalizeBone[i]
                , 0.05f
            );
            DrawLine(BoneList[NormalizeJoint[i, 0]].position, BoneList[NormalizeJoint[i, 1]].position, Color.red);
        }
        for (int i = 0; i < joints.Length / 2; i++)
        {
            DrawLine(points[joints[i, 0]] * 0.001f + new Vector3(-1, 0.8f, 0), points[joints[i, 1]] * 0.001f + new Vector3(-1, 0.8f, 0), Color.blue);
        }
    }

    void DrawLine(Vector3 s, Vector3 e, Color c)
    {
        Debug.DrawLine(s, e, c);
    }
}

enum OpenPoseRef
{
    Hips,
    LeftKnee, LeftFoot,
    RightKnee, RightFoot,
    Neck, Head,
    RightArm, RightElbow, RightWrist,
    LeftArm, LeftElbow, LeftWrist
};

enum NormalizeBoneRef
{
    Hip2LeftKnee, LeftKnee2LeftFoot,
    Hip2RightKnee, RightKnee2RightFoot,
    Hip2Neck, Neck2Head,
    Neck2RightArm, RightArm2RightElbow, RightElbow2RightWrist,
    Neck2LeftArm, LeftArm2LeftElbow, LeftElbow2LeftWrist
};

[System.Serializable]
public class BodyParts
{
    public Position[] body_parts;
}

[System.Serializable]
public class Position
{
    public int id;
    public float x;
    public float y;
    public float z;
}

以上で準備完了です.

実行

server.pyを実行したあとにUnity側のPlayボタンを押してください.

まとめ

多少ラグはあるものの,予想より速く動きました.
もう少し軽いモデルも探したら見つかりそうなのでまだ改良の予知はありそうです.

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

Unity CodeGeneratorをMacで使えるようにする。MessagePack-CSharp

環境

MacでCodeGeneratorが使えない。。。

Windows -> MessagePack -> CodeGenerator
スクリーンショット 2020-02-27 22.08.02.png

MacではCodeGeneratorが上記画面から進みません。
それを解決します。

今回の方法は複数人での開発に向きません。
複数人で開発する場合はCodeGeneratorの担当者を一人決める必要があります。他に方法が見つかれば、この記事を書き直します。

必要なアプリがインストール済みかチェックする

ターミナルを開き、下記手順で進めます。

1 .NET Core 3 Runtime

dotnet --version

ターミナルに上記を入力しEnterを押すと3.1.102などのバージョンが表示されます。表示されなければ未インストールなのでのインストールしてください。

.NET Core 3 Runtimのダウンロード

2 MessagePackCompiler(mpc)

dotnet tool list -g

messagePack.generatorが未インストールであればターミナルで下記を実行しインストールしてください。

dotnet tool install --global messagePack.generator

必要なアプリのパスを取得

1 .NET Core 3 Runtime

type dotnet

ターミナルに上記を入力しEnterを押すとdotnet is hashed (/usr/local/share/dotnet/dotnet)の形で出力されます。()内のパスを後ほど使用します。

2 MessagePackCompiler(mpc)

type dotnet-mpc

同様に()内のパスを後ほど使用します。

コードを一部書き換える

書き換えてしまうのでWindowsで動作しなくなります。もしWindowsでも使う予定があればディレクティブなど使って工夫してください。違うMacでもパスが代わるので対応が必要になります。

1 Assets/Scripts/MessagePack/Unity/MessagePackWindow.csを開きます。
2 InvokeProcessStartAsyncの引数dotnetをすべて上記で取得したパスに修正します。例)

var list = await InvokeProcessStartAsync("/usr/local/share/dotnet/dotnet", "tool list -g");

3 153行目 ProcessHelper.InvokeProcessStartAsyncの引数dotnet-mpcを上記で取得したパスに修正します。例)

var log = await ProcessHelper.InvokeProcessStartAsync("/Users/account/.dotnet/tools/dotnet-mpc", "mpc " + commnadLineArguments);

最後に

MessagePack-CSharpいいですよね!
製作者のCysharp, Inc. CEO/CTOの河合 宜文さんに日々感謝です。
いつかお逢いしたいものです。
多分緊張しすぎてしゃべれないと思いますが。。。

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

CodeGeneratorをMacで使えるようにする。MessagePack for C# [Unity]

環境

MacでCodeGeneratorが使えない。。。

Windows -> MessagePack -> CodeGenerator
スクリーンショット 2020-02-27 22.08.02.png

MacではCodeGeneratorが上記画面から進みません。
それを解決します。

今回の方法は複数人での開発に向きません。
複数人で開発する場合はCodeGeneratorの担当者を一人決める必要があります。他に方法が見つかるか、またはOfficialで修正があればこの記事を書き直します。

基礎知識

AOT Code Generationとは?

MessagePack for C#をIL2CPP上で動作されるには中間コードが必要になります。それを出力するのがAOT Code Generationです。iOS/AndroidアプリをリリースするにはIL2CPPで出力する必要があるので必須となります。

CodeGeneratorとは?

スクリーンショット 2020-02-28 1.18.00.png

Unity上から「Generate」ボタンをクリックするだけで中間コードを出力してくれる便利機能です。

必要なアプリがインストール済みかチェックする

ターミナルを開き、下記手順で進めます。

1 .NET Core 3 Runtime

dotnet --version

ターミナルに上記を入力しEnterを押すと3.1.102などのバージョンが表示されます。表示されなければ未インストールなのでのインストールしてください。

.NET Core 3 Runtimのダウンロード

2 MessagePackCompiler(mpc)

dotnet tool list -g

messagePack.generatorが未インストールであればターミナルで下記を実行しインストールしてください。messagePack.generatorの文言が表示されなければ未インストールです。

dotnet tool install --global messagePack.generator

必要なアプリのパスを取得

1 .NET Core 3 Runtime

type dotnet

ターミナルに上記を入力しEnterを押すとdotnet is hashed (/usr/local/share/dotnet/dotnet)の形で出力されます。()内のパスを後ほど使用します。

2 MessagePackCompiler(mpc)

type dotnet-mpc

同様に()内のパスを後ほど使用します。

コードを一部書き換える

書き換えてしまうのでWindowsで動作しなくなります。もしWindowsでも使う予定があればディレクティブなど使って工夫してください。違うMacでもパスが代わるので対応が必要になります。

1 Assets/Scripts/MessagePack/Unity/MessagePackWindow.csを開きます。
2 InvokeProcessStartAsyncの第一引数dotnetをすべて上記で取得したパスに修正します。例)

var list = await InvokeProcessStartAsync("/usr/local/share/dotnet/dotnet", "tool list -g");

3 153行目 ProcessHelper.InvokeProcessStartAsyncの第一引数dotnet-mpcを上記で取得したパスに修正します。例)

var log = await ProcessHelper.InvokeProcessStartAsync("/Users/account/.dotnet/tools/dotnet-mpc", "mpc " + commnadLineArguments);

以上です!
CodeGeneratorを開く(Windows -> MessagePack -> CodeGenerator)と下記のような画面が表示されれば成功です。
もし表示されなければひとつずつ見直してみてください。

スクリーンショット 2020-02-28 1.18.00.png

設定

下記のように設定しました。

[dllの場所]-i input path(csproj or directory):

../Assembly-CSharp.csproj

[中間コード出力先]-o output filepath(.cs) or directory(multiple):

拡張子.csを入れるとひとつのファイルにすべての中間コードをまとめてくれます(多分)。
逆に下記のように.csを含めなければクラス別に出力+その出力されたクラスを参照するファイルが出力されます。

Scripts/MessagePackGenerated

最後に

MessagePack for C#いいですよね!
製作者のCysharp, Inc. CEO/CTOの河合 宜文さんに日々感謝です。
いつかお逢いしたいものです。
多分緊張しすぎてしゃべれないと思いますが

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