2021年2月27日土曜日

6回目のランクイン

とうとう甲勲章の数と並びました。というか、今後甲勲章が取れるタイミングってあるんでしょうか。最近はイベントはなんとかクリアしているものの、ボスを倒した記憶が無いのですが。

まあ、流石にランクインするとは思っていたんですけどね。先月最後の順位が270番台でしたし。ちなみに現在の順位は850番台です。

昨年末からの残念イベント以降、提督が激減したと言われましたがどうなんでしょう?減ってきている事は実感しますが、アクティブは恐らく1サーバー6000人弱の全体12万人弱位かと思います。

ソシャゲではかなり貴重なガチャの無いゲームでゲーム外での収入で運営していましたが、2期以降専用武器偏重(題材にする艦が枯渇したので兵装にフォーカスしないとならなくなった)、それらを揃える為に課金に頼らざる得ない状況を作っており、ゲーム本編でお金を取る方向にシフトしてきたなと実感します。
又、アンソロや艦娘型録の発売延期が続いて採算とれないんだろうなあと。
22年のアニメ化まで10万ユーザー残るでしょうか?ランクインし易くなって嬉しいようなそうでもないような。

暗い話ばかり書くのも何なので、2年程前に「今後黒人艦娘は来るのか~」と書いてましたが、シロッコが初っぽいですね。いや、艦娘に人種は無いですけど、ストンと収まった感じでなんの心配も要らんかったなあと。丹陽も出る前はなんやかんや言われていましたが、無難に落ち着きましたし、キャラデザの安定感は流石ですね。

ゲームデザインももう少し安定してくれれば助かるんですけどね。

2021年2月26日金曜日

Python+Kivy 備忘録 番外編 MOスケルトンを作ってみた

よし、緊急事態宣言の解除には間に合ったな!
出来上がりましたよ。ちなみに動作は LinuxMint20.1 上でしか試していません。
後述しますが、既知の…というか意図的に放置しているバグがあります。(意図しないバグが無いとは言っていない)
という訳で、サーバー側になる server.py とクライアント側になる main.py です。
クライアントを main.py としたのは KivyLauncher で動作させる予定だったからなのですが…
そもそも PlayStore 上の物は16年位から更新が途絶えている為、 Android 6 以降では FileChooser 系が動かないのでなんともという感じです。
このため、今後 Android 上で動作確認をするかは不明。

で、このセットはシンプルにサーバーはログインしてきたクライアントの情報を収集して、集計情報を還すだけ。
クライアントは位置クリック時にサーバー側に情報を渡し、一定時間でサーバーが吐いた集計情報を受け取って情報を更新、描画を行うだけ。

という、それだけの動作しかしません。
代わりにソースは大変短く、サーバーが200行、クライアントが500行程度となっています。
又、一般的なネットゲームではクライアント、ゲームサーバーの他にユーザー登録等を行うログインサーバーがありますが、本質から離れる為作っていません。
本プログラムは同名のクライアントが複数ログインすると動作が不安定になり、本来はログインサーバーが弾くべきなのですが上記の理由から放置しています。
同時に複数サーバーを起動した場合の暴走除け処理も書いていないのですが…製品ならともかく流石にどうかと思って書いていません。

  • 使い方
クライアントとサーバーのどちらから先に起動しても構いませんが、上に表示されている共有ディレクトリを同じものを示すように選択してください。
※本プログラムは本格的なサーバーの代わりに共有ディレクトリを使用します。尚、テストケースでは USB メモリを使っていました。破損する事はあまり無いと思いますが、リムーバブルメディアの使用を推奨します
次にサーバーの Start ボタンを押します。
※尚、サーバーのスクリプト実行時点で停止中を示す stop というファイルが共有ディレクトリに作られるので、クライアントを実行しても停止中の表示になります。
起動後に毎回共有ディレクトリを選択するのが面倒な場合、サーバー、クライアント各ソースの MyData クラスの初期化時に書かれている
directory=""
部分をに適宜作業ディレクトリのパスを書く事でディレクトリの初期値を決める事ができます。

サーバー起動後、クライアント側でログインするのですが必ず別の名称をつけてログインしてください。
サーバー側にはクライアントが出力しているログファイルのリストが、クライアント側には自分と他のクライアントの表示が出るハズです。
又、現在既知のバグとしてウィンドウが非アクティブの状態でクリック(タップ)した場合、動作が不安定になったり、タップした座標が乱れる現象がでています。
非アクティブの場合は一度ウィンドウタイトル(ウィンドウ上部)をクリックしてから入力してみてください。

  • 確認できる内容
