ゲームエフェクトデザイナーのブログ (新)

レポート記事とかUE4のマテリアルとか。C#とかも触ったり。

メインコンテンツ目次

●ニュース

タスク管理アプリ『TaskQuest』をリリースしました!

▼作ったツールやスライドはこちらで公開しています

Unreal Engine 4UE4)について書いた記事(主にVFX Artist向け)

エフェクトツールを使ってできること
エフェクトのワークフロー

テクスチャのインポートについて
テクスチャのプロパティについて
テクスチャのフォーマットについて

マテリアル作成の基本的な知識
最終マテリアル入力の各ポートについて
パーティクル向けのマテリアルの設定について

BISHAMONについて書いた記事

BISHAMONの最初のステップ
BISHAMON Personal v1.8 の新機能について
BISHAMONへのカメラのインポートについて

●Houdiniについて書いた記事

Houdiniの学習について
Billowy Smokeを使った煙のチュートリアル(前半)
Billowy Smokeを使った煙のチュートリアル(後半)

Mayaについて書いた記事

MELの最初のステップ
MELのウインドウ作成について
MELでシェーダー作成 1
MELでシェーダー作成 2
MELスクリプトのある場所を参照する
MELを配布する時のプロシージャについて
UVの正規化
頂点カラーを別のモデルに転送
テクスチャの色を頂点カラーに転送

Photoshopについて書いた記事

Photoshopのスクリプトを書いてみる

C#やプログラム全般について書いた記事

C#のはじめ方
C# ソリューションの基本的なファイル構成について

C#で正規表現による置換を行う
C# DataGridViewの主なプロパティ

C#で作ったツール一覧

GitHubでのツールやソース公開について(CGアーティスト向け)
ライセンスについてのメモ

●その他の記事

数学が苦手な人向けのCG数学シリーズ

イチオシゲームエフェクトアレコレ

処理速度と処理量のお話 ms(ミリセカンド)について
ドローコールについて

おおまかな開発コストの計算方法
おおまかなゲームソフトの売り上げの計算方法

勉強会開催のすすめ
大阪・京都の貸し会議室メモ

続きを読む

ノートPCを『Inspiron 14 7472』に新調。そしてファンクションキーの設定について。

Houdiniが動いてコンパクトで軽量なノートPCを探していたところ、swさんが実際に使われていてHoudini動作実績のあるDellのノートPCをおすすめしてもらいました。

そしてDellのサイトでこちらのCorei7 Office付きのものを購入(7000シリーズの7472)。

スペックは上記のものより若干低くなっていますがAmazonだとこちら。

Dellのサイトで20%割引クーポンコードが配布されていた時期に購入、4年間保証やら色々オプションを追加して13万円。

14インチ以下でGeForce搭載のものってあまり見かけないため選択肢もかなり限られる印象ですが、スペック的にもかなりコストパフォーマンス良いのではと思いました。

色はゴールドが良いと思ってチョイスしましたが、非常に気に入りました!
こちらのレビュー記事の写真で確認できます。
https://little-beans.net/review/inspiron14-7460/

 

ちなみに届いてセットアップ中に困ったことをメモしておきたいと思います。

まずデフォルト設定ではキーボードのファンクションキーを押すとノートPC専用の機能が発動され、テキスト入力時にカタカナや半角英数に変換しようと思うとFnキーと一緒に押さないといけない状態です。

こちらの変更方法をネットで検索すると、Windowsアイコンを右クリック「モビリティ センター」から設定を変える方法と、Windowsの設定の「回復」でBIOS設定を編集する方法が出てきますが、前者は設定項目が見当たらなかったため後者を試そうと思っていたのですが‥

Windows 10 におけるファンクションキーの動作変更について』
http://takaaki.hatenablog.jp/entry/20180127/1517059168

こちらの記事に行き当たり、試してみたら無事に設定変更できました!
BIOSはなるべくいじりたくなかったので良かった。。

 

それからノートPCにプリイントールされるOfficeについて。

ライセンスが Office Personal の場合は PowerPointが含まれていないので要注意です。

Home & Business なら付いています。

PowerPointだけライセンスを単体で追加購入することも可能ですが、Office 2019 では Personal から Home & Business にグレードアップするようなことはできないようです。

単体購入は1万6000円ほどしてお高いですが、1ヶ月1200円のサブスクリプションもあるようなので、たまに使うくらいならそれで十分かもですね!

「膨大なタスクの捌き方」講演メモ

こちらに参加してきました!その際のメモです。

「膨大なタスクの捌き方」

株式会社スクウェア・エニックス
プログラマ 菊池さん

一昨年認定スクラムマスターを取得 アジャイルを勉強中
キングダムハーツシリーズを10年以上やっている

アンケート
 プロ・セミプロ 8割
 PG 多い
 企画 そこそこ
 アーティスト かなり少ない

■スケジュールやマネージメント

得意ですか?
たいがい苦手と答えますよね
特にプログラマはロジック考えるのになぜスケジュールが考えられないのかな?と思ったりする

■ゲームの制作手順

見積もり
計画
実行(作成)

■見積もりについて

制作規模の把握
 結局はそれだけ 細かくやる必要はないですよという話
 プレイヤープログラム関連
 情報収集から入る
 アクションリストがある 企画さんが用意する今回どんな技があるというリスト
  カテゴリ別など

 作業数の洗い出しを行っていく
  必要なプログラムシステムを検討
  各カテゴリの最大個数を設定
  未定のものも枠として確保しておく
  企画が固まっていき次第埋めていく

 コストを見積もる
  大中小の3つに分ける
  大 1ヵ月 工数は1.00
  中 1週間 工数は0.25
  小 1日 工数は0.05

 リストを再構築
  通常アクション・フリーラン・攻撃など大きなくくり

 進捗表としても流用可能
  大の作業が21個あると21ヵ月かかるということになる
  残りの数を出すと進捗表にもなる

 注意点
  必要以上に正確性を求めない
  見積もりに時間をかけても仕方がない
  がちがちに決めると融通が利かなくなる
  プリプロ実績からコスト算出が理想
   プリプロをやる段階でこれを作るのにこれくらいかかったというのはちゃんと出しておく

 不確実性コーン
  マスター締め日に近づくにつれズレは収束されていく
  初期の段階の予想に16倍の誤差が出る
  見積もり無しで仕事しようって言い出す人も
  一年先の天気予報は絶対に予想できないですよね
  見積もりはほぼほぼ

 制作規模の把握ができればOK

