refactor: projectiles logic
- add hitscan - add camera arrow key move
This commit is contained in:
parent
579fc26423
commit
868810ca82
11 changed files with 137 additions and 124 deletions
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
|
||||
[node name="Projectile" instance=ExtResource("1_d01p1")]
|
||||
speed = 15
|
||||
damage = 2
|
||||
|
||||
[node name="HitBox" parent="." index="2"]
|
||||
collision_mask = 2
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
19
Projectiles/Scenes/projectile-maxence.tscn
Normal file
19
Projectiles/Scenes/projectile-maxence.tscn
Normal file
|
|
@ -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")
|
||||
|
|
@ -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"]
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue