チャチャチャおもちゃの抹茶っちゃ

ゲームのこととかプログラミングのこととか。気が向いたら書く。ブログタイトルは友人が考えました。

PCデスク環境を一新しました。

PCデスクを整えた話

どうもみなさんこんばんは。最近は新型コロナウイルスが流行っているのもあってリモートワークとかになっていますね。 ぼくの研究室もオンラインゼミになり、バイトもリモートです。

いい機会なので、今まで放置していたPC周りの環境を整えることに。

Before

とりあえず整理するまえの環境はこんな感じ。

f:id:mattyan1053:20200417222246j:plain
整理前

小学校入学前に買ってもらった学習机にマット。もともとは本を立てたりできるところがあったのだけれど、モニターを置くために取っ払ってしまった。サブモニターは父からもらったお下がりでかなり古めのやつ。

この絨毯、みんな似たようなのもってなかった?ミニカー走らせてたよね?みんなやるよね??? あとこれよれてしまうのがどうしようもなくてな・・・・・・。

いい加減スペース狭くて困っていた。デスクライトがないのも困りポイント。

まずは片付け

学習机は引き出しがあって、机を乗り換えるためには引き出しの中のものをどうにかしなければいけなかった。貰い物のペンとかが大量にあったのでひたすら捨てまくった。引き出しにあったものを仕舞う場所を作ることも考えたら丸一日かかった。さようなら土曜日。

購入物

このご時世なので基本的には外にでて見に行くこともできない、ひたすらAmazonで注文した。

デスク

  • 幅130cm以内
  • クランプ式のものが取付可能

あたりが条件。なんとか部屋で開けられた幅が130cmだったので、幅が120cmのデスクを購入。奥行きは学習机と殆ど変わらない60cmを維持しつつ、横幅を増やして、鏡面にしておしゃれに。地味にクランプ式のものが取り付け可能ってのを重視した。あとでデスクライトとかモニターアームとかつけたかったので・・・・・・。

汚れは目立ちます。近くにウェットティッシュを常備。

デスクライト

ほしかった条件は

  • クランプ式
  • 明るすぎない
  • 向きを変えられるもの

あとは安いのを選んだ感じ。いい感じに手元をちゃんと照らしてくれているので良い。もう少し柄が長いとより使いやすかったかも?明るさと色味を若干ながら調節できるのも良い。スイッチの位置が少し使いづらいかな?もう少し根元のあたりにあると助かった。

マット

黒のシンプルなマット。

  • 滑り止めがあることと
  • 椅子のキャスターが引っかかりにくい
  • デスクと椅子の範囲をカバーできる

あたりを条件に探していた。値段も安くてGood。開封してすぐはまだ癖がのこって丸まってしまうけれどしばらく辞書とかで抑えておくと◎。汚れが目立つのでこまめに掃除機をかけたくなるところは無精者な自分的にはヨシ。

モニターアーム

条件は次のような感じ。

  • クランプ式
  • デュアルアーム
  • 角度調節が割とできる
  • 高さは少し高くできる
  • 21.5inchと17inchを設置できる

モニター下のスペースが確保できるか否かでだいぶ自由に使えるスペースに差ができると思っていたのでモニターアームを採用。クランプ式のものが取り付けられるデスクを購入した理由もこれ。確保できた幅的にあまり大きなモニターにかえることはできないのでサイズを控えめにしつつ、更に今まで同様角度をつけて収められるように。モニターは高めの位置にできると背もたれによりかかっていても見やすいですね。

サブモニター

条件は

  • DVIまたはDisplayPort
  • VESA規格対応
  • 19.5inch以内

今まで使っていた古いモニターはアームに対応していなかったので新しいものを購入。個人的にはかなり満足する収まり具合、ピッタリ範囲に収まった。

PCのグラフィックボードがNVIDIA GeForce 1060 6GBなので、ついているのが

  • DVI × 1
  • HDMI × 1
  • DisplayPort × 3

