とりとめも

藻類が学んだり感じたりしたことを未来の自分のために書き留めるところ

【Godotお勉強メモ】マウスホイールでキャラクターの状態をぐるぐる循環して変更する

はじめに

キャラクターに追従して動く図形(攻撃範囲)を、マウスホイールでぐるぐる変更できるようにします。

前回のあらすじ

marimomemo.hatenablog.jp

なんか色々作って、敵とか攻撃機能ができました。

今回は

攻撃範囲を切り替えられるようにします。
マウスホイールをぐるぐる回して、事前に設定してある順番通りに切り替えます。
具体的には下図のような感じです。

マウスを下に回すと・・・

形状が変わる!

やっていきます

下準備

まずは細々とした準備から。

形状の配列を作成

Polygon2DやCollisionPolygon2Dの形状を切り替えるにあたり、各形状を事前に作成して配列として持っておきます。
このあたりのデータの持ち方も、規模が大きくなってきたら考える必要がありそうです。

以下のような感じで、attack_range_shapes配列に各関数で出力した形状を格納しておき、indexで取り出して使います。

var attack_range_shapes = [
    (calculate_fan_shape()),
    calculate_rectangle_shape(),
    calculate_triangle_shape()
    ]
var current_shape_index = 0

func calculate_fan_shape() -> PackedVector2Array:
    var center = Vector2()
    var radius = 100.0
    var angle_deg = 90.0
    var points = PackedVector2Array([center])
    var angle_step = angle_deg / 10
    for i in range(11):
        var rad = deg_to_rad(angle_step * i - angle_deg / 2)
        points.append(center + Vector2(cos(rad), sin(rad)) * radius)
    return points

func calculate_rectangle_shape() -> PackedVector2Array:
    var center = Vector2()
    var points = PackedVector2Array()
    var angle_deg = 90.0
    # 長方形の4つの頂点を計算
    points.append(center + Vector2(0, 20))  
    points.append(center + Vector2(200, 15))
    points.append(center + Vector2(200, -15)) 
    points.append(center + Vector2(0, -20))  
    return points

func calculate_triangle_shape() -> PackedVector2Array:
    var center = Vector2()
    var points = PackedVector2Array()
    # 三角形の3つの頂点を計算
    points.append(center + Vector2(0, 0)) 
    points.append(center + Vector2(30, 150))  
    points.append(center + Vector2(30, -150)) 
    return points

インプットマップの設定

例によって、マウスホイール用のインプットマップを設定します。
名前はwheel_downとwheel_upです。

いつもの

それでは本格的に作っていきます。

攻撃範囲(AttackRangeシーン)の修正

攻撃範囲の形状をindexで指定できるようにする

先ほど作った形状配列を使用して、番号を指定すればその要素の形状へと変更してくれる関数を作ります。

func change_attack_range_shape(shape_num):
    # 配列の長さを越えたshape_numにも対応させる
    var new_index = shape_num % attack_range_shapes.size()
    var new_shape = attack_range_shapes[new_index]
    polygon2D.polygon = new_shape
    collision_polygon.polygon = new_shape
    current_shape_index = new_index

ここでのポイントははじめのvar new_index = shape_num % attack_range_shapes.size()です。
たとえばホイールを下に回し続けるとindexが1ずつ増加し続ける想定ですが、どれだけ大きい数が指定されても配列のindexに収まるようにモジュロ演算してあげます。

また、初期の形状もこの関数で設定するため、_ready関数も修正します。

@onready var polygon2D = $Polygon2D
@onready var collision_polygon = $CollisionPolygon2D
func _ready():
    # Polygon2DとCollisionPolygon2Dの形状を設定
    change_attack_range_shape(current_shape_index)

    # 半透明の青色にする
    polygon2D.color = Color(0, 0, 100, 0.5) 

ホイール上下時の処理を作る

次に、ホイールが上下したときの想定で、配列の前後の形状に変化させる関数を作ります。
indexの循環に関する処理はchange_attack_range_shapeで済んでいるので、ここでは単純に現在のindexに1加算・減算するだけで大丈夫です。

# ホイールアップ時、攻撃範囲形状を前のものに変更する
func previous_attack_range_shape():
    change_attack_range_shape(current_shape_index - 1)

# ホイールダウン時、攻撃範囲形状を次のものに変更する
func next_attack_range_shape():
    change_attack_range_shape(current_shape_index + 1)

これで攻撃範囲側の準備はおしまいです。

Playerシーンの修正

次に、Playerシーン側で、ホイールが上下されるたびに攻撃範囲を変更する処理を追加します。
といっても既存の_process関数に追加してあげるだけですね。

func _process(delta):
    if Input.is_action_just_pressed("attack"):
        attack()
    if Input.is_action_just_pressed("wheel_up"):
        attack_range.previous_attack_range_shape()
    if Input.is_action_just_pressed("wheel_down"):
        attack_range.next_attack_range_shape()

これでホイールを回せば、プレイヤーにくっついている攻撃範囲の形状が変わるようになります。

コリジョンの形状も同期しているので、ちゃんと範囲内の敵に攻撃できます。

変な攻撃範囲

おわりに(考え事)

途中で書いた通り、形状を事前に準備しておく方法は何か考えたいですね。
AttackRangeノードに直書きというのも違和感がありますし、「プレイヤーが変更可能な攻撃範囲(習得済の技みたいな)」を管理したい場合、形状に関するデータは別に持っておき、複数箇所で流用できるのが正しい気がします。

また、今回、ホイールイベントの検知はPlayer側で行い、attack_rangeの関数を呼び出すようにしていました。
ただ、イベントの検出から実際の攻撃範囲切り替えまで、attack_range側に処理を集約するのもありな気がしています。
このあたりの役割分担の考え方も、たぶん世の中には様々な知見があると思うのですが、まだその調べ方もわかっていない段階です。

ともあれ今日はここまで。頑張りましょう。