Gaming Life

一日24時間、ゲームは10時間

(Linux) UDPを使って一定時間おきに文字列を送信するプログラム

研究室で,UDPを使った無線通信で,受信側が受信待ち状態にない時のパケットキャプチャをする必要があったので書いた.

劣化iperfにすぎないとか言ってはいけない

#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

enum { DEST_PORT = 12345 };

void rand_text(int length, char* result) {
    int i, rnd_int;
    const char char_set[] = "01234567890abcdefghijklmnopqrstuvwkyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
    for (i = 0; i < length; i++) {
        result[i] = char_set[rand() % strlen(char_set)];
    }
    result[length] = 0;
}

// ./main 192.168.20.2 7360(1パケットあたりのUDPデータ長[Byte]) 1(何秒間隔で送るか) count(合計何回送るか)
int main(int argc, char* argv[]) {
    if (argc < 5) {
        printf("command line arguments is invalid!\n");
        return 1;
    }
    const char* dest_addr = argv[1];
    const int udp_data_len = atoi(argv[2]);
    const int interval_sec = atoi(argv[3]);
    const int count_max = atoi(argv[4]);

    printf("Send random data(%dByte) %dtimes to %s through UDP to every %d sec\n", udp_data_len, count_max, dest_addr, interval_sec);
    char* str;
    str = (char*)malloc(sizeof(char) * udp_data_len);
    srand(time(NULL));
    rand_text(udp_data_len, str);
    printf("create string strlen(%ld)\n", strlen(str));

    int sock = socket(AF_INET, SOCK_DGRAM, 0);
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(DEST_PORT);
    addr.sin_addr.s_addr = inet_addr(dest_addr);

    int count = 0;
    while (count < count_max) {
        printf("send %ldByte\n", strlen(str));
        sendto(sock, str, strlen(str), 0, (struct sockaddr*)&addr, sizeof(addr));
        printf("----------\n");
        sleep(interval_sec);
        count++;
    }
    free(str);
    return 0;
}

30分程度で書いた使い捨てのプログラム故,エラー処理が一切書いてないのに注意.

std::bindではデフォルト引数を省略できない

std::bindを使っていたらある問題に遭遇した。

void func(int a){
    std::cout << a * 10 << std::endl;
}
int main(){
    using namespace std::placeholders;
    auto fuction = std::bind(func, _1);
    function(2);
}

