diff --git a/project.godot b/project.godot index 381184be..c2c6c7e0 100644 --- a/project.godot +++ b/project.godot @@ -162,6 +162,7 @@ locale/translations=PackedStringArray("res://addons/maaacks_game_template/base/t 3d_physics/layer_4="4" 3d_physics/layer_5="enemies" 3d_physics/layer_6="InteractiveGeo" +3d_physics/layer_7="Aim Assist" 3d_physics/layer_9="terrain" 3d_physics/layer_17="weapon" diff --git a/scenes/enemies/flying_enemy/flying_enemy.tscn b/scenes/enemies/flying_enemy/flying_enemy.tscn index 4829f40b..39b51541 100644 --- a/scenes/enemies/flying_enemy/flying_enemy.tscn +++ b/scenes/enemies/flying_enemy/flying_enemy.tscn @@ -47,6 +47,9 @@ albedo_color = Color(0.06469653, 0.06469653, 0.06469653, 1) [sub_resource type="BoxShape3D" id="BoxShape3D_4yfjf"] +[sub_resource type="SphereShape3D" id="SphereShape3D_ykkxn"] +radius = 2.0 + [node name="FlyingEnemy" type="CharacterBody3D" unique_id=2090203407] collision_layer = 16 collision_mask = 273 @@ -116,3 +119,10 @@ monitorable = false [node name="CollisionShape3D" type="CollisionShape3D" parent="DamageBox" unique_id=68664931] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -0.5) shape = SubResource("BoxShape3D_4yfjf") + +[node name="AimAssistTarget" type="Area3D" parent="." unique_id=178575959] +collision_layer = 64 +collision_mask = 0 + +[node name="CollisionShape3D" type="CollisionShape3D" parent="AimAssistTarget" unique_id=843739364] +shape = SubResource("SphereShape3D_ykkxn") diff --git a/scenes/enemies/grounded_enemy/grounded_enemy.tscn b/scenes/enemies/grounded_enemy/grounded_enemy.tscn index b89da631..92d0517e 100644 --- a/scenes/enemies/grounded_enemy/grounded_enemy.tscn +++ b/scenes/enemies/grounded_enemy/grounded_enemy.tscn @@ -48,6 +48,9 @@ albedo_color = Color(0.06469653, 0.06469653, 0.06469653, 1) [sub_resource type="BoxShape3D" id="BoxShape3D_4yfjf"] size = Vector3(1, 2, 1.5) +[sub_resource type="SphereShape3D" id="SphereShape3D_k2cew"] +radius = 2.0 + [node name="GroundedEnemy" type="CharacterBody3D" unique_id=1747444936] collision_layer = 16 collision_mask = 273 @@ -120,3 +123,11 @@ monitorable = false [node name="CollisionShape3D" type="CollisionShape3D" parent="DamageBox" unique_id=978720734] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, -0.25) shape = SubResource("BoxShape3D_4yfjf") + +[node name="AimAssistTarget" type="Area3D" parent="." unique_id=328174571] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0) +collision_layer = 64 +collision_mask = 0 + +[node name="CollisionShape3D" type="CollisionShape3D" parent="AimAssistTarget" unique_id=1960476920] +shape = SubResource("SphereShape3D_k2cew") diff --git a/scenes/fixed_dash_target/fixed_dashthrough_target.tscn b/scenes/fixed_dash_target/fixed_dashthrough_target.tscn index 4017d543..7f83e3b1 100644 --- a/scenes/fixed_dash_target/fixed_dashthrough_target.tscn +++ b/scenes/fixed_dash_target/fixed_dashthrough_target.tscn @@ -17,6 +17,9 @@ roughness = 0.1 emission_enabled = true emission = Color(0, 0.68968636, 0.7473501, 1) +[sub_resource type="SphereShape3D" id="SphereShape3D_gxutf"] +radius = 2.5 + [node name="FixedDashthroughTarget" type="AnimatableBody3D" unique_id=1291663508] collision_layer = 32 collision_mask = 0 @@ -28,3 +31,10 @@ shape = SubResource("SphereShape3D_nkm8n") [node name="MeshInstance3D" type="MeshInstance3D" parent="." unique_id=1965985422] mesh = SubResource("SphereMesh_r0j7a") surface_material_override/0 = SubResource("StandardMaterial3D_kgb3i") + +[node name="AimAssistTarget" type="Area3D" parent="." unique_id=1141164558] +collision_layer = 64 +collision_mask = 0 + +[node name="CollisionShape3D" type="CollisionShape3D" parent="AimAssistTarget" unique_id=784189005] +shape = SubResource("SphereShape3D_gxutf") diff --git a/scenes/player_controller/PlayerController.tscn b/scenes/player_controller/PlayerController.tscn index ceb72970..fb526dc7 100644 --- a/scenes/player_controller/PlayerController.tscn +++ b/scenes/player_controller/PlayerController.tscn @@ -109,6 +109,9 @@ blend_mode = 1 [node name="Player" type="CharacterBody3D" unique_id=709076448] collision_mask = 272 script = ExtResource("1_poq2x") +AimAssistStrength = 0.3 +AimAssistReductionWhenCloseToTarget = 0.1 +AimAssistReductionStartDistance = 8.0 RDamage = SubResource("Resource_cb2lu") RKnockback = SubResource("Resource_abfq8") RHealth = SubResource("Resource_ue7xq") @@ -345,6 +348,12 @@ shape = SubResource("SphereShape3D_cmijs") target_position = Vector3(0, 0, -5) collision_mask = 48 +[node name="AimAssistRayCast" type="RayCast3D" parent="." unique_id=995133571] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.6, 0) +target_position = Vector3(0, 0, -6) +collision_mask = 112 +collide_with_areas = true + [node name="GroundDetector" type="ShapeCast3D" parent="." unique_id=1681055424] shape = SubResource("CapsuleShape3D_6lejt") collision_mask = 256 diff --git a/scenes/player_controller/components/head/HeadSystem.cs b/scenes/player_controller/components/head/HeadSystem.cs index eadf8c52..9d454f09 100644 --- a/scenes/player_controller/components/head/HeadSystem.cs +++ b/scenes/player_controller/components/head/HeadSystem.cs @@ -361,6 +361,11 @@ public partial class HeadSystem : Node3D return GetGlobalTransform().Basis.Z; } + public Vector3 GetGlobalForwardVector() + { + return _camera.GlobalBasis.Z; + } + public Vector3 GetGlobalLookRotation() { return new Vector3( diff --git a/scenes/player_controller/components/weapon/weapon.tscn b/scenes/player_controller/components/weapon/weapon.tscn index 301f5a21..5905b367 100644 --- a/scenes/player_controller/components/weapon/weapon.tscn +++ b/scenes/player_controller/components/weapon/weapon.tscn @@ -59,6 +59,7 @@ transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0, 0, mesh = ExtResource("3_svc06") [node name="WeaponLocationIndicator" type="MeshInstance3D" parent="." unique_id=406396593] +visible = false mesh = SubResource("SphereMesh_jpdh0") [node name="StateChart" type="Node" parent="." unique_id=1135887603] diff --git a/scenes/player_controller/scripts/PlayerController.cs b/scenes/player_controller/scripts/PlayerController.cs index b88c4589..b5100d56 100644 --- a/scenes/player_controller/scripts/PlayerController.cs +++ b/scenes/player_controller/scripts/PlayerController.cs @@ -81,6 +81,14 @@ public partial class PlayerController : CharacterBody3D, // Combat stuff [ExportCategory("Combat")] + [ExportGroup("General")] + [Export(PropertyHint.Range, "0,1f,0.01,or_greater")] + public float AimAssistStrength { get; set; } = 0.1f; + [Export(PropertyHint.Range, "0,1f,0.1,or_greater")] + public float AimAssistReductionWhenCloseToTarget { get; set; } = 0.3f; + [Export(PropertyHint.Range, "0,10f,0.1,or_greater")] + public float AimAssistReductionStartDistance { get; set; } = 10f; + [ExportGroup("Damage")] [Export] public RDamage RDamage { get; set; } [Export] public RKnockback RKnockback { get; set; } @@ -372,6 +380,7 @@ public partial class PlayerController : CharacterBody3D, private readonly List _hitEnemies = new List(); private ShapeCast3D _closeEnemyDetector; + private RayCast3D _aimAssisRayCast; private Camera3D _camera; public override void _Ready() @@ -386,6 +395,8 @@ public partial class PlayerController : CharacterBody3D, PlayerUi = GetNode("UI"); _closeEnemyDetector = GetNode("%CloseEnemyDetector"); _closeEnemyDetector.TargetPosition = _closeEnemyDetector.TargetPosition.Normalized() * TargetingDistance; + _aimAssisRayCast = GetNode("AimAssistRayCast"); + _aimAssisRayCast.TargetPosition = _aimAssisRayCast.TargetPosition.Normalized() * (TargetingDistance*1.5f); // DashIndicator = GetNode("%DashIndicator"); PowerCooldownIndicator = GetNode("%DashCooldownIndicator"); @@ -939,6 +950,31 @@ public partial class PlayerController : CharacterBody3D, public float CalculateGravityForce() => _gravity * Weight; // Camera stuff + private Vector2 ComputeAimAssist() + { + _aimAssisRayCast.SetRotation(HeadSystem.GetGlobalLookRotation()); + if (!_aimAssisRayCast.IsColliding()) return Vector2.Zero; + + // Hard dependency on the aim assist to be an Area3D and having a parent + var collidedObject = _aimAssisRayCast.GetCollider() as Area3D; + if (collidedObject is null) return Vector2.Zero; + if (collidedObject.GetParent() is not ITargetable targetable) return Vector2.Zero; + + var targetPosition = targetable.GetTargetGlobalPosition(); + var targetPositionOnCamera = _camera.UnprojectPosition(targetPosition); + var centerOfScreen = _camera.GetViewport().GetVisibleRect().Size / 2f; + var aimAssistDirection = centerOfScreen - targetPositionOnCamera; + var aimAssist = aimAssistDirection * AimAssistStrength / 1000f; + + var distanceToTarget = (targetPosition - HeadSystem.GlobalPosition).Length(); + if (distanceToTarget > AimAssistReductionStartDistance) return aimAssist; // useless but clearer + + // Reduce aim assist when closing in on target because it can mess with the camera when dashing through + var distanceFactor = Mathf.Clamp(distanceToTarget / AimAssistReductionStartDistance, 0f, 1f); + var aimAssistReductionFactor = Mathf.Lerp(AimAssistReductionWhenCloseToTarget, 1f, distanceFactor); + return aimAssist * aimAssistReductionFactor; + } + private void LookAround(double delta) { Vector2 inputLookDir = new Vector2(_inputRotateY, _inputRotateFloorplane); @@ -947,6 +983,9 @@ public partial class PlayerController : CharacterBody3D, var wallHugContactPoint = _onWallRunning.Active ? _currentWallContactPoint : Vector3.Zero; var moveInput = GetGlobalMoveInput(); + if (_isUsingGamepad) + inputLookDir += ComputeAimAssist(); + var lookAroundInputs = new HeadSystem.CameraParameters( Delta: delta, LookDir: inputLookDir, @@ -1772,6 +1811,9 @@ public partial class PlayerController : CharacterBody3D, // DashIndicatorMesh.Visible = true; if (!isOnFloorCustom()) ReduceTimeScaleWhileAiming(); + + _aimAssisRayCast.TargetPosition = _aimAssisRayCast.TargetPosition.Normalized() * + (DashSystem.DashCast3D.TargetPosition.Length() + DashSystem.DashCastRadius); } public void HandleAiming(float delta) { @@ -1785,6 +1827,8 @@ public partial class PlayerController : CharacterBody3D, { DashSystem.StopPreparingDash(); + _aimAssisRayCast.TargetPosition = _aimAssisRayCast.TargetPosition.Normalized() * (TargetingDistance*1.5f); + // DashIndicatorMesh.Visible = false; }