■計画について

 今まで‥
  ガントチャートを引いて計画 みんな大好き

 ガントチャート
  何から引いていく?コツは
  上流工程の状況が分からないからムリ
  気にせず好きなように引くと良いと思う 大体そうしていた
  他のセクションもそれに合わせて調整してくれていた

 ムリな予定になる
  締め切りに合わせて引くのですし詰め状態になる
  ほぼ間違いなく予定通りにならない
  割り込み作業 仕様変更 準備不足
  線を引いてやった気になる

 今回
  上からの要望
  PV関連セクションをまたいで計画してね
   ガントチャートなんて無理!
  基本単位となるワークフローを設定するところから始めた

 ワークフローをグラフ化してイメージしやすくした
 4つのフェーズに分けた
 1準備
  HDの開発は準備にものすごく時間がかかる
  企画概要作成
  アート・キャラ作成 携帯機だと1ヵ月だったのが半年になる
   ここはちゃんと把握しておこう
  ゲーム及び技術検証をその間に済ませておく
  方向性の確認
  マイルストーンの設定

 2作成
  企画仕様の作成
   キャラが出来上がる直前までにやっておきましょう
  モーション、エフェクト、UI作成
  フェイシャル、カメラ、プログラム作成
   これらを順次開始していく
   仮作成
  レビューして修正事項検討

 3修正
  企画仕様FIXを行う
  モーション、エフェクト 修正・追加作成
  UI、カメラ、プログラム 修正・追加作成
  レビュー&成果物FIX承認
   これが通らないと延々と繰り返すことになる
 
4調整
 バランス調整
  モーション、エフェクト、フェイシャル仕上げ
  大体こういったイメージ
 最終確認

 プログラムは大体最初から最後まで関わる
 重要なポイントは2と3 コアな期間 一番注目してスケジューリングに回そう

 マイルストーンの設定をする
 関連性のある成果物をグループ化
  プレイヤー作業での分類
   武器別
   大技別
   追加操作キャラ別
   それぞれごとにマイルストーンを設定する

 1週間単位のグラフに落とし込む

 作業見積もりの可視化
  二ヵ月くらいかかるとかがパっと見で分かる

 フェーズを当てはめる
  プログラム以外の各パートもグラフに落とし込む

 タイムラインに乗せていく
  基本は重複させないで割り当てていく
  担当者を割り振れる場合のみ重複可
  パズルのように当てはめていく 楽しくなってくる
  キャラ班がスケジュールをきっちり決めているので割とやりやすかった

 簡単でしょう?

■実行について

 今まで‥
  頑張って作る!
   それはそうなのだけど‥
  計画通りに作れない
   なぜ遅れるのか?
    割り込み作業が発生する
    割り込み会議
    不具合の発生
    仕様不足
    ‥
    ‥
   なぜ割り込みが発生?
    ちゃんと計画していない 計画が伝わっていない 思いつき 一時の感情で判断 神の啓示‥
   結局はコミュニケーションが問題かも?

 なので今回、働き方を変えました

■働き方について

 作業時間と成果物の関係
  今まで‥
   働く時間が右肩上がりになっていく
   成果物がある一時を境に増えなくなっていく
   成果物量を頭打ち
   さらに働く時間が増えてしまう‥
  作業時間を増やすと
   短期的には瞬発力の向上
   長続きしない
   副作用で生産性が低下 寝不足になったり
   作業時間増加スパイラル そういった反省がある

  今回は‥
   働く時間を変えない 一定の時間で働く
   成果物を常に同じ量出し続ける
   繰り返しによる効率上昇 成果物は少しずつ上がっていく

 働き方変えます!

 コンセプト
  計画的に作る
   ムリ・ムダ・ムラを無くす
  価値の共有
   コミュニケーション
   進行状況を明確にする
   品質レベルの共有をみんなでしていこう

 スプリント開発を行った

■スプリント開発について

 スプリント開発とは‥
  アジャイル的な開発をしている人は? ⇒ ちらほらと手が上がる

 1週間単位のサイクル
  初日にスプリントプランニング
  最終日に作ったものをレビュー
  毎週同じ時間、同じ場所、同じリズムで繰り返す(ダイエットと同じ)

 初日に計画 ずっと作っていって 最後にレビューをする
  これを毎週繰り返す

 バックログ
  要件定義のリスト
   価値の高いものから作っていきましょう
    ユーザーにとって価値の高いもの 僕らではなく
   今回は前述のマイルストーンを母体にしました
   リストを優先度を高~低の順位で並べる
  利点
   緊急な案件も一旦バックログに積むことで開発のリズムを崩さない
    どっちの優先が高い?かと話し合ってからタスクに積む
   作るもの作ったものが確認できる
   計画の精度を上げながら開発できる

 スプリントプランニング
  1週間の作業を計画
   バックログの内容を確認
   タスクの落とし込み
   見積もりを立てる
  見積もり
   1日5時間x5営業日=週25時間分を1週間分のマイルストーンとして組み込む
   1日8時間働くとして、実作業は5時間ほどが現実
    あとはお昼ご飯や会議や準備だったりするので
   2週間分、見積もる
    翌週分はざっくり曖昧でもOK
    少し先を見通すことで準備が容易になる

 リストに対して予定時間を書いていく 時間単位で
  経験した時間を書いていくので開発後半ほど精度が上がっていく
  上から25時間分を選択
  チームのみんなに同意を得てやっていく

 利点
  レビューで何が見れるのかを全員で共有できる
  何を目指すのかを明確にしてから作業に入れる
  実現の仕方について話し合える

  ここにすごく時間がかかると聞くが 10~15分で終わる感じでやっていた

 スプリントレビュー
  みんなで確認
  成果物に対する改善の場
   暴露具の要件が満たせたか
   価値が確認できるか
   改善するものはバックログに追加

  利点
   定期的に確認するので品質が安定
   プロダクトの仕様を全員が把握できる
   プロダクトへの意識が高くなる
   動いているものが見れるのでレビュー会は楽しい!

  補足(人と場所について)
   最初はプログラマの共有ブースで10人くらいでやっていた
   その後はオープンな場所でやるようにした
    担当外の型にも何やっているか分かるように透明化を図った
    無駄な不安を払拭した
   お昼ご飯を食べるスペース 50インチくらいの液晶モニタとプロジェクタスクリーン2台

  チェック者の人がちゃんと一緒に参加して見てくれていた

  ビデオに撮って社内のWeb上で動画配信可能なページでみんなが見れるようにした

 最終的に何が起こったか
  レビューでバグるとすぐ直すので
  QAでの件数が減った
  いい加減な作業をするとレビューで散々な結果になるので緊張感を持てた
  進捗が安定した
  メンバー間の信頼関係が強化された
  楽しい開発ができた
  開発が間に合った!

 GCC2019で登壇して、そちらでは動くものを用意する予定

