C++ ときどき ごはん、わりとてぃーぶれいく☆

USAGI.NETWORKのなかのひとのブログ。主にC++。

UE4: スクリーンサイズ可変に対応したスクリーン座標へのUMGの表示のブループリント備忘録

アクターのロケーションなど適当な World 座標を基準にHUD風にUMGを表示したい場合、 World 座標から ConvertWorldLocationToScreenLocation で得られるスクリーン座標をそのまま Set Render Translation へ放り込むとスクリーンサイズ可変の場合には実際の表示位置がずれる。そういうわけで、最終的にどうなるか、のメモ:

f:id:USAGI-WRP:20180301130233p:plain

この例では、適当なワールド座標から ConvertWorldLocationToScreenLocation で得られたスクリーン座標を元に、適当なウィジェット p0 を最終的に黄色の枠の Set Render Translation により位置合わせして表示する。アプリの想定する標準のスクリーンサイズは 1920 x 1080 だがスクリーンサイズはウィンドウサイズに併せて任意にユーザーが可変できる。橙色の枠の部分でスクリーンサイズが標準サイズではない場合、アスペクト比に応じて高さまたは幅に併せて実際の表示位置へ調整される係数を計算、決定している。また、赤色の枠の部分では、スクリーンサイズが一定以下となる場合にUMGが縮小しなくなるためそれに併せて係数の下限が一定となるよう調整している。

UE4: FHttpModule 系の HTTP リクエスト機構を使い "同期処理" する方法

概要

UE4 で HTTP リクエストを飛ばしたい場合、 HTTP モジュールを有効化し、 FHttpModule::Get で得たオブジェクトから FHttpModule::CreateRequest し、 IHttpRequest のプラットフォームのオブジェクトを得て URL 等の必要なリクエストパラメーター及び OnProcessRequestComplete 等を設定した後、 ProcessRequest する。

この UE4 の HTTP モジュールは "非同期処理" を前提に実装され、インターフェースが提供され、 examples でも非同期処理で HTTP モジュールを扱っている例が幾つか見当たる。

この記事では "諸事情により UE4 の HTTP モジュールを同期処理したい場合" に遭遇した際にどのような実装を行えば良いか整理する。

実装

同期処理でも非同期処理でも同様の部分:

// 1. リクエストを生成
auto request = FHttpModule::Get().CreateRequest();
// 2. リクエストを設定
request->OnProcessRequestComplete().BindLambda( /* 省略 */ );
request->SetURL( /* 省略 */ );
/* 省略 */
// 3. リクエストの開始処理を呼ぶ
request->ProcessRequest();

同期処理したい場合に加える必要のある実装:

// 4.a. HTTP モジュールを強制的に作動させる
FHttpModule::Get().GetHttpManager().Flush( false );

別の実装方法:

// 4.b. HTTP モジュールを "ちまちま" と強制的に作動させる
do
{
  std::this_thread::sleep_for( std::chrono::milliseconds( 10 ) );
  FHttpModule::Get().GetHttpManager().Tick( 0.010f );
}
while ( request->GetStatus() == EHttpRequestStatus::Processing );

概説

  • IHttpRequest::ProcessRequestFHttpManager へ自身のスマートポインターを登録し処理開始させるなどする
  • FHttpManager::Flush は保持中の IHttpRequest のスマートポインター群のレスポンス取得処理が完了するまで自身の Tick をマシン時間で進める
  • FHttpManager::Tick は保持中のIHttpRequest のスマートポインター群のレスポンス取得処理を進め、 IHttpRequest オブジェクトの Tick も呼ぶ
  • IHttpRequest::Tick レスポンス取得、ステータス更新、タイムアウト処理などを進める

と、いうわけで簡単に同期処理として HTTP モジュールを使いたい場合には非同期処理の実装に加えて FHttpModule::Get().GetHttpManager().Flush( false ) を呼べば良い。

参考

  1. FHttpModule | Unreal Engine
  2. FHttpManager | Unreal Engine
  3. IHttpRequest | Unreal Engine
  4. EHttpRequestStatus::Type | Unreal Engine

Note: 実際にこのような実装が可能かはそれぞれのソースコードの実装詳細を読んで確認している。

キーボード: Azio Retro Classic Posh

美しいキーボードというのも魅力的なもので、 Azio Retro Classic Posh を Makuake で購入していたものが昨年末に届き、数日間使ってみた。

美しさについてのみ評価すれば100点満点とした上で85点。15点分の減点理由は次の通り:

  1. キーが樹脂部品でチープ感が否めない。銅製とは言わないが、せめてキーボード本体のフレームと同じ程度の金属感のある素材を期待していた。
  2. キーボード右上のインジケーターのLEDが内部で隣と仕切りなしに繋がっているようで光が漏れ安っぽさを感じる。

