「技術」カテゴリーアーカイブ

VPSをHTTP2に対応させてみた。

簡単にできるんだね。

こちらのサイトを参考にしました。

http://www.dcom-web.co.jp/lab/web-server/nginx_with_http2

nginxの設定でlistenにhttp2と書き足すだけで良いらしい。

Protocolがh2になっていれば、HTTP2を使用しています。

HTTP2で何が変わるのか?

従来のHTTP1.1では、1リクエストに対して必ず1レスポンスを返す必要があります。

1ページの中に複数のコンテンツ(htmlとかjsとかcssとか)が合った場合、これらのファイルを一つずつダウンロードする必要がありました。

しかし、HTTP2では、多リクエスト、多レスポンスが可能になります。

これはどういうことかというと、送信中のリクエストの応答を待たずして次のリクエストを出せるのですね。

なので、複数のファイルを平行してダウンロード出来るようになります。

なので、その分ページの読み込みが早くなります。

今のブラウザはほとんどHTTP2に対応しているので、あとはサーバ側の設定を変えればHTTP2での通信になります。

あ、それと、HTTP2はデフォルトSSLによる暗号化通信なので、サーバ側に証明書が必要になりますよ。

HTTP2に関する小話

HTTP2は元々Googleによって開発された通信プロトコルで、後に正式にHTTP2の規格に採用されました。

TCPより上位のプロトコルなので、簡単に双方向の通信を行うことができます。

おそらく、最近のスマホゲームのマルチプレイなんかは、この技術が使われていると思われます。

それ以前は、一つ下位のプロトコルである、TCPで通信していたと思われます。

でも、TCPって思った以上に扱いが難しんですよね。

【北海道大戦】確認ダイアログ追加とバグ修正

https://github.com/takishita2nd/HokkaidoWar

最初のプレイヤーの都市選択で確認ダイアログを出すようにしました。

あとは、最後までプレイできることも確認しました。

ただ、戦力差がありすぎると、バトルがワンパンで決着が付いてしまうのが、ちょっとよろしくないですね。

【ラズパイ】【カメラ】クライアントを閉じたら動画撮影を終了する

前回のままだと、クライアント側(ブラウザ)を撮影中に閉じてしまうと、動画撮影を終了する人がいなくなってしまいます。

これを防ぐためには、クライアントが生存していることを常に確認する処理が必要になります。

まぁ、今回はプレビュー画面で常にデータのやりとりを行っているので、これを利用しましょう。

    def do_GET(self):
        parsed = urlparse(self.path)
        if parsed.path == '/Streaming':
            global lasttime
            lasttime = time.time()

            enc = sys.getfilesystemencoding()

プレビュー画をリクエストがあったら、その時間を記憶しておきます。

def videoCapture():
    global capture
    global out

    while capture:
        nowtime = time.time()
        if nowtime - lasttime > 10:
            capture = False
            out.release()
            out = None
            break
        _, img = cap.read()
        out.write(img)

ビデオキャプチャーの周期処理の中で、現在時刻と、プレビュー画要求時の時刻を比較します。

周期処理の時刻がキャプチャー時の時刻より10秒経過していたら撮影を終了します。

ブラウザを撮影途中で閉じた場合、プレビュー画要求時の時刻が更新されなくなりますので、こうすることで、ブラウザを閉じてから10秒後に撮影は終了します。

さて、カメラでやりたいことが終わってしまった・・・

次何しようかな。

【ダイエット支援】【入力履歴機能】履歴管理画面の作成

特に特筆することはやっていないので、進捗報告だけ。

https://github.com/takishita2nd/diet-mng

とりあえず画面の表示だけ。

全チェック処理とか、テンプレートに移す処理とか、データを削除する処理とかは次回やります。

あと、管理者アカウントを別に作って、他の人がこのページにアクセスできないようにする必要もありますね。

【COCOS2D-X】アイコン画像を配置する。

画像を配置するのは簡単なんですが、

その配置位置を調整するのがめちゃくちゃ大変だったりします。

    auto Button = Sprite::create("btnChara.png");
    if (Button == nullptr)
    {
        problemLoading("'btnChara.png'");
    }
    else
    {
        Button->setPosition(Vec2(sprite->getPosition().x + sprite->getContentSize().width * scaleRate / 2 + origin.x, visibleSize.height + origin.y));
        Button->setAnchorPoint(Vec2(1.0,1.0));
        Button->setScale(3.0);
        this->addChild(Button, 1);
    }

まず、setAnchorPoint()についてですが、

これは画像位置の基準となるポイントを設定する関数ですね。

デフォルトでは、アンカーポイントは中心(0.5, 0.5)の位置にあり、これを左下にするには(0, 0)、右上にするには(1, 1)と設定します。

