Updating the positions of a bezier from a VFXGraph in real time

Hi! I’m a complete beginner and I’m using tutorials and chatgpt to learn, I hope that doesn’t make everyone immediately mad. Sorry!

What I’m trying to do is make a top down car combat game, I’m currently working on a weapon that orbits around a car and zaps electric arcs out at cars nearby. I’ve created a Electric arc using this great tutorial: https://www.youtube.com/watch?v=8NWqTKYEIlU

Then I’m trying to use code to move the positions (Pos1-4) around, making Pos1 the weapon and Pos4 the target car then setting 2 and 3 in between. However when the prefab is called it just displays the arc in the preset bezier curve in a fixed position on the map.

What am i doing wrong? Here is my (and chatgpt’s) code:

using UnityEngine;
using UnityEngine.VFX;

public class OrbitingWeapon : MonoBehaviour
{
    public float flySpeed = 10f; // Speed at which the weapon flies forward
    public float detectionRadius = 5f; // Radius to detect nearby cars
    public float orbitDistance = 3f; // Distance to maintain while orbiting
    public float orbitDuration = 5f; // Duration of the orbit before self-destructing
    public float zapRadius = 2f; // Radius for zapping nearby cars
    public int damagePerZap = 10; // Damage dealt per zap
    public float orbitSpeedMultiplier = 1.5f; // Speed multiplier for orbiting

    public GameObject zapVFXPrefab; // Electric Ark prefab for zap effect

    private Transform targetCar; // Car being targeted
    private Transform firingCar; // Car that fired this weapon
    private bool isOrbiting = false; // Status of the weapon's orbiting
    private float orbitTimer; // Timer to track orbit duration
    private Vector2 orbitOffset; // Offset position for orbiting
    private VisualEffect zapVFXInstance; // Instance of the zap effect

    private bool hasLoggedZapInfo = false; // Flag to track zap logging

    public void Initialize(Transform firingCarTransform)
    {
        firingCar = firingCarTransform; // Set the firing car
    }

    void Update()
    {
        if (isOrbiting)
        {
            OrbitAroundTarget();
            ZapNearbyCars();

            orbitTimer += Time.deltaTime; // Increment orbit timer
            if (orbitTimer >= orbitDuration)
            {
                Destroy(gameObject); // Destroy the weapon after orbit duration
            }
        }
        else
        {
            FlyForward(); // Move the weapon forward
            CheckForNearbyCars(); // Check for nearby cars to target
        }
    }

    private void FlyForward()
    {
        transform.Translate(Vector2.up * flySpeed * Time.deltaTime); // Move forward
    }

    private void CheckForNearbyCars()
    {
        Collider2D[] hits = Physics2D.OverlapCircleAll(transform.position, detectionRadius); // Detect nearby cars

        foreach (var hit in hits)
        {
            if (hit.CompareTag("Damaging") && hit.transform != firingCar) // Check if hit is a damaging car and not the firing car
            {
                targetCar = hit.transform; // Set target car
                Debug.Log($"Target car detected: {targetCar.name}");
                BeginOrbit(); // Start orbiting around the target car
                break;
            }
        }
    }

    private void BeginOrbit()
    {
        isOrbiting = true; // Set orbiting status
        orbitTimer = 0f; // Reset orbit timer
        orbitOffset = (transform.position - targetCar.position).normalized * orbitDistance; // Calculate orbit offset
        Debug.Log($"Orbiting started. Orbit offset: {orbitOffset}");
    }

    private void OrbitAroundTarget()
    {
        // Update orbit offset based on orbit speed
        orbitOffset = Quaternion.Euler(0, 0, flySpeed * orbitSpeedMultiplier * Time.deltaTime) * orbitOffset;
        transform.position = (Vector2)targetCar.position + orbitOffset; // Update position based on target car's position

        Debug.Log($"Orbiting around target car at position: {targetCar.position}. Current position: {transform.position}");
    }