真っ当な操作としては、クライアント起動時にサーバーを停止( Stop を押す)のサービス正常停止。
クライアントを正常ログアウト( Logout を押す)させての他のクライアントからの消滅。

異常な操作としては、サーバー異常停止(サーバーのウィンドウをxボタン等で閉じる)しての、クライアント側からの自分だけ動けて他のクライアントが停止している、典型的なサーバーダウン状態。
及び、その状態からサーバー再起動でのサーバー復旧。
クライアント異常停止(クライアントのウィンドウをxボタン等で閉じる)しての、他のクライアント側からの強制終了したクライアントが停止して見える、『ゲーム落として逃げやがった』状態。
以上が思いつく限り確認できました。

  • 何をやっているのか?
端的に書けばファイルを介したプロセス間通信なんですが…作業フォルダ上に上記のようなファイルが出来まして。

クライアント出力ファイル(生成:クライアント)
クライアントが画面をクリックする度に更新されるファイル。
現状ではファイル名:_[ClientName].csv
内容
名前/色/最後に座標を確認できた時間/最後に確認できた座標/次の座標への到着予定時間/次の座標
となります。

サーバー出力ファイル(生成:サーバー)
サーバーが一定時間で更新するファイル。
単純に全クライアントの出力ファイルを結合した物。

サーバー状態ファイル(生成:サーバー)
サーバーの状態を示すファイル

この中で重要なのはサーバー状態ファイルで、サーバー起動時はファイル名が stop となり、クライアント側はこのファイルを発見すると停止します。
次にサーバー稼働( Start を押す )すると全クライアント出力ファイルを結合し new_journal.csv というファイルを作成します。
この処理の間はクライアントは旧 journal.csv を読んでいる可能性があります。
次にサーバーは旧 journal.csv を削除して new_journal.csv をリネームし、journal.csv として更新を行います。
※普通のサーバーならば、旧 journal.csv のファイル名に時間を付加してリネーム→保存を行う所ですが、膨大なファイルができる(現状1秒で13個ファイルが作られる)為、行っていません。
このリネーム処理中はクライアントにアクセスされないようにサーバー状態ファイルを「 work 」という名前に変名し、クライアント側に更新中である事を報せます。
リネーム処理が終了するとサーバー状態ファイルを「 release 」という名前に変名し、クライアント側に読み込み可である事を報せます。
…正直割と雑な処理をしています。

又、現状の処理タイミングですが、サーバーは1/13秒、クライアントは描画が1/12、サーバー出力ファイルへのアクセスが1秒に1回となっています。
どの程度の負荷に耐えられるかは機材によるのでなんとも言えません。

…と、まあ、なんとか今年の目標を早くも達成…他にも目標はあるのですが。
あと、最近モデリングをやっていないのは故意に Blender2.7 系の操作を忘れて 2.8 以降に移行する意図がありまして、その間にやる事…として思い立ったという側面もあります。
もう少し手を加えてゲームっぽくする事も可能なのですが、ひとまず一旦ここらで〆(まあ、重いバグが出れば修正しますが)としたいと思います。

2021年2月16日火曜日

Elite Book 2760p のゴム留め具を応急処置してみた

HP の Elite Book 2760p は 2in1 ノートでして、最近はあまり見かけない、モニターを捻るタイプのヒンジを持ちます。
そんなわけで、タブレット時にモニターがぐらつかないようにモニター部分の溝に合わせてキーボード部の左右にゴムで出来た突起が付いています。
が、当然このゴムは経年劣化する上に重いモニターを支えているので良く千切れるのですよ。中古出品されている同機種を良く見ると千切れている中古品が多くある事に気づくと思います。

で、この突起はキーボード部にプラスチックの芯があり、その芯にゴムパーツを被せています。そこで、今回は残ったゴム部分を取り除き、代替えになりそうな物を被せてみようという企画です。
ひとまずピンセット等をつかってはがしてみました。
代替えになるゴム素材…とは言え、消しゴム等では摩擦に弱くて使えません。
そこで私が探してきた物はこちら。
厚みも丁度4mmくらいだし。硬さも悪くない。パステルカラーしか無かったのが残念ですが…
レッツ加工。
加工精度の残念さはいかんともし難いですね。
一番面倒なのは芯を入れる穴を作る事。私は 1.5mm ピンバイスで穴を空けて、ニッパー状の爪切りで穴を繋げていきました。
ひたすら根気が居る作業です。あとゴムなのでヤスリで仕上げる事もできません。
不格好ながらも接着。普通にアロンアルファで大丈夫でした。
うーんかなり残念な出来上がりですが…
実際に使ってみると実用上は問題の無い状態に収まりになりました。
もう10年くらい前の機種ですし(64BitLinuxが載る現役機ですが)、別に中古に売る訳でも無く自分で使うだけならこれでも良い気がします。
同じ悩みがある暇な人は挑戦するのも一興かと、素材は110円ですしね。

※追記:以前書いて忘れていたのですが、交換したバッテリーは Note Parts 製互換バッテリー。
19年にマックのバッテリーを買った所はワールドプラス製なので、一通り互換バッテリーは買ったような。
まあ、今後はグローバルスマート製だけは買いませんが。

2021年2月15日月曜日

雑記いろいろ

  • CORSAIR の SCIMITAR RGB買い損ねた
現在モデリングで使用しているマウスは HAVIT の magic eagle な訳ですが。
入手できるなら欲しいマウスが CORSAIR の SCIMITAR RGB (黄色い奴)でした。
どっちも12ボタンマウスですが、シミターの方はゲームに使えるくらい12ボタンが押しやすいのですよ。
いや、既に magic eagle も慣れてますし、結構雑な扱いをしている割に3年使っても壊れてませんし、なにより公式サイトから買うと無茶苦茶安い(現在12ボタンマウスは扱っていません)ので、価格の HAVIT 性能の CORSAIR で住み分けていると思いますが。
そんな SCIMITAR RGB が少し前にモデルチェンジをしていまして、在庫処分で安くなっていたようなんですよね。
本来秋葉原に行けていれば必ずアークで確認していたのですが…ここの所は行くに行けず…そしてニューモデルさんは高い所で2万円オーバー安くても1.2万円です。PC より高いがな。

  • HP Elite Book 2760p のバッテリーが又壊れた
実は時々使う事が有ったので、昨年互換バッテリーをグローバルスマートで買っていたのですが…
きっちり保障期間の1年で壊れました。S〇NY の技術流出ですかね。
※後日確認した所、この互換バッテリーの購入日が昨年5月でした…9か月しか持たないバッテリーって…ちなみに充電回数は20回も行きません。完全に不良品ですね。
コレに関してはキーボード両脇のゴム留め具の破損も応急修理したので合わせて後日記事にしようかと思います。

  • サイトの閲覧状況を見てみた
いやー昨年の記事は見事に引っかかりもしません。2桁行く記事が無いような。
ちなみにこのサイトの閲覧はゲーム>ジャンク>プログラムの順に閲覧されているようです。
ちなみに等サイトは当初、晒さないとサボるモデリングの公開と知人への生存報告の為に作ったハズなのですが…
で、ゲームなんですが2位以降は Kenshi 、アッシュアームズ、その他の順となります。
いつも長々書いているアレに関しては分母が多いという理由が大きいですが、故意にタイトルを書かないようにしています。
で、1位は現在このサイトで唯一4桁の閲覧数があるコレ
…一切キャプチャー画像も無いコレが何故?と思ったのですがね。どうもスチームのセール期間中に一気に閲覧が上がるらしいのですよ。
で、グーグル先生に「Hob 考察」で尋ねてみたら…現在2位。
…拙い解説ですが良かったんだろうか。いや、読んで頂く為に書いてますし、ブロガー冥利に尽きるのですが。

  • 進捗いかがですか
正月にぶち上げてしまった「MOスケルトンを作る」
流石に来月には緊急事態宣言が終りそうですが…私の勤め先もすこーしだけ休みが増えているようなそうでもないような。
ひとまずクライアントはこんな感じ。
ファイルを使って通信を行うので、共有ファイルを置くディレクトリと、操作キャラクターの色と名前を決めるログイン画面。
本当に最小限です。
で、ログイン後はこんな感じ。
正月に書いた通り、動けるだけ。
で、ココまで作って気が付いた、これ以上開発する場合、Geany を複数開いてコーディングしないとならない。
少なくともクライアント2個とサーバー一つは必要。まさかそれぞれの為にパソコン用意する訳には行かないし。(数だけはあるが、場所的な問題で)
最悪、クライアントを先に完成させて、クライアントはターミナル直接起動、サーバーだけ開発って手も有るけど…どうしようか。
予定より遅れて完成は緊急事態宣言終了後になりそうです。

2021年2月12日金曜日

