スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。
[ --/--/-- --:-- ] スポンサー広告 | TB(-) | CM(-)

UE4のプロシージャルメッシュ・スライシング

こんにちは。

ちょっと気になって最近のUE4の新機能を調べてみたら面白いことができるのを発見しました!

最後に触ったバージョンが4.12.3でしたが、現在は4.17.1まで上がっています。
その中で4.13で追加された機能にプロシージャルメッシュ・スライシングというのがあります。

動画を見るとメッシュをすきな角度で動的にカットしているように見えます。
依然、モデルを居合切りでカットするゲームを作ろうとして、アルゴリズムを考えたことがありますが、面倒になってやめたんですよね。。。

UE4の使い方を忘れたな。。。
まず、4.17.1を起動してみます。

さあ、どうしようかな。
プロシージャルメッシュ・スライシングについてググってみたけど、断片的な情報しかないです。
バージョンの違いのせいか、書いてある通りに動かなかったりするし。。。

おっ、いい動画を見つけた。
Procedural Mesh Slicing | Live Training | Unreal Engine
二時間近い長時間の動画ですが、最後の方で椅子を切り刻んでいるので、この通りにやればやり方がわかるかな?

まず、親クラスアクターのブループリントを作成して、プロシージャルメッシュコンポーネントを追加します。
このときDefaultScenRootは削除して良いようです。
このとき、ProceduralMeshのUse Complex as Simple Collisionのチェックを外します。
コリジョンタイプはBlockAllのままで良いみたい。

Construction Scriptを書きます。
ProcedualMeshをターゲットにしてCrate Mesh Sectionを実行。
Vertices、Trianglesの入力パラメータにMakeArrayで値を設定。
これでプロシージャルメッシュを自由に作成できそうです。
頂点を指定して自由にモデルを作成する方法を探していましたが、これが使えそうですね。

プロシージャルメッシュの作成は、もっと簡単な方法があるみたい。

ブループリントにスタティックメッシュコンポーネントを追加。
あと、スタティックメッシュを格納する変数も用意しておきます。
ブループリントの先頭でスタティックメッシュを変数にコピー。
Copy Procedural Mesh from Static Mesh Componentでスタティックメッシュをプロシージャルメッシュにコピー。
その後DestroyComponentで不要になったスタティックメッシュを削除します。
このとき、スタティックメッシュのAllow CPU Accessにチェックを付けておく必要があります。

プロシージャルメッシュのコリジョンのSimulation GeneratesとPhysicsのSimulate Physicsをチェックします。
これでプロシージャルメッシュの物理エンジンが有効になって地上に落下するようになります。

Event BeginPlayでSliceProcedualMeshを起動します。
Create Other Halfにチェックして、Cap OptionをCreate New Section for Capに指定、Cap Materialはスタティックメッシュのマテリアルを指定します。
Plane PositionはGetActorLocationから持ってきます。
Plane NormalはY=1.0とすると縦にスライスされます。
これで実行すると、もともとのプロシージャルメッシュだけが落下します。

SliceProcedualMeshの出力であるOutOtherHalfProcMeshを入力としてSet Simulate Physicsを起動すると、スライスされた残りも落下するようになります。

うぉ、ここまで作ったブループリントを消してる。
ここから新しいことを始めるみたい。

まず、ゲームモード、DefaultPawn、PlayerControllerを親クラスとしたブループリントを作成します。

ゲームモードの新クラスでDefaultPawnClassとPlayerControllerClassを継承した新クラスに変更。
プロジェクトセッティングのMap&ModesでDefaultGameModeを継承した新クラスに変更。

プレイヤーコントローラの詳細でMouse InterfaceのShow Mouse Cursor、EnableMouseOverEventsをチェック。
ClickEventKeysを追加して、右クリックを有効にします。
あ、また消した。やっぱいらないのか。
プロジェクトセッティングのEngine-CollisionでObjectChannelsにSliceを追加。デフォルトはBlock。

プロシージャルメッシュを持つアクターのブルーポイントでCollisionをBlockAllをCustom、Object TypeをSliceに変更。

プレイヤーコントローラにStaticMeshコンポーネントを追加。ここに平面のメッシュを登録。

イベントグラフで、EventTickに処理を追加。
GetHitResultUnderCursorforObjectsでオブジェクトタイプSliceにカーソルが乗った場合にBreakHitResultで座標を割り出して平面を表示。カーソルが乗っていない時は平面を消す。