今回は画像の右上を、背景の右上に合わせたいので、(1.0 , 1.0)、すなわち、画像のアンカーポイントを右上に設定しています。

次に背景画像の右上の座標を調べる必要があるのですが、

getContentSize()でspriteのサイズがわかりますが、

これはsetScale()で拡大する前の値になっていました。

なので、getContentSize()の値に拡大率を計算式に入れることで、見事に位置が合いました。

あとは、ちょうど良い感じで画像の大きさを調整。

もう少し大きくしても良いかな?

用意したアイコンを全部配置して、バランスを取ってみたいと思います。

ホストPC外部から仮想PCに接続できるようにする

これの続き。

仮想PCにUbuntu20.04をセットアップしたものの、

今の状態ではホストPCからでしか仮想PCに接続できないので、

これをホストPCの外からでも接続できるようにします。

仮想PCのネットワーク設定はデフォルトこうなっています。

内部ネットワークが選択されているから、外からアクセス出来ないんでしょう。

なので、新しい仮想ネットワークスイッチを追加。

接続の種類を外部ネットワークにして、物理ネットワークデバイスを設定。

これを仮想PCのネットワークに設定します。

これで仮想PCを起動。

ルータのDHCPでIPアドレスが割り当てられていれば成功。

あとは、MACアドレスを静的な値に設定、ルータのDHCP設定で固定IPアドレスを登録すればOKでしょう。

マインクラフトサーバを自宅に設置したい。

今使用しているモデムですが、

無線ルータ・ひかり電話内蔵のモデルなんですが、

今までは業者が全部セットアップしてくれたおかげで、設定をいじくることができませんでした。

しかし、ビッグローブにプロバイダを乗り換えたことで、設定が初期化・再設定を行ったことにより、設定をいじくることができるようになったのです。

マインクラフトサーバはメインPC内の仮想PCで動作させたいと思います。

今持っているラズパイ4では、メモリは十分でもCPUパワーが足りない。

そして、外部からマイクラサーバに接続するには、ルータの設定で仮想PCに接続できるよう設定を変える必要があるのですが。

通常のルータはポートフォワーディングといって、特定ポート番号でのアクセスのみを内部PCに転送する機能があるのですが、

このモデム一体型ルータはポート番号に関係なく、外からのアクセスを内部PCに転送する設定にしかできないみたいです。

ということは、その設定を有効にした場合、転送先のPCは常に外からのアクセスに晒されるので、ファイヤーウォールの設定は必須になりますな。

さて、実際に仮想PCを立ち上げようか。

【ラズパイ】【カメラ】WEBから動画を撮影する。

今回はWebのプレビュー画面から動画の撮影を行いたいと思います。

赤丸のボタンを設置し、押すと録画開始、もう一度押すと録画停止という感じです。

まずはサーバ側。

すでに動画を撮影する方法は知っているので、これを使用します。

POSTリクエストを受け付ける処理を書いていきます。

    def do_POST(self):
        global thread
        global aviFilename
        global capture
        global out

        content_len = int(self.headers.get('content-length'))
        requestBody = json.loads(self.rfile.read(content_len).decode('utf-8'))

        if requestBody['contents']['command'] == 1:
            _, img = cap.read()
            dt_now = datetime.datetime.now()
            filename = dt_now.strftime('%Y%m%d_%H%M%S')+".jpg"
            cv2.imwrite(filename, img)

            response = {
                'status' : 200,
                'path': "http://pi4.local:8000/" + filename
            }
        if requestBody['contents']['command'] == 2:
            dt_now = datetime.datetime.now()
            aviFilename = dt_now.strftime('%Y%m%d_%H%M%S')+".avi"
            out = cv2.VideoWriter(aviFilename, fourcc, 20.0, (640,480))
            
            capture = True
            thread = threading.Thread(target=videoCapture)
            thread.start()

            response = {
                'status' : 200,
            }
        if requestBody['contents']['command'] == 3:
            capture = False

            thread.join()

            out.release()
            out = None

            response = {
                'status' : 200,
                'path': "http://pi4.local:8000/" + aviFilename
            }
        else:
            response = {
                'status' : 200
            }

        self.send_response(200)
        self.send_header('Content-type', 'application/json')
        self.end_headers()
        responseBody = json.dumps(response)

        self.wfile.write(responseBody.encode('utf-8'))

録画開始(command=2)を受信した場合、VideoWriterを作成し、動画を保存するスレッドを起動します。

録画停止(command=3)を受信した場合は、このスレッドを停止するように動作します。

実際のスレッド処理はこんな感じです。

