gologiusの巣

プログラミング、モデリングなどのメモです。誰かの役に立てるとうれしいです。

【コマンドプロンプト(cmd)】AAを表示する

以下のようなスクリプトを作りましたが、その作り方

コマンドプロンプト(cmd.exe)で大きく「本番」と「検証」の文字を表示します · GitHub

f:id:gologius:20180616094423p:plain

画像変換ツールをDL

Unix系だとFIGlet というツールがapt-getで使えるようです。 Win版も配布されていましたが、Win10では使用できませんでした。

なので以下のツールを使用しました 文字絵エディターの詳細情報 : Vector ソフトを探す!

使い方は簡単なので割愛

編集

正直あとは適当に編集してやればできるのですが、私の方法を記載しておきます。

  • notepad++を使用します(そこそこのテキストエディタならなんでもよいですが)
  • Alt+Shitで行、列ごとの選択ができます
  • 先頭行にechoを追加します

まとめ

文字数少ないね ( ^ω^)・・・

【Python】 loggerのログが重複する

ログが再起動のたびに増えていくバグと、運命の出会いを果たしたのでメモします。

Pythonのログの取り方をお勉強しました

以下の記事で、ログの取り方について学びました。

ログ出力のための print と import logging はやめてほしい

超絶的に雑な解釈ですが、以下のようなものだと理解しました

  • print → エラーなのか警告なのかログなのか分からない。ログとる際に使うべきでない
  • logging → グローバル変数的な立ち位置。
  • logger → ローカル変数的な立ち位置。

※詳しくは元記事を参考にしてください・・・。

お、同じログがいっぱいでるぞ~

以下の記事で解決

uyamazak.hatenablog.com

再実行するたびにログが増えていく

上記の記事のようなログ用モジュールを作成して、実行する。

超具体的には、Anacondaで環境構築して、SpyderをIDEとして利用しており、 実行時にF5を使用すると、実行のたびにどんどん増える

# 一回目
[log info] ログテスト
# 二回目
[log info] ログテスト
[log info] ログテスト
#三回目
[log info] ログテスト
[log info] ログテスト
[log info] ログテスト

f:id:gologius:20180606214449p:plain

解決策

終了時に、killLoggers()のようにハンドラを全削除してあげます

from logging import getLogger, Formatter, FileHandler,StreamHandler, DEBUG, shutdown
from logging.handlers import RotatingFileHandler

loggers = {}

def getModuleLogger(moduleName):
    if moduleName is None:
        moduleName = __name__

    if loggers.get(moduleName):
        return loggers.get(moduleName)

    formatter = Formatter('[%(asctime)s | '
                          '%(name)s | '
                          '%(levelname)s] '
                          '%(message)s')

    streamHandler = StreamHandler()
    streamHandler.setFormatter(formatter)
    streamHandler.setLevel(DEBUG)

    fileHandler = RotatingFileHandler("download.log", maxBytes=5000, backupCount=3)    
    fileHandler.setFormatter(formatter)
    fileHandler.setLevel(DEBUG)

    logger = getLogger(moduleName)
    logger.setLevel(DEBUG)
    logger.addHandler(streamHandler)
    logger.addHandler(fileHandler)

    logger.propagate = False
    loggers[moduleName] = logger

    return logger

def killLoggers():

    for l in loggers:
        logger = loggers.get(l)
        for h in logger.handlers:
            logger.removeHandler(h)

    shutdown()

    return

これで重複出力はなくなります。

以上。

Selenium+Pythonにて、アラートが出る新規ウインドウに遷移したい

※Teratailで私が質問した問題を、結局自分で解決した際のメモです

Python - Selenium+Pythonにて、アラートが出る新規ウインドウに遷移したい(125969)|teratail

※2018/06/03追記

どうやらヘッドレスモード(GUI、ウインドウを表示しないモード)にすると、いちいち前のウインドウに遷移する必要がなくなるようです。 記事の最期の方に記載しておきます。

やりたいこと

  • リンクをクリックする→新規ウインドウが開く(※ただしウインドウが開くと即アラートが出る)

という動きをSeleniumPythonで実現したいと考えています。

実際にはその先のボタンを押したいのですけどね。

下記のリンクで例を見ることが出来ます。

テストページ | gologiusのページ

問題点

調べているとどうも

  • アラートを消すためには、アラートが出ているウインドウに遷移しなければならない
  • ウインドウに遷移するためには、アラートを消さなければならない