Python+Kivy 備忘録 当然 kivy の文字描画関数も描画関数じゃない編

 
今度は文字ですよ。
予想はしていたのですが、描画領域に文字を描画する際もやっぱりラベルオブジェクトを生成する形で実現しています。
…のですが、label オブジェクトは他の描画オブジェクトとは様相が異なります。
生成されたオブジェクトを print すると

kivy.uix.label.Label object

と出ており、ellipse 等の kivy.graphics.vertex_instructions 系統とは別物のようです。
…と言うことは、開放の仕方も異なります。

で、書いてみたコードはこちら

from kivy.app import App
from kivy.uix.widget import Widget

from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.clock import Clock

from kivy.uix.label import Label

class BtRev(Button):
    def on_press(self):
        self.parent.Rev()

class BoxMain(BoxLayout):
    orientation='vertical'
    def __init__(self,**kwargs):
        super(BoxMain,self).__init__(**kwargs)
        self.speed = 0.1
        self.text_x = 0.0
        self.list_la = []

        self.boxDraw = BoxLayout(size_hint_y=4)
        self.add_widget(self.boxDraw)

        self.b1 = BtRev(text='Rev',size_hint_y=1)
        self.add_widget(self.b1)

    def  Rev(self):
        self.speed = self.speed * -1.0

    def update(self,dt):
        with self.canvas.before:
            w = self.boxDraw.size[0]
            h = self.boxDraw.size[1]

            x = self.text_x * w - w/20.0
            y = self.boxDraw.top-h/2.0

            self.text_x = self.text_x + self.speed
            if self.text_x > 1.0:
                self.text_x = 0.0
            elif self.text_x < 0.0:
                self.text_x = 1.0

            for old in self.list_la:
                self.canvas.before.children.remove(old.canvas)
                self.list_la.remove(old)

            f_size =w/20
            if f_size < 10:
                f_size = 10

            self.list_la.append(Label(pos=(x,y),text='TEST',color=(1,1,1,1),font_size=f_size))

class BoundText(App):
    def build(self):
        bxmain = BoxMain()
        Clock.schedule_interval(bxmain.update, 1.0/12.0)
        return bxmain

if __name__ == "__main__":
    BoundText().run()

…うん、kivy の描画系は名前の付け方を変えてほしい。
というか、OS間の互換性を取るのが難しいのかもしれないけど、描画バッファに直接描画して、描画しっぱなしの所謂普通の描画関数にしてほしい。
古い考え方で書くと、画面描画をすべてスプライトで行っているような現状で、流石にゲームを作るのは無理なのでは無いかと感じますね。
いや、調べたら別の方法があるかもしれませんが。

2021年2月8日月曜日

Python+Kivy 備忘録 kivy の描画関数は描画関数じゃない?編

 実はまだ作っています MO スケルトン。予想通り緊急事態宣言が延長されましたが、別に休みが増えている訳でも無く、タイムリミットが迫っています。
当初サーバー側を作ろうかと思っていましたが、もう少しクライアント側を作らないと、サーバーが失敗しているか?の判断も付かない為、クライアント側を重視で制作中。
…その間に見つかったちょっと残念な kivy の仕様を書いてみます。(割と今更な内容らしいですけど、日本語でコードを載せている方が見当たらなかったので)

さて、kivy は強力なUIを持つライブラリですし、UIとゲーム画面の混在するアプリを作りたいものです。
こんな画面を作る場合、
ゲーム画面(描画領域)は空の BoxLayout を置いてココに色々描きこむ事になります。
…まあ、それは良いのですが、 kivy のゲーム系サンプルコードを読むと描画の開始時に
self.canvas.clear()
を行いなさいというコードを良くみかけるのですが…。
21年2月現在でUI混ざりのアプリで self.canvas.clear() を行うと全てのUIが消えてしまいます。
しかも、表示されないボタンの当たり判定だけ残っていてクリックできるという状態になります。
…多分、この怪し過ぎる挙動は改善されると思うのですが。

で、次善策として BoxLayout の描画領域だけ Rectangle で塗りつぶして描画するコードを書いてみたのがこちら

コード

from kivy.app import App
from kivy.uix.widget import Widget

from kivy.graphics import Color, Ellipse, Line, Rectangle
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.clock import Clock

class BtRev(Button):
    def on_press(self):
        self.parent.Rev()

