mirror of
https://github.com/GDQuest/godot-platformer-2d.git
synced 2026-01-23 02:14:19 +00:00
New enemy type, a hopper who jumps back and forth. Invulnerability flag added to Stats, and a function to do so with a timer.
This commit is contained in:
parent
0dfc7a7ca1
commit
6ac88fa2ee
11 changed files with 207 additions and 11 deletions
|
|
@ -48,9 +48,6 @@ collision_layer = 16
|
|||
collision_mask = 2
|
||||
script = ExtResource( 1 )
|
||||
|
||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
|
||||
shape = SubResource( 1 )
|
||||
|
||||
[node name="Hitbox" type="Area2D" parent="."]
|
||||
monitorable = false
|
||||
collision_layer = 16
|
||||
|
|
@ -59,6 +56,9 @@ script = ExtResource( 2 )
|
|||
[node name="CollisionShape2D" type="CollisionShape2D" parent="Hitbox"]
|
||||
shape = SubResource( 1 )
|
||||
|
||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
|
||||
shape = SubResource( 1 )
|
||||
|
||||
[node name="HookTarget" parent="." instance=ExtResource( 3 )]
|
||||
editor/display_folded = true
|
||||
is_one_shot = true
|
||||
|
|
@ -69,7 +69,6 @@ shape = SubResource( 2 )
|
|||
[node name="Body" type="Node2D" parent="."]
|
||||
script = ExtResource( 4 )
|
||||
size = Vector2( 100, 40 )
|
||||
outline = Vector2( 6, 6 )
|
||||
color_fill = Color( 0.615686, 0, 1, 1 )
|
||||
color_outline = Color( 0.270588, 0.0745098, 0.619608, 1 )
|
||||
|
||||
|
|
@ -86,7 +85,6 @@ hooked_color = Color( 0.886275, 0.415686, 0.133333, 1 )
|
|||
|
||||
[node name="Stunned" type="Node" parent="StateMachine"]
|
||||
script = ExtResource( 8 )
|
||||
next_state = "Destroyed"
|
||||
|
||||
[node name="Destroyed" type="Node" parent="StateMachine"]
|
||||
script = ExtResource( 9 )
|
||||
|
|
|
|||
13
game/src/AI/HopperEnemy.gd
Normal file
13
game/src/AI/HopperEnemy.gd
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
extends KinematicBody2D
|
||||
|
||||
onready var hitbox: Area2D = $Hitbox
|
||||
onready var hook_target: HookTarget = $HookTarget
|
||||
onready var collider: CollisionShape2D = $CollisionShape2D
|
||||
onready var body: Node2D = $Body
|
||||
|
||||
export(int, 0, 360, 1) var jump_angle_left: = 45
|
||||
export(int, 0, 360, 1) var jump_angle_right: = 45
|
||||
export var jump_power_left: = 500
|
||||
export var jump_power_right: = 500
|
||||
export(int, -1, 1, 2) var direction: = 1
|
||||
export var gravity: = 6000.0
|
||||
92
game/src/AI/HopperEnemy.tscn
Normal file
92
game/src/AI/HopperEnemy.tscn
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
[gd_scene load_steps=13 format=2]
|
||||
|
||||
[ext_resource path="res://src/AI/HopperEnemy.gd" type="Script" id=1]
|
||||
[ext_resource path="res://src/Combat/DamageSource.gd" type="Script" id=2]
|
||||
[ext_resource path="res://src/Objects/HookTarget.tscn" type="PackedScene" id=3]
|
||||
[ext_resource path="res://src/Player/Rectangle.gd" type="Script" id=4]
|
||||
[ext_resource path="res://src/Main/StateMachine/StateMachine.gd" type="Script" id=5]
|
||||
[ext_resource path="res://src/AI/States/Jump.gd" type="Script" id=6]
|
||||
[ext_resource path="res://src/AI/States/Hooked.gd" type="Script" id=7]
|
||||
[ext_resource path="res://src/AI/States/Stunned.gd" type="Script" id=8]
|
||||
[ext_resource path="res://src/AI/States/Destroyed.gd" type="Script" id=9]
|
||||
|
||||
[sub_resource type="RectangleShape2D" id=2]
|
||||
extents = Vector2( 25, 25 )
|
||||
|
||||
[sub_resource type="RectangleShape2D" id=1]
|
||||
extents = Vector2( 25, 25 )
|
||||
|
||||
[sub_resource type="Animation" id=3]
|
||||
resource_name = "FadeOut"
|
||||
tracks/0/type = "value"
|
||||
tracks/0/path = NodePath("../../Body:color_outline")
|
||||
tracks/0/interp = 1
|
||||
tracks/0/loop_wrap = true
|
||||
tracks/0/imported = false
|
||||
tracks/0/enabled = true
|
||||
tracks/0/keys = {
|
||||
"times": PoolRealArray( 0, 1 ),
|
||||
"transitions": PoolRealArray( 1, 1 ),
|
||||
"update": 0,
|
||||
"values": [ Color( 0.0352941, 0.176471, 0.372549, 1 ), Color( 0.0745098, 0.133333, 0.619608, 0 ) ]
|
||||
}
|
||||
tracks/1/type = "value"
|
||||
tracks/1/path = NodePath("../../Body:color_fill")
|
||||
tracks/1/interp = 1
|
||||
tracks/1/loop_wrap = true
|
||||
tracks/1/imported = false
|
||||
tracks/1/enabled = true
|
||||
tracks/1/keys = {
|
||||
"times": PoolRealArray( 0, 0.2, 1 ),
|
||||
"transitions": PoolRealArray( 1, 1, 1 ),
|
||||
"update": 0,
|
||||
"values": [ Color( 0.760784, 0.203922, 0.658824, 1 ), Color( 1, 0, 0, 1 ), Color( 1, 0, 0, 0 ) ]
|
||||
}
|
||||
|
||||
[node name="HopperEnemy" type="KinematicBody2D"]
|
||||
collision_layer = 16
|
||||
collision_mask = 2
|
||||
script = ExtResource( 1 )
|
||||
|
||||
[node name="Hitbox" type="Area2D" parent="."]
|
||||
collision_layer = 16
|
||||
script = ExtResource( 2 )
|
||||
|
||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="Hitbox"]
|
||||
shape = SubResource( 2 )
|
||||
|
||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
|
||||
shape = SubResource( 1 )
|
||||
|
||||
[node name="HookTarget" parent="." instance=ExtResource( 3 )]
|
||||
|
||||
[node name="Body" type="Node2D" parent="."]
|
||||
script = ExtResource( 4 )
|
||||
size = Vector2( 50, 50 )
|
||||
color_fill = Color( 0.760784, 0.203922, 0.658824, 1 )
|
||||
color_outline = Color( 0.0352941, 0.176471, 0.372549, 1 )
|
||||
|
||||
[node name="StateMachine" type="Node" parent="."]
|
||||
script = ExtResource( 5 )
|
||||
initial_state = NodePath("Jump")
|
||||
|
||||
[node name="Jump" type="Node" parent="StateMachine"]
|
||||
script = ExtResource( 6 )
|
||||
|
||||
[node name="Cooldown" type="Timer" parent="StateMachine/Jump"]
|
||||
wait_time = 0.6
|
||||
one_shot = true
|
||||
|
||||
[node name="Hooked" type="Node" parent="StateMachine"]
|
||||
script = ExtResource( 7 )
|
||||
hooked_color = Color( 0.890196, 0.670588, 0.14902, 1 )
|
||||
|
||||
[node name="Stunned" type="Node" parent="StateMachine"]
|
||||
script = ExtResource( 8 )
|
||||
|
||||
[node name="Destroyed" type="Node" parent="StateMachine"]
|
||||
script = ExtResource( 9 )
|
||||
hurt_color = Color( 1, 0, 0, 1 )
|
||||
|
||||
[node name="AnimationPlayer" type="AnimationPlayer" parent="StateMachine/Destroyed"]
|
||||
anims/FadeOut = SubResource( 3 )
|
||||
|
|
@ -8,11 +8,14 @@ It simply waits until the player body collides with the hitbox, colors the body,
|
|||
|
||||
export var hooked_color: Color
|
||||
|
||||
var hook_position: Vector2
|
||||
|
||||
|
||||
func enter(msg: Dictionary = {}) -> void:
|
||||
owner.body.set_color_fill(hooked_color)
|
||||
hook_position = msg.hook_position
|
||||
owner.hitbox.connect("body_entered", self, "_on_Player_body_entered", [], CONNECT_ONESHOT)
|
||||
|
||||
|
||||
func _on_Player_body_entered(body: Player) -> void:
|
||||
_state_machine.transition_to("Stunned", {player = body})
|
||||
_state_machine.transition_to("Stunned", {player = body, hook_position = hook_position})
|
||||
|
|
@ -26,7 +26,7 @@ func physics_process(delta: float) -> void:
|
|||
|
||||
|
||||
func _on_Hook_hooked_onto_from(hook_position: Vector2) -> void:
|
||||
_state_machine.transition_to("Hooked")
|
||||
_state_machine.transition_to("Hooked", {hook_position = hook_position})
|
||||
|
||||
|
||||
func _on_Player_body_entered(player: Player) -> void:
|
||||
|
|
|
|||
59
game/src/AI/States/Jump.gd
Normal file
59
game/src/AI/States/Jump.gd
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
extends State
|
||||
|
||||
|
||||
onready var timer: Timer = $Cooldown
|
||||
onready var acceleration: = Vector2(0, owner.gravity)
|
||||
onready var move_direction: = Vector2(-owner.direction, 1)
|
||||
onready var jump_vector_right = Vector2(cos(deg2rad(owner.jump_angle_right)), -sin(deg2rad(owner.jump_angle_right)))
|
||||
onready var jump_vector_left = Vector2(cos(deg2rad(owner.jump_angle_left)), -sin(deg2rad(owner.jump_angle_left)))
|
||||
|
||||
var _velocity: = Vector2.ZERO
|
||||
var _jumping: = false
|
||||
|
||||
|
||||
func enter(msg: Dictionary = {}) -> void:
|
||||
owner.hitbox.connect("body_entered", self, "_on_Player_body_entered")
|
||||
owner.hook_target.connect("hooked_onto_from", self, "_on_Player_hooked_onto_from", [], CONNECT_ONESHOT)
|
||||
timer.connect("timeout", self, "_on_Cooldown_timeout")
|
||||
timer.start()
|
||||
|
||||
|
||||
func exit() -> void:
|
||||
owner.hitbox.disconnect("body_entered", self, "_on_Player_body_entered")
|
||||
|
||||
|
||||
func physics_process(delta: float) -> void:
|
||||
if not _jumping and owner.is_on_floor():
|
||||
return
|
||||
|
||||
_velocity = calculate_velocity(_velocity, acceleration, delta, move_direction)
|
||||
owner.move_and_slide(_velocity, Vector2.UP)
|
||||
if owner.is_on_floor():
|
||||
_jumping = false
|
||||
_velocity = Vector2.ZERO
|
||||
move_direction.x *= -1
|
||||
timer.start()
|
||||
|
||||
|
||||
func _on_Player_body_entered(player: Player) -> void:
|
||||
player.take_damage(Hit.new(owner.hitbox))
|
||||
|
||||
|
||||
func _on_Player_hooked_onto_from(hook_position: Vector2) -> void:
|
||||
_state_machine.transition_to("Hooked", {hook_position = hook_position})
|
||||
|
||||
|
||||
func _on_Cooldown_timeout() -> void:
|
||||
var jump_vector: Vector2 = jump_vector_right if move_direction.x > 0 else jump_vector_left
|
||||
var jump_power: float = owner.jump_power_right if move_direction.x > 0 else owner.jump_power_left
|
||||
_velocity += calculate_velocity(_velocity, jump_vector * jump_power * Vector2(1,-1), 1.0, jump_vector * move_direction)
|
||||
_jumping = true
|
||||
|
||||
|
||||
static func calculate_velocity(old_velocity: Vector2, acceleration: Vector2,
|
||||
delta: float, move_direction: Vector2) -> Vector2:
|
||||
var new_velocity: = old_velocity
|
||||
|
||||
new_velocity += move_direction * acceleration * delta
|
||||
|
||||
return new_velocity
|
||||
|
|
@ -6,15 +6,16 @@ State that connects to the player's signal about hopping off an entity,
|
|||
|
||||
|
||||
export var knock_back_speed: = 450.0
|
||||
export var next_state: String
|
||||
|
||||
var knocked_away: = false
|
||||
var current_speed: float
|
||||
var knock_back_direction: Vector2
|
||||
|
||||
var _player: Player
|
||||
|
||||
|
||||
func enter(msg: Dictionary = {}) -> void:
|
||||
knock_back_direction = (msg.hook_position - owner.global_position).normalized()
|
||||
knocked_away = false
|
||||
_player = msg.player
|
||||
_player.connect("hopped_off_entity", self, "_on_Player_hopped_off_entity", [], CONNECT_ONESHOT)
|
||||
|
|
@ -23,11 +24,11 @@ func enter(msg: Dictionary = {}) -> void:
|
|||
|
||||
func physics_process(delta: float) -> void:
|
||||
if knocked_away:
|
||||
owner.move_and_collide(Vector2.DOWN * delta * current_speed)
|
||||
owner.move_and_collide(knock_back_direction * delta * current_speed)
|
||||
current_speed *= 0.95
|
||||
|
||||
|
||||
func _on_Player_hopped_off_entity() -> void:
|
||||
knocked_away = true
|
||||
yield(get_tree().create_timer(0.5), "timeout")
|
||||
_state_machine.transition_to(next_state)
|
||||
_state_machine.transition_to("Destroyed")
|
||||
|
|
@ -12,6 +12,8 @@ signal damage_taken()
|
|||
|
||||
var modifiers = {}
|
||||
|
||||
var invulnerable: = false
|
||||
|
||||
var health: int
|
||||
export var max_health: int = 1 setget set_max_health
|
||||
|
||||
|
|
@ -24,6 +26,9 @@ func _ready() -> void:
|
|||
|
||||
|
||||
func take_damage(hit: Hit) -> void:
|
||||
if invulnerable:
|
||||
return
|
||||
|
||||
var old_health = health
|
||||
health -= hit.damage
|
||||
emit_signal("damage_taken")
|
||||
|
|
@ -51,3 +56,12 @@ func add_modifier(id: int, modifier) -> void:
|
|||
|
||||
func remove_modifier(id: int) -> void:
|
||||
modifiers.erase(id)
|
||||
|
||||
|
||||
func set_invulnerable_for_seconds(time: float) -> void:
|
||||
invulnerable = true
|
||||
|
||||
var timer: = get_tree().create_timer(time)
|
||||
yield(timer, "timeout")
|
||||
|
||||
invulnerable = false
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
[gd_scene load_steps=7 format=2]
|
||||
[gd_scene load_steps=8 format=2]
|
||||
|
||||
[ext_resource path="res://assets/tilesets/valley.tres" type="TileSet" id=1]
|
||||
[ext_resource path="res://assets/tilesets/prototype.tres" type="TileSet" id=2]
|
||||
|
|
@ -6,6 +6,7 @@
|
|||
[ext_resource path="res://src/Objects/Checkpoint.tscn" type="PackedScene" id=4]
|
||||
[ext_resource path="res://src/Objects/Portal.tscn" type="PackedScene" id=5]
|
||||
[ext_resource path="res://src/AI/EnemyPassivePatrol.tscn" type="PackedScene" id=6]
|
||||
[ext_resource path="res://src/AI/HopperEnemy.tscn" type="PackedScene" id=7]
|
||||
|
||||
[node name="Level1" type="Node2D"]
|
||||
|
||||
|
|
@ -72,6 +73,9 @@ next_level_portal_name = ""
|
|||
[node name="EnemyPassivePatrol" parent="Enemies" instance=ExtResource( 6 )]
|
||||
position = Vector2( 1100.16, 370.077 )
|
||||
|
||||
[node name="EnemyPassivePatrol3" parent="Enemies" instance=ExtResource( 6 )]
|
||||
position = Vector2( 1294.42, 370.077 )
|
||||
|
||||
[node name="EnemyPassivePatrol2" parent="Enemies" instance=ExtResource( 6 )]
|
||||
position = Vector2( 1570.94, -229.096 )
|
||||
direction = 0
|
||||
|
|
@ -79,3 +83,10 @@ direction = 0
|
|||
[node name="EnemyPassivePatrol7" parent="Enemies" instance=ExtResource( 6 )]
|
||||
position = Vector2( 1412.34, -750.226 )
|
||||
direction = 0
|
||||
|
||||
[node name="HopperEnemy" parent="Enemies" instance=ExtResource( 7 )]
|
||||
position = Vector2( 1392.78, 942.911 )
|
||||
jump_angle_left = 42
|
||||
jump_angle_right = 60
|
||||
jump_power_left = 1867
|
||||
jump_power_right = 3000
|
||||
|
|
|
|||
|
|
@ -11,7 +11,11 @@ export var wait_duration: = 0.6
|
|||
|
||||
|
||||
func enter(msg: Dictionary = {}) -> void:
|
||||
owner.stats.set_invulnerable_for_seconds(wait_duration*3)
|
||||
|
||||
var timer: = get_tree().create_timer(wait_duration)
|
||||
yield(timer, "timeout")
|
||||
|
||||
owner.emit_signal("hopped_off_entity")
|
||||
|
||||
owner.state_machine.transition_to('Move/Air', {impulse = hop_impulse, velocity = Vector2.ZERO})
|
||||
|
|
@ -10,6 +10,7 @@ func _on_Player_animation_finished(anim_name: String) -> void:
|
|||
|
||||
func enter(msg: Dictionary = {}) -> void:
|
||||
assert "last_checkpoint" in msg
|
||||
owner.stats.set_invulnerable_for_seconds(2)
|
||||
owner.global_position = msg.last_checkpoint.global_position
|
||||
owner.is_active = false
|
||||
owner.camera_rig.is_active = false
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue