TECH PLAY

電通総研

電通総研 の技術ブログ

836

thumbnail こんにちは!金融ソリューション事業部WEB3グループの山下です。 本記事は「SpatialComputing入門」シリーズのPart2となります。 前回のPart1 で追加した3Dオブジェクトに対して複数のGestureによるインタ ラク ションを実装していきます。 「SpatialComputing入門」シリーズ: Part1:3Dオブジェクトを生成して空間に配置する Part2:3DオブジェクトにGestureを追加する Part3:現実空間の平面に3Dオブジェクトを配置する 動作環境 Apple M1 Max ( macOS 14.5) Xcode 15.4 VisionPro (visionOS 1.2) 実装手順 TapGestureの追加 VisionPro動作確認 DragGestureの追加 VisionPro動作確認 1. TapGestureの追加 Part1 で作成したVolumeView.SwiftのStructを、以下に修正します。 オブジェクトをTapをすると、3Dコンテンツのスケールを増加する処理を実装します。 struct VolumeView : View { @State private var scale : Float = 1.0 var body : some View { RealityView { content in // Generate ModelEntity let model = ModelEntity( mesh : .generateCone(height : 0.1 , radius : 0.1 ), materials : [ SimpleMaterial ( color: .lightGray , isMetallic: true )] ) // Add components model.components. set (InputTargetComponent()) model.components. set (CollisionComponent(shapes : [ .generateSphere ( radius: 0.1 )] )) // Add ModelEntity to RealityView content content.add(model) } update : { content in if let model = content.entities.first { model.transform.scale = [scale, scale, scale] } }.gesture(TapGesture().targetedToAnyEntity().onEnded{ _ in scale += 0.1 }) } 変更点は以下のとおりです。 State変数の追加 @State private var scale:Float = 1.0 :スケール率を管理するState変数を追加 .update: クロージャ を追加: RealityViewで最初に取得するcontentに対して、scaleを変更する State変数が変更されたら毎回呼び出される処理 .gestureイベントを追加: TapGester()が終了したら、scaleを0.1増加 2. VisionPro動作確認 実行すると、以下のような挙動になります。 オブジェクトを見てTapすると、スケールが増加するインタ ラク ションを確認できます。 3. DragGestureの追加 次に、Dragをすることで3Dコンテンツが回転する処理を実装します。 VolumeView.Swiftを、以下に修正します。 struct VolumeView : View { @State private var scale : Float = 1.0 @State private var rotationAngleX : Float = 0.0 @State private var rotationAngleY : Float = 0.0 @State private var lastTranslation : CGSize = .zero var body : some View { RealityView { content in // Generate ModelEntity let model = ModelEntity( mesh : .generateCone(height : 0.1 , radius : 0.1 ), materials : [ SimpleMaterial ( color: .lightGray , isMetallic: true )] ) // Add components model.components. set (InputTargetComponent()) model.components. set (CollisionComponent(shapes : [ .generateSphere ( radius: 0.1 )] )) // Add ModelEntity to RealityView content content.add(model) } update : { content in if let model = content.entities.first { model.transform.scale = [scale, scale, scale] model.transform.rotation = simd_quatf(angle : rotationAngleY , axis : [ 0, 1, 0 ] ) * simd_quatf(angle : rotationAngleX , axis : [ 1, 0, 0 ] ) } } .gesture(TapGesture().targetedToAnyEntity().onEnded{ _ in scale += 0.1 }) .simultaneousGesture( DragGesture(minimumDistance : 0 ) .onChanged { value in let translation = value.translation if abs(translation.height) > 0 { rotationAngleX += Float(translation.height - lastTranslation.height) * 0.01 } if abs(translation.width) > 0 { rotationAngleY += Float(translation.width - lastTranslation.width) * 0.01 } lastTranslation = translation } .onEnded { _ in // ドラッグ終了時に変化量をリセット lastTranslation = .zero } ) } 変更点は以下のとおりです。 State変数の追加 @State private var rotationAngleX: Float = 0.0 : @State private var rotationAngleY: Float = 0.0 : @State private var lastTranslation: CGSize = .zero : .update クロージャ の修正 Drag ジェスチャー で取得したrotationAngleを用いて、モデルを回転させる処理を追加 .simultaneousGestureイベントの追加 Drag ジェスチャー の上下/左右の動きに対して、それぞれrotationAngleXとrotationAngleYを割り当てる 既にTap ジェスチャー があるので、非同期での ジェスチャー を有効にする為に.gestureではなく.simultaneousGestureを追加 以下の回転処理のコードが少し特殊なので、補足します。 model.transform.rotation = simd_quatf(angle: rotationAngleY, axis: [0, 1, 0]) * simd_quatf(angle: rotationAngleX, axis: [1, 0, 0]) ここでは simd_quatf(angle:axis)関数 を用いてmodelを回転させています。 引数であるangle:では、Float値で回転角度を指定しています。 axis:では回転させる座標軸を指定([0, 1, 0]の場合はY軸周り、[1, 0, 0]の場合はX軸周り)しています。 上記により、DagGestureで取得した上下左右の動きをモデルの回転に変換することで、直接物体をつかんで回転させているようなジェスチャを実現しています。 4. VisionPro動作確認 実行すると、以下のような挙動になります。 Dragしている手の動きに応じて、モデルが回転していることを確認できました。 終わりに 今回はSpatialComputingの入門編Part2として、VisionProにて導入された目および手を用いたインタ ラク ションを実装しました。 今回はシステムが提供する ジェスチャー を用いましたが、ARKitを用いることで独自のカスタム ジェスチャー を実装することも可能です。ARKitの空間認識機能やアンカー機能を用いるには、今回実装したようなWindowやVolumeではなく、ImmersiveViewでの実装が必要になります。 次回 Part3 はImmersiveViewの実装方法をご紹介します。 現在、 電通 総研は web3領域のグループ横断組織 を立ち上げ、web3および メタバース 領域のR&Dを行っております(カテゴリー「3DCG」の記事は こちら )。 もし本領域にご興味のある方や、一緒にチャレンジしていきたい方は、ぜひお気軽にご連絡ください! 私たちは一緒に働いてくれる仲間を募集しています! 電通総研の採用ページ 参考文献 Design for spatial input Gestures simd_quatf(angle:axis)関数 執筆: @yamashita.yuki 、レビュー: @handa.kenta ( Shodo で執筆されました )
thumbnail こんにちは!金融ソリューション事業部WEB3グループの山下です。 本記事は「SpatialComputing入門」シリーズのPart3となります。 前回の Part1 および Part2 にて紹介したWindowやVolumeとは別の、現実世界とインタ ラク ションが可能なImmersiveViewを用いて現実世界の平面に対してオブジェクトを配置します。 「SpatialComputing入門」シリーズ: Part1:3Dオブジェクトを生成して空間に配置する Part2:3DオブジェクトにGestureを追加する Part3:現実空間の平面に3Dオブジェクトを配置する WorldTrackingについて 実装に入る前に、今回のデモ実装で利用するWorldTracking機能、およびARKitのプライバシーについて解説します。 画像参考: Meet ARKit for spatial computing iOS などと異なりvisionOSでは、カメラや加速度センサーのデータがアプリに対して直接開放されていません。 その代わり、ARKit daemonsによって適切に処理された結果をアプリが利用できるようになっています。 また基本的に、ARKitを用いた空間認知やハンドト ラッキング にアクセスする為にはユーザーの アクセス許可 が必要になります。 画像参考: Meet ARKit for spatial computing ただ今回用いる AnchorEntity に関してはWorldTrackingのデータを用いており、ユーザーによるアクセス許可は不要になります。 動作環境 Apple M1 Max ( macOS 14.5) Xcode 15.4 VisionPro (visionOS 1.2) 実装手順 App.swiftにImmersiveViewを追加 ContentViewにButtonを追加 ImmersiveView.swiftを新規作成 VisionPro動作確認 1. App.swiftにImmersiveViewを追加 Part2 との変更点のみ記載します。 ModelEntityTestApp.swiftのbody内に、以下を追記します。 ImmersiveSpace(id : "ImmersiveSpace" ) { ImmersiveView() } Part1 でVolumeを追加した際と同様、ImmersiveSpaceでもidを指定して呼びだす際に利用します。 2. ContentViewにButtonを追加 ContentView.swiftのbody内に、ImmersiveViewを開く為のButtonを追加します。 Button(action : { showImmersiveSpace = true }) { Text( "Show ImmersiveSpace" ) } 今回は、既にVolumeを開く為のButton Viewがあった為、VStack内で上記Buttonを追加しました。 3. ImmersiveView.swiftを新規作成 新規にImmersiveView.swiftを作成して、以下を記載します。 import SwiftUI import RealityKit import RealityKitContent struct ImmersiveView : View { @State private var contentReference : RealityViewContent? @Environment ( \.dismiss ) private var dismiss var body : some View { RealityView { content in self .contentReference = content setupTimer() } .ignoresSafeArea() } private func setupTimer () { Timer.scheduledTimer(withTimeInterval : 0.01 , repeats : true ) { _ in DispatchQueue.main.async { addAnchor() } } } @MainActor private func addAnchor () { guard let content = contentReference else { return } let anchorEntity = AnchorEntity(plane : .vertical) let model = ModelEntity( mesh : .generateCone(height : 0.01 , radius : 0.01 ), materials : [ SimpleMaterial ( color: .yellow , isMetallic: true )] ) anchorEntity.addChild(model) content.add(anchorEntity) removeAnchor(after : 3 , anchor : anchorEntity ) } private func removeAnchor (after seconds : Double , anchor : AnchorEntity ) { DispatchQueue.main.asyncAfter(deadline : .now() + seconds) { anchor.removeFromParent() } } } #Preview { ImmersiveView() } 概要は、以下のとおりです。 変数宣言 @State private var contentReference: RealityViewContent? RealityViewのcontent参照を保持するための変数 外部に定義した関数でRealityViewのcontentにアクセスする際に利用します @Environment(\.dismiss) private var dismiss ビューを閉じるための 環境変数 addAncor関数 AnchorEntity(plane: .vertical) で垂直な面を検出して、アンカーを取得します RealityViewでは、画面に描画されるUIやEntityの変更はメインスレッドで行う必要があります。その為、addAnchor関数に@MainActorを付与しています setupTimer関数 Timerクラスを用いて、0.01秒毎にaddAncor()を呼び出します VisionProのフレームレートは90FPXの為、描画回数の限界を試すために約0.01を指定してみました DispatchQueue.main.asyncを用いることで、メインスレッドで行う必要のあるAddAnchorの呼び出しを、非同期にQuereとして追加しています removeAnchor関数 引数で受け取ったAnchorの削除処理を、同じく引数で受け取った秒数を経過後に実行します DispatchQueue.main.asyncAfterにより、非同期で削除処理を予約しています 4. VisionPro動作確認 実行すると、以下のような挙動になります。 ヘッドセットの動きに合わせて、 AnchorEntity(plane: .vertical) で壁面が自動的に検出され、3Dオブジェクトが配置されることを確認できました。また天井には描画がされないことからも、正しく垂直面を取得できていることが分かります。 終わりに 今回はSpatialComputingの入門編Part3として、現実空間の平面に3Dオブジェクトを配置しました。 今回はシンプルなWorldTrackingを利用しましたが、ARKitを活用することでより詳細な空間の認知やハンドト ラッキング を用いることも可能です。 SwiftUI, RealityKit, ARKitといった馴染みのある開発 フレームワーク を利用することで、驚くほど簡単にVisionProおよびvisionOSのパワフルな機能にアクセス可能です。 今週の WWDC2024 でもvisionOS 2が発表されていますので、引き続き今後のvisionOSの進化にも注目していきたいと思います。 現在、 電通 総研は web3領域のグループ横断組織 を立ち上げ、web3および メタバース 領域のR&Dを行っております(カテゴリー「3DCG」の記事は こちら )。 もし本領域にご興味のある方や、一緒にチャレンジしていきたい方は、ぜひお気軽にご連絡ください! 私たちは一緒に働いてくれる仲間を募集しています! 電通総研の採用ページ 参考文献 Meet ARKit for spatial computing Tracking specific points in world space WorldAnchor 執筆: @yamashita.yuki 、レビュー: @takami.yusuke ( Shodo で執筆されました )
こんにちは、 電通 総研金融ソリューション事業部の岡崎です。 前回の記事 では群衆の服の色をランダムにする手順まで説明しました。 この記事は前回の続きからになりますので、まだご覧になっていない方は、前編も是非ご一読ください! 検証環境/ツール OS: Windows 11 pro GPU : NVIDIA GeForce RTX 4070 Ti Game Engine: Unreal Engine 5.2.1 実装手順 群衆で使用する人型のメッシュの用意 人型のメッシュにアニメーションを付与する 群衆のアセットをPCGで配置する 群衆の服の色をランダムにする 群衆の髪型をランダムにする 群衆のアニメーションをランダムにする 群衆の性別をランダムにする 5. 群衆の髪型をランダムにする 後編では初めにMetaHumanのアセットに髪を生やしていきます。 「BP_Cheering_metamale」を開き、 コンポーネント 追加ボタンから「StaticMesh」を追加して「hair1」と 命名 します。 MetaHumanCreatorからダウンロードした際に、髪の毛のメッシュも入っているのでそれを使っていきます。 群衆という事で負荷を考えて、できるだけ軽いLOD7で使うための髪型を使いました。 「hair1」のスタティックメッシュのセレクトボックスで 「コンテンツ>Crowd>Character>Male>m_001>Hair>Helmets」の中にある「Hair_S_AfroFade_Helmet_LOD7」を選択します。 このままでは追加した髪の毛と、人間の本体が連動していないので、ソケットというものを使って連動していきます。 「hair1」を選択した状態で、詳細タブにあるソケットの欄から親ソケットを「HeadTop_End」に変更します。 これはスケルタルメッシュのボーンの名前で、頭の頂点にある点と髪の毛を連動させるために変更しました。 次に髪の毛の位置を変更して頭にの位置に合わせます。 コンパイル と保存をする事で、ビューポート上のMetaHumanに髪の毛を生やす事が出来ました。 ここから髪型をランダムにするために、他の髪型も追加していきます。 「BP_Cheering_metamale」を開きます。 一旦「hair1」の「Visible」のチェックを外し、 レンダリング されない様に変更します。 次に「hair1」の時と同じ要領で「hair2」、「hair3」を作成します。 髪型は「コンテンツ>Crowd>Character>Male」内に入っているスタティックメッシュを自由に使います。 「hair2」、「hair3」も同様に「Visible」のチェックを外し、 レンダリング されない様に変更しておきます。 イベントグラフに移動し、ランダムに髪型を レンダリング する処理を作っていきます。 先ほどまで作っていたノードに追加して、「整数型でスイッチ」ノードを追加します。 「Selection」のピンには0から2までの整数がランダムで生成される「RandomIntegerInRange」ノードをつなげます。 「0」のピンに「SetVisibility」ノードをつなぎ、ターゲットを「Hair1」とします。 同様に「hair2」、「hair3」を レンダリング する用のノードも作成します。 これにより、髪型もランダムに生成される様になりました。 現段階では、アニメーションが全て同じものを使用しており、再生のタイミングも一緒なので 次の工程ではアニメーションを複数用意して、タイミングもずらす処理を行っていきます。 6. 群衆のアニメーションをランダムにする まずはアニメーションの開始タイミングをずらす処理を作成します。 先ほど「BP_Cheering_metamale」に作成したノードにつづけて「Delay」のノードをつなぎます。 「Duration」ピンには「RandomFloatInRange」のノードをつなげて、0~3の間でランダムにFloat型の数値の秒数待機させます。 これにつなげて「PlayAnimation」ノードを作成して、ターゲットを「SkeletalMesh」にして、「NewAnimToPlay」のセレクトボックスで「Cheering 1 Anim_mixamo_com」を選択します。 これによりアニメーションの開始タイミングをずらす事ができます。 今作成したノードの一覧がこちらになります。 次に違うアニメーションを複数用意していきます。 Mixamo に移動して、使用したいアニメーションを検索します。 ダウンロードが完了したら、今まで行ってきたことと同じ手順で新規のブループリントアクターを作成します。 今回私は「 HipHop 」というアニメーションを使って「BP_ hiphop _metamale」を作成しました。 まだこの新規アクターはPCGグラフ上では使用していないので、もちろんビューポートには出てきません。 PCGグラフを編集していきます。 先ほど作成した「SpawnActor」の接続を一度解除して、代わりに「TransformPoints」ノードをつなぎます。 ここでは作成するPCGコンテンツの大きさや向きなどを調整できます。 私は「ScaleMin/Max」で90%~110%の大きさにランダム性を持たせて生成させています。 また、向きに関しても前方に対して70~120度のランダム性を持たせています。 次に「DensityFilter」ノードを追加します。 「Lower/Upper Bound」の値を編集することで、PCGコンテンツをフィルターする事ができます。 具体的には「LowerBound」と「UpperBound」に「0」と「0.25」と入力すると、 PCGコンテンツ内のBound値が0~0.25までの間のコンテンツにのみ干渉する事ができます。 デバッグ モードで見るとボックスの色の濃い黒いものだけをフィルターする事ができます。 (Bound値でフィルターする前) (Bound値でフィルターした後) 「DensityFilter」から「SpawnActor」に接続する事で、 デバッグ ボックスの黒い位置だけに人間を生成できます。 次に、同様に「Lower/Upper Bound」の値を「0.26」と「0.5」に設定した「DensityFilter」ノードを追加します。 デバッグ モードで見ると濃いグレーのボックスに干渉できる事が確認できます。 この「DensityFilter」にも「SpawnActor」を接続し、新規で作成した「BP_ hiphop _metamale」をセットします。 これにより、PCGの黒いボックスが表示された場所は「BP_Cheering_metamale」を表示して 濃いグレーのボックスが表示された場所には「BP_ hiphop _metamale」が表示できる様になります。 同様に他のアニメーションのブループリントアクターを作成する事で、3つのアニメーションがランダムで配置されたPCGを作成する事ができます。 7. 群衆の性別をランダムにする 最後に群衆の性別をランダムにするために女性のアセットを追加していきます。 とはいえ、ここでは今まで紹介してきた内容を繰り返し行うだけの内容になるので、詳細な説明は割愛いたします。 まず「MetaHumanCreator」を使用して女性を作成し、「Quixel Bridge」からプロジェクトへ追加します。 MetaHumanをスタティックメッシュ化してMixamoを使いアニメーションを追加する事で、 任意のブループリントアクターを作成し、PCGに追加する事ができます。 「DensityFilter」の数を増やすことによってアクターを好きな種類だけ増やすことができます。 おわりに 今回はPCGを使ってアニメーションのあるキャ ラク ターをランダムに生成する方法をご紹介しました。 スケルタルメッシュにルールを作成して、いろいろなアニメーションをつける事ができるので、いろいろな場面で応用できそうな技術です。 しかし、木などのスタティックメッシュを配置するよりも、アクターを配置する方がCPU, GPU ともに負荷がかかるので 大量に配置する際は負荷対策もしっかりと学ばないといけないと感じました。 記事をご覧いただきまして、ありがとうございました! 現在、 電通 総研は web3領域のグループ横断組織 を立ち上げ、Web3および メタバース 領域のR&Dを行っております(カテゴリー「3DCG」の記事は こちら )。 もし本領域にご興味のある方や、一緒にチャレンジしていきたい方は、ぜひお気軽にご連絡ください! 私たちと同じチームで働いてくれる仲間を、是非お待ちしております! 電通総研採用ページ 参考 https://mozpaca.hatenablog.com/entry/20170816/1502873390 https://www.youtube.com/watch?v=ZXh6oesGTGg https://gam0022.net/blog/2024/01/01/ue5-pcg-introduction-tips/ https://www.youtube.com/watch?v=PwEDb84sxi8 執筆: @okazaki.wataru 、レビュー: @yamashita.tsuyoshi ( Shodo で執筆されました )
電通 総研 X(クロス) イノベーション 本部 の三浦です。 現在開催されている AWS re:Inforce 2024 の Keynote にて、 AWS IAMのrootユーザーおよびIAMユーザーのMFA(多要素認証)としてPasskeyのサポートが発表されました。 以下、公式文書です。 AWS adds passkey multi-factor authentication (MFA) for root and IAM users AWS Identity and Access Management now supports passkey as a second authentication factor 概要 本アップデートで、IAMユーザーのMFAデ バイス としてPasskeyが利用できるようになりました。 わずらわしいTOTPともおさらばです。6桁の数字を残り時間を見ながら入れるの結構ストレスだったのではないでしょうか? ということで設定 では、サクサク試していきましょう。 『MFAデ バイス の割り当て』を選び MFAデ バイス を選択で、「パスキー、または、セキュリ ティー キー」を選択 で、下記のようなダイアログが私の環境では出てきたので(環境によって動作が異なります)、 ここでは スマートフォン 、 タブレット を選択 そうすると、下記のようなバーコードが出てきてカメラで読み込ませるだけで パスキーが登録されました。 という感じで設定が終わりました。とても簡単ですね。 残課題 Windows Helloのpin認証、顔認証で利用できるとさらに便利になるかと考えました。しかし、筆者の持っている端末では Windows Hello、または外部セキュリ ティー キー」を選択すると、セキュリ ティー キーの使用を強制されてしまいました。この辺は利便性、セキュリ ティー 強度に大きく関係しますので何が最適かよく検討する必要がありそうです。 使いどころ 社としては、認証をIDP( アイデンティティ プロバイダー)に依存させるべきですが、どうしてもその標準から外れる部分が出てきます。筆者は全社レベルで管理者業務を行っており、IDPのトラブル時にも対応できる必要があります。しかし、これをTOTP(タイムベース ワンタイムパスワード )で管理していると、数百ものTOTPエントリが必要になり、非常に煩雑に感じていました。 が、Passkeyを使うことにより、モバイルや Windows Helloで簡易に認証を集約できるメリットが得られます。 私たちは一緒に働いてくれる仲間を募集しています! クラウドアーキテクト 執筆: @miura.toshihiko 、レビュー: @akutsu.masahiro ( Shodo で執筆されました )
こんにちは、 電通 総研金融ソリューション事業部の岡崎です。 今回はUE5でPCGを使用した群衆を作成します。 作成する群衆は、ライブ会場などに配置するためのアセットとして使用する想定ですので、 後ろから見た姿の、服の色や髪、背丈や性別、動きのアニメーションなどをランダムになるように作成します。 また、今回は後ろ姿のみが映る予定ですので、顔の違いは考慮せずに作成していきます。 本記事は前編後編に分かれており、この記事では手順4の「群衆の服の色をランダムにする」までをご紹介します。 検証環境/ツール OS: Windows 11 pro GPU : NVIDIA GeForce RTX 4070 Ti Game Engine: Unreal Engine 5.2.1 実装手順 群衆で使用する人型のメッシュの用意 人型のメッシュにアニメーションを付与する 群衆のアセットをPCGで配置する 群衆の服の色をランダムにする 群衆の髪型をランダムにする 群衆のアニメーションをランダムにする 群衆の性別をランダムにする 1. 群衆で使用する人型のメッシュの用意 まずは群衆で使う人型のアセットを用意します。 今回は、EPICが提供しているデジタルヒューマンの MetaHuman を使用します。 別記事ではありますが、写真からMetahumanを生成する方法については 松崎さんの記事 が参考になるので、ぜひご覧ください。 まずはUnrealEngineのコンテンツ追加タブから「Quixel Bridge」を選択します。 その後出てくるウィンドウからMetaHumanCreatorで作成した任意のMetaHumanを選択し、「Download」ボタンを押下します。 ダウンロードが完了したらMetaHumanをプロジェクトに追加するために「Add」ボタンを押下します。 コンテンツブラウザ内の「コンテンツ>MetaMumans>meta_male(作成したMetaHumanの名前)」にファイルが追加されます。 追加された「BP_meta_male」を アウトライナー に配置する事で、MetaHumanが正常にインポートできた事を確認します。 追加されたMetaHumanはスケルタルメッシュを使用しているのですが、 大量に配置した際のパフォーマンス向上の為スタティックメッシュに変換して使用したいので変換作業を行います。 画面上部のアクタタブから「スタティックメッシュへ変換」を押下します。 追加したい場所を選び、保存を押すとスタティックメッシュが生成されます。 髪の毛は変換元のスケルタルメッシュには含まれていないため、スタティックメッシュに変換した際に消えてしまいますが、 後々追加するので問題はありません。 さらに作成したスタティックメッシュのファイルを右クリックして「アセットアクション」から「エクスポート」を選択してFBXファイルを作成します。 次の工程で、作成したスタティックメッシュのFBXファイルにアニメーションをつけていきます。 2. 人型のメッシュにアニメーションを付与する ここではMetaHumanを元にしたスタティックメッシュに群衆のアニメーションをつけていきます。 アニメーションを作成するにあたり Adobe 社が提供している Mixamo というサービスを利用します。 Mixamo は、二足歩行のアセットに対して、自動でス ケルト ンを作成して、 用意してある、歩行やダンスなど、さまざまなアニメーションをアセットに付与する事ができるサービスです。 Mixamoを開いたら、まずFBXファイルをアップロードします。 画面右側の「Upload character」を選択し、FBXファイルを選びます。 キャプチャのように、作成したMetaHumanのスタティックメッシュが表示されるので「Next」を押します。 次にアニメーションで使用するためのリグ付けを行っていきます。 キャプチャのように右側のサンプルを参考に、顎や腕、膝などの場所を登録します。 完了して生成されるまで少し待つと、リグのついたMetaHumanが完成します。 次に群衆に使いたいアニメーションを検索します。 私は今回、ライブ会場で盛り上がる人を作りたかったので「Cheering」のアニメーションを使いました。 選択したら、右側の「Download」ボタンを押します。 「Skin」を「With Skin」にしてダウンロードします。 ダウンロードしたファイルをUE上にインポートする事で、アニメーションに対応したスケルタルメッシュを使用する事ができます。 アウトライナー に配置する事でMetaHumanがCheeringのアニメーションをしているのが確認できます。 今回は、アニメーション毎に、それぞれ異なるスケルタルメッシュを用いてアニメーションを作成します。 ちなみにアニメーションを付与する方法として、 共通のスケルタルメッシュに対してアニメーションシーケンスを複数切り替える方法もありますが、 こちらは後日別の記事で紹介する予定です。 (Mixamoからアニメーションをダウンロードして、UE上でスケルタルメッシュにリターゲティングを行い、アニメーションを再現する) 次の手順で、現在作成したアニメ付きのMetaHumanをPCGを利用して複数配置し、群衆にしていきます。 3. 群衆のアセットをPCGで配置する まずは以前の私の PCGの記事 を参考にPCGを作成していきます。 PCG作成の詳しい方法は本記事では割愛するので、 PCGの記事 を参考にしてください。 「PCGVolume」と「PCGグラフ」を作成します。 「PCGグラフ」を開き、「Input」の「Landscape」から「SurfaceSampler」をつなぎます。 群衆を作成するにあたり、大体の人数のあたりをここでつけておきます。 次に群衆を配置するために「SpawnActor」ノードを作成します。 前回のこちらの記事 では「StaticMeshSpawner」を使用しましたが、 アニメーションをさせるために必要なスケルタルメッシュアセットは、「StaticMeshSpawner」では配置できないので、「SpawnActor」ノードを使用します。 本来はスケルタルメッシュスポナーのようなものを使いたいですが、そのようなノードはないので「SpawnActor」を使用します。まずは「SpawnActor」で使用するブループリントアクターを作成します。 Mixamoで出力されたアセットはスケルタルメッシュのアセットなのでまずはこれをブループリントアクター上に配置します。 コンテンツブラウザ上でブループリントアクターを「BP_Cheering_metamale」という名前で生成します。 コンポーネント 追加ボタンから「SkeletalMesh」を選択して追加します。 追加したら右側の詳細パネルの「メッシュ>SkeletalMeshAsset」のセレクトボックスから、先ほどMixamoで作成したUE上にインポートしてきたスケルタルメッシュのファイル「Cheering_1」を選択します。 さらに詳細パネルの「Animation>AnimationMode」の項目を「アニメーションアセット」にセットします。 アニメーションアセットを選択するタブが出てくるので同時にインポートしてきた「Cheering_1_Anim_mixamo_com」を選択します。 これにより、MetaHumanが手を挙げているアニメが再生されているブループリントアクターが出来ました。 PCGグラフに戻り、「SpawnActor」の「TemplateActorClass」から現在作成したブループリントアクター「BP_Cheering_metamale」を選択します。 また「Option」を「MergePCGOnly」を選びます。 この設定は後述する「ConstructionScript」を使用する際の条件になります。 これで アウトライナー 上にPCGで作成した群衆が配置できます。 それでは次の工程で、配置した群衆の服の色をランダムにしていきます。 4. 群衆の服の色をランダムにする まずは服の色をランダムにするためのマテリアルを作成します。 コンテンツブラウザでマテリアルを作成します。 私は「M_RandomColor」と 命名 しました。 作成したらベー スカラー のピンに「VectorParameter」を追加して「basecolor」と 命名 します。 パラメーターは全てゼロで黒のままで大丈夫です。 私の場合は服の素材を表すために「ラフネス」のピンに「10」の値を接続して反射をなくしています。 次にMixamoから持ってきたスケルタルメッシュのファイル「Cheering__1」を選択します。 編集画面上で服のマテリアルが入っているエレメント2の「M_Lights_Shorts」のマテリアルを 今作成した「M_RandomColor」に変更します。 ツヤの消えた黒い服を着ていることを確認します。 次に「BP_Cheering_metamale」を開き、「Construction Script」タブを開きます。 「CreateDynamicMaterialInstance」ノードを作成してターゲットを「SkeletalMesh」にします。 「Construction Script」に書いた処理は、ゲーム中には実行されずに、レベルにそのブループリントを配置、変更した時点で処理が実行されます。 また「Element index」の値を「2」にします。 (これは「M_Lights_Shorts」のマテリアルがエレメント2に格納されているため) 次に「ReturnValue」を変数に昇格させ、変数名を「DMI_random」とします。 「BP_Cheering_metamale」の「イベントグラフ」に戻ります。 「BeginPlay」の時にシャツのマテリアルの値を変更するために「SetVectorParameterValue」ノードをつなぎます。 ターゲットは先ほど作成した「DMI_random」にして、「ParameterName」は、マテリアル作成時に 命名 した「basecolor」と書きます。 次に「MakeColor」ノードを作成し、「RGB」にそれぞれ「RandomFloat」の値を接続します。 (「A」はデフォルトのまま「1.0」にしておきます) これでイベントが起こるたびに服の色のマテリアルの「RGB」の値が変わり、服の色がランダムに変更される様になります。 ビューポートに戻ると服の色がランダムになっているのが確認できます。 おわりに 前編では、MetaHumanをPCGで配置する方法や、mixamoの使用方法、マテリアルの変更方法などを紹介しました。 今回は要件的にmixamoから出力したスケルタルメッシュとアニメーションをそのまま使いましたが、 アニメーションだけを書き出して既存のキャ ラク ターの動きを上書きするためのリターゲティング処理などもあります。 そちらも今後使う機会がたくさんありそうなので勉強をしてみたいと思います。 後編では、髪型や性別、アニメーションをランダムに振り分ける方法などもご紹介しています。 近日中に公開するので、ぜひそちらもご覧ください! 現在、 電通 総研は web3領域のグループ横断組織 を立ち上げ、Web3および メタバース 領域のR&Dを行っております(カテゴリー「3DCG」の記事は こちら )。 もし本領域にご興味のある方や、一緒にチャレンジしていきたい方は、ぜひお気軽にご連絡ください! 私たちと同じチームで働いてくれる仲間を、是非お待ちしております! 電通総研採用ページ 参考 https://mozpaca.hatenablog.com/entry/20170816/1502873390 https://www.youtube.com/watch?v=ZXh6oesGTGg https://gam0022.net/blog/2024/01/01/ue5-pcg-introduction-tips/ https://www.youtube.com/watch?v=PwEDb84sxi8 執筆: @okazaki.wataru 、レビュー: @handa.kenta ( Shodo で執筆されました )
こんにちは。コミュニケーションIT事業部 ITソリューション部の英です。 普段はWebアプリや スマホ アプリの案件などを担当しています。あと、趣味で AI を勉強しています。 今回は IoTデ バイス (温度センサー)の異常検知 をテーマに記事を書いてみます。 温度センサーから Amazon Kinesis Data Streamsにデータが送信され、送信されたデータに対して Amazon Managed Service for Apache Flinkでリアルタイムにデータを処理します。 今回は 閾値 で異常を検知し、 SNS に通知するシンプルな仕組みにしています。 Amazon Kinesis Data Streams(KDS)とは Amazon Kinesis Data Streams(KDS)は、リアルタイムで大量のデータを収集、処理、および分析するためのサービスです。データのストリーミング処理を簡単に行うことができます。 高いスケーラビリティ:必要に応じて スループット を調整可能で、大規模なデータ処理にも対応します。 データの分割:データを複数のシャードに分けることで、並列処理が可能です。 データ保持:デフォルトで24時間、最大8760 時間(365日)までデータを保持できます。 Amazon Managed Service for Apache Flinkとは? Apache Flink は、リアルタイムでデータを処理・分析するためのソリューションです。 マネージドサービスなのでインフラの管理が不要で、ユーザーはコードの記述に集中できます。 Kinesis Data Streams、 Kinesis Data Firehose、 Amazon S3 などの AWS サービスと簡単に統合できます。 ここから本題 STEP1:データストリームの作成 Kinesis Data Streamsを選択した状態で「データストリームの作成」を押下します。 以下のように設定します。今回はシャード分割なしとします。 STEP2:通知用の SNS を作成する Amazon SNS のトピックを作成し、Eメールでサブスクライブしておきます。 とくに難しい手順はないのでこの記事では解説しません。 STEP3:Cloud9に Kinesis への権限を与える Cloud9から先ほどのデータストリームに送信できるように権限を設定します。 新規のIAMポリシーを作成して、新規ロールにアタッチしましょう。 Cloud9用のデフォル トロール にポリシーをアタッチすることはおすすめしません。 特定のデータストリームにだけ権限を与えればよいのでARNを指定しましょう。 その後、Cloud9が稼働しているEC2のIAMロールを切り替えます。 STEP4:テストデータの送信 Cloud9を立ち上げ、以下の スクリプト を実行します。 1秒ごとに温度データが正弦波で連携され、0.5%の確率で異常データが生成されます。 以下のようにデータが流れ始めます。 正常なデータ:20~30℃の範囲内 異常なデータ:35~50℃の範囲内 送信されたデータは Kinesis のデータビューワーでも確認できます。 STEP5: Apache Flinkのノートブックを作成 データ分析タブからノートブックを新規作成できます。 ボタン一つでGlueのDBも自動生成されます。 作成が完了すると「Open in Apache Zeppelin」からノートブックを開くことができます。 STEP6:サンプル SQL の確認 初期状態から全文SELECTの SQL が作成されています。 connectorに kinesis を指定し、rawデータを取得しています。 STEP7:新規ノートブックを作成する(カスタマイズ用) Managed Apache Flinkの画面で「Studio ノートブックを作成」を押下します。 先ほど自動生成されたDBを流用します。 ノートブックに紐づいているIAMロールに以下の権限を追加します。 Kinesis Data Streamsの読み書き Glueの読み書き ※すでにGlueを利用している場合はリソースを指定してください SNS の発行 { "Effect": "Allow", "Action": [ "sns:Publish" ], "Resource": "arn:aws:sns:ap-northeast-1:(アカウントID):(トピック名)" }, { "Effect": "Allow", "Action": [ "kinesis:ListShards", "kinesis:DescribeStream", "kinesis:GetRecords", "kinesis:GetShardIterator", "kinesis:PutRecord", "kinesis:PutRecords" ], "Resource": "arn:aws:kinesis:ap-northeast-1:(アカウントID):stream/TemperatureStream" }, { "Effect": "Allow", "Action": [ "glue:GetPartitions", "glue:GetTable", "glue:GetTables", "glue:GetDatabase", "glue:GetDatabases", "glue:DeleteTable", "glue:CreateTable" ], "Resource": [ "arn:aws:glue:ap-northeast-1:(アカウントID):catalog", "arn:aws:glue:ap-northeast-1:(アカウントID):database/*", "arn:aws:glue:ap-northeast-1:(アカウントID):table/*" ] } STEP8: SQL を定義していく まずは、ストリームデータが取得できるか、デフォルトの SQL を投げてみます。 Cloud9からダミーデータを流してみると、 JSON 形式でrawデータが返ってきました。 次に JSON 内の要素を分割してみます。 temperatureとtimestampの2つに分割することができました。 STEP10:異常検知のジョブを定義する 以下をノートブックに貼り付けて実行する。 <>内は環境に合わせて書き換えてください。 %flink.pyflink import boto3 from pyflink.table import TableEnvironment, EnvironmentSettings, DataTypes from pyflink.table.udf import udf from pyflink.datastream import StreamExecutionEnvironment from pyflink.table import StreamTableEnvironment # ストリーミング実行環境とテーブル環境の設定 exec_env = StreamExecutionEnvironment.get_execution_environment() exec_env.set_parallelism(1) t_env = StreamTableEnvironment.create(exec_env, environment_settings=EnvironmentSettings.new_instance().in_streaming_mode().build()) # データソースの作成 t_env.execute_sql(""" CREATE TABLE raw_single_column_table ( single_column_output STRING ) WITH ( 'connector' = 'kinesis', 'stream' = '<ストリーム名>', 'aws.region' = '<リージョン名>', 'scan.stream.initpos' = 'LATEST', 'format' = 'raw' ) """) # データ解析ビューの作成 t_env.execute_sql(""" CREATE VIEW parsed_data AS SELECT CAST(JSON_VALUE(single_column_output, '$.temperature') AS DOUBLE) AS temperature, TO_TIMESTAMP(CAST(JSON_VALUE(single_column_output, '$.timestamp') AS STRING), 'yyyy-MM-dd''T''HH:mm:ss.SSSSSS''Z''') AS `timestamp` FROM raw_single_column_table """) # UDF(ユーザー定義関数)を作成して、SNS通知を送信 @udf(result_type=DataTypes.BOOLEAN()) def notify_sns(temperature, timestamp): if temperature >= 30: sns_client = boto3.client('sns', region_name='ap-northeast-1') message = f"Temperature alert: {temperature}℃ at {timestamp}" sns_client.publish( TopicArn='arn:aws:sns:ap-northeast-1:<アカウントID>:<トピック名>', Message=message ) print(message) return True return False # UDFをテーブル環境に登録 t_env.create_temporary_function("notify_sns", notify_sns) # 結果を出力するテーブルを作成 t_env.execute_sql(""" CREATE TABLE alert_table ( temperature DOUBLE, `timestamp` TIMESTAMP(3), notified BOOLEAN ) WITH ( 'connector' = 'print' ) """) # 温度が30℃以上のデータをフィルタリングし、通知を送信するSQLクエリを実行 result_table = t_env.sql_query(""" SELECT temperature, `timestamp`, notify_sns(temperature, `timestamp`) AS notified FROM parsed_data WHERE temperature >= 30 """) # 結果をalert_tableに保存 result_table.execute_insert("alert_table") # ストリーミングジョブの実行 exec_env.execute("Temperature Alert Job") ジョブの実行状況はノートブックの右上の「FLINK JOB」を押下することで確認できます。 ログやチェックポイントなど詳細なジョブの実行状況を監視できます。 Cloud9からダミーデータを流すと、Eメールで通知されることが確認できました。 温度センサーのデータが30℃以上ならばメールで通知される仕組みです。↓は45.61℃。 まとめ IoTデ バイス の温度センサーから送信されるデータをリアルタイムで処理し、異常を検知するシステムの構築方法を紹介しました。 今回はメールで通知するだけでしたが、 AWS の様々なサービスと統合できるので、業務シナリオに合わせて柔軟に組み替えてリアルタイム監視システムを構築していきましょう。 また今回は 閾値 で異常を検知しましたが、時系列データの異常検知にはRCF(ランダムカットフォレスト)を用いることが一般的になっています。 これはまた別の記事で紹介しようと思います。 ↓ のスターを押していただけると嬉しいです。励みになります。 最後まで読んでいただき、ありがとうございました。 私たちは一緒に働いてくれる仲間を募集しています! <電通×IT>電通グループ基幹システムプロジェクトマネージャー <電通×IT>顧客DX案件プロジェクトマネージャー <電通×IT>クラウドアーキテクト <電通×IT>アプリケーションアーキテクト 執筆: 英 良治 (@hanabusa.ryoji) 、レビュー: Ishizawa Kento (@kent) ( Shodo で執筆されました )
こんにちは、 電通 総研金融ソリューション事業部の岡崎です。 今回はUE5.2の機能であるプロシージャルコンテンツ生成 フレームワーク (PCG:Procedural Content Generation Framework)に関して、 前回の記事 に引き続き、より詳しい機能の紹介を行っていきます。 今回はPCGのスプラインという機能を使用して、PCGで草を配置し、さらに道を生成する方法を紹介します。 検証環境/ツール OS: Windows 11 pro GPU : NVIDIA GeForce RTX 4070 Ti Game Engine: Unreal Engine 5.2.1 実装手順 PCGを使用して草を配置 スプラインを作成し、レベル上に配置 PCGグラフを変更してスプライン上の草を排除 スプラインで任意の範囲を指定して草を配置 1. PCGを使用して森を生成 前回の記事 と同様、まずはPCGを使用して森を生成します。 今回は簡潔な手順のみ説明します。詳細な説明は前回の記事を参考にしてください。 「PCGVolume」を作成し、レベル上のViewPort上に配置します。 次に「PCG グラフ」を作成します。 今回、PDGグラフ名は「Tech1PCGGraph」と 命名 しました。 アウトライナー 上で先ほど範囲を決めるために作成した「PCGVolume」を選択し、子要素の「PCGComponent」を選択します。 インスタンス 直下に Graph を選択する欄があるので、先ほど作成した「PCG グラフ」を選択します。 次に森を作成するためのノードを作成します。 PCGグラフを開き、「Input」の「Landscape」から「SurfaceSampler」ノードを作成して繋ぎます。 さらに「TransformPoints」を接続し、地上に生やしていく草の大きさや向きなどを指定します。 PCGグラフを編集した際に、リアルタイムにViewportに変更が入るので、 同時に変更点を見る事ができるよう、下記キャプチャのようにエディターを配置をして編集をする方法もおすすめです。 次に「StaticMeshSpawner」ノードを作成し、草のStatic Meshを配置します。 「Descriptor > Static Mesh」の順に開き、配置したい草のアセットを選択します。 ここまででPCGを使って任意のアセットをランダムに配置する事ができました。 より詳しいPCGの作成方法は、 前回の記事 に記載しています。 次は、現在作成した草原に道を作成して、道の上には草を生やさないルールを作成します。 2. スプラインを作成し、レベル上に配置 ここでは道を作成するためにスプラインを使用します。 UEのスプラインとは、ポイントとポイントをラインで繋いだ形状のアクターのことを指します。 まずはブループリントアクターを作成し、「BP_PathSpline」と 命名 します。 「BP_PathSpline」を開き、左上のAddボタンから「Spline」を追加します。 レベル上でスプラインを見やすくするために「ScaleVisualizationWidth」を300に設定します。 スプラインも少し伸ばします。 次に画面上部の「クラスのデフォルト」を押下し、詳細パネルから「タグ」を検索し、プラスボタンからインデックスを「Path」と 命名 します。 コンパイル とセーブをして、「BP_Path」をViewPortに配置します。 作りたい道の向きに合わせて「Alt」キーを押しながら頂点をドラッグしてPathを伸ばします。 (スプラインが見えづらかったので、草を一度非表示に変更しています) PCGで配置した草の上に道にしたいスプラインを配置できたら、PCGグラフに戻ります。 3. PCGグラフを変更してスプライン上の草を排除 先ほどのPCGグラフ「Tech1PCGGraph」を開いて、PCGグラフを編集します。 「GetSplineData」ノードを作成し、詳細パネルの「ActorSelectionTag」を先ほど設定した「Path」にします。 また、スプラインを複数配置した時にも使用したいので「SelectMultiple」にもチェックを入れておきます。 次に「SplineSampler」ノードを作成し、「Dimension」のセレクトボックスから「OnSpline」を選択します。 次に「BoundsModifier」ノードを作成し、「SplineSampler」のアウトプットに繋ぎます。 詳細パネルから「BoundsMin / Max」のY/Zを300にして デバッグ モード(ノード上で「D」キー押下)にするとキャプチャのように、スプラインの範囲が白い棒状に表示されます。 次に、この棒状のスプラインと重なっている草を排除するために「Difference」ノードを作成します。 草を生やしている「 Surface Sampler」ノードのアウトプットから「Difference」の「Source」に繋ぎ、 「BoundsModifier」のアウトプットを「Difference」の「Differences」に接続します。 キャプチャのようにノードをつなげる事で、 スプライン上の道に草のメッシュが排除された形で、ランダム生成された形状を作成する事ができます。 4. スプラインで任意の範囲を指定して草を配置 ここでは、スプラインを使用した応用として、スプラインで任意の範囲を指定して草を配置する方法を紹介します。 この記事では、草の生成を「 Surface Sampler」を使用して行いましたが、 スプラインを使用して、任意の形の内部にのみ、草を生やすことなどができます。 まずは草を生やす形を決めるために新しいブループリントアクターを「BP_LandSpline」で作成します。 「BP_PathSpline」の時と同様に、スプラインを作成し、キャプチャのように中心点以外の頂点を削除します。 残った頂点を選択し、右クリックで「スプライン生成パネル」を選択し、サークルを作成します。 頂点の数は6にして、半径を2000ほどにしておきます。 選択されている頂点をキャプチャのように上に移動して削除して、途切れた円形にします。 その後、詳細パネルの「ClosedLoop」にチェックを入れて閉じた丸いスプラインに変更します。 完成したら「BP_PathSpline」と同様にタグに「Land」と 命名 して作成します。 作成した円状のスプラインをViewPortに配置します。 これから配置したスプラインの内部のみ草を生やします。 PCGグラフに戻ります。 「GetSplineData」と、「SplineSampler」を作成し、ノードをつなぎます。 「GetSplineData」はPathの時と同様に、Tagに「Land」を設定します。 「SplineSampler」の「Dimension」のセレクトボックスから「OnInterior」を選択します。 これで円の内側にPCGキューブが配置されました。 「InteriorSampleSpacing」の値を200に、「InteriorBorderSampleSpacing」の値を50程度に調整してPCGキューブの表示間隔を調整します。 また、キャプチャのように大きく隙間が空いている時は、「TreatSplineAsPolyline」にチェックを入れることで均等に配置されます。 続いて、PCGキューブの向きや配置をランダムにするために「TransformPoints」を追加します。 「Projection」のアウトプットから「TransformPoints」に接続して、「RotationMax」のZ値を360に変更し 「Offset Min/Max」のX/Yの値を-200と200に変更し、キューブの重なりなどを排除するために「SelfPruning」ノードを追加します。 当初使用していた「SurfaceSampler」を使用してメッシュを配置していた方法と、今回の「SplineSampler」を使用したメッシュを入れ替える事で、 Landのスプライン上に生成した草から、Pathのスプラインで生成した道上の草を排除する事もできます。 さらに、LandのスプラインやPathのスプラインはコピー生成する事もできるので、下記キャプチャのように草や道を自由に配置する事ができるようになります。 (円形のLandのスプラインを2つに複製したパターン) (円形のLandのスプラインを変形して複製し、Pathのスプラインも複製して配置したパターン) 今回の機能の紹介はこれで以上になります。 おわりに 今回は、PCGで使用できるスプラインを利用して、より複雑なルールの作成方法を紹介しました。 どのような仕様でメッシュを配置したいかを、PCGグラフ上でノードに落とし込む方法を調査するのは時間がかかりますが、一度ノードを作成できれば複製や編集が容易になるため、UEで何かを制作する際にはとても有効な方法だと改めて感じました。 また、スプラインを使用したスタティックメッシュの配置では、スプラインの形や大きさ、数によってさまざまな地形を作ることができそうです。 ひきつづき、PCGに関する知識を学習していきたいと思います。 現在、 電通 総研は web3領域のグループ横断組織 を立ち上げ、Web3および メタバース 領域のR&Dを行っております(カテゴリー「3DCG」の記事は こちら )。 もし本領域にご興味のある方や、一緒にチャレンジしていきたい方は、ぜひお気軽にご連絡ください! 私たちと同じチームで働いてくれる仲間を、是非お待ちしております! 電通総研採用ページ 参考文献 https://historia.co.jp/archives/34360/ https://ue5exp0.com/pcg/ https://www.youtube.com/watch?v=KZHNyESAyw4 執筆: @okazaki.wataru 、レビュー: @miyazawa.hibiki ( Shodo で執筆されました )
みなさんこんにちは!  電通 総研 金融ソリューション事業部の松崎です。 今回から、 Unreal Editor for Fortnite(以下UEFN)の初学者向け紹介記事を執筆していきます。 本記事では UEFNってそもそも何なのか Fortniteのクリエイティブモードと何が違うのか Unreal Engine とはどう違うのか 具体的にどうやって導入・操作するのか といった部分を紹介します。 これからUEFNの導入を考えている方は、是非最後までチェックしてください。 目次 UEFN概要 UEFN vs Fortnite(クリエイティブモード) UEFN導入方法 基礎概念と操作方法 目次 1 UEFN概要 1.1 UEFNとは 1.2 UEFN最新情報(2024年4月現在) ① Metahuman to UEFN ② 外部ライセンスIP利用(レゴブランド) 1.3 UEFNの企業活用事例 ① NIKE 「Airphoria」 ② ソニー 「SHIBUYA MULTIVERSE」 2 UEFN vs Fortnite(クリエイティブモード) 2.1 外部アセット利用 2.2 UE機能 ①ランドスケープモード ②ポストプロセスボリューム ③レベルシーケンス 2.3 Verse 2.4 チームでの共同開発 3 UEFN導入方法 3.1 動作環境 3.2 インストール手順 4 基本概念と操作方法 4.1 プロジェクト ①チーム作成方法 ②プロジェクト作成方法 4.2 エディタ 4.3 アセット ①外部アセットのインポート方法 ②UEからUEFNへのアセット移行方法 ③FABマーケットプレイスからの購入方法 4.4 デバイス(仕掛け) ①島設定デバイスの設定方法 ②通常デバイスの設置方法 4.5 Verse おわりに 参考文献 1 UEFN概要 1.1 UEFNとは UEFN( Unreal Editor for Fortnite)とは、その名の通り「 Unreal Engine の機能を使用して、Fortnite上で動くゲームが作成できるツール」です。 この説明を見ると、 Fortnite上で動くゲームを作るってどういうこと? Unreal Engine の機能を使用する とは? という疑問が出てくると思いますので、これらを説明していきます。 Fortniteは Epic Games によって開発されたゲームです。 プレイ可能なプラットフォームは、 スマホ , PC, PlayStation , Nintendo Switch , Xbox と多岐に渡ります。 一般的にはバトルロワイヤルゲームとして有名ですが、他にも「クリエイティブモード」という、プレイヤーが独自にゲーム(マップ)を制作して公開できるモードがあります。 このクリエイティブモードでは、既に8万を超えるゲームが制作されています。(2024年現在) また、 Epic Games はクリエイティブモードでのゲーム制作者に対するエンゲージメント配当プログラム(作成したゲームが多く遊ばれると配当金を貰える仕組み)を実施しており、1年目にして総額3億2千万ドル以上配当されております。 Fortniteのユーザー数は5億人を越えており、Fortniteクリエイティブモードは「ユーザー数5億人のゲーム制作プラットフォーム」となっています。 ※配当プログラムの詳細は こちら をご参照ください クリエイティブモードでのゲーム制作はFortnite上で行うことが可能ですが、外部アセットの利用や細かいロジック設定ができないなど、様々な制限があります。 そこで登場した拡張ツールがUEFNです。UEFNは、本格的な ゲームエンジン である Unreal Engine (以下UE)の機能をクリエイティブモードのゲーム制作に使用できるようにしたツールです。 これにより、クリエイティブモード向けのリッチなゲーム制作が可能となりました。(具体的な拡張要素は2章で記載します) なお、UEの開発もFortniteと同様に Epic Games が行っております。最新版であるUE5では以下の特徴的な機能があり、これらを使用できるのもUEFNの強みとなります。 Lumen(リアルな光照射効果を提供するソリューション) Nanite(非常に詳細な視覚効果を実現する仮想 幾何学 技術) UEの一部機能に関しては 以前の記事 にて説明しておりますので、興味のある方はご参照ください。 1.2 UEFN最新情報(2024年4月現在) 2024年3月 20日 開催の「State of Unreal 」の基調講演において、 Epic Games から新機能紹介が行われました。その中から、UEFNに関する2つを紹介します。 ① Metahuman to UEFN MetahumanをUEFNにインポートし、Fortniteのあら ゆるキャラ クターへ適用できるようになりました。Metahumanとは、人間の顔を忠実度高く再現したデジタルヒューマンを作成できるソリューションです。 Metahumanの詳細や作成方法は 以前の記事 にて紹介しておりますので、興味のある方はご参照ください。 Metahuman Creator やMetahuman Animatorを活用し、自分の顔にそっくりなキャ ラク ター( NPC )を作成することもできます。( 作成方法の紹介記事 ) ② 外部ライセンスIP利用(レゴブランド) レゴブランドのテンプレート、小道具、消耗品、アイテムなどを使用できるようになりました。また、ミニフィギュアやレゴスタイルのコスチュームをFortniteのプレイヤーへ適用できるようになりました。 今後はレゴブランド以外にも「Rocket Racing」や「Fall Guys」のテンプレート・アセットが使えるようになり、 Epic Games はさまざまなライセンスIPがFortnite上で使用できる世界を目指している、とのことです。 今後も様々なライセンスIPアセットが利用できるようになりそうで、続報が楽しみですね。 1.3 UEFNの企業活用事例 企業におけるUEFN(Fortniteクリエイティブモード)活用事例を紹介します。 ① NIKE 「Airphoria」 Airphoriaは、 NIKE が Epic Games イノベーション ラボのサポートのもと、フォートナイトスタジオBeyondと共同制作したマップです。Airphoriaでは、 Air Maxをモチーフにした雲上都市でのスニーカーハントゲームが提供されていました。 ゲームを一定以上クリアするとFortnite上のバックアクセサリーが貰えたり、Airphoriaにインスピレーションを受けたコレクションが NIKE .com(北米地域)で発売されるなど、UEFNを活用した マーケティング の試みが展開されました。 ※日本では2023/6/28までの限定公開 ② ソニー 「SHIBUYA MULTIVERSE」 株式会社 ソニー・ピクチャーズ・エンタテインメント 配給の映画『 スパイダーマン :アクロス・ザ・スパイダーバース』の公開を記念し、 ソニー が Sony Esports Projectとして制作したマップです。 UEFNを活用して渋谷の街並みが再現されています。 またその後、SHIBUYA MULTIVERSEの上空でバトルが可能な「Sky Battle in Shibuya Multiverse」マップも公開されました。 最初に公開されたSHIBUYAマップを軸に、追加で様々なマップ(ゲーム)を展開する試みを行っています。 ※2024/7/30までの公開 (マップコードは こちら をご参照ください) 2 UEFN vs Fortnite(クリエイティブモード) 本章では、UEFNの登場によって拡張された要素を紹介します。 UEFNによって拡張された機能は、大きくまとめると以下になります。 外部アセット利用(3Dモデル・アニメーション・テクスチャのインポート等) UE機能( ランドスケープ 構築、レベルシーケンス等) Verse( プログラミング言語 ) チームでの共同開発( Unreal Revision Control) 上記に関して、次節以降で具体的に説明します。 2.1 外部アセット利用 Fortniteクリエイティブモードでは Epic Games 側が用意したアセットのみに利用が制限されていましたが、UEFNでは自分のアセットをインポートして利用できます。 ここでは、外部サイトで購入した3DモデルやBlederなどで自作したものを使うこともできます。 3DモデルはFBX,OBJ,glTF,GLB形式に対応しています。また、テクスチャ( PNG , JPEG , TIFF 形式など)やオーディオ(WAV,AIF,FALC, OGG 形式)のインポートにも対応しており、アセットの自由度が格段に高くなりました。 (画像:Fortniteクリエイティブモードで用意されているアセット一覧) (画像:UEFNで自作アセットをインポートした画面) 外部アセットのインポート方法や、UEからUEFN用にモデルをアウトプットする方法は4章にて紹介します。 2.2 UE機能 UEFNでは、UEの主要な機能をクリエイティブモード向けに利用できます。 UEFNで使用可能なUE機能は様々にありますが、ここでは目玉となる機能を紹介します。 ① ランドスケープ モード ランドスケープ モードでは、マップの土台となる島の地形を操作できます。 地形の特定部分を盛り上がらせて山のようにしたり、逆にへこませて谷のようにすることも可能です。 また、土や水の動きによる地面の浸食を再現させることもできます。 (画像:UEFNの ランドスケープ モード画面) ランドスケープ モードに関しては 以前の記事 にて紹介しておりますので、こちらもご参照ください。 その他、フォリッジモードで木などを簡単に敷き詰めることも可能です。 ②ポストプロセスボリューム ポストプロセスボリュームを使用することで、特定の領域内にポストプロセスを適用できます。 プレイヤーが特定の領域に入った際に色調や明るさを変えさせるなど、ゲームプレイにおける映像表現の幅が広がりました。 (画像:配置したポストプロセスボリューム) (画像:ポストプロセスボリューム内でのホワイトバランス変化) ③レベルシーケンス UEFNでは、レベルシーケンスを使用してゲーム内のシネマティクス(リアルタイムで レンダリング される動画)を作成できます。UEと同様、レベル シーケンサー 上にオブジェクト・キャ ラク ター・カメラを追加し、キーフレームを打っていくことでシネマティクスを作成します。 2.3 Verse Verseは Epic Games が開発した プログラミング言語 です。Verseを使用してプログラミングを行うことで、クリエイティブモードで使う各種ロジックをカスタマイズできるようになりました。 UEでゲーム制作を行う際はBPでプログラミングを行いますが、UEFNではBPは使用できません。また、逆にUEでVerseを使用することも不可となります Verseによる基本的な開発方法に関しては4章にて紹介します。 2.4 チームでの共同開発 UEFNでは、ユーザー同士でチームを組んでマップを共同編集するための「 Unreal Revision Control」機能が備わっています。 Unreal Revision Controlによって制作中のマップはバージョン管理がされており、「どの変更が」「いつ」「誰」によって行われたのかを常に確認できます。また、ファイル編集時にはチェックアウト(ロック)が掛かるため、編集の競合や デグレード が防止されています。 UEFNでは上記機能がデフォルトで備わっており、チームでの共同開発が行いやすい環境となります。 ※2024年4月現在 Unreal Revision ControlはUEFN専用の機能となり、UEでは使用不可です チーム設定や Unreal Revision Controlの設定方法に関しては4章にて紹介します。 3 UEFN導入方法 本章では、UEFNの具体的な導入方法を紹介します。 3.1 動作環境 UEFN導入に際し、動作環境に求められるスペックを紹介します。 Epic Gamesアプリサイト を参照に、UEFNの必須スペックと推奨スペックは以下になります。 ※OSは Windows のみ対応 必須スペック OS : Windows 10(64ビット) バージョン1703 CPU : Core i3 -3225 3.3GHz メモリ : 8GB RAM GPU : Intel HD4000, AMD RadeonVega8 推奨スペック OS : Windows10/11(64ビット) CPU : Core i5 -7300U 3.5 GHz、 AMD Ryzen 3 3300Uまたは同等のプロセッサ メモリ : 16GB RAM以上 GPU : NVIDIA GTX960, AMD R9280, または同等のDX11対応 GPU VRAM : 8GB VRAM以上 追加要件 : NVMe SSD なお、Fortniteの動作環境に求められるスペックも上記と同様になります。 3.2 インストール手順 UEFNのインストール手順を紹介します。UEFNで作成したマップをテストプレイする際にFortniteが必要になるため、Fortniteのインストール手順も合わせて紹介します。 最初に、 Epic Games が提供している Epic Games Launcherをインストールします。 Epic Games Launcherは こちらのサイト から インストーラ をダウンロードし、起動してインストールします。 インストールが完了したら、Epic Game Launcherを開き Epic Games アカウントでサインインします。 Epic Games アカウントを所持していない場合は、新規サインアップを行います。 サインインが完了すると Epic Games Launcherのメイン画面が開きます。 ストア上部の検索欄から「Fortnite」を検索します。 Fortniteのストアページへ移動し、画面右の「入手」から注文を行います。(無料です) ※下記画像では既に注文済みのため、「ライブラリに所有」と記載されております。 同様にUEFNも注文します。 ライブラリに移動し、Fortniteをクリックしてインストールします。 Fortniteのインストールが完了しましたら、同様にUEFNもクリックしてインストールします。 ※下記画像はインストール後の状態です。 両方のインストールが完了した後は、ライブラリのFortnite・UEFNからそれぞれのアプリケーションを起動できます。以上にてUEFN・Fotniteの導入は完了です。 4 基本概念と操作方法 この章では、UEFNでマップ制作を行うにあたって基本となる概念と、関連する操作手順を紹介します。 4.1 プロジェクト プロジェクトとは、アセットの集まりとそのアセットの共有やテストに必要なデータが含まれた1つのまとまりを指します。プロジェクトには複数のマップ(レベル)を含めることも可能です。 UEFNでマップ制作を行う際は、まず初めにプロジェクトの作成を行います。 また、プロジェクトをチームで共同編集したい場合は事前にチームを作成しておき、プロジェクト作成時に Unreal Rivision Controlを設定する必要があります。 チーム作成方法とプロジェクトの作成方法を紹介します。 ①チーム作成方法 フォートナイトクリエイターポータル へアクセスします。 Epic Games アカウントでログインします。 画面左上の「選択中のチーム」をクリックすると、UEFNのチーム選択画面が表示されます。 「新規チームを作成する」をクリックし、チーム作成画面を表示します。 チーム名や追加するチームメンバーを記載します。既に作成済みのプロジェクトをチームに移管する場合は、ここで移管対象のプロジェクトを選択しましょう。 記載できましたら、「チームを作成」をクリックし、作成完了です。 なお、チーム名称やチームメンバー・役割などは後から変更可能です。 ②プロジェクト作成方法 Epic Games Launcherの「ライブラリ」を開きます。 UEFNを起動すると、UEFNプロジェクトブラウザが表示されます。 「ISLAND TEMPLATES」などからテンプレートとする島を選び、「プロジェクトの場所」「プロジェクト名」を記載します。 この際、画面右の「 Unreal Revision Control」にチェックを入れ、チームで開発する際は対象とするチームを選択します。 「create」をクリックするとプロジェクトが作成され、UEFNエディタが表示されます。 4.2 エディタ UEFNにおけるゲーム制作は、UEと同様にエディタ画面上で行います。 以下に、 Epic Gamesが公開しているUEFNとUEの比較ドキュメント から抜粋したエディタ画面比較を示します。 上記の見ての通り、エディタ画面上の各ツールはUEFNとUEで基本的には同じです。 分かりやすい違いとして、UEFNではアウトライナの部分に追加で「ISLAND SETTINGS(島設定)」が記載されていますが、こちらに関しては4.4で紹介します。 UEFNとUEにおける各ツール内の詳細な機能差は、 Epic Games公式ドキュメント をご参照ください。 4.3 アセット アセットとは、ゲームを制作する際に用いる各種構成要素を指します。例えば、「スタティックメッシュ」「マテリアル」「テクスチャ」「 サウンド キュー」などが含まれます。 UEFNにおける各種アセットはコンテンツブラウザに格納されています。 UEFNでプロジェクトを作成した際、コンテンツブラウザにはデフォルトで「Fortnite」「Epic」フォルダが作成されています。これらのフォルダには、 Epic Games が用意したアセット(Forniteクリエイティブモードでの既存アセットを含む)などが格納されており、自由に使うことが可能です。 ここからは、各種外部アセットのインポート方法と、公式 マーケットプレイス (FAB)からの購入方法を紹介します。 ①外部アセットのインポート方法 コンテンツブラウザ上に、インポートしたアセットを格納するフォルダを新規作成します。 コンテンツブラウザの「インポート」をクリックします。 インポートするアセットを選択し、「開く」をクリックします。 インポートオプションを設定します。 FBX形式の3Dモデルをインポートする際は、以下のように設定しましょう。 メッシュ > 欠落している コリジョン を生成:on メッシュ > 詳細設定 > メッシュを結合:on メッシュ > 詳細設定 > メッシュLODをインポート:on マテリアル > Search Location (検索場所): Local (ローカル) マテリアル > Material Import Method (マテリアルのインポート方法): Create New Materials (新規マテリアルを作成) 「全てをインポート」をクリックし、インポートします。 UEと同様、インポートしたアセットは ドラッグ&ドロップ でビューポート上に設置可能です。 ②UEからUEFNへのアセット移行方法 UEで利用しているアセットをUEFNに移行したい場合は、UEの機能を用いて簡単に行うことができます。 UEのプロジェクトを開き、移行したいアセットのフォルダを右クリックして「移行」を選択します。 送り先となるUEFNプロジェクトフォルダ内の「Content」フォルダを選択します。 UEFNプロジェクトにアセットが移行されました。 ③FAB マーケットプレイス からの購入方法 FAB マーケットプレイス (以下、FAB)では、UEFNプロジェクトで使用できるカスタム仕様のアセットが販売されています。(無料アセット存在) FABを開くには、 ツールバー の「Fab」をクリックします。 別ウィンドウでFABが開きます。 購入したいアセットを選択したら、「コンテンツブラウザに追加」をクリックします。 なお、今回は参照アセットとして追加します。追加したアセットの中身を編集したい場合は、変更可能なアセットとして追加しましょう。 参照アセットとして追加したため、コンテンツブラウザ内の「Referenced Content」に追加されました。 ドラッグ&ドロップ でビューポート上に設置します。 4.4 デ バイス (仕掛け) UEFNのコンテンツブラウザ(「Fortnite > Devices」フォルダ配下)には、デ バイス (仕掛け)と呼ばれる特殊なアセットが格納されています。 デ バイス とは、ゲームの仕組みを構築をするために使用される一定のロジックを持ったアセットです。 デ バイス を配置することで、ゲームプレイを行うプレイヤーとのインタ ラク ション要素を定義できます。 デ バイス の具体例 タイマーの仕掛け:カウントダウン(時間が0になったときに別イベントを起動)や、ストップウォッチとして表示 アイテムスポーンの仕掛け:特定のアイテムをスポーンさせたり、一定時間毎にランダムにアイテムをスポーンさせる エンドゲームの仕掛け:現在のラウンドやゲーム全体を終了させ、勝利/敗北の判定やスコアボードを表示させる 2024年4月現在、デ バイス は約200種類ほど用意されています。各種デ バイス の詳細に関しては Epic Gamesの公式ドキュメント をご参照ください。 以下では、デ バイス の中で重要となる「島設定(Island Setting)」の設定方法と、その他の通常デ バイス の設置方法を紹介します。 ①島設定デ バイス の設定方法 デ バイス の種類は数多くありますが、その中で必ず使用されるものは「島設定(Island Setting)」です。 島設定デ バイス では、「ゲームルール」「ゲーム設定」「UI設定」「 デバッグ 設定」を調整できます。そのため、UEFNゲーム制作における「ゲームとしての基盤要素」はすべて島設定デ バイス で行うことになります。 島設定デ バイス は、UEFNプロジェクト作成時にビューポート内にデフォルトで生成されます。(アウトライナから確認可能) アウトライナから島設定デ バイス の インスタンス を選択し、詳細画面から各種設定を変更できます。 島設定デ バイス の設定項目詳細に関しては、 Epic Gamesの公式ドキュメント をご参照ください。 ②通常デ バイス の設置方法 通常デ バイス は、「Fortnite > Devices」内のデ バイス をビューポート上に ドラッグ&ドロップ することで設置( インスタンス を作成)できます。 ゲーム制作にあたっては、自分の作りたいゲーム内容に合わせて各種デ バイス の設置・設定を行いましょう。 4.5 Verse 2章にて軽く紹介した通り、UEFN内ではVerseを使用してプログラミングを行います。 2024年4月現在、Verseプログラミングでは「デ バイス (仕掛け)ロジック」「 NPC ロジック」をカスタマイズすることが可能です。 本記事では、Verseによるプログラミング開発の基本的な進め方を紹介します。 Verse言語の詳細に関しては今後別記事にて紹介予定となるため、興味のある方は是非 そちら もご参照ください。 メニューバーから「Verse > Verse Explorer 」をクリックし、Verse Explorer を表示させる。 Verse Explorer の最上段にあるプロジェクト名を右クリックし、「Add new Verse file to project」をクリック。 カスタマイズしたいロジックのテンプレートを選択し、Verseファイルを作成する。 今回は画面上に「 Hello World 」を表示するVerseファイルを作成していくため、デ バイス 用のテンプレートを使います。 Verseファイルが作成されたら、ファイル名を右クリックして「Open in Visual Studio Code 」をクリック。 ※ VS Code をインストールしていない方は、事前に こちら からインストールが必要です。なおUEFNから VS Code を起動すると、 VS Code に自動で「Verse」Extensionがインストールされます。 VS Code にてVerseファイルが開かれました。 画像で表示されているテンプレートファイルでは、以下の処理が記述されています。 hello_world_device というデ バイス 名で新規デ バイス を定義 ゲーム開始時に動くロジックを定義(OnBegin関数) 画面に「Hello, world!」「2 + 2 = 4」を表示  ※Verseにおける標準出力(Print関数)は、ゲーム画面左上に表示されます。 テンプレートから少しだけ編集します。 計算式の部分を、「3 x 5 = 15」となるように編集しました。 Verseファイルを VS Code 上で保存し、UEFNエディタに戻ります。 メニューバーから「Verse > Verseコードをビルド」をクリックし、ビルド( コンパイル )します。 ビルドに成功したら、コンテンツブラウザ上のVerseクラスをビューポート内に ドラッグ&ドロップ します。 デ バイス ロジックテンプレートで作成したVerseクラスは「(プロジェクト名)コンテンツ > Creative Devices」に格納されています。 Verseクラスの インスタンス が作成され、ゲーム内に配置されました。 デ バイス のオブジェクト自体はゲームに不要なため、今回は「ゲーム中に表示」をオフにします。 FortniteでのテストプレイでVerseファイルの動作を確認します。 ツールバー の「セッションを開始」をクリックします。 しばらく待つとセッションが開始され、同時にFortniteがクリエイティブモードの状態で起動します。 Fortniteの画面でEscを押下し、「ゲームをスタート」をクリックします。 ゲーム開始と同時に、画面左上にVerseで出力した文字を確認できました。 また、Mキーを押下して「ログ」をクリックすると、ログ上からも文字の出力を確認できます。 以上が、Verseによるプログラミング開発の基本的な進め方です。 既存デ バイス 間の連動や NPC ロジックの変更方法は別記事にて紹介予定となりますので、是非 そちら もご参照ください。 おわりに 本記事では、UEFNの基礎となる部分を広く紹介しました。 いままでFortniteクリエイティブモードだけでマップ制作をしていた方や、UEでゲーム制作をしていた方たちが、UEFNに踏み出す一助となれれば幸いです。 UEFNは特にVerse部分の奥が深いので、今後はそちらに焦点を当てて記事を執筆する予定です。 UEFNには、リーチできる広いユーザー規模やクリエイターに対しての収益還元など、他のプラットフォームにはない魅力があると思います。今後、世界中で愛されている日本のアニメやゲーム、音楽などの様々なコンテンツ・IPにおけるUEFN参入支援を展開していければと考えております。 現在、 電通 総研は web3領域のグループ横断組織 を立ち上げ、Web3および メタバース 領域のR&Dを行っております(カテゴリー「3DCG」の記事は こちら )。 もし本領域にご興味のある方や、一緒にチャレンジしていきたい方は、ぜひお気軽にご連絡ください! 私たちと同じチームで働いてくれる仲間を、是非お待ちしております! 電通総研の採用ページ 参考文献 ・ Fortniteクリエイティブモード エンゲージメント配当 ・ Unreal Engine5 を使ってワールドの地形を作成してみました ・ フォトグラメトリ×Mesh to Metahumanで人物そっくりな3Dキャラクターを作成してみた(前編) ・ GDC2024のState of Unrealの重要ニュース ・ フォートナイト クリエイティブおよび UEFN で独自のレゴ® の島を制作しましょう ・ UEFN を使用して構築された Nike のバーチャル スニーカー ハント Airphoria の全貌を探る ・ 「フォートナイト」×「NIKE」がコラボレーション!UEFNを活用した“Airphoria”がフォートナイト内に登場! ・ SHIBUYA MULTIVERSE ・ Sky Battle in Shibuya Multiverse ・ Epic Games Store UEFN ・ Epic Games公式ドキュメント UEFN と UE の比較 ・ Epic Games公式ドキュメント Unreal Editor for Fortnite で共同作業を行う ・ Epic Games公式ドキュメント アセットをインポートする ・ UEのマーケットプレイスでダウンロードしたアセットをUEFNのプロジェクトにインポートするにはどうしたらよいでしょうか? ・ Epic Games公式ドキュメント 仕掛けの使用 ・ Epic Games公式ドキュメント 島設定 ・ 写真フィルターのようにシーンの雰囲気を大きく変えるUE5「ポストプロセス」入門 ・ Epic Games公式ドキュメント Verse を使用して独自の仕掛けを作成する 執筆: @matsuzaki.shota 、レビュー: @nakamura.toshihiro ( Shodo で執筆されました )
みなさんこんにちは!  電通 総研 金融ソリューション事業部の松崎です。 本記事は、 Unreal Editor for Fortnite(以下UEFN)の初学者向け紹介記事の第2弾です。 前回の記事 ではUEFN全体の基礎編として紹介しました。今回はUEFN特有のプログラム言語である「Verse」に注目して紹介します。 「UEFNを使い始めたけど、Verseというものがイマイチ分からない」という方は、是非最後までチェックしてください。 目次 Verseとは 実装の流れ 実装例 目次 1 Verseとは 1.1 Verseの言語的特性 1.2 Verseの特徴的要素 ①失敗コンテキスト ②並行処理 1.3 基本概念と記述方法 2 実装の流れ 2.1 クラスの定義 ① デバイス(仕掛け)ロジック ② NPCロジック 2.2 デバイス参照の定義 2.3 処理の記述 ① creative_device クラス ② npc_behavier クラス ③ fort_playspace インタフェース ④ player クラス(agent の子クラス) ⑤ fort_charcter インタフェース 2.4 イベント処理 ① Awaitable インタフェース ② Subscribable インタフェース 3 実装例 おわりに 参考文献 1 Verseとは Verseに関しては 前回の記事 でも軽く触れていましたが、改めて紹介します。 Verseは Epic Games が開発した プログラミング言語 です。Verseを使用してプログラミングを行うことで、Fortniteクリエイティブモードで使う各種ロジックをカスタマイズできるようになりました。 UEでゲーム制作を行う際はBlueprint(以下、BP)や C++ を用いますが、UEFNではBPは使用できません。また、逆にUEでVerseを使用することも不可となります 2024年4月現在のVerseプログラミングでは、主に「デ バイス (仕掛け)ロジック」と「 NPC ロジック」をカスタマイズすることが可能です。 「デ バイス (仕掛け)ロジック」に関しては、既存のデ バイス 同士を連動させるロジックを作成したり、特定の条件(キャ ラク ターの状態変化、時間経過など)でデ バイス を起動させるロジックを作成できます。 また、「 NPC ロジック」に関しては、 NPC スポナーデ バイス からスポーンさせる NPC の挙動ロジックをカスタマイズすることが可能です。 1.1 Verseの言語的特性 Epic Games公式ドキュメント を参照に、Verseの言語的な特性は以下のように示されます。 静的な型付け マルチ パラダイム 言語(関数型・ オブジェクト指向 ・命令型プログラミング) 式で記述(文の記述はなし) 失敗コンテキスト 言語レベルでの並行処理 Epic Games は、Verseを「 メタバース 開発の際の共通言語」とさせることを目的としており、将来的には現在のWeb開発における JavaScript の立ち位置を目指しています。 そのため、Verseは設計思想として「シンプル」「汎用的」「ハイパフォーマス」「長期的な利用可能性」などを掲げており、上記の特性はその思想が反映されたものです。 1.2 Verseの特徴的要素 1.1で示した中で、Verseの大きな特徴である「失敗コンテキスト」「並行処理」に関して解説します。 ①失敗コンテキスト 失敗コンテキストとは「失敗する可能性がある式を実行できるコンテキスト」です。 失敗する可能性がある式とは、具体的には「成功して何かしらの値を生み出すか、失敗して何も値を返さない式」を意味します。つまり、失敗を前提として式を実行させることができる、ということです。 注意点として、失敗コンテキストで例外処理が走ることはありません。そもそもVerseの言語仕様として例外処理が存在しません。失敗コンテキスト内で式が失敗した場合は、コンテキスト内の処理がすべて ロールバック されます。 失敗コンテキストにより、特定の式をコミットすることなく試し実行することが可能になります。 以下に、失敗コンテキストを利用した記述例を示します。 if (Element := MyArray[Index]): Log(Element) 「:=」は変数や定数の初期化 演算子 ifの()内にて、MyArrayt配列のインデックスが有効か否かをチェック インデックスが有効であれば、ElementへアクセスしてLog出力 インデックスが無効の際は ロールバック (ElementへのアクセスやLog出力は実行されない) ②並行処理 ここで紹介する「並行処理」に関して、 Epic Games公式ドキュメント では「並列処理」と記載されています。 プログラミングにおける一般的な認識として、「並行処理」と「並列処理」は以下のように説明されます。 並行処理: 処理を細かく分割し、それぞれの処理を切り替えながら実行することで複数の処理を並行で進めること。時間を「範囲」で見ると複数の処理が実行されているが「点」で見るとどれか1つの処理のみが実行されている状態。 並列処理: マルチコアCPUなどにより、複数の処理を同時に実行すること。時間を「点」で見た際も複数の処理が実行されている状態。 Verseでは言語レベルにて並行処理の制御が可能であり、これはノン プリエンプティブマルチタスク で動作させていることを意味します。(逆にVerseからマルチコアCPUを制御できるわけではないため、本記事では「並列処理」とは呼称しません) 次に、Verseにおける並行処理の制御方法を説明します。 Verseのすべての式は、「immediate式」と「async式(以下、非同期式)」に分けられます。 Epic Games公式ドキュメント にて、それぞれの式は以下のように説明されています。 immediate式:遅延なく評価され、現在の「シミュレーションアップデート」内にて評価が完了する 非同期式:評価が後回しになる可能性があり、現在または後続の「シミュレーションアップデート」で完了することもあれば、完了しないこともある ※ 「シミュレーションアップデート」という概念が出てきていますが、これはVerseにおける最小実行単位です。3DCGにおける レンダリング の最小単位は「1フレーム」ですので、基本的にはこれと同じになります。 (ただし、「シミュレーションアップデート」と「フレーム」の時間を意図的にズラすことは可能です。また、オンラインのゲームサーバーが同期されなくなった場合など、「フレーム」の更新前に「シミュレーションアップデート」が完了することはあります。詳細は こちら をご参照ください) Verseでは、上記の「immediate式」と「非同期式」を使い分けることによって、並行処理を言語側から制御することが可能です。 1.3 基本概念と記述方法 Verseはマルチ パラダイム 言語として設計されており、主に以下の パラダイム 要素を取り入れています。 オブジェクト指向 プログラミング 関数型プログラミング 命令型プログラミング Epic Games公式ドキュメント によると、Verseにおけるマルチ パラダイム は「 決定論 的プログラミング」を可能な限り追求する形で取り入れられているとのことです。 Verse プログラミング言語 としての基本的な構文方法や機能は、 Epic Games公式ドキュメント(Verse言語のクイックリファレンス) に記載されております。実装する際はご参照ください。 2 実装の流れ Verseファイルを実装する際の流れを紹介します。 2.1 クラスの定義 Verseファイルを作成する際は、最初にクラスの定義を行います。 この際、Verseファイルの目的によって継承するクラスが変わります。 ① デ バイス (仕掛け)ロジック デ バイス (仕掛け)ロジックを作成する場合は、[creative_device]クラスを継承させます。 このクラスは、カスタムしたデ バイス (仕掛け)ロジック作成用に Epic Games が用意したクラスです。 [creative_device]クラスが継承されたクラスでVerseファイルを作成すると、 コンパイル される際に自動でUEFNコンテンツブラウザに表示されます。また、Verseファイル(クラス)の インスタンス はUEFNにてコンテンツブラウザからビューポート上にドラッグすることで配置されます。 なお、[creative_device]クラスは[/Fortnite.com/Devices]モジュールにて定義されているため、事前にこのモジュールをインポートする必要があります。 インポート式 using { /Fortnite.com/Devices } クラス定義式例(継承含む) hello_world_device := class(creative_device): ② NPC ロジック NPC ロジックを作成する場合は、[ npc _behavior]クラスを継承します。 このクラスはカスタム NPC 動作の作成用に Epic Games が用意したクラスです。 また、[ npc _behavior]クラスを継承したクラスでは、CharacterDefinitionアセットか npc _spawner_deviceで生み出されるキャ ラク ターに対しての挙動ロジックを定義できます。 なお、[ npc _behavior]クラスは[/Fortnite.com/AI]モジュールにて定義されているため、事前にこのモジュールをインポートする必要があります。 インポート式 using { /Fortnite.com/AI } クラス定義式例(継承含む) npc_custom_action := class(npc_behavior): Verseにてインポート可能なモジュール一覧は こちら をご参照ください。 2.2 デ バイス 参照の定義 デ バイス 間の連動ロジック作成時などに、Verse内で既存デ バイス (仕掛け)を参照する必要があるときは、そのデ バイス 型の変数を「UEFNから編集可能な形」で定義する必要があります。 「UEFNから編集可能な形」で定義する理由としては、実際のデ バイス インスタンス との紐づけをUEFNで行う必要があるためです。 Verseでは、紐づけ用の箱を作っておくイメージになります。 デ バイス 用の変数を作成する際に用いる各種デ バイス 型名は こちら をご参照ください。 デ バイス 参照の定義式例 @editable MyDevice:timer_device = timer_device{} 上記では、「タイマーの仕掛け」紐づけ用の箱を作成しています。 「@editable」を記述することでUEFNから編集可能な形にしています。 上記にモジュールインポートとクラス定義の式を加え、以下のように記述します。 using { /Fortnite.com/Devices } using { /Verse.org/Simulation } using { /UnrealEngine.com/Temporary/Diagnostics } teckblog_device := class(creative_device): @editable MyDevice:timer_device = timer_device{} 試しにこの記述のVerseファイルを コンパイル してUEFNのビューポート内に配置すると、 インスタンス の詳細から「タイマーの仕掛け インスタンス 」との紐づき設定が行えるようになっていることを確認できます。 2.3 処理の記述 デ バイス 参照の定義まで完了しましたら、Verseで行う処理を記述します。 ここでは、Verse特有の機能をいくつか紹介します。 機能の網羅的な詳細に関しては、 Verse モジュールリスト から対象となるクラスを探してご参照ください。 (例:creative_classの機能 → /Fortnite.com/Devices/creative_device/Functions) ① creative_device クラス OnBegin/OnEnd:クリエイティブモードのゲーム終了/開始時に呼び出されるメソッド Show/Hide:デ バイス インスタンス のオブジェクトをゲーム内で表示/非表示 GetPlayspace:fort_playspaceインタフェースへアクセス ② npc _behavier クラス OnBegin/OnEnd: NPC の追加/削除時に呼び出されるメソッド GetAgent:ゲーム内の全 NPC 情報を取得(agentクラス) ③ fort_playspace インタフェース GetPlayers:ゲーム内の全プレイヤー情報を取得(playerクラス) PlayerAddedEvent/PlayerRemovedEvent:ゲーム内にプレイヤーが参加/離脱した際に信号を送信 ④ player クラス(agent の子クラス) GetFortCharacter:fort_characterインタフェーズへアクセス ⑤ fort_charcter インタフェース GetAgent:キャ ラク ターに紐づいたエージェントの状態を取得 EliminatedEvent:キャ ラク ターが試合から除外された際に信号を送信 2.4 イベント処理 Verseでは、イベント処理を定義することが可能です。イベント処理とは、「特定のイベントが発生した際に動きだす」処理のことを指します。 イベント処理の際に使用する機能を紹介します。 ① Awaitable インタフェース Await:信号が送信されるまで実行中のタスクを待機 以下は、プレイヤー追加時に特定のメッセージが出される処理例です。 プレイヤー追加時にPlayerAddedEventによって信号が送信され、Await関数が解除されることによって後続処理(Print)が動き出します。 PlayerAddedEvent.Await() Print("プレイヤーが追加されました") ② Subscribable インタフェース Subscribe(t):信号が送信された際に(t)に登録したメソッドを呼びだす 以下は、プレイヤー追加時にAddWeaponメソッドが呼び出される処理例です。 プレイヤー追加時にPlayerAddedEventによって信号が送信され、Subscribe関数がAddWeaponメソッドを呼び出します。 (AddWeaponメソッドは別で定義されている前提になります) PlayerAddedEvent.Subscribe(AddWeapon) ①のAwait関数を用いたイベント処理では、「待機状態を解除する」というトリガーを用いているため、1回処理が完了すると2回に信号が送信されても起動しません。(「ループ処理を組んで再度待機状態に戻す」などで、再利用すること自体は可能です) 一方、②のSubscribe関数を用いたイベント処理では信号が送信される度にイベント処理が起動します。 そのため、起動されるイベント処理の用途に合わせて使い分けが必要です。 なお、上記で紹介しているイベント処理用インタフェースは、PlayAddedEventのように特定条件で信号を送信してくれる機能を使うことが前提になります。このような機能が用意されていない条件をトリガーに設定したい際は、[event(t)]クラスを用いて、「信号を送信するロジック」から実装する必要があります。 こちらに関しては別記事にて紹介予定です。 3 実装例 2章で紹介した各種実装方法などを用いて、例となるロジックを実装していきます。 今回はデ バイス 間の連携ロジックを作成します。 具体的には、 タイマーのカウントダウンが0になるごとに、1つずつバリアを消すロジック を実装します。 このロジック内で連携するデ バイス は「バリアの仕掛け」と「タイマーの仕掛け」です。 ※バリアとはUEFNがデフォルトで提供しているデ バイス の一つで、主に「キャ ラク ターや銃弾を通さない半透明な壁」として利用されます。 最初に、デ バイス ロジックテンプレートからVerseファイルを作成します。 以下のようにVerseを記述しました。 using { /Fortnite.com/Devices } using { /Verse.org/Native } using { /Verse.org/Simulation } using { /UnrealEngine.com/Temporary/Diagnostics } timer_barrier_connect_device := class<concrete>(creative_device): @editable Barriers : []barrier_device = array{barrier_device{}} @editable Timers : []timer_device = array{timer_device{}} OnBegin<override>()<suspends>:void= for(Index -> Timer : Timers): Timer.SuccessEvent .Subscribe(event_handler{Device := Self,Timer_num := Index} .TimerCompleatedHandler) DropBarriers(Index: int):void= if(Barrier := Barriers[Index]): Barrier.Disable() event_handler := class: Device : timer_barrier_connect_device Timer_num : int TimerCompleatedHandler(PlayerAgent:?agent): void = Device.DropBarriers(Timer_num) 処理の流れをまとめると以下になります。 Onbeginメソッド内でタイマーカウントダウンの完了イベント(SuccessEvent)を、待つ タイマー完了イベントが発生したら、Subscribe関数に登録されている[event_handler]クラスのTimerCompleatedHandlerメソッドが呼び出される。 (この際、完了した「タイマーの仕掛け」のIndex数値が[event_handler]クラスへ渡される) TimerCompleatedHandlerメソッドの中で、メインクラスのDropBarriersメソッドが呼び出される。 (「2.」で渡されたIndex数値をDropBarriersメソッド呼び出しの引数に用いる) DropBarriersメソッドにて、Index数値に対応する「バリアの仕掛け」を無効化 以下、各部に分けて説明します。 最初はモジュールのインポートです。 using { /Fortnite.com/Devices } using { /Verse.org/Native } using { /Verse.org/Simulation } using { /UnrealEngine.com/Temporary/Diagnostics } 次に、メインクラスとなる[timer_barrier_connect_device]クラスを定義します。 [creative_device]クラスを継承する形で定義しています。 timer_barrier_connect_device := class<concrete>(creative_device): メインクラスの中に、「バリアの仕掛け」と「タイマーの仕掛け」用の変数を定義します。 今回はそれぞれの仕掛けを複数個用いるので、配列で定義しています。 @editable Barriers : []barrier_device = array{barrier_device{}} @editable Timers : []timer_device = array{timer_device{}} OnBeginメソッドの中身を記述します。(OnBeginはゲーム開始と同時に動きだすメソッドです) for文を利用し、「タイマーの仕掛け」の個数分だけイベント処理が登録されるようにしています。 Subscribe関数にて呼び出されているのは[event_handler]クラスのTimerCompleatedHandlerメソッドです。(後で定義します) OnBegin<override>()<suspends>:void= for(Index -> Timer : Timers): Timer.SuccessEvent .Subscribe(event_handler{Device := Self,Timer_num := Index} .TimerCompleatedHandler) DropBarriersメソッドです。名前の通り「バリアの仕掛け」を無効化するためのメソッドです。 引数のIndexに対応した「バリアの仕掛け」を無効化します。 無効化の処理がif文に入っている理由は、配列インデックスの存在チェックのためです。(1.2 ①で紹介した失敗コンテキストを利用しています) DropBarriers(Index: int):void= if(Barrier := Barriers[Index]): Barrier.Disable() メインクラスとは別で、[event_handler]クラスを定義します。 クラス内のTimerCompleatedHandlerメソッドでは、メインクラスのDropBarriersメソッドが呼び出されるように定義しています。 event_handler := class: Device : timer_barrier_connect_device Timer_num : int TimerCompleatedHandler(PlayerAgent:?agent): void = Device.DropBarriers(Timer_num) 各部の説明は以上となりますが、ここで 「Subscribe関数から、なぜ直接DropBarriersメソッドを呼び出していないのか?」 を説明します。 [event_handler]という別クラスを経由してDropBarriersメソッドを呼び出している理由は、 「Subscribe関数からメソッド呼び出しを行う際は、呼び出しメソッドに対してパラメータ(Index数値)を渡すことができないから」です。 Subscribe関数からメソッドを呼び出す際、ほとんどの場合はagentパラメータしか渡すことができません。 渡せるパラメータは、Subscribe関数に信号を送っているイベント関数に依存します。 (今回の場合は[timer_device]クラスのSuccessEventが該当し、以下の通りagentパラメータしか渡せません) Subscribe関数に上記の制約があるため、 「 イベントハンドラ 用のクラスを別で作成し、(メソッド呼び出しではなく)クラス呼び出しの部分でパラメータを渡す」 という手段を取っています。詳細には UEFNフォーラム投稿 もご参照ください。 Verseの実装が完了したので、ビルドした後にVerseクラスをビューポートへ ドラッグ&ドロップ し、Verse インスタンス を作成します。 UEFNの「Fortnite/Devices」から、「タイマーの仕掛け」と「バリアの仕掛け」を3つずつ配置します。 この際、「タイマーの仕掛け」は以下のように設定します。 持続時間:30秒(タイマー1) / 60秒(タイマー2) / 90秒(タイマー3) ゲーム開始時にスタート:オン また、「バリアの仕掛け」は見分けがつくよう「バリアの素材」を変化させています。 Verse インスタンス の詳細から、「バリアの仕掛け」と「タイマーの仕掛け」をVerseに紐づけます。 アウトライナからVerse インスタンス を選択し、詳細画面でデ バイス 名の欄を開きます。(今回は「Timer Barrier Connect Device」) BarriersとTimersの欄を開き、「+」を押してインデックスの配列要素を3つに増やします。 ビューポートの左から順に対応するよう、各インデックス配列要素に「バリアの仕掛け」「タイマーの仕掛け」を設定(紐付け)しました。 Fortniteにてテストプレイを行います。 ゲーム開始時 30秒経過後 60秒経過後 90秒経過後 想定通りにロジックが動いていることを確認できました。 おわりに 本記事では、UEFNにおけるVerseの基礎となる概念を実装例と共に紹介しました。 細かい構文方法や API に関しては、本文中に紹介した Epic Games 公式ドキュメント( これ とか これ )にまとまっております。実際にVerseのコードを書く際はそれらをじっくり参照しながら記述すると、深い学びになると思います。 Verseでは NPC の挙動ロジックをカスタマイズすることも可能ですので、次はそちらの実装例を紹介したいと思います。 現在、 電通 総研は web3領域のグループ横断組織 を立ち上げ、Web3および メタバース 領域のR&Dを行っております(カテゴリー「3DCG」の記事は こちら )。 もし本領域にご興味のある方や、一緒にチャレンジしていきたい方は、ぜひお気軽にご連絡ください! 私たちと同じチームで働いてくれる仲間をお待ちしております! 電通総研の採用ページ 参考文献 ・ Epic Games公式ドキュメント Verse 言語のリファレンス ・ Epic Games公式ドキュメント Verse 言語のクイック リファレンス ・ Epic Games公式ドキュメント Verse を使用したプログラミングの詳細 ・ Epic Games公式ドキュメント モジュールとパス ・ Epic Games公式ドキュメント Verse API Reference ・ Epic Games公式ドキュメント Verse の仕掛けのプロパティをカスタマイズする ・ Epic Games公式ドキュメント 3.プレイヤー イベントをサブスクライブする ・ Epic Games公式ドキュメント クラス ・ Epic Games公式ドキュメント 失敗 ・ Epic Games公式ドキュメント 並列処理の概要 ・ Epic Games公式ドキュメント frame ・ Epic Gamesフォーラム 追加パラメーターを使用したイベント サブスクライブのガイド (ハンドラー関数) ・ Epic Games公式ドキュメント 4. 仕掛けをリンクする ・ Verse Concurrency—Time Flow: Everything, Everywhere in UEFN, All at Once | Unreal Fest 2023 ・ UEデザイナーがUEFNで遊んでみた ・ verse言語の設計思想を読み解きたい(2)失敗コンテキストとは何か ・ Verse言語の設計思想を読み解きたい(9)非同期処理① 並行処理と並列処理 ・ Verse言語の設計思想を読み解きたい(10)非同期処理② 非同期式と非同期コンテキスト ・ [UEFN][verse] UEFNのカスタムイベント実装を考える[1] Await()の使い方 ・ [UEFN][verse] UEFNのカスタムイベント実装を考える[2] 「イベントリスナ」と「イベントハンドラ」についておさらい ・ 【UEFN】 VerseでDeviceを連携させる(Verseの学習①) ・ 【UEFN】Verseでイベント発生時に追加でパラメータを渡す ・ 【UEFN】Verseでcreative_deviceクラスの多用をやめてみる 執筆: @matsuzaki.shota 、レビュー: @nakamura.toshihiro ( Shodo で執筆されました )
こんにちは!HCM事業部の小林です。 今回は、私が2024年3月に受験したUX検定基礎についてご紹介します。 最近はUIUXが重要視されつつあり、力を入れていきたいと思っている組織も多いのではないでしょうか。UIUXに少しでも興味がある人はぜひ最後まで読んでみてください! UX検定基礎とは なぜ受けようと思ったのか UX検定基礎の勉強方法 受験と結果 今回の試験を終えてみて UX検定基礎はこんな人におすすめ! おわりに UX検定基礎とは UX検定基礎は、UXを学び始めた人がUX向上の取組みに向けた マインドセット と基礎スキルを学ぶことができる資格です。 IT系の資格で例えるとITパスポートやG検定と同じような位置づけです。 以下の図は、UX向上に必要な3段階のスキルを示しています。 UX検定基礎では、一番基礎となるUXジェネラリストに達しているか確かめる問題が出題されます。受験資格の制限がないため、興味がある人であれば誰でも受けられます! 将来UIUXデザイナーになりたい人は受けておいて損はないと思います。 (UX検定公式HPより引用) 試験時間は100分です。4択の中から選ぶ形式で100問が出題されます。 合格ラインは2024年3月時点では公表されていません。 オンライン実施(自宅受験)なので自分のパソコンを使って受験しました。 なぜ受けようと思ったのか 以前からUIUXに興味があり、独学で学んでいたときに先輩社員におすすめされたのがきっかけです。この機会を通じて、UIUXについて学びつつ、自分がこの分野に適性があるのか知れればいいなと思って受験しました。 UX検定基礎の勉強方法 2024年3月時点では、UX検定基礎の過去問は公開されていません。 そのため、公式サイトにある シラバス や学習推奨図書をもとに勉強を進めました。 試験勉強期間中にやったこととしては以下のとおりです。 ① シラバス にある単語の意味を調べる 公式サイトには、出題範囲として シラバス が掲載されています。小カテゴリや重要ワードにある単語について1つ1つ意味を調べ、 スプレッドシート にまとめていきました。文字だけでなく図も一緒にのせておくと、あとで見直すときに思い出しやすかったです。 ②学習推奨図書を読む 公式サイトでは勉強方法として4冊の学習推奨図書が掲載されています。 本を読みながら シラバス にあった単語があれば単語帳に追記していきました。また、 シラバス に載っていない単語でも分からない単語があったらすぐに調べることを意識していました。 ・アフターデジタル2 UXと自由 海外のサービスの事例を踏まえて解説しています。日本にはあまりないサービスやビジネスモデルがたくさん紹介されていて読むのが楽しかったです。日本企業のあるべき姿について学ぶことができました。 ・UXグロースモデル アフターデジタルを生き抜く実践方法論 UX業務を組織に浸透させる方法について書いてある本です。UXの知識というよりも、考え方が中心に書いてある印象でした。アフターデジタル2の続編なので、まずそちらを読んでからのほうが理解しやすいと思います。 ・人間中心設計入門(HCDライブラリー第0巻) UXの考え方が定義されるまでの歴史やHCD(人間中心設計)のプロセスなど幅広く紹介されていました。イラストがたくさんあって読みやすかったです。 専門用語の解説がたくさん掲載されているため、他の本を読んで分からなかった単語はこの本で調べるという使い方もできます。UXを学ぶにあたっての入門書としてぴったりだと思うので、受験するか迷っている人はまずこの本を読んでから決めてもいいのかなと思いました。 ・ ユーザビリティ エンジニアリング: ユーザエクス ペリエ ンスのための調査、設計、評価手法 タイトルの通り、調査・設計・評価まで実際にどのように進めていくのか詳しく書いてありました。実務で行うことになったとしても、この本を持っておけば自分でもできそう!と思わせてくれるような本でした。 「業務が忙しくて本を読む時間がない!」「本を読むのが苦手、、」という方は、本の難易度と合格に向けて読むべき本の優先度を考えてみたので、そちらを参考にしてみてください。(あくまで個人の主観です) <難易度> アフターデジタル2< ユーザビリティ エンジニアリング≒人間中心設計入門<UXグロースモデル <優先度> 人間中心設計入門< ユーザビリティ エンジニアリング<アフターデジタル2≒UXグロースモデル ③webサイトを見て10 ヒューリスティック 評価をしてみる 上の2つは他の体験談でも紹介されていた勉強法ですが、これは独自で考えた勉強法です。 いろんなサイトを見漁って「このサイト見やすい!」と思ったサイトをピックアップして10 ヒューリスティック 評価を行いました。 ④(時間があれば)動画コンテンツ視聴 UX検定の公式サイトでは「UXインテリジェンス基礎コース」という動画コンテンツも紹介されています。8時間ほどで出題範囲を学ぶことが可能です。プラスアルファで学びたい方はこちらも勉強に取り入れてみてもいいと思います。 試験勉強のスケジュール 試験勉強の全体スケジュールとしてはこんな感じでした。 学習推奨図書を読み始めたのは2か月前からで、全て読了したのが1か月前でした。 試験日の1か月前からは学習推奨図書の2周目を読みつつも単語帳に割く時間を増やしました。 全て含めた勉強時間は40時間ほどでした。 受験と結果 結果は100点中92点で合格でした!(ぱちぱち) 問題は70分で解き終わり、残りの30分間で不安な問題を見直す時間に充てました。問題数が多く、ボリューミーだったので30分では全ての問題を見直すことができませんでした。。ただひねった問題はなく、 シラバス の単語の意味を覚えていれば分かる印象でした。 合否は1か月後と書いてありましたが約2週間で来ました。 最初に述べた通り合格ラインは分かりませんが、 SNS を見てると80点台で合格している人がいたので80点台が取れていればいいのかなと思います。 今回の試験を終えてみて いつもは勉強している途中でモチベーションが下がることが多かったのですが、UX検定基礎は新しい学びがたくさんあり、勉強期間全体を通じて楽しみながら勉強できました。 学んだ成果として、UIUX関係の本の内容がすらすら読めるようになったのがすごくうれしかったです。また、デザイナーさんの話が頭に入ってきやすくなった気がしています。 試験後もUIUXに関する本は読んでいるのですが、個人的に試験前に読みたかったと思える本があったので載せておきます。UXについて書いてあるところだけでも読んでみてください。 UX検定基礎はこんな人におすすめ! ・UIUXに興味がある人 ・デザイナーさんと話す機会がある人 ・デザイン思考を社内に広めていきたい人 ・UXの考え方を業務に活かしたい人 おわりに 今回はUX検定基礎の勉強方法や難易度、受けてみての感想などを紹介しました。UIUXに少しでも興味があったら受けてみてはいかがでしょうか!この体験記が参考になれば幸いです。 HCM事業部では技術職を募集しています。職種ごとの詳しい募集要項は以下のリンクからご覧ください。 私たちは一緒に働いてくれる仲間を募集しています! 新卒採用ページ 【オープンポジション】自社パッケージ製品の導入プロジェクトマネージャ/リーダ 統合HCMソリューション導入におけるクラウドチームリーダー (東京)統合HCMソリューション 導入プロジェクトマネージャー/リーダー (名古屋)統合HCMソリューション 導入プロジェクトマネージャー/リーダー (大阪)統合HCMソリューション  導入プロジェクトマネージャー/リーダー 執筆: @kobayashi.hinami 、レビュー: @nagamatsu.yuji ( Shodo で執筆されました )
こんにちは、 電通 総研の瀧川亮弘です。 現在、Flutter(FlutterFlow)とSupabaseによる アプリ開発 を行っています。 本記事ではSupabaseのEdge FunctionsでDrizzle ORMを利用して トランザクション に対応する実装について記載します。 Edge FunctionsからDBへのアクセスには、Supabase Javascript Client(以下supabase-js)とDrizzle ORM(以下drizzle)という二つのライブラリを併用しています。 drizzleはsupabase-jsに備わっていない トランザクション 機能を補う目的で利用しています。 https://github.com/orgs/supabase/discussions/526 クライアントの初期化 依存ライブラリ supabase-jsの初期化 drizzleの初期化 トランザクション処理の実装 ユーザー退会 ユーザー登録 終わりに クライアントの初期化 それぞれのクライアントの インスタンス 化処理です。 依存ライブラリ まずは必要な依存ライブラリをインポートします。 { "imports": { "supabase": "https://esm.sh/@supabase/supabase-js@2.40.0", "drizzle-orm": "npm:drizzle-orm", "drizzle-orm/": "npm:/drizzle-orm/", "postgres": "https://deno.land/x/postgresjs@v3.4.4/mod.js", } } supabase-jsの初期化 デフォルトで用意されている 環境変数 からDBの接続情報を取得し インスタンス を生成します。 import { createClient } from "supabase"; // 環境変数から必要な情報を取得 const supabaseUrl = Deno.env.get("SUPABASE_URL") as string; const supabaseKey = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY") as string; // Supabaseのクライアントを初期化 export const supabase = createClient(supabaseUrl, supabaseKey); drizzleの初期化 自身で設定した 環境変数 CUSTOM_SUPABASE_DB_URL からSupervisorの接続情報を取得し インスタンス を生成します。 Supervisor経由でDBに接続することでmax connectionsエラーを抑制できます。 詳細は以下をご参照ください。 https://supabase.com/docs/guides/database/connecting-to-postgres import { drizzle as createDrizzle } from "drizzle-orm/postgres-js"; import postgres from "postgres"; // 環境変数から必要な情報を取得 const connectionString = Deno.env.get("CUSTOM_SUPABASE_DB_URL") as string; // Postgresのクライアントを初期化 const pg = postgres(connectionString, { prepare: false, ssl: true, }); // Drizzleのクライアントを初期化 export const drizzle = createDrizzle(pg, { logger: true }); トランザクション 処理の実装 まず基本方針としてauth スキーマ に対してはsupabase-js、public スキーマ に対してはdrizzleでアクセスするという使い分けをしています。 auth スキーマ はSupabaseがデフォルトで用意している スキーマ であり、開発者は スキーマ の変更を行いません。 supabase-jsを通して、ユーザー登録やメールアドレス変更など認証関連の処理を実行することで、間接的にauth スキーマ にアクセスします。 具体的な処理はsupabase-jsが隠蔽しているため、開発者は スキーマ の詳細を知る必要がありません。 public スキーマ ではアプリケーションに必要な任意のテーブルを管理しています。 supabase-jsでもpublic スキーマ へのアクセスは可能ですが、 トランザクション をサポートしていないため、drizzleでアクセスしています。 Supabaseで開発を行う場合、2つの スキーマ (auth, public)にまたがり、副作用のある処理を実行したいケースがあります。 しかし、supabase-jsとdrizzleで トランザクション を共有できないため、若干の工夫が必要です。 例としてユーザー退会とユーザー登録について記載します。 ユーザー退会 ユーザー退会では、auth.users(auth スキーマ のusersテーブル)とpublic.profilesのレコードを同時に 論理 削除します。 まずは、Edge Functionsでpublic スキーマ に対する処理のみを実装します。 import { drizzle, supabase } from "../_shared/client.ts"; // ユーザー退会 const handleDelete = async (req: Request): Promise<Response> => { // 省略 await drizzle.transaction(async (tx) => { // お気に入り削除 await tx.delete(favorite).where(eq(favorite.userId, uid)); // プロフィール削除 await tx.update(profiles) .set({ isDeleted: true, }) .where(eq(users.authUserId, uid)); }); // 省略 }; Deno.serve((req) => { return handleDelete(req); }); 次にauth スキーマ に対する処理を追加します。 auth.usersへの削除処理に失敗した場合、明示的に tx.rollback(); でpublic側も ロールバック する点がポイントです。 import { drizzle, supabase } from "../_shared/client.ts"; // ユーザー退会 const handleDelete = async (req: Request): Promise<Response> => { // 省略 await drizzle.transaction(async (tx) => { // お気に入り削除 await tx.delete(favorite).where(eq(favorite.userId, uid)); // プロフィール削除 await tx.update(profiles) .set({ isDeleted: true, }) .where(eq(profiles.authUserId, uid)); // ユーザー削除 const { error: errorDeleteAuthUser } = await supabase .auth .admin .deleteUser( uid, true, // 論理削除 ); if (errorDeleteAuthUser) { tx.rollback(); // 明示的にロールバック // 省略 } }); // 省略 }; Deno.serve((req) => { return handleDelete(req); }); ユーザー登録 ユーザー登録では、auth.usersとpublic.profilesのレコードを同時に追加する必要があります。 外部キーの依存関係の都合上、auth.usersから先にインサートします。 public側の処理に失敗した場合に、drizzleの トランザクション 機能でauth側を ロールバック することはできないため、ユーザー退会と同様の方法は取れません。 結果、Edge Functionsでのpublic.profilesへのインサートは諦め、公式ページの通りの実装としました。 https://supabase.com/docs/guides/auth/managing-user-data#advanced-techniques auth.usersのインサートをトリガーに PostgreSQL の関数でpublic.profilesへのインサート処理を行っています。 関数内でエラーが発生した場合、auth側の処理も ロールバック されることは確認できました。 おおよそ公式の通りですが、一応ソースも掲載します。 -- ユーザー登録関数 CREATE OR REPLACE FUNCTION public .handle_new_user() RETURNS TRIGGER LANGUAGE plpgsql SECURITY DEFINER SET search_path = public AS $$ DECLARE role public . " Role " ; BEGIN role := (NEW.raw_user_meta_data ->> ' role ' ):: public . " Role " ; INSERT INTO public .profiles (auth_user_id, role, is_deleted) VALUES (NEW.id, role, false ); RETURN NEW; END ; $$; -- ユーザー登録関数を実行するトリガー DROP TRIGGER IF EXISTS on_auth_user_created ON auth.users; CREATE TRIGGER on_auth_user_created AFTER INSERT ON auth.users FOR EACH ROW EXECUTE FUNCTION public .handle_new_user(); 終わりに SupabaseもDrizzleも公式ページが充実しているため、基本そちらで事足りそうです。 それでは素敵なSupabase生活を^^ 私たちと一緒に働いてくれる仲間を、是非お待ちしております! 電通総研の採用ページ 執筆: @takigawa.akihiro 、レビュー: @kobayashi.hinami ( Shodo で執筆されました )
こんにちは!X イノベーション 本部プロダクト イノベーション センターの 米久 保 剛です。 弊社のテックブログ上では今回が初めての記事執筆となります。 アーキテクチャ 設計やアプリケーション設計の話を中心に、 不定 期に情報発信していきたいと考えています。 YAGNI 原則 YAGNI 原則をご存知でしょうか。 エクストリーム・プログラミング (XP)の重要な原則の一つであるこの原則は、You Ain't Gonna Need Itのアクロニム(頭字語)から 命名 されています。日本語にすると「どうせ要らないって」というニュアンスでしょうか。推測に基づいて余計な機能を作り込んだところで将来実際に使われる可能性は低く、時間と労力を無駄にするばかりかコードの複雑化などのリスクさえあります。ですから、現時点でわかっている要件をちょうど満たすだけの機能を実装すべきであると YAGNI 原則は主張します。 YAGNI 原則は機能(振る舞い)の大小に限った話ではなく、設計についても言えるものです。つまり、不確定な将来の要件に対応するための仕組みをあらかじめ作っておくという、無駄になる可能性のある過剰設計を抑制するのです。推測に基づいた設計は、無駄なだけでなく、間違った設計や良くない設計を生んでしまう危険性もはらみます。正しい設計を行えるのに十分な情報が得られる時点(つまり、本当にその要件が必要となるとき)まで、設計判断を遅らせるのです。 サンプル サンプルコードで確認しましょう。(本記事中の完全な ソースコード は、筆者の GitHubリポジトリ で確認可能です)。 最初の設計 次のコードは、商品リストを CSV ファイルに出力するプログラムです。サンプルコードなので CLI ですが、Webアプリケーションの場合は、クライアントからのリク エス トを受け付けて処理を行うコントローラーに該当すると考えてください。 // main_1.ts import { Product , getProducts } from "./products.js" ; import { outputCsv } from "./output_csv_1.js" ; const products: Product [] = getProducts (); // 商品リスト取得 outputCsv ( products ); // CSVファイル出力 実際の CSV 出力処理は、別のファイルに関数を定義し、 csv-writer というNodeモジュールを利用して実装しています。 // output_csv_1.ts import { createObjectCsvWriter } from "csv-writer" ; import { Product } from "./products.js" ; const outputCsv = ( products: Product [] ) : void => { const csvWriter = createObjectCsvWriter ( { path: "./dist/products.csv" , header: [ { id: "productCode" , title: "商品コード" } , { id: "productName" , title: "商品名" } , { id: "price" , title: "単価" } , { id: "category" , title: "カテゴリー" } , { id: "notes" , title: "備考" } , ] , } ); csvWriter.writeRecords ( products ) .then (() => { console .log ( "...CSVを出力しました" ); } ); } ; export { outputCsv } ; この関数内に CSV のレイアウト情報がベタ書きされていることに、不安を抱いたという方は、 プログラマー のセンスとして正しいです。なぜなら、運用に入って以下のような仕様変更要求が発生した場合に、現在の設計だと容易に対応できないケースがあるからです。 カラムの追加や削除、順序変更 データの加工 レコードのソート順の変更 条件に応じたレイアウトの切り替え 仮にこれらの要求にすべて応えられるような設計をしようとすると、プログラムの数は増え、処理も複雑化するでしょう。また、現時点でリアルな要件が決まっていないのならば、何らかの仮定に基づいて仕様を決めて進めることになります。この仮定が外れていた場合は、結局設計を見直す必要が生じ、大きな手戻りとなってしまうのです。 ですので、素敵な設計をしたいという欲求をぐっとこらえて、現時点で十分な設計にとどめることが YAGNI の考え方となります。現在の設計でも、一つ目の仕様変更要求「カラムの追加や削除、順序変更」であれば問題なく対応可能です。 設計の見直し 次に、「条件に応じたレイアウトの切り替え」の仕様変更要求が実際に発生したとしましょう。具体的には次の内容とします。 ログインユーザーが管理者の場合は CSV にすべての列を出力するが、通常のユーザーの場合は列を限定する 当初の設計は、 CSV のレイアウトは固定で一つであることを前提としているため、この仕様変更要求を満たすことができません。設計を見直すときです。 まず、 CSV 出力関数を修正し、出力対象のカラム情報を引数で受け取るようにします。 // output_csv_2.ts import { createObjectCsvWriter } from "csv-writer" ; import { Product } from "./products.js" ; const outputCsv = ( products: Product [] , columns: { id: string ; title: string }[] ) : void => { const csvWriter = createObjectCsvWriter ( { path: "./dist/products.csv" , header: columns , } ); csvWriter.writeRecords ( products ) .then (() => { console .log ( "...CSVを出力しました" ); } ); } ; export { outputCsv } ; 呼び出し元のプログラムを修正します。 // main_2.ts import { Product , getProducts } from "./products.js" ; import { User , getUser } from "./users.js" ; import { outputCsv } from "./output_csv_2.js" ; const userId = 1 ; // 1: Alice(管理者), 2: Bob const loginUser: User = getUser ( userId ); // ログインユーザー情報取得 const products: Product [] = getProducts (); // 商品リスト取得 let columns ; if ( loginUser.isAdmin ) { columns = [ { id: "productCode" , title: "商品コード" } , { id: "productName" , title: "商品名" } , { id: "supplier" , title: "仕入先" } , { id: "price" , title: "単価" } , { id: "category" , title: "カテゴリー" } , { id: "notes" , title: "備考" } , ] ; } else { columns = [ { id: "productCode" , title: "商品コード" } , { id: "productName" , title: "商品名" } , { id: "price" , title: "単価" } , { id: "category" , title: "カテゴリー" } , ] ; } outputCsv ( products , columns ); // CSVファイル出力 これで仕様変更要求に対応することができました。 ただ、 if-else による条件分岐は少し気になりますね。 リファクタリング この条件分岐が放つ「不吉な匂い」は、このままでよいでしょうか。 この程度の単純な条件分岐であれば一旦よしとし、次の条件分岐が発生するときに リファクタリング するという考え方もあるでしょう。しかしながら、次の変更時に もリフ ァクタリングが先送りされ、そのときは永久にやってこないということが往々にしてあります。 設計判断は YAGNI 原則によって先送りしますが、 リファクタリング は先送りすべきでない のです。 コントローラーの役割を担うプログラムは、処理フローを記述すべきであって、詳細なルールを記述すべきではありません。そう考えると、条件分岐を記述する場所が適切ではないようです。 条件に応じて CSV レイアウトを決定する役割を分離して、新しい関数を導入します。 // csv_layout_1.ts import { User } from "./users.js" ; type Column = { id: string ; title: string ; } ; const getProductsCsvLayout = ( user: User ) : Column [] => { if ( user.isAdmin ) { return [ { id: "productCode" , title: "商品コード" } , { id: "productName" , title: "商品名" } , { id: "supplier" , title: "仕入先" } , { id: "price" , title: "単価" } , { id: "category" , title: "カテゴリー" } , { id: "notes" , title: "備考" } , ] ; } else { return [ { id: "productCode" , title: "商品コード" } , { id: "productName" , title: "商品名" } , { id: "price" , title: "単価" } , { id: "category" , title: "カテゴリー" } , ] ; } } ; export { Column , getProductsCsvLayout } ; 呼び出し側は次のようにすっきりし、処理フローの見通しがよくなりました。 // main_3.ts import { Product , getProducts } from "./products.js" ; import { User , getUser } from "./users.js" ; import { Column , getProductsCsvLayout } from "./csv_layout_1.js" ; import { outputCsv } from "./output_csv_2.js" ; const userId = 1 ; // 1: Alice(管理者), 2: Bob const loginUser: User = getUser ( userId ); // ログインユーザー情報取得 const products: Product [] = getProducts (); // 商品リスト取得 const columns: Column [] = getProductsCsvLayout ( loginUser ); // CSVレイアウト取得 outputCsv ( products , columns ); // CSVファイル出力 拡張性 拡張性(extensibility) は、ソフトウェアに機能を追加したり、機能を拡張したりすることが容易に行えることを表す品質特性です。 YAGNI 原則は、不確かな推測に基づいた早すぎる拡張性の導入を抑制します。本当にそれが必要となるときまで、拡張性を導入するという設計判断を遅延させるのです。 ただし、建前はそうであっても、実際にはあらかじめ拡張性をもたせた設計をするケースもあります。例えば以下のようなケースです。 今回は見送ったが、将来対応すべき要求として バックログ アイテムが登録されており、具体的な要求もある程度見えている パッケージソフト として、拡張性を持たせておいた方がよいことを経験上わかっている もちろん、先を見越して拡張性を導入することが誤った設計を生んでしまうリスクとの トレードオフ であることを認識する必要はあります。 ふたたび、サンプル 拡張性を先に考慮しておくという設計判断を妥当だと考えたならば、最初の実装段階で、先のサンプルの最終形まで持っていくことは「あり」です。 パッケージソフト における拡張性の重要さについて少し言及しましたので、さらに掘り下げてみましょう。 パッケージソフト の機能を拡張する手段は様々ですが、例えば以下のような方法があります。 設定ファイルによって振る舞いを変更する 設定テーブルによって振る舞いを変更する アドオン開発によって振る舞いを変更する 製本本体の ソースコード に手を入れるモディフィケーションという方法がアドオン開発と呼ばれることもあるのですが、モディフィケーションにはデメリットもあります。Add-onの文字通り、追加モジュールを製品に足す方法が優れていると言えるでしょう。 今回のサンプルコードに以下の要件が与えられたという前提で、対応方法の例を見てみましょう。 アドオン開発によって、出力する CSV ファイルのレイアウトを変更できること アドオンのインターフェース設計 アドオンモジュールを製品に組み込むためには、接合面としてのインターフェースを定める必要があります。 オブジェクト指向設計 ならばその名のとおり「インターフェース」を定義することになりますが、今回は関数型のアプローチで設計しているため、インターフェースの役割となる関数の型を定義しました。 (TypeScriptでは type 文で型名を宣言できますが、関数もその対象となります)。 // csv_layout_2.ts(一部抜粋) type AddonFunction = ( user: User ) => Column [] ; アドオンの実装 アドオンモジュールには、 AddonFunction 型の関数の実体を実装します。製品本体から利用できるように、 default キーワードを付けてエクスポートしています。 // addon.js import { User } from "./users.js" ; import { Column , AddonFunction } from "./csv_layout_2.js" ; const getProductsCsvLayout: AddonFunction = ( user: User ) : Column [] => { return [ { id: "category" , title: "カテゴリー" } , { id: "productCode" , title: "商品コード" } , { id: "productName" , title: "商品名" } , { id: "price" , title: "単価" } , ] ; } ; export default getProductsCsvLayout ; アドオンの呼び出し 今回のサンプルコードでは、es2020のDynamic import機能を用いて、アドオンモジュールを動的に読み込んで関数を呼びだすように実装しました。 // csv_layout_2.ts(一部抜粋) const getProductsCsvLayout = async ( user: User ) : Promise < Column [] > => { const addonModulePath = "./addon.js" ; return import( addonModulePath ) .then ((module) => { // アドオンモジュールが存在する場合はそれを利用 console .log ( "アドオンモジュールを読み込みました" ); return module . default( user ); } ) . catch (( error ) => { // アドオンモジュールが存在しない場合は標準の振る舞い if ( user.isAdmin ) { return [ { id: "productCode" , title: "商品コード" } , { id: "productName" , title: "商品名" } , { id: "supplier" , title: "仕入先" } , { id: "price" , title: "単価" } , { id: "category" , title: "カテゴリー" } , { id: "notes" , title: "備考" } , ] ; } else { return [ { id: "productCode" , title: "商品コード" } , { id: "productName" , title: "商品名" } , { id: "price" , title: "単価" } , { id: "category" , title: "カテゴリー" } , ] ; } } ); } ; サンプルのため、所定の場所に所定のファイル名でアドオンモジュールが格納されていることを前提としています。実際のプロダクトでは、設定ファイルにパスやファイル名を記述したり、管理画面から登録できるようにしたりすることになるでしょう。 マイクロカーネル アーキテクチャ アドオンモジュールを用いた拡張性の実現方法について、要点をまとめると以下のとおりです。 ソフトウェアの機能を拡張できる箇所(拡張点)を定める 拡張点のインターフェースを定義する 拡張点においてアドオンモジュールを読み込んで実行する仕組みを作る これは、 マイクロカーネル アーキテクチャ と呼ばれる アーキテクチャ スタイルです。 まとめ YAGNI 原則に従い、不用意な過剰設計を避け、設計をシンプルに保っておくことは開発のベロシティを向上させます いつで もリフ ァクタリングによって設計を洗練させることができます 必要な場合は、 YAGNI 原則から離れて拡張性を事前に設計することも間違いではありません。つまるところ設計とは常に トレードオフ なのです 執筆: @tyonekubo 、レビュー: @nakamura.toshihiro ( Shodo で執筆されました )
こんにちは。コミュニケーションIT事業部 ITソリューション部の英です。 普段はWebアプリや スマホ アプリの案件などを担当しています。あと、趣味で AI を勉強しています。 XGBoostは 機械学習 で非常に人気のある アルゴリズム の一つで、特に表データにおける予測問題で高い性能を発揮します。 "非 ディープラーニング の手法"の中ではとても高い性能を発揮するとされています。用途としては売上予測や顧客の離脱予測など、分類問題や回帰問題に用いられます。 なんだか難しい言葉がいっぱい出てきました。 今回は初歩から丁寧に解説していこうと思います。まずXGBoostは「eXtreme Gradient Boosting」の略です。 "エクストリーム勾配決定木"、かっこいいですね。 そもそも決定木とは? データを分類を行うためのシンプルで強力な 機械学習 の手法です。質問を繰り返し選択することで答えを導く「 木構造 」のモデルです。入力値を根から投入すると、内部ノード(if文)をたくさんくぐって最終的に1つの葉に落ちます。 アプリに課金するかどうかの決定木(イメージ) 葉の部分を良くみると、最後の分岐で必ず「Yes/No」の2択に落ちていますよね。 古いレコメンドシステムだとこのようにif文をベタ打ちだったりしますよね。 ランダムフォレストとはなにか? 先ほどの決定木では 過学習 (Overfitting)に陥ることがあり、モデルの汎用性が低い状態です。 過学習 とはト レーニン グデータに過剰にフィットしてしまい、検証データで良い性能が出ない状態のことです。 そこで登場したのがランダムフォレストです。複数の決定木を同時に作成し、全ての木の出力結果をもとに最終的な出力が決まります。 このように、複数の学習モデル(分類器や回帰器)を組み合わせて、単一のモデルよりも優れた予測性能を得る 機械学習 の手法をアンサンブル学習と言います。これにより予測の安定性が増し、 過学習 のリスクを抑えることが期待できます。 以下は同じテーマですが、木の構造が先ほどと異なります。このように、最終的なYes / Noへのルートや分岐が木ごとに異なるわけです。分類モデルの場合、最終的な結論(Yes / No)はすべての木が出力する予測結果の多数決で決まります。 勾配決定木とは何か? 先ほどの木を見ていただきたいのですが、「フレンド数 ≦ 3」がNoって少し"適当"だと思いませんか? このように、各Yes / Noは同じ価値ではないわけです。そこで各葉にスコアリングし、決定木が前の決定木の残差(誤差)を逐次的に学習することで、モデルの予測性能を段階的に向上させます。 勾配決定木は、以下の手順で構築されます。 初期モデル : 最初に簡単なモデルを作成する 残差の計算 : 予測値(確率)と正解ラベル(1 / 0)の差(残差)を計算する。 新しい木を作成 : 残差を減らすように新しい決定木を作成する モデルの更新 : 新しい決定木をモデルに追加する 繰り返し : このプロセスを繰り返して、モデルを改善する XGBoostとは何か? 勾配ブースティングの手法を高度に最適化した実装であり、 機械学習 コンペティション や実用的なデータ分析で広く使われている非常に人気のあるライブラリです。そう、ライブラリです。モデルと言っても良いです。 ここから本題 今回はSageMakerのXGBoostを使って分類モデルの学習を行い、k分割交差検証を使って混合行列でモデルの性能を評価してみます。 「 オンラインゲームのユーザーがゲームに課金するかどうか 」を予測させるモデルを作ります。ゲームのプレイ時間や実績の達成状況、年齢などを学習データとして与えてみます。検証をシンプルにするために特徴量は少なめにします。 STEP1:学習データの準備 Python の スクリプト で学習用データの CSV を一括で作成します。10000レコード作成しましょう。 ゲーム時間(hours_played)と実績数(achievement_count)は 正規分布 を使って割合を調整しています。これはゲームにハマっているユーザーと、そうでないユーザーの傾向を表現するためです。完全なランダムで生成すると予測精度に影響が出てしまいます。ゲーム時間と実績数を主成分だと仮定して、これらが多ければpaid(正解ラベル)が1を取りやすくします。係数は適宜調整をしてください。 課金する確率を操作しているだけであるため、ゲームのプレイ時間が長く、実績数が多いユーザーでも 無課金 ユーザーになる可能性があるということです。これは現実でもそうですね。 年齢(age)とフレンド数(friends_count)は課金確率に影響を与えない仕組みになっているため今回はノイズ要素になります。 生成された CSV は以下のようなかたちです。 今回は全体の約38%のユーザーが課金したと仮定したデータになりました。 今回使用するXGBoostは一般的に正規化や 正則化 が不要なモデルのため、前処理を施さずにこのまま学習データとして使用します。 列名 説明 age ユーザーの年齢 hours_played プレイ時間 ※paidの確率に影響 friends_count フレンド数 achievement_count 実績数 ※paidの確率に影響 paid 目的変数 ※1が課金ユーザー、0が 無課金 ユーザー STEP2:各種ライブラリのインポート 以下の通り、各種ライブラリをインポートします。 ※下線は未使用警告なので無視していただいて構いません STEP3:データの読み込みと準備 paidは"課金したユーザーかどうか"を示す正解ラベルですから、yにセットします。それ以外を特徴量としてxにセットします。 STEP4:k分割交差検証の準備 今回は全データを5つのサブセットに分割します。サブセット4つで学習し、残り1つで評価することを5回繰り返します。shuffle(True)にすることで、データの並び順によって生じるデータの偏り、それによる学習結果への影響を抑えることができます。random_stateはシャッフル時の乱数生成のシード値を設定するものです。再現性のために指定しています。 STEP5:検証 以下の通り繰り返し検証を行い、そのたびに推論エンドポイントをdeleteします。推論エンドポイントは放置すると課金されてしまうので、必ず削除するようにしてください。 今回のデータでは課金ユーザーが少数派であり学習結果に影響を与える可能性があるため、SMOTE(Synthetic Minority Over-sampling Technique)を使用してオーバーサンプリングしています。SMOTEは少数派のサンプルとその近傍のサンプルを組み合わせて新しいサンプルを作成し、データのバランスを改善します。 ト レーニン グが開始されました。 結果を待っている間に少しハイパーパラメーターについて解説します。 RandomizedSearchCVを用いることで、最適なハイパーパラメーターを見つけながら、最適なモデルを構築します。 パラメータ 説明 max_depth 各決定木の最大の深さです。モデルの複雑さを制御します。 eta 学習率です。各学習ステップでの重みの更新量を調整して学習の安定性を向上させます。 gamma 分割が必要かどうかを決めるためのノード分割コストに対する 閾値 です。予測の正確性が少なくとも設定値以上改善されない限り、新しい分岐は作りません。 min_child_weight 子ノードが持つべき最小のサンプル数の合計重みです。 過学習 を防ぐために小さなサンプルの分割を抑制します。 subsample 各決定木を構築する際に使用するランダムに選択されたサンプルの割合です。 objective='binary:logistic' バイナリ分類のためのロジスティック回帰を目的関数として設定し、確率的な出力を生成します。 num_round 構築する決定木の数です。 STEP6:評価 confusion_matrixの結果は以下の並びで表示されます。TP(True Positive)が左上にくるイメージがありますよね。TN(True Negative)が左上に来るので注意してください。 Actual / Predicted Negative Positive Negative TN FP Positive FN TP 学習と検証が完了したので評価を表示してみます。 一番性能が良さそうなFold5をピックアップして中身を読み解きましょう。 全体的な精度(accuracy)は70% であり、比較的良好な結果と言えます。 クラス0の適合率が82%で、これはモデルがクラス0を予測する際に正確であることを示しています。 クラス1の適合率が58%と低めであり、これはクラス1を予測する際に誤った予測が多いことを示しています。 クラス1の再現率が77%で、これはモデルがクラス1を見逃す確率が低いことを示しています。 クラス0の再現率が65%と低めであり、クラス0を見逃すケースが一定数存在することを示しています。 全体的に良好な結果ですが、クラス1の適合率が低い ことが分かります。 なぜ、クラス1の適合率が低いのかはテストデータを見れば明らか です。今回は学習用データの作成の際に"ゲームに課金する確率"を操作して、"ゲームに課金する素質がある人"でも課金する(paid=1)確率は約4割、課金しない(paid=0)確率が約6割存在するためです。このような不確実性を持ったデータに対する学習にしては、モデルの品質はかなり良い結果であると考えられます。 機械学習 の分類手法は他にもたくさんあるので、気が向いたら検証してみようと思います。 最後まで読んでいただき、ありがとうございました。 私たちは一緒に働いてくれる仲間を募集しています! <電通×IT>電通グループ基幹システムプロジェクトマネージャー <電通×IT>顧客DX案件プロジェクトマネージャー <電通×IT>クラウドアーキテクト <電通×IT>アプリケーションアーキテクト 執筆: 英 良治 (@hanabusa.ryoji) 、レビュー: @yamada.y ( Shodo で執筆されました )
こんにちは。X(クロス) イノベーション 本部 クラウド イノベーション センターの柴田です。 本記事ではDatadog Logsにてホストタグが継承されない問題の原因と解決方法をご紹介します。 発生した問題 Amazon EKS クラスタ &マネージド型ノードグループを構築し、その上のPodのログをDatadog Logsで収集・管理していました。 あるとき一部のログに AWS Integration 関連のタグ( region 、 availability-zone 、 autoscaling_group 、EC2 インスタンス のタグなど)が付与されていないことがわかりました。 以下は期待通り先述のタグが付与されたログ(以降は正常なログと呼称)です。 { " msg ":" structured log without host attribute " } 以下は期待に反して先述のタグが付与されなかったログ(以降は問題のあったログと呼称)です。 正常なログと比べてタグの件数が少ないことがわかります。 { " msg ":" structured log with host attribute "," host ":" localhost " } 原因 先述の問題の原因は、問題のあったログの host 属性が誤って設定されたせいでした。 ログの host 属性について Datadogの公式ドキュメント ではログの host 属性について以下のように説明しています。 メトリクスで定義された送信元ホストの名前。Datadog で一致したホストから、対応するホストタグが自動的に取得され、ログに適用されます。Agent では、この値が自動的に設定されます。 正常なログの場合 正常なログの host 属性の値はマネージド型ノードグループのEC2 インスタンス の インスタンス IDです。 そのため一致したホストのホストタグがログに適用されます。 問題のあったログの場合 一方、問題のあったログの host 属性の値は localhost です。 Datadog上には localhost に一致するホストが存在しなかったため、ホストタグの継承が行われませんでした。 なぜログの host 属性の値は インスタンス IDではなく localhost だったのでしょうか? Datadog Logsの Pipeline の Preprocessing for JSON logs は以下のとおり設定されています。 そのため、ログが host hostname syslog.hostname のいずれかの属性を含む構造化ログの場合、その値がログの host 属性として設定されます。 問題のあったログは host 属性を含む構造化ログでした。 そのため、ログの host 属性の値が インスタンス IDから localhost へ上書きされていました。 解決方法 先述の問題はログの host 属性をDatadog上のホストと一致するように設定することで解決できます。 具体的には2つの方法が挙げられます。 Pipeline の Preprocessing for JSON logs の Host attributes から問題となっている構造化ログの属性( host )を削除する。 ログから問題となっている構造化ログの属性( host )のキーをリネームまたは削除する。 前者の方法はDatadog Organization全体に影響を及ぼすため、今回は後者の方法を採用しました。 その結果、問題のあったログに対して、期待通り AWS Integration関連のタグが付与されるようになりました。 おわりに 本記事ではDatadog Logsにてホストタグが継承されない問題の原因と解決方法をご紹介しました。 最後までお読みいただき、ありがとうございました。 私たちは一緒に働いてくれる仲間を募集しています! クラウドアーキテクト 執筆: @shibata.takao 、レビュー: @yamada.y ( Shodo で執筆されました )
こんにちは! XI本部 プロダクト イノベーション センター の佐藤です。 先日、社内のとある既存の Java プロジェクト(Gradle プロジェクト)に静的解析ツールの SpotBugs を導入する機会がありました。 『SpotBugs の Gradle プラグイン を追加して、 CI で実行すれば良いだけ。簡単かな』 と高をくくっていたら意外と工夫が必要な取り組みだったので、その知見を共有したいと思います。 なお、本記事は以下の方々を対象とした内容になります。 静的解析の基本について復習しておきたい方 冒頭にて、静的解析の概要についてあらためて説明します 既存プロジェクトに対する静的解析ツールの導入を検討されている方 本記事では SpotBugs を扱いますが、導入時の観点は他のツールにも適用可能です reviewdog の利用を検討されている方 本記事では GitHub Actions における reviewdog の利用例を掲載しています 静的解析とは 概説 実行すべきタイミング 既存のプロジェクトに対して導入する際の課題 要件を満たす運用案 reviewdog に吠えてもらう リファクタリングを推進するもう一吠え 最後に 採用リンク 静的解析とは せっかくなので、はじめに静的解析の特徴を簡単におさらいしておきましょう。 概説 静的解析とは、プログラムを実行する事なく(=静的に)隠れた潜在バグやコーディング 規約違反 を検出する解析手法です。 「プログラムを実際に動かさない」ため、静的解析の解析対象は必然的に ソースコード (あるいは バイトコード )それ自体となります。今回利用した SpotBugs も、解析対象は Java バイトコード です。プログラムの実行環境を整備する必要がない分、実行の容易性が高いと言えます。実際、SpotBugs を含め、ツールによっては IDE 上で気軽に実行可能です。 また、多くの静的解析ツールには、標準のルールセットが用意されています。したがって、例えば動的なテストにおけるテストケース設計のような手間をかける事なく、簡単にプロジェクトに導入する事ができる点も、静的解析の特徴の一つと言えるでしょう。 実行すべきタイミング もう一つ、静的解析ツールの重要な特徴として、開発者へのフィードバックの早さが挙げられます。 これは上述した「実行の容易性」とも関連しますが、コード自体を解析対象とする分、実際に手を動かしている開発者により近い場所から、迅速なフィードバックが可能です。 したがって、静的解析ツールは 開発プロセス のより早い段階で実行するのがベストプ ラク ティスと言えます。 IDE 、あるいは CI に組み込むのが良いでしょう。 今回は、SpotBugs による検出箇所の修正が確実に完了している事を担保するため、CI にて実行しています。 既存のプロジェクトに対して導入する際の課題 ここからが本題です。 既存プロジェクトに対して SpotBugs を導入する場合、「既存のコードに対する検出」をどう扱うかが課題となります。 SpotBugs が検出してくれる問題は、確かに緊急で修正すべきバグや 脆弱性 の場合もあります。 一方で、あくまでベストプ ラク ティスから逸脱しているに過ぎない、プロジェクトの状況次第では今すぐ修正すべきとまでは言えない「 リファクタリング 推奨」程度の問題である事も多いでしょう。 前者の指摘箇所は迅速に改修してしまえば良い(せざるを得ない)のですが、問題は後者です。 今回、私が SpotBugs を導入したプロジェクトは既にある程度開発が進んでおり、かつ規模が大きい事もあって、ざっと 400 件ほど「 リファクタリング 推奨」の検出箇所がありました。 これらの指摘を一気に改修するのは、 工数 的な観点から現実的ではありません。 かと言って、 CI 実行時にそれらの解析結果を無視する事を認めてしまっては、SpotBugs の運用が形骸化してしまう未来が見えてきます。 したがって 要件1:新規コードに対する SpotBugs 検出箇所の改修は必須とする(=SpotBugs の運用を形骸化させない) 要件2:既存コードに対する リファクタリング を徐々に進めていく事ができる これらの要件を満たす運用を考える必要がありました。 要件を満たす運用案 今回、上述の要件を満たすために利用したのが reviewdog というツールです。 reviewdog に吠えてもらう reviewdog は、各種解析ツールの結果を入力値として受け取り、 GitHub や GitLab といったコード ホスティング サービス上のレビューコメントとして出力してくれます。 そして、この優秀な番犬は GitHub Actions と組み合わせる事で解析ツールの実行結果を GitHub Checks のレポートとして出力する事もできるのです。 したがって、Pull Request 時の GitHub Actions による CI において SpotBugs の解析結果を reviewdog に渡す事で、当該 Pull Request の差分上に検出されたバグパターンだけを GitHub Checks で検証する事が可能となります。 なお、reviewdog はメジャーな解析ツールについては標準のパーサーがプリセットされており、 -f オプションでツール名を指定するだけで、解析結果から良い感じの指摘メッセージを生成してくれます。 残念ながら 2024年4月23日 時点で SpotBugs はサポートされていませんが、SpotBugs の出力形式として選択可能な OASIS 標準である SARIF 形式には対応済みのため、今回はこちらを利用しました。 以下、build.gradle および GitHub Actions の ワークフロー定義を掲載します。 plugins { id 'java' id( "com.github.spotbugs" ) version "6.0.12" } // ... 中略 spotbugs { // Pull Request の変更ファイル以外に検出されたバグパターンにより // CI が失敗してしまう事を回避するため、SpotBugs の失敗判定は無視 ignoreFailures = true ; } spotbugsMain { // 出力形式に sarif を指定 reports { sarif { required = true } } jvmArgs = [ '-Duser.language=ja' ] } name: reviewdog_demo on: pull_request jobs: reviewdog: name: reviewdog runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: ref: ${{ github.head_ref }} - name: Set up Java 17 uses: actions/setup-java@v4 with: distribution: corretto java-version: 17 - name: Setup Gradle uses: gradle/actions/setup-gradle@v3 - name: Grant permission for gradlew run: chmod +x ./gradlew - name: Run spotBugsMain run: ./gradlew spotBugsMain - name: Set up reviewdog uses: reviewdog/action-setup@v1 with: reviewdog_version: v0.17.2 - name: Run reviewdog env: REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: > cat ./build/reports/spotbugs/main.sarif | reviewdog -f sarif -reporter=github-pr-check -name run-reviewdog -tee reviewdog が吠えてくれたおかげで、開発者は「 リファクタリング 推奨」である既存コード上の検出は意識せず、自身が変更を加えたコード上の検出だけに集中し、CI 成功を目指すことになります。 これで、上述の要件1を満たす事ができました。 リファクタリング を推進するもう一吠え 先ほどの reviewdog による アノテーション を確認すると、SpotBugs が検出しているはずの赤枠部分のコードには アノテーション が付与されていない事が分かります。 これでは、要件1は満たせていますが、既存コード上の検出は無視され続けてしまいます。 少しずつで もリフ ァクタリングを進めていくためには、たとえ自分が書いたコードでないとしても「自身が変更を加えたファイル上に存在する既存バグパターン」くらいは一緒に修正していく事が望ましいと言えるでしょう。 そこで今回は、 reviewdog の -filter-mode という実行時オプションを利用します。 このオプションは、reviewdog がツールから受け取った解析結果を出力する際のフィルタリング方法を制御するためのフラグです。この値を file に設定する事で「追加あるいは変更されたファイル」に対する解析結果を出力させることができます。 name: reviewdog_demo on: pull_request jobs: reviewdog: name: reviewdog runs-on: ubuntu-latest steps: #... 中略 - name: Run reviewdog env: REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }} # -filter-mode file を指定 run: > cat ./build/reports/spotbugs/main.sarif | reviewdog -f sarif -reporter=github-pr-check -name run-reviewdog -filter-mode file -tee これにより、Pull Request の変更ファイル上のバグパターンはすべて CI で検出される事となり、要件2も良い感じに満たす事ができました。 細かい要望にも答えてくれる reviewdog、ハチ公もびっくりの忠犬ですね 🐶 最後に 本記事では、既存プロジェクトに対する SpotBugs の導入を、reviewdog + GitHub Actions により着実に進めていくノウハウを紹介しました。 「既存コードに対する検出をどう扱うか」という問題は、SpotBugs に限った話ではないと考えています。 本記事のアプローチが、既存プロジェクトに対する解析ツール運用を検討している方々の一助となれば幸いです。 採用リンク 私たちは一緒に働いてくれる仲間を募集しています! 次世代会計プロダクトDevOpsエンジニア(Ci*Xシリーズ) 執筆: @satorin 、レビュー: @miyazawa.hibiki ( Shodo で執筆されました )
こんにちは。コーポレート本部 サイバーセキュリティ推進部の耿です。 Web サービスへの攻撃を防ぐために WAF を使いましょうというのはよく聞きます。 ではインターネットに公開した Web サービスに送信される悪意のあるリク エス トがどれぐらい WAF によって防御され得るのでしょうか? お手軽に使える AWS WAF のマネージドルールを対象に確かめてみました。 この記事の概要 実験用に固定レスポンスを返すだけの HTTP エンドポイントを作成し、約半年間インターネットからアクセス可能な状態で放置した AWS WAF のマネージドルールをアタッチしており、それぞれのルールに一致したリク エス ト数を集計し、ランキング形式にまとめた 一致したリク エス ト数が多いマネージドルールそれぞれに対して、どのようなリク エス トが多かったのか集計し、ランキング形式にまとめた 実験用システムの構成 基本的なデータの集計 ログ総数 国名コード別のリクエスト数 HTTPメソッド別のリクエスト数 それぞれのマネージドルールグループに一致したリクエスト数 一致した数が多い個別のマネージドルール 1位: AWSManagedRulesAnonymousIpList - HostingProviderIPList 2位: AWSManagedRulesKnownBadInputsRuleSet - ExploitablePaths_URIPATH 3位: AWSManagedRulesAdminProtectionRuleSet - AdminProtection_URIPATH 4位: AWSManagedRulesCommonRuleSet - NoUserAgent_HEADER 5位: AWSManagedRulesLinuxRuleSet - LFI_URIPATH 6位: AWSManagedRulesCommonRuleSet - UserAgent_BadBots_HEADER 7位: AWSManagedRulesAmazonIpReputationList - AWSManagedReconnaissanceList 8位: AWSManagedRulesKnownBadInputsRuleSet - ExploitablePaths_URIPATH_RC_COUNT 9位: AWSManagedRulesCommonRuleSet - RestrictedExtensions_URIPATH 10位: AWSManagedRulesAmazonIpReputationList - AWSManagedIPReputationList 11位: AWSManagedRulesCommonRuleSet - GenericLFI_URIPATH まとめ 実験用システムの構成 ALB を主体としたシンプルな構成としました。 AWS 東京リージョンに200番の固定レスポンスを返すだけの ALB を構築した ALB のセキュリティグループは 80 ポートと 443 ポートのみをオープンにし、 0.0.0.0/0 からのアクセスを許可した 独自ドメイン の ACM 証明書を取得し、ALB にアタッチした。ホストゾーンのレコードも ALB を向くように構成した ただし ALB の IP アドレスも、 独自ドメイン 名も特に外部向けに公表していない WAF Web ACL をアタッチし、 AWS マネージドルールグループを複数適用した ただしマネージドルールグループのアクションは全て COUNT にオーバーライドし、ルールに一致してもリク エス トがブロックされないようにした 適用したマネージドルールグループは次の11個である AWSManagedRulesCommonRuleSet AWSManagedRulesAdminProtectionRuleSet AWSManagedRulesKnownBadInputsRuleSet AWSManagedRulesSQLiRuleSet AWSManagedRulesLinuxRuleSet AWSManagedRulesUnixRuleSet AWSManagedRulesWindowsRuleSet AWSManagedRulesPHPRuleSet AWSManagedRulesWordPressRuleSet AWSManagedRulesAmazonIpReputationList AWSManagedRulesAnonymousIpList 全てデフォルトバージョンを適用した。期間中にバージョンがアップデートされている場合がある WAF のログを S3 バケット に出力するよう構成した 補足として、ルールのアクションは全て COUNT にオーバーライドしても、特定のルールに一致した場合はリク エス トに特定のラベルが付与されます。ログに出力された各リク エス トのラベルを確認することで、どのルールに一致したかを判別できるようになっています。アクションは全て COUNT なので、複数のルールに一致した場合には、一致した全てのルールのラベルが付与されます。 2023年10月中旬に稼働を開始し、2024年4月上旬までの約173日間のログを対象に集計を行いました。 基本的なデータの集計 ログ総数 稼働期間中のログ総数と、マネージドルールに一致したログエントリー数をまとめました。 説明 件数 総数に対する割合 ログエントリー総数 153,220 件 100% いずれかのマネージドルールに一致したログエントリー数 115,844 件 76% いずれのマネージドルールにも一致しなかったログエントリー数 37,376 件 24% 記録されていたログエントリー数(リク エス ト数)は153,220件で、稼働期間の173日で割ると約886件/日となります。何も外部に公表していない HTTP エンドポイントでもこれだけのリク エス トがインターネットから届いていることがわかります。 いずれかのマネージドルールに一致したログエントリー数は 115,844件で、全体の約76%にあたります。これだけの割合のリク エス トが、今回利用した AWS WAF マネージドルールのブロック対象となっていたということです。 国名コード別のリク エス ト数 マネージドルールに一致したかどうかに関わらず、全てのリク エス トについて国名コード別の上位10件をまとめました。 順位 国名コード 件数 総数に対する割合 1 US 47,129 件 31 % 2 IL 17,172 件 11 % 3 FR 14,883 件 10 % 4 GB 12,890 件 8 % 5 SG 7,189 件 5 % 6 JP 7,026 件 5 % 7 DE 5,972 件 4 % 8 ID 5,759 件 4 % 9 NL 5,408 件 4 % 10 CN 5,219 件 3 % HTTPメソッド別のリク エス ト数 マネージドルールに一致したかどうかに関わらず、全てのリク エス トについてHTTPメソッド別のリク エス ト数をまとめました。 順位 HTTPメソッド 件数 総数に対する割合 1 GET 129,700 件 85 % 2 POST 19,839 件 13 % 3 HEAD 3,113 件 2 % 4 OPTIONS 565 件 0 % 5 PURGE 2 件 0 % 6 PUT 1 件 0 % GET リク エス トが圧倒的に多く、全体の 85% を占めています。 それぞれのマネージドルールグループに一致したリク エス ト数 適用した11個のマネージドルールグループそれぞれに一致したリク エス ト数と割合をまとめました。注意点として複数のルールに一致した場合には、一致したルール全てに重複して計上されています。 マネージドルールグループ 件数 リク エス ト総数に対する割合 AWSManagedRulesCommonRuleSet 18,286 件 12 % AWSManagedRulesAdminProtectionRuleSet 10,069 件 7 % AWSManagedRulesKnownBadInputsRuleSet 16,081 件 10 % AWSManagedRulesSQLiRuleSet 6 件 0 % AWSManagedRulesLinuxRuleSet 8130 件 5 % AWSManagedRulesUnixRuleSet 23 件 0 % AWSManagedRulesWindowsRuleSet 111 件 0 % AWSManagedRulesPHPRuleSet 243 件 0 % AWSManagedRulesWordPressRuleSet 226 件 0 % AWSManagedRulesAmazonIpReputationList 6,289 件 4 % AWSManagedRulesAnonymousIpList 100,886 件 66 % 大きな差はありますが、いずれのルールグループも一致するリク エス トがありました。目立って多いのは AWSManagedRulesAnonymousIpList ルールグループで、全リク エス トの 66% が一致しました。 一致した数が多い個別のマネージドルール マネージドルールグループの中でも、一致したリク エス ト数が多い個別のマネージドルールを集計しました。 1000件以上のリク エス トが記録された上位11種類を掲載します。 順位 マネージドルールグループ 個別ルール名 件数 リク エス ト総数に対する割合 1 AWSManagedRulesAnonymousIpList HostingProviderIPList 100,706 件 66 % 2 AWSManagedRulesKnownBadInputsRuleSet ExploitablePaths_URIPATH 12,499 件 8 % 3 AWSManagedRulesAdminProtectionRuleSet AdminProtection_URIPATH 10,069 件 7 % 4 AWSManagedRulesCommonRuleSet NoUserAgent_HEADER 8,154 件 5 % 5 AWSManagedRulesLinuxRuleSet LFI_URIPATH 7,654 件 5 % 6 AWSManagedRulesCommonRuleSet UserAgent_BadBots_HEADER 4,958 件 3 % 7 AWSManagedRulesAmazonIpReputationList AWSManagedReconnaissanceList 4,728 件 3 % 8 AWSManagedRulesKnownBadInputsRuleSet ExploitablePaths_URIPATH_RC_COUNT 3,525 件 2 % 9 AWSManagedRulesCommonRuleSet RestrictedExtensions_URIPATH 2,774 件 2 % 10 AWSManagedRulesAmazonIpReputationList AWSManagedIPReputationList 1,493 件 1 % 11 AWSManagedRulesCommonRuleSet GenericLFI_URIPATH 1,363 件 1 % 最多は AWSManagedRulesAnonymousIpList ルールグループの HostingProviderIPList ルールで、全体の 66%を占めています。 2位以下は目立って多いルールはなく、ルールグループも分散している印象です。 続いてそれぞれのルールについて多かったリク エス トをもう少し詳しく集計してみました。 1位: AWSManagedRulesAnonymousIpList - HostingProviderIPList 公式ドキュメント の説明: エンドユーザー トラフィック のソースになる可能性が低いウェブ ホスティング プロバイダーと クラウド プロバイダーの IP アドレスのリストを検査します。IP リストには AWS IP アドレスは含まれません。 このルールに一致したリク エス トのうち、国名コード別にリク エス ト数が多かった5件をまとめました。「割合」列はこのルールに一致したリク エス トに占める割合を示しています。 順位 国名コード 件数 割合 1 US 28,305 件 28 % 2 IL 17,165 件 17 % 3 FR 14,043 件 14 % 4 GB 9,265 件 9 % 5 JP 6,020 件 6 % 2位: AWSManagedRulesKnownBadInputsRuleSet - ExploitablePaths_URIPATH 公式ドキュメント の説明: URI パスに、悪用可能な ウェブアプリケーション パスにアクセスする試みがないかを検査します。パターンの例には、web-inf などのパスがあります。 このルールに一致したリク エス トのうち、リク エス トURLパス別にリク エス ト数が多かった10件をまとめました。 順位 リク エス トURLパス 件数 1 /.env 3,328 件 2 /. aws /config 154 件 3 / api /.env 96 件 4 / aws /credentials 94 件 5 /laravel/.env 77 件 6 /[具体的な IPアドレス ]/.env 72 件 7 /.env.prod 68 件 8 /app/.env 63 件 8 /.env.production 63 件 8 /docker/.env 63 件 機密情報が記録されている可能性がある .env 関係のファイル、 AWS の configや機密情報が格納されているファイルを取得しようとしているリク エス トが上位を占めました。 6位は具体的な IPアドレス が記録されており、リク エス トが行われた当時のALBに割り当てられた IPアドレス だと思われます。 3位: AWSManagedRulesAdminProtectionRuleSet - AdminProtection_URIPATH 公式ドキュメント の説明: 一般的にウェブサーバーまたはアプリケーションの管理用に確保されている URI パスの有無を検査します。パターンの例には、sqlmanager などがあります。 このルールに一致したリク エス トのうち、リク エス トURLパス別にリク エス ト数が多かった10件をまとめました。 順位 リク エス トURLパス 件数 1 /admin-app/.env 281 件 2 /admin/phpinfo. php 210 件 3 /boaform/admin/formLogin 182 件 4 /admin. php 172 件 5 /admin/.env 141 件 6 /linusadmin-phpinfo. php 97 件 7 /admin/index.html 88 件 8 / phpmyadmin /index. php 77 件 9 /admin/info. php 74 件 10 /admin/config. php 73 件 さまざまな admin を含むパスへのリク エス トが一致していることがわかります。 4位: AWSManagedRulesCommonRuleSet - NoUserAgent_HEADER 公式ドキュメント の説明: HTTP User-Agent ヘッダーが欠落しているリク エス トを検査します。 User-Agent ヘッダーがないリク エス トなので、別観点での集計は割愛します。 5位: AWSManagedRulesLinuxRuleSet - LFI_URIPATH 公式ドキュメント の説明: リク エス トパスに、 ウェブアプリケーション のローカルファイル インクルージョン (LFI) の 脆弱性 を悪用する試みがないかを検査します。パターンの例には、攻撃者に オペレーティングシステム の情報を提供できる /proc/version などのファイルがあります。 このルールに一致したリク エス トのうち、リク エス トURLパス別にリク エス ト数が多かった10件をまとめました。 順位 リク エス トURLパス 件数 1 /wp-config. php .bak 102 件 1 /. vscode /sftp. json 102 件 3 /wp-config. php .backup 76 件 3 /wp-config.txt 76 件 3 /wp-config.old 76 件 6 /admin/config. php 73 件 7 /app/config/parameters.yml 61 件 8 / phpMyAdmin /scripts/setup. php 60 件 9 /wp-config. php -backup 59 件 10 /wp-config. php .old 53 件 1位の /wp-config.php.bak は WordPress の設定ファイルのバックアップを取得しようとするリク エス トだと思われます。 2位の /.vscode/sftp.json は FTP や SFTP でファイル転送を行う VSCode の拡張の設定ファイルで、パスワード情報が記録されている可能性のあるものです。 全体として設定情報や機密情報の窃取を目的としているリク エス トが多く一致しているようです。 6位: AWSManagedRulesCommonRuleSet - UserAgent_BadBots_HEADER 公式ドキュメント の説明: リク エス トが不正なボットであることを示す一般的な User-Agent ヘッダー値を検査します。パターンの例には、nessus、nmap などがあります。 このルールに一致したリク エス トのうち、 User-Agent ヘッダー別にリク エス ト数が多かった順にまとめました。 一部の User-Agent ヘッダーには URL リンクが付与されていましたが、改変し削除しました。 順位 User-Agent ヘッダー 件数 1 Mozilla /5.0 zgrab/0.x 3,207 件 2 Mozilla /5.0 (compatible; Nmap Scripting Engine;) 1,519 件 3 Fuzz Faster U Fool v2.1.0-dev 70 件 4 masscan/1.3 65 件 5 ivre-masscan/1.3 30 件 5 masscan/1.0 30 件 7 Fuzz Faster U Fool v2.0.0-dev 21 件 8 masscan-ng/1.3 14 件 1位の Mozilla/5.0 zgrab/0.x は 脆弱性 スキャンツールによるリク エス トに付与される User-Agent ヘッダー値のようです。 他は主にポートスキャンツールによるリク エス トに付与されるヘッダー値のようです。 7位: AWSManagedRulesAmazonIpReputationList - AWSManagedReconnaissanceList 公式ドキュメント の説明: AWS リソースに対して偵察を実行している IP アドレスからの接続を検査します。 このルールに一致したリク エス トのうち、国名コード別にリク エス ト数が多かった5件をまとめました。「割合」列はこのルールに一致したリク エス トに占める割合を示しています。 順位 国名コード 件数 割合 1 FR 2,853 件 60 % 2 US 578 件 12 % 3 BR 363 件 8 % 4 DE 294 件 6 % 5 HK 216 件 5 % 8位: AWSManagedRulesKnownBadInputsRuleSet - ExploitablePaths_URIPATH_RC_COUNT 2位と同じ現象だと思われるので割愛します。 AWS WAFでは新しいマネージドルールのリリース候補に対し、テストの期間を設けて RC_COUNT を名前に付加して稼働しているそうです。 9位: AWSManagedRulesCommonRuleSet - RestrictedExtensions_URIPATH 公式ドキュメント の説明: 読み取りや実行が安全でないシステムファイルの拡張子が URI パスに含まれているリク エス トを検査します。パターンの例には、.log や .ini などの拡張子があります。 このルールに一致したリク エス トのうち、リク エス トURLパス別にリク エス ト数が多かった12件をまとめました。 順位 リク エス トURLパス 件数 1 /.env.bak 148 件 2 /wp-config. php .bak 102 件 3 / php .ini 88 件 4 /wp-config. php .backup 76 件 5 /.env.backup 69 件 6 /.config 48 件 7 /env.backup 34 件 8 /wp-config.bak 32 件 9 /wp-config.backup 30 件 9 /wp-config.conf 30 件 9 /wp-config-sample. php .bak 30 件 9 /wp-config.cfg 30 件 他のルールにも一致しているリク エス トパスも含まれていました。 .env 関係のファイルや WordPress 関係のファイルの取得が多く一致している印象です。 10位: AWSManagedRulesAmazonIpReputationList - AWSManagedIPReputationList 公式ドキュメント の説明: ボットとして識別された IP アドレスを検査します。 このルールに一致したリク エス トのうち、国名コード別にリク エス ト数が多かった5件をまとめました。「割合」列はこのルールに一致したリク エス トに占める割合を示しています。 順位 国名コード 件数 割合 1 ID 878 件 59 % 2 US 144 件 10 % 3 FR 125 件 8 % 4 GB 98 件 7 % 5 AU 62 件 4 % 11位: AWSManagedRulesCommonRuleSet - GenericLFI_URIPATH 公式ドキュメント の説明: URI パスに、ローカルファイル インクルージョン (LFI) を悪用する形跡がないかを検査します。例には、../../ などの手法を使用した パストラバーサル の試行があります。 このルールに一致したリク エス トのうち、リク エス トURLパス別にリク エス ト数が多かった順にまとめました。 順位 リク エス トURLパス 件数 1 /. aws /credentials 1287 件 2 /. aws /credentials.js 19 件 2 /root/. aws /credentials 19 件 2 /home/. aws /credentials 19 件 5 / api /v1/totp/user-backup-code/../../system/system-information 11 件 6 /rjdi../meme.1 4 件 7 /static../.git/config 3 件 8 /media../.git/config 1 件 AWS の機密情報が記録されているファイルを窃取しようとするリク エス トが多く一致していました。 まとめ どれぐらいの悪意があったのかは判断しませんが、全リク エス トの76%が適用したいずれかの WAF マネージドルールに一致していました。 集計の上位にあたるリク エス トは明確に機密情報ファイルの窃取を狙っているものが多数あり、 AWS WAF のマネージドルールを利用するだけでもそのうちのかなりの割合をブロックできると考えると、インターネットからアクセスできるWebエンドポイントを公開する場合には ( AWS WAFに限らず) WAF の適用をぜひ検討したほうが良いでしょう。 AWS WAFについては、過去にもいくつか記事を執筆しているのでご参照いただけますと幸いです。 AWS WAFがリクエストをブロックした時に、自動的にIPアドレスレベルで一定期間ブロックし続ける方法 AWS WAF について最初から知りたかったこと8選 AWS WAF マネージドルールグループの個別ルールに条件を追加する方法 私たちは一緒に働いてくれる仲間を募集しています! キャリア採用 クラウドアーキテクト 執筆: @kou.kinyo 、レビュー: 寺山 輝 (@terayama.akira) ( Shodo で執筆されました )
こんにちは。CIT事業部の石際です。 LINE周辺のシステム構築やカスタマーサポート領域のシステム導入などを担当しています。 LINEをカスタマーサポートのチャネルとして利用している企業は多いですが、まだまだ電話問い合わせのチャネルとして利用しているケースは少ないです。 そこで今回は、LINEからコンタクトセンターへ電話ができる「LINEコールPlus」というサービスについて、 LINEコールPlusの 利用 ユースケース や 顧客体験 などもあわせてご紹介します! 1. LINEコールPlusとは LINEコールPlusの仕組み 2. システム構成のパターン ① 複数の既存の問い合わせ窓口に転送 ② 複数の電話番号にルーティング ③ ソフトフォンで電話応対 ④ コールセンターシステムとの連携 3. LINEコールPlusを利用して顧客体験を向上させる 発信用URLに追加できるパラメーター ビジュアルIVR 4. まとめ 1. LINEコールPlusとは 「LINEコールPlus」は、LINE公式アカウントの 有料オプション 機能で、ユーザーがLINE公式アカウントから無料でカスタマーサポートに電話できるサービスです。 LINEコールPlusの仕組み LINEとPBXなどの音声通信基盤を接続することで、LINEアプリからコンタクトセンターへの着信が可能になります。 LINEから発行された発信用URLにユーザーがアクセスすることで通話を開始します。 発信時には、LINE公式アカウントの友だち追加(ブロック解除)が確認され、通話が開始されます。 音声通信基盤は各社さまざまな製品・サービスがありますが、これまでの経験から、導入コストが低く拡張性に富んだTwilioを採用しています。 コンタクトセンタープラットフォーム「Twilio Flex」 | ソリューション | 電通総研 これから説明する ユースケース は全てTwilioの利用を想定しています。 2. システム構成のパターン 「ただLINEと電話を繋ぐだけでしょう?」と思うかもしれませんが、それぞれの企業により、お問い合わせ窓口の体制や運用状況は異なります。 各々の要件を満たすためには、単純に繋ぐだけでなく音声通信基盤側のカスタマイズが欠かせません。 具体的にどのようなシステム構成、カスタマイズパターンがあるか、要件に応じて解説します。 ① 複数の既存の問い合わせ窓口に転送 LINEコール(無料機能)では、1つの電話番号のみに転送が可能でした。 しかし、LINEコールPlusを利用すれば、複数の電話番号に転送できます。 IVR(Interactive Voice Response:自動音声応答システム)で要件を確認すれば、複数の窓口があってもそれぞれに転送できます。 既存のコンタクトセンターを大きく変えることなく、新たなチャネルとしてLINEを追加できるのが大きなメリットです。 ② 複数の電話番号にルーティング TwilioのACD(Automatic Call Distribution:着信呼自動分配装置)機能を使って、複数の電話番号にルーティングします。 最初の電話番号と通話ができなければ、次の電話番号にコールするなどコールフローの設計が可能です これは、コールフローを新たに作るので、LINEを中心とした問い合わせ窓口を新設する場合などに有効です。 ③ ソフトフォンで電話応対 電話番号への転送ではなく、オペレーターがソフトフォン(Twilio Flex )で受電し、通話します。 ※ Twilio Flex とは? クラウド 型コンタクトセンタープラットフォーム。 電話、チャット、SMSなどマルチチャネルに対応し、オペレーターの画面のUIやIVR、ACDなどのフルカスタマイズが可能です。 TwilioのACD機能を使って、オペレーターに電話を割り当てます。 オペレーターは Flex の画面から着信を確認し、通話します。 Flex は クラウド サービスのため、オペレーターは 在宅ワーク など場所を問わず働くことが可能です。 また、電話番号はTwilioで取得するため、電話回線を準備する必要もなく、コストを抑えられるメリットもあります。 ④ コールセンターシステムとの連携 Twilio Flex は、コンタクトセンターシステムと CTI (Computer Telephony Integration)連携することも可能です。 他のチャネルでのカスタマーサポートと同時に、LINEからのお問い合わせも管理できます。 このように柔軟な設計が可能なのが、LINEコールPlusとTwilioの最大の魅力です。 規模やLINEというチャネルの位置づけに応じた設計が重要となります。 3. LINEコールPlusを利用して顧客体験を向上させる 次に、ユーザーの視点からどのような価値を提供できるかを考えてみましょう。 もちろん、ユーザーにとって最大のメリットは、馴染みのあるLINEから無料で電話ができることです。 その他にLINEコールPlusを利用した場合、どのような顧客体験を提供できるでしょうか。 以下に少し発展的な方法をご紹介します。 発信用URLに追加できるパラメーター LINEコールPlusでは、ユーザーが発信用URLをクリックすることで通話を開始します。 この発信用URLには「パラメーター」を追加できます。 パラメーターを利用することで、カスタマーサポートをよりシームレスかつ効率的に行うことができます。 ビジュアルIVR IVRの音声ガイダンスを聞いてボタンを押すのは面倒なことがあります。 しかし、チャットまたはWeb画面から要件を選択し、その選択を発信用URLの「パラメーター」に入れて渡すことで要件に応じた電話のルーティングが可能になります。 これにより、ユーザーはIVRによるストレスから解放されます。 4. まとめ いかがでしたか。 LINEコールPlusの利用は、事業者にもユーザーにも多くのメリットがあります。 個人的には、 海外の日本人向けの問い合わせ窓口 LINE利用者層を対象とした問い合わせ窓口 などが非常に有効だと思います。 チャネルが充実しコンタクトセンター内の電話の件数が減っていると言われている今でも、一定の需要は存在します。 そして、電話、チャット、Webアプリなどが使えるLINEは、カスタマーサポートの領域で顧客体験(CX)の向上に貢献してくれるでしょう。 弊社のLINEコールPlusのソリューションページには導入時に必要な手続きなども記載していますので よろしければ合わせてご覧ください。 www.dentsusoken.com 最後までお読みいただき、ありがとうございました。 私たちは同じ事業部で共に働いていただける仲間を募集しています! みなさまのご応募、お待ちしています! <電通×IT>電通グループ基幹システムプロジェクトマネージャー エンタープライズ向けDX推進リーダー/エンジニア <電通×IT>クラウドアーキテクト <電通×IT>アプリケーションアーキテクト 製品・プラットフォーム開発エンジニア 執筆: @ishigiwa_yumi 、レビュー: @miyazawa.hibiki ( Shodo で執筆されました )
こんにちは。 電通 総研X イノベーション 本部プロダクト イノベーション センターの中山です。好きなものは自動化、嫌いなものは属人化です。 この記事では、私の所属するチームがとある課題を解決するために開発したアプリケーションについて、課題の発見からアプリケーションの内容、開発にあたって重要視した価値観について掲載します。 背景(IaCの素晴らしさについて) 電通 総研では様々なパッケージ製品を展開し、それらを様々な業界のお客様にお使い頂いています。 特に大企業のお客様にご利用頂く場合、パッケージ製品の標準機能にお客様専用のアドオンを付与し、インフラについても専用の環境を求められるケースが多くあります。 有難いことに数多くのお客様に製品をご利用頂いている 電通 総研としては、お客様ごとに専用となるインフラ環境を構築し、管理する必要が生じるわけです。 インフラを構築する方法として、最も原始的なのは、手順書を作成し、それに倣って構築を実施する方法です。 この方法は特別なスキルを必要とせず、チームへの導入が容易で、比較的属人化が発生しにくいという利点があります。一方で、人間が手順書を見ながら慎重に作業を行う必要がありますので、時間や 工数 などのコスト効率に課題があります。また、人間は必ずミスを犯してしまうという前提に立つと、作業ミスのリスクもあり、作業品質面にも課題があります。 この問題を解決するために、インフラをコード化して管理するInfrastructure as code(IaC)が近年広く使われるようになっています。代表的な技術として、 Terraform 等があります。 IaCは個人的にとても大好きな技術です。従来の手順書を用いてインフラを構築していた時代には、どのような作業を実施し、どのようなインフラを構築したか、といった作業履歴を残すのは大変な作業でした。ドキュメントの記述の粒度やルールを統一することには限界があり、作業者の性質にドキュメントの品質が左右され、運用が長期化するほどに、ドキュメントの信憑性が低下してしまうことは避けられない課題でした。 IaCの登場により、その状況は一変します。コードに厳密に定義した内容を入力として、インフラを出力するIaCにおいては、最新のコードの内容が、最新の環境を表す証拠そのものになります。コードはGit等のバージョン管理ツールを用いて管理することが可能なので、コードの変更履歴、すなわち、インフラの変更履歴を正確に管理できます。人間と違って、コードは嘘をつけません。このドキュメントは本当にちゃんと正確に更新され続けているのだろうか...等と悩む必要はなくなったのです。 属人化という課題 電通 総研でも、様々なプロジェクトにIaCが導入されています。その中で、一つの課題が見えてきました。 IaCは非常に強力なツールですが、うまく標準化できないと、その資産が構築した技術者に依存してしまうということが発生します。例えば、「新規にインフラ環境を追加する」、「既存のインフラ環境の設定を少し更新する」、といった様々な作業が、下手をすると一人の技術者に依存し、その人でなければ作業が実施できない、といった状況へと陥ります。 世はまさに、超人手不足時代。業績を伸ばし続けている 電通 総研にとってもそれは例外ではなく、増え続ける案件の数に対して、IaCに対応できる技術者の数は限られています。そのようなある意味貴重な技術者に、運用で発生するあらゆる作業が依存されることは、本人にとっても周囲にとっても不幸なことです。プロジェクトにとってもリスクとなり得る、避けるべき事態です。 解決に向けて そこで我々は、「新規にインフラ環境を追加する」、「既存のインフラ環境の設定を更新する」といった運用で頻繁に発生し得る作業を、シンプルなアプリケーション操作で実行可能となるよう標準化することで、この問題の解決へと乗り出しました。 頻発する運用作業がシンプルな GUI 操作で実行可能になることで、属人化の問題を防止することに加え、このアプリケーションを導入することで、抽象化されて用意されている手順に従うだけで、高効率・高品質な作業プロセスが実現可能となるというところに、大きな価値があります。 加えて、これまでは別で管理していた管理運用に関わる情報を、こちらのアプリケーションに集約することも可能になります。 開発方針 このアプリケーションの開発に際して最も重視したのは、「まずは PMF (Product Market Fit)を確かめることに注力する」ということです。時間をかけて機能を作り込んでも、それらがユーザーにとって価値がなく、まったく使ってもらえないものとなってしまったら意味がありません。そこで我々は、「低コストかつ短期間で必要最低限の機能を備えた製品を開発し、実際にユーザーに使ってもらい検証し、学びを得ながら製品を磨いていく」という リーンスタートアップ の考え方を取り入れてプロダクトを開発しています。ユーザーの反応を的確に取得して、より満足されるサービスに仕立てていくということが、何よりも重要なことです。 まとめ 非常に簡単にではありますが、我々のアプリケーション開発についてご紹介しました。 開発方針としてもご紹介した通り、我々の製品はユーザーの声を大切にしながら、日々進化を続けています。完成するアプリケーションも、それを支えるプロセスも、日々変化していくことを楽しみながら、これからも開発を継続していきたいと思います。 私たちは一緒に働いてくれる仲間を募集しています! フルサイクルエンジニア 執筆: @nakayama.seiya2 、レビュー: @nakamura.toshihiro ( Shodo で執筆されました )
みなさん、こんにちは。 電通 総研 金融事業部の鈴木です。 最近業務で AWS を触る機会がなくなり、復習したいと考えていたところ「 AWS Cloud Quest 」というサービスを見つけました。 2024年3月にSolution Architect用のコンテンツが日本語対応したという AWSブログ の記事を読み、存在を知りました。 今回はこの「 AWS Cloud Quest」を利用して AWS の基本サービスを復習してみて、良かった点・気になった点やどんな人にオススメかを紹介します。 AWS Cloud Quest とは? 利用方法 実際に試してみた 1.学習 2.計画 3.実践 4.DIY 完了報告 良かった点 気になった点 どんな人にオススメか? まとめ AWS Cloud Quest とは? AWS Cloud Questとは、実用的な AWS Cloud スキルを身につけるための唯一の3D ロールプレイングゲーム です。( AWS より引用) つまり、ゲームを通して AWS の実用的な知識と経験を積んでいくための学習サービスです。 学習者はロールと呼ばれる学習レベル・分野を選択し、 クラウド スキルを身につけながら仮想都市の市民の困りごとを解決します。 ロールには、Cloud Practitioner、Solution Architect、サーバーレス デベロッパ ー、 機械学習 スペシャ リスト、セキュリティ スペシャ リストなどがあります。 現在日本語に対応しているコンテンツは、Cloud Practitioner、Solution Architectの2つです。 また、Cloud Practitionerのみ無料で学習できます。 今回は、このCloud Practitionerを利用して学習します。 利用方法 AWS Cloud Questを利用するために、まずは AWS Skill Builder のサイトにアクセスします。 フィルターから「Training Category > Game-Base Learning」にチェックを入れ、表示された「 AWS Cloud Quest: Cloud Practitioner (Japanese) 日本語版」をクリックします。 表示されたサービスが「 AWS Cloud Quest: Cloud Practitioner (Japanese) 日本語版」で「Free」であることを確認し、「ENROLL」をクリックします。 するとサインインが求められるため、「 SIGN IN AWS SKILL BUILDER」をクリックして、サインイン画面に進みます。 AWS Cloud Questの利用には、無料のアカウント登録が必要のためアカウントを登録します。APN( AWS Partner Central)アカウントをお持ちの方はAPNアカウントでもログイン可能です。 ログイン後、画面中央に表示されるコンテンツをスクロールし、「ゲームを開始する!」をクリックしてください。 別タブでゲーム画面が開きます。 初期の言語設定が英語になっているため、以下画像のように「歯車マーク→言語→日本語」を選択して、言語を日本語に設定してください。 (日本語版を選択したのにデフォルトは英語なんですよね…) 言語設定ができましたら、トップ画面の「ゲーム開始」をクリックしてゲームを開始してください。 実際に試してみた ゲームを開始すると、ストーリーの説明が入ります。 AWS を活用して街の課題を解決していくのが目的のようです。 しばらく進んでいくと、 アバター の作成に移ります。身体、顔、服装などが設定できます。自分の好みで作成してみてください。 そのまま進めると、困りごとを抱えた人に出会います。これらの課題を ヒアリ ングし、 AWS を用いてどのように課題解決するかを考えていきます。 以下の画像ではイン フラリ ソースのプロビジョニングに関する課題を抱えており、 Amazon S3 を利用して静的Webサイトをデプロイすることで解決しようとします。 AWS Cloud Questでは、上記のような依頼者とのやり取りの後、「1.学習」「2.計画」「3.実践」「4. DIY 」の順で学習を進めていきます。 1.学習 まずは学習です。今回構成する アーキテクチャ の概要や、サービスについて学習します。 それぞれ英語ではありますが解説動画がついており、「3.実践」で実際にコンソールを触る前に、課題で利用するサービスの概要やベストプ ラク ティスを学習できます。 2.計画 次に計画です。今回構築する アーキテクチャ について全体像を理解し、何をすべきかを把握します。 この アーキテクチャ 構成や、やるべき操作を事前に理解しておくことで学習の理解度が上がります。 3.実践 ここまで来ましたら、実際に AWS のコンソールを操作してシステムを構築します。 左上の「 AWS コンソールを開く」をクリックすることで、課題用の環境が AWS 側でプロビジョニングされてコンソール画面から利用できます。 学習用に別途 AWS アカウントを用意する必要がなくてとても快適です。 これによってプロビジョニングされる環境では、課題に関係のないサービスにはアクセスできないようになっているとのことです。 しかし、事故を防ぐためにも画像赤枠のステップにある手順や、課題解決に関係ある操作から逸脱したことはしないように心がけましょう。 画像赤枠のステップに沿って操作をしていくと、課題解決に必要な知識・操作を身につけることができます。 ステップを進めていくと、コンソール画面の操作について説明や解説を表示してくれます。 4. DIY 最後に DIY です。「3.実践」のコンソール画面から課題を達成します。 DIY では「3.実践」のようなステップはありませんが、これまでの知識を用いてシステムを構築していきましょう。 左上の「 AWS コンソールを開く」をクリックしてコンソール画面を開きます。 課題をクリアするために、以下画像の赤点線枠を参考に AWS リソースの設定をしてシステムを構築します。 システムの構築が完了したら検証します。 指定された内容を入力して、「検証」をクリックすることで正しく設定できているかを確認してもらえます。 (以下の画像は設定が間違っていたため検証エラーになっており、何がよくなかったのかを教えてくれています) 正しく設定できた場合、「お疲れ様でした」のメッセージが表示されます。 正しくシステム構築できたため、依頼主に報告して完了という流れになります。 完了報告 課題を解決したら依頼主に報告します。 お礼に住んでいる街を発展させることができます。結構テンション上がりますね。 以上のような流れで、困りごとを抱えた人の課題を AWS で解決していくことで AWS の知識と実践的な経験を積むことができます。 良かった点 AWS Cloud Questの良かった点をまとめていきます。 総じて良い学習体験を得られましたが、特に以下の点が良いと思いました。 ①課題ごとに学習環境を提供してくれるため、自分で学習環境をセットアップする手間を省ける。 「実際に試してみた」の章で紹介した通り、 AWS Cloud Questではボタンひとつで学習環境がプロビジョニングされて利用できます。 別途 AWS アカウントを用意せずとも、ワンクリックで10秒程度で環境が提供されるため、学習ハードルはとても下がります。 通常 AWS の学習をする場合、 AWS アカウントを作成して無料枠を気にしながら学習する場合が多いのではないでしょうか。 この環境は、課題が終了するとアクセス権がなくなり、自動的に破棄されます。 そのため、リソースの削除し忘れによる課金を心配することもありません。 (EC2などの止め忘れとかでエライ目を見る心配がなくて良いですね) ②コンソール画面の操作解説が丁寧で、実際にコンソール画面を操作しながら学習できる。 「実際に試してみた」の章で紹介した通り、課題をクリアするために必要な操作は丁寧に解説されます。 特に初学者に対して、ハンズオン形式でコンソール画面を操作できるため、安心して操作できると思います。 ③ AWS リソースを設定後、設定内容が正しいかを AWS 側で簡単に検証してくれる。 「実際に試してみた」の章で紹介した通り、設定したリソースの設定内容が正しいかどうかを AWS 側で検証してくれます。 紹介した例では、S3の バケット 名を入力することで検証されます。 学習用に自分で環境構築して AWS リソースを設定した場合、意図した設定になっているかを検証することは、時として大変なことがあるかと思います。 どのように検証するかを自分で考えることに学びがある場合は多々ありますが、検証を簡単にできることは学習ハードルを下げる面でメリットだと思います。 気になった点 AWS Cloud Questの気になった点は、特にありませんでした。 強いていうなら主人公の アバター が少し怖いところでしょうか…? どんな人にオススメか? AWS Cloud Questは以下のような人たちにオススメしたいです。 AWS について勉強したいが、どこから勉強して良いかわからない人 コンソール画面を実際に触って学習したい人 今回はCloud Practitionerを触ったということもありますが、 AWS 初学者には入門教材としてとても良いのではないかと思います。 AWS 環境を自前で用意する必要がなく、Cloud Practitionerについては無料でコンソールを操作しながら学習できます。そのため、「従業員に手を動かしながら AWS を学習してほしいが、 AWS 環境を用意するのが難しい」という課題にもマッチしているのではないでしょうか? まとめ 今回は AWS Cloud QuestのCloud Practitionerを利用して、 AWS の基本サービスを復習してみました。 ゲーム形式と言いつつ、 AWS のコンソール画面を操作しながら学習できるため学びが多いと感じました。 「良かった点」の章でも紹介した通り、 AWS の学習ハードルを下げつつ、コンソール画面を操作しながら学べるというのが強みだと思います。 もちろん AWS Cloud Questの範囲から外れる内容がある場合や自由に AWS サービスを触りたい場合は、自前の AWS アカウントで学習するのが良いと思いますが、 AWS を学習する上で選択肢の一つには入れても良いのではないかと思いました。 機会があれば、Cloud Practitioner以外も試してみたいと思います。 私たちは一緒に働いてくれる仲間を募集しています! 【金融×IT】システム アプリケーション・アーキテクト 【金融×IT】クラウドアーキテクト ◎技術力を備えたリーダを目指せる フルサイクルエンジニア 執筆: @suzuki.takuma 、レビュー: @handa.kenta ( Shodo で執筆されました )
こんにちは、 電通 総研、X イノベーション 本部 AIトランスフォーメーションセンター所属の徳原光です。 普段は、主に Python でPandasを使ってデータ分析やAI学習・推論のための特徴量を生成を行っていますが、最近KotlinでKotlin DataFrameを操作してデータ処理を実装する機会が増えました。 これまでローカル上で開発してきた 機械学習 モデルをONNXを使ってアンドロイド スマホ で利用するために、 スマホ 上で動く前処理のコードを実装しているのですが、Kotlinを開発しているJetBrains社が開発しているKotlin DataFrameはデータサイエンスに必要な基本的な処理をサポートしているので非常に有効なライブラリですね。 ただ今年に入るまで、Kotlin DataFrameはおろかKotlinさえも書いたことがなかったので、 Python やPandasとの使用感の違いに戸惑いましたね。そこで、早く慣れるために、UdemyでKotlinの基礎を学びつつ、PandasとKotlin DataFrameの操作の対応表を実装時の チートシート として 公式ドキュメント から作成しました。 日ごろPandasの操作に慣れていると、一覧になっているPandasの処理からKotlin DataFrameの処理を引けると便利ですね。毎回DataFrameやDataColumn、それらが提供するメソッドの仕様を調べるより、実装速度が格段に上がりました。 Pandas ⇔ Kotlin DataFrame対応表 今回の実装で自分が使用した処理から(Pandas的に)基本的なものを抜粋しています。無理やり表にしているので改行が変ですがご容赦ください。 処理 pandas Kotlin DataFrame ドキュメント データフレーム生成 df = pd.DataFrame({ "A": [2, 4, 6, 8, 10], "B": [3, 6, 9, 12, 15], "C": [5, 10, 15, 20, 25], "G": ["a", "b", "c", "a", "b"] }) var df: DataFrame = dataFrameOf("A", "B", "C", "G")( 2, 3, 5, "a", 4, 6, 10, "b", 6, 9, 15, "c", 8, 12, 20, "a", 10, 15, 25,"b") https://kotlin.github.io/dataframe/createdataframe.html データフレーム読み込み df = pd.read_ csv ('hogehoge. csv ') var df:DataFrame = DataFrame.read("hogehoge. csv ") https://kotlin.github.io/dataframe/read.html 冒頭表示 df.head() df.head()   データフレームを縦に結合 pd.concat([df1, df2], axis=0) listOf(df1, df2).concat()df1.concat(df2) https://kotlin.github.io/dataframe/concat.html 列を取得 df[”A”] df.getColumn {A} https://kotlin.github.io/dataframe/getcolumn.html 行を取得 df.loc[0] df[0]df[0..0](データフレームとして取得) 行をforループで一づつ取得 for index, row in row_df.iterrows(): print(row["A"]) df.forEach { println(it["A"]) } https://kotlin.github.io/dataframe/iterate.html 行数を取得 len(df) df.rowsCount() https://kotlin.github.io/dataframe/rowscount.html 条件にあうデータを抽出 df.loc[df["G"] == "a", :] df.filter{ G == "a"} https://kotlin.github.io/dataframe/filter.html カラムの値を更新 df[”A”] = df[”A”] - 2 df = df.update{ A }.with{it -2} https://kotlin.github.io/dataframe/update.html カラムの値をDataColumnで取得 df[”A”] df.getColumn {A}df[”A”] https://kotlin.github.io/dataframe/getcolumn.html カラム名 のリストに該当するカラムを選択 list_col = ["A", "B"] df[list_col] val list_col = listOf("A", "B") df.select(*list_col.toTypedArray()) https://kotlin.github.io/dataframe/select.html カラム追加 df['D'] = 1df['D'] = df['C'] - df['B'] df.add("D") { 1 }df.add(”D”) { C - B } https://kotlin.github.io/dataframe/add.html カラムを削除 df. drop ("A") df.remove( "A" ) https://kotlin.github.io/dataframe/remove.html カラム名 を変更 df.rename(columns={'A': 'K'}) df.rename { A }.into( K ) https://kotlin.github.io/dataframe/rename.html 列の値を編集 df[”A”] - 5 df.getColumn {A}.map{ it -5} https://kotlin.github.io/dataframe/getcolumn.html#getcolumngroup カラム名 をリストで取得 df.columns df.columnNames() https://kotlin.github.io/dataframe/columnnames.html カラム名 をリストで変更 df.columns = ["a", "b", "c"] val listCol = df.columnNames() for ( (oldCol, newCol) in listCol.zip(listOf("a", "b", "c"))) {df = df.rename { it[oldCol] }.into(newCol)} https://kotlin.github.io/dataframe/rename.html 値を書き換え # 2 - B列の値を計算し列XとしてAに上書き df["A"] = 2 - df["B"]df = df.rename(columns={"A": "X"}) // 2 - B列の値を計算し列XとしてAに上書き df = df.replace("A").with{ 2 - B named "X" } https://kotlin.github.io/dataframe/replace.html 集計 df.groupby('G').agg(["count", "sum", "mean" ]) df.groupBy("G").aggregate {count() into "count"count{ C > 6 } into "over 6"sum() into "sum"mean() into "mean"} https://kotlin.github.io/dataframe/groupby.html#aggregation 移動平均 を計算 window_size = 2df.rolling(window_size).mean().dropna() var rolling_df = dfval windowSize = 2 for (col in df.columnNames()){rolling_df = rolling_df.replace(col).with {it.values.map { (it as Number).toDouble() }.windowed(windowSize){ it.average() }.plus(List(windowSize - 1) { null }).toValueColumn(col)}}rolling_df = rolling_df.dropNulls() https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/windowed.html 1つ思ったのが、Pandas → Kotlin DataFrameと変換していくとKotlin DataFrame独自の便利な機能が身につきませんね。selectメソッドなんか後述の動画では使いこなせれば便利だと思うんですが、Pandas移植ではあまり使わなかったので残念です(これからめちゃ使う可能性はありますが)。 あと、KotlinはPyhton以上に ラムダ式 が重要だなと感じました。 Python では普段あまり ラムダ式 は使わないので、 ラムダ式 が頻繁に出てくることに苦戦しましたが、使いこなせればより効果的な実装ができるのではないでしょうか? あたりまえですが、Kotlin DataFrameを使いこなすにはKotlin自体の理解が大切だと思いました。 Java は少し書いたことがありますが、基本 Python しか書かない(書けない)人間なのでKotlinがもっと分かれば自由に実装できるのになと思う場面が多かったです。 Kotlin DataFrameによる スマホ 上での前処理の実施 今回は、アプリを使用するユーザーの個人情報をモデルに多く入力するので、モデルによる推論をサーバー上で実行することができず、データの前処理も スマホ 上で処理できるようにしましたが、ONNXとKotlin DataFrameを使えばモバイル端末上でAIモデルによる一連の処理を実行することも可能だと感じました。 もちろん、PCと スマートフォン では処理能力が異なるので、PCでは一瞬で終わる処理も スマホ 上では予想以上に時間がかかります。また、 Python と比較するとモバイル端末上で利用できるKotlinや Android Java のデータサイエンス系のライブラリは充実していないので、 OSS を利用できず自分で実装しないといけなくなる処理が Python よりも増えます。 あと、 コンパイル 言語なのでいちいち コンパイル しないといけないのも開発効率が下がる原因になりますね。逆に、今回 Python のありがたみが身にしみて感じました。 Jupyter Kotlin kernel を使用すれば、Jupytern notebookで インタラクティブ にKotlinのコードをセルごとに実行でき、開発効率がかなり向上しました。PytrhonからKotlinを学ぶ人には必須の環境なんじゃないでしょうか? なんとなくKotlin DataFrameの雰囲気を理解したいなら Kotlin DataFrameのキャッチアップには、こちらの動画がおすすめです。よくデータサイエンス関係の技術の説明で用いられるKaggle チュートリアル のTitanicコンペを題材にKotlin DataFrameでデータ分析をどのように進めるか解説されています。 まずは、PandasとKotlin DataFrameの違いはなにか感覚をつかむことが、重要かなと思います。といいつつ、まだまだ自分もよくわかってないですね。ただ、今回の経験でKotlinや Android アプリ開発 に興味を持ちましたし(もともとモバイルアプリの開発はやりたかった)、AIモデルの応用先として、どこにも持ち運びができて、バッテリーで長時間稼働でき、カメラやマイクと言った入力データを収集するセンサーを持つ スマートフォン は非常に重要なAIモデルの実行環境だと感じました。 最近の スマートフォン の処理能力は以前と比較して格段に向上しましたし、エッジデ バイス として スマートフォン を利用するのではなく、AIサービスをモバイルデ バイス 上で完結して実行するのも今回の件でありだなと感じました。 そのためには、Kotlin DataFrameのようなデータサイエンスに不可欠な処理をサポートするライブラリはより重要性を増すのではないでしょうか? 最後に、私が所属しているAIトランスフォーメーションセンターでは、一緒に働いてくださる方を募集しております。こちらのページに採用に関する内容がまとめられております。また、カジュアル面談の募集もこちらのページからできますので、是非ご覧ください。 https://aitc.dentsusoken.com/recruit/recruit.html それでは。 執筆: @tokuhara.hikaru 、レビュー: @kinjo.ryuki ( Shodo で執筆されました )