うぉ、平面が表示されなくて、デバッグが始まった。。。
これは見ているのが辛すぎますね。

ということで別の動画を探します。
Tutorial on Slicing blade. (100% Blueprint, Unreal Engine 4)

おお、これはやりたいことピッタリっぽいです。
時間も15分。
ブループリントの説明が端折ってありそうだけど、こっちをじっくり見てみます。

まずはTherdPersonのブループリントプロジェクトを新規作成します。

アクターのブループリントを新規追加。
StaticMeshとProceduralMeshを追加。
DefaultSceneRootは削除したみたい。

ConstructionScriptでCopy Procedural Mesh from Static Mesh Componetを使ってStatic MeshからProceduralMeshを作成。
Create Collisionをチェック。

StaticMeshのCollision PresetsをBlockAllDynamicからNo Collisionに変更。
ProceduralMeshのSimulatePhysicsをチェック。
StaticMeshのHidden in Gameをチェック。

1M_Cube_ChamferのAllow CPUAccessをチェック。
アクターのStaticMeshに1M_Cube_Chamerを設定。

このアクターをヴューポートにドロップ。
アクターのProceduralMeshのUseComplex as Sample Collisionのチェックを外す。
これで、アクターが地面に落ちて、押すと移動できるようになりました。

ThirdPersonCharacterのブループリントを開きます。
AddComponentでPlaneを追加。
PlaneをBladeという名前に変えてMeshにアタッチ。
ソケットはHand_lを選択。
BasicShapeMaterial(これは事前に作ってあった?)を開いてベースカラーを白に。う~ん。なんの操作で白になったのか分からなかった。。。
BladeのマテリアルをBasicShapeMaterialからRedに変更。さっきのは何だったんだ。。。

Bladeを拡大、回転させて剣になるように。剣というより畳持ってるみたい。。。
BladeのCollisionPresetsをBlockAllDynamicからOverlapAllに変更。

cancutというブーリアン変数を定義。
LeftMouseButtonで押されたらtrue、離されたらfalseを設定。
押されたときは、Bladeをターゲットに、Bind Event to OnComponentBeginOverlapを起動して、その後DoOnceを起動。離されたらDoOnceのReset。
OnComponentBeginOverlap_Event_0のOther CompをCastして、As ProceduralMeshComponentを入力にしてSliceProceduralMeshを起動。
Create Other Halfをチェック。
Cap OptionはCreate New Section for Cap。
Cap Materialにはredを。
Plane PositionはBladeのGetWorldLocationで取得。
Plane NomalはBladeのGet Up Vectorで取得。

Out Other Half Proc MeshをターゲットにしてSetSimulate Physicを起動。
Simulateにチェック。

その後、Out Other Half Proc MeshをターゲットにしてAdd Radial Impulseを起動。
OriginはBladeのGetWorldLocationで取得。
Radiusを300、Strengthを500にして、Vel Changeにチェック。

おお、これでスパスパ切れますね。

この後、Construction Scriptでスタティックメッシュコンポーネントからではなく、メッシュ、マテリアル、スケールをエディッタブルな変数として用意して、インスタンスで違うスタティックメッシュを切り刻めるようにしています。

最後はSliceを二回実行しているようですが、動画が終わったので何がしたかったのか明確には分かりません。

それでは、自分でやってみます。

剣を手に持つときのソケットの設定でちょっと悩みました。
Bladeの詳細でソケットを選択すればよかったようです。
動画では左手に持ってましたが、今回は右手でやってみます。
あれ?剣の片面に色がつかない。
剣のマテリアルに両面処理のチェックを入れたら表示されました。

OnComponentBeginOverlap_Event_0というのが出てこないな。。。
Bladeから引き出してoverlapで検索し、OnComponentBeginOverlapを割り当てるを選んだら出てきました。分かり難いな。。。

うん、できた。
それ以外は特に悩まずに完成しました。

色々と苦労しましたが、分かってみればかなり簡単にメッシュをスライスできますね。
これを使って何か面白いことができないか考えてみます。

それでは、また。
スポンサーサイト
[ 2017/09/19 00:22 ] プログラミング | TB(0) | CM(0)

Pythonで遊んでみよう#2

こんにちは。

Pythonの使い方をある程度思い出したところで、今日は機械学習(ディープラーニング)を試してみます。

まずはこれから。
機械学習の Python との出会い

