前回は端点がある連続したエッジだったが、エッジがループしている場合のコードも書く。ループしているときはどのエッジでもいいから一本取り出して、その二点から探索を開始する。
探索終了は、探索した結果一番最初の要素が見つかったら終了する。
import bpy ###################################################### # textオブジェクトの追加 def add_text_numbers(vertexes): # 新しいCollrectionを作成 newCol = bpy.data.collections.new('Vertex Numbers') # 現在のシーンにコレクションをリンク bpy.context.scene.collection.children.link(newCol) for v in range(len(vertexes)): fontCurve1 = bpy.data.curves.new(type="FONT",name="fontCurve1") obj = bpy.data.objects.new("vert[ "+str(v)+" ]",fontCurve1) obj.data.body = "" + str(v) # textオブジェクトの内容 obj.location.x=vertexes[v].co.x obj.location.y=vertexes[v].co.y obj.location.z=vertexes[v].co.z obj.scale=[0.5,0.5,0.5] newCol.objects.link(obj) ###################################################### # 現在選択中のオブジェクトを取得 def select_object(): ob = bpy.context.active_object if ob.type != 'MESH': raise TypeError("Active object is not a Mesh") mesh = ob.data #print( len(mesh.edges) ) #print( len(mesh.vertices) ) return mesh.vertices, mesh.edges ###################################################### ###################################################### # @bref 端点を取得 # @param [in] verts 頂点リスト # @param [in] edges エッジリスト # @param [in] target 端点は二つあるので0,1のどちらか # @return 頂点番号 def get_end_points(verts,edges,target): dic = {} for v in range(len(verts)): dic[v] = 0 for e in edges: dic[e.vertices[0]] += 1 dic[e.vertices[1]] += 1 ends = [] for i in range(len(dic)): if dic[ i ] == 1: ends.append( i )
######################### ## ループしている場合 ### if not ends: return None
return ends[target] ###################################################### ###################################################### # @brief vtxを持つエッジのリストを取得 # @param [in] edges エッジリスト # @param [in] vtx 頂点index # @return エッジindexの配列 def get_edge_by_point(edges,vtx): edgelist = [] for e in _edges: if e.vertices[0] == vtx or e.vertices[1] ==vtx: edgelist.append(e.index) return edgelist ###################################################### ###################################################### # @brief V1を持ち、V2を持たないエッジ # @param [in] V1 頂点index(持つ頂点) # @param [in] v2 頂点index(持たない頂点) # @return 見つかったエッジindex # @retval None そのようなエッジはなかった def get_edge_V1_but_V2(edges,V1,V2): for e in _edges: if e.vertices[0] == V1 and e.vertices[1] != V2: return e.index if e.vertices[1] == V1 and e.vertices[0] != V2: return e.index return None ###################################################### ###################################################### # @brief edgeを構成する2頂点のうち、vertexでないほうの頂点を返す # @param [in] edge エッジリスト # @param [in] vertex 基準となる頂点のIndex # @return 頂点番号 def get_another_point_on_edge(edge,vertex): if edge.vertices[0] == vertex: return edge.vertices[1] if edge.vertices[1] == vertex: return edge.vertices[0] ###################################################### ###################################################### # @brief 一直線に連続した順番の頂点一覧を取得 # @param [in] verts 頂点リスト # @param [in] edges エッジリスト # @param [in] order 端点のどちら側から探索するか(0 or 1) # @return 頂点一覧 def get_connected_vertex_list(verts,edges,order): # 結果の格納先 vlist = [] # 最初の頂点を取得。「端点」は二つだけ存在するので # 結果は二つ求まる(0,1)。そのうちのorder番を取得 EP = get_end_points(verts,edges,order) if EP is not None: vlist.append(EP) #その要素が結果の一番目 edgeidxlist = get_edge_by_point(edges,EP)# EPを持つエッジの一覧。要素数1の配列になるはず # エッジ最初の頂点を持つエッジ endpointedge = edges[edgeidxlist[0]] #EP(最初の端点)でない方の頂点を取得 another = get_another_point_on_edge( endpointedge , EP ) # 端点を持つエッジの、端点でないほうの頂点は、二番目の頂点 vlist.append(another) else:############################# ## ループしている場合 ####### if order == 0: vlist.append( edges[0].vertices[0] ) vlist.append( edges[0].vertices[1] ) else: vlist.append( edges[0].vertices[1] ) vlist.append( edges[0].vertices[0] )# 無限ループを使うのでループ上限を設けておく looplimit = 100 while True: # 前回の前回見つけた頂点 before = vlist[len(vlist)-2] # 前回見つけた頂点。この頂点の次の頂点を探したい now = vlist[len(vlist)-1] #nowを持ち、beforeを持たないエッジを検索 nextedge = get_edge_V1_but_V2(edges,now,before) # 端点には「nowを持ち、beforeを持たないエッジ」がないので、Noneが入っている if nextedge is None: break #nextedgeの、nowでない方の頂点を取得 next = get_another_point_on_edge( edges[nextedge] , now )##################################### ## ループしている場合 ############### if next == vlist[0]: breakvlist.append(next) looplimit -= 1 if looplimit == 0: break return vlist # 頂点群を取得 ret = select_object() _verts = ret[0] _edges = ret[1] # 頂点番号のテキストオブジェクトを追加 add_text_numbers(_verts) vtx_c_list = get_connected_vertex_list(_verts,_edges,0) print(vtx_c_list)
import bpy
###################################################### # textオブジェクトの追加 def add_text_numbers(vertexes): # 新しいCollrectionを作成 newCol = bpy.data.collections.new('Vertex Numbers') # 現在のシーンにコレクションをリンク bpy.context.scene.collection.children.link(newCol) for v in range(len(vertexes)): fontCurve1 = bpy.data.curves.new(type="FONT",name="fontCurve1") obj = bpy.data.objects.new("vert[ "+str(v)+" ]",fontCurve1) obj.data.body = "" + str(v) # textオブジェクトの内容 obj.location.x=vertexes[v].co.x obj.location.y=vertexes[v].co.y obj.location.z=vertexes[v].co.z obj.scale=[0.5,0.5,0.5] newCol.objects.link(obj) ###################################################### # 現在選択中のオブジェクトを取得 def select_object(): ob = bpy.context.active_object if ob.type != 'MESH': raise TypeError("Active object is not a Mesh") mesh = ob.data #print( len(mesh.edges) ) #print( len(mesh.vertices) ) return mesh.vertices, mesh.edges
###################################################### ###################################################### # @brief 端点を取得 # @param [in] verts 頂点リスト # @param [in] edges エッジリスト # @param [in] target 端点は二つあるので0,1のどちらか # @return 頂点番号 def get_end_points(verts,edges,target): dic = {} for v in range(len(verts)): dic[v] = 0 for e in edges: dic[e.vertices[0]] += 1 dic[e.vertices[1]] += 1 ends = [] for i in range(len(dic)): if dic[ i ] == 1: ends.append( i ) return ends[target] ###################################################### ###################################################### # @brief vtxを持つエッジのリストを取得 # @param [in] edges エッジリスト # @param [in] vtx 頂点index # @return エッジindexの配列 def get_edge_by_point(edges,vtx): edgelist = [] for e in _edges: if e.vertices[0] == vtx or e.vertices[1] ==vtx: edgelist.append(e.index) return edgelist ###################################################### ###################################################### # @brief V1を持ち、V2を持たないエッジ # @param [in] V1 頂点index(持つ頂点) # @param [in] v2 頂点index(持たない頂点) # @return 見つかったエッジindex # @retval None そのようなエッジはなかった def get_edge_V1_but_V2(edges,V1,V2): for e in _edges: if e.vertices[0] == V1 and e.vertices[1] != V2: return e.index if e.vertices[1] == V1 and e.vertices[0] != V2: return e.index return None ###################################################### ###################################################### # @brief edgeを構成する2頂点のうち、vertexでないほうの頂点を返す # @param [in] edge エッジリスト # @param [in] vertex 基準となる頂点のIndex # @return 頂点番号 def get_another_point_on_edge(edge,vertex): if edge.vertices[0] == vertex: return edge.vertices[1] if edge.vertices[1] == vertex: return edge.vertices[0] ###################################################### ###################################################### # @brief 一直線に連続した順番の頂点一覧を取得 # @param [in] verts 頂点リスト # @param [in] edges エッジリスト # @param [in] order 端点のどちら側から探索するか(0 or 1) # @return 頂点一覧 def get_connected_vertex_list(verts,edges,order): # 結果の格納先 vlist = [] # 最初の頂点を取得。「端点」は二つだけ存在するので # 結果は二つ求まる(0,1)。そのうちのorder番を取得 EP = get_end_points(verts,edges,order) vlist.append(EP) #その要素が結果の一番目 edgeidxlist = get_edge_by_point(edges,EP)# EPを持つエッジの一覧。要素数1の配列になるはず # エッジ最初の頂点を持つエッジ endpointedge = edges[edgeidxlist[0]] #EP(最初の端点)でない方の頂点を取得 another = get_another_point_on_edge( endpointedge , EP ) # 端点を持つエッジの、端点でないほうの頂点は、二番目の頂点 vlist.append(another) # 無限ループを使うのでループ上限を設けておく looplimit = 100 while True: # 前回の前回見つけた頂点 before = vlist[len(vlist)-2] # 前回見つけた頂点。この頂点の次の頂点を探したい now = vlist[len(vlist)-1] #nowを持ち、beforeを持たないエッジを検索 nextedge = get_edge_V1_but_V2(edges,now,before) # 端点には「nowを持ち、beforeを持たないエッジ」がないので、Noneが入っている if nextedge is None: break #nextedgeの、nowでない方の頂点を取得 next = get_another_point_on_edge( edges[nextedge] , now ) vlist.append(next) looplimit -= 1 if looplimit == 0: break return vlist
# 頂点群を取得 ret = select_object() _verts = ret[0] _edges = ret[1] # 頂点番号のテキストオブジェクトを追加 add_text_numbers(_verts) vtx_c_list = get_connected_vertex_list(_verts,_edges,0) print(vtx_c_list)
まだやってなかったと思う。多分。
import bpy
###################################################### # textオブジェクトの追加 def add_text_numbers(vertexes): # 新しいCollrectionを作成 newCol = bpy.data.collections.new('Vertex Numbers') # 現在のシーンにコレクションをリンク bpy.context.scene.collection.children.link(newCol) for v in range(len(vertexes)): fontCurve1 = bpy.data.curves.new(type="FONT",name="fontCurve1") obj = bpy.data.objects.new("vert[ "+str(v)+" ]",fontCurve1) obj.data.body = "" + str(v) # textオブジェクトの内容 obj.location.x=vertexes[v].co.x obj.location.y=vertexes[v].co.y obj.location.z=vertexes[v].co.z obj.scale=[0.5,0.5,0.5] newCol.objects.link(obj)
###################################################### # 現在選択中のオブジェクトを取得 def select_object(): ob = bpy.context.active_object if ob.type != 'MESH': raise TypeError("Active object is not a Mesh") mesh = ob.data #print( len(mesh.edges) ) #print( len(mesh.vertices) ) return mesh.vertices
# 頂点群を取得 verts = select_object() # 頂点番号のテキストオブジェクトを追加 add_text_numbers(verts)
クリック位置の検出はPlayerControlのブループリントでGet Hit Result Under Cursor by Channelノードを追加しHit ResultからBreak Hit Resultを見れば取り出せる。
まず以前やった方法でアクタのクリックの検出をできるようにする。
https://suzulang.com/unrealengine4-blueprint-4-actorclickevent/
アクタのクリック検出にはPlayerControlのオーバーライドが必要なので、この作業で必然的にPlayerControlのブループリントができたことになる。これをMyPlayerControlとして、以下のように設定する
クリック位置にオブジェクトをスポーンする
Add Forceノードを使えば簡単に力を加えられる。力の方向の数字が大きいのはデフォルトのアクタの重量が100kgなど重いため。
ここでは、アクタが他の物体にヒットしたときに、0.5秒置いて左上方向に力がかかるようにした。
床か壁にぶつかると、その0.5秒後に左上に力がかかるので延々と壁にぶつかり続ける。
重量を変更するには、アクタを選択し、詳細→物理→MassInKgをチェックし、数字を入力する。
こちらのサイトが大変詳しいです:
https://qiita.com/mml/items/e614b441438ac9788d81
Add Force以外にもいろいろな力の加え方がこちらのサイトでまとめられています:
初速は開始直後に与える力。
ブループリントを開き、projectile Movementコンポーネントを追加する
ProjectileMovementを選択すると詳細が設定できるようになるので、Initial Speed、Velocityを設定する
これで、起動直後に物体に力がかかる。
アクタをクリックしたときに何か動作をする場合、「アクタがクリックされたときにアクタのClickイベントが呼ばれる」という設定をしなければならない。
① 「新規の空のブループリントクラス」から、Player Controllerを親クラスとするクラスを作成する(My_PlayerController)
② 「新規の空のブループリントクラス」から、Game Mode Baseを親クラスとするクラスを作成する(My_GameMode)
③ (My_GameMode)のPlayer Controller Classに(My_PlayerController)を設定する
④ アウトライナのGame Mode→ゲームモードオーバーライドに(My_GameMode)を設定する
⑤ (My_PlayerController)のマウスインターフェイスで"Show Mouse Cursor","Enable Click Events"をチェックする
この設定で、アクタのClickイベントが呼ばれるようになるので、アクタのOnClickedの処理を設定する。
なお、調べるとアクタのオーバーライド可能な関数にActorOnClickedというのもあるのだが、違いがわからなかった。
ActorOnClickedはオーバーライド一覧で選択する。
キーボードやマウスの入力を拾うにはレベルブループリントを使う。
「レベル」とはマップの事で、つまりマップにI/Oの処理を書く事になる(多分)
この設定でマウス入力はできるようになるがあくまでレベルでクリックを検出できるようになるだけで、アクタをクリックされたら何かすると言う事はできない。それは次回やる。
① 前回と同じ方法でSphereを追加し、ブループリントクラスに変換する
② 同様にCubeを追加し、Cube_Blueprintクラスに変換する
ただスポーンさせたいだけなら、例えば以下のように設定する。Sphere BlueprintのBeginPlayにSpawnActorを接続すれば、Sphereが生成された瞬間にCubeをSpawnできる。
上記の例はあまりにも地味なので、前回の方法でSphereのOn Component Hitを有効にした上で、Sphere Blueprintに以下のようなノードを設定する
破棄はDestroyActorで行う。以下では、上記、SphereのOn Component Hit時に生成されたCubeの、生成時に走るBeginPlay後、0.5秒のdelayの後self(=Cube)をDestroyActorする。
UE4のブループリントに少し触れたい。
今回はアクタ(3Dのオブジェクト)が落下して何かに衝突したら文字列を表示させてみる。
UE4のブループリントはイベントドリブン型みたいな感じで、アクタに対してイベントが起こるとOn Compornent Hit イベントハンドラが呼び出されるので、そこに処理の内容を書く、と考えるとわかりやすい。
① アクタを追加して高い位置に移動
② アクタが重力の影響を受けるように設定、On Comprnent Hitを処理できるように設定
③ アクタをブループリントクラスに変換
④ ビューポートでオブジェクトを選択、On Compornent Hit イベントを追加
⑤ On Compornent Hit に Print Textを接続
⑥ コンパイル→プレイ
改めてみると間違っていた気がするのでここに修正版を置く。
あと確認のためにBlenderのスクリプトを組んで確認した。
このd1,d2を、最初の②の式に代入する。
import bpy import mathutils
def _nearestLineLinePoints3_(A,B,C,D): AC = C-A n1 = B-A n1.normalize() n2 = D-C n2.normalize() AmC=A-C CmA=C-A ############################################### numerator = n2.dot(AmC) + n2.dot(n1) * ( n1.dot(CmA) / n1.dot(n1) ) denominator = n2.dot(n2) - n1.dot(n2)**2 / n1.dot(n1) d2 = numerator / denominator ############################################### numerator = n1.dot(CmA) + n1.dot(n2) * d2 denominator = n1.dot(n1) d1 = numerator / denominator ############################################### dn1 = n1 * d1 _PAB = A + dn1 dn2 = n2 * d2 _PCD = C + dn2 return _PAB,_PCD
A = mathutils.Vector((0.390268,-0.093855,-0.48373)) B = mathutils.Vector((-0.401277 ,0.95607,-0.045612)) C = mathutils.Vector((-0.475275 ,-0.448733 ,0.344921)) D = mathutils.Vector((0.231105 ,0.937974 ,-0.776157)) PAB = mathutils.Vector((0,0,0)) PCD = mathutils.Vector((0,0,0)) ret = _nearestLineLinePoints3_(A,B,C,D) PAB = ret[0] PCD = ret[1]
############################################# print( PAB,PCD) #頂点定義 verts = [] verts.append( A ) verts.append( B ) verts.append( C ) verts.append( D ) verts.append( PAB ) verts.append( PCD ) #面を、それを構成する頂点番号で定義 edges =[] edges.append( [0,1] ) edges.append( [2,3] ) edges.append( [4,5] ) #頂点と頂点番号からメッシュを作成 mymesh = bpy.data.meshes.new("mymesh") mymesh.from_pydata(verts,edges,[]) #作成部分 mymesh.update() #オブジェクトを作成し、メッシュを登録 obj = bpy.data.objects.new("My_Object", mymesh) #オブジェクトのマテリアルを作成 mat = bpy.data.materials.new("Mat1") mat.diffuse_color = (0.3,0.7,0.5,1.0) obj.active_material = mat #オブジェクトを登録 bpy.context.scene.collection.objects.link(obj)