2021-08-07 Godot Engine > 複数のインスタンスを生成して、Collisionレイヤとマスクで衝突判定をして、消す

### 動作環境

```
Windows 10 Pro v21H1
Godot Engine v3.3.2.stable.official
```

### 概要

- Main.tscn : メインのシーン
- Enemy.tscn : インスタンス生成のシーン
- MainにおいてEnemyを複数生成する
- 右クリックでPlayerの移動をする
- 「Audd inst」ボタン(Button)押下ごとにEnemyインスタンスを生成する
- Main上のPlayerインスタンスと、生成したEnemyインスタンスの衝突を判定する
    - Collisionレイヤとマスクを利用
- 衝突したEnemyインスタンスを削除する

Button2は確認用に実装した。

## 準備

### シーンとスクリプト

- Main.tscn > Main.gdをアタッチ
    - Node2D
    - Player : KinematicBody2D
        - Sprite : 自作の猫pngをセット
        - CollisionShape2D
    - Button
    - Button2
- Enemy.tscn
    - Node2D
    - Enemy : KinematicBody2D > Enemy.gdをアタッチ
        - Sprite : Godotのアイコンをセット
        - CollisionShape2D

### プロジェクト設定 > 2d Physics

以下のレイヤを設定した

- Layer1 : Player
- Layer2 : Enemy

### Collisionレイヤとマスク設定

- Main.tscn > Player
    - Layer: Playerレイヤ
    - Mask: Enemyレイヤ
- Enemy.tscn > Enemy
    - Layer: Enemyレイヤ

## 処理

### 削除の概要

- Enemy.tscn > EnemyのEnemy.gd側
    - move_and_collide()で衝突判定する
    - 衝突で得られたインスタンス(KinematicCollision2D)を削除する
- Main.tscn > Main.gd側
    - childが空になったインスタンスを削除対象として削除処理を行う

### 実装 > Main.gd

```python:Main.gd
extends Node2D

var enemies = []  # Enemy character list

func _input(event):
	if event is InputEventMouseButton:
		if event.button_index == 2:  # right
			$Player.move_and_collide(event.position - $Player.position)

func _process(delta):
	# clear list when child is freed
	for idx in range(enemies.size()):
		var achild = enemies[idx].get_child(0)
		if achild == null:
			print("remove:", idx)
			enemies[idx].queue_free()
			enemies.remove(idx)
			return

func _on_Button_pressed():
	for idx in range(enemies.size()):
		var elem = enemies[idx]
		var dist = elem.transform.origin.distance_to($Player.transform.origin)
		print("%d, %f" % [ idx, dist ])
	make_child()

func make_child():
	var name = "res://Enemy.tscn"
	var scene = load(name)
	var instance = scene.instance()
	enemies.append(instance)
	add_child(instance)
	var posx = 100 * enemies.size()
	instance.position = Vector2(posx, 200)

func _on_Button2_pressed():  # for debug
	for idx in range(enemies.size()):
		print(idx, ",", enemies[idx])
		var child = enemies[idx].get_child(0)
		print(child)
```

### 実装 > Enemy.gd

```python:Enemy.gd
extends KinematicBody2D

func _process(delta):
	var coll = move_and_collide(Vector2.ZERO * delta)
	if coll != null:
		print("Enemy:", coll)
		queue_free()
```

## 実行例

削除前:
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32870/3f82f351-ab40-7f07-338a-d386ae2bec3c.png)

削除後:
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32870/5645624c-3b1c-5880-e770-cae529fd5b6b.png)


## 備考

- 衝突をしても消えない場合もある

以下のPlayer.gdをPlayerにアタッチして動作確認したところ、衝突時の削除は自然になった。

```python:Player.gd
extends KinematicBody2D

var shift_right =  Vector2(32, 0) * 4
var shift_left  = -Vector2(32, 0) * 4
var shift_up    = -Vector2(0, 32) * 4
var shift_down  =  Vector2(0, 32) * 4

const CHARACTER_SIZE = 64  # 64 x 64
const DISP_WIDTH = 1024
const DISP_HEIGHT = 600

func myMoveAndCollide(delta):
	if position.x < CHARACTER_SIZE / 2:
		position.x = CHARACTER_SIZE / 2
		return false
	if position.x > DISP_WIDTH:
		position.x = DISP_WIDTH
		return false
	if position.y < CHARACTER_SIZE / 2:
		position.y = CHARACTER_SIZE / 2
		return false
	if position.y > DISP_HEIGHT:
		position.y = DISP_HEIGHT
		return false
	return move_and_collide(delta)

func _process(delta):
	# acceleration
	var accel = 1.0
	var collide
	if Input.is_action_pressed("ui_accept"):
		accel = 2.0
	# movement
	if Input.is_action_pressed("ui_right"):
		collide = myMoveAndCollide(shift_right * delta * accel)
	if Input.is_action_pressed("ui_left"):
		collide = myMoveAndCollide(shift_left * delta * accel)
	if Input.is_action_pressed("ui_up"):
		collide = myMoveAndCollide(shift_up * delta * accel)
	if Input.is_action_pressed("ui_down"):
		collide = myMoveAndCollide(shift_down * delta * accel)
```

## 関連

- [2021-08-07 Godot Engine > 猫とiconの衝突判定 > (キャラクタをカーソルで移動, キャラクタをマウスで移動, 衝突判定)](https://qiita.com/7of9/items/06dd48cc23dfde47929c)