出てくる数式と、高速化のためのpythonのテクニックについては着いていけませんでしたが、データを学習させてそれをもとに予測する仕組みはなんとなく分かりました。
単純ベイズ(ナイーブベイズ)のベルヌーイ分布の学習と予測を自分で実装しているようです。

機械学習をググるとscikit-learn(以下sklearn)というパッケージが良く出てきます。
これにも単純ベイズのベルヌーイ分布の機能が入っているようなので、同じことをやらせてみます。
やったのは、run_nbayes1.pyの中でnbayes1のNavieBayes1を使う変わりに、sklearn.naive_bayesのBernoulliNBを使うように変えただけです。
importとインスタンス生成の2行を変えただけで動きました。
結果はほぼ同じで、sklearnの方がほんのちょっと精度が下がりました。(誤差の範囲かな。。。)

ナイーブベイズのようなsklearnの学習アルゴリズムは25種類ほど用意されているようですが、ディープラーニングはサポートされてないみたい。一部のサイトではsklearnでディープラーニングできるように書いてあるけど、他のサイトを見るとchainerとかtensorflowを使うのが一般的みたいです。

ディープラーニングは機械学習の一種で、教師なし学習に相当するようで、実用化するのは中々大変に見えました。
簡単なゲームとかに使うなら、高速なナイーブベイズとかの方が良いのかな?

試しに何か作ってみたいです。
生態系シミュレーションで天候をコントロールして生物を長く存続させるゲームでも作って自動学習してみようかな。

まずは生態系シミュレーションの仕様から。
・生物は植物、草食動物、肉食動物の3種類
・変化させられるのは天候(晴れ、曇り、雨)のみ
・パラメータは気温、湿度、日照度
・気温は晴れだと上昇、雨だと下降、曇りだと緩やかに下降
・湿度は雨だと上昇、晴れだと下降、曇りだと緩やかに下降
・日照度は晴れだと上昇、曇り、雨だと下降
・植物は適正気温、適正湿度、適正日照度で繁殖、範囲外だと減少、草食動物が増えると減少
・草食動物は植物が増えると繁殖、肉食動物が増えると減少、減ると減少、気温、湿度が範囲外だと急速に減少
・肉食動物は草食動物が増えると繁殖、減ると減少、気温、湿度が範囲外だと急速に減少

超適当ですね。。。
こんなんで、コントロールが難しい生態系になるのかな?

とりあえず実装してみます。
う~ん。一応動いてるけど今一ですね。。。
天候で生物の数をコントロールするという発想に無理があるみたい。タイムスケールが違いすぎですよね。。。

きょうのところはここまで。
もうちょっと面白い事を考え付いたらまたやってみます。

それでは、また。
[ 2017/08/20 23:43 ] プログラミング | TB(0) | CM(0)

Pythonで遊んでみよう#1


こんにちは。

長らく放置していてすみません。
今日は、久しぶりにプログラミングをやってみます。

題材はPython。
最近、AI(人工知能)関連で良く聞くプログラミング言語です。
このブログでもメタセコのスクリプトで使いました。

まずはPythonのインストールから。
依然は2.x系のバージョンを使っていましたが、そろそろ新しいバージョンが一般的になってきているようなので、今回は3.x系をインストールします。

Anacondaというパッケージを使うのが便利なようなので、まずはこれをインストールします。
このサイトを参考にしました。
Anaconda を利用した Python のインストール (Windows)

現在の最新はバージョン3.6のようです。
Windowsの64bit版をインストールします。

次にPyCharmというIDE(統合開発環境)をインストールします。
Community版にしました。

う~ん。書いてある通りに打ち込んだのにHello world!が表示されないのはなぜかな。。。
プロジェクト名、ソースファイル名をtestにしたせいで、他と名前がダブってたようです。
ちゃんと選んだら動きました。

さてと、Python環境は手に入ったけど、何しようかな。

最終的にはAI(ディープラーニング)を組み込んで、進化する生態系シミュレーションでも作ってみたいな。
まずは、AIなしの生態系シミュレーションを軽く作ってみます。

えっと、画面に絵を描くのってどうするんだったかな。。。
ググったらturtleというパッケージを見つけました。
短いサンプルを打ち込んでみたら、ウィンドウが開いてタートルグラフィックが表示されました。
これも面白そうですね。

import turtle
import random

screen = turtle.Screen()
kame = turtle.Turtle()
kame.goto(0,0)

