Refactor enemy view model for data viewer project

This commit is contained in:
2024-12-05 22:45:13 -08:00
parent 270ded8ef7
commit 8068f33302
10 changed files with 1270 additions and 1207 deletions

View File

@@ -53,21 +53,9 @@ public partial class Enemy : RigidBody3D, IEnemy, IProvide<IEnemyLogic>
[Node] public Timer AttackTimer { get; set; } = default!;
[Node] public AnimatedSprite2D AnimatedSprite { get; set; } = default!;
[Node] public RayCast3D Raycast { get; set; } = default!;
[Node] public AnimationPlayer AnimationPlayer { get; set; } = default!;
[Node] public AnimationTree AnimationTree { get; set; } = default!;
[Node] public IHitbox Hitbox { get; set; } = default!;
private const string IDLE_FORWARD = "idle_front_walk";
private const string IDLE_LEFT = "idle_left_walk";
private const string IDLE_BACK = "idle_back_walk";
private const string ATTACK_FORWARD = "attack";
[Node] public EnemyModelView EnemyModelView { get; set; } = default!;
private float _knockbackStrength = 0.0f;
private Vector3 _knockbackDirection = Vector3.Zero;
@@ -78,20 +66,6 @@ public partial class Enemy : RigidBody3D, IEnemy, IProvide<IEnemyLogic>
EnemyLogic.Set(EnemyStatResource);
EnemyLogic.Set(this as IEnemy);
EnemyLogic.Set(GameRepo);
AnimationTree.AnimationFinished += AnimationTree_AnimationFinished;
AnimationTree.Get("parameters/playback").As<AnimationNodeStateMachinePlayback>().Start(IDLE_FORWARD);
}
private void AnimationTree_AnimationFinished(StringName animName)
{
if (animName == "attack")
AnimationTree.Get("parameters/playback").As<AnimationNodeStateMachinePlayback>().Travel(IDLE_FORWARD);
}
private void DeathAnimationPlayer_AnimationFinished(StringName animName)
{
GameEventDepot.OnEnemyDefeated(GlobalPosition, EnemyStatResource);
QueueFree();
}
public void OnReady()
@@ -100,7 +74,7 @@ public partial class Enemy : RigidBody3D, IEnemy, IProvide<IEnemyLogic>
CollisionDetector = CollisionDetectorScene.Instantiate<Area3D>();
CollisionDetector.AreaEntered += OnPlayerHitboxEntered;
AddChild(CollisionDetector);
Hitbox.AreaEntered += Hitbox_AreaEntered;
EnemyModelView.Hitbox.AreaEntered += Hitbox_AreaEntered;
}
private void Hitbox_AreaEntered(Area3D area)
@@ -130,15 +104,13 @@ public partial class Enemy : RigidBody3D, IEnemy, IProvide<IEnemyLogic>
EnemyBinding
.Handle((in EnemyLogic.Output.MovementComputed output) =>
{
RotateEnemy(-GameRepo.PlayerGlobalTransform.Value.Basis.Z);
EnemyModelView.RotateModel(GlobalTransform.Basis, -GameRepo.PlayerGlobalTransform.Value.Basis.Z);
_knockbackStrength = _knockbackStrength * 0.9f;
MoveAndCollide(output.LinearVelocity + (_knockbackDirection * _knockbackStrength));
})
.Handle((in EnemyLogic.Output.HitByPlayer output) =>
{
LoadShader("res://src/vfx/shaders/DamageHit.gdshader");
var tweener = GetTree().CreateTween();
tweener.TweenMethod(Callable.From((float x) => SetShaderValue(x)), 0.0f, 1.0f, 1.0f);
EnemyModelView.PlayHitAnimation();
// TODO: Make this an event to notify game that player hit someone
if (GameRepo.PlayerData.Inventory.EquippedWeapon.Value.WeaponStats.WeaponTags.Contains(WeaponTag.SelfDamage))
GameRepo.PlayerData.SetCurrentHP(GameRepo.PlayerData.CurrentHP.Value - 5);
@@ -150,15 +122,15 @@ public partial class Enemy : RigidBody3D, IEnemy, IProvide<IEnemyLogic>
})
.Handle((in EnemyLogic.Output.Attack _) =>
{
AnimationTree.Get("parameters/playback").As<AnimationNodeStateMachinePlayback>().Travel("attack");
EnemyModelView.PlayAttackAnimation();
})
.Handle((in EnemyLogic.Output.Defeated output) =>
{
LoadShader("res://src/vfx/shaders/PixelMelt.gdshader");
EnemyModelView.PlayDeathAnimation();
var tweener = GetTree().CreateTween();
tweener.TweenMethod(Callable.From((float x) => SetShaderValue(x)), 0.0f, 1.0f, 0.8f);
GameEventDepot.OnEnemyDefeated(GlobalPosition, EnemyStatResource);
tweener.TweenInterval(1.0f);
tweener.TweenCallback(Callable.From(QueueFree));
GameEventDepot.OnEnemyDefeated(GlobalPosition, EnemyStatResource);
});
this.Provide();
@@ -175,14 +147,6 @@ public partial class Enemy : RigidBody3D, IEnemy, IProvide<IEnemyLogic>
PatrolTimer.WaitTime = rng.RandfRange(7.0f, 15.0f);
}
private void LoadShader(string shaderPath)
{
var shader = GD.Load<Shader>(shaderPath);
AnimatedSprite.Material = new ShaderMaterial();
var shaderMaterial = (ShaderMaterial)AnimatedSprite.Material;
shaderMaterial.Shader = shader;
}
public void OnExitTree()
{
EnemyLogic.Stop();
@@ -255,42 +219,14 @@ public partial class Enemy : RigidBody3D, IEnemy, IProvide<IEnemyLogic>
}
}
private void RotateEnemy(Vector3 cameraDirection)
{
var rotateUpperThreshold = 0.85f;
var rotateLowerThreshold = 0.3f;
var enemyForwardDirection = GlobalTransform.Basis.Z;
var enemyLeftDirection = GlobalTransform.Basis.X;
var leftDotProduct = enemyLeftDirection.Dot(cameraDirection);
var forwardDotProduct = enemyForwardDirection.Dot(cameraDirection);
// Check if forward facing. If the dot product is -1, the enemy is facing the camera.
if (forwardDotProduct < -rotateUpperThreshold)
AnimationTree.Get("parameters/playback").As<AnimationNodeStateMachinePlayback>().Travel("idle_front_walk");
// Check if backward facing. If the dot product is 1, the enemy is facing the same direction as the camera.
else if (forwardDotProduct > rotateUpperThreshold)
AnimationTree.Get("parameters/playback").As<AnimationNodeStateMachinePlayback>().Travel("idle_back_walk");
else
{
// If the dot product of the perpendicular direction is positive (up to 1), the enemy is facing to the left (since it's mirrored).
AnimatedSprite.FlipH = leftDotProduct > 0;
// Check if side facing. If the dot product is close to zero in the positive or negative direction, its close to the threshold for turning.
if (Mathf.Abs(forwardDotProduct) < rotateLowerThreshold)
AnimationTree.Get("parameters/playback").As<AnimationNodeStateMachinePlayback>().Travel("idle_left_walk");
}
}
private void OnHPChanged(double newHP)
{
if (newHP <= 0)
EnemyLogic.Input(new EnemyLogic.Input.EnemyDefeated());
}
private void SetShaderValue(float shaderValue)
private void DoNothing()
{
var shaderMaterial = (ShaderMaterial)AnimatedSprite.Material;
shaderMaterial.SetShaderParameter("progress", shaderValue);
}
}