だった。DisplayPort対応のディスプレイ、あんま多くないんだよね・・・・・・。そんな中このディスプレイはDisplayPort対応なのもGood。変換かませるのなんか嫌だものね・・・・・・。付属のコネクタはD-subだったのでDisplayPortのやつも購入した。

USBハブ

外部機器が増えてきて、ポートにまだ余裕はあるものの混濁して整理しづらくなってきたので購入。条件は

  • ON・OFFができること
  • 電源をコンセントから供給できること

の2点。USB給電をあまりしたくなかったので外部から電力を供給できるように。

マイク

最近だれかしらと通話ばっかりしているのでいい加減安物マイクはやめたくて新調。せっかくなのでクランプ式のアームをつけた。今まで置きマイクに腕ぶつけたりしてたのがなくなって、邪魔なとき上に上げておくことができるようになってよい。音質もよくなったんじゃないかなあ。これだけでまるで配信者みたいに見えるのすき。

After

そんなこんなで生まれ変わったデスク。

f:id:mattyan1053:20200417230302j:plain
整理後

手元がめっちゃひろくなった。あと横幅が広くなったので左側にノートPCが置けるように。今後ここには仕事用のMacが配置される予定。今までメインモニターをおいていた台が左側に移動したことでHHKBが下に収納できるようになったのも良い。自分が日頃長くいる場所の環境を良くすると気分もいいですね!

おわりに

いかがでしたか?(圧)
みなさんもこの機会にPC周りを一新してみてはどうでしょうか。

OpenSiv3D勉強会 in 千葉

OpenSiv3D勉強会 in 千葉

2020年2月21日(金)に、千葉市生涯学習センターにて、OpenSiv3D勉強会を開催させていただきました!

connpass.com

最終的に19人もの参加者になりました!感謝!

開始前 ~会場~

かなりいい場所でした、少し暑かったものの窓を開ければすごしやすく快適。施設自体も新しいのかどうかはよく知りませんがとてもきれい!

Wi-Fiも使えてプロジェクターも借りれる、電源も使用可!完璧や!

始まる前は少しみんな初対面とかで緊張してるのか静かでしたね、ぼくは以前もお会いしているので、軽いお気持ち。

少し遅れる人とかもいたので、13:10開始にしました。 勉強会の資料はネット上にあります。これです。

siv3d.github.io

勉強会開始 ~OpenSiv3Dの紹介~

みんなの反応はTogetterにまとめてあります。

togetter.com

徐々に賑わっていく感じがわかる!講師の方からの即レス、かなりウケがいいですね。いやほんとすごいとおもいます。

最初はみんなで公式サイトにあるサンプルで遊びながらどんな機能があるのか見ていく感じでした。

遊べるものばかりなので良い。何ができるのかわからない状態でやることは苦にすらなりうるので、こういった活動を最初にやるのはとてもいいなとおもいました。みんなたのしそう

サンプルコードのジャンルが割と広くて、それぞれ刺さるものがあったぽいのがよかったんじゃないでしょうか。ネタ系から音楽系まで。

中盤 ~チュートリアル・作業~

サンプルを一通り試したところで実際にOpenSiv3Dを使って作業! ぼくはLTの準備をする時間がなかったのでまだこのときは何やるか考えてました。遅い。

やっぱり手を動かしてみるのが大事ですね。いろんな便利な機能があることを知れたんじゃないでしょうか。

終盤 ~Lightning Talks~

終盤は質疑応答とかLTをば。LTをしたのは僕含め三人です。

一人目は過去にSiv3Dで作ったゲームの紹介。彼はクッキークリッカーマインスイーパーがとても好きなのでそれを組み合わせたゲームを紹介してくれました。

二人目は疑似3Dをかんたんに描画する方法。すごいシンプルな式と直線のみで3D描画を表現していた、すごい。そのうち情報がうpされるんじゃないでしょうか。