と、デッドロックみたいな状況になっているようです。

以下ソースの★1、★2の部分です。

ソース

# -*- coding: utf-8 -*-
"""
Created on Wed Mar  7 22:16:01 2018

@author: 
"""

from selenium import webdriver
from selenium.webdriver.common.alert import Alert
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.ui import WebDriverWait
from selenium.common.exceptions import TimeoutException

import datetime
import traceback

###########################################################################
def waitOpenWindow(driver, beforeWinNum, maxWaitSec = 10):
    """
    指定された時間まで、ウインドウが開くのを待つ
    """

    beginTime = datetime.datetime.now()
    endTime = beginTime + datetime.timedelta(seconds=maxWaitSec)

    while(datetime.datetime.now() <= endTime):

        afterWinNum = len(driver.window_handles[-1])
        if afterWinNum == beforeWinNum:
            continue
        elif  afterWinNum > beforeWinNum:
            #ウインドウ数が増える=新規ウインドウが開いた
            return True , "指定時間内に新規ウインドウが開きました"
        else:
            return False, "ウインドウが外部から閉じられた可能性があります"

    return False, "指定時間内に新規ウインドウが開きませんでした"

###########################################################################
def waitAlert(driver, maxWaitSec = 10):
    """
    指定された時間まで、アラートが開くのを待つ    
    """

    try:
        wait = WebDriverWait(driver, maxWaitSec)
        wait.until(expected_conditions.alert_is_present())
    except TimeoutException as time_e:
        return 0, "アラート表示待機中にタイムアウトしました"
    except Exception as e:
        print(traceback.print_exc)
        return -1, "アラート表示待機中に予期しないエラーが発生しました"

    return 1, "アラートが表示されています : " + Alert(driver).text

###########################################################################
#関数定義
def access():

    try :
        print("begin process")

        driver = webdriver.Chrome("chromedriver_win32\chromedriver.exe")
        driver.implicitly_wait(10)
        driver.get("https://gologius.github.io/test.html")

        print("画面名", driver.title)
        winNum = len(driver.window_handles)

        #リンクをクリック
        driver.find_element_by_link_text("javascriptによる小ウインドウ表示").click()

        #ウインドウ表示待機     
        print("画面遷移待機中")
        waitResult, msg = waitOpenWindow(driver, winNum)
        if waitResult == False:
            return False, "ウインドウ表示に失敗しました"
        print(msg)

        #新規ウインドウに遷移する ★1
        win = driver.window_handles[-1] #リストの最後=最後に開いたウインドウ
        driver.switch_to.window(win)
        print("画面名", driver.title)
        winNum = len(driver.window_handles)

        #アラート待機 ★2
        print("アラート表示待機中")
        resultCode, msg = waitAlert(driver)
        if resultCode == 1:
            Alert(driver).accept()
        print(msg)

        print("end process")

    except:
        print("予期しないエラーが発生しました")
        print(traceback.format_exc())
        #ログ出力
        driver.quit()

    return True, "アクセス成功"

###########################################################################
#実行

result, msg = access()
print(msg)

解決方法

ボタンクリック後、「遷移前のウインドウ」にdriver.switch_to_window(win)で遷移すると、 その後のアラート処理、ウインドウ遷移が実行できるようです

win1 = driver.window_handles[-1]

driver.find_element_by_link_text("javascriptによる小ウインドウ表示").click()

#遷移前のウインドウに遷移する
driver.switch_to_window(win1)

#アラート処理

#ウインドウ遷移
win2 = driver.window_handles[-1]
driver.switch_to_window(win2)

理由は不明ですが、推測するに、新規ウインドウが表示される際に、

  • 新規ウインドウが開く際に、そのウインドウがアクティブになろうとする
  • Alertのせいでアクティブになりきれない →無限ループの原因
  • 遷移前のウインドウをアクティブに(switch_to_window)する
  • Alert(driver).accept()なども実行可能になる

みたいな気がします。


※2018/06/03追記↓

ヘッドレスモードで起動すると、この話は無視できる・・・?

        chromeOptions = webdriver.ChromeOptions()
        chromeOptions.add_argument('--headless') #ブラウザのGUIが表示されなくなる 
        driver = webdriver.Chrome("hogehoge/chromedriver.exe", chrome_options=chromeOptions)

雑感

上記のような不具合を抜きにしてもSelenium便利。

ちなみに検証用サイトはgithubioで作成しました。 テストページ | gologiusのページ

JavaScript使えるのでこういう際にサクッと作れるので便利。 ただJSの書き方を忘れていたのはナイショ...(/ω\)

【Akeytsu】 Reverse Footがうまく設定できない

Akeytsuを使用していて発生した問題についてメモ

Reverse Footとは

f:id:gologius:20180504170536p:plain

リファレンスの動画見た方がよくわかるのですが、足用のIKです。

問題発生

  • 片足には設定できるがもう片足に設定できない
  • 挙動がおかしい(膝を曲げて頂きたいのですが、曲げてくれません)

f:id:gologius:20180504171111p:plain

解決方法

これ、つま先のボーン(足のボーンの末端)に対して設定しないといけません。

上記二つの問題はこれで解決できます。やったね!

f:id:gologius:20180504171300p:plain

静止してくれない場合

f:id:gologius:20180504173841g:plain

上記のようになる場合、対象のフレームすべてにおいて、右クリックでリングを赤にすると解消されます。

f:id:gologius:20180504173906g:plain

まとめ

つまずいた点をメモりました。 Akeytsuは日本語情報が少ないので、小さなことでもメモっていきたいですね。 というかここ一年でこのブログ以外に日本語情報がないのですが・・・

また、以下の本が簡素で分かりやすい説明になっています(図が異様に雑な気がしますが・・・)。

おしまい。

【Unity】Playable APIを用いてモーション遷移時に補間に考慮したいこと

※都度更新予定

モーションを補間させながら遷移させる場合、Playable APIには関数は用意されていません(Unity2017.3現在)。 よって、PlayableAPIを使用する場合、自分でコードを書かなければなりません。 まぁ今後関数が用意される可能性もありますが・・・

というわけで、PlayableAPIでモーション遷移させる場合に、 注意したほうがさそうな場合について記載します。

個人的にはモーション切り替えパターンは

  • とにかく指定したモーションにスムーズに移行してほしいパターン(棒立ち→走り始める、など)
  • 上半身が別のモーションによって上書きされるパターン(走る→走りながら撃つ、など)
  • 指定したモーションの最初のフレームから最後のフレームまで再生してほしいパターン(武器をちゃんと振ってほしい場合など)
  • 遷移前の時間を引き継いでほしいパターン(前方向に歩く→右方向に歩く場合など。)

この四つのパターンにに分けられると思っています。今回はこの四つのパターンを実装したサンプルプロジェクトを作成しました。

サンプルプロジェクト

というわけで作成したサンプルプロジェクトを置いておきます。 github.com

実体はMotionPlayerコンポーネントです。Unityちゃんにアタッチされてます。 UIからモーション再生方法をいろいろ弄れます。 ちなみに逆再生も可能。

次からモーションの切り替えパターンの説明となります。

とにかく指定したモーションにスムーズに移行してほしいパターン

切り替え時にPlayableへのinputWeightを変更しながら、モーション切り替えをします(サンプルプロジェクトのMotionMixer. crossFadeCoroutine()が該当)

・・・という基本的な考え方をこちらの記事から学びました。 tsubakit1.hateblo.jp

上半身が別のモーションによって上書きされるパターン

例えば、

layerMixer.SetLayerMaskFromAvatarMask(layerIndex=1, upperMask);

のように、Layer1に上半身のマスクを割り当てた状態で

layerMixer.SetInputWeight(layer=1, weight=1f);

のようにweight=1fにすれば上半身のモーションは上書きされますし、weight=0fにすれば、layer=0に適用されているモーションが上半身にも適用されます。

ただ、このweight切り替えの際にいきなり値を変えると、モーションが補間されすに切り替わってしまいます。 なので

private IEnumerator crossFadeLayerWeight(int layer, float duration, bool enable)
 {
     float waitTime = Time.time + duration;
     yield return new WaitWhile(() =>
     {

         float diff = waitTime - Time.time;
         float rate = Mathf.Clamp01(diff / duration);
         float weight = (enable) ? 1 - rate : rate;
         layerMixer.SetInputWeight(layer, weight);

         if (diff <= 0)
             return false;
         else
             return true;
     });
 }

のようにして値を補間させながら切り替えましょう(詳しくはMotionPlayer.setLayerEnabled( )を参照)。

※サンプルプロジェクトの場合「上半身上書き」にチェックを入れ、遷移時間のスライダを0以上にすると、遷移が見れます。

指定したモーションの最初のフレームから最後のフレームまで再生してほしいパターン

※サンプルプロジェクトの場合「完全遷移まで待機」にチェックを入れる、に該当します。

例えば武器を 1. 振り上げて 2. 振り下ろす

という二つで一つのモーションに遷移したい場合、遷移前のモーションがミックスされてしまうと、 2.振り下ろす のモーションしか再生されてないようにみえる可能性があります。

f:id:gologius:20180331223208p:plain

ですので、遷移後のモーションの一フレーム目の姿勢になるまで、再生を待ってあげる必要があります。

f:id:gologius:20180331223210p:plain

またせる処理はコルーチンを使えば大して難しくないですね。以下のように遷移先のモーション再生を停止させておき、一定時間後にモーション再生を再開します(詳しくはMotionMixer. crossFadeCoroutine()参照)

     if (waitCrossFade)
      nowPlayable.SetSpeed(0f); //遷移先のモーション時間を止めておく

      //inputWeightの変更 略

       if (waitCrossFade)
       {
           float play_speed = playParam.reverse ? -1f : 1f;
           nowPlayable.SetSpeed(play_speed); //遷移先のモーション再生時間を元に戻す
       }

遷移前の時間を引き継いでほしいパターン

※サンプルプロジェクトの場合「アニメーション時間同期」にチェックを入れる、に該当します。

例えば走るモーションが、正面、左、右の3パターンあり、すべて同じ周期で走っているとします。 この場合時間同期させながら遷移させないと、片足で移動しているように見えてしまいます。

f:id:gologius:20180331224118g:plain

なので、再生する時に、遷移前のモーションの再生中時間を

nowPlayable.SetTime(beforePlayable.GetTime());

のようにして引き継いであげる必要があります。 サンプルではMotionMixer.recconect()内に処理があります。

f:id:gologius:20180331224255g:plain

まとめ

  • Playable APIでモーション遷移する際のパターンについて書きました
  • いかんせん動画か資料がないとわかりづらい内容なので、記事を書く準備が面倒でしたね・・・
  • この記事は追記、更新しまくると思います

【Python】【Selenium】 Webサイトから自動でファイルDLする

概要

  • Webサイトにアクセス→ログイン→ボタンを押してファイルをDL

みたいな処理を自動化したい。

会社で使おうと思ったスクリプトなので、具体的なサイトは出せないのは勘弁してください。 あと特定サイトを例に挙げると、そのサイトの負荷を上げそうなのでそれも理由にあります。

環境構築

PythonSeleniumを使用します。 Pythonは環境構築が楽で、パッケージがいろいろあるのでいいですよね。

OSはWin10です。 ブラウザはクロームを使用します。

Pythonの環境

初めて環境構築する場合は、 以下のリンクからインストーラをDLして実行すると、 Python、エディタ(Spyder)など全部入ります。

初めて入れるならPython 3.X版にしましょう。 2.X版は2020年にサポートが終了します。

ちなみに私はPython2のAnacondaが入っている状態でさらにPython3版をインストールしたため、 パスがめちゃくちゃになりました。

解決方法としては、環境変数を適当に消してから再インストールすると正常に動作するようになりました。

Seleniumのインストール

コマンドプロンプト

pip install selenium

以上。

次、ここからwebdriverをDLして、適当なディレクトリに置いておきます。

パスは後で使用するので覚えておきましょう。

ちなみに、これをDLしてない or スクリプト上でパス指定が間違っている場合、以下のエラーが出ます。

WebDriverException: 'chromedriver' executable needs to be in PATH. Please see https://sites.google.com/a/chromium.org/chromedriver/home

実装

最初に書きましたが会社で使用しようとしてたので、xpathはごまかしてます。 かといって適当なサイトを例に挙げて、そのサイトの負荷上げるのも犯罪臭がするので・・・

gist.github.com

想定するシナリオは

  • Webページにアクセス
  • ダウンロードする資料の番号を入力
  • ボタンを押す
  • 別ウインドウでポップアップが出る
  • DLボタンを押す

となります。

driver.implicitly_wait(WAIT_SEC) はかなり重要です。 要素がなくても'WAIT_SEC'秒だけ待ち続けます。 これを指定することで、読み込みが完了しておらず、要素がないのでエラーになる、といった事態を避けられます。