def star(d):
if d > 0:
for n in range(5):
kame.forward(d * 100)
kame.right(144)
star(d - 1)

star(4)

screen.exitonclick()


サンプルをちょっと弄ってみたら、こんなものが描けました。
20170817-1.png

でも、やりたいことと違うんだよな。。。

ググったら、matplotlibというパッケージを見つけました。
importできたのでanacondaの中に入ってるみたい。
サンプルを実行してみましたが、画面に表示されません。
サンプルが2.x系だからかな。。。
色々試して表示されました。
原因は、サンプルに以下が無かったからみたい。。。
plt.show()

でも、これも何か違うな。。。
おっ、見つけた。
tkinterというパッケージが探していたもののようです。
サンプルを実行してみましたが、四角形や線を表示できました。
でも、PyCharmでimportする時に、tkinterだとエラーになって、Tkinterだと実行できます。
コマンドコンソールから実行する時は逆にTkinterだとエラーになって、tkinterだと実行できます。

ググってみたら海外サイトに原因が載ってました。
2.x系がTkinter、3.x系がtkinterのようです。
でもって、PyCharmの設定が2.x系になっていたもよう。
PyCharmのFile-Settings-Project Interpreterで、2.7.11から3.6.1に変更したらtkinterでも実行できるようになりました。

tkinterは10年ほど前に使った気がするけど、記憶に無いなぁ。。。
ググっても2.x系の情報ばっかりなので、3.x系の差分を調べないとハマりそうです。

ライフゲームのサンプルがあったので動かしてみました。
世代数を表示したくて改造しました。
after()で定期的に起動する関数内で描画していたので、ここに世代数の表示を追加しようとしたんですが世代をカウントするグローバル変数を関数内で参照できません。
かなりハマってしまいましたが、関数内で、
global 変数名
と宣言してから参照することで、グローバル変数にアクセスできることが分かりました。
C言語系列と文法が大きく変わるので、調べるのが難しいですね。。。

クラス化すればグローバル変数を使わなくてもよいのかな。
まあ、クラス内の全変数がクラス内ではグローバル変数みたいなものか。。。
ということで、クラス化したものがこれです。

m random import randint
import tkinter as tk

class Application(tk.Frame):
def __init__(self, master = None):
super().__init__(master)
self.pack()
self.create_widgets()

def create_widgets(self):
self.COLS, self.ROWS = [180, 90]
self.CW = 10
self.is_playing = False
self.data = []
self.age = 0
for y in range(0, self.ROWS):
self.data.append([False for x in range(0, self.COLS)])
root.title("Life Game")
self.cv =tk.Canvas(self, width = self.COLS * self.CW, height = self.ROWS * self.CW)
self.cv.pack()
self.cv.bind("<1>", self.canvas_click)
self.btn = tk.Button(self, text = "Start / Stop")
self.btn.pack(fill = 'x')
self.btn.bind('<Button-1>', self.start_click)
self.draw_stage()
self.game_loop()

def check(self, x, y):
cnt = 0
tbl = [(-1, -1), (0, -1), (1, -1), (1, 0), (1, 1), (0, 1), (-1, 1), (-1, 0)]
for t in tbl:
xx, yy = [x + t[0], y + t[1]]
if 0 <= xx < self.COLS and 0 <= yy < self.ROWS:
if self.data[yy][xx]: cnt += 1
if cnt == 3: return True
if self.data[y][x]:
if 2 <= cnt <= 3: return True
return False
return self.data[y][x]

def next_turn(self):
data2 = []
for y in range(0, self.ROWS):
data2.append([self.check(x, y) for x in range(0, self.COLS)])
self.data = data2