美しさについてはおおむね満足。

しかし、実用性についてはあまり良くない:

  1. キーがぐらつく。ほぼ垂直に打鍵しない限り、ぐらつきのために指の動きが安定しない。
  2. 抵抗が大きい。特に高速にタイプする際に引っ掛かりを大変強く感じ指の制御が乱れされてしまう。私は赤軸に慣れていることもあり、やや押下に強い力を必要とするため指が疲れる。カチ音などどうでもよいのでこのキートップとぐらつきならば赤軸かそれ以上に抵抗のないキースイッチを採用したバージョンがあるとよかったのではないか。
  3. ファンクションキー群が遠い。通常の手首の位置からでは指を伸ばしても素早く正確に打鍵できるほどには届かない。世間一般のユーザーにとってはファンクションキーは滅多に使わない飾りやマルチメディアキーになっているのかもしれないが、ゲーマーやプログラマー、あるいは文書書きであっても相応にファンクションキー群を使う事に慣れている方には「よいしょ」と手の位置をキーボード上部へシフトする必要があり疲れる。
  4. JIS配列なのに RETURN] の位置が英語配列。JISならJISにして欲しかった。
  5. 打鍵後の残響が安っぽい。ずしりとした音響ならともかく、樹脂部品がはねたような残響はチープでやや不快感の方が強いものだった。

メインのキーボードとして使うつもりで購入したが、私にはどうも実用上の都合が合わないところが大きいようで残念な買い物だったようだ。

Ultimate Hacking Keyboard が想定より開発が遅くまだ届かないところ、闇のキーボードに対して光のキーボードのような面白さも思い購入した Azio Retro Classics Posh であったが、 Ultimate Hacking Keyboard の到着を前に、キートップの禿げてしまった Corsair K60 へ戻そうか検討している。

慣れでどうにかなろうかと思い、もうしばらくは使ってみるつもりだが、いずれ、 Ultimate Hacking Keyboard が届けば倉庫行きになってしまいそうだ。 Azio Retro Classics Posh の購買層は美しさにある程度お金を出せる層だろうから、1つ5万円程度にコストアップしても、どうせならばもっと重厚で本格的な作りだとより嬉しかったかもしれない。

A Poem of "Hello, World!" using C++

0. はじめに

この記事は 初心者C++er Advent Calendar 2017 の DAY 24 に寄稿したものです。😃

概要

C++初心者向けの記事を書く」というお題のアドベンカレンダーがあったので、私が思いつく限りで兎にも角にも "Hello, World!" を出力する C++ を使ったコード例を淡々と紹介したいと思います。

Note: この記事は「かれんだー埋まらなさそー><」と思い、ふんわり書いたポエムです。途中からかるぅく怪しいわぁるどになりますのであんまり真面目に読まず、春はあけぼの的なゆるっとした気持ちで眺めて頂ければ幸いです。😏

1. いーじーもーど 編

よくあるやつ:

#include <iostream>
int main() { std::cout << "Hello, World!"; }
Hello, World!

モダンな世界のデファクトスタンダードな亜種:

#include <iostream>
int main() { std::cout << u8"Hello, World!"; }
Hello, World!

むかしはこれがモダンだった亜種:

#include <iostream>
int main() { std::wcout << L"Hello, World!"; }
Hello, World!

悲しみの亜種:

#include <iostream>
// Note: C++17 Deprecated
#include <codecvt>
#include <locale>
int main()
{
  std::cout
    <<  std::wstring_convert
        < std::codecvt_utf8_utf16
          < char16_t
          >
        , char16_t
        >().to_bytes( u"Hello, World!🍣" )
    ;
}
Hello, World!

悲しみの亜種の亜種:

#include <iostream>
// Note: C++17 Deprecated
#include <codecvt>
#include <locale>
int main()
{
  std::cout
    <<  std::wstring_convert
        < std::codecvt_utf8
          < char32_t
          >
        , char32_t
        >().to_bytes( U"Hello, World!🍣" )
    ;
}
Hello, World!

ひつようじゅうぶん:

#include <cstdio>
int main() { puts( "Hello, World!" ); }
Hello, World!

そういうの、こせいじゃないから…:

#include <cstdio>
int main() { printf( "%s, %s!", "Hello", "World" ); }
Hello, World!

さいしゅうへいき:

#include <cstdlib>
int main() { system( "echo Hello, World!" ); }
Hello, World!

ばいなりあん:

#include <cstdio>
#include <initializer_list>
int main()
{
  for
  ( auto b 
  : { 0x48, 0x65, 0x6c, 0x6f
    , 0x2c, 0x20, 0x57, 0x6f
    , 0x72, 0x6c, 0x64, 0x21
    }
  )
    putchar( b );
}
Hello, World!