def videoCapture():
    while capture:
        _, img = cap.read()
        out.write(img)

captureフラグがTrueのときは延々とカメラの映像を動画ファイルに保存します。

録画停止時にcaptureフラグをFalseに変更し、このスレッドが終了するのをjoinで待ってから、作成された動画ファイルのパスを返信します。

動画のダウンロードですが、たぶん、content-typeを用意しないと行けないので、Dictionaryに追加しています。

CONTENT_TYPE = {'.html': 'text/html; charset=utf-8', '.txt': 'text/plain; charset=utf-8', '.js': 'text/javascript', '.json': 'application/json',
        '.jpeg': 'image/jpeg', '.jpg': 'image/jpeg', '.png': 'image/png', '.gif': 'image/gif',
        '.css':'text/css', '.avi': 'video/x-msvideo'}

さて、このままだと録画中にブラウザを閉じてしまって、録画を止める手段が無くなってしまいます。

これの対処を次回やります。

(そろそろカメラネタも無くなってきた。)

cocos2d-xのコーディングしてて思ったこと。(ガベコレの話)

C#やJavaにはガベージコレクションと言って、使用しなくなったオブジェクトを自動的に解放してくれる仕組みがありまして、

だからnewしたオブジェクトの使用後を意識しなくても使用できるんですが、

C++にはそんな機能は無く、

newしたオブジェクトは明示的にdeleteしないと、そのオブジェクトは消えません。

で、cocos2d-xはspriteなどを作成するときにnewを使用せずにcreate()メソッドを使用しているんです。

そして、サンプルを見る限りではdeleteは行っていない。

これはcocos2d-xのプラットフォームの中で独自にガベージコレクション的な動きをしています。

ただ、cocos2d-xのガベコレとC#やJavaのガベコレとは少しロジックが異なるようです。

詳しい内容はこの記事を見て欲しく

http://furicotech.blogspot.com/2015/03/memory-management-in-cocos2d-x.html

まぁ、この内容をまとめると、

cocos2d-xのspriteなどのオブジェクトは1回使い切り。(その都度createしなければならない。)

これ、自分もコーディングしててハマりました。

あると思っていたspriteのポインタが無効なポインタになっていた、と言うことが多々ありました。

C#と同じ感覚で使用することができないんですね。

なので、クラス設計をどうするか、少し悩んでいます。

spriteなどの部品はシーンクラスの中で全部処理させた方が良いかもしれない。

【cocos2d-x】タッチした場所にエフェクトを入れる

よくスマホゲームって、タッチした場所にアニメーションを入れてエフェクトかけてるじゃないですか。

あれを実装して見たいと思います。

今回もフリー素材のぴぽや倉庫さんから素材を拝借しました。

すでに、タッチ処理のやり方もアニメーションのやり方も知っているので、これを組み合わせます。

コードはこうなりました。

bool HelloWorld::init()
{

  中略

    auto listener1 = EventListenerTouchOneByOne::create();
    listener1->onTouchBegan = CC_CALLBACK_2(HelloWorld::onTouchBegan, this);
    _eventDispatcher->addEventListenerWithSceneGraphPriority(listener1, this);

    return true;
}

bool HelloWorld::onTouchBegan(cocos2d::Touch* touch, cocos2d::Event* event)
{
    auto anime = Sprite::create("pipo-btleffect007.png");
    if (anime == nullptr)
    {
        problemLoading("'pipo-btleffect007.png'");
    }
    else
    {
        this->addChild(anime, 0);
    }
    float frameHeight = anime->getContentSize().height;
    float frameWidth = anime->getContentSize().width / 14.0;
    Vector<SpriteFrame*> animFrames;
    animFrames.reserve(14);
    for(int i = 0; i < 14; i++) {
        animFrames.pushBack(SpriteFrame::create("pipo-btleffect007.png", Rect(frameWidth * i,0,frameHeight,frameWidth)));
    }
    Animation* animation = Animation::createWithSpriteFrames(animFrames, 0.02f);
    Animate* animate = Animate::create(animation);

    anime->setPosition(touch->getLocation().x, touch->getLocation().y);
    anime->runAction(animate);

    return true;
}

前回のタッチ処理でラムダ式を使いましたが、

C++のラムダ式は思った以上に使いづらいので

普通にコールバックとして関数を定義し、マクロで登録する形にしました。

アニメーション画像は14枚横に並んでいるので、この画像を横に14等分割にクリッピングして使用しています。

あとはアニメーション画像の位置をタップ位置に設定してアニメーションを実行します。

前回はanimateをforever(無限ループ)していましたが、今回は1回きりのアニメーションなので、animateをそのまま使用しています。

どちらも親クラスがActionクラスなので、問題ありません。