Unityで作成したColorChangePanicをGodotへ移植する作業がだいぶ進んで、簡単なテストができるレベルまでになったのですが
youtu.be
敵増殖時の生成が、Unityは元の敵に吸い付くように生成されているのに対して、Godotではちょっと反発して生成されているのが気になりました。
Object(GodotではNode)の移動手段も、衝突判定方法も、UnityとGodotでは異なっているのですが
まだ習い始めということもあって、今はよくわからないままに、弾も敵もRigidBody2DというNodeをルートノードにして作成していました。
どういう理屈で生成の挙動がUnityとGodotで変わってしまっているのか判りませんが、いい機会なのでルートノードになりやすい、いくつかのNodeについて、それぞれの移動手段と、衝突判定方法について纏めておこうと思います。
Unityの場合はGameObjectを移動させる方法として、
①transform.positionを直接変更する。
②transform.translateで移動量を指定して、移動させる。
③Rigidbodyコンポーネントをアタッチして、力を加えて移動させる。
などがあり、①と②の方法であれば、どのGameObjectでも使うことができます。
衝突が発生した場合は、イベント関数のOnCollisionEnter
などが呼び出されます。
そのため、Unityでは比較的容易にObjectを移動させて、衝突判定をとることができます。
ところがGodotの場合は、①のpositionを直接変更する移動方法であれば、どのNodeでもできる様ですが、
②と③の様な手段となると、そうもいかない様で、移動させるメソッド・衝突を検知する方法ともNodeによって異なるみたいです。
zenn.dev
こちらのサイト様でかなり詳しく説明されていましたので、実際にNodeとスクリプトを作成して実験してみようと思います。
RigidBody2Dノード
まずはUnityで馴染みが深いRigidBodyです。
このノードはUnityの時と同じ様に、add_force
で力を加えたり、velocity
を直接操作したりするという移動方法のほか、move_and_collide()
メソッドでも移動させる事ができます。
衝突判定については、RigidBodyはシグナルでBody_enteredが使えるので、シグナルから衝突判定することがきます。
もスクリプトで衝突判定をとる事もできますが、この場合、移動方法によって判定方法が変わる様です。
まずmove_and_collide()
メソッドを使う場合です。
extends RigidBody2D var speed :int = 160 var coll_item var Obj_A var Obj_B var message_label func _ready(): Obj_A=get_node("../ObjA").get_class() $"../ObjA/CollisionShape2D/ColorRect/Label".text="Obj-A \n"+str(Obj_A) Obj_B=get_node("../ObjB").get_class() $"../ObjB/CollisionShape2D/ColorRect/Label".text="Obj-B \n"+str(Obj_B) message_label=$"../Label" func _physics_process(delta): coll_item=move_and_collide(Vector2(1,0)*speed*delta)#右にspeed*delta移動する。 if coll_item!=null: message_label.text="Obj-Aがぶつかったのは、" message_label.text += str(coll_item.get_collider().get_class()) #衝突した相手の型を表示 message_label.text += "の" + str(coll_item.get_collider().name) #衝突した相手の名前を表示
move_and_・・・
というのは、Body系ノードを移動させるメソッドで、
move_and_collide()
は移動中に他のcolliderにぶつかった場合、動きを止めて「衝突情報」を返してくれます。
そして、この移動方法は、衝突した相手に物理的な運動効果を与えない移動になる様です。
この「衝突情報」から.get_collider
で相手のcolliderを取得し、更にget_collider.get_class
でノードの型を、get_collider.name
で名前を確認する事ができます。
youtu.be
次に、velocity
を直接操作する場合です。
(add_force
の場合は、加えられた力を物理計算した結果として、velocity
で移動していると思いますので、そちらは省略します。)
func _physics_process(delta): linear_velocity=Vector2(1,0)*speed coll_item=get_colliding_bodies() if coll_item!=null: for body in coll_item: message_label.text="Obj-Aがぶつかったのは、" message_label.text += str(body.get_class()) #衝突した相手の型を表示 message_label.text += "の" + str(body.name) #衝突した相手の名前を表示
velocity
の移動の場合は、move_and_collide()
の様に、移動中colliderにぶつかると止まるというわけではないため、同じフレーム内で複数の衝突が発生する可能性があります。
get_colliding_bodies()
を使用すると、そのフレームで衝突があった場合、相手のcollider全てが配列変数に取得されます。
ちょっと分かりにくかったのですが、move_and_collide()
での取得は「衝突情報」でしたが、get_colliding_bodies()
では相手の「collider」が取得されるため、型や名前を取得する場合には、.get_collider
が不要でしたので、注意が必要です。
そしてvelocity
での移動は、衝突した相手に物理的な運動効果を与える移動になる様です。
youtu.be
相手がRigidBodyの場合、この様に相手を移動させてしまいます。
ただ、相手がCharacterBodyやStaticBodyの場合は、物理挙動をしているノードでは無いからなのかvelocity
での移動でも、相手を移動させてしまうことはない様です。
CharacterBody2Dノード
次は、CharacterBodyです。
CharacterBodyは、RigidBodyで使用できたmove_and_collide()
メソッドの他、move_and_slide()
メソッドも使用できるようになります。
move_and_collide()
は移動中にcolliderにぶつかると、そこで動きを止めますが
move_and_slide()
は移動中にcolliderにぶつかっても、動きを止めるのではなく、あらかじめ指定しておいた角度以下なら障害物に沿ってスライドするなど、まさにゲームキャラクターの移動に使うことを前提にしたような便利なメソッドです。
そして、CharacterBody2Dノードは、move_and_collide()
とmove_and_slide()
の2つのメソッドを移動に使用することができます。
衝突の検知については、移動手段のバリエーションに反してか、シグナルにBody_enteredが用意されていないため、シグナルで衝突を検知することができない様です。
移動の実験ですがmove_and_collide
の使い方はRigidBodyと同じ様でしたので、move_and_slide
を実験してみます。
extends CharacterBody2D # ← ここが変わっている事に注意。 var speed :int = 160 var coll_item var Obj_A var Obj_B var message_label func _ready(): Obj_A=get_node("../ObjA").get_class() $"../ObjA/CollisionShape2D/ColorRect/Label".text="Obj-A \n"+str(Obj_A) Obj_B=get_node("../ObjB").get_class() $"../ObjB/CollisionShape2D/ColorRect/Label".text="Obj-B \n"+str(Obj_B) message_label=$"../Label" func _physics_process(delta): if coll_item==null: #ぶつかるまでは右に毎フレームspeedピクセルの移動を定義 velocity=Vector2(1,0)*speed else: #ぶつかったら左に毎フレームspeedピクセルの移動を定義 velocity=Vector2(-1,0)*speed move_and_slide()#velocityの定義にしたがって移動。 if get_slide_collision_count()!=0: #このフレームでの衝突数を確認 for i in range(get_slide_collision_count()): #衝突数でforを回す coll_item = get_slide_collision(i) #i番目の衝突情報を格納 #衝突情報coll_itemからget_collider()で衝突したNodeの情報を取得して、 #get_class()でNodeの型を取得 message_label.text="Obj-Aがぶつかったのは、" message_label.text += str(coll_item.get_collider().get_class()) #衝突した相手の型を表示 message_label.text += "の" + str(coll_item.get_collider().name) #衝突した相手の名前を表示
move_and_slide()
はcolliderにぶつかっても移動を止めるわけではないため、velocity
の移動と同じように、同じフレーム内に複数の衝突が発生する可能性があり、「衝突情報」は配列変数として格納されます。
しかし、velocity
とは異なり、取得されるのは「collider」ではなく、「衝突情報」なのでcolliderを取得するためにget_collider
が必要になります。
そして、move_and_slide()
の移動はvelocity
に従った移動のように見えますが、衝突した相手に力を及ぼす事がない移動になる様です。
youtu.be
Area2Dノード
最後は、Area2Dです。
StaticBodyというものもあるのですが、これは地面のような動かないObjectを作ることを想定しているようで、その使用用途からあまり移動させる事はないのでは?と思いましたので省略します。
一応、StaticBodyはmove_and_collide()
メソッドを使うことができます。
そして、シグナルはBody_enteredが用意されておらず、この辺りはRigidBodyと同じイメージなのかもしれません。
最後のArea2Dは、今までのNodeと異なり、move_and_・・・
メソッドやvelocity
のような移動手段は用意されていないようで、移動させたい場合はpositionを直接操作する様です。
移動手段が限られていますが、衝突判定としてはシグナルでbody_enteredだけではなく、area_enteredというArea同士の衝突を検知する、ちょっと変わったシグナルが用意されています。
公式ドキュメントを読むと、Areaは例えば、取得できるコインのようなObjectを作成したりするのに向いている様です。
UnityでいうところのOnTriggerEnter
の様なものなのかもしれません。
あとは、今回のテーマとは変わりますが、エリア内に特殊は効果を設定したい場合などにも利用できるみたいです。
youtu.be
参考サイト
zenn.dev
zenn.dev