2. るなてぃっく 編

Note: "なんいど" とは別のいみで "るなてぃっく" ・x・

ふてくされ:

#include <cassert>
int main() { assert( false && "Hello, World!" ); }
prog.exe: prog.cc:2: int main(): Assertion `false && "Hello, World!"' failed.
Aborted

こんぱいるたいむ・ふてくされ:

int main() { static_assert( false, "Hello, World!" ); }
prog.cc: In function 'int main()':
prog.cc:1:29: error: static assertion failed: Hello, World!
 int main() { static_assert( false, "Hello, World!" ); }
                             ^~~~~

がめんにでれば、いいんでしょ:

#include <Hello, World!>
int main() { }
prog.cc:1:10: fatal error: Hello, World!: No such file or directory
 #include <Hello, World!>
          ^~~~~~~~~~~~~~~

がめんにでれば、いいんでしょの亜種:

#pragma Hello, World!
int main() { }
prog.cc:1: warning: ignoring #pragma Hello  [-Wunknown-pragmas]
 #pragma Hello, World!

がめんにでるとは・・・:

"Hello, World!"
prog.cc:1:1: error: expected unqualified-id before string constant
 "Hello, World!"
 ^~~~~~~~~~~~~~~

えらーじゃないからはずかしくないもん:

"Hello, World!"
clang++ -E a.cpp
# 1 "a.cpp"
# 1 "<built-in>" 1
# 1 "<built-in>" 3
# 325 "<built-in>" 3
# 1 "<command line>" 1
# 1 "<built-in>" 2
# 1 "<a.cpp>" 2
Hello, World!

おしまい・x・
ゆるゆると肩の力が抜けて頂けていれば幸いです。

・・・それから・・・
メリークリスマス!😃
🍣🍗🍰

UE4: 点や線を簡易的に3D空間内へ描画する方法、あるいは UKismetSystemLibrary::DrawDebug 系の紹介

概要

見栄えはともかく、3D空間内の任意の座標へ、点や線、あるいは立方体や球などを、さっくりと簡単に表示したい事がしばしばあります。特にデバッグ用途でそのような表示機能を上手く仕込んで置くと開発効率が向上します。

そのような場合に、わざわざ Billboard にテクスチャーを貼ったオブジェクトを用意したり、BOXを引き伸ばして配置したり、 UProceduralMeshComponent を使うまでもなく便利に使える UE4 に標準搭載された UKismetSystemLibrary::DrawDebug 系の機能を紹介したい。

f:id:USAGI-WRP:20171221045628p:plain

UKismetSystemLibrary::DrawDebug 系

UE4 に標準搭載された UKismetSystemLibrary::DrawDebugXxxx 系の関数群を用いると次のジオメトリー群の描画を簡単に実装できる:

  • header: #include "Runtime/Engine/Classes/Kismet/KismetSystemLibrary.h"
  • Xxxx: ここは実際には Point などジオメトリーの種類で置換する。
Xxxx 効果
Arrow 矢印
Box 立方体
Camera カメラ
Capsule カプセル
Circle
Cone 円錐(長さで開きを定義)
ConeInDegrees 円錐(角度で開きを定義)
CoordinateSystem 座標系
Cylinder 円柱
FloatHistoryLocation 軌跡
FloatHistoryTransform 変形跡
Frustum 錐台
Line 線分
Plane 平面
Point 点(正方形)
Shpere
String 文字列

使い方

#include "Runtime/Engine/Classes/Kismet/GameplayStatics.h"
#include "Runtime/Engine/Classes/Kismet/KismetSystemLibrary.h"

// 適当なアクターに実装
void AMyPawn::Tick( float DeltaTime )
{
  Super::Tick( DeltaTime );

  // 2.5 秒ごとに表示を on/off する細工
  static float tt = 0;
  tt = FMath::Fmod( tt + DeltaTime, 5.0f );
  if ( tt < 2.5f )
  {
    // アクターの位置に赤い球を描画
    UKismetSystemLibrary::DrawDebugSphere( GetWorld(), GetActorLocation(), 100.0f, 12, FLinearColor::Red, 0.0f, 3.0f );
    // プレイヤーの位置を取得(本題と直接は関係しない)
    auto l0 = UGameplayStatics::GetPlayerPawn( GetWorld(), 0 )->GetActorLocation();
    // プレイヤーの位置からこのアクターの位置へ黄色の矢印を描画
    UKismetSystemLibrary::DrawDebugArrow( GetWorld(), l0, GetActorLocation(), 100.0f, FLinearColor::Yellow, 0, 3.0f );
  }
}

// プレイヤーのアクターに実装
void TestCharacter::Tick( float DeltaTime )
{
  Super::Tick( DeltaTime );

  // プレイヤーの位置に "PLAYER[0]" を緑色で描画
  UKismetSystemLibrary::DrawDebugString( GetWorld(), GetActorLocation(), TEXT( "PLAYER[0]" ), nullptr, FLinearColor::Green, 0 );
}

このシリーズの関数の引数は始めに UWorld を取り、続く前半はそれぞれ点であったり、始点と終点であったり、さまざま、後半は色と表示の持続時間、それと設定可能な場合は線の太さなどを与える仕組みです。具体的には API Reference をどうぞ。

この方法の特徴

  1. 見栄えはともかく、表示したいだけならばとても簡単。すぐに実装できる。
  2. 表示だけ、なので当たり判定を取りたい場合などには役に立たない。
  3. 色に不透明度を定義しても反映されない。
  4. 球などの立体も稜線しか描画できない。
  5. ライティングも適用されないし、発光もできない。

参考

UE4: TPSカメラの当たり判定を on/off する簡単な方法、あるいは USpringArmComponent のそれについて

概要

TPS テンプレートで生成されるような、プレイヤーの回りにぐるぐるとカメラを回す実装ではしばしば床、天井、柱、地形、キャラクター、その他の何らかの物体に対して「カメラの当たり判定」が有って欲しい状況と無い方が好ましい状況、あるいは実行中のその切り替えが欲しい場合がある。

UE4では一般にカメラとの当たり判定処理についてググると「カメラに当たりたくないオブジェクトのコリジョンからカメラを外せ」的な方法ばかり出てきますが、 TPS テンプレートで標準でプレイヤーに付いているようなカメラの場合はもっと簡単に対応できるので紹介したい。

前提知識: TPS のカメラとは何か

TPS テンプレートで標準で設定されるカメラは「プレイヤーキャラクターにアタッチしたスプリングアームにアタッチしたカメラ」です。

AMyCharacter <-- USpringArmComponent <-- UCameraComponent

TPS カメラの当たり判定の on / off

// TPS テンプレートで作られるプレイヤーキャラクターまたは同様の何かの ctor で
// TPS カメラの当たり判定を無効にする場合
AMyCharacter::AMyCharacter()
{
  // 生成とアタッチと回転制御はテンプレートまま(本題ではない)
  CameraBoom = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraBoom"));
  CameraBoom->SetupAttachment(RootComponent);
  CameraBoom->bUsePawnControlRotation = true;
  // これだけでカメラの当たり判定が無効になる
  CameraBoom->bDoCollisionTest = false;
}

// おまけ: ToggleCameraCollision 関数として実装する場合
void AMyCharacter::ToggleCameraCollision()
{ CameraBoom->bDoCollisionTest = ! CameraBoom->bDoCollisionTest; }

Note

  • 大雑把に on / off できれば構わない場合はおそらく最も簡単
  • オブジェクト個別やトレースチャンネルレベルでの細かい設定ではないので、その必要がある際は諦めてちまちまコリジョン設定する

Reference

  1. USpringArmComponent | Unreal Engine

UE4: UnrealWebServer Plugin が 1.4+ のバージョンアップで URL ハンドラーの path ワイルドカードと接続情報からの path の取得に対応

概要

プラグインのバージョン情報の定義は 1.4 のままですが、 UnrealWebServer が更新され、新機能が追加されました。今回追加された新機能は以前にバグレポートとパッチをなかのひとへ送った際に「ついでに将来的にこんな機能にも対応してくれたら嬉しいな😃」と相談していたもので、私の用途ではこれまでのアップデートも含め、 UnrealWebServer の実用性が必要十分に達しました。

isaratech.com

今回追加された機能

  1. Add URIHandler のパラメーター Path* によるワイルドカードを設定可能になった。
  2. Connection に対するメソッド Get Uri Path が追加されリクエストされたパスを接続情報から取得可能になった。

f:id:USAGI-WRP:20171212200248p:plain

f:id:USAGI-WRP:20171212200357p:plain

それでどんな事ができるようになったのか

今回の「URLハンドラーのパスのワイルドカード対応」と「接続情報からのパス取得」、それと以前の更新で追加された「リクエストのRAWボディー取得」と「レスポンスへのRAW書き出し」を組み合わせると、ファイルサーバーをエミュレートする事もできます。例えば、 JSON-RPC-2.0 で API を提供するアプリでもファイル単位のバイナリーを受け取ったり、提供したり、そんなような事を簡単に実装したいニーズがあるときに、 UWS を使えば少々のコストで簡単にそんなような API サブシステムを実装できるようになりました。

DELETE などもハンドラーとしては扱えるので、 いわゆる REST API 的な実装もかんたんに対応できるようになりました。

Related

usagi.hatenablog.jp

usagi.hatenablog.jp