def canvas_click(self, e):
xx, yy = [e.x // self.CW, e.y // self.CW]
self.data[yy][xx] = not self.data[yy][xx]
self.draw_stage()

def draw_stage(self):
self.cv.delete('all')
for y in range(0, self.ROWS):
for x in range(0, self.COLS):
if not self.data[y][x]: continue
x1, y1 = [x * self.CW, y * self.CW]
self.cv.create_oval(x1, y1, x1 + self.CW, y1 + self.CW,
fill="blue", width=0)

def game_loop(self):
if self.is_playing:
self.next_turn()
self.draw_stage()
self.age = self.age + 1
self.cv.create_text(20, 20, text=str(self.age), font=('FixedSys', 14))
self.after(16, self.game_loop)

def start_click(self, e):
self.is_playing = not self.is_playing

root = tk.Tk()
app = Application(master = root)
app.mainloop()


動かすと、こんな感じになりました。
20170817-2.png

もう少し調べれば、簡単なゲームくらいは作れそうです。
今日のところは、ここまで。

それでは、また。
[ 2017/08/17 22:59 ] プログラミング | TB(0) | CM(0)

マイクラmodを作ってみよう#9

こんにちは。

続きをやります。

ItemクラスにonUpdate()があったのでオーバーライドします。
ツルハシを右クリックしたら、ブロックの座標を覚えてフラグを立てます。
onUpdate()メソッドでフラグが立っていたら座標の上に丸石を置きます。
次に呼ばれたら、前回丸石を置いた座標のさらに上に置いていきます。
128回置いたらフラグを落とします。
これで、右クリックしたブロックの上に128個の丸石を自動的に積み上げることができました。

でも、積み上げる速度が速すぎます。
カウンタを持って10回に1回処理するようにしたら、1秒間に6個ずつ積みあがるようになりました。
onUpdate()メソッドは1/60秒に1回起動されるようです。
DirectXと同じで画面の描画周期(fps)で呼ばれる関数ですね。

音が出ないと寂しいので、ちょっと調べてみました。
シャベルの右クリックに音を出す処理があります。
ブロックを地面に配置する音はどれかな。。。

色々試したらこれっぽいです。

worldIn.playSound(player, pos, SoundEvents.ENTITY_ITEMFRAME_ADD_ITEM, SoundCategory.BLOCKS, 1.0F, 1.0F);


配置するブロックの位置でposを更新したら、遠くなるほど音が小さくなるように実装できました。

配置するブロックの上に乗って、エレベーターみたいに上がれないかな。
BlockPos pPos = new BlockPos((int)Math.floor(player.posX), (int)Math.floor(player.posY), 
(int)Math.floor(player.posZ));
if (pos.equals(pPos)) {
player.setPositionAndUpdate(player.posX, player.posY + 1.0F, player.posZ);
}
こんな感じにしたら、ブロックの上に乗って登れるようになりました。

20170211-1.png

でも、登り切った後で再度ツルハシを右クリックすると落ちちゃいます。なぜかな。。。

一秒間の6回だと処理が追い付かないのかもしれないな。
う~ん。色々試してみましたが、onUpdate()で処理するとかなり不安定になるみたいです。
パケットとか使うと安定するのかな?

それでは、また。
[ 2017/02/11 21:00 ] プログラミング | TB(0) | CM(0)

マイクラmodを作ってみよう#8

こんばんは。

ちょっと閃いたので、続きをやります。
今まで上から下に処理してたので、下の丸石が無い状態でレールを敷いたり、レールを敷いた後で下の丸石を置いたりすることになっていました。
これを下から上に処理するようにしてみます。
おお、ちゃんとレールが敷けました。

でも、Z方向の時だけレールが大量にドロップします。
あっ、三重ループをX,Y,Zの順にしていましたが、横方向もブロックを置く順番が関係しているのかな?
試しに高さを一番外側のループにして、レールを伸ばす方向を二番目、幅方向を一番内側のループにしてやってみます。

おお、今度はレールがドロップされなくなりました。
でも、なぜドロップされなくなったのかが良くわからないです。。。
もしかしたら、ForgeかMinecraftのバグかな?
斜面にレールを敷くアルゴリズムの関係かもしれません。

とりあえず、255ブロックの長さのレールを自動で敷いて、トロッコで移動できました。
海もあり、山もあり、トンネルもあり、いい景色が見れました。

20170210-1.png

でも、トロッコって遅いですね。
もっと早いトロッコを作ってみようかな。

あと、一瞬でレールを敷くよりも、音をつけてながら手前から遠くまで少しずつレールを敷いた方が雰囲気が出そうですね。
今回はツルハシの右クリックイベントの中で全ての処理をやってますが、周期的に動いているイベントに処理を入れれば実現できそうです。
もしかしたらonUpdate()メソッドとかが使えるのかもしれません。
これができれば、256×256×256の穴を鉱石をドロップさせながら自動で掘ったりもできるかも。
次回はそんな感じのこともやってみようと思います。

それでは、また。
[ 2017/02/11 00:05 ] プログラミング | TB(0) | CM(0)








上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。