    private void ZapNearbyCars()
    {
        Collider2D[] carsInRange = Physics2D.OverlapCircleAll(transform.position, zapRadius); // Check for cars in zap range

        foreach (Collider2D car in carsInRange)
        {
            if (car.CompareTag("Damaging") && car.transform != targetCar && car.transform != firingCar) // Validate car for zapping
            {
                TopDownCarController carController = car.GetComponent<TopDownCarController>(); // Get the car controller
                if (carController != null)
                {
                    // Log zap info only once
                    if (!hasLoggedZapInfo)
                    {
                        Debug.Log($"Weapon Position: {transform.position}, Target Position: {car.transform.position}, Zap Effect Position: {transform.position + (car.transform.position - transform.position).normalized}");
                        hasLoggedZapInfo = true; // Set the flag to true after logging
                    }

                    carController.TakeDamage(damagePerZap); // Apply damage
                    Debug.Log($"Zapping car: {car.name}, Damage dealt: {damagePerZap}");

                    // Instantiate zap effect if it doesn't exist yet
                    if (zapVFXInstance == null)
                    {
                        GameObject zapEffectObj = Instantiate(zapVFXPrefab, transform.position, Quaternion.identity);
                        zapVFXInstance = zapEffectObj.transform.Find("ElectricArk-VFXGraph")?.GetComponent<VisualEffect>();

                        if (zapVFXInstance == null)
                        {
                            Debug.LogError("ElectricArk-VFXGraph child object with VisualEffect component is missing!");
                            Destroy(zapEffectObj); // Clean up if the effect is not valid
                            return;
                        }
                        Debug.Log("Zap effect instantiated.");
                    }

                    // Update positions for the zap effect
                    UpdateZapEffectPositions(car); // Update the bezier points for the zap effect
                }
            }
        }

        // Reset the flag after the zap is performed to allow future logging if needed
        if (carsInRange.Length > 0)
        {
            hasLoggedZapInfo = false;
        }
    }

    private void UpdateZapEffectPositions(Collider2D car)
    {
        Vector3 carPosition = car.transform.position;
        Vector3 weaponPosition = transform.position; // Get current weapon position

        // Set the start point (orbiter) and end point (car being zapped)
        zapVFXInstance.SetVector3("Pos1", weaponPosition); // Start point (orbiter)
        zapVFXInstance.SetVector3("Pos4", carPosition); // End point (car being zapped)

        // Calculate the midpoints for bezier curve dynamically
        Vector3 midPoint = (weaponPosition + carPosition) / 2;

        // Control point for bezier curve
        Vector3 controlOffset = new Vector3(0, 1.5f, 0); // Height adjustment for curve

        // Set control points for bezier curve
        zapVFXInstance.SetVector3("Pos2", midPoint + controlOffset);
        zapVFXInstance.SetVector3("Pos3", midPoint - controlOffset);

        // Debug log for bezier positions
        Debug.Log($"Zap effect positions updated: Pos1={weaponPosition}, Pos2={midPoint + controlOffset}, Pos3={midPoint - controlOffset}, Pos4={carPosition}");

        zapVFXInstance.Play(); // Play the effect each time we zap
    }
}

While I’m at it actually, the weapon also doesn’t ignore the firing car and in fact attaches itself to the car that fired the weapon. thats the problem after this problem, maybe I can get a twofer?

Morning I’m not really sure why your Positions aren’t properly updated.
Usually, when a thing like this happens, a good practice is to take a step back and simplify as much as possible the setup so that you can isolate what’s causing the issue.

So, first, what you can do is to isolate the part that sends the position with a simpler script and a simple VFX.
VisualEfffect.SetVector3("Pos1", Targetpos);

You can also temporally simplify your VFX so that it’s easier to debug.
I’m joining a small package so that you can take a look. As you can see, I’ve simplified the VFX. Instead of using a sample Bezier and four positions, I’m only interpolating between the local Pivot and a “Target Position” property.

This allows me to be sure that the problem isn’t coming from the Sample Bezier operator and I only have one Target Position to set in C# which is easier to test.

I’ve kept the script simple but similar in the order of operation compared to yours:

  1. Instance VFX
  2. Set property
  3. VisualEffect.Play()
    using UnityEngine;
    using UnityEngine.VFX;
    public class SpawnTest : MonoBehaviour
    {
        [SerializeField] private VisualEffect vfx;

        private VisualEffect _vfxInstance;

        void Start()
        {
            _vfxInstance = Instantiate(vfx, transform);
            InvokeRepeating(nameof(PlayVfx), 1.0f, 0.5f);

        }
        private void PlayVfx()
        {
            var targetPos = transform.position + RandomPos();
            _vfxInstance.SetVector3("TargetPos", targetPos);
            _vfxInstance.Play();
        }

        private Vector3 RandomPos()
        {
            var pos = Random.insideUnitSphere * Random.Range(0.0f, 8.0f);
            return pos;
        }
        
    }

If this is working on your end, you should see the TargetPos being updated in the inspector while in Play Mode. With this working, you can then build upon and replace the TargetPos by your Vehicle’s positions and slowly checking each part of your script.

Unity_krR1WMqHry
Unity_CsySteQNQg

I hope that this process will help you find what’s not working in your setup. Have a lovely day.
VFXG_ElectricZapDebug.unitypackage (21.8 KB)