Improve aim assist target stickiness
This commit is contained in:
parent
c2ebbfaa2d
commit
3975959017
5 changed files with 122 additions and 64 deletions
|
|
@ -54,42 +54,66 @@ func apply_stats() -> void:
|
|||
# past about 45 the gun starts choosing targets on its own, which is the
|
||||
# behaviour #80 deliberately took away.
|
||||
const ASSIST_CONE_DEGREES: float = 28.0
|
||||
# #181: a nearby cursor is a second way to express intent. This is deliberately
|
||||
# smaller than the directional cone at ordinary weapon range: it catches a
|
||||
# pointer placed beside something without making the whole screen magnetic.
|
||||
const ASSIST_CURSOR_RADIUS: float = 180.0
|
||||
const ASSIST_TARGET_GROUP: StringName = &"player_aim_targets"
|
||||
|
||||
|
||||
# The heading a shot fired from `origin` along `direction` should actually
|
||||
# take: straight, unless an enemy sits inside the assist cone and within
|
||||
# reach, in which case it points at the nearest one.
|
||||
# The concrete point an aim should stick to. A target can qualify in two ways:
|
||||
# it is close to the cursor, or it lies inside the established directional
|
||||
# cone. Cursor proximity wins, then distance from the shooter breaks the old
|
||||
# directional case. This preserves approximate stick aiming while letting a
|
||||
# deliberately placed cursor choose a farther target in the same direction.
|
||||
#
|
||||
# Static, and used by BOTH the gun and the aim guide (see
|
||||
# FireStickController._draw). If the guide drew the raw stick heading while
|
||||
# the shot took an assisted one, the line would be telling the player
|
||||
# something the gun does not do - the same class of mismatch #86 removed
|
||||
# between the guide's length and a bolt's actual reach.
|
||||
#
|
||||
# Enemies only, never asteroids. Snapping onto a rock while the player is
|
||||
# trying to hit the thing shooting at them would be the assist working
|
||||
# against them; rocks are large, stationary enough, and easy to hit on
|
||||
# purpose.
|
||||
# Targets opt in through one group rather than this utility branching on
|
||||
# enemies versus asteroids. That keeps future aimable compositions out of the
|
||||
# weapon code: joining the contract is all they need to do.
|
||||
static func assisted_point(tree: SceneTree, origin: Vector2, point: Vector2,
|
||||
reach: float) -> Vector2:
|
||||
var to_point: Vector2 = point - origin
|
||||
var heading: Vector2 = to_point.normalized()
|
||||
if heading == Vector2.ZERO or tree == null or reach <= 0.0:
|
||||
return point
|
||||
var cone: float = cos(deg_to_rad(ASSIST_CONE_DEGREES))
|
||||
var cursor_pick: Node2D = null
|
||||
var cursor_distance: float = INF
|
||||
var direction_pick: Node2D = null
|
||||
var direction_distance: float = INF
|
||||
for target_node in tree.get_nodes_in_group(ASSIST_TARGET_GROUP):
|
||||
if not is_instance_valid(target_node) or not (target_node is Node2D) \
|
||||
or target_node.is_queued_for_deletion():
|
||||
continue
|
||||
var target: Node2D = target_node as Node2D
|
||||
var to_target: Vector2 = target.global_position - origin
|
||||
var distance: float = to_target.length()
|
||||
if distance > reach or distance < 0.001:
|
||||
continue
|
||||
var from_cursor: float = target.global_position.distance_to(point)
|
||||
if from_cursor <= ASSIST_CURSOR_RADIUS and from_cursor < cursor_distance:
|
||||
cursor_distance = from_cursor
|
||||
cursor_pick = target
|
||||
if to_target.normalized().dot(heading) >= cone and distance < direction_distance:
|
||||
direction_distance = distance
|
||||
direction_pick = target
|
||||
if cursor_pick != null:
|
||||
return cursor_pick.global_position
|
||||
if direction_pick != null:
|
||||
return direction_pick.global_position
|
||||
return point
|
||||
|
||||
|
||||
# Compatibility entry point for systems that genuinely have only a heading.
|
||||
# Their virtual cursor sits at maximum range; the returned vector still points
|
||||
# at the same target selected by assisted_point().
|
||||
static func assisted_heading(tree: SceneTree, origin: Vector2, direction: Vector2,
|
||||
reach: float) -> Vector2:
|
||||
var heading: Vector2 = direction.normalized()
|
||||
if heading == Vector2.ZERO or tree == null:
|
||||
if heading == Vector2.ZERO:
|
||||
return heading
|
||||
var cone: float = cos(deg_to_rad(ASSIST_CONE_DEGREES))
|
||||
var nearest: Vector2 = Vector2.ZERO
|
||||
var nearest_distance: float = INF
|
||||
for enemy in tree.get_nodes_in_group("enemies"):
|
||||
if not is_instance_valid(enemy) or not (enemy is Node2D):
|
||||
continue
|
||||
var to_enemy: Vector2 = (enemy as Node2D).global_position - origin
|
||||
var distance: float = to_enemy.length()
|
||||
if distance > reach or distance < 0.001 or distance >= nearest_distance:
|
||||
continue
|
||||
if to_enemy.normalized().dot(heading) < cone:
|
||||
continue
|
||||
nearest_distance = distance
|
||||
nearest = to_enemy
|
||||
return heading if nearest == Vector2.ZERO else nearest.normalized()
|
||||
var point: Vector2 = assisted_point(tree, origin, origin + heading * reach, reach)
|
||||
return (point - origin).normalized()
|
||||
|
||||
|
||||
# Where this gun is firing FROM. The hull it is bolted to.
|
||||
|
|
@ -130,10 +154,12 @@ func is_firing() -> bool:
|
|||
func firing_heading() -> Vector2:
|
||||
if not has_fire_target:
|
||||
return Vector2.ZERO
|
||||
var to_point: Vector2 = fire_target - muzzle()
|
||||
var origin: Vector2 = muzzle()
|
||||
var to_point: Vector2 = fire_target - origin
|
||||
if to_point.length() <= 0.001:
|
||||
return Vector2.ZERO
|
||||
return assisted_heading(get_tree(), muzzle(), to_point, fire_range)
|
||||
var point: Vector2 = assisted_point(get_tree(), origin, fire_target, fire_range)
|
||||
return (point - origin).normalized()
|
||||
|
||||
|
||||
func tick(delta: float) -> void:
|
||||
|
|
|
|||
Loading…
Reference in a new issue