また、driver.find_element_by_xpath()xpathを指定して要素を取得できます。 xpathの説明はこの記事が詳しいです。 上の例だと、

  • //input[@id='page'] → ページ全体(//)の中で<input id="page"></input>を取得
  • //a[@onclick='download(); → ページ全体(//)の中で<a onclick="download"></a>を取得

となります。 要素取得後はsend_keys()で値セット、click()で要素をクリックしています。 ちなみにsend_keys()は複数同時入力(Ctrl+Cなど)に対応しているので、複数形(keys)になってるみたいです。今回は使ってませんが。

ファイルのダウンロードの終了検知は、Seleniumの機能ではできなさそうでね。 ダウンロードが完了したらファイルが作成されるはずなので、ファイル名の検出などで対応する必要がありそうです。

結論

個人的にはdriver.implicitly_wait()が便利。 いちいち読み込み完了を検知するスクリプトを書かなくともよいのは便利。

あと、こんな環境が一瞬でインストールできるPythonが便利ですね。 ただ僕は型が明示的に決まる言語の方が好きです。C#とか。

  • ゲーム、アプリを作る→C#
  • 若干めんどい数学計算、機械学習、サーバー弄り系→Python

みたいな使い分けになってきてますね。

そろそろWeb系も勉強したいところ。

【Unity】一週間GameJamに参加した話

ゲーム作ったので報告しますね

概要

一週間でゲームを作るイベントです

Unity 1週間ゲームジャム | 無料ゲーム投稿サイト unityroom - Unityのゲームをアップロードして公開しよう

今回のテーマは「当てる」です

今回の目標

  • とりあえず完成させる
  • UniRxで書いてみたい
  • TimeLine使いたい

とりあえずこんなところですね。 最近は製作途中のものが多かったので、なにか完成物を作りたいと思いました。

また、UniRxは最近使い始めてなかなかいい感じに理解できてきたので、 使いたいと思っていました。 今回のソースの解説とか後日やりたいですね(やるとはいっていない)。

あとはTimeLineですね。前までいちいちAnimationClipとAnimationControlllerを作ってたのですが、 ファイル増えすぎて嫌だと思ってました(厳密には内部でAnimationClipが作成されているみたいですが)。

ゲームを考える

当たるといわれると恐らく誰もが「シューティングゲーム」を思い浮かぶでしょう。

・・・ならばその逆をいきます。自分から「当たりに」いきます。 僕の思い付きが面白いかは別にして、人とは違うオリジナルな発想というのは大事だと思うのですよ。

そしてコンセプトが決まったので

  • 誰が当たるか
  • どうやって当たるか
  • 何に当たるか
  • 当たったらどうなるか

を決める必要があります。これによってゲームの面白さがきまるといっても過言ではありません。

時間的な都合もあって

  • 誰が当たるか → 昔作った棒人間
  • どうやって当たるか → スタイリッシュなポーズで
  • 何に当たるか → メタセコイアの機能で「文字の3D化」があったのでこれで
  • 当たったらどうなるか → 音がだんだん変わっていって、もっとコンボをつなげたくなるように

になりました。

できたもの

動画は製作途中のものしかありません。

アタレ、サラバアタエラレン | 無料ゲーム投稿サイト unityroom - Unityのゲームをアップロードして公開しよう

トラブル

WebGLでビルド後、実行するとエラーで死ぬ。 ブラウザの開発者ツールからコンソールを見ると、AudioPlayableかエラー吐いていました。 おそらくTimeLineが悪いのだと推測しました。 この時点で日曜日17時50分。遅刻確定 (締め切りは日曜日20時です。過ぎてもペナルティ等はありませんが)

どうもバージョンアップで治るようです。イベントに参加する前にはアップデートしましょう(戒め)。

https://forum.unity.com/threads/timeline-animations-on-webgl-from-assetbundle-dont-play-at-all.500185/

なおこのアップデート後にLayerMixerがおかしくなりました(以前の記事参照)。

gologius.hatenadiary.com

とりあえずこのゲームには不要だったので、LayerMixerを経由しないことで対応しました。

反省

ほんとは当たったときのエフェクトとかもっと作りこみたかったです。 ヒットストップ、画面振動などなど・・・

またゲームとして、難易度アップや、敵のランダム性もなかったです。 最初はくる文字の色も変える予定だったのですが・・・。

まとめ

反省点もいろいろあります。

が、やらないより、とりあえず一つのものを作りきる、という経験は大切だと思います。

今後もこの精神でちょくちょく参加したいです。 f:id:gologius:20180301220231p:plain