Delayed click targeting getting cancelled

Hi, I need help. The case is:
when I cast the spell super mega splash on the ground, my player instantly moves to the targeted location instead of executing the spell.
but when I cast the spell and the mouse pointer is above an object with a different layer something like in the default layer. The spell is being executed normally.
I have my ground object set to ground layer, and targeting to also ground layer.
ground layer

I am having trouble figuring out why that is. I’m looking at the code and I think I have something wrong with my delayed click targeting which I don’t understand and don’t know where.

using RPG.Control;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace RPG.Abilities.Targeting
{
	[CreateAssetMenu(fileName = "Delayed Targeting", menuName = "Abilities/Targeting/DelayedClick", order = 0)]
	public class DelayedClickTargeting : TargetingStrategy
	{
		[SerializeField] private Texture2D _cursorTexture;
		[SerializeField] private Vector2 _cursorHotspot;
		[SerializeField] private float _areaAffectRadius;
		[SerializeField] private LayerMask _layerMask;
		[SerializeField] private Transform _targetingPrefab;

		private Transform _targetingPrefabInstance = null;

		public override void StartTargeting(AbilityData data, Action finished)
		{
			PlayerController playerController = data.GetUser().GetComponent<PlayerController>();
			playerController.StartCoroutine(Targeting(data, playerController, finished));
		}

		private IEnumerator Targeting(AbilityData data, PlayerController playerController, Action finished)
		{
			playerController.enabled = false;

			if (_targetingPrefabInstance == null)
			{
				_targetingPrefabInstance = Instantiate(_targetingPrefab);
			}
			else
			{
				_targetingPrefabInstance.gameObject.SetActive(true);
			}

			_targetingPrefabInstance.localScale = new Vector3(_areaAffectRadius * 2, 1, _areaAffectRadius * 2);

			while (!data.IsCancelled())
			{
				Cursor.SetCursor(_cursorTexture, _cursorHotspot, CursorMode.Auto);
				RaycastHit raycastHit;
				if (Physics.Raycast(PlayerController.GetMouseRay(), out raycastHit, 1000, _layerMask))
				{
					_targetingPrefabInstance.position = raycastHit.point;

					if (Input.GetMouseButtonDown(0))
					{
						// Absorb the whole mouse click
						yield return new WaitWhile(() => Input.GetMouseButton(0));
						data.SetTargetedPoint(raycastHit.point);
						data.SetTargets(GetGameObjectsInRadius(raycastHit.point));
						
						break;
					}
				}
				yield return null;
			}
			_targetingPrefabInstance.gameObject.SetActive(false);
			playerController.enabled = true;
			finished();
		}

		private IEnumerable<GameObject> GetGameObjectsInRadius(Vector3 point)
		{

				RaycastHit[] hits = Physics.SphereCastAll(point, _areaAffectRadius, Vector3.up, 0);
				foreach (var hit in hits)
				{
					yield return hit.collider.gameObject;	
				}
			}
		}
	}

The code looks correct, meaning that the click should be consumed. This leaves two possibilities… one (unlikely) is that you have a second PlayerController on the Player Gameobject. In this case, the 2nd one won’t be deactivated.
I said unlikely because I’ve only seen it happen once.

The other possibility is that something in the effec strategy is transporting the User instead of transporting the effect prefab to the target point. Paste in the effect strategy(ies) you’re using for this effect and we’ll take a look.

This is my ability setup.

Effect Strategy Orient to Target

	public class OrientToTargetEffect : EffectStrategy
	{
		public override void StartEffect(AbilityData data, Action finished)
		{
			data.GetUser().GetComponent<Transform>().LookAt(data.GetTargetedPoint());
			finished();
		}
	}

Effect Strategy Trigger animation

    public class TriggerAnimationEffect : EffectStrategy
    {
		[SerializeField] private string _animationTrigger;

		public override void StartEffect(AbilityData data, Action finished)
		{
            Animator animator = data.GetUser().GetComponent<Animator>();
            animator.SetTrigger(_animationTrigger);
            finished();
        }
    }

Effect Strategy COMPOSITE

public class DelayCompositeEffect : EffectStrategy
	{
		[SerializeField] private float _delay = 0f;
		[SerializeField] private EffectStrategy[] _delayedEffects;
		[SerializeField] private bool _abortIfCancelled = false;

		public override void StartEffect(AbilityData data, Action finished)
		{
			data.StartCoroutine(DelayedEffects(data, finished));
		}

		private IEnumerator DelayedEffects(AbilityData data, Action finished)
		{
			yield return new WaitForSeconds(_delay);
			if (_abortIfCancelled && data.IsCancelled()) yield break;

			foreach (var effect in _delayedEffects)
			{
				effect.StartEffect(data, finished);
			}
		}
	}

Effect Damage

public class HealthEffects : EffectStrategy
    {
		[SerializeField] private float _healthChange;

		public override void StartEffect(AbilityData data, Action finished)
		{
			foreach (var target in data.GetTargets())
			{
				var health = target.GetComponent<Health>();

				if (health)
				{
					if (_healthChange < 0)
					{
						health.TakeDamage(data.GetUser(), -_healthChange);
					}
					else
					{
						health.Heal(_healthChange);
					}
				}
			}
			finished();
		}
    }

Effect Prefab Spawner

public class SpawnTargetPrefabEffects : EffectStrategy
    {
        [SerializeField] private Transform prefabToSpawn;
        [SerializeField] private float destroyDelay = -1;

        public override void StartEffect(AbilityData data, Action finished)
        {
            data.StartCoroutine(Effect(data, finished));
        }

        private IEnumerator Effect(AbilityData data, Action finished)
        {
            Transform instance = Instantiate(prefabToSpawn);
            instance.position = data.GetTargetedPoint();
            if (destroyDelay > 0)
            {
                yield return new WaitForSeconds(destroyDelay);
                Destroy(instance.gameObject);
            }
            finished();
        }
    }

This is quite long. :grinning_face_with_smiling_eyes:

This is what is happening. When I execute spell on the ground. player just moves.
but when I target like a tree on default layer it is okay spell is casted as intended.

Hmmm… it looks like the Input.GetMouseButton() is still firing true when the PlayerController gets control back from the effect.

Let’s try a couple things to ensure that extra frame break:
Before the break; statement in the DelayedClickTargetting, in the if(Input.GetMouseButtonDown(0)), put this line in the blank linebefore break;

yield return null;

If that doesn’t work, then let’s trick PlayerController into not acting even though you’ve turned it back on… This one is a bit more complex:

in PlayerController, add this:

private bool isLocked=false;

private IEnumerator LockRoutine()
{
    isLock=true;
    yield return new WaitForSeconds(1);
    isLock=false;
}

public void Lock()
{
    StartCoroutine(LockRoutine());
}

and in the beginning of Update()

if(isLock) return;

Now back to DelayedClickTargetting, right after playerController.enabled=true; add this:

playerController.Lock();

Hey, So I implemented both of this solutions and it didn’t work.
but because of this I got the idea of maybe I am implementing somewhere that enables the player controller while casting is supposed to disable it and re-enable once the spell is done.

I searched through my whole project and found it.
It works now. It came from my own faulty code. :sob:

Thank you for the help. I learned something again.

These things do happen. If the last patch didn’t do the trick then it had to be something else allowing the PC to be enabled and pick up the click.

Good job finding the culprit!

1 Like

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.

Privacy & Terms