三人目はぼく。特に準備をしていなかったので、かんたんに紹介できるOpenSiv3Dの小ネタ。写真のフレームみたいに表示して、影をつかってうまいことそれっぽく見えるやり方を紹介。素材不足に悩むプログラマーの手助けに少しでもなれば。アイデア募集もしています。

解散

17:30頃に解散。コロナウイルスも流行っているのでサクッと。

最後に

講師として来てくださった鈴木遼さん、及び参加してくださったみなさん、ありがとうございました!

楽しめたのであれば良かったと思います。今後もよきSiv3Dライフを!

【C++】ステートマシンをつくる【OpenSiv3D】

はじめに

この記事はCCS Advent Calendar 2019 12/09の記事です。

adventar.org

こまつなさん 2019年、本厄だったらしい[CCS Advent Calendar 2019 7日目] ←前 | 後→MeerIiokaさん

C++でステートマシンをつくる

プログラミング言語C++を用いて、ステートマシンと呼ばれる機構をつくります。

ステートマシンって何?

もともとはオートマトンとかの分野で使われている言葉で、入力と現在の状態によって次の状態が決まる論理回路のことをステートマシンと呼んだりします。状態遷移図なんかでモデル化でき、複数の状態をなんらかの起因で変化していくものに対して扱います。

f:id:mattyan1053:20191209185151p:plain
状態遷移図の例(状態数2)

本記事では、C++のクラスなどを用いて、複数の状態を遷移することで描画内容や更新内容を変化させることのできる機構をつくることにします。

やりたいこと

ステートマシンを用いてやりたいことは以下のような感じです。

  • 現在の状態に適したupdate()draw()関数を呼びたい
  • 状態変化のトリガーを外部から与えたい
  • 状態変化時に呼びたい関数など毎フレーム呼ばれない関数も定義したい

これらの要望に応える機構を目指します。

つくったもの

実際に作ったものをはります。

注意点

以下のコードはOpenSiv3Dを用いて使うことを想定したコードです。とはいえ、OpenSiv3Dの機能で用いているのはHashTableとログ出力のみなので、std::unordered_mapcerrなどで代用すれば問題なく他でも使えると思います。

あとそれなりに新しいC++じゃないと動かないともいます。explicitとかある・・・・・・。

コード

  • StateMachine.hpp
# pragma once
# include <Siv3D.hpp>

/// <summary>
/// 状態基底クラス
/// </summary>
template<typename T>
class State {
private:

    T m_id;

public:

    /// <summary>
    /// デフォルトコンストラクタ
    /// </summary>
    /// <remarks> 必ず状態IDをつけます。 </remarks>
    State() = delete;

    /// <summary>
    /// 新しい状態を作成します。
    /// </summary>
    /// <param name="id"> 状態ID </param>
    explicit State(T id) :m_id(id) {}

    /// <summary>
    /// デフォルトデストラクタ
    /// </summary>
    virtual ~State() = default;

    /// <summary>
    /// 状態IDを取得します。
    /// </summary>
    const T& Id() { return m_id; }

    /// <summary>
    /// 状態に入ったときに呼ばれます。
    /// </summary>
    virtual void setUp() {}

    /// <summary>
    /// 情報を更新します。
    /// </summary>
    /// <remarks> 毎フレーム呼ばれます。 </remarks>
    virtual void update() {}

    /// <summary>
    /// 描画を更新します。
    /// </sumamry>
    /// <remarks> 毎フレーム呼ばれます。 </remarks>
    virtual void draw() const {}

    /// <summary>
    /// 次の状態に移る前に呼ばれます。
    /// </summary>
    virtual void cleanUp() {}

};

/// <summary>
/// 状態管理クラス
/// </summary>
template<typename T>
class StateMachine {

private:

    /// <summary>
    /// 状態リスト
    /// </summary>
    HashTable<T, std::shared_ptr<State<T>>> m_stateList;

    /// <summary>
    /// 現在の状態
    /// </summary>
    std::shared_ptr<State<T>> m_state;

public:

    /// <summary>
    /// デフォルトコンストラクタ
    /// </summary>
    StateMachine() = default;

    /// <summary>
    /// デフォルトデストラクタ
    /// </summary>
    virtual ~StateMachine() = default;

    /// <summary>
    /// 現在の状態のIDを返します。
    /// </summary>
    /// <returns> 現在の状態のID </returns>
    Optional<const T&> getCurrentStateName() const {
        if (m_state == nullptr) {
            return none;
        }
        return m_state->Id();
    }

    /// <summary>
    /// 指定の状態へ移行します。
    /// </summary>
    /// <param name="nextStateId"> 移行先の状態ID </param>
    void goToState(T nextStateId) {

        if (!m_stateList.contains(nextStateId)) {
            Print << U"Error: Not exist state: " << nextStateId;
            return;
        }

        if (m_state != nullptr) {
            m_state->cleanUp();
        }

        m_state = m_stateList[nextStateId];
        m_state->setUp();

    }

    /// <summary>
    /// ステートマシンの初期化を行います。
    /// </summary>
    /// <remarks> 必ずオーバーライドします。 </remarks>
    virtual void initializeStateMachine() = 0;

    /// <summary>
    /// 状態を追加します。
    /// </summary>
    /// <param name="state"> 追加する状態のstd::make_shared</param>
    void addState(const std::shared_ptr<State<T>>& state) {
        if (state == nullptr) {
            Print << U"Error: This state is nullptr";
            return;
        }
        if (m_stateList.contains(state->Id())) {
            Print << U"Error: Already exist state: " << state->Id();
            return;
        }
        m_stateList[state->Id()] = state;
    }

    /// <summary>
    /// 毎フレーム呼ばれます。情報を更新します。
    /// </summary>
    void update() {
        if (m_state == nullptr) {
            return;
        }
        m_state->update();
    }

    /// <summary>
    /// 毎フレーム呼ばれます。描画を更新します。
    /// </summary>
    void draw() const {
        if (m_state == nullptr) {
            return;
        }
        m_state->draw();
    }

};

使い方

以下使い方です。

State クラス

状態に関するクラスです。インターフェースみたいなやつです。状態遷移図でいう丸のやつですね。

状態を表すクラスに継承します。テンプレート引数に状態名管理クラスをつけるのを忘れずに。

enum TestStates{
    Idle,
    Runnning
};
struct Idle : public State<TestStates> {
};

デフォルトコンストラクタは許可していません。必ず状態名を引数につけましょう。

struct Idle : public State<TestStates> {
    Idle() : State<TestStates>(TestStates::Idle) {}
};
  • Id()

状態名を取得します。

  • setUp()

この状態に入ったときだけ呼ばれます。繰り返しは呼ばれません。

  • update()

毎フレーム呼ばれる更新用関数です。

  • draw()

毎フレーム呼ばれる描画用関数です。

  • cleanUp()

次の状態に移る直前に呼ばれます。

setUp()update()draw()cleanUp()はオーバーライドしなくても動きます。つまり何もしないということもOKです。

StateMachine クラス

ステートマシンとして管理したいクラスに継承します。継承の際はテンプレート引数に状態名を管理するものを入れてあげましょう。

class Test : public StateMachine<String> {
};
enum TestStates {
    Idle,
    Running
};
class Test2 : public StateMachine<TestStates> {
};

特に指定はありません。インスタンスを作ればOKです。特に理由がないのであればここでinitializeStateMachine()を呼んであげるようにするのが丸いです。

  • initializeStateMachine()