■Q&A

Q:
スプリントレビューについて
モデルやPGが1週間で成果が出せないものはどうした?

A:
マイルストーンの話
1週間のスプリントとは別に1か月のマイルストーンをデザイナー向けに用意した
必要なものを全てプールしておく
できたものから1週間のスプリントの中に入れていく
今週これが取り掛かれるので

Q:
進捗自体は確認しない?

A:
1ヵ月に1回モーションとエフェクトと一緒に話し合ってやっていった


Q:
バックログを登録するツールは?

A:
バックログ」というツール ※Webベースの有料サービス
初心者にも扱いやすい感じで良かった

Q:
スプリントの頻度について

A:
最後の1~2ヵ月は1Dayスプリントでやっていた
レビュー会は1週間に1回
1日でやるのは直結している上司と夕方にやった

 

「サルでも分かるゲームAIの話」講演メモ

こちらに参加してきました!その際のメモです。

「サルでも分かるゲームAIの話」

モリカトロン株式会社 森川幸人さん
「がんばれ森川くん2号」

いつもはAIの勉強会で話す 今日はゲームの勉強会
数式とかは出さない
利用範囲が広い 時間が足りない
ゲームに関わる部分だけ 入口をご紹介

PS初期の頃

■1:AIを使うゲームを作っている
1956年ダートマス会議ではじめて 人工知能という言葉が使われた
当時は人間の知能をナメていて人間の知能を軽く追いつけるのではと思われていた
今、ようやく将棋とか自動運転の一部だけとかレントゲンの病巣発見の一部だけとか
パーツパーツで人間を超え始めた 特化型
東大入試に受かっても東大への行き方が分からないとかよく言われる
目指すのは人間のように電車にも乗れて会場にも行けて解答も書けてと全てできるのを目指す
生き物の知能 自然知能

Aiの歴史 今は第三次ブームと言われている
ダートマス会議で致命的な欠陥があってすぐにブームが終わる

1980年くらいに改良版が出てきて第二次ブーム
命やお金を預かるシビアな問題は学習できない 実用に向かない 下火になった

2012年 DeepLearning AIを改良したモデルが登場
かなり高機能なAI 今度こそ実用に足りる道具になった
研究者の間だけのブームではなく世間を巻き込んだブーム
ファジー搭載 F/1揺らぎがどうの 家電会社が言ったりしていたが

高機能なものから小さいものまで優秀なAIがいっぱい
ゲーム機やスマホはハードがプアなのでDeep Learningを中ですることはできない
コンビニに行くのにスポーツカーで行く必要が無い
荷物を運ぶためとか 燃費の良さとか 用途に応じて車種が変わる
気を付けた方が良いところ

