Résumé de cette [[ https://wiki.bipboup.club/doku.php?id=godot:strategie_pattern&do=edit|vidéo]] \\
Strategy Pattern :
Idée c'est de faire du code modulable. Le code fait appel à une fonction qui est ailleurs.
On crée un script global avec une fonction qui ne fait rien (pass ou autre) puis des sous scripts qui étendent le script global, avec des fonctions spécifiques.
Exemple avec un filtre de nombres, on aurait :
class_name NumFilter #on déclare le script global NumFilter
func is_filtered(num) -> bool : #fonction is filtered qui vérifie si on filtre ou non (vrai,faux condition booléenne)
return false #ne filtre rien
class_name EvenNumFilter #filtre les nombres pairs
extends Num Filter
func is_filtered(num) -> bool :
return num % 2 == 0 #ça dit si le numéro modulo deux fait bien 0, donc pair, donc tej
class_name NegativeNumFilter #filtre les nombres négatifs
extends Num Filter
func is_filtered(num) -> bool :
return num < 0 #ça dit si le numéro est inférieur à zéro, donc négatif, donc tej
Une fois ces filtres créés on peut les utiliser comme ça :
var filter = EvenNumFilter.new()
filter_list(filter)
func filter_list(filter : NumFilter): #on met dans la fonction la variable filter qui a la classe NumFilter, donc les filtres puisque ils étendent (sont de la classe) NumFilter
var list = [-4, -2, 0, 1.35, 4, 5]
for x in list :
if filter.is_filtered(x):
list.remove(x)
Ici ça n'a pas énormément d'intérêt parce que ça rallonge le code mais en tout cas la partie filtre est complète, si on veut filtrer différemment il suffit de créer un autre script qui étend NumFilter avec une autre fonction, par exemple :
class_name FiveNumFilter
extends Num Filter
func is_filtered(num) -> bool :
return num % 5 == 0
Un cas où ça à de l'intérêt c'est s'il faut beaucoup de filtres différents (un rts par ex) ou dans le cas où il faut appliquer plusieurs upgrades. On peut avoir plusieurs stratégies, plusieurs scripts qui se rapportent à un, et passer l'objet (une balle par exemple) à travers chaque upgrade, chaque script d'une certaine class.
Donc on a la stratégie de base :
class_name BaseBulletStrategy
extends Resource
func apply_upgrade(bullet: Bullet):
pass
class_name DamageBulletStrategy
extends BaseBulletStrategy
func apply_upgrade(bullet: Bullet):
bullet.damage += 5.0
Ou même ajouter des éléments à la balle comme des particules :
class_name ParticleBulletStrategy
extends BaseBulletStrategy
var particle_scene : PackedScene = preload("res://Objects/Scenes/bullet_particles.tscn")
func apply_upgrade(bullet: Bullet):
var spawned_particles : Node2D = particle_scene.instantiate()
bullet.add_child(spawned_particles)
spawned_particles.global_position = bullet.global_position
Une fois qu'on a tout ça, on peut aller un cran plus loin et construire un système de ressources, c'est à dire des set de données qu'on attache à des objets qui vont utiliser les stratégies.
Alors on retravaille la stratégie de base pour quelle accepte une texture et un nom
class_name BaseBulletStrategy
extends Resource
@export var texture : Texture2D #une variable texture qui accepte une Texture2D
@export var upgrade_text : String #une variable texte qui accepte du texte
func apply_upgrade(bullet: Bullet):
pass
Ensuite on créer une ressource qu'on attache à des objets, une Area2D avec une CollisionShape2D, un sprite, un label, avec le script suivant
@export var upgrade_label : Label
@export var sprite : Sprite2D
@export var bullet_strategy : BaseBulletStrategy
func _ready() -> void:
body_entered.connect(on_body_entered)
sprite.texture = bullet_strategy.texture
upgrade_label.text = bullet_strategy.upgrade_text
func on_body_entered(body: PhysicsBody2D):
if body is Player:
body.upgrades.append(bullet_strategy) #on ajoute la stratégie dans la liste des upgrades du joueur
queue_free()
Finalement, l'arme applique toutes les stratégies comme cela
@onready var player : Player = get_owner()
for strategy in player.upgrades: #comme d'hab avec ça, strategy est un nom au hasard, c'est le nom de variable qu'on donne aux éléments dans player.upgrades
strategy.apply_upgrade(spawned_bullet)