純粋仮想関数になっていて、必ずオーバーライドする必要があります。あとは初期化したいときに呼びましょう。ここでは、作成したインスタンスに状態を追加していきます。状態の追加にはaddState()メソッドを使います。追加する際には、安全のためshared_ptrを使っているのでstd::make_sharedします。

addState(std::make_shared<Idle>());
addState(std::make_shared<Running>());
  • addState(const std::shared_ptr<State>& state)

状態を追加する関数です。最初に呼ばずともあとから呼ぶこともできます。Stateクラスを継承したオブジェクトをmake_sharedして渡してあげましょう。

  • update()

現在の状態のupdate()関数を呼びます。

  • draw()

現在の状態のdraw()関数を呼びます。

  • goToState(T nextStateId)

指定の状態へ移行します。引数には状態名をいれてあげましょう。

if (ball.x >= 500) main.goToState(TestStates::Idle);
  • getCurrentStateName() const

現在の状態名を取得できます。

使用例

以下使用例です。ステートマシンで管理するオブジェクト本体の数値を状態クラスからもいじりたいのでコンストラクタで自身の参照を渡しています。よしなにやりましょう。
ちなみにこのコードでは円の移動に座標計算を行っていますがこれは悪い例で移動速度がフレームレートに依存します。Scene::Delta()を使うとかしましょう。

  • Main.cpp
# include <Siv3D.hpp> // OpenSiv3D v0.4.0

# include "StateMachine.hpp"

// 状態
// 列挙体を使っているがStringなどでも良い
enum BallStates {
    Idle,
    Moving,
};

// 状態管理したいクラス
class MovingBall : public StateMachine<BallStates> {

    Circle m_ball;

public:

    MovingBall() {
        m_ball.r = 40;
        m_ball.setPos({ 100, 200 });
        initializeStateMachine();
        goToState(BallStates::Idle);
    }

    // ステートマシンを初期化する。
    void initializeStateMachine() override {

        // 状態を追加していく。
        addState(std::make_shared<Idle>(*this));
        addState(std::make_shared<Moving>(*this));

    }

    // 状態をインナークラスで定義する。
    struct Idle : public State<BallStates> {
        // 外側クラスを覚えておくとアクセスできる
        MovingBall& main;
        Idle(MovingBall &_main) :State<BallStates>(BallStates::Idle), main(_main) {}

        // 状態移行直後
        void setUp() override {
            main.m_ball.x = 100;
            Print << U"Start Idle";
        }
        // 状態の最中
        void update() override {
            if (MouseL.down()) {
                main.goToState(BallStates::Moving);
            }
        }
        void draw() const override {
            main.m_ball.draw(Palette::Blue);
        }
        // 状態終了時
        void cleanUp() override {
            Print << U"Finish Idle";
        }
    };

    struct Moving : public State<BallStates> {
        MovingBall& main;
        Moving(MovingBall& _main) :State<BallStates>(BallStates::Moving), main(_main) {}

        void setUp() override {
            Print << U"Start Moving";
        }
        void update() override {
            main.m_ball.x += 10;
            if (main.m_ball.x >= 500) {
                main.goToState(BallStates::Idle);
            }
        }
        void draw() const override {
            main.m_ball.draw(Palette::Red);
        }
        void cleanUp() override {
            Print << U"Finish Moving";
        }
    };

};

void Main()
{
    // 背景を水色にする
    Scene::SetBackground(ColorF(0.8, 0.9, 1.0));

    // 状態を持つオブジェクト
    MovingBall b;

    while (System::Update())
    {

        b.update();
        b.draw();

    }
}

f:id:mattyan1053:20191209193102p:plain

最後に

以上ステートマシンをC++で作ってみた話でした。意外とこういう便利機能作ってみるのも楽しいので皆さんやってみては?
アドベントカレンダー明日は守銭奴な話か電子書籍のススメが読めるらしいです。

謝辞

作ったコードはOpenSiv3D作者のReputelessさんにレビュー頂きました、ありがとうございました!