Add HFSM + pushdown automaton demo

This commit is contained in:
Nathan
2018-06-10 16:45:02 +09:00
parent 4f40b0940c
commit 5b744fa1e6
42 changed files with 1784 additions and 0 deletions

View File

@@ -0,0 +1,20 @@
[gd_scene load_steps=5 format=2]
[ext_resource path="res://player/Player.tscn" type="PackedScene" id=1]
[ext_resource path="res://debug/StatesStackDiplayer.tscn" type="PackedScene" id=2]
[ext_resource path="res://debug/ControlsPanel.tscn" type="PackedScene" id=3]
[ext_resource path="res://debug/Explanations.tscn" type="PackedScene" id=4]
[node name="Demo" type="Node" index="0"]
[node name="Player" parent="." index="0" instance=ExtResource( 1 )]
[node name="StatesStackDiplayer" parent="." index="1" instance=ExtResource( 2 )]
[node name="ControlsPanel" parent="." index="2" instance=ExtResource( 3 )]
[node name="Explanations" parent="." index="3" instance=ExtResource( 4 )]
[connection signal="state_changed" from="Player" to="StatesStackDiplayer" method="_on_Player_state_changed"]

View File

@@ -0,0 +1,3 @@
extends Node
enum STATUSES { STATUS_NONE, STATUS_INVINCIBLE, STATUS_POISONED, STATUS_STUNNED }

View File

@@ -0,0 +1,77 @@
[gd_scene load_steps=3 format=2]
[ext_resource path="res://debug/top_level_ui.gd" type="Script" id=1]
[ext_resource path="res://fonts/source_code_pro_explanations.tres" type="DynamicFont" id=2]
[node name="ControlsPanel" type="Panel" index="0"]
anchor_left = 1.0
anchor_top = 0.0
anchor_right = 1.0
anchor_bottom = 0.0
margin_left = -220.0
margin_bottom = 170.0
rect_pivot_offset = Vector2( 0, 0 )
rect_clip_content = false
mouse_filter = 0
mouse_default_cursor_shape = 0
size_flags_horizontal = 1
size_flags_vertical = 1
script = ExtResource( 1 )
[node name="Keys" type="Label" parent="." index="0"]
anchor_left = 0.0
anchor_top = 0.0
anchor_right = 1.0
anchor_bottom = 1.0
margin_left = 10.0
margin_top = 10.0
margin_right = -10.0
margin_bottom = -10.0
rect_pivot_offset = Vector2( 0, 0 )
rect_clip_content = false
mouse_filter = 2
mouse_default_cursor_shape = 0
size_flags_horizontal = 1
size_flags_vertical = 4
custom_fonts/font = ExtResource( 2 )
text = "Shoot:
Attack:
Stagger:
Jump:
Sprint:"
percent_visible = 1.0
lines_skipped = 0
max_lines_visible = -1
_sections_unfolded = [ "BBCode", "custom_fonts" ]
[node name="Keys2" type="Label" parent="." index="1"]
anchor_left = 0.0
anchor_top = 0.0
anchor_right = 1.0
anchor_bottom = 1.0
margin_left = 10.0
margin_top = 10.0
margin_right = -10.0
margin_bottom = -10.0
rect_pivot_offset = Vector2( 0, 0 )
rect_clip_content = false
mouse_filter = 2
mouse_default_cursor_shape = 0
size_flags_horizontal = 1
size_flags_vertical = 4
custom_fonts/font = ExtResource( 2 )
text = "R
F
X
Space
Shift"
align = 2
percent_visible = 1.0
lines_skipped = 0
max_lines_visible = -1
_sections_unfolded = [ "BBCode", "custom_fonts" ]

View File

