サンプルプロジェクトは以下リンクからダウンロードいただけます。
本プロジェクトは、物理ジョイント(PinJoint2D、GrooveJoint2D)を使ったロープ・チェーンシステムを実装しています。
上下左右の矢印キーで移動、スペースでジャンプ、zキーで前方の鎖を切ることができます。
各チェーンには角度や伸び率に応じて破断するような設定がされております。
プロジェクト構造
ディレクトリ構成
steam_achieve/
├── scripts/ # ジョイント・チェーン・ロープ関連スクリプト
│ ├── breakable_joint_monitor_2d.gd
│ ├── chain_path_binder.gd
│ ├── chain_setup_tool.gd
│ ├── chain_part.gd
│ ├── joint_break_params.gd
│ ├── rope_solver.gd
│ ├── rigid_body_pusher.gd
│ ├── area_joint_breaker.gd
│ ├── path_ribbon_mesh_2d.gd
│ ├── boolean_sprite_cutter_with_tagged_shapes.gd
│ └── fragment_manager.gd
├── *.tscn # オブジェクトファイル
├── *.png # テクスチャファイル
└── addons/ # アドオン(gdDelaunay、godotsteam)
オブジェクトファイル解説
1. testrope_2.tscn(ロープテストオブジェクト)
目的
7つのRigidBody2DをGrooveJoint2Dで接続したチェーンをテストするオブジェクトです。
構造
Testrope2 (Area2DGameObject)
├── Node2D (チェーンのルート)
│ ├── RigidBody2D (先頭、固定されていない)
│ │ ├── CollisionShape2D (RectangleShape2D)
│ │ ├── seg1 (Node2D, position: 0, -10)
│ │ └── seg2 (Node2D, position: 0, 10)
│ ├── RigidBody2D2 (2番目)
│ │ ├── GrooveJoint2D (前のRigidBody2Dに接続)
│ │ │ ├── node_a: ../../RigidBody2D
│ │ │ ├── node_b: ..
│ │ │ ├── position: (0, -16)
│ │ │ ├── bias: 0.2
│ │ │ ├── length: 8.0
│ │ │ └── initial_offset: 2.0
│ │ ├── CollisionShape2D
│ │ ├── seg1
│ │ └── seg2
│ ├── RigidBody2D3~RigidBody2D7 (同様の構造)
│ └── RigidBody2D7 (末尾、CapsuleShape2Dを使用)
├── Path2D (チェーンの可視化用)
├── PathRibbonMesh2D (リボンメッシュで描画)
├── StaticBody2D (先頭を固定)
│ └── GrooveJoint2D (先頭のRigidBody2Dに接続)
├── StaticBody2D2 (末尾を固定)
│ └── GrooveJoint2D (末尾のRigidBody2D7に接続)
├── BreakableJointMonitor2D
│ ├── search_root: ..
│ ├── break_angle_degrees: 80.0
│ ├── break_angle_duration_sec: 0.3
│ └── use_break_delay_frames: false
│ └── break_delay_frames: 3
└── ChainPathBinder2D
├── auto_detect_mode: true
├── search_root: ../Node2D
├── output_paths: [../Path2D]
├── joint_monitor: ../BreakableJointMonitor
├── ribbon_mesh: ../PathRibbonMesh2D
└── enable_smooth_handles: true
ジョイント構造
- GrooveJoint2Dが各RigidBody2Dの子として配置されています
- 各ジョイントは
node_aで前のRigidBody2Dを、node_bで自身(..)を参照します - 先頭と末尾は
StaticBody2DにGrooveJoint2Dで固定されています - すべてのジョイントは
bias=0.2、length=8.0、initial_offset=2.0で統一されています
設定の特徴
BreakableJointMonitor2Dは角度80度以上で0.3秒継続したら切断する設定ChainPathBinder2Dは自動検出モードで、PathRibbonMesh2Dで可視化- 各RigidBody2Dには
linear_damp=10.0、angular_damp=10.0で減衰が設定されています
2. testrope_ball_3.tscn(ボール付きチェーンテストオブジェクト)
目的
2つのチェーン(UnbreakableChainとBreakableChain)と、ボール(破壊可能なスプライト)をテストするオブジェクトです。
構造
Ball (Area2DGameObject)
├── ChainJointSetupTool (チェーン自動セットアップツール)
│ ├── target_parent: ../UnbreakableChain
│ └── auto_create_seg_nodes: false
├── UnbreakableChain (Node2D)
│ ├── Path2D (RopePathBinder2Dスクリプトがアタッチ)
│ ├── RigidBody2D (先頭、freeze=trueで固定)
│ │ ├── CollisionShape2D
│ │ ├── seg1
│ │ └── seg2
│ ├── RigidBody2D2~RigidBody2D6 (中間リンク)
│ │ └── GrooveJoint2D (各々前のRigidBody2Dに接続)
│ │ ├── bias: 0.9
│ │ ├── length: 8.0
│ │ └── initial_offset: 1.0
│ ├── RigidBody2D7 (末尾、ボール)
│ │ ├── mass: 8.0 (他の3.0より重い)
│ │ ├── CollisionShape2D (CircleShape2D)
│ │ ├── AttackArea2D (攻撃判定)
│ │ ├── Sprite2D (ball.png)
│ │ ├── Node2D/CollisionShape2D (CutterShapeGroup、破壊用)
│ │ └── GrooveJoint2D
│ ├── ChainPathBinder2D
│ │ ├── auto_detect_mode: true
│ │ ├── search_root: ..
│ │ ├── output_paths: [../Path2D]
│ │ └── joint_monitor: ../BreakableJointMonitor2D
│ ├── BreakableJointMonitor2D
│ │ ├── search_root: ..
│ │ ├── groove_end_epsilon_pixels: 0.0
│ │ ├── joint_params: [JointBreakParams] (個別設定)
│ │ │ └── joint_path: ../RigidBody2D2/GrooveJoint2D
│ │ │ └── groove_break_angle_degrees: 90.0
│ │ └── use_break_delay_frames: false
│ │ └── break_delay_frames: 10
│ └── PathRibbonMesh2D (chain.pngテクスチャ)
├── BreakableChain (Node2D)
│ ├── RigidBody2D (先頭、freeze=true、collision_layer=0)
│ ├── RigidBody2D2~RigidBody2D6 (中間リンク)
│ │ └── GrooveJoint2D
│ ├── RigidBody2D6
│ │ └── GrooveJoint2D2 (UnbreakableChain/RigidBody2D7に接続)
│ ├── ChainPathBinder2D
│ ├── BreakableJointMonitor2D
│ └── PathRibbonMesh2D (rope.pngテクスチャ)
└── BooleanSpriteCutterWithTaggedShapes (スプライト破壊システム)
├── voronoi_seed_count: 30
└── disable_related_collisions_on_cut: true
ジョイント構造
-
UnbreakableChain: 7つのRigidBody2DがGrooveJoint2Dで接続
- 先頭のRigidBody2Dは
freeze=trueで固定 - 末尾のRigidBody2D7は質量8.0で重い
- 2番目のGrooveJoint2Dには個別の
JointBreakParamsが設定され、角度90度以上で0.5秒継続したら切断
- 先頭のRigidBody2Dは
-
BreakableChain: 6つのRigidBody2DがGrooveJoint2Dで接続
- 先頭のRigidBody2Dは
freeze=true、collision_layer=0で衝突無効 - RigidBody2D6には2つのGrooveJoint2Dがあり、1つはUnbreakableChain/RigidBody2D7に接続(2つのチェーンを連結)
- 先頭のRigidBody2Dは
設定の特徴
ChainJointSetupToolが配置されているが、auto_create_seg_nodes=falseなので手動でsegノードを配置RopePathBinder2DスクリプトがPath2Dにアタッチされている(UnbreakableChain用)BooleanSpriteCutterWithTaggedShapesでスプライト破壊が可能
3. player.tscn(プレイヤー)
目的
プレイヤーキャラクターで、RigidBodyPusherNaturalを使用してRigidBody2Dを押し出す機能を持ちます。
構造
Player (GameObject / CharacterBody2D)
├── MoveAndJumpSettings
├── BaseSettings
├── CollisionShape2D (CircleShape2D, radius: 25.02)
├── AnimationPlayer
├── VariableSettings
├── Node2D (RigidBodyPusherNatural)
│ ├── script: rigid_body_pusher.gd
│ ├── min_owner_position_delta_pixels: 1.0
│ ├── min_owner_real_speed: 1.0
│ ├── push_strength: 2000.0
│ ├── min_push_force: 100.0
│ ├── max_force: 2000.0
│ ├── max_impulse: 2000.0
│ ├── mass_influence: 1.0
│ ├── normal_direction_weight: 0.0
│ └── movement_direction_weight: 1.0
├── Connector
└── Sprite2D (mech.png)
設定の特徴
normal_direction_weight=0.0、movement_direction_weight=1.0で、移動方向のみで押し出しますpush_strength=2000.0で強く押し出しますmass_influence=1.0で質量を完全に考慮します
4. areabreaker.tscn(エリアブレーカーテストオブジェクト)
目的
AreaJointBreaker2Dを使用して、Area2D範囲内のジョイントを一括切断する機能をテストするオブジェクトです。
構造
Areabreaker (Area2DGameObject)
├── AreaJointBreaker2D (Node2D)
│ ├── script: area_joint_breaker.gd
│ ├── target_area: Area2D
│ └── rotation: 0.952787
│ └── Area2D
│ └── CollisionShape2D (RectangleShape2D, size: 155x35)
├── BaseSettings
├── CollisionShape2D (disabled)
├── AnimationTree (AnimationPlayer)
│ └── cut アニメーション
│ └── AreaJointBreaker2D.break_joints_in_target_area() を呼ぶ
├── PathRibbonMesh2D (切断エフェクト用)
│ ├── path_nodes: [../Path2D]
│ ├── width: 3.0
│ └── progress_percent, start_percent がアニメーションで制御
└── Path2D (切断エフェクトのパス)
設定の特徴
AreaJointBreaker2Dのtarget_areaにArea2Dが設定されています- アニメーションで
break_joints_in_target_area()を呼び出します PathRibbonMesh2Dで切断エフェクトを可視化します
5. game_scene.tscn(メインゲームシーン)
目的
メインのゲームシーンで、各種オブジェクトを配置します。
構造
GameScene
├── SceneLayer
│ ├── DistantView (Parallax2D)
│ ├── MiddleView (Parallax2D)
│ ├── NearView (Parallax2D)
│ ├── WallLayer (Parallax2D)
│ │ ├── Wall (TileMapLayer)
│ │ └── WallDecoration (TileMapLayer)
│ └── BaseLayer (Parallax2D)
│ └── Base (TileMapLayer)
│ ├── Testrope2 (testrope_2.tscnのインスタンス)
│ │ └── position: (-564, -17)
│ │ └── rotation: 0.0794073
│ ├── StaticBody2D (床)
│ ├── Player (player.tscnのインスタンス)
│ │ └── position: (-612, -80)
│ ├── Ball (testrope_ball_3.tscnのインスタンス)
│ │ └── position: (121, 38)
│ ├── Platform1, Platform2 (platform_1.tscnのインスタンス)
│ └── Pillar (pillar.tscnのインスタンス)
│ └── position: (186, 118)
├── PostEffectLayer (CanvasLayer)
└── UI (CanvasLayer)
配置されているオブジェクト
Testrope2: ロープテストオブジェクト(位置: -564, -17)Player: プレイヤー(位置: -612, -80)Ball: ボール付きチェーンテストオブジェクト(位置: 121, 38)Platform1,Platform2: プラットフォームPillar: 柱(SpriteGroupに属し、破壊可能)
ジョイント使用箇所の詳細
1. testrope_2.tscn のジョイント構造
GrooveJoint2Dの配置
| ジョイント | 親 | node_a | node_b | 位置 | 設定 |
|---|---|---|---|---|---|
| StaticBody2D/PinJoint2D3 | StaticBody2D | .. | ../../Node2D/RigidBody2D | (12, 4) | 先頭固定 |
| RigidBody2D2/PinJoint2D2 | RigidBody2D2 | ../../RigidBody2D | .. | (0, -16) | RigidBody2D → RigidBody2D2 |
| RigidBody2D3/PinJoint2D2 | RigidBody2D3 | ../../RigidBody2D2 | .. | (0, -16) | RigidBody2D2 → RigidBody2D3 |
| RigidBody2D4/PinJoint2D2 | RigidBody2D4 | ../../RigidBody2D3 | .. | (0, -16) | RigidBody2D3 → RigidBody2D4 |
| RigidBody2D5/PinJoint2D2 | RigidBody2D5 | ../../RigidBody2D4 | .. | (0, -16) | RigidBody2D4 → RigidBody2D5 |
| RigidBody2D6/PinJoint2D2 | RigidBody2D6 | ../../RigidBody2D5 | .. | (0, -16) | RigidBody2D5 → RigidBody2D6 |
| RigidBody2D7/PinJoint2D2 | RigidBody2D7 | ../../RigidBody2D6 | .. | (0, -16) | RigidBody2D6 → RigidBody2D7 |
| StaticBody2D2/PinJoint2D2 | StaticBody2D2 | ../../Node2D/RigidBody2D7 | .. | (-13, 6) | 末尾固定 |
共通設定
bias: 0.2(低めの値で柔らかい接続)length: 8.0(溝の長さ)initial_offset: 2.0(初期オフセット)
監視設定
BreakableJointMonitor2Dが角度80度以上で0.3秒継続したら切断ChainPathBinder2Dが自動検出モードでパスを生成
2. testrope_ball_3.tscn のジョイント構造
UnbreakableChain の GrooveJoint2D
| ジョイント | 親 | node_a | node_b | 設定 |
|---|---|---|---|---|
| RigidBody2D2/GrooveJoint2D | RigidBody2D2 | ../../RigidBody2D | .. | bias: 0.9, length: 8.0, initial_offset: 1.0 |
| RigidBody2D3/GrooveJoint2D | RigidBody2D3 | ../../RigidBody2D2 | .. | 同上 |
| RigidBody2D4/GrooveJoint2D | RigidBody2D4 | ../../RigidBody2D3 | .. | 同上 |
| RigidBody2D5/GrooveJoint2D | RigidBody2D5 | ../../RigidBody2D4 | .. | 同上 |
| RigidBody2D6/GrooveJoint2D | RigidBody2D6 | ../../RigidBody2D5 | .. | 同上 |
| RigidBody2D7/GrooveJoint2D | RigidBody2D7 | ../../RigidBody2D6 | .. | 同上 |
BreakableChain の GrooveJoint2D
| ジョイント | 親 | node_a | node_b | 設定 |
|---|---|---|---|---|
| RigidBody2D2/GrooveJoint2D | RigidBody2D2 | ../../RigidBody2D | .. | bias: 0.9, length: 8.0, initial_offset: 1.0 |
| RigidBody2D3/GrooveJoint2D | RigidBody2D3 | ../../RigidBody2D2 | .. | 同上 |
| RigidBody2D4/GrooveJoint2D | RigidBody2D4 | ../../RigidBody2D3 | .. | 同上 |
| RigidBody2D5/GrooveJoint2D | RigidBody2D5 | ../../RigidBody2D4 | .. | 同上 |
| RigidBody2D6/GrooveJoint2D | RigidBody2D6 | ../../RigidBody2D5 | .. | 同上 |
| RigidBody2D6/GrooveJoint2D2 | RigidBody2D6 | .. | ../../../UnbreakableChain/RigidBody2D7 | 同上(2つのチェーンを連結) |
個別設定
RigidBody2D2/GrooveJoint2DにはJointBreakParamsが設定され、角度90度以上で0.5秒継続したら切断
監視設定
UnbreakableChainとBreakableChainそれぞれにBreakableJointMonitor2DとChainPathBinder2Dが設定されています
システム連携
1. チェーン可視化システム
testrope_2.tscn
ChainPathBinder2DがNode2D配下のRigidBody2Dを自動検出- 各RigidBody2Dの
seg1とseg2を節点として使用 Path2DのCurve2Dを更新PathRibbonMesh2Dがbelt.pngテクスチャでリボンメッシュを描画
testrope_ball_3.tscn
UnbreakableChainとBreakableChainそれぞれにChainPathBinder2Dが設定UnbreakableChainはchain.png、BreakableChainはrope.pngで描画UnbreakableChainのPath2DにはRopePathBinder2Dスクリプトがアタッチ(別の可視化方法)
2. ジョイント切断システム
testrope_2.tscn
BreakableJointMonitor2Dが全ジョイントを監視- 角度80度以上で0.3秒継続したら切断
- 切断時に
ChainPathBinder2Dが自動でパスを分割
testrope_ball_3.tscn
UnbreakableChainのBreakableJointMonitor2Dは個別設定ありRigidBody2D2/GrooveJoint2Dのみ角度90度以上で0.5秒継続したら切断- 他のジョイントはグローバル設定(無効)を使用
3. プレイヤー押し出しシステム
player.tscn
RigidBodyPusherNaturalがCharacterBody2Dのmove_and_slide()後に実行- 接触した
RigidBody2Dを移動方向に押し出します push_strength=2000.0で強く押し出します
4. エリア切断システム
areabreaker.tscn
AreaJointBreaker2DがArea2D範囲内のジョイントを一括切断- アニメーションで
break_joints_in_target_area()を呼び出します PathRibbonMesh2Dで切断エフェクトを可視化
プロジェクト設定
Autoload
FragmentManagerSingleton: 破片管理システム(fragment_manager.tscnオブジェクト)
グローバルグループ
CutterShapeGroup: スプライト切断用のマスク形状グループSpriteGroup: スプライト切断対象グループ
Input Map
right: 右移動(右矢印キー)left: 左移動(左矢印キー)jump: ジャンプ(Space)cut: 切断(Z)
使用されているスクリプトの配置
testrope_2.tscn
BreakableJointMonitor2D(scripts/breakable_joint_monitor_2d.gd)ChainPathBinder2D(scripts/chain_path_binder.gd)PathRibbonMesh2D(scripts/path_ribbon_mesh_2d.gd)ChainPartResource(scripts/chain_part.gd) - リソースとして7つ
testrope_ball_3.tscn
ChainJointSetupTool(scripts/chain_setup_tool.gd)RopePathBinder2D(scripts/rope_solver.gd) - Path2DにアタッチChainPathBinder2D(scripts/chain_path_binder.gd) - 2つBreakableJointMonitor2D(scripts/breakable_joint_monitor_2d.gd) - 2つJointBreakParams(scripts/joint_break_params.gd) - リソースとして1つPathRibbonMesh2D(scripts/path_ribbon_mesh_2d.gd) - 2つBooleanSpriteCutterWithTaggedShapes(scripts/boolean_sprite_cutter_with_tagged_shapes.gd)
player.tscn
RigidBodyPusherNatural(scripts/rigid_body_pusher.gd)
areabreaker.tscn
AreaJointBreaker2D(scripts/area_joint_breaker.gd)PathRibbonMesh2D(scripts/path_ribbon_mesh_2d.gd)
ジョイントの種類と使用状況
GrooveJoint2D
使用箇所
testrope_2.tscn: 7つのRigidBody2D間 + 2つのStaticBody2D固定用 = 合計9個testrope_ball_3.tscn:- UnbreakableChain: 6個(RigidBody2D2~RigidBody2D7)
- BreakableChain: 6個(RigidBody2D2~RigidBody2D6)+ 1個(UnbreakableChain連結用)= 合計7個
設定パターン
-
testrope_2.tscn パターン
bias: 0.2(柔らかい)length: 8.0initial_offset: 2.0
-
testrope_ball_3.tscn パターン
bias: 0.9(硬い)length: 8.0initial_offset: 1.0
PinJoint2D
使用箇所
- 本プロジェクトでは使用されていません(すべてGrooveJoint2D)
チェーンの構造パターン
パターン1: 単一チェーン(testrope_2.tscn)
StaticBody2D (固定)
└── GrooveJoint2D
└── RigidBody2D1
└── GrooveJoint2D
└── RigidBody2D2
└── GrooveJoint2D
└── ...
└── RigidBody2D7
└── GrooveJoint2D
└── StaticBody2D (固定)
パターン2: 分岐チェーン(testrope_ball_3.tscn)
UnbreakableChain:
RigidBody2D1 (freeze)
└── GrooveJoint2D
└── RigidBody2D2
└── GrooveJoint2D
└── ...
└── RigidBody2D7 (ボール)
BreakableChain:
RigidBody2D1 (freeze)
└── GrooveJoint2D
└── RigidBody2D2
└── GrooveJoint2D
└── ...
└── RigidBody2D6
├── GrooveJoint2D (BreakableChain内)
└── GrooveJoint2D2 (UnbreakableChain/RigidBody2D7に接続)
物理設定
RigidBody2D の設定
testrope_2.tscn
linear_damp: 10.0(線形減衰)angular_damp: 10.0(角減衰)
testrope_ball_3.tscn
mass: 3.0(通常のリンク)mass: 8.0(RigidBody2D7、ボール部分)freeze: true(先頭のRigidBody2D)continuous_cd: 2(連続衝突検出)
可視化システム
PathRibbonMesh2D の使用
testrope_2.tscn
- テクスチャ:
belt.png - 幅: 20.0
testrope_ball_3.tscn
- UnbreakableChain:
chain.png、幅: 20.0 - BreakableChain:
rope.png、幅: 20.0
areabreaker.tscn
- テクスチャ:
GradientTexture2D(グラデーション) - 幅: 3.0(アニメーションで変化)
破壊システム
BooleanSpriteCutterWithTaggedShapes
testrope_ball_3.tscn
voronoi_seed_count: 30(破片の細かさ)disable_related_collisions_on_cut: true(切断時に周辺コリジョンを無効化)UnbreakableChain/RigidBody2D7/Node2D/CollisionShape2DがCutterShapeGroupに属し、破壊用マスクとして使用
まとめ
本プロジェクトでは、以下のような構造でジョイント・チェーン・ロープシステムが実装されています:
- testrope_2.tscnオブジェクト: 7リンクのチェーンをGrooveJoint2Dで接続、角度監視で切断可能
- testrope_ball_3.tscnオブジェクト: 2つのチェーン(UnbreakableChainとBreakableChain)を連結、個別設定で一部のみ切断可能
- player.tscnオブジェクト: RigidBodyPusherNaturalでRigidBody2Dを押し出し
- areabreaker.tscnオブジェクト: AreaJointBreaker2Dで範囲内のジョイントを一括切断
すべてのチェーンはChainPathBinder2Dで可視化され、PathRibbonMesh2Dでリボンメッシュとして描画されます。