ニューラルネットワークについて少し
生物の脳をモデルにしたAI
ニューロン細胞がいっぱい繋がっている
信号を送って情報を伝達する
初期の頃の非常にシンプルな問題についてはうまく答えられる
パーセプトロンググると良い
代表的なモデルがDeep Learning
脳細胞の数が増えていった 階層が増えていった
2階層>3階層>200階層みたいに
驚異的な能力を今発揮している
人間の脳細胞(ニューロン)は約1000憶個
じゃあうちは1ちょう個を目指す!(Google
Google量子コンピュータの開発も進めている
ブレイクスルーを起こすんじゃないかと言われている

■2:Aiについて、ざっと説明

 

 

■3:ゲームAIって、なに?

2014年くらいゲームにAIが必要ですかねと言われるようになってきた
ゲームAIに特化した会社を設立
AIのソムリエ
もっと生き物っぽくならないかとか色んな依頼を受ける
AIを開発したりコンサルタントをしたり

ソムリエとの違いは、自分たちで加工しないといけない
今みたいな人的な作業でクリアできないぞという状態でお話をいただく
AIを突っ込むにもメモリが足りないとか制限されていて既存のAIを突っ込めなかったりして
色んなことを相談しながらちっこく作業していく

例えば‥
「オートプレイ+AIで、寝ている間にパラメータを生成」
スマホゲームなどで、昔出したキャラと今出したキャラでバランスが成立するかなど
24時間働いても効率良く働いてくれる

「ユーザーに合わせた会話ができます」
キャラクターが最近よくしゃべるので自動化できない?
プレイログに合わせて趣味嗜好に合わせた形でどんどんメッセージを自動的に作れないか
自然言語AI

「大量生産、うけたまわれます」
多量のステージを作ってオートプレイして最適なステージを作る

「ユーザーに合わせて「サービス」するAI」(道半ば)
ユーザーはこのレベルに対してこの強さなのでこのくらいの敵を配置して‥とやるが
今のように行動が多岐になると一般的なユーザーを測るというのが難しい
ゲーム全体を俯瞰しながら手詰まりを起こしていたり飽きていたりというのをチェックして
ゲームバランスを変えていくAI メタAI
一番ホットなAI ゲームのディレクターの役割をするAI

「お財布にやさしい「外のAI」」(まだまだこれから)
ゲームのデバッグやバランス調整、品質管理などをする
ここがなんとかならないか、コストダウンできると嬉しいなどの要請が多い
スクエニもやっているが、世界中が今やっている
QA AI

■4:ゲームAIの難儀なところ

ゲームは普通のAI開発と違って難しいところがある
ならではの難しい点
昔 20年前 「アストロノーカ」を作った
夢の島
遺伝的アルゴリズムというAIを使った
プレイヤーは農家で作物を食べにくる害虫をトラップで対峙する
しかしトラップに耐性がついてきて進化する
手付けで丁度良いパラメータを設定できない
このゲームの場合
AIでパラメータを生成する
 ユーザーがトラップを置いたら自然淘汰が行われて
 強い害虫が生き残る
 どうトラップを置かれようがバランスが崩れる心配がいらない
 最終的に何体か分からないくらい敵が出る
 初期の20体以外は全てAIがパラメータを生成
 誰もAIを使っていることを知らなかったのがショック 宣伝してくれなかった
 今でもアーカイブで動くので遊んで欲しい

遺伝的アルゴリズム
生物の進化のアルゴリズムをそのままモデルにしている
それはそれでちゃんとAIとして成り立つ
今あまり表舞台には出てこないがゲームでまだ有用なAI
ユーザーに応じて 外注の進化が自動的に変わるよう重みづけしたグラフ
AIに組み込んでいる すごく手練れ多ユーザーならそれなりに進化
苦手な人にはそれに合わせて進化
動的に進化速度を速めたりする
スクエニ三宅さん そこが素晴らしい と言ってもらえた

正しいAI
 最短で最適な解を見つける学習が、偉い
 ゲーム的にまずい進化をしたりする

楽しいAI
 ユーザーが気持ちよくなる学習が、偉い
 こちらの形にAIを加工していかないといけない

環境
・ゲーム機の計算力、メモリはプアー
・ユーザは待ってくれない
・面白い「学習」でなくてはならない
 ゲームにそのまま使えないことが多々起こる

問題点
・既存のAIをカスタマイズしないといけない
・仕様を実現できるAIを見つけないといけない
・ゲームの「面白い」を理解していないといけない

AI設計者的知識+ゲームデザイナー的感性が必要
 ゲームの都合を知っている完成がないとダメ
 それを活かすためにAIをどうダイエットしないといけないかという両方の知識が必要
 人材も不足
 自分たちも募集しているがなかなか集まらない
 困るようなことがあればぜひ相談してください

まとめると
AIはスコンと学習が上がるがゲームではそれではダメ

中のAI
 キャラクターの行動判断、経路設計など

外のAI
 パラメータ設計、調整、品質管理など
 今ホット これから利用される

ゲーム業界は何でもAIと言い過ぎる
    楽しいAI 正しいAI
中のAI
外のAI
商売になるのは右下

スクエニのFF
コリジョンにより歩ける場所は決まっているが最短ルートを選んで歩く
最適なルートをAIが見つける

NVIDIA 仮想環境を作る
自動車や自動運転ですごく要請が多い 交通事故で人が死んだりするので
なかなか実証実験がしづらいのでシミュレーションのリアルな世界を再現することが求められている
仮想空間 結構進んでいると聞いている

FurCry2 フィールドを作る
昔なら一本一本木のモデルを差し込んでいたが
あらかじめ木の種類は沢山用意しておいて
ここは草原ですとかここはうっそうな森ですのように設定を与えておくだけで
フィールドを自動的に生成してくれる
オープンワールドだとフィールドが広大になるので非常に役立つだろうと

AIが作った萌えキャラクター
日本の萌えキャラをいっぱい学習して新しい萌えキャラを生成するAI
メイクガールズ 萌え でググるといっぱい出てくる
パラメータを設定して生成するだけ

■5:いま、AIができること

架空アイドルを自動生成する
日本の会社 データグリッドさんの
色んなタレントの顔を覚えさせて新しいタレントを生成
アニメに限らず実写でもほとんど区別せずに作れる
ドットでも大丈夫

レンブラントの学習
レンブラント風の絵を生成できる
17世紀の作家 特集をいっぱい学習
3Dプリンターで作られている 近づくとでこぼこがある
絵具の盛り どれくらい絵具をつけたかとか どのくらいの速度で筆を動かしたかまで再現

Articoolo
キーワードを入力するdかえでコンテンツを自動作成してくれる
すでに商用サービスになっている
キーワード:日本 サッカー
これだけの入力で自動的に文章を作ってくれる
ひながたがあってところどころサッカー用語が入れられている
実際に起こったニュースを取り入れていたりする
全体的には日本のサッカー事情が分かっている上でニュースを作ってくれる
ニュースや天気予報などはっきりしているものはAIでは楽勝

「作家ですのよ!プロジェクト」
SF作家 星新一ショートショートを利用したもの
実際にAIが書いた文章
家族の方が非常に強力的で学習のために利用させてもらえた
開発者 松原先生より「人間8割」
 テーマや構造は人間が与えてやらないとちゃんとした文学は作れない
法律用語や医学用語など限られた範囲でエキスパートになるが
一般常識になると途端にダメ
「部屋に雨がしとしと降る」みたいな文章を作る
 部屋には屋根があり雨が降らないということをいちいち教えてやらないといけない
 一般常識を全部教えないといけない データベースも無いので
AIが小説を書いたり会話したりする時に大きな問題になっている

どちらがAIか見破ってみてください
キャッチコピー 人間のコピーライターvsAI

涙をこぼしながら、きみを作った
あなたは、史上最高の発明だ 〇

AIするんじゃねぇぜ 〇
綾瀬、井川、箕輪。

運命と向き合う覚悟はあるか 〇
メノウの輝きとピアノの音色が、僕を苦しくさせる。

60%しか当たらないが、プロのコピーライターは100%当てていたのでまだAI臭さがある

「イライザ」
世界最初のチャットボット
最近ではりんな
気の利いたことを言う 会話を交わす

AIの利用範囲は広い
ウェザーロイドAirin
毎日3時にお天気をお伝えしてくれる
AIでレポートを生成して、音声合成Vtuberにレポートさせる

ダークサイドについてもお伝えする
フェイク・オバマ
実際に言っていない発言をAIに言わせる フェイシャルも作っている
フェイクな画面がどんどんできるようになってきた

中国のニュースキャスター
フルCG フルAI モデルになっている人はいるらしい
音声もテキストもAIが作っている
こうなると真実は何?となる
これからの時代はねつ造かなかなか区別がつかなくなる
例えば選挙の二週間前に候補者がこんなひどいことを言っていたみたいなねつ造が上がったら‥

AIも包丁と同じで使う人間次第で武器にもなってしまう

Vtuber

■6:AIの勉強方法

今までは先生が例題や模範解答などいっぱい教えていた
そのあとに応用していく

強化学習は全く先生が必要ない

ブロック崩しを攻略する
有名なAI
AIにスコアを上げなさいと命令しただけ ルールさえも教えない
パドルを動かしてブロックが消えてスコアが上がったことに気づいて学習
天井を抜けると良いということに自分で気づく

レーシングゲーム
ちゃんと走りなさいよと命令だけ与える
自分で勉強していく
どこでブレーキしてどこでアクセルを踏むか 延々試行錯誤を繰り返すとできていく
車間距離なども
学習後にスムーズに動くようになる

車の運転やブランコの乗り方を独学する
ロボット 日立のH
後ろでも前でも屈伸している 人間は教えられない たまたま発見

AlphaGo
囲碁 韓国トッププロに勝ったAI 4世代目まである
最初は人間の棋譜を教えていた
4代目でとうとう人間の棋譜を必要としなくなった
教師なしで学習
1~3台目よりも強い
200戦200勝
チェスやオセロみたいに完全情報ゲームについては
ゲームの種類を超えた汎用的なモデルができた
Alpha Zero
一から学習させなおしが必要ない チェスでも何でもできる
先生も必要とせず、しかも強い

■7:この先AIができること

グランツーリスモなど
物理シミュレーションがしっかり入っている
自動運転にゲームが使われている
グランセフトオートの物理シミュレーションが素晴らしいということで走行テストが使われている
グランセフトオートでは見られない丁寧な運転 信号待ちまでする
UIも非常にしっかりしている
ゲームの機能が上がってきたのでAIなど他の分野にも応用が利いている

自殺予防
テキストチェック
Facebookなんかもやっている

西洋と東洋の知能感
日本はAIは遅れている
神>人間>人工知能
日本は横並びなのでキャラクターを使ったAIはがんがん日本がリードできる

 

C# WordドキュメントをMarkdownに変換したい

MS Wordで装飾したテキストに画像を沢山貼り付けたドキュメントをMarkdownに変えたい‥と思うも、手動だとめちゃくちゃ面倒ですね。

画像だけでも、ドキュメント内から全て手動で保存するのも、Wordの拡張子をzipにして解凍して画像だけ取り出すのも面倒です。

そこでWIndowsフォームでRichTextBoxを使い、RichTextBoxに貼り付けられたデータをMarkdownの書式に則ったテキストデータに変換してtxtで保存できたらいいなと思いました。

ただ、軽くググってみたところ思いのほかこれまた大変そうと思いそっ閉じ。

とりあえずリッチテキストの構造はRich Text Format(RTF)の書式を理解する必要があるということで、そのあたりについて解説されている記事のメモだけしておこうと思います。

Rich Text Format(RTF)の構造(の覚え書き) - Part 1 - うぃずのひとりごと

Rich Text Format(RTF)の構造(の覚え書き) - Part 2 - うぃずのひとりごと

Rich Text Format(RTF)の構造(の覚え書き) - Part 3 - うぃずのひとりごと

 

 

UE4でのキャラクターの基本的な設定メモ

Udemyで購入したdaibond(@daibond1)さんのチュートリアルUE4.21で進めています。

作って覚えるアンリアルエンジン【Unreal Engine 4】~ダンジョンゲーム編~

f:id:moko_03_25:20190120144821j:plain

序盤のすでに知っている部分は1.5倍速再生で流し見しつつ、1週間でPart60まで進めました。
土日がっつりx2回あれば終了できるボリュームでしょうか。

感想としては、ゲームのロジックだけでなくタイトル画面や終了画面への遷移・UIの作成とゲーム状況の反映などゲーム制作で必要な一通りが広く扱われている上、非常に分かりやすく丁寧に解説されていて、初めてゲームを作る場合に大変良い内容だと思いました。おすすめです!

しかしゲームを作る過程でキャラクターの様々な設定を行いますが、一気に沢山の情報が入ってくるためすぐに忘れてしまいそうです。。なので主な設定項目についてメモっておきたいと思います。

続きを読む

C# 穴の空いた多角形の3Dモデルにテクスチャを貼ってみた

前回に続いてテクスチャマッピングを行ってみました。
f:id:moko_03_25:20190114214652j:plain 頂点を共有していてもUVにシームを入れたい場合、1つの頂点にはUV座標は1つしか設定できないため、頂点を増やしてUVを別々に設定してあげないといけないようです。
こちらもんしょ(@monsho1977)さんにアドバイスいただいてうまくいきました!ありがとうございます!
 
コードは命名なども適当で公開するのは恥ずかしいですが、どなたかのお役に立つかもなので一応載せておきたいと思います。

using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace MonoGame3DTest2
{
    public class MyGame : Game
    {
        ////////////////////////////////////////////////////
        //                                               //
        // 穴の空いた円ポリゴンにテクスチャを貼るサンプル //
        //                                               //
        ////////////////////////////////////////////////////

        GraphicsDeviceManager graphics;
        BasicEffect effect;

        static int polyCircleCount = 48;
        float r = 0.3f;
        // UVのシーム用に2箇所で2重に頂点を生成するため+2している
        VertexPositionTexture[] vertices = new VertexPositionTexture[polyCircleCount * 2 + 2];
        int[] indices  = new int[polyCircleCount * 2 * 3];
        
        // テクスチャ
        Texture2D texture;

        // カメラの制御用
        float angle = 0;
        float height = 0;

        public MyGame()
        {
            graphics = new GraphicsDeviceManager(this);
        }

        protected override void LoadContent()
        {
            // 多角形の頂点情報
            for (int i = 0; i < polyCircleCount + 1; i++)
            {
                // 円を分割した1つ分の角度
                double cut = (Math.PI * 2) / polyCircleCount;
                // 角度
                double theta = cut * i;

                // X, Y の算出
                float x2 = (float)(Math.Cos(theta));
                float y2 = (float)(Math.Sin(theta));
                float x1 = r * x2;
                float y1 = r * y2;

                // UVのU座標の算出
                float u = 1.0f / polyCircleCount * i;

                // 円の内側の頂点
                vertices[i] = new VertexPositionTexture
                    (
                        new Vector3(x1, y1, 0),
                        new Vector2(u, 1)
                    );

                // 円の外側の頂点
                vertices[i + polyCircleCount + 1] = new VertexPositionTexture
                    (
                        new Vector3(x2, y2, 0),
                        new Vector2(u, 0)
                    );

                // デバッグ用
                //Console.WriteLine("U: " + u);
                theta = (theta / Math.PI) *180;
                float dX1 = (float)(((double)x1 / Math.PI) * 180);
                float dY1 = (float)(((double)y1 / Math.PI) * 180);
                float dX2 = (float)(((double)x2 / Math.PI) * 180);
                float dY2 = (float)(((double)y2 / Math.PI) * 180);
                Console.WriteLine("i: " + i + " theta: " + theta +
                " x1: " + dX1 + " y1: " + dY1 +
                " x2: " + dX2 + " y2: " + dY2);
            }

            // 穴の空いた円ポリゴンのインデックス生成
            int vertexId = 0;
            for (int i = 0; i < polyCircleCount; i++)
            {
                // 一度に三角形2つ分のIndexを指定するので6ずつ増やす
                int i2 = i * 6;

                // 三角形1つ目
                indices[i2]     = vertexId;
                indices[i2 + 1]    = vertexId + polyCircleCount + 1;
                indices[i2 + 2]    = vertexId + polyCircleCount + 2;
                // 三角形2つ目
                indices[i2 + 3]    = vertexId;
                indices[i2 + 4]    = vertexId + polyCircleCount + 2;
                indices[i2 + 5]    = vertexId + 1;

                vertexId++;
            }

            effect = new BasicEffect(GraphicsDevice)
            {
                TextureEnabled = true,

                Projection = Matrix.CreatePerspectiveFieldOfView
                (
                    MathHelper.ToRadians(45),              // 視野角
                    GraphicsDevice.Viewport.AspectRatio,    // アスペクト比(横/縦)
                    1,     // ニアクリップ
                    100        // ファークリップ
                )
            };

            // 画像を読み込んでテクスチャに設定
            texture = Content.Load<Texture2D>("Content/sample");
        }

        protected override void UnloadContent()
        {
            effect.Dispose();
        }

        protected override void Update(GameTime gameTime)
        {
            KeyboardState keyboardState = Keyboard.GetState();
            if (keyboardState.IsKeyDown(Keys.Left))
                angle -= MathHelper.ToRadians(2.0f); // 1/60秒に0.5°回転
            if (keyboardState.IsKeyDown(Keys.Right))
                angle += MathHelper.ToRadians(2.0f); // 1/60秒に0.5°回転
            if (keyboardState.IsKeyDown(Keys.Up))
                height += 0.1f; // 1/60秒に0.1移動
            if (keyboardState.IsKeyDown(Keys.Down))
                height -= 0.1f; // 1/60秒に0.1移動
        }

        protected override void Draw(GameTime gameTime)
        {
            // 背景のクリア
            Color c = new Color(100, 110, 60);
            GraphicsDevice.Clear(c);

            // テクスチャの設定
            effect.Texture = texture;

            // 両面描画
            GraphicsDevice.RasterizerState = RasterizerState.CullNone;

            // カメラの位置
            effect.View = Matrix.CreateLookAt
            (
                new Vector3
                (
                    3 * (float)Math.Sin(angle),
                    height,
                    3 * (float)Math.Cos(angle)
                ),
                new Vector3(0, 0, 0),   //カメラの注視点
                new Vector3(0, 1, 0)    //カメラのUPベクトル
            );

            // 三角形の描画
            foreach (var pass in effect.CurrentTechnique.Passes)
            {
                pass.Apply();

                // 3角形を実際に描画する
                GraphicsDevice.DrawUserIndexedPrimitives<VertexPositionTexture>(
                    PrimitiveType.TriangleList,
                    vertices,
                    0,
                    vertices.Length,
                    indices ,
                    0,
                    indices .Length / 3
                    );
            }
        }
    }
}

 
例によってこの記述ではポリゴンの面が裏を向いて生成されてしまっていると思います。
(両面描画にしているので表示されていますが)

C# 穴の空いた多角形の3Dモデルの描画

前回の多角形ポリゴンに引き続いて、MonoGame(XNA)を使ってC#で穴の空いた多角形ポリゴンを描画してみました。
f:id:moko_03_25:20190114151255g:plain

こういうパイナップル型のモデルを何と呼べば良いんでしょう。リング?
 
「多角形の分割数」を変えれば、四角形>五角形>六角形‥と増やしていけます。
頂点インデックスは法則を見つけて落とし込む感じですね。。

using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace MonoGame3DTest2
{
    public class MyGame : Game
    {
        ////////////////////////////////////////
        //                                   //
        // 穴の空いた円ポリゴンを描くサンプル //
        //                                   //
        ////////////////////////////////////////

        GraphicsDeviceManager graphics;
        
        // データがどのように描画されるかを制御するクラス
        BasicEffect effect;

        // 多角形の分割数
        static int polyCircleCount = 12;
        // 中心の穴の半径
        float r = 0.3f;

        // 座標と色の2つのデータを持った頂点情報の構造体
        VertexPositionColor[] vertices = new VertexPositionColor[polyCircleCount * 2];

        // 頂点インデックス指定用
        int[] indices  = new int[polyCircleCount * 2 * 3];
        
        // カメラの制御用
        float angle = 0;
        float height = 0;

        // コンストラクタ
        public MyGame()
        {
            graphics = new GraphicsDeviceManager(this);
        }

        // 表示物の準備
        protected override void LoadContent()
        {
            // 円の頂点情報
            for (int i = 0; i < polyCircleCount; i++)
            {
                // 円を分割した1つ分の角度
                double cut = (Math.PI * 2) / polyCircleCount;
                // 角度
                double theta = cut * i;

                // X, Y の算出
                float x2 = (float)(Math.Cos(theta));
                float y2 = (float)(Math.Sin(theta));
                float x1 = r * x2;
                float y1 = r * y2;

                // 円の内側の頂点
                vertices[i] = new VertexPositionColor
                    (
                        new Vector3(x1, y1, 0),
                        Color.White
                    );

                // 円の外側の頂点
                vertices[i + polyCircleCount] = new VertexPositionColor
                    (
                        new Vector3(x2, y2, 0),
                        Color.White
                    );

                // デバッグ用
                //theta = (theta / Math.PI) *180;
                //float dX1 = (float)(((double)x1 / Math.PI) * 180);
                //float dY1 = (float)(((double)y1 / Math.PI) * 180);
                //float dX2 = (float)(((double)x2 / Math.PI) * 180);
                //float dY2 = (float)(((double)y2 / Math.PI) * 180);
                //Console.WriteLine("i: " + i + " theta: " + theta +
                //" x1: " + dX1 + " y1: " + dY1 +
                //" x2: " + dX2 + " y2: " + dY2);
            }

            // 穴の空いた円ポリゴンのインデックス生成
            int vertexId = 0;
            for (int i = 0; i < polyCircleCount; i++)
            {
                // 一度に三角形2つ分のIndexを指定するので6ずつ増やす
                int i2 = i * 6;

                // 最後のポリゴン生成かどうか判定
                if (vertexId != polyCircleCount - 1)
                {
                    // 三角形1つ目
                    indices[i2]     = vertexId;
                    indices[i2 + 1]    = vertexId + polyCircleCount;
                    indices[i2 + 2]    = vertexId + polyCircleCount + 1;
                    // 三角形2つ目
                    indices[i2 + 3]    = vertexId;
                    indices[i2 + 4]    = vertexId + polyCircleCount + 1;
                    indices[i2 + 5]    = vertexId + 1;
                }
                else
                {
                    // 三角形1つ目
                    indices[i2]     = vertexId;
                    indices[i2 + 1]    = vertexId + polyCircleCount;
                    indices[i2 + 2]    = polyCircleCount;
                    // 三角形2つ目
                    indices[i2 + 3]    = vertexId;
                    indices[i2 + 4]    = polyCircleCount;
                    indices[i2 + 5]    = 0;
                }

                // デバッグ用
                //Console.WriteLine("i: " + i + " vertexId: " + vertexId);

                vertexId++;
            }

            effect = new BasicEffect(GraphicsDevice)
            {
                Projection = Matrix.CreatePerspectiveFieldOfView
                (
                    MathHelper.ToRadians(45),              // 視野角
                    GraphicsDevice.Viewport.AspectRatio,    // アスペクト比(横/縦)
                    1,     // ニアクリップ
                    100        // ファークリップ
                )
            };
        }

        // メモリからの解放
        protected override void UnloadContent()
        {
            effect.Dispose();
        }

        // 計算の更新
        protected override void Update(GameTime gameTime)
        {
            // キーボード制御
            KeyboardState keyboardState = Keyboard.GetState();

            if (keyboardState.IsKeyDown(Keys.Left))
            {
                angle -= MathHelper.ToRadians(2.0f); // 1/60秒に0.5°回転
            }
            if (keyboardState.IsKeyDown(Keys.Right))
            {
                angle += MathHelper.ToRadians(2.0f); // 1/60秒に0.5°回転
            }
            if (keyboardState.IsKeyDown(Keys.Up))
            {
                height += 0.1f; // 1/60秒に0.1移動
            }
            if (keyboardState.IsKeyDown(Keys.Down))
            {
                height -= 0.1f; // 1/60秒に0.1移動
            }
        }

        // 描画
        protected override void Draw(GameTime gameTime)
        {
            // 背景のクリア
            Color c = new Color(100, 110, 60);
            GraphicsDevice.Clear(c);

            // 両面描画
            GraphicsDevice.RasterizerState = RasterizerState.CullNone;

            // カメラの位置
            effect.View = Matrix.CreateLookAt
            (
                new Vector3
                (
                    3 * (float)Math.Sin(angle),
                    height,
                    3 * (float)Math.Cos(angle)
                ),
                new Vector3(0, 0, 0),   //カメラの注視点
                new Vector3(0, 1, 0)    //カメラのUPベクトル
            );

            // 三角形の描画
            foreach (var pass in effect.CurrentTechnique.Passes)
            {
                pass.Apply();

                // 3角形を実際に描画する
                GraphicsDevice.DrawUserIndexedPrimitives<VertexPositionColor>(
                    PrimitiveType.TriangleList,
                    vertices,
                    0,
                    vertices.Length,
                    indices ,
                    0,
                    indices .Length / 3
                    );
            }
        }
    }
}

 
内側と外側で円状に配置する頂点は 半径Cos(角度) と 半径Sin(角度) でXとY座標を出しています。
(外側は 半径=1 にしているため r* は省略しています)
 
例によってこの記述ではポリゴンの面が裏を向いて生成されてしまっていると思います。
(両面描画にしているので表示されていますが)

C# 多角形の3Dモデルの描画

MonoGame(XNA)を使ってC#で多角形ポリゴンを描画してみました。
MonoGame 自体に関してはこちらで紹介しています。
f:id:moko_03_25:20190114124710g:plain  
かんたんXNA4.0 (XNA入門)|Memeplexes
ポリゴン生成などの基本的な記述に関してはこちらのサイトを参考にさせていただいています。
 
「多角形の分割数」を変えれば、四角形>五角形>六角形‥と増やしていけます。
頂点インデックスを生成するのが大変ですね。。

using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace MonoGame3DTest2
{
    public class MyGame : Game
    {
        //////////////////////////////
        //                         //
        // 円ポリゴンを描くサンプル //
        //                         //
        //////////////////////////////

        GraphicsDeviceManager graphics;
        
        // データがどのように描画されるかを制御するクラス
        BasicEffect effect;

        // 円の分割数
        static int polyCircleCount = 12;

        // 座標と色の2つのデータを持った頂点情報の構造体
        VertexPositionColor[] vertices = new VertexPositionColor[polyCircleCount + 1];

        // 頂点インデックス指定用
        int[] indices = new int[polyCircleCount * 3];
        
        // カメラの制御用
        float angle = 0;
        float height = 0;

        // コンストラクタ
        public MyGame()
        {
            graphics = new GraphicsDeviceManager(this);
        }

        // 表示物の準備
        protected override void LoadContent()
        {
            // 中心点
            vertices[0] = new VertexPositionColor(new Vector3(0, 0, 0), Color.White);

            // 円の外周
            for (int i = 1; i <= polyCircleCount; i++)
            {
                // 円を分割した1つ分の角度
                double cut = (Math.PI * 2) / polyCircleCount;
                // 角度
                double theta = cut * (i - 1);
                // X, Y の算出
                float x = (float)Math.Cos(theta);
                float y = (float)Math.Sin(theta);

                vertices[i] = new VertexPositionColor
                    (
                        new Vector3(x, y, 0),
                        Color.White
                    );

                // デバッグ用
                //theta = (theta / Math.PI) *180;
                //float dX = (float)(((double)x / Math.PI) * 180);
                //float dY = (float)(((double)y / Math.PI) * 180);
                //Console.WriteLine("cut: " + cut + " d; " + theta + " X: " + dX + " Y: " + dY);
            }

            // 円ポリゴンのインデックス生成
            int vertexId = 0;
            for (int i = 0; i <= (polyCircleCount - 1) * 3; i += 3)
            {
                // 中心
                indices[i]      = 0;

                // 外側1
                indices[i + 1] = vertexId + 1;

                // 外側2
                if (vertexId != polyCircleCount - 1)
                {
                    indices[i + 2] = vertexId + 2;
                }
                else
                {
                    indices[i + 2] = 1;
                }
                vertexId++;

                // デバッグ用
                //Console.WriteLine("i: " + i + " c: " + vertexId);
            }

            effect = new BasicEffect(GraphicsDevice)
            {
                Projection = Matrix.CreatePerspectiveFieldOfView
                (
                    MathHelper.ToRadians(45),              // 視野角
                    GraphicsDevice.Viewport.AspectRatio,    // アスペクト比(横/縦)
                    1,     // ニアクリップ
                    100        // ファークリップ
                )
            };
        }

        // メモリからの解放
        protected override void UnloadContent()
        {
            effect.Dispose();
        }

        // 計算の更新
        protected override void Update(GameTime gameTime)
        {
            // キーボード制御
            KeyboardState keyboardState = Keyboard.GetState();

            if (keyboardState.IsKeyDown(Keys.Left))
            {
                angle -= MathHelper.ToRadians(2.0f); // 1/60秒に0.5°回転
            }
            if (keyboardState.IsKeyDown(Keys.Right))
            {
                angle += MathHelper.ToRadians(2.0f); // 1/60秒に0.5°回転
            }
            if (keyboardState.IsKeyDown(Keys.Up))
            {
                height += 0.1f; // 1/60秒に0.1移動
            }
            if (keyboardState.IsKeyDown(Keys.Down))
            {
                height -= 0.1f; // 1/60秒に0.1移動
            }
        }

        // 描画
        protected override void Draw(GameTime gameTime)
        {
            // 背景のクリア
            Color c = new Color(100, 110, 60);
            GraphicsDevice.Clear(c);

            // 両面描画
            GraphicsDevice.RasterizerState = RasterizerState.CullNone;

            // カメラの位置
            effect.View = Matrix.CreateLookAt
            (
                new Vector3
                (
                    3 * (float)Math.Sin(angle),
                    height,
                    3 * (float)Math.Cos(angle)
                ),
                new Vector3(0, 0, 0),   //カメラの注視点
                new Vector3(0, 1, 0)    //カメラのUPベクトル
            );

            // 三角形の描画
            foreach (var pass in effect.CurrentTechnique.Passes)
            {
                pass.Apply();

                // 3角形を実際に描画する
                GraphicsDevice.DrawUserIndexedPrimitives<VertexPositionColor>(
                    PrimitiveType.TriangleList,
                    vertices,
                    0,
                    vertices.Length,
                    indices,
                    0,
                    indices.Length / 3
                    );
            }
        }
    }
}

 
中心の頂点だけ座標が(0, 0, 0)で、外周の頂点は Cos(角度) と Sin(角度) でXとY座標を出しています。
 
ちなみにこの記述では裏を向いて生成されてしまっていると思います。
(両面描画にしているので表示されていますが)

C# MonoGameで3Dモデルを表示してみる

こちらの記事にまとめてるように「Maneged DirectX」や「SlimDX」の解説記事をもとに3Dモデルの表示を片っ端から試してみましたが、途中でうまくいかなくなったりコードが複雑で理解が追いつけなかったりしました。

そんな中、DirectXよりかなり敷居が低いと言われているXNAマイクロソフト公式でそのまま動くよう移植したという「MonoGame」が良さそうに思い、XNAの解説を頼りに学習してみることにしました。

その際に躓いたことなどメモっておきたいと思います。※随時更新

続きを読む