Issue with script updating in editor

Hi,

I am trying to create a scrip that can be attached to a game object, has a variable “maxPoints” which when changed in the editor, I want it to draw the new sprites in the editor (and of course, create the sprites at runtime).

I’ve created a script that creates the sprites in the editor when I set the variable, but if I decrease the variable, it doesn’t get rid of the old sprites, it just gives a warning “SendMessage cannot be called during Awake, CheckConsistency, or OnValidate (Lane0L1: OnDidAddComponent)”.

Script:

using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.SocialPlatforms.Impl;

[ExecuteInEditMode]
public class BottomBumperManager : MonoBehaviour
{
    [SerializeField]
    public GameObject exitAnim;
    [SerializeField]
    public Sprite emptyPoint;
    [SerializeField]
    public Sprite fullPoint;
    [SerializeField]
    public int maxPoints = 3;
    [SerializeField]
    public int laneNumber = 0;

    private int currentScore = 0;
    private List<GameObject> markers = new List<GameObject>();
    private List<GameObject> markersToDestroy = new List<GameObject>();

    void Start()
    {
        if (Application.isPlaying)
        {
            CreateMarkers();
        }
    }

    void CreateMarkers()
    {
        Debug.Log("Creating Markers");

        // Manage old markers
        if (!Application.isPlaying)
        {
            // Clear any existing markers for editing
            markersToDestroy.AddRange(markers);
            markers.Clear();
        }

        // Create and position markers
        for (int i = 0; i < maxPoints; i++)
        {
            GameObject marker = new GameObject($"Lane{laneNumber}L{i + 1}");
             = transform; // Set this as the parent

            SpriteRenderer spriteRenderer = marker.AddComponent<SpriteRenderer>();
            spriteRenderer.sprite = emptyPoint;

            // Position marker vertically
            var width = transform.localScale.x / 4;

            Vector3 currentPosition = transform.position;
            currentPosition.y += ((transform.localScale.x / 2) * i);

            marker.transform.position = currentPosition;

            marker.transform.localScale = new Vector3(width, width, 1);

            markers.Add(marker);
        }

        // Start coroutine to destroy old markers after the frame is rendered
        StartCoroutine(DestroyOldMarkers());
    }

    void OnCollisionEnter2D(Collision2D collision)
    {
        if (collision.gameObject.name == "Ball")
        {
            //Reduce bounce to stop ball leaving it's lane
            var rb2d = collision.gameObject.GetComponent<Rigidbody2D>();
            rb2d.sharedMaterial.bounciness = 0.001f;

            //Report back that drop is complete
            GameManager.Instance.DropComplete();

            //Start exit animation if one is attached
            if (exitAnim != null)
            {
                var _exitAnim = Instantiate(exitAnim);
                _exitAnim.transform.position = collision.gameObject.transform.position;
            }

            //Increment lane score
            currentScore++;

            //Increment lane marker
            //var marker = GameObject.Find($"Lane{laneNumber}L{currentScore}");
            var marker = markers.Where(p =>  == $"Lane{laneNumber}L{currentScore}").FirstOrDefault();

            if (marker != null && maxPoints > 0)
            {
                var spriteRender = marker.GetComponent<SpriteRenderer>();
                spriteRender.sprite = fullPoint;
            }

            //Destroy ball
            Destroy(collision.gameObject);

            //Check if we are at our max
            if (currentScore == maxPoints)
            {
                Debug.Log("Done");
            }

        }
    }

    void OnValidate()
    {
        int currentMarkerCount = markers.Count;

        if (currentMarkerCount != maxPoints)
        {
            CreateMarkers();
        }
    }

    IEnumerator DestroyOldMarkers()
    {
        yield return new WaitForEndOfFrame();

        foreach (var marker in markersToDestroy)
        {
            Destroy(marker);
        }
        markersToDestroy.Clear();
    }
}

I am sure I am doing a whole long wrong here, but any help would be appreciated.

OnValidate should be used for validation only. If you make any API calls that end up performing things such as callbacks then it’ll throw that error (callbacks use SendMessage to get the work done).

Look at the “Collision2D” that’s passed, it gives you everything involved in the collision. Don’t go searching for components.

Thank you, so how would I update this script so that when I change the variable in the editor, it updates the amount of sprites visible in the editor? It is creating the sprites in the editor, but if I reduce the variable, it is not clearing the old ones before creating new ones

You would use a custom inspector, editor window, or perhaps a custom Editor tool.

Custom Inspector: Unity - Scripting API: Editor
Editor Window: Unity - Scripting API: EditorWindow
Editor Tool: Unity - Scripting API: EditorTool

OnValidate should only modify values within the same asset. It shouldn’t reach out and modify other assets.

1 Like