Im trying to convert the Wedge formation to work as a TacticalBase class, so the units can move as a wedge while attacking.
Changing it to derive from tacticalBase does get it to attack, but when I set protected override bool StopWithinRange => true;, the formation tends to collapse, losing its wedge formation. If I leave it set to false, the formation holds but the units get far too close to their attack target.
Is there a proper way to get the formation to move into range and stop, continuing to attack, while holding formation?
Changing it to derive from tacticalBase does get it to attack, but when I set protected override bool StopWithinRange => true;, the formation tends to collapse, losing its wedge formation. If I leave it set to false, the formation holds but the units get far too close to their attack target.
Code:
namespace Opsive.BehaviorDesigner.AddOns.TacticalPack.Runtime.Tasks
{
using Opsive.BehaviorDesigner.AddOns.Shared.Runtime;
using Opsive.BehaviorDesigner.Runtime.Tasks;
using Opsive.BehaviorDesigner.Runtime.Utility;
using Opsive.GraphDesigner.Runtime;
using Opsive.GraphDesigner.Runtime.Variables;
using Unity.AppUI.UI;
using Unity.Entities;
using UnityEngine;
[Opsive.Shared.Utility.Description("Attack in Wedge Formation")]
[DefaultAttackDelay(AttackDelay.GroupArrival)]
public class WedgeAttack : TacticalBase
{
/// <summary>
/// Specifies how the wedge should be oriented.
/// </summary>
public enum WedgeOrientation
{
Forward, // The wedge points forward.
Backward, // The wedge points backward.
Left, // The wedge points left.
Right // The wedge points right.
}
protected override bool StopWithinRange => true;
[Tooltip("The spacing between agents.")]
[SerializeField] protected SharedVariable<float> m_Spacing = 2f;
[Tooltip("The angle between the two sides of the wedge in degrees.")]
[SerializeField] protected SharedVariable<float> m_WedgeAngle = 45f;
[Tooltip("Specifies how the wedge should be oriented.")]
[SerializeField] protected SharedVariable<WedgeOrientation> m_WedgeOrientation = WedgeOrientation.Forward;
[Tooltip("The current command ID from blackboard.")]
[SerializeField] protected SharedVariable<string> m_CurrentCommandId;
/// <summary>
/// Calculate the position for this agent in the wedge formation.
/// </summary>
/// <param name="index">The index of this agent in the formation.</param>
/// <param name="totalAgents">The total number of agents in the formation.</param>
/// <param name="center">The center position of the formation.</param>
/// <param name="forward">The forward direction of the formation.</param>
/// <param name="samplePosition">Should the position be sampled?</param>
/// <returns>The position for this agent.</returns>
public override Vector3 CalculateFormationPosition(int index, int totalAgents, Vector3 center, Vector3 forward, bool samplePosition)
{
if (index == 0)
{
return center;
}
// Calculate the position in local space.
var row = Mathf.FloorToInt((Mathf.Sqrt(8 * index + 1) - 1) / 2);
var rowStart = row * (row + 1) / 2;
var angle = ((index - rowStart) - row / 2f) / (row / 2f) * (m_WedgeAngle.Value / 2f) * Mathf.Deg2Rad;
var distance = (row + 1) * m_Spacing.Value;
// Determine the position and rotation based on orientation.
Quaternion rotation;
Vector3 localPosition;
if (m_Is2D)
{
// Use the XY plane for 2D.
localPosition = new Vector3(Mathf.Sin(angle) * distance, -(row * m_Spacing.Value), 0);
var forward2D = new Vector2(forward.x, forward.y).normalized;
angle = Mathf.Atan2(forward2D.y, forward2D.x) * Mathf.Rad2Deg;
switch (m_WedgeOrientation.Value)
{
case WedgeOrientation.Backward:
angle += 180f;
break;
case WedgeOrientation.Left:
angle += 90f;
break;
case WedgeOrientation.Right:
angle -= 90f;
break;
}
rotation = Quaternion.Euler(0, 0, angle);
}
else
{
// Use the XZ plane for 3D.
localPosition = new Vector3(Mathf.Sin(angle) * distance, 0, -(row * m_Spacing.Value));
switch (m_WedgeOrientation.Value)
{
case WedgeOrientation.Backward:
rotation = Quaternion.LookRotation(-forward, m_Transform.up);
break;
case WedgeOrientation.Left:
rotation = Quaternion.LookRotation(Vector3.Cross(forward, m_Transform.up), m_Transform.up);
break;
case WedgeOrientation.Right:
rotation = Quaternion.LookRotation(-Vector3.Cross(forward, m_Transform.up), m_Transform.up);
break;
default: // Forward.
rotation = Quaternion.LookRotation(forward, m_Transform.up);
break;
}
}
// Calculate the agent's position.
var position = center + rotation * localPosition;
var validPos = position;
if (samplePosition && SamplePosition(ref validPos))
{
position = validPos;
}
return position;
}
/// <summary>
/// Resets the task values back to their default.
/// </summary>
public override void Reset()
{
base.Reset();
m_Spacing = 2f;
m_WedgeAngle = 45f;
m_WedgeOrientation = WedgeOrientation.Forward;
}
}
}
Is there a proper way to get the formation to move into range and stop, continuing to attack, while holding formation?