Hello World / plɹoM ollǝH

主に自分用メモ

PySide drag start from QPushButton

Qt (PySide) は一部のWidget以外でドラッグ開始を実装しようとすると意外と面倒
ググると大体受け入れる側のサンプル出てきちゃうし

class MyButton(QPushButton):
    def __init__(self, parent):
        super().__init__(parent)
        self._startPos = QPoint()

    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton:
            self._startPos = event.pos()
        super().mousePressEvent(event)

    def mouseMoveEvent(self, event):
        if 0 != (event.buttons() & Qt.LeftButton):
            if (event.pos() - self._startPos).manhattanLength() >= QApplication.startDragDistance():
                self.startDrag()
                self.setDown(False)
                return
        super().mouseMoveEvent(event)

    def startDrag(self):
        drag = QDrag(self)
        mime = QMimeData(self)
        mime.setText('hogehoge')
        drag.setMimeData(mime)
        drag.start(Qt.CopyAction)

QToolButtonなどでも同様
mousePressEvent, mouseMoveEventあたりが継承できるWidgetはこれでいけそう

Unityの回転値を右手座標系に変換

まず平行移動のX方向が逆なのでxに-1を掛けます

trans.x *= -1.0f;
return glm::translate(glm::mat4(), trans);

回転値はZXYオーダーで、ZとY回転に-1を掛けます

glm::mat4 m;
m = glm::rotate(glm::mat4(), -rotate.z, glm::vec3(0.f,0.f,1.f)) * m;
m = glm::rotate(glm::mat4(), +rotate.x, glm::vec3(1.f,0.f,0.f)) * m;
m = glm::rotate(glm::mat4(), -rotate.y, glm::vec3(0.f,1.f,0.f)) * m;
return m;

あとはお好きにどうぞ

ここがクソだよ Insta360 GO

辛口レビュー

いいところはあちこちで散々書かれるので私がクソな点をまとめます
YouTuberの販促動画だけ見て買って不幸になる人(私)がでないようにするためです
まだ買ったばかりなので、使い込んでみての感想はまた別に書くかもしれません

TLDR

Don't Buy.

1. 一度地面に落としたら最後。高確率でレンズに傷がつく

買ったその日に落としましたわ

レンズには傷が付いて、撮影した動画はモヤモヤしたゴミが映る感じになりました
(只今、修理手続き中です)

Insta360 GOは180度撮影するカメラなのでレンズが割と出っ張っています

まぁそれはいいのですが、レンズは強化ガラスでも何でもないようで、本体が18gしかないにもかかわらず傷が付きやすいようです

f:id:dungeonneko:20200108010321j:plain
レンズ上部に傷が付いてしまいました

Insta360 Careというサービスに入っていないと購入直後でも修理は有料っぽいです

Insta360 Careに入るには
公式Webサイトから購入しないといけない

っぽいので、保証が欲しい人は公式サイトから購入したほうが良いでしょう
Insta360 Careに入ると1年間保証が付くようです

Insta360 360 Camera - Insta360, the leader in 360 cameras

私はAmazonで買ってしまったので、速攻で保護ケースも買いました

2. ペンダントが安心して使えない

Insta360 GOは磁石がくっつくペンダントを服の下に提げて、本体だけ服の上からカチッとつけられるというスマートな見た目がポイントなのですが

これがだいぶ危なっかしい

私がInsta360 GOを落としてレンズを割ったときは

  • 薄いウインドブレーカーの下にペンダント
  • ウインドブレーカーの上から本体

だったのですが、服を引っ張ってしまったのが落下の原因になったようでした

f:id:dungeonneko:20200108023313g:plain
引っ張るとすぐ取れてしまいます

摩擦や紐の範囲外への移動にはかなり弱いわけですね。ペンダントの紐は伸縮性がないので、首から提げた状態で下方向に服か本体をずらしたら一発アウトです

伸び縮みする紐にして欲しいですね

3. 充電ケースが使いづらい

3-1. iPhoneユーザーはスマホケース使ってるとデータ転送できない