class BoxMain(BoxLayout):
    orientation='vertical'
    def __init__(self,**kwargs):
        super(BoxMain,self).__init__(**kwargs)
        self.speed = 0.1
        self.ball_x = 0.0
        self.boxDraw = BoxLayout(size_hint_y=4)
        self.add_widget(self.boxDraw)
        self.b1 = BtRev(text='Rev',size_hint_y=1)
        self.add_widget(self.b1)

    def  Rev(self):
        self.speed = self.speed * -1.0

    def update(self,dt):
        with self.canvas.before:
            w = self.boxDraw.size[0]
            h = self.boxDraw.size[1]

            x = self.ball_x * w - w/20.0
            y = self.boxDraw.top-h/2.0-h/20.0

            self.ball_x = self.ball_x + self.speed
            if self.ball_x > 1.0:
                self.ball_x = 0.0
            elif self.ball_x < 0.0:
                self.ball_x = 1.0

            Color(0,0,0,1)
            Rectangle(pos=(self.boxDraw.right-w,self.boxDraw.top-h),size=(w,h))

            Color(1,1,1,1)
            Ellipse(pos=(x,y),size=(w/10.0,h/10.0))

class BoundBall_miss(App):
    def build(self):
        bxmain = BoxMain()
        Clock.schedule_interval(bxmain.update, 1.0/12.0)
        return bxmain

if __name__ == "__main__":
    BoundBall_miss().run()


…あらかじめ書いておきますが、コレは失敗コードです。
実行すると始めのうちは良いのですが、だんだん処理が重くなっていきます。
原因を探る為「kivy Ellipse 遅くなる」等で検索すると…出るわ出るわ、同様の事例が山ほど出ます。

結論から書いてしまえば、kivy の Rectangle や Ellipse 関数はただの描画関数ではありません。
試しに返り値を print で出力してみると一目瞭然。
オブジェクトを作ってらっしゃる!?
つまりコレは四角や丸を描画するオブジェクトを生成する関数であり、生成されたオブジェクトはフレーム毎にそれぞれが描画を行っています。
こんな事をしていればどんどん処理が重くなっていくのは当然です。
ちなみに、何処に積み重なっているのかと言えば、描画先の self.canvas や self.canvas.before 等であり、割り当てられた描画オブジェクトを全開放する関数が前述の self.canvas.clear() 等のようです。
私の古い常識からすれば Ellipse 関数は CreateEllipseObject とでも名乗るべきで、返り値でハンドラを返してユーザーに開放させるべきですが(というかこの関数自体、オブジェクトの実体参照先を返してくる)今の時代の感性と合わないのかもしれません。
又、python はメモリ管理をガーベジコレクションで行っており、コードを書く人間はメモリ開放を明記しない事が多いのでこういう書き方になったのかもしれないのですが…逆に python しか言語を知らない人は大ハマりするのでは?

で、改善版コードはこちら

from kivy.app import App
from kivy.uix.widget import Widget

from kivy.graphics import Color, Ellipse, Line, Rectangle
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.clock import Clock

class BtRev(Button):
    def on_press(self):
        self.parent.Rev()

class BoxMain(BoxLayout):
    orientation='vertical'
    def __init__(self,**kwargs):
        super(BoxMain,self).__init__(**kwargs)
        self.speed = 0.1
        self.ball_x = 0.0
        self.list_el = []

        self.boxDraw = BoxLayout(size_hint_y=4)
        self.add_widget(self.boxDraw)

        self.b1 = BtRev(text='Rev',size_hint_y=1)
        self.add_widget(self.b1)

    def  Rev(self):
        self.speed = self.speed * -1.0

    def update(self,dt):
        with self.canvas.before:
            w = self.boxDraw.size[0]
            h = self.boxDraw.size[1]

            x = self.ball_x * w - w/20.0
            y = self.boxDraw.top-h/2.0-h/20.0

            self.ball_x = self.ball_x + self.speed
            if self.ball_x > 1.0:
                self.ball_x = 0.0
            elif self.ball_x < 0.0:
                self.ball_x = 1.0
            # remove old ball
            for old in self.list_el:
                self.canvas.before.children.remove(old)
                self.list_el.remove(old)

            Color(1,1,1,1)
            self.list_el.append(Ellipse(pos=(x,y),size=(w/10.0,h/10.0)))

class BoundBall(App):
    def build(self):
        bxmain = BoxMain()
        Clock.schedule_interval(bxmain.update, 1.0/12.0)

        return bxmain

if __name__ == "__main__":
    BoundBall().run()


※21年2/12 更新。以前は self.canvas.before.remove としていましたが、self.canvas.before.children.remove に変更しました。
自分で生成した Ellipse オブジェクトをリスト化しておいて、描画の開始時に古いオブジェクトを開放しています。
うーん kivy ライブラリってなんというか、妙な所で設計が雑な気がするのは気のせいでしょうか?