Oculus Quest2でのOnApplicationFocusとOnApplicationPauseの挙動まとめ【Oculus,Quest2,Unity】

いろいろ詰まったためメモしておきます. Questでのアプリ開発でアプリを終了したときに何かの処理をしたいと思うことが多々あるかと思います. (間違えている可能性もがあるため,コメントまたはtwitterのDMにてご指摘等お待ちしています)

環境

  • Unity 2019.4.5f1
  • Oculus XR Plugin 1.7.0
  • XR Plugin Management 3.2.16
  • Oculus Integration 25.0

挙動まとめ表

関数名 UI表示[1] 時間経過[2] アプリ終了
OnApplicationFocus(bool hasFocus) 変化なし true → false true → false
OnApplicationPause(bool pauseStatus) false → true 変化なし false → true

[1] Oculusボタンを押してメニュー画面を出した状態
[2] HMDを外してHMDが非アクティブになった状態

またそれぞれの関数のEventStatusの初期値は以下の通りです.

  • OnApplicationFocus(bool hasFocus) : true
  • OnApplicationPause(bool pauseStatus) : false

その他の関数の挙動

  • OnDestroy() 呼ばれなかった
  • OnApplicationQuit() 呼び出されなかった

追記 以下のサイトより,Oculus Goの時代から残る問題として上記の二つが呼ばれない問題が残っているそうです. forum.unity.com

関連サイト

developer.oculus.com

stackoverflow.com

qiita.com

greenkour.hateblo.jp

pafu-of-duck.hatenablog.com

developer.android.com

docs.unity3d.com

NotionにTrelloのアーカイブを毎日記録する【Trello,Notion,Python,GCE】

自分のやったタスクを記録しておきたいとおもい,前にEvernoteにTrelloのアーカイブを保存する記事を書きました. その後Notionを知りこちらのほうが便利だと思い乗り換えたのですが,アーカイブの保存のスクリプトの切り替えがやっと終わったのでメモです GCEのcronで毎日0:00に実行してNotionに保存するようにしています

ayousanz.hatenadiary.jp

環境

使用ライブラリ

準備

pageIDとtokenの取得

DBとなるページを公開します. PAGE_IDとTOKENが必要だと言うことで,こちらのサイトを参考にして取得します.

trello側のkey,tokenの取得

こちらのページにアクセスをして,key,tokenを取得します

ライブラリのインストール

使用するライブラリをインストールします

pip install py-trello

pip install notion

Trelloからアーカイブしたカードを取得する

from trello import TrelloClient
import datetime

client = TrelloClient(
    api_key='your trello api key',
    api_secret='your trello api secret',
    token='your trello token'
)


def get_cards():

    cards = []

    board = client.get_board("board id")
    for card in board.all_cards():
        jp_time = card.dateLastActivity.astimezone(datetime.timezone(datetime.timedelta(hours=9)))
        if card.closed:
            card.dateLastActivity = jp_time
            cards.append(card)
    return cards

Notionのtableに書き込む

from notion.client import NotionClient

import get_trello_data

# login
token_v2 = 'token'
client = NotionClient(token_v2=token_v2)

url = "url"
cv = client.get_collection_view(url)

for card in get_trello_data.get_cards():
    row = cv.collection.add_row()
    row.name = card.name
    row.listName = card.get_list().name
    row.task_completion_date = card.dateLastActivity
    row.description = card.description

print("終了")

GCEにサーバーを立てて,cronで毎日実行する

GCEのサーバーの立て方等はいかのサイトを参考にしました.

qiita.com

Anacondaのインストール&ライブラリのインストール

  1. wget http://repo.continuum.io/archive/Anaconda3-2020.11-Linux-x86_64.sh でダウンロードして,ダウンロードしたファイルを bash Anaconda3-2020.11-Linux-x86_64.sh で実行してインストールします.

  2. pip install py-trello ,pip install notion この二つもインストールします.

NotionのAPI制限を回避するためにNotinoスクリプトの書き換え

2021-3-8現在こちらの問題によりNotionライブラリを自分でいけないみたいです. t.co

AnacondaでGUIを簡単に使う方法がすぐにわからなかったので,あきらめてviで書き換えていきましょう(数行なのでなんとかなりました)

上のissueによると store.pyclient.py を書き換えないといけないみたいなので,この二つのファイルパスを調べます.

