Weapon handling #2

Hi there! If you haven’t seen the previous post (weapon handling #1), then here it is, or here are the very beginning.

Setup the player's weapon

So we have projectiles, let's make the weapon which will start them on their way. I won't go into details according to the ComponentData and Proxy, nothing special about them, so here are the code:

ComponentData


// Assets/Scripts/Player/WeaponData.cs

using Unity.Entities;

public struct WeaponData : IComponentData
{
  public float MovementMultiplier;
  public float FireRate;

  public Entity WeaponTip;
}

MovementMultiplier will affect the rotation speed, and the WeaponTip is the WeaponTip GameObject in the hierarchy from the previous post.

WeaponProxy


// Assets/Scripts/Player/WeaponProxy.cs

using Unity.Entities;
using UnityEngine;

public class WeaponProxy : MonoBehaviour, IConvertGameObjectToEntity
{
#pragma warning disable 0649
  [SerializeField] private GameObject _weaponTip;
  [SerializeField] private float _movementMultiplier;
  [SerializeField] private float _fireRate;
#pragma warning restore 0649

  public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem)
  {
    WeaponData wd = new WeaponData()
    {
      MovementMultiplier = _movementMultiplier,
      FireRate = _fireRate,

      WeaponTip = conversionSystem.GetPrimaryEntity(_weaponTip)
    };
    dstManager.AddComponentData(entity, wd);

    World.Active.GetOrCreateSystem<ProjectileMovementSystem>().SetSingleton(wd);
  }
}

WeaponSystem


// Assets/Scripts/Player/WeaponSystem.cs

using Unity.Entities;
using Unity.Transforms;
using UnityEngine;

public class WeaponSystem : ComponentSystem
{
  private EntityQuery _weaponMoveQuery, _projectileQuery;
  private float _curFireRate;

  protected override void OnCreate()
  {
    _weaponMoveQuery = GetEntityQuery(ComponentType.ReadWrite<WeaponData>(), ComponentType.ReadWrite<Rotation>());
    _projectileQuery = GetEntityQuery(ComponentType.ReadWrite<ProjectileData>());
  }

  protected override void OnUpdate()
  {
    bool canShoot = false;
    Entity weaponTip = Entity.Null;

    Entities.With(_weaponMoveQuery).ForEach(
      (Entity entity, ref WeaponData weaponData, ref Rotation rotation) =>
      {
        Vector2 delta;
#if UNITY_EDITOR || UNITY_STANDALONE
        delta = new Vector2(Input.GetAxis("Mouse X"), Input.GetAxis("Mouse Y")) * weaponData.MovementMultiplier;
        _curFireRate -= Time.deltaTime;
        if (!Input.GetMouseButton(0)) return;
#endif

#if UNITY_ANDROID || UNITY_IOS
#if UNITY_EDITOR
        if (!Application.isEditor)
        {
#endif
          if (Input.touchCount == 0) return;
          delta = Input.touches[0].deltaPosition;
#if UNITY_EDITOR
        }
#endif
#endif

        Quaternion rot = rotation.Value;
        rot = Quaternion.Euler(
          rot.eulerAngles.x - delta.y,
          rot.eulerAngles.y + delta.x,
          rot.eulerAngles.z);

        rotation.Value = rot;

        if (_curFireRate <= .0f)
        {
          _curFireRate = weaponData.FireRate;
          weaponTip = weaponData.WeaponTip;
          canShoot = true;
        }
      });

    if (weaponTip == Entity.Null) return;

    Entities.With(_projectileQuery).ForEach(
      (Entity entity, ref ProjectileData projectileData) =>
      {
        if (canShoot && projectileData.Status == ProjectileStatus.Disabled)
        {
          projectileData.WeaponTipPos = EntityManager.GetComponentData(weaponTip);
          projectileData.Status = ProjectileStatus.Created;

          canShoot = false;
          return;
        }
      });
  }
}

Because of the Input processing, we can't use the Job system, so to make the Entities.With as fast as possible we create two query, one for getting the weapon by the WeaponData and Rotation, and all of the projectiles with the ProjectileData.

In our first Entities.With, we get the Input data. In this example in case of editor or standalone I process the mouse input, and on mobile platforms the touches. We reduce the current firerate with DeltaTime and check if there is any input. If not, then return.

When we have the delta position of the mouse or touch, we apply it to the rotation ot the weapon, and then we check the cooldown of the weapon. If it's equals or below zero, then we can shoot.

In our next Entities.With we check if we can shoot and the actual projectile is ready. If yes, then we place it to the WeaponTip's position and change it's status what is handled in the ProjectileMovementSystem, described in the previous post.

That's it. Now we can shoot projectiles from out weapon, which can be rotated by our mouse or finger.

In the next post, we will destroy the enemies with the projectiles.