std::bindを使ってライブラリで用意されている関数を束縛し別のところで呼び出す、といったことをしたかったのだが、これが何故かコンパイルが通らなかった。(実際は、クラスのメンバ関数をbindした。簡単のためそのコードはここに示す

なぜだろうか……といろいろ調べて回ったところ、とても単純な話だった。

void func(int a, int b = 10){
    std::cout << a * b << std::endl;
}
int main(){
    using namespace std::placeholders;
    auto fuction = std::bind(func, _1);
    function(2);
}

引数1つで呼び出すものと思っていたfunc()関数が、実際は第2引数にintを取る関数で、それにデフォルト引数が設定されていた。じゃあどうやってこの関数を束縛すればいいかというと、bind時、デフォルト引数も明示的に渡せばよい。

// Error!
// auto function = std::bind(func, _1);

// OK!
auto function = std::bind(func, _1, 10);

// call
function(2);

wandbox.org

bindは使うべきではないという話

ここまでbindを使って関数を束縛してきたが、調べてみると、bindはパフォーマンス上に問題があるらしい。

じゃあ代わりに何を使うのかっていうと、みんな大好きラムダ式である。

// Error!
// auto function = std::bind(func, _1);

// OK! (low performance)
auto function = std::bind(func, _1, 10)

// OK! (high performance)
auto function = [=](int _1){ func(_1); };

// call
function(2);

Compiler Explorerbindラムダ式を比較してみると一目瞭然でパフォーマンスにかなり影響することがわかる。

まとめ

bindなんか使わずラムダを使おう。

VSCodeでAsciidocを書く時相対パス入力補完を働かせる

当ブログで何度か紹介しているAsciidocだが,仕様書を書いたり,レポートを書いたりするのに非常に重宝している.

ただ,画像挿入時などで,相対パスの記述をする時に,Path InteliSenceによる入力補完が効かないことがとにかく不便だった.

import {} from './include/hogehoge'  // 引用符で囲む時は補完が効く
image::./img/sample.png     // 引用符で囲んでいないので補完が効かない

この度この問題を解決出来たのでメモ.

Path Autocompleteによるパス入力補完

まず,Path Intelisenceを入れている場合はアンインストールし,代わりに,Path Autocompleteを導入する.

marketplace.visualstudio.com

ただ,Path Autocompleteのデフォルト設定のままでは,目的の入力補完が効かない.

  • Path Autocompleteのデフォルト設定
import {} from './include/hogehoge'  // 引用符で囲む時は補完が効く
image::./img/sample.png     // 引用符で囲んでいないので補完が効かない

Path Autocompleteの設定の内, path-autocomplete.triggerOutsideStringstrueにすると,引用符で囲まなくても入力補完が効くようになる.

しかしながら,これでも,目的の入力補完が効かない.

  • Trigger Outside Strings On
import {} from './include/hogehoge'  // 引用符で囲む時は補完が効く
./img/sample.png            // 引用符で囲まなくても補完が効く
image::./img/sample.png     // ':' と './'の区切りを認識出来ないので補完が効かない.

path-autocomplete.pathSeparators は,文と ./ の区切りを定義している.ここに,: を加えることで, image::./img/sample.png と入力する時も,相対パスの補完が効くようになる.

まとめ

  1. Path Autocompleteを導入
  2. path-autocomplete.triggerOutisideStringsをtrueに設定
  3. path-autocomplete.pathSeparatorsに : を追加
  4. おわり!

プログラミングに関する調べ物や最新技術のキャッチアップ方法

※注 ポエム要素が高い記事です

最近は研究室で忙しく、ブログに公開できるようなネタになるようなことをやれないでいる。 しかし、月イチ更新は守りたい → じゃあプログラミングを始めた頃の自分が知りたかった情報を書こう → プログラマーってどうやって最新技術を追いかけて、わからなかったことを調べているんだ、と当時考えていたことを思い出す

というわけで、今の自分が実践している、プログラミングに関する調べ方、最新技術のキャッチアップ方法をつらづらと書いていく。

Google検索

まずはここから。どんなときも、わからないことがあったらGoogleに聞けば大体解決する。しかしながら、ここ最近のプログラミング関連のワードの、Google日本語検索の上位に出てくる記事は くだらないゴミ記事 正しい情報が書かれていないモノが増えてしまい、求めている、正確な情報には簡単にたどり着けなくなっている(特にPythonとか酷い)。あまりにも酷いサイトは、ブラウザの拡張機能で検索結果に表示されなくするのが吉。(FireFoxユーザの私はPersonal BlockList を使っている)

初学者の内は日本語情報でも十分だが、そのうち、日本語情報では不足するようになる。そういったときは、公式リファレンスや、Stack Overflow、Google英語検索を利用している。英語が読めなくても近年の機械翻訳は相当な精度なので、十分読めている。

書籍

信頼と安心の情報源。古い本だと、最新の環境で使えないことがままあるが、それでも、基礎を抑えるには最良の手。なにか新しい言語、ライブラリに挑戦するときは、まず、書籍を当たるようにしている。

また、特に買う予定の本もないのに本屋に行く事も続けている。何か行き詰まっていたり、求めている機能を提供してくれるソフトが見当たらなかったりした時、本屋に行くと答えがあったりする。そして買う予定のなかった本を買って財布が薄くなる。

はてなブックマーク

Qiitaやら、ITMediaやら、engadgetやら、各サイトそれぞれ見にいくってのも一つの手ではあるが、毎日全部見るというのは、現実的には難しい。私は、代わりにはてブテクノロジーカテゴリのトレンド を一通り見ている。 トレンドに上がっている中で、気になる情報があればそのサイトに飛んでみるし、タイトルだけ見ておくだけでも、最新トレンドを知(った気にな)ることができる。

Twitter

その分野の第一人者とも言えるような人をフォローし、 TweetDeck でその人の全ツイートを追っかける、みたいなストーカー的ムーブができる。真似しないほうがいい。

最近、ハマっているのは、エラーに悩まされていたり、ソフトの使い方がわからなかったときに、Google検索の前に、Twitter検索で調べるということ。「Python リスト」みたいなざっくりした検索ワードだと、なんの役にも立たないが、エラー番号等、詳細なキーワードで検索すると、Google検索するより早く解決することがある。

興味のある技術分野に関わる人々を沢山フォローすると、TLを眺めているだけで有益な情報が集まってくる。

デメリットはTwitterにハマりすぎて、作業が疎かになりがちなこと。(そしてこれが最近の悩みでもある)

まとめ

こういって調べ事をしていると、最初の内は何のことかわからないワードだらけだが、継続は力なり。いずれ理解できる日が来る。そして1理解すると10理解できない事が生まれ、またそれを調べる。プログラマーとはそうやって永遠に終わりのない調べ物をし続ける生き物なのだなぁとつくづく思う近頃。これからも続けていきたい。

Dungeon Template LibraryをUE4で利用してLandscapeを自在に操る

@wanotaitei さんが開発しているDungeon Template Library(以後DTL) がUE4で使えそうだったので使ってみた。

f:id:ai_gaminglife:20190518201300g:plain

github.com

画像

UE4でDTLを使えるようにする

上記リンクからDTLをcloneしておく。

C++プロジェクトとしてUE4プロジェクトの Source/ 以下に library/DTL ディレクトリを作成。そこに、DTLライブラリの include 以下をコピーする。

UE4プロジェクトを開き直し、 (プロジェクト名)Build.cs に以下を記述。

        PublicIncludePaths.Add(ModuleDirectory + "/library/DTL/");

        PrivateDependencyModuleNames.AddRange(new string[] { "Landscape", "LandscapeEditor" });

これで、DTLと、後に使うLandscape関連の関数が利用できるようになる。

Landscapeを操作する関数を実装する

BlueprintFunctionLibraryを継承したクラスを作成し、以下をコピペ。

  • DTLLandscapeControlInEditor.h
#pragma once

#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "DTLLandscapeControlInEditor.generated.h"

UCLASS()
class DTLSAMPLEUE4_TPS_API UDTLLandscapeControlInEditor : public UBlueprintFunctionLibrary
{
    GENERATED_BODY()

    UFUNCTION(BlueprintCallable, Category = DTL, meta = (HidePin = "worldContextObject_", DefaultToSelf = "worldContextObject_"))
    static bool perlinNoise(const UObject* worldContextObject_, float scale);
};
  • DTLLandscapeControlInEditor.cpp
#include "DTLLandscapeControlInEditor.h"

#include "EngineUtils.h"
#include "Landscape.h"
#include "LandscapeInfo.h"
#include "LandscapeEditor/Public/LandscapeEditorUtils.h"

#include <DTL.hpp>

bool UDTLLandscapeControlInEditor::perlinNoise(const UObject* worldContextObject_, float scale)
{
    UWorld* world = worldContextObject_->GetWorld();
    for (TActorIterator<ALandscape> actorItr(world); actorItr; ++actorItr)
    {
        ALandscape* landscape = *actorItr;
        if (landscape != nullptr)
        {
            // 2: ULandscapeInfoの初期化
            ULandscapeInfo::RecreateLandscapeInfo(world, false);

            FIntRect rect = landscape->GetBoundingRect();
            int32 w = rect.Width() + 1;
            int32 h = rect.Height() + 1;

            using shape_t = uint16;
            std::vector<std::vector<shape_t>> matrix(h, std::vector<shape_t>(w, 0));
            constexpr double frequency = 6.0;
            constexpr uint8_t octaves{ 16 };
            const uint32_t seed{ static_cast<uint32_t>( rand() ) };

            const dtl::utility::PerlinNoise perlin(seed);
            double frequency_x{ w / frequency };
            double frequency_y{ h / frequency };

            for (std::size_t row{}; row < h; ++row)
                for (std::size_t col{}; col < w; ++col)
                    matrix[row][col] = static_cast<shape_t>(50.0 * perlin.octaveNoise(octaves, col / frequency_x, row / frequency_y));

            TArray<uint16> Data;
            Data.Init(0, w * h);
            for (auto x = 0; x < w; x++)
            {
                for (auto y = 0; y < h; y++)
                {
                    Data[x * h + y] = static_cast<uint16>(FMath::FloorToInt(scale * matrix[y][x]));
                }
            }

            LandscapeEditorUtils::SetHeightmapData(landscape, Data);
            return true;
        }
    }
    return false;
}

エディタ側からコンパイルし、通れば成功。

実装した関数を呼び出すActorを作成

Actorを継承したブループリントクラスを作成。画像のようなBPを組む。この際、EventPerlinNoiseCall in Editor フラグにチェックを、Scale float変数は、Instance EditableExpose on Spawn にチェックを入れる、

f:id:ai_gaminglife:20190518201112p:plain

作成したActorを 適当にレベルに配置、続いて、Landscapeを作成し、機能別サンプルから取ってきたLandscape Material を適当に割り当てて上げれば完成。

まとめ

現在エディタ側からしかLandscapeを操作出来ないが、ランタイムでLandscapeを操作できると嬉しい。また、まだ試してないが、他のダンジョン生成関数との連携は簡単にできそう。

参考サイト

unwitherer.blogspot.com

WSLのUbuntu18.04でmatplotlibを使えるようにするまで

WSLのUBuntu18.04はGUI表示が存在しないので、matplotlibを使ったグラフ描画ができない。だが、調べてみると、何とか描画できるようにできたので、メモしておく。

前準備

> sudo apt update && sudo apt upgrade
> sudo apt install python3-pip

matplotlibの導入

pip3を使ってmatplotlibと、セットで使われがちなnumpyを導入しておく

> pip3 install numpy
> pip3 install matplotlib

matplotlibを使った参考プログラム

vim等のエディタを使って、以下のコードを入力

> vim sampleGraph.py
import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 10, 100)
y = np.random.randn(100)
plt.plot(x, y)
plt.show()

