From 868810ca828c33127fbb570484eb4093136d5b35 Mon Sep 17 00:00:00 2001 From: Varylios Date: Sat, 20 Sep 2025 19:58:50 +0200 Subject: [PATCH] refactor: projectiles logic - add hitscan - add camera arrow key move --- Globals/EventBus.gd | 2 +- Levels/Scripts/Camera.gd | 26 ++- Levels/Scripts/WorldManager.gd | 4 +- Projectiles/Projectile.gd | 170 +++++++++----------- Projectiles/Scenes/projectile-Aline.tscn | 12 +- Projectiles/Scenes/projectile-James.tscn | 3 +- Projectiles/Scenes/projectile-Pierre.tscn | 1 - Projectiles/Scenes/projectile-Victoria.tscn | 13 +- Projectiles/Scenes/projectile-maxence.tscn | 19 +++ Projectiles/projectile.tscn | 7 +- Towers/Scenes/maxence.tscn | 4 +- 11 files changed, 137 insertions(+), 124 deletions(-) create mode 100644 Projectiles/Scenes/projectile-maxence.tscn diff --git a/Globals/EventBus.gd b/Globals/EventBus.gd index 9b81764..b03b7b8 100644 --- a/Globals/EventBus.gd +++ b/Globals/EventBus.gd @@ -29,7 +29,7 @@ signal team_in_rest_changed(count : int) ## [b]Emitter[/b] : [Projectile][br] ## [b]Subscriber[/b] : [code]null[/code] -signal projectile_shooted(projectile: Projectile, startPosition: Vector3) +signal projectile_shooted(projectile: Projectile, transform: Transform3D) ## [b]Emitter[/b] : [WorldManager][br] diff --git a/Levels/Scripts/Camera.gd b/Levels/Scripts/Camera.gd index e2ad8af..2a32ad1 100644 --- a/Levels/Scripts/Camera.gd +++ b/Levels/Scripts/Camera.gd @@ -22,12 +22,12 @@ func _process(_delta: float) -> void: var mousePosition : Vector2 = get_viewport().get_mouse_position() if mousePosition.x < SCREEN_MARGIN: - position.x += -SCREEN_MOVEMENT_SPEED - if mousePosition.y < SCREEN_MARGIN: - position.z += -SCREEN_MOVEMENT_SPEED - if mousePosition.x > windowSize.x - SCREEN_MARGIN: + position.x -= SCREEN_MOVEMENT_SPEED + elif mousePosition.x > windowSize.x - SCREEN_MARGIN: position.x += SCREEN_MOVEMENT_SPEED - if mousePosition.y > windowSize.y - SCREEN_MARGIN: + if mousePosition.y < SCREEN_MARGIN: + position.z -= SCREEN_MOVEMENT_SPEED + elif mousePosition.y > windowSize.y - SCREEN_MARGIN: position.z += SCREEN_MOVEMENT_SPEED @@ -38,9 +38,13 @@ func _notification(what: int) -> void: func _input(event: InputEvent) -> void: - if not event is InputEventMouseButton: - return + if event is InputEventMouseButton: + onEventMouseButton(event) + elif event is InputEventKey: + onEventKey(event) + +func onEventMouseButton(event: InputEventMouseButton) -> void: var newPosition : float = position.y match event.button_index: MOUSE_BUTTON_WHEEL_UP: newPosition -= .2 @@ -50,3 +54,11 @@ func _input(event: InputEvent) -> void: rotation.x = deg_to_rad(curve.sample(newPosition)) position.y = newPosition + + +func onEventKey(event: InputEventKey) -> void: + match event.keycode: + KEY_LEFT: position.x -= SCREEN_MOVEMENT_SPEED * 4 + KEY_RIGHT: position.x += SCREEN_MOVEMENT_SPEED * 4 + KEY_UP: position.z -= SCREEN_MOVEMENT_SPEED * 4 + KEY_DOWN: position.z += SCREEN_MOVEMENT_SPEED * 4 diff --git a/Levels/Scripts/WorldManager.gd b/Levels/Scripts/WorldManager.gd index 2167566..34cf397 100644 --- a/Levels/Scripts/WorldManager.gd +++ b/Levels/Scripts/WorldManager.gd @@ -118,6 +118,6 @@ func addMap(mapScene : PackedScene) -> void: state = STATE.SPAWN if level.auto_start else STATE.IDLE -func onProjectileShooted(projectile: Projectile, startPosition: Vector3) -> void: +func onProjectileShooted(projectile: Projectile, _transform: Transform3D) -> void: add_child(projectile) - projectile.global_position = startPosition + projectile.transform = _transform diff --git a/Projectiles/Projectile.gd b/Projectiles/Projectile.gd index 3d88299..35f3c0b 100644 --- a/Projectiles/Projectile.gd +++ b/Projectiles/Projectile.gd @@ -2,27 +2,35 @@ extends CharacterBody3D class_name Projectile -enum MODE { - FOLLOW, ## Follow Entity - LOCATION, ## Go to entity location - HITSCAN, ## DANGER NOT implemented yet +enum Mode { + FOLLOW, ## Follow target + LOCATION, ## Go to target location + SPAWN_ON_TARGET, ## Spawn on target location + HITSCAN, ## Spawn on target location } -enum TYPE { ## Types of projectiles - BASIC, ## One target - AOE, ## Multiple targets[br]work with [member damageArea] - PIERCING, ## Piercing through enemies[br]work with [member maxTarets] and [member damageArea] - BOUNCING, ## Bouncing over enemies[br]work with [member maxTarets] and [member damageArea] - DISABLING, ## Disable ally tower for [member damage] duration [br]Usable on [Boss] projectiles +enum Duration { + ONE_HIT, ## Make damage on hit + DAMAGE_OVER_TIME, ## Make damage every tick for specified duration[br]work with [member maxTargets] for number of tick +} + +enum Type { ## Types of projectiles + BASIC, ## One defined target + AOE, ## Multiple targets + BOUNCING, ## Bouncing over enemies[br]work with [member maxTargets] + DISABLING, ## Disable ally tower for [member amount] duration [br]Usable on [Boss] projectiles } -@export var type : TYPE = TYPE.BASIC -@export var mode : MODE = MODE.FOLLOW +@export var mode : Mode = Mode.FOLLOW +@export var duration : Duration = Duration.ONE_HIT +@export var type : Type = Type.BASIC @export var speed : int +## Usefull when [enum Type] is not [constant BASIC][br] +## [code]-1[/code] for no maximum +## Used as time when [enum Duration] is [constant HITSCAN] @export var maxTargets : int = 1 - var amount : float var target : PhysicsBody3D var vectorTarget : Vector3 @@ -31,93 +39,75 @@ var collidingBodies : Array[Node3D] var affectedTarget : Array[Node3D] +func _ready() -> void: + $HitBox.body_entered.connect(collidingBodies.append) + #$HitBox.body_entered.connect(test) + $HitBox.body_exited.connect(collidingBodies.erase) + $EffectArea.body_entered.connect(bodiesInRange.append) + $EffectArea.body_exited.connect(bodiesInRange.erase) + + +func test(body): + print(body) + +var firstTick : bool = true func _physics_process(_delta: float) -> void: - if mode == MODE.LOCATION && vectorTarget.distance_squared_to(global_position) < .4: - resolveContact() - maxTargets = 0 # Ensure queue free in next if + if firstTick: + firstTick = false + return + + resolveContact() if shouldQueueFree(): - return queue_free() + queue_free.call_deferred() + return - if not collidingBodies.is_empty() && collidingBodies.has(target): - return onBodyCollideWithProjectile(target) - - var globalPos : Vector3 = vectorTarget if vectorTarget else target.global_position - if target: + if mode == Mode.FOLLOW: + var globalPos : Vector3 = target.global_position globalPos.y += Helper.getHitBoxLocation(target, Helper.POSITION.CENTER) - look_at(globalPos) - velocity = global_position.direction_to(globalPos) * speed + look_at(globalPos) + velocity = global_position.direction_to(globalPos) * speed + move_and_slide() func shouldQueueFree() -> bool: - if maxTargets < 1: + if maxTargets == 0 || !is_instance_valid(target) && mode == Mode.FOLLOW: return true - - if !is_instance_valid(target): - return mode == MODE.FOLLOW + elif mode != Mode.FOLLOW: + return isOnTarget() elif target is Tower: return not target.visible return false -func onBodyEnteredDamageArea(body: Node3D) -> void: - if type != TYPE.BASIC && targetable(body): - addBodyInRange(body) - if type == TYPE.PIERCING: - resolveContact() - - -func onBodyCollideWithProjectile(body: Node3D) -> void: - if not collidingBodies.has(body): - collidingBodies.push_back(body) - - if mode != MODE.LOCATION && (body == target || type == TYPE.PIERCING) && targetable(body): - addBodyInRange(body, true) - resolveContact() - - -func addBodyInRange(body: Node3D, pushFront: bool = false) -> void: - var idx : int = bodiesInRange.find(body) - - if idx == -1: - if pushFront: - bodiesInRange.push_front(body) - else: - bodiesInRange.push_back(body) - elif pushFront && idx != 0: - bodiesInRange.remove_at(idx) - bodiesInRange.push_front(body) - - -func targetable(body: Node3D) -> bool: - return not affectedTarget.has(body) +func isOnTarget() -> bool: + match mode: + Mode.LOCATION, Mode.SPAWN_ON_TARGET: return vectorTarget.distance_squared_to(global_position) < .4 + Mode.FOLLOW: return collidingBodies.has(target) + Mode.HITSCAN: return true + _: return false func resolveContact() -> void: - if bodiesInRange.is_empty(): + if collidingBodies.is_empty() || not isOnTarget(): return - resolveEffect(bodiesInRange[0]) - match type: - TYPE.AOE: - for bodyInRange in bodiesInRange: - if is_instance_valid(bodyInRange): - resolveEffect(bodyInRange, false) - - TYPE.BOUNCING: - target = null if bodiesInRange.is_empty() else bodiesInRange[0] + Type.AOE: + collidingBodies.map(resolveEffect) + _ when collidingBodies.has(target): + resolveEffect(target) + if type == Type.BOUNCING: + target = null if bodiesInRange.is_empty() else bodiesInRange[0] -func resolveEffect(body : Node3D, erase : bool = true) -> void: - if erase: - bodiesInRange.erase(body) - if affectedTarget.has(body) || body is GameTile: +func resolveEffect(body : Node3D) -> void: + if affectedTarget.has(body) || body is GameTile || maxTargets == 0: return - if type == TYPE.DISABLING && body.has_method("disable"): + if type == Type.DISABLING && body.has_method("disable"): body.disable(amount) elif body.has_method("take_damage"): body.take_damage(amount) @@ -126,23 +116,23 @@ func resolveEffect(body : Node3D, erase : bool = true) -> void: maxTargets -= 1 -func removeTarget(body: Node3D) -> void: - bodiesInRange.erase(body) - - -func removeCollidingBody(body: Node3D) -> void: - collidingBodies.erase(body) - - func shoot(_target: Node3D, globalPos: Vector3) -> void: target = _target - match mode: - Projectile.MODE.HITSCAN: - globalPos = target.global_position - globalPos.y += Helper.getHitBoxLocation(target, Helper.POSITION.CENTER) - Projectile.MODE.LOCATION: - vectorTarget = target.global_position - vectorTarget.y += Helper.getHitBoxLocation(target, Helper.POSITION.CENTER) + var transform3D : Transform3D = Transform3D() + transform3D.origin = globalPos - EventBus.projectile_shooted.emit(self, globalPos) + var targetPosition : Vector3 = target.global_position + targetPosition.y += Helper.getHitBoxLocation(target, Helper.POSITION.CENTER) + transform3D = transform3D.looking_at(targetPosition) + match mode: + Mode.SPAWN_ON_TARGET: + transform3D.origin = targetPosition + transform3D = transform3D.looking_at(globalPos) + vectorTarget = targetPosition + Mode.LOCATION: + vectorTarget = targetPosition + velocity = transform3D.origin.direction_to(vectorTarget) * speed + + print(transform3D) + EventBus.projectile_shooted.emit(self, transform3D) diff --git a/Projectiles/Scenes/projectile-Aline.tscn b/Projectiles/Scenes/projectile-Aline.tscn index d68c3ab..79ac8fb 100644 --- a/Projectiles/Scenes/projectile-Aline.tscn +++ b/Projectiles/Scenes/projectile-Aline.tscn @@ -2,20 +2,20 @@ [ext_resource type="PackedScene" uid="uid://oykrff3g74eo" path="res://Projectiles/projectile.tscn" id="1_4tmpc"] -[sub_resource type="SphereShape3D" id="SphereShape3D_k24mn"] +[sub_resource type="SphereShape3D" id="SphereShape3D_2ioqj"] radius = 0.3 [node name="Projectile" instance=ExtResource("1_4tmpc")] -type = 1 mode = 1 +type = 1 speed = 3 -damage = 6 +maxTargets = -1 [node name="HitBox" parent="." index="2"] collision_mask = 2 +[node name="ProjectileSize" parent="HitBox" index="0"] +shape = SubResource("SphereShape3D_2ioqj") + [node name="DamageArea" parent="." index="3"] collision_mask = 2 - -[node name="Node" type="CollisionShape3D" parent="DamageArea" index="0"] -shape = SubResource("SphereShape3D_k24mn") diff --git a/Projectiles/Scenes/projectile-James.tscn b/Projectiles/Scenes/projectile-James.tscn index 06e8c6e..2508618 100644 --- a/Projectiles/Scenes/projectile-James.tscn +++ b/Projectiles/Scenes/projectile-James.tscn @@ -4,9 +4,8 @@ [ext_resource type="Texture2D" uid="uid://b7jiyk3w5tl02" path="res://Assets/Icones/Spritesheet_Cakes_WITH_OUTLINE.png" id="2_ckawd"] [node name="Projectile" instance=ExtResource("1_do0ca")] -type = 4 +type = 3 speed = 2 -damage = 3 [node name="Sprite3D" parent="." index="1"] transform = Transform3D(1.3, 0, 0, 0, 1.3, 0, 0, 0, 1.3, 0, 0, 0) diff --git a/Projectiles/Scenes/projectile-Pierre.tscn b/Projectiles/Scenes/projectile-Pierre.tscn index d9c0126..26b91e3 100644 --- a/Projectiles/Scenes/projectile-Pierre.tscn +++ b/Projectiles/Scenes/projectile-Pierre.tscn @@ -4,7 +4,6 @@ [node name="Projectile" instance=ExtResource("1_d01p1")] speed = 15 -damage = 2 [node name="HitBox" parent="." index="2"] collision_mask = 2 diff --git a/Projectiles/Scenes/projectile-Victoria.tscn b/Projectiles/Scenes/projectile-Victoria.tscn index 78bf3d2..46791c6 100644 --- a/Projectiles/Scenes/projectile-Victoria.tscn +++ b/Projectiles/Scenes/projectile-Victoria.tscn @@ -6,15 +6,12 @@ radius = 1.5 [node name="Projectile" instance=ExtResource("1_suva6")] -type = 3 +type = 2 speed = 5 maxTargets = 3 -[node name="HitBox" parent="." index="2"] -collision_mask = 2 - -[node name="DamageArea" parent="." index="3"] -collision_mask = 2 - -[node name="BoucingRange" type="CollisionShape3D" parent="DamageArea" index="0"] +[node name="DamageArea#BoucingRange" type="CollisionShape3D" parent="." index="0"] shape = SubResource("SphereShape3D_k24mn") + +[node name="HitBox" parent="." index="3"] +collision_mask = 2 diff --git a/Projectiles/Scenes/projectile-maxence.tscn b/Projectiles/Scenes/projectile-maxence.tscn new file mode 100644 index 0000000..d6141d2 --- /dev/null +++ b/Projectiles/Scenes/projectile-maxence.tscn @@ -0,0 +1,19 @@ +[gd_scene load_steps=3 format=3 uid="uid://baixm8pfsdo3t"] + +[ext_resource type="PackedScene" uid="uid://oykrff3g74eo" path="res://Projectiles/projectile.tscn" id="1_a1h27"] + +[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_a1h27"] +radius = 0.1 +height = 10.0 + +[node name="Projectile" instance=ExtResource("1_a1h27")] +mode = 3 +type = 1 +maxTargets = 5 + +[node name="HitBox" parent="." index="2"] +collision_mask = 2 + +[node name="ProjectileSize" parent="HitBox" index="0"] +transform = Transform3D(1, 0, 0, 0, -4.371139e-08, -1, 0, 1, -4.371139e-08, 0, 0, -5) +shape = SubResource("CapsuleShape3D_a1h27") diff --git a/Projectiles/projectile.tscn b/Projectiles/projectile.tscn index 3d0bee3..89f3037 100644 --- a/Projectiles/projectile.tscn +++ b/Projectiles/projectile.tscn @@ -30,11 +30,6 @@ collision_mask = 0 shape = SubResource("SphereShape3D_dsts2") debug_color = Color(0.926858, 0.237749, 0.335021, 0.42) -[node name="DamageArea" type="Area3D" parent="."] +[node name="EffectArea" type="Area3D" parent="."] collision_layer = 0 collision_mask = 0 - -[connection signal="body_entered" from="HitBox" to="." method="onBodyCollideWithProjectile" flags=3] -[connection signal="body_exited" from="HitBox" to="." method="removeCollidingBody"] -[connection signal="body_entered" from="DamageArea" to="." method="onBodyEnteredDamageArea"] -[connection signal="body_exited" from="DamageArea" to="." method="removeTarget"] diff --git a/Towers/Scenes/maxence.tscn b/Towers/Scenes/maxence.tscn index 2edd650..da57ed9 100644 --- a/Towers/Scenes/maxence.tscn +++ b/Towers/Scenes/maxence.tscn @@ -1,7 +1,8 @@ -[gd_scene load_steps=5 format=3 uid="uid://b1pg1hgysx3am"] +[gd_scene load_steps=6 format=3 uid="uid://b1pg1hgysx3am"] [ext_resource type="PackedScene" uid="uid://trg7ag3dqr2l" path="res://Towers/tower.tscn" id="1_laam8"] [ext_resource type="Texture2D" uid="uid://boxdrq4nrq7hv" path="res://Assets/Icones/flamingo.svg" id="2_sciv6"] +[ext_resource type="PackedScene" uid="uid://baixm8pfsdo3t" path="res://Projectiles/Scenes/projectile-maxence.tscn" id="3_7fox5"] [sub_resource type="SphereShape3D" id="SphereShape3D_pw4mj"] radius = 10.0 @@ -16,6 +17,7 @@ icone = ExtResource("2_sciv6") bio = "" price = 200 damage = 10 +projectileScene = ExtResource("3_7fox5") towerRange = SubResource("SphereShape3D_pw4mj") action_cooldown = 3.0 max_energy = 50.0