充電ケースはlightinig端子がニョキっと伸びていて、普段は(本体バッテリーがそんなに持たないので)充電ケースにいれて持ち歩きます

f:id:dungeonneko:20200108011018j:plain
端子の部分にはカバーが付いてます

f:id:dungeonneko:20200108011054j:plain
短っ

撮影したデータをスマホに保存するには充電ケースごと端子で接続しないといけないのですが iPhoneスマホカバーをつけている人はそのまま接続することができません

データ転送に充電ケースごと有線接続要求する意味ある?

f:id:dungeonneko:20200108024925j:plain
これで高速データ転送とかしてるんですかね?

とりあえずlightinig延長コードを買おうと思ったのですが、まともそうな商品なかなか見つからず。apple純正品の延長コードってないんですかね?

3-2. 入れる向きを間違えると充電&認識されない

最初買ったときはこれが全然わからなくて「全然認識されないなー」とか言ってイライラしていました。今はそれがわかったので、そのクソデザインにイライラしてます

めちゃくちゃ取り出しづらいです

少し手が乾燥していたり、油っぽかったりするともうイライラが止まりません

f:id:dungeonneko:20200108005422g:plain
反対向きにつけてしまうとさらに厄介

ペンダントのときは微妙な磁力だったくせに…

  • なんで間違った方向でもピタッとハマるデザインにしたの?
  • どっちでも接続できるようにするとか
  • 接着したままグルっと回せるようにするとか
  • 指を掛けやすくしとくとかさぁ、やりよういくらでもあるじゃん?

設計者への疑問は尽きません…

f:id:dungeonneko:20200108005241g:plain
ちなみにカバーも取り外しずらい

3-3. 充電ケースの充電はUSB繋がないといけない

1つの製品をiPhoneAndroid両方のユーザーに売ろうとした結果がこれ

f:id:dungeonneko:20200108005935j:plain
iPhoneだと充電にしか使わないUSB端子(メス)

結局USBケーブルいるなら充電ケースからlightning端子出てる意味ある?

せめてそのUSBからlightningの充電と本体に接続できる変換ケーブルを付けて欲しかった

4. 本体の状態がわかりづらい

撮影中?電源ON?スリープ中?

本体のLEDが一応点灯したり点滅したりしてるんですよね

f:id:dungeonneko:20200108022347g:plain
撮影が始まって点滅するLED

本体身に付けてたら LED見れないので 意味ないですけどね

f:id:dungeonneko:20200108022216j:plain
撮影者視点

覗き込んだら自分の顔映るし

まとめ

散々Disってきましたが、コンセプトや本体の見た目は素晴らしく、今のところライフログ系では代替が効かない製品なので、今後の改良(またはライバル出現)に期待しています

改善してほしい点
  • レンズを強化ガラスにして
  • データ転送時に有線接続を求めないようにして
  • 意味不明な充電ケースの端子減らす
  • ペンダントの紐には伸縮性を持たせて
  • 充電ケースと本体は簡単に分離できるようにして
  • 本体の状態は撮影者にわかりやすく伝えるようにして
  • シリアルコードトラッキングするなら年間保障標準で付けてください

どうしても使ってみたいっていう人は、借りるとか中古で買うとかして気に入ったら中古を買値で売って、Insta360 Care付きで新品買えば良いと思います

Tech Toolbox for Game Programmers @GDC2016

せっかくだから俺はこの動画を要約するぜ

www.youtube.com

1. The Poor Man's Dialogue Tree

お金を掛けないダイアログツリーの作り方