find . -name "store.py"こちらのコマンドを使って指定したファイルを探します.

./anaconda3/lib/python3.8/site-packages/_pytest/store.py
./anaconda3/lib/python3.8/site-packages/notion/store.py
./anaconda3/pkgs/pytest-6.1.1-py38_0/lib/python3.8/site-packages/_pytest/store.py
./anaconda3/pkgs/pytest-6.2.2-py38h06a4308_2/lib/python3.8/site-packages/_pytest/store.py

という結果が返ってきたので,二番目のパスのファイルを書き換えます.

viなどの使い方は調べたらすぐに出てくると思うので割愛します.

cronの設定

crontab -e を使ってcronの設定をします

日付が変わると記事実行をしてほしいので,00:01に実行するように設定しましょう. また,エラーが出て時にもわかるようにログも残すようにします.

1 0 * * * /home/your name/anaconda3/bin/python3.8 /home/your name/save_trello_task/post_notion.py > /home/your name/save_trello_task/post_notion.log 2 > &1

参考サイト

note.com

一覧からScriptableObjectのカスタム作成と検索ができるScriptableObject拡張【Odin,Unity】

ゲームで敵を複数作るときに ScriptableObject を使っています.少ない数なら自分で画像や名前を追加して生成するというのはいいかもしれません. しかし,これを List<ScriptableObject>で管理する場合作成したものをリストに追加するというのは割と手間です そこで,リストから ScriptableObjectを自分の作りたいようにカスタマイズしながら作成でき,これらを検索できるようにエディタ拡張をOdinを使って作成していきます.

Demo

敵データの作成

f:id:ayousanz:20201226134947g:plain

各データからも再度ステータスを調節することができます

敵の個別データからステータスを再生成

f:id:ayousanz:20201226134902g:plain

作成したデータの検索

f:id:ayousanz:20201226134759g:plain

実装内容

  • リストから敵を作成(名前と画像の指定)
  • 敵の特徴を指定 →特徴に応じてステータスを生成時に決定できます
  • 重複した名前の敵が作られない,空の名前の敵などのミス作成の防止機能(ボタンにヴァリエーション機能)
  • リストの中に敵の検索機能(特徴を指定)
  • 各データからステータスの再生成

Odinについて

既存のワークフローに完璧に展開できる簡単な統合で、Odin を使ってすべてをシリアル変換することができ、80種類以上あるインスペクター属性やその他の機能を備えた、定型コードのない Unity を利用できます。

assetstore.unity.com

実装について

リストから敵を作成

実装方法

  1. CreateInstance<EnemyInfo>() を使って,ScriptableObject を作成します.
  2. enemyInfoの変数を設定します
  3. AssetDatabase.CreateAsset(enemy,createPath) を使って特定のフォルダに .assetを作成します

Code

private const string DATA_PATH = "Assets/TurnGame/Scripts/ScriptableObject/EnemyInfo/EnemyInfos/";

    [BoxGroup("敵生成"), LabelText("名前")] public new string name;
    [BoxGroup("敵生成"), LabelText("敵Sprite"),PreviewField(64)] public List<Sprite> sprites;
    [BoxGroup("敵生成"), LabelText("高HP")] public bool isHighHp;
    [BoxGroup("敵生成"), LabelText("高ATK")] public bool isHighAtk;
    [BoxGroup("敵生成"), LabelText("高攻撃速度")] public bool isHighAtkInterval;

    [Button("敵を追加",ButtonSizes.Large),BoxGroup("敵生成")][DisableIf("IsValidation")]
    public void CreateEnemy()
    {
        var enemy = CreateInstance<EnemyInfo>();
        enemy.name = name;
        enemy.enemySprites = sprites;
        if (isHighHp)
        {
            enemy.hp = Random.Range(3, 5);
            enemy.isHighHp = true;
        }
        else
        {
            enemy.hp = Random.Range(1, 3);
            enemy.isHighHp = false;
        }

        if (isHighAtk)
        {
            enemy.atk = Random.Range(3, 5);
            enemy.isHighAtk = true;
        }
        else
        {
            enemy.atk = Random.Range(1, 3);
            enemy.isHighAtk = false;
        }

        if (isHighAtkInterval)
        {
            enemy.atkInterval = Random.Range(3f, 6f);
            enemy.isHighAtkInterval = true;
        }
        else
        {
            enemy.atkInterval = Random.Range(6f, 10f);
            enemy.isHighAtkInterval = false;
        }
        var createPath = DATA_PATH + name + ".asset";
        AssetDatabase.CreateAsset(enemy,createPath);
        enemies.Add(enemy);
    }

ミス作成の防止機能

実装方法

  1. ListのExistsを使ってリスト内に同じものがあるかどうかを検索します.
  2. attributeの部分に [DisableIf("IsValidation")] を追加して,以下の関数を作成しました.

Code

private bool IsValidation()
    {   
        //名前が空鶴またはスペースの時,リストの中に同じ名前がある場合はボタンを非アクティブにする
        return name.IsNullOrWhitespace() || enemies.Exists(lName => lName.name == name);
    }

各データからステータスの再生成

実装方法

リストから作成する方法と同じ方法で,ステータスを再生成します.

参考サイト

baba-s.hatenablog.com

kan-kikuchi.hatenablog.com

her-hibari-blog.ssl-lolipop.jp

bocchi-games.hatenablog.com

BeatSaverのカスタム曲をYouTubeから好きな曲を作成する【VR,Oculus】

BeatSaverはVRゲームの中では私は一番いいと思っています. 体を動かすので運動を兼ねてゲームができるし,狭い空間でもできるし,あまり酔わないってのもいいですね

Webサイトからカスタム曲を作成する

beatsage.com

こちらのサイトを使用します.

  1. 作成したいYouTubeのリンクをURL欄に入れます.

  2. 難易度設定などを行います.

  3. 作成を押すと1-2分ほど待つとできたマップがダウンロードされます

Windowsアプリから作成する

github.com

こちらのアプリを使用します. 詳しい使い方はReadMeを日本語訳すれば使い方等はわかると思います

いくつかできることの説明を書いておきます * YouTubeのリストをそのまま指定できる * 設定などは保存される * 既存で同じファイルがあった場合などの設定も変更可能 * ほぼWebサイトでできることはこちらでもできる

Ryzen5900xにCPUをアップデートする(B450:ASRock)【Ryzen CPU】

ちょっと動かなくなったり等々大変だったので,今後同じような方が居たら見てください 普通はその辺のことを知っている人が変えるので,見ることもないかもですが..

はじめに

マザーボードが変えるCPUに対応していることを確認しましょう

自分のマザーボードを確認する

自分のマザーボードを確認するのに以下を使いました

forest.watch.impress.co.jp

f:id:ayousanz:20201219000741p:plain

私の場合,上のようにB450 Steel Legendというのが出ています.

(買ったときにコスパがよかったです)

www.amazon.co.jp

今回はRyzen5900xに変えています. 5000番台の場合は以下同じような手順でやっていくといいと思います

kakaku.com

(2020/12/19現在)

Ryzen5900x対応のBIOSにアップデートする(重要)

ここにたどり着くまでに数十分かかったので,わかるまで結構焦りました.. 割と上位の者にする場合はアップデートしないといけないのですね

まずはやり方は以下を参考にしています.

www.asrock.com

1 以下のサイトから5000に対応したBIOSのアップデートファイルをダウンロードします. 2020/12/19現在 3.7.0 で動きました

www.asrock.com

2 ダウンロードしたものを解答し,USBに入れます.

3 BIOSの設定画面からUSBを起動し,BIOSのアップデートを行います. f:id:ayousanz:20201219001814p:plain

4 終わるまで待ちます(少し時間がかかります)

参考文献等

les2.net

www.asrock.com

CPUを新しいものに変える

www.dospara.co.jp

やり方はこのページを見る方ならわかっていると思いますが,参考は上のサイトを見てもらえると助かります.

(グリスは塗りすぎないようにしましょう)

今回はダイヤモンドグリスを使いました

調べた中(初心者向け)では熱伝導率が一番よかったです

https://www.amazon.co.jp/Thermalright-%E7%86%B1%E4%BC%9D%E5%B0%8E%E6%80%A713-8W-m-k%E3%81%AE%E9%AB%98%E6%80%A7%E8%83%BDCPU%E3%82%B0%E3%83%AA%E3%82%B9-TF8-2g/dp/B07JN1ZRHG

起動確認

無事にCPUを変えることができましたね これでつよつよPCデス

注意点?

CPUの交換後,メモリスロットの認識位置がずれていました. 複数枚差している場合は,認識しないところが出てくる可能性があるので,メモリがすべて読み込まれているかを確認してください

タワーディフェンス型?ゲームを作ってみる【Unity,DoozyUI,Odin】

(2021/3/9 修正) こちらのオンライン版は作成しないことにしました. 記事のタイトルを変更しました

Demo

www.youtube.com

公開場所

t.co

開発環境

どんなゲームを作る?

何事も簡単なゲームからやっていきましょう
複雑にするとわからなくなっていきます.

(作成中なので,どんどん変わっていきます) ←ここ重要

以下のようなゲームを作っていきたいと思います.
(図の適当は気にしないでください) f:id:ayousanz:20201207222705j:plain

  • ひたすらボスを殴る
  • チャットができたらうれしい
  • アニメションもつけたい
  • HPだけオンラインで共有

簡易アニメションの実装

アタック,ダメージ

f:id:ayousanz:20201207224105g:plain

上のような動きを作成します.

  • アタック:前に移動後,元の位置に戻る
  • ダメージ:後ろに移動後,元の位置に戻る

アタックとダメージのアニメションの実装はほぼ同じです

public void Attack(int attack value)
    {
        transform.DOMoveX(-1f,0.5f).SetRelative(true)
            .OnComplete(() =>
            {
                transform.DOMoveX(1f, 0.5f).SetRelative(true);
            });
    }

スタート画面とゲーム画面の切り替えの実装

以下のような画面の切り替えを行っていきます.

使用アセットはOdinを主に使っています.

f:id:ayousanz:20201208021714g:plain

Odinの設定

まずは使用するViewを作成します. f:id:ayousanz:20201208022011p:plain

で,スタート画面からゲーム画面に移行するためのボタンも作成します f:id:ayousanz:20201208022059p:plain

次にgraphを作成します.

f:id:ayousanz:20201208022130p:plain

OdinのViewが Show,Hideされるときのアニメションはそれぞれ以下のように設定しています. f:id:ayousanz:20201208022237p:plain

プレイボタンのアニメション

こんな感じにスタートを強調?する感じのアニメションをつけています. f:id:ayousanz:20201208024155g:plain

しっかり使わなくなったタイミングでDOTweenをKillしてあげましょう

private void GameStartLogo()
    {
        playButton.transform.DOScale(0.05f, 1f)
            .SetRelative(true)
            .SetEase(Ease.OutQuad)
            .SetLoops(-1, LoopType.Yoyo);
    }

private void OnDisable()
    {
        _tweener.Kill();
    }

敵データの作成

ayousanz.hatenadiary.jp

参考サイト

unity-yuji.xyz

Fadeで出現・消滅するshieldの実装

Demo

f:id:ayousanz:20201213181152g:plain

実装内容

DOTween のSequenceを再利用するときは 以下を使います

Sequence生成時

.SetAutoKill(false)
.SetLink(gameObject);

Sequence実行時

tweener.Restart();

(注)参考サイトでは,.Pause() を入れていますが,私の場合DOTweenの初期化の時に DOTween.defaultAutoPlay = AutoPlay.None で自動スタートをoffにしているので,入れていません.

以下アニメーションの実装部分のスクリプトです

public void Guard(bool guard action)
    {
        if (guardAction)
        {
            var guardStartSequence = DOTween.Sequence()
                .OnStart(() =>
                {
                    _isGuard = true;
                    PlayerStatus.Mp -= PlayerStatus.GuardCost;
                })
                .Append(shieldSpriteRenderer.DOFade(1f, 0.5f))
                .SetAutoKill(false)
                .SetLink(gameObject);
            guardStartSequence.Restart();
        }
        else
        {
            var guardEndSequence = DOTween.Sequence()
                .Append(shieldSpriteRenderer.DOFade(0f, 0.5f))
                .OnComplete(() =>
                {
                    _isGuard = false;
                })
                .SetAutoKill(false)
                .SetLink(gameObject);
            guardEndSequence.Restart();
        }
    }

DOTweenのSequenceを使いまわすときの注意点(2020/12/15 追記)

あれこれ2,3時間悩んでいたのですが,多分あっているはずです

Sequenceを使いまわすときとは PrependCallback を使いましょう

参考サイト

qiita.com

特定の条件でメッセージを表示する

Demo

f:id:ayousanz:20210103234826g:plain

実装方法

BoolReactiveProperty を使用してTrueになったときにアニメーションが実行されるようにしています.

private readonly BoolReactiveProperty _isMessageInfoShow = new BoolReactiveProperty();

 public void Initialize()
    {
        _isMessageInfoShow.Value = false;

        _isMessageInfoShow.Where(x => x).Subscribe(_ =>
        {
            _messageInfoSequence.Restart();
        }).AddTo(this);
    }

敵の攻撃タイミングを可視化する (2021/1/9追記)

Demo

f:id:ayousanz:20210109155734g:plain

実装方法

UniRxの Observable.Interval を使うと数行できてます

時間をカウントするところは以下

Observable.Interval(TimeSpan.FromSeconds(0.1f))
            .Where(_=> !_playerController.IsMove.Value && !_enemyController.IsMove.Value)
            .Subscribe(_ =>
            {
                _enemyActionTime += 0.1f;
                _enemyController.SetEnemyAttackTimeSlider(_enemyActionTime);
                if (_enemyController.AtkInterval < _enemyActionTime)
                {
                    Damage();
                    _enemyActionTime = 0f;
                }
            }).AddTo(this);

スライドの値を変化しているのは以下

public void SetEnemyAttackTimeSlider(float time)
    {
        enemyAttackTime.value = AtkInterval - time;
    }

使用しているアセット

assetstore.unity.com

assetstore.unity.com

assetstore.unity.com

assetstore.unity.com

assetstore.unity.com

assetstore.unity.com

使用している素材

フォント

moji-waku.com

戦闘背景画像

pipoya.net

NotionAPI(Python)を触ってみる【Notion,Python】

最近Notionというものを教えてもらいました!!(今更ですが) TrelloとEvernoteから乗り換えるのにいろいろと連携したりGASとの連携とかもあるので,scriptの移行のためにNotionAPIを触っていきたいと思います

<span style="color: #ff0000">NotionのAPIは2020/12現在 APIは非公式になっています</span>

今回は試しに触ってみるだけになるかと思われます.

以下NotionAPIとTrelloで毎日をタスクをアーカイブにしたものです

ayousanz.hatenadiary.jp

非公式サイトを確認する

github.com

このサイトで多くの方がすでに作られているのでこちらを使っていきます.

使い方等はこちらのReadMeに書かれています(今回はほぼReadMeを試すだけ??)

Notion Tokenを取得する

以下のサイトにやり方は書かれているので,参考にしてください

www.notion.so

環境を構築する

` Note: the latest version of notion-py requires Python 3.5 or greater. ‘と書いているので,ちょっと現在最新versinoのPython 3.9.1を使ってみたいと思います.

AnacondaでもPython3.9~に対応してるみたいなので,安心です

  1. conda crate -n notion python=3.9 で作成します.
  2. requirements.txtがあったので,こちらも入れていきます.
  3. pip install -r requirements.txt で依存ライブラリを入れていきます
  4. pip install notion なぜか Notionライブラリだけ入らないので,追加で入れます.

タイトルの取得とタイトルの更新

Quickstart
この部分をやってみます. 先ほど取得したTokenをいれて,テスト用のページの作成→URLを取得します

from notion.client import NotionClient

# Obtain the `token_v2` value by inspecting your browser cookies on a logged-in (non-guest) session on Notion.so
client = NotionClient(token_v2="your token")

# Replace this URL with the URL of the page you want to edit
page = client.get_block("page url")

print("The old title is:", page.title)

# Note: You can use Markdown! We convert on-the-fly to Notion's internal formatted text data structure.
page.title = "The title has now changed, and has *live-updated* in the browser!"

実行すると以下のようになります. f:id:ayousanz:20201212143306g:plain

tableの操作(2021-3-2 追記)

tableの取得

url = "page url"
cv = client.get_collection_view(url)

for row in cv.collection.get_rows():
    print(row.title)

tableへの書き込み

row."なまえ"の部分は自分のtableのコラムの名前と一致しないと書き込みできないみたいです. また日本語はだめだったので英語のほうがいいと思います.

f:id:ayousanz:20210302043122p:plain

row = cv.collection.add_row()
row.name = "Just some data"
row.listName = "test"
row.deadline = datetime.date.today()
row.description = "hoge"