これを実行してみる。

> python3 sampleGraph.py

この時点で Tkinterがないだのというエラーが出たら、以下のコマンドを実行

> sudo apt install python3-tk
> sudo apt install tk-dev

ウィンドウに描画できるようにする

sourceForgeで、VcXsrvをDLする。

インストーラーでは特に追加設定の必要なし。指示通り勧めていけばよい。

インストールが終わったら、Windowキーを押し、XLaunchを実行する。

デフォルト設定のまま進め、完了を押す前にSave configurationを押し、設定ファイルをわかりやすいところ、例えばデスクトップに保存しておく。

完了を押せば、XLaunchが起動する。

WSLに戻って、以下のコマンドを実行。

> sudo vim ~/.bashrc

最後の行に以下を追加して保存する。

export DISPLAY=:0.0

WSLを再起動(ウィンドウを閉じるか、exitすれば良い)すればこの設定が読み込まれて、設定が反映される。

この後pythonを実行すると

> python3 sampleGraph.py

f:id:ai_gaminglife:20190429204952p:plain

こんな感じでウィンドウが表示されるようになる。

ほぼ週刊UE4日記02 Editor Utility Widgetを試してみる

UE4.22から、Editor Utility Widgetなる、エディタ拡張をUMGとBlueprintで簡単に実装できる機能が出来たらしいので試してみた。

といっても正直公式の人のQiita記事が十分過ぎる程丁寧に説明してくれている。

qiita.com

qiita.com

ここでは、1つ目の記事で紹介されている「レベル上にあるTick有効なActor, Componentを可視化する」エディタ拡張に、「レベル上にあるTick有効なActorを選択する」ボタンを追加してみた。

BPの中身はこんな感じ。

f:id:ai_gaminglife:20190421171952p:plain

用意したボタンの「On Clickedイベント」で、レベル上のTick有効アクターを配列に格納し、「Set Selected Level Actors」ノードでそれらを選択するというだけ。

Create TransactionとSequenceノードを使うことで、Undo・Redoに対応している。

Create TransactionとSequenceノードの組み合わせでUndo・Redo対応させるのは、毎回使う処理なので、以下のような、"Auto Close Create Transaction"マクロを作成し、マクロライブラリに用意しておくのはありだと思う。

f:id:ai_gaminglife:20190421172002p:plain

まとめ

  • Editor Utility Widgetは現時点でかなり沢山の事が、気軽に実装できる
  • Create Transactionを使えばUndo・Redoに対応させられる