Add RecoilComponent and SquashAndStretchComponent; implement recoil and animation effects on shooting
This commit is contained in:
@@ -1,8 +1,10 @@
|
|||||||
[gd_scene load_steps=5 format=3 uid="uid://dstko446qydsc"]
|
[gd_scene load_steps=7 format=3 uid="uid://dstko446qydsc"]
|
||||||
|
|
||||||
[ext_resource type="Texture2D" uid="uid://djifxc5x0dyrw" path="res://sprites/ppc_tileset.png" id="1_6gptm"]
|
[ext_resource type="Texture2D" uid="uid://djifxc5x0dyrw" path="res://sprites/ppc_tileset.png" id="1_6gptm"]
|
||||||
[ext_resource type="Script" uid="uid://bnaxy8cw3wrko" path="res://scripts/components/PeriodicShootingComponent.cs" id="2_q37h7"]
|
[ext_resource type="Script" uid="uid://bnaxy8cw3wrko" path="res://scripts/components/PeriodicShootingComponent.cs" id="2_q37h7"]
|
||||||
[ext_resource type="PackedScene" uid="uid://bhc7y4xugu4q7" path="res://objects/entities/bullet.tscn" id="3_bhwy3"]
|
[ext_resource type="PackedScene" uid="uid://bhc7y4xugu4q7" path="res://objects/entities/bullet.tscn" id="3_bhwy3"]
|
||||||
|
[ext_resource type="Script" uid="uid://b3j23e7b7x8ro" path="res://scripts/components/RecoilComponent.cs" id="4_bhwy3"]
|
||||||
|
[ext_resource type="Script" uid="uid://c707c53k7c5ae" path="res://scripts/components/SquashAndStretchComponent.cs" id="5_ww0hb"]
|
||||||
|
|
||||||
[sub_resource type="RectangleShape2D" id="RectangleShape2D_j5sus"]
|
[sub_resource type="RectangleShape2D" id="RectangleShape2D_j5sus"]
|
||||||
size = Vector2(16, 16)
|
size = Vector2(16, 16)
|
||||||
@@ -23,7 +25,7 @@ shape = SubResource("RectangleShape2D_j5sus")
|
|||||||
[node name="PeriodicShootingComponent" type="Node" parent="." node_paths=PackedStringArray("BulletSpawnPointRight")]
|
[node name="PeriodicShootingComponent" type="Node" parent="." node_paths=PackedStringArray("BulletSpawnPointRight")]
|
||||||
script = ExtResource("2_q37h7")
|
script = ExtResource("2_q37h7")
|
||||||
BulletScene = ExtResource("3_bhwy3")
|
BulletScene = ExtResource("3_bhwy3")
|
||||||
ShootInterval = 3.5
|
ShootInterval = 0.2
|
||||||
ShootDirection = Vector2(0, -1)
|
ShootDirection = Vector2(0, -1)
|
||||||
BulletSpawnPointRight = NodePath("../Bullet spawn")
|
BulletSpawnPointRight = NodePath("../Bullet spawn")
|
||||||
ShootingIntervalVariation = 0.3
|
ShootingIntervalVariation = 0.3
|
||||||
@@ -31,3 +33,15 @@ metadata/_custom_type_script = "uid://bnaxy8cw3wrko"
|
|||||||
|
|
||||||
[node name="Bullet spawn" type="Marker2D" parent="."]
|
[node name="Bullet spawn" type="Marker2D" parent="."]
|
||||||
position = Vector2(0, -16)
|
position = Vector2(0, -16)
|
||||||
|
|
||||||
|
[node name="RecoilComponent" type="Node" parent="." node_paths=PackedStringArray("RecoilTarget")]
|
||||||
|
script = ExtResource("4_bhwy3")
|
||||||
|
RecoilTarget = NodePath("../Sprite2D")
|
||||||
|
RecoilDistance = 4.0
|
||||||
|
RecoilDuration = 0.12
|
||||||
|
metadata/_custom_type_script = "uid://b3j23e7b7x8ro"
|
||||||
|
|
||||||
|
[node name="SquashAndStretchComponent" type="Node" parent="." node_paths=PackedStringArray("TargetNode")]
|
||||||
|
script = ExtResource("5_ww0hb")
|
||||||
|
TargetNode = NodePath("../Sprite2D")
|
||||||
|
metadata/_custom_type_script = "uid://c707c53k7c5ae"
|
||||||
|
@@ -1056,7 +1056,7 @@ texture = ExtResource("2_43n76")
|
|||||||
|
|
||||||
[resource]
|
[resource]
|
||||||
physics_layer_0/collision_layer = 1
|
physics_layer_0/collision_layer = 1
|
||||||
physics_layer_0/collision_mask = 29
|
physics_layer_0/collision_mask = 93
|
||||||
terrain_set_0/mode = 0
|
terrain_set_0/mode = 0
|
||||||
terrain_set_0/terrain_0/name = "Village"
|
terrain_set_0/terrain_0/name = "Village"
|
||||||
terrain_set_0/terrain_0/color = Color(1, 1, 1, 1)
|
terrain_set_0/terrain_0/color = Color(1, 1, 1, 1)
|
||||||
|
@@ -12,6 +12,8 @@ public partial class PeriodicShootingComponent : Node
|
|||||||
[Export] public Node2D BulletSpawnPointRight { get; set; }
|
[Export] public Node2D BulletSpawnPointRight { get; set; }
|
||||||
[Export] public Node2D BulletSpawnPointLeft { get; set; }
|
[Export] public Node2D BulletSpawnPointLeft { get; set; }
|
||||||
[Export] public float ShootingIntervalVariation { get; set; } = 0.0f;
|
[Export] public float ShootingIntervalVariation { get; set; } = 0.0f;
|
||||||
|
|
||||||
|
[Signal] public delegate void ShotFiredEventHandler(Vector2 shootDirection);
|
||||||
|
|
||||||
private Timer _timer;
|
private Timer _timer;
|
||||||
private RandomNumberGenerator _rng;
|
private RandomNumberGenerator _rng;
|
||||||
@@ -84,5 +86,6 @@ public partial class PeriodicShootingComponent : Node
|
|||||||
|
|
||||||
bulletInstance.GlobalPosition = spawnPosition;
|
bulletInstance.GlobalPosition = spawnPosition;
|
||||||
GetTree().CurrentScene.AddChild(bulletInstance);
|
GetTree().CurrentScene.AddChild(bulletInstance);
|
||||||
|
EmitSignalShotFired(ShootDirection);
|
||||||
}
|
}
|
||||||
}
|
}
|
51
scripts/components/RecoilComponent.cs
Normal file
51
scripts/components/RecoilComponent.cs
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
using Godot;
|
||||||
|
|
||||||
|
namespace Mr.BrickAdventures.scripts.components;
|
||||||
|
|
||||||
|
[GlobalClass]
|
||||||
|
public partial class RecoilComponent : Node
|
||||||
|
{
|
||||||
|
[Export] public Node2D RecoilTarget { get; set; }
|
||||||
|
[Export] public float RecoilDistance { get; set; } = 8f;
|
||||||
|
[Export] public float RecoilDuration { get; set; } = 0.1f;
|
||||||
|
|
||||||
|
private Vector2 _originalPosition;
|
||||||
|
private Tween _recoilTween;
|
||||||
|
private PeriodicShootingComponent _shootingComponent;
|
||||||
|
|
||||||
|
public override void _Ready()
|
||||||
|
{
|
||||||
|
RecoilTarget ??= Owner as Node2D;
|
||||||
|
if (RecoilTarget == null)
|
||||||
|
{
|
||||||
|
GD.PushError("RecoilComponent: RecoilTarget is null");
|
||||||
|
SetProcess(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_originalPosition = RecoilTarget.Position;
|
||||||
|
|
||||||
|
_shootingComponent = Owner.GetNodeOrNull<PeriodicShootingComponent>("PeriodicShootingComponent");
|
||||||
|
if (_shootingComponent != null)
|
||||||
|
{
|
||||||
|
_shootingComponent.ShotFired += TriggerRecoil;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void TriggerRecoil(Vector2 shootDirection)
|
||||||
|
{
|
||||||
|
if (RecoilTarget == null) return;
|
||||||
|
|
||||||
|
_recoilTween?.Kill();
|
||||||
|
|
||||||
|
var recoilDirection = -shootDirection.Normalized();
|
||||||
|
var recoilPosition = _originalPosition + recoilDirection * RecoilDistance;
|
||||||
|
|
||||||
|
_recoilTween = CreateTween();
|
||||||
|
_recoilTween.SetEase(Tween.EaseType.Out);
|
||||||
|
_recoilTween.SetTrans(Tween.TransitionType.Cubic);
|
||||||
|
|
||||||
|
_recoilTween.TweenProperty(RecoilTarget, "position", recoilPosition, RecoilDuration / 2);
|
||||||
|
_recoilTween.TweenProperty(RecoilTarget, "position", _originalPosition, RecoilDuration / 2).SetDelay(RecoilDuration / 2);
|
||||||
|
}
|
||||||
|
}
|
1
scripts/components/RecoilComponent.cs.uid
Normal file
1
scripts/components/RecoilComponent.cs.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://b3j23e7b7x8ro
|
71
scripts/components/SquashAndStretchComponent.cs
Normal file
71
scripts/components/SquashAndStretchComponent.cs
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
using Godot;
|
||||||
|
|
||||||
|
namespace Mr.BrickAdventures.scripts.components;
|
||||||
|
|
||||||
|
[GlobalClass]
|
||||||
|
public partial class SquashAndStretchComponent : Node
|
||||||
|
{
|
||||||
|
[Export] public Node2D TargetNode { get; set; }
|
||||||
|
[Export(PropertyHint.Range, "0.1, 1.0, 0.01")] public float AnimationDuration { get; set; } = 0.25f;
|
||||||
|
|
||||||
|
[ExportGroup("Effect Strength")]
|
||||||
|
[Export(PropertyHint.Range, "1.0, 2.0, 0.05")] public float SquashFactor { get; set; } = 1.2f;
|
||||||
|
[Export(PropertyHint.Range, "0.5, 1.0, 0.05")] public float StretchFactor { get; set; } = 0.8f;
|
||||||
|
|
||||||
|
private Vector2 _originalScale;
|
||||||
|
private Tween _tween;
|
||||||
|
|
||||||
|
public override void _Ready()
|
||||||
|
{
|
||||||
|
TargetNode ??= Owner as Node2D;
|
||||||
|
|
||||||
|
if (TargetNode == null)
|
||||||
|
{
|
||||||
|
GD.PrintErr("SquashAndStretchComponent: No valid TargetNode found. Disabling component.");
|
||||||
|
SetProcess(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_originalScale = TargetNode.Scale;
|
||||||
|
|
||||||
|
var shootingComponent = Owner.GetNodeOrNull<PeriodicShootingComponent>("PeriodicShootingComponent");
|
||||||
|
if (shootingComponent != null)
|
||||||
|
{
|
||||||
|
shootingComponent.ShotFired += OnShotFired;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
GD.PrintErr("SquashAndStretchComponent requires a PeriodicShootingComponent on the same owner to function.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnShotFired(Vector2 shootDirection)
|
||||||
|
{
|
||||||
|
if (TargetNode == null) return;
|
||||||
|
|
||||||
|
_tween?.Kill();
|
||||||
|
|
||||||
|
Vector2 squashScale;
|
||||||
|
Vector2 stretchScale;
|
||||||
|
|
||||||
|
if (Mathf.Abs(shootDirection.X) > Mathf.Abs(shootDirection.Y))
|
||||||
|
{
|
||||||
|
squashScale = new Vector2(StretchFactor, SquashFactor) * _originalScale;
|
||||||
|
stretchScale = new Vector2(SquashFactor, StretchFactor) * _originalScale;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
squashScale = new Vector2(SquashFactor, StretchFactor) * _originalScale;
|
||||||
|
stretchScale = new Vector2(StretchFactor, SquashFactor) * _originalScale;
|
||||||
|
}
|
||||||
|
|
||||||
|
_tween = CreateTween();
|
||||||
|
_tween.SetTrans(Tween.TransitionType.Elastic).SetEase(Tween.EaseType.Out);
|
||||||
|
|
||||||
|
var partDuration = AnimationDuration / 3.0f;
|
||||||
|
|
||||||
|
_tween.TweenProperty(TargetNode, "scale", squashScale, partDuration);
|
||||||
|
_tween.TweenProperty(TargetNode, "scale", stretchScale, partDuration);
|
||||||
|
_tween.TweenProperty(TargetNode, "scale", _originalScale, partDuration);
|
||||||
|
}
|
||||||
|
}
|
1
scripts/components/SquashAndStretchComponent.cs.uid
Normal file
1
scripts/components/SquashAndStretchComponent.cs.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://c707c53k7c5ae
|
Reference in New Issue
Block a user