普通のやり方
  • Unityのアセットストア等で買うと高い
  • サードパーティ製(本当に必要なものではない可能性がある)
  • 退屈!(人にやらせるなら楽しい仕事のほうがいいよね)
  • 大量のデータを作るためにちゃんとしたツール(GUIなども)を用意してあげる必要がある
  • パフォーマンス重要でゲームのコードも共有する必要がでてくる(C++C#
我々のやり方
  • 8割問題を解決するための2割(本当に必要なもの)を自分で書く
  • HTML5クロスプラットフォーム、実装簡単、パフォーマンスは重要じゃない、色々なライブラリが使える、JSONみんな使える)
  • JointJSとNW.jsを使ってノードグラフが動くデスクトップアプリを作成
  • NW.jsつかえばファイルリネームするだけでデスクトップアプリができるのでめっちゃ便利
  • ファイルフォーマットは3種類用意(編集用JSON、最適化JSONローカライズ用のテキストをxlsxに)
  • なぜExcelか→テキストデータがExcel形式だと有志に手伝ってもらいやすい
まとめ

2. Automating Data Implementation With IDs

Darkest Dungeonでどのようにデータが実装されたか

  • Darkest Dungeon → ターンベースRPG、プロシージャル。プログラム2人。C++の内製(?)エンジンとミドルウェア
  • 定義したIDから必要なデータ(ファイルパス)やコードが自動生成される
  • コード生成はマクロとプリプロセッサ

特別なことはしてないけど、ゲーム内容にあった実装を正しく選択した感じ

3. Tracery - generating texts, graphics, and more!

自動生成システムTracery.jsの紹介

  • SporeとかSimCityにも携わったKate Compton(@galaxykate)さん、プロシージャルコンテンツ生成をしている
  • Tracery最初はテキスト生成のToyを作ろうと思ったが、多目的言語ができて、TwitterBotを作るサービスができた
  • Botで絵文字(AA)や画像を自動生成する人もでてきた
  • ゲームにも使えるんじゃないですかね?ストーリー、音楽、コードの自動生成等々

機能

  • ハッシュタグで囲まれた文字列をルールに沿って置換する({"greetings": ["hello", "nihao"], "origin": "#greetings#, World!"}的な)
  • 再帰的置換、テキストのモディファイア(例:複数形への変換)、生成されたテキストの保存と再利用

4. Putting the "Game" in "edi-game-tor"

A Good Snowman Is Hard To Build のレベルデザインPuzzleScriptを使ったよという話

  • ほとんどのゲームはレベルエディタが必要
  • 「外部ツール 」か「ゲーム内エディタ」のどちらかを選択する
  • 第三の選択として「エディタ内のゲーム」がある → PuzzleScript
  • イテレーションを加速する既存のツールを使おう

5. Making Your Own Tools & Hiden Benefits

自作ツールの隠れたメリット

  • 何故自作ツールが必要なのか→「とにかく始められる&レベルデザイン」にフォーカスしすぎで「システムの理解とオーサリング」が足りない
  • ゲームロジックとデータを可視化して、チームで問題を素早く解決したい
  • imguiがめっちゃ便利
  • ツールはドキュメントの代わりになる(常に最新&ナレッジも共有される)

C++でインゲームGUIつくるならimgui一択か

程々に快適なゲーミングPCを5万円台で組む

経緯

GPD POCKET(初代)のバッテリーが膨張してしまったので、もうUMPCは懲り懲りということで久しぶりにデスクトップPCにしようと思い立ちました。ゲーム制作&遊ぶ用に安くてある程度3Dゲームも動くPCを探そうと思いましたがどれも予算オーバーなのでコスパが良いという噂のRyzen自作PCを組んでみることに(十数年ぶりN回目)。久しぶりの自作PCで苦労したくないので日和ってベアボーン買いました。

用意したもの(合計¥54,974)

マザボ ¥17,156

CPU ¥16,920

AMD CPU Ryzen 5 2400G with Wraith Stealth cooler YD2400C5FBBOX

AMD CPU Ryzen 5 2400G with Wraith Stealth cooler YD2400C5FBBOX

メモリ ¥13,338

DDR3しか持ってなかったので仕方なく買った

SSD ¥7,560

これも本来ノートPCの部品を流用する予定だったがMNVeではなくAHCIだったので購入

【国内正規代理店品】WD 内蔵 SSD M.2 2280 WD Blue SN500 NVMe 500GB WDS500G1B0C-EC

【国内正規代理店品】WD 内蔵 SSD M.2 2280 WD Blue SN500 NVMe 500GB WDS500G1B0C-EC

キーボード ¥0

家にあったものを使用

PFU Happy Hacking Keyboard Lite2 日本語配列かな印字なし USBキーボード ブラック PD-KB220B/U

PFU Happy Hacking Keyboard Lite2 日本語配列かな印字なし USBキーボード ブラック PD-KB220B/U

マウス ¥0

家にあったものを使用

OS ¥0

使っていないPCのWindows10ライセンスを移行して事なきを得た

ライセンス移行手順メモ

  1. 移行元PCでUSBメモリ(8GBくらいでOK)でWindows10のインストールメディアを作成する
  2. 移行元PCのログイン方法をWindowsアカウントに切り替えてログインしておく
  3. 移行先PCにUSB挿して起動→インストール→同じアカウントでログイン
  4. 移行先PCで設定→ライセンス認証→このデバイス上のハードウェアを最近変更しました→まだ怒られる
  5. 移行元PCでプロンプトから「wmic path softwarelicensingservice get OA3xOriginalProductKey」でプロダクトキー表示→移行先PCに入力して移行完了

承認されてなくても起動できてWebからライセンス購入もできるっぽいですね

LANケーブル ¥0

家にあったものを使用。Wifi Kitを買うのが癪だったのでケチった(でも¥3,000くらい)

ASRock DeskMini用Wi-Fiキット DESKMINI WIFI KIT

ASRock DeskMini用Wi-Fiキット DESKMINI WIFI KIT

PCモニタ¥0

家にあったものを使用

まとめと反省

数年間UMPCの沼にどっぷり浸かってたせいで、メモリもストレージも使いまわせずに予想外の出費でした(普通にノートかデスクトップ使ってれば3万円くらいで済んだよね…)。とりあえず1080pでApex Legendsも普通に動きますし、YouTubeAmazon Prime Videoもサクサクだし良しとしましょう。今度はもっとお金を使わずに地を這って必要なものを手に入れていきたいと思いました。

追記(購入後にやること)

ドライバのインストール

ヘッドホン挿しても音が出ないなーと思っていたらRealtek入れてなかった https://www.asrock.com/nettop/AMD/DeskMini%20A300%20Series/index.jp.asp#Download

Visual Studio 2015/2017 でマルチスレッドのプロファイリング

拡張機能をインストールして
分析 -> 同時実行ビジュアライザー -> 現在のプロジェクトで開始
(日本語の場合)

f:id:dungeonneko:20190828154716p:plain

スレッドごとの状況見れるようになります

PySide2 + librosa + pyaudio + numpy, play audio repeatedly

import librosa
import numpy as np
import pyaudio
from PySide2.QtCore import *
from PySide2.QtGui import *
from PySide2.QtWidgets import *
import sys

class Widget(QWidget):
    def __init__(self):
        super().__init__()
        self._audio = pyaudio.PyAudio()
        self._wave = librosa.load('test.wav', sr=16000, mono=True)
        self._wave_pos = 0
        self._wave_len = len(self._wave[0])
        self._stream = None
        self._auto_replay()

    def _auto_replay(self):
        if self._stream is None or not self._stream.is_active():
            self._wave_pos = 0
            self._stream = self._audio.open(
            format=pyaudio.paFloat32, channels=1, rate=16000, output=True, stream_callback=self._on_streaming)
        QTimer.singleShot(1000, self._auto_replay)

    def _on_streaming(self, _data, frames, _time, status_flags):
        left = self._wave_pos
        right = min(self._wave_pos + frames, self._wave_len - 1)
        data = np.ndarray.tobytes(self._wave[0][left: right])
        self._wave_pos += frames
        if self._wave_pos >= self._wave_len:
            return data, pyaudio.paComplete
        return data, pyaudio.paContinue

if __name__ == '__main__':
    app = QApplication(sys.argv)
    main = Widget()
    main.show()
    sys.exit(app.exec_())