@@ -0,0 +1,59 @@
[gd_scene load_steps=4 format=2]
[ext_resource path="res://fonts/source_code_pro_explanations_bold.tres" type="DynamicFont" id=1]
[ext_resource path="res://fonts/source_code_pro_explanations.tres" type="DynamicFont" id=2]
[ext_resource path="res://debug/top_level_ui.gd" type="Script" id=3]
[node name="Explanations" type="RichTextLabel"]
anchor_left = 0.0
anchor_top = 0.0
anchor_right = 1.0
anchor_bottom = 1.0
margin_left = 10.0
margin_top = -370.0
margin_right = -10.0
margin_bottom = -730.0
rect_pivot_offset = Vector2( 0, 0 )
rect_clip_content = false
mouse_filter = 2
mouse_default_cursor_shape = 0
size_flags_horizontal = 1
size_flags_vertical = 4
custom_fonts/bold_font = ExtResource( 1 )
custom_fonts/normal_font = ExtResource( 2 )
bbcode_enabled = true
bbcode_text = "This example shows how to apply the State programming pattern in GDscript, including Hierarchical States, and a pushdown automaton.
States are common in games. You can use the pattern to:
1. Separate each behavior and transitions between behaviors, thus make scripts shorter and easier to manage
2. Respect the Single Responsibility Principle. Each State object represents [b]one[/b] action
3. Improve your code's structure. Look at the scene tree and FileSystem tab: without looking at the code, you'll know what the Player can or cannot do.
You can read more about States in the excellent [url=http://gameprogrammingpatterns.com/state.html]Game Programming Patterns ebook[/url]."
visible_characters = -1
percent_visible = 1.0
meta_underlined = true
tab_size = 4
text = "This example shows how to apply the State programming pattern in GDscript, including Hierarchical States, and a pushdown automaton.
States are common in games. You can use the pattern to:
1. Separate each behavior and transitions between behaviors, thus make scripts shorter and easier to manage
2. Respect the Single Responsibility Principle. Each State object represents one action
3. Improve your code's structure. Look at the scene tree and FileSystem tab: without looking at the code, you'll know what the Player can or cannot do.
You can read more about States in the excellent Game Programming Patterns ebook."
scroll_active = true
scroll_following = false
selection_enabled = false
override_selected_font_color = false
script = ExtResource( 3 )
_sections_unfolded = [ "BBCode", "custom_fonts" ]
__meta__ = {
"_edit_lock_": true
}

View File

@@ -0,0 +1,107 @@
[gd_scene load_steps=4 format=2]
[ext_resource path="res://debug/states-stack-displayer.gd" type="Script" id=1]
[ext_resource path="res://fonts/SourceCodePro-Bold.ttf" type="DynamicFontData" id=2]
[sub_resource type="DynamicFont" id=1]
size = 20
use_mipmaps = false
use_filter = true
font_data = ExtResource( 2 )
_sections_unfolded = [ "Font", "Settings" ]
[node name="StatesStackDiplayer" type="Panel"]
anchor_left = 0.0
anchor_top = 0.0
anchor_right = 0.0
anchor_bottom = 0.0
margin_right = 210.0
margin_bottom = 170.0
rect_pivot_offset = Vector2( 0, 0 )
rect_clip_content = false
mouse_filter = 0
mouse_default_cursor_shape = 0
size_flags_horizontal = 1
size_flags_vertical = 1
script = ExtResource( 1 )
__meta__ = {
"_edit_group_": true
}
[node name="Title" type="Label" parent="." index="0"]
anchor_left = 0.5
anchor_top = 0.0
anchor_right = 0.5
anchor_bottom = 0.0
margin_left = -105.0
margin_right = 105.0
margin_bottom = 40.0
rect_pivot_offset = Vector2( 0, 0 )
rect_clip_content = false
mouse_filter = 2
mouse_default_cursor_shape = 0
size_flags_horizontal = 1
size_flags_vertical = 4
custom_fonts/font = SubResource( 1 )
text = "Pushown"
align = 1
valign = 1
uppercase = true
percent_visible = 1.0
lines_skipped = 0
max_lines_visible = -1
_sections_unfolded = [ "custom_fonts" ]
[node name="States" type="Label" parent="." index="1"]
anchor_left = 0.0
anchor_top = 0.0
anchor_right = 0.0
anchor_bottom = 0.0
margin_left = 20.0
margin_top = 50.0
margin_right = 190.0
margin_bottom = 170.0
rect_pivot_offset = Vector2( 0, 0 )
rect_clip_content = false
mouse_filter = 2
mouse_default_cursor_shape = 0
size_flags_horizontal = 1
size_flags_vertical = 4
custom_fonts/font = SubResource( 1 )
text = "Jump
Test"
align = 1
percent_visible = 1.0
lines_skipped = 0
max_lines_visible = -1
_sections_unfolded = [ "custom_fonts" ]
[node name="Numbers" type="Label" parent="." index="2"]
anchor_left = 0.0
anchor_top = 0.0
anchor_right = 0.0
anchor_bottom = 0.0
margin_left = 20.0
margin_top = 50.0
margin_right = 190.0
margin_bottom = 170.0
rect_pivot_offset = Vector2( 0, 0 )
rect_clip_content = false
mouse_filter = 2
mouse_default_cursor_shape = 0
size_flags_horizontal = 1
size_flags_vertical = 4
custom_fonts/font = SubResource( 1 )
text = "1.
2."
percent_visible = 1.0
lines_skipped = 0
max_lines_visible = -1
_sections_unfolded = [ "custom_fonts" ]

View File

@@ -0,0 +1,18 @@
tool
extends Panel
func _ready():
set_as_toplevel(true)
func _on_Player_state_changed(states_stack):
var states_names = ''
var numbers = ''
var index = 0
for state in states_stack:
states_names += state.get_name() + '\n'
numbers += str(index) + '\n'
index += 1
$States.text = states_names
$Numbers.text = numbers

View File

@@ -0,0 +1,5 @@
tool
extends Control
func _ready():
set_as_toplevel(true)

View File

@@ -0,0 +1,101 @@
[gd_resource type="Environment" load_steps=2 format=2]
[sub_resource type="ProceduralSky" id=1]
radiance_size = 4
sky_top_color = Color( 0.0470588, 0.454902, 0.976471, 1 )
sky_horizon_color = Color( 0.556863, 0.823529, 0.909804, 1 )
sky_curve = 0.25
sky_energy = 1.0
ground_bottom_color = Color( 0.101961, 0.145098, 0.188235, 1 )
ground_horizon_color = Color( 0.482353, 0.788235, 0.952941, 1 )
ground_curve = 0.01
ground_energy = 1.0
sun_color = Color( 1, 1, 1, 1 )
sun_latitude = 35.0
sun_longitude = 0.0
sun_angle_min = 1.0
sun_angle_max = 100.0
sun_curve = 0.05
sun_energy = 16.0
texture_size = 2
[resource]
background_mode = 2
background_sky = SubResource( 1 )
background_sky_custom_fov = 0.0
background_color = Color( 0, 0, 0, 1 )
background_energy = 1.0
background_canvas_max_layer = 0
ambient_light_color = Color( 0, 0, 0, 1 )
ambient_light_energy = 1.0
ambient_light_sky_contribution = 1.0
fog_enabled = false
fog_color = Color( 0.5, 0.6, 0.7, 1 )
fog_sun_color = Color( 1, 0.9, 0.7, 1 )
fog_sun_amount = 0.0
fog_depth_enabled = true
fog_depth_begin = 10.0
fog_depth_curve = 1.0
fog_transmit_enabled = false
fog_transmit_curve = 1.0
fog_height_enabled = false
fog_height_min = 0.0
fog_height_max = 100.0
fog_height_curve = 1.0
tonemap_mode = 0
tonemap_exposure = 1.0
tonemap_white = 1.0
auto_exposure_enabled = false
auto_exposure_scale = 0.4
auto_exposure_min_luma = 0.05
auto_exposure_max_luma = 8.0
auto_exposure_speed = 0.5
ss_reflections_enabled = false
ss_reflections_max_steps = 64
ss_reflections_fade_in = 0.15
ss_reflections_fade_out = 2.0
ss_reflections_depth_tolerance = 0.2
ss_reflections_roughness = true
ssao_enabled = false
ssao_radius = 1.0
ssao_intensity = 1.0
ssao_radius2 = 0.0
ssao_intensity2 = 1.0
ssao_bias = 0.01
ssao_light_affect = 0.0
ssao_color = Color( 0, 0, 0, 1 )
ssao_quality = 0
ssao_blur = 3
ssao_edge_sharpness = 4.0
dof_blur_far_enabled = false
dof_blur_far_distance = 10.0
dof_blur_far_transition = 5.0
dof_blur_far_amount = 0.1
dof_blur_far_quality = 1
dof_blur_near_enabled = false
dof_blur_near_distance = 2.0
dof_blur_near_transition = 1.0
dof_blur_near_amount = 0.1
dof_blur_near_quality = 1
glow_enabled = false
glow_levels/1 = false
glow_levels/2 = false
glow_levels/3 = true
glow_levels/4 = false
glow_levels/5 = true
glow_levels/6 = false
glow_levels/7 = false
glow_intensity = 0.8
glow_strength = 1.0
glow_bloom = 0.0
glow_blend_mode = 2
glow_hdr_threshold = 1.0
glow_hdr_scale = 2.0
glow_bicubic_upscale = false
adjustment_enabled = false
adjustment_brightness = 1.0
adjustment_contrast = 1.0
adjustment_saturation = 1.0

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,12 @@
[gd_resource type="DynamicFont" load_steps=2 format=2]
[ext_resource path="res://fonts/SourceCodePro-Bold.ttf" type="DynamicFontData" id=1]
[resource]
size = 20
use_mipmaps = false
use_filter = true
font_data = ExtResource( 1 )
_sections_unfolded = [ "Font", "Settings" ]

View File

@@ -0,0 +1,12 @@
[gd_resource type="DynamicFont" load_steps=2 format=2]
[ext_resource path="res://fonts/SourceCodePro-Black.ttf" type="DynamicFontData" id=1]
[resource]
size = 24
use_mipmaps = false
use_filter = true
font_data = ExtResource( 1 )
_sections_unfolded = [ "Font", "Settings" ]

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@@ -0,0 +1,32 @@
[remap]
importer="texture"
type="StreamTexture"
path="res://.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex"
[deps]
source_file="res://icon.png"
source_md5="66cdb591455c91e0e285218a159ee4a1"
dest_files=[ "res://.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex" ]
dest_md5="302305b1ae85287055e65e5202170a74"
[params]
compress/mode=0
compress/lossy_quality=0.7
compress/hdr_mode=0
compress/normal_map=0
flags/repeat=0
flags/filter=true
flags/mipmaps=false
flags/anisotropic=false
flags/srgb=2
process/fix_alpha_border=true
process/premult_alpha=false
process/HDR_as_SRGB=false
stream=false
size_limit=0
detect_3d=true
svg/scale=1.0

View File

@@ -0,0 +1,198 @@
[gd_scene load_steps=20 format=2]
[ext_resource path="res://player/state-machine.gd" type="Script" id=1]
[ext_resource path="res://player/shadow.png" type="Texture" id=2]
[ext_resource path="res://player/body.png" type="Texture" id=3]
[ext_resource path="res://player/weapon/weapon_pivot.gd" type="Script" id=4]
[ext_resource path="res://player/weapon/Sword.tscn" type="PackedScene" id=5]
[ext_resource path="res://player/health/Health.tscn" type="PackedScene" id=6]
[ext_resource path="res://player/states/motion/on_ground/idle.gd" type="Script" id=7]
[ext_resource path="res://player/states/motion/on_ground/move.gd" type="Script" id=8]
[ext_resource path="res://player/states/motion/in_air/jump.gd" type="Script" id=9]
[ext_resource path="res://player/states/combat/stagger.gd" type="Script" id=10]
[ext_resource path="res://player/states/combat/attack.gd" type="Script" id=11]
[ext_resource path="res://player/states/die.gd" type="Script" id=12]
[ext_resource path="res://player/bullet/bullet_spawner.gd" type="Script" id=13]
[ext_resource path="res://fonts/SourceCodePro-Bold.ttf" type="DynamicFontData" id=14]
[ext_resource path="res://player/states/debug/state-name-displayer.gd" type="Script" id=15]
[sub_resource type="Animation" id=1]
length = 1.0
loop = false
step = 0.1
[sub_resource type="Animation" id=2]
length = 0.6
loop = false
step = 0.1
tracks/0/type = "value"
tracks/0/path = NodePath("BodyPivot/Body:modulate")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/keys = {
"times": PoolRealArray( 0, 0.05, 0.1, 0.15, 0.2, 0.25, 0.4 ),
"transitions": PoolRealArray( 1, 1, 1, 1, 1, 1, 1 ),
"update": 0,
"values": [ Color( 1, 1, 1, 1 ), Color( 1, 0, 0, 1 ), Color( 1, 1, 1, 1 ), Color( 1, 0, 0, 1 ), Color( 1, 1, 1, 1 ), Color( 1, 0, 0, 1 ), Color( 1, 1, 1, 1 ) ]
}
[sub_resource type="Animation" id=3]
length = 1.0
loop = false
step = 0.1
[sub_resource type="DynamicFont" id=4]
size = 20
use_mipmaps = false
use_filter = true
font_data = ExtResource( 14 )
_sections_unfolded = [ "Font", "Settings" ]
[node name="Player" type="KinematicBody2D"]
position = Vector2( 628.826, 391.266 )
input_pickable = false
collision_layer = 1
collision_mask = 1
collision/safe_margin = 0.08
script = ExtResource( 1 )
_sections_unfolded = [ "Visibility" ]
__meta__ = {
"_edit_horizontal_guides_": [ ]
}
[node name="AnimationPlayer" type="AnimationPlayer" parent="." index="0"]
root_node = NodePath("..")
autoplay = ""
playback_process_mode = 1
playback_default_blend_time = 0.0
playback_speed = 1.0
anims/idle = SubResource( 1 )
anims/stagger = SubResource( 2 )
anims/walk = SubResource( 3 )
blend_times = [ ]
[node name="Shadow" type="Sprite" parent="." index="1"]
self_modulate = Color( 1, 1, 1, 0.361098 )
position = Vector2( 0, -4 )
texture = ExtResource( 2 )
_sections_unfolded = [ "Visibility" ]
[node name="BodyPivot" type="Position2D" parent="." index="2"]
[node name="Body" type="Sprite" parent="BodyPivot" index="0"]
position = Vector2( 0, -58.8242 )
texture = ExtResource( 3 )
[node name="WeaponPivot" type="Position2D" parent="." index="3"]
position = Vector2( 1.17401, -61.266 )
script = ExtResource( 4 )
[node name="Offset" type="Position2D" parent="WeaponPivot" index="0"]
position = Vector2( 110, 0 )
[node name="Sword" parent="WeaponPivot/Offset" index="0" instance=ExtResource( 5 )]
[node name="CollisionPolygon2D" type="CollisionPolygon2D" parent="." index="4"]
build_mode = 0
polygon = PoolVector2Array( -20, 0, -20, -20, 20, -20, 20, 0 )
[node name="Health" parent="." index="5" instance=ExtResource( 6 )]
[node name="States" type="Node" parent="." index="6"]
[node name="Idle" type="Node" parent="States" index="0"]
script = ExtResource( 7 )
[node name="Move" type="Node" parent="States" index="1"]
script = ExtResource( 8 )
MAX_WALK_SPEED = 450
MAX_RUN_SPEED = 700
[node name="Jump" type="Node" parent="States" index="2"]
script = ExtResource( 9 )
BASE_MAX_HORIZONTAL_SPEED = 400.0
AIR_ACCELERATION = 1000.0
AIR_DECCELERATION = 2000.0
AIR_STEERING_POWER = 50.0
JUMP_HEIGHT = 120.0
JUMP_DURATION = 0.8
GRAVITY = 1600.0
[node name="Stagger" type="Node" parent="States" index="3"]
script = ExtResource( 10 )
[node name="Attack" type="Node" parent="States" index="4"]
script = ExtResource( 11 )
[node name="Die" type="Node" parent="States" index="5"]
script = ExtResource( 12 )
[node name="BulletSpawn" type="Node2D" parent="." index="7"]
editor/display_folded = true
position = Vector2( 1.17401, -61.266 )
script = ExtResource( 13 )
_sections_unfolded = [ "Transform" ]
[node name="CooldownTimer" type="Timer" parent="BulletSpawn" index="0"]
process_mode = 1
wait_time = 0.2
one_shot = true
autostart = false
[node name="StateNameDisplayer" type="Label" parent="." index="8"]
editor/display_folded = true
anchor_left = 0.0
anchor_top = 0.0
anchor_right = 0.0
anchor_bottom = 0.0
margin_left = -109.0
margin_top = -172.0
margin_right = 110.0
margin_bottom = -138.0
rect_pivot_offset = Vector2( 0, 0 )
rect_clip_content = false
mouse_filter = 2
mouse_default_cursor_shape = 0
size_flags_horizontal = 1
size_flags_vertical = 4
custom_fonts/font = SubResource( 4 )
text = "Test"
align = 1
valign = 1
uppercase = true
percent_visible = 1.0
lines_skipped = 0
max_lines_visible = -1
script = ExtResource( 15 )
_sections_unfolded = [ "Rect", "custom_fonts" ]
[connection signal="state_changed" from="." to="StateNameDisplayer" method="_on_Player_state_changed"]
[connection signal="animation_finished" from="AnimationPlayer" to="." method="_on_animation_finished"]
[connection signal="attack_finished" from="WeaponPivot/Offset/Sword" to="States/Attack" method="_on_Sword_attack_finished"]

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -0,0 +1,32 @@
[remap]
importer="texture"
type="StreamTexture"
path="res://.import/body.png-313f6363670a5852a7b7126ab476d8b1.stex"
[deps]
source_file="res://player/body.png"
source_md5="e76fc4b6b913ad7e09352eada55fc124"
dest_files=[ "res://.import/body.png-313f6363670a5852a7b7126ab476d8b1.stex" ]
dest_md5="b65921b7af7d7cea3cb9567625d04d34"
[params]
compress/mode=0
compress/lossy_quality=0.7
compress/hdr_mode=0
compress/normal_map=0
flags/repeat=0
flags/filter=true
flags/mipmaps=false
flags/anisotropic=false
flags/srgb=2
process/fix_alpha_border=true
process/premult_alpha=false
process/HDR_as_SRGB=false
stream=false
size_limit=0
detect_3d=true
svg/scale=1.0

View File

@@ -0,0 +1,25 @@
[gd_scene load_steps=3 format=2]
[ext_resource path="res://player/bullet/bullet.gd" type="Script" id=1]
[sub_resource type="CircleShape2D" id=1]
custom_solver_bias = 0.0
radius = 12.0
[node name="Bullet" type="KinematicBody2D"]
input_pickable = false
collision_layer = 2
collision_mask = 2
collision/safe_margin = 0.08
script = ExtResource( 1 )
_sections_unfolded = [ "Collision", "collision" ]
SPEED = 1000.0
[node name="CollisionShape2D" type="CollisionShape2D" parent="." index="0"]
shape = SubResource( 1 )

View File

@@ -0,0 +1,28 @@
extends KinematicBody2D
var direction = Vector2()
export(float) var SPEED = 1000.0
func _ready():
set_as_toplevel(true)
func _physics_process(delta):
if is_outside_view_bounds():
queue_free()
var motion = direction * SPEED * delta
var collision_info = move_and_collide(motion)
if collision_info:
queue_free()
func is_outside_view_bounds():
return position.x > OS.get_screen_size().x or position.x < 0.0 \
or position.y > OS.get_screen_size().y or position.y < 0.0
func _draw():
draw_circle(Vector2(), $CollisionShape2D.shape.radius, Color('#ffffff'))

View File

@@ -0,0 +1,16 @@
extends Node2D
var bullet = preload("Bullet.tscn")
func fire(direction):
if not $CooldownTimer.is_stopped():
return
$CooldownTimer.start()
var new_bullet = bullet.instance()
new_bullet.direction = direction
add_child(new_bullet)
func update(host, delta):
return 'previous'

View File

@@ -0,0 +1,17 @@
[gd_scene load_steps=2 format=2]
[ext_resource path="res://player/health/health.gd" type="Script" id=1]
[node name="Health" type="Node"]
script = ExtResource( 1 )
max_health = 9
[node name="PoisonTimer" type="Timer" parent="." index="0"]
process_mode = 1
wait_time = 0.6
one_shot = false
autostart = false

View File

@@ -0,0 +1,62 @@
extends Node
signal health_changed
signal health_depleted
signal status_changed
var health = 0
export(int) var max_health = 9
var status = null
const POISON_DAMAGE = 1
var poison_cycles = 0
func _ready():
health = max_health
$PoisonTimer.connect('timeout', self, '_on_PoisonTimer_timeout')
func _change_status(new_status):
match status:
GlobalConstants.STATUS_POISONED:
$PoisonTimer.stop()
match new_status:
GlobalConstants.STATUS_POISONED:
poison_cycles = 0
$PoisonTimer.start()
status = new_status
emit_signal('status_changed', new_status)
func take_damage(amount, effect=null):
if status == GlobalConstants.STATUS_INVINCIBLE:
return
health -= amount
health = max(0, health)
emit_signal("health_changed", health)
if not effect:
return
match effect[0]:
GlobalConstants.STATUS_POISONED:
_change_status(GlobalConstants.STATUS_POISONED)
poison_cycles = effect[1]
# print("%s got hit and took %s damage. Health: %s/%s" % [get_name(), amount, health, max_health])
func heal(amount):
health += amount
health = max(health, max_health)
emit_signal("health_changed", health)
# print("%s got healed by %s points. Health: %s/%s" % [get_name(), amount, health, max_health])
func _on_PoisonTimer_timeout():
take_damage(POISON_DAMAGE)
poison_cycles -= 1
if poison_cycles == 0:
_change_status(GlobalConstants.STATUS_NONE)
return
$PoisonTimer.start()

Binary file not shown.

After

Width:  |  Height:  |  Size: 967 B

View File

@@ -0,0 +1,32 @@
[remap]
importer="texture"
type="StreamTexture"
path="res://.import/shadow.png-493c4635eca1ce8bdece629560617dc7.stex"
[deps]
source_file="res://player/shadow.png"
source_md5="ba1c3f4ef3a93dcd980bc4d020c4a22c"
dest_files=[ "res://.import/shadow.png-493c4635eca1ce8bdece629560617dc7.stex" ]
dest_md5="7dd3fb33b53c00ce76edd77833bce15d"
[params]
compress/mode=0
compress/lossy_quality=0.7
compress/hdr_mode=0
compress/normal_map=0
flags/repeat=0
flags/filter=true
flags/mipmaps=false
flags/anisotropic=false
flags/srgb=2
process/fix_alpha_border=true
process/premult_alpha=false
process/HDR_as_SRGB=false
stream=false
size_limit=0
detect_3d=true
svg/scale=1.0

View File

@@ -0,0 +1,60 @@
extends KinematicBody2D
signal state_changed
var look_direction = Vector2()
var current_state = null
var states_stack = []
onready var states_map = {
'idle': $States/Idle,
'move': $States/Move,
'jump': $States/Jump,
'shoot': $States/Shoot,
}
func _ready():
states_stack.push_front($States/Idle)
current_state = states_stack[0]
_change_state('idle')
# Delegate the call to the state
func _physics_process(delta):
var new_state = current_state.update(self, delta)
if new_state:
_change_state(new_state)
func _input(event):
var new_state = current_state.handle_input(self, event)
if new_state:
_change_state(new_state)
# Exit the current state, change it and enter the new one
func _change_state(state_name):
# The pushdown mechanism isn't very useful in this example.
# If the player stops moving mid-air Jump will still return to move
# print('Exiting %s and enterig %s' % [current_state.get_name(), new_state.get_name()])
current_state.exit(self)
# removing state previously pushed on the stack
if state_name == 'previous':
states_stack.pop_front()
elif state_name in ['jump', 'shoot']:
states_stack.push_front(states_map[state_name])
if state_name == 'jump':
$States/Jump.initialize(current_state.speed, current_state.velocity)
else:
# pushing new state on to the stack
var new_state = states_map[state_name]
states_stack[0] = new_state
# We only reinitialize the state when we don't use the pushdown automaton
if state_name != 'previous':
current_state.enter(self)
current_state = states_stack[0]
emit_signal('state_changed', current_state.get_name())

View File

@@ -0,0 +1,133 @@
"""
The point of the State pattern is to separate concerns, to help follow
the single responsibility principle. Each state describes an action or
behavior.
The state machine is the only entity that"s aware of the states.
That"s why this script receives strings from the states: it maps
these strings to actual state objects: the states (Move, Jump, etc.)
are not aware of their siblings. This way you can change any of
the states anytime without breaking the game.
"""
extends KinematicBody2D
signal state_changed
signal direction_changed(new_direction)
var look_direction = Vector2(1, 0) setget set_look_direction
"""
This example keeps a history of some states so e.g. after taking a hit,
the character can return to the previous state. The states_stack is
an Array, and we use Array.push_front() and Array.pop_front() to add and
remove states from the history.
"""
var states_stack = []
var current_state = null
onready var states_map = {
"idle": $States/Idle,
"move": $States/Move,
"jump": $States/Jump,
"stagger": $States/Stagger,
"attack": $States/Attack,
}
func _ready():
for state_node in $States.get_children():
state_node.connect("finished", self, "_change_state")
states_stack.push_front($States/Idle)
current_state = states_stack[0]
_change_state("idle")
# The state machine delegates process and input callbacks to the current state
# The state object, e.g. Move, then handles input, calculates velocity
# and moves what I called its "host", the Player node (KinematicBody2D) in this case.
func _physics_process(delta):
current_state.update(self, delta)
func _input(event):
"""
If you make a shooter game, you may want the player to be able to
fire bullets anytime.
If that"s the case you don"t want to use the states. They"ll add micro
freezes in the gameplay and/or make your code more complex
Firing is the weapon"s responsibility (BulletSpawn here) so the weapon should handle it
"""
if event.is_action_pressed("fire"):
$BulletSpawn.fire(look_direction)
return
elif event.is_action_pressed("attack"):
if current_state == $States/Attack:
return
_change_state("attack")
return
current_state.handle_input(self, event)
func _on_animation_finished(anim_name):
"""
We want to delegate every method or callback that could trigger
a state change to the state objects. The base script state.gd,
that all states extend, makes sure that all states have the same
interface, that is to say access to the same base methods, including
_on_animation_finished. See state.gd
"""
current_state._on_animation_finished(anim_name)
func _change_state(state_name):
"""
We use this method to:
1. Clean up the current state with its the exit method
2. Manage the flow and the history of states
3. Initialize the new state with its enter method
Note that to go back to the previous state in the states history,
the state objects return the "previous" keyword and not a specific
state name.
"""
current_state.exit(self)
if state_name == "previous":
states_stack.pop_front()
elif state_name in ["stagger", "jump", "attack"]:
states_stack.push_front(states_map[state_name])
elif state_name == "dead":
queue_free()
return
else:
var new_state = states_map[state_name]
states_stack[0] = new_state
if state_name == "attack":
$WeaponPivot/Offset/Sword.attack()
if state_name == "jump":
$States/Jump.initialize(current_state.speed, current_state.velocity)
current_state = states_stack[0]
if state_name != "previous":
# We don"t want to reinitialize the state if we"re going back to the previous state
current_state.enter(self)
emit_signal("state_changed", states_stack)
func take_damage(attacker, amount, effect=null):
if self.is_a_parent_of(attacker):
return
$States/Stagger.knockback_direction = (attacker.global_position - global_position).normalized()
$Health.take_damage(amount, effect)
func set_dead(value):
set_process_input(not value)
set_physics_process(not value)
$CollisionPolygon2D.disabled = value
func set_look_direction(value):
look_direction = value
emit_signal("direction_changed", value)

View File

@@ -0,0 +1,14 @@
"""
The stagger state end with the stagger animation from the AnimationPlayer
The animation only affects the Body Sprite"s modulate property so
it could stack with other animations if we had two AnimationPlayer nodes
"""
extends "../state.gd"
func enter(host):
host.get_node("AnimationPlayer").play("idle")
func _on_Sword_attack_finished():
emit_signal("finished", "previous")

View File

@@ -0,0 +1,16 @@
"""
The stagger state end with the stagger animation from the AnimationPlayer
The animation only affects the Body Sprite"s modulate property so
it could stack with other animations if we had two AnimationPlayer nodes
"""
extends "../state.gd"
var knockback_direction = Vector2()
func enter(host):
host.get_node("AnimationPlayer").play("stagger")
func _on_animation_finished(anim_name):
assert anim_name == "stagger"
emit_signal("finished", "previous")

View File

@@ -0,0 +1,12 @@
extends Label
var start_position = Vector2()
func _ready():
start_position = rect_position
func _physics_process(delta):
rect_position = $"../BodyPivot".position + start_position
func _on_Player_state_changed(states_stack):
text = states_stack[0].get_name()

View File

@@ -0,0 +1,10 @@
extends "state.gd"
# Initialize the state. E.g. change the animation
func enter(host):
host.set_dead(true)
host.get_node("AnimationPlayer").play("die")
func _on_animation_finished(anim_name):
emit_signal("finished", "dead")

View File

@@ -0,0 +1,70 @@
extends "../motion.gd"
export(float) var BASE_MAX_HORIZONTAL_SPEED = 400.0
export(float) var AIR_ACCELERATION = 1000.0
export(float) var AIR_DECCELERATION = 2000.0
export(float) var AIR_STEERING_POWER = 50.0
export(float) var JUMP_HEIGHT = 120.0
export(float) var JUMP_DURATION = 0.8
export(float) var GRAVITY = 1600.0
var enter_velocity = Vector2()
var max_horizontal_speed = 0.0
var horizontal_speed = 0.0
var horizontal_velocity = Vector2()
var vertical_speed = 0.0
var height = 0.0
func initialize(speed, velocity):
horizontal_speed = speed
max_horizontal_speed = speed if speed > 0.0 else BASE_MAX_HORIZONTAL_SPEED
enter_velocity = velocity
func enter(host):
var input_direction = get_input_direction()
update_look_direction(host, input_direction)
horizontal_velocity = enter_velocity if input_direction else Vector2()
vertical_speed = 600.0
host.get_node("AnimationPlayer").play("idle")
func update(host, delta):
var input_direction = get_input_direction()
update_look_direction(host, input_direction)
move_horizontally(host, delta, input_direction)
animate_jump_height(host, delta)
if height <= 0.0:
emit_signal("finished", "previous")
func move_horizontally(host, delta, direction):
if direction:
horizontal_speed += AIR_ACCELERATION * delta
else:
horizontal_speed -= AIR_DECCELERATION * delta
horizontal_speed = clamp(horizontal_speed, 0, max_horizontal_speed)
var target_velocity = horizontal_speed * direction.normalized()
var steering_velocity = (target_velocity - horizontal_velocity).normalized() * AIR_STEERING_POWER
horizontal_velocity += steering_velocity
host.move_and_slide(horizontal_velocity)
func animate_jump_height(host, delta):
vertical_speed -= GRAVITY * delta
height += vertical_speed * delta
height = max(0.0, height)
host.get_node("BodyPivot").position.y = -height

View File

@@ -0,0 +1,22 @@
# Collection of important methods to handle direction and animation
extends "../state.gd"
func handle_input(host, event):
if event.is_action_pressed("simulate_damage"):
emit_signal("finished", "stagger")
func get_input_direction():
var input_direction = Vector2()
input_direction.x = int(Input.is_action_pressed("move_right")) - int(Input.is_action_pressed("move_left"))
input_direction.y = int(Input.is_action_pressed("move_down")) - int(Input.is_action_pressed("move_up"))
return input_direction
func update_look_direction(host, direction):
if direction and host.look_direction != direction:
host.look_direction = direction
if not direction.x in [-1, 1]:
return
host.get_node("BodyPivot").set_scale(Vector2(direction.x, 1))

View File

@@ -0,0 +1,14 @@
extends "on_ground.gd"
func enter(host):
host.get_node("AnimationPlayer").play("idle")
func handle_input(host, event):
return .handle_input(host, event)
func update(host, delta):
var input_direction = get_input_direction()
if input_direction:
emit_signal("finished", "move")

View File

@@ -0,0 +1,38 @@
extends "on_ground.gd"
export(float) var MAX_WALK_SPEED = 450
export(float) var MAX_RUN_SPEED = 700
func enter(host):
speed = 0.0
velocity = Vector2()
var input_direction = get_input_direction()
update_look_direction(host, input_direction)
host.get_node("AnimationPlayer").play("walk")
func handle_input(host, event):
return .handle_input(host, event)
func update(host, delta):
var input_direction = get_input_direction()
if not input_direction:
emit_signal("finished", "idle")
update_look_direction(host, input_direction)
speed = MAX_RUN_SPEED if Input.is_action_pressed("run") else MAX_WALK_SPEED
var collision_info = move(host, speed, input_direction)
if not collision_info:
return
if speed == MAX_RUN_SPEED and collision_info.collider.is_in_group("environment"):
return null
func move(host, speed, direction):
velocity = direction.normalized() * speed
host.move_and_slide(velocity, Vector2(), 5, 2)
if host.get_slide_count() == 0:
return
return host.get_slide_collision(0)

View File

@@ -0,0 +1,9 @@
extends "../motion.gd"
var speed = 0.0
var velocity = Vector2()
func handle_input(host, event):
if event.is_action_pressed("jump"):
emit_signal("finished", "jump")
return .handle_input(host, event)

View File

@@ -0,0 +1,29 @@
"""
Base interface for all states: it doesn't do anything in itself
but forces us to pass the right arguments to the methods below
and makes sure every State object had all of these methods.
"""
extends Node
signal finished(next_state_name)
# Initialize the state. E.g. change the animation
func enter(host):
return
# Clean up the state. Reinitialize values like a timer
func exit(host):
return
func handle_input(host, event):
return
func update(host, delta):
return
func _on_animation_finished(anim_name):
return

View File

@@ -0,0 +1,278 @@
[gd_scene load_steps=8 format=2]
[ext_resource path="res://player/weapon/sword.gd" type="Script" id=1]
[ext_resource path="res://player/weapon/sword.png" type="Texture" id=2]
[sub_resource type="Animation" id=1]
resource_name = "SETUP"
length = 1.0
loop = false
step = 0.1
tracks/0/type = "value"
tracks/0/path = NodePath(".:rotation_degrees")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/keys = {
"times": PoolRealArray( 0 ),
"transitions": PoolRealArray( 1 ),
"update": 0,
"values": [ 0.0 ]
}
tracks/1/type = "value"
tracks/1/path = NodePath(".:scale")
tracks/1/interp = 1
tracks/1/loop_wrap = true
tracks/1/imported = false
tracks/1/enabled = true
tracks/1/keys = {
"times": PoolRealArray( 0 ),
"transitions": PoolRealArray( 1 ),
"update": 0,
"values": [ Vector2( 1, 1 ) ]
}
tracks/2/type = "value"
tracks/2/path = NodePath(".:visible")
tracks/2/interp = 1
tracks/2/loop_wrap = true
tracks/2/imported = false
tracks/2/enabled = true
tracks/2/keys = {
"times": PoolRealArray( 0 ),
"transitions": PoolRealArray( 1 ),
"update": 1,
"values": [ true ]
}
tracks/3/type = "value"
tracks/3/path = NodePath(".:monitoring")
tracks/3/interp = 1
tracks/3/loop_wrap = true
tracks/3/imported = false
tracks/3/enabled = true
tracks/3/keys = {
"times": PoolRealArray( 0 ),
"transitions": PoolRealArray( 1 ),
"update": 1,
"values": [ true ]
}
tracks/4/type = "value"
tracks/4/path = NodePath(".:monitorable")
tracks/4/interp = 1
tracks/4/loop_wrap = true
tracks/4/imported = false
tracks/4/enabled = true
tracks/4/keys = {
"times": PoolRealArray( 0 ),
"transitions": PoolRealArray( 1 ),
"update": 1,
"values": [ false ]
}
[sub_resource type="Animation" id=2]
resource_name = "attack_circular"
length = 0.3
loop = false
step = 0.05
tracks/0/type = "value"
tracks/0/path = NodePath(".:rotation_degrees")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/keys = {
"times": PoolRealArray( 0, 0.15, 0.2 ),
"transitions": PoolRealArray( 0.439427, 1, 1 ),
"update": 0,
"values": [ -100.0, 100.0, 90.0 ]
}
tracks/1/type = "value"
tracks/1/path = NodePath(".:scale")
tracks/1/interp = 1
tracks/1/loop_wrap = true
tracks/1/imported = false
tracks/1/enabled = true
tracks/1/keys = {
"times": PoolRealArray( 0, 0.05, 0.15, 0.2 ),
"transitions": PoolRealArray( 1, 2.50795, 1, 1 ),
"update": 0,
"values": [ Vector2( 1, 1 ), Vector2( 1, 1.3 ), Vector2( 1, 1 ), Vector2( 1, 1 ) ]
}
tracks/2/type = "value"
tracks/2/path = NodePath(".:visible")
tracks/2/interp = 1
tracks/2/loop_wrap = true
tracks/2/imported = false
tracks/2/enabled = true
tracks/2/keys = {
"times": PoolRealArray( 0 ),
"transitions": PoolRealArray( 1 ),
"update": 1,
"values": [ true ]
}
[sub_resource type="Animation" id=3]
length = 0.45
loop = false
step = 0.05
tracks/0/type = "value"
tracks/0/path = NodePath(".:rotation_degrees")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/keys = {
"times": PoolRealArray( 0, 0.15, 0.2 ),
"transitions": PoolRealArray( 0.439427, 1, 1 ),
"update": 0,
"values": [ -80.0, 85.0, 75.0 ]
}
tracks/1/type = "value"
tracks/1/path = NodePath(".:scale")
tracks/1/interp = 1
tracks/1/loop_wrap = true
tracks/1/imported = false
tracks/1/enabled = true
tracks/1/keys = {
"times": PoolRealArray( 0, 0.05, 0.15, 0.2 ),
"transitions": PoolRealArray( 1, 2.50795, 1, 1 ),
"update": 0,
"values": [ Vector2( 1, 1 ), Vector2( 1, 1.3 ), Vector2( 1, 1 ), Vector2( 1, 1 ) ]
}
tracks/2/type = "value"
tracks/2/path = NodePath(".:visible")
tracks/2/interp = 1
tracks/2/loop_wrap = true
tracks/2/imported = false
tracks/2/enabled = true
tracks/2/keys = {
"times": PoolRealArray( 0 ),
"transitions": PoolRealArray( 1 ),
"update": 1,
"values": [ true ]
}
tracks/3/type = "method"
tracks/3/path = NodePath(".")
tracks/3/interp = 1
tracks/3/loop_wrap = true
tracks/3/imported = false
tracks/3/enabled = true
tracks/3/keys = {
"times": PoolRealArray( 0.1, 0.25 ),
"transitions": PoolRealArray( 1, 1 ),
"values": [ {
"args": [ ],
"method": "set_attack_input_listening"
}, {
"args": [ ],
"method": "set_ready_for_next_attack"
} ]
}
[sub_resource type="Animation" id=4]
resource_name = "attack_medium"
length = 0.5
loop = false
step = 0.05
tracks/0/type = "value"
tracks/0/path = NodePath(".:rotation_degrees")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/keys = {
"times": PoolRealArray( 0.05, 0.25, 0.35 ),
"transitions": PoolRealArray( 0.439427, 1, 1 ),
"update": 0,
"values": [ 95.0, -95.0, -90.0 ]
}
tracks/1/type = "value"
tracks/1/path = NodePath(".:scale")
tracks/1/interp = 1
tracks/1/loop_wrap = true
tracks/1/imported = false
tracks/1/enabled = true
tracks/1/keys = {
"times": PoolRealArray( 0, 0.1, 0.2, 0.25 ),
"transitions": PoolRealArray( 1, 2.50795, 1, 1 ),
"update": 0,
"values": [ Vector2( 1, 1 ), Vector2( 1, 1.3 ), Vector2( 1, 1 ), Vector2( 1, 1 ) ]
}
tracks/2/type = "value"
tracks/2/path = NodePath(".:visible")
tracks/2/interp = 1
tracks/2/loop_wrap = true
tracks/2/imported = false
tracks/2/enabled = true
tracks/2/keys = {
"times": PoolRealArray( 0 ),
"transitions": PoolRealArray( 1 ),
"update": 1,
"values": [ true ]
}
[sub_resource type="Animation" id=5]
length = 0.01
loop = false
step = 0.1
tracks/0/type = "value"
tracks/0/path = NodePath(".:visible")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/keys = {
"times": PoolRealArray( 0 ),
"transitions": PoolRealArray( 1 ),
"update": 1,
"values": [ false ]
}
[node name="Sword" type="Area2D"]
input_pickable = false
gravity_vec = Vector2( 0, 1 )
gravity = 98.0
linear_damp = 0.1
angular_damp = 1.0
monitorable = false
collision_layer = 16
collision_mask = 3
audio_bus_override = false
audio_bus_name = "Master"
script = ExtResource( 1 )
_sections_unfolded = [ "Transform" ]
[node name="AnimationPlayer" type="AnimationPlayer" parent="." index="0"]
root_node = NodePath("..")
autoplay = ""
playback_process_mode = 1
playback_default_blend_time = 0.0
playback_speed = 1.0
anims/SETUP = SubResource( 1 )
anims/attack_circular = SubResource( 2 )
anims/attack_fast = SubResource( 3 )
anims/attack_medium = SubResource( 4 )
anims/idle = SubResource( 5 )
blend_times = [ ]
_sections_unfolded = [ "Playback Options" ]
[node name="sword" type="Sprite" parent="." index="1"]
position = Vector2( 4, 0 )
texture = ExtResource( 2 )
offset = Vector2( 67, 0 )
_sections_unfolded = [ "Offset", "Transform" ]
[node name="CollisionPolygon2D" type="CollisionPolygon2D" parent="." index="2"]
build_mode = 0
polygon = PoolVector2Array( 28.0001, -15.9999, 136, -15.9995, 160, 0, 136, 16.0005, 27.9999, 16.0001 )

View File

@@ -0,0 +1,107 @@
extends Area2D
signal attack_finished
enum STATES { IDLE, ATTACK }
var state = null
enum ATTACK_INPUT_STATES { IDLE, LISTENING, REGISTERED }
var attack_input_state = IDLE
var ready_for_next_attack = false
const MAX_COMBO_COUNT = 3
var combo_count = 0
var attack_current = {}
var combo = [{
'damage': 1,
'animation': 'attack_fast',
'effect': null
},
{
'damage': 1,
'animation': 'attack_fast',
'effect': null
},
{
'damage': 3,
'animation': 'attack_medium',
'effect': null
}]
var hit_objects = []
func _ready():
$AnimationPlayer.connect('animation_finished', self, "_on_animation_finished")
self.connect("body_entered", self, "_on_body_entered")
_change_state(IDLE)
func _change_state(new_state):
match state:
ATTACK:
hit_objects = []
attack_input_state = IDLE
ready_for_next_attack = false
match new_state:
IDLE:
combo_count = 0
$AnimationPlayer.stop()
visible = false
monitoring = false
ATTACK:
attack_current = combo[combo_count -1]
$AnimationPlayer.play(attack_current['animation'])
visible = true
monitoring = true
state = new_state
func _input(event):
if not state == ATTACK:
return
if attack_input_state != LISTENING:
return
if event.is_action_pressed('attack'):
attack_input_state = REGISTERED
func _physics_process(delta):
if attack_input_state == REGISTERED and ready_for_next_attack:
attack()
func attack():
combo_count += 1
_change_state(ATTACK)
# use with AnimationPlayer func track
func set_attack_input_listening():
attack_input_state = LISTENING
# use with AnimationPlayer func track
func set_ready_for_next_attack():
ready_for_next_attack = true
func _on_body_entered(body):
if not body.has_node('Health'):
return
if body.get_rid().get_id() in hit_objects:
return
hit_objects.append(body.get_rid().get_id())
body.take_damage(self, attack_current['damage'], attack_current['effect'])
func _on_animation_finished(name):
if not attack_current:
return
if attack_input_state == REGISTERED and combo_count < MAX_COMBO_COUNT:
attack()
else:
_change_state(IDLE)
emit_signal("attack_finished")

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@@ -0,0 +1,32 @@
[remap]
importer="texture"
type="StreamTexture"
path="res://.import/sword.png-fc7f0084cdf333c826eda2b33f2ec3cc.stex"
[deps]
source_file="res://player/weapon/sword.png"
source_md5="17fb5e0e6c6fc2c23b36fdbb53f93b1d"
dest_files=[ "res://.import/sword.png-fc7f0084cdf333c826eda2b33f2ec3cc.stex" ]
dest_md5="68d99f8d04dc7b6a17bb7ce168593030"
[params]
compress/mode=0
compress/lossy_quality=0.7
compress/hdr_mode=0
compress/normal_map=0
flags/repeat=0
flags/filter=true
flags/mipmaps=false
flags/anisotropic=false
flags/srgb=2
process/fix_alpha_border=true
process/premult_alpha=false
process/HDR_as_SRGB=false
stream=false
size_limit=0
detect_3d=true
svg/scale=1.0

View File

@@ -0,0 +1,16 @@
extends Position2D
var z_index_start = 0
func _ready():
$"..".connect("direction_changed", self, '_on_Parent_direction_changed')
z_index_start = z_index
func _on_Parent_direction_changed(direction):
rotation = direction.angle()
match direction:
Vector2(0, -1):
z_index = z_index_start - 1
_:
z_index = z_index_start

View File

@@ -0,0 +1,68 @@
; Engine configuration file.
; It's best edited using the editor UI and not directly,
; since the parameters that go here are not all obvious.
;
; Format:
; [section] ; section goes between []
; param=value ; assign values to parameters
config_version=3
[application]
config/name="Hierarchical Finite State Machine example"
run/main_scene="res://Demo.tscn"
config/icon="res://icon.png"
[autoload]
GlobalConstants="*res://autoload/global_constants.gd"
[display]
window/size/width=1280
window/size/height=720
window/size/test_width=1280
window/size/test_height=720
window/stretch/mode="2d"
window/stretch/aspect="keep"
[gdnative]
singletons=[ ]
[input]
move_left=[ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777231,"unicode":0,"echo":false,"script":null)
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":65,"unicode":0,"echo":false,"script":null)
, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":0,"axis":0,"axis_value":-1.0,"script":null)
]
move_up=[ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777232,"unicode":0,"echo":false,"script":null)
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":87,"unicode":0,"echo":false,"script":null)
, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":0,"axis":1,"axis_value":-1.0,"script":null)
]
move_right=[ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777233,"unicode":0,"echo":false,"script":null)
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":68,"unicode":0,"echo":false,"script":null)
, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":0,"axis":0,"axis_value":1.0,"script":null)
]
move_down=[ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777234,"unicode":0,"echo":false,"script":null)
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":83,"unicode":0,"echo":false,"script":null)
, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":0,"axis":1,"axis_value":1.0,"script":null)
]
fire=[ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":82,"unicode":0,"echo":false,"script":null)
, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":2,"pressure":0.0,"pressed":false,"script":null)
]
run=[ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777237,"unicode":0,"echo":false,"script":null)
, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":7,"pressure":0.0,"pressed":false,"script":null)
]
jump=[ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":32,"unicode":0,"echo":false,"script":null)
, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":0,"pressure":0.0,"pressed":false,"script":null)
]
simulate_damage=[ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":88,"unicode":0,"echo":false,"script":null)
]
attack=[ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":70,"unicode":0,"echo":false,"script":null)
]
[rendering]
environment/default_environment="res://default_env.tres"