Sometimes the link to a component breaks. Is it the component?

Hi all!

I use Cocos Creator version 3.8.6.

I have my own IconView component in my project, it is located in the following path:

assets/scripts/coreView/IconView.ts

I don’t know exactly under what conditions, but sometimes the link to this component becomes invalid. I just start the game and get an error that I’m trying to call a function on an object that is not assigned.

This doesn’t happen often, but it’s still unpleasant. For example, I’m working with some part of the UI, placing completely different things, assigning links, saving the prefab (usually I then close the prefab), launching the scene and getting an error (although I didn’t touch the IconView links manually). As far as I understand, this only happens if I open the prefab. I don’t think I’ve ever had links get messed up like that in prefabs that I haven’t opened recently. Afterwards, I have to open the prefab and assign the links again (in the inspector it shows as if I didn’t set anything, and not a missing object).

To be honest, I’m already tired of this. I noticed this since version 3.8.3.

I have many components in my project, but I only got this error with three of my components. Two of them are gone - only IconView remains.

What’s wrong with him?

import { Color, Component, Enum, Size, Sprite, SpriteFrame, UITransform, _decorator } from "cc";
const { ccclass, property } = _decorator;

export enum IconFillingMethod {
	filling,																	// May go out of area
	fitting																		// Will be included in the area
}
Enum(IconFillingMethod);

@ccclass('IconView')
export class IconView extends Component {
	@property({ type: UITransform })
    private areaUITransform: UITransform;
	@property({ type: UITransform })
    private iconUITransform: UITransform;
	@property({ type: Sprite })
    private icon: Sprite;
	@property({ type: IconFillingMethod })
    private fillingMethod: IconFillingMethod = IconFillingMethod.fitting;

	public SetIcon(sprite: SpriteFrame): void {
		this.icon.spriteFrame = sprite;
		this.UpdateSize();
	}

	public SetIconWithOriginalSize(sprite: SpriteFrame, scale: number = 1): void {
		this.areaUITransform.setContentSize(sprite.rect.width * scale, sprite.rect.height * scale);
		this.icon.spriteFrame = sprite;
		this.UpdateSize();
	}

	public GetIcon(): SpriteFrame {
		return this.icon.spriteFrame;
	}

	public GetIconSize(): Readonly<Size> {
		return this.iconUITransform.contentSize;
	}

	public SetIconColor(color: Readonly<Color>): void {
		this.icon.color = color;
	}

	public GetIconColor(): Readonly<Color> {
		return this.icon.color;
	}

	public SetGrayscale(isGrayscale: boolean): void {
		this.icon.grayscale = isGrayscale;
	}

	private UpdateSize(): void {
		let areaWidth = this.areaUITransform.contentSize.width;
		let areaHeight = this.areaUITransform.contentSize.height;
		let originalSpriteWidth = this.icon.spriteFrame.rect.width;
		let originalSpriteHeight = this.icon.spriteFrame.rect.height;

		switch(this.fillingMethod) {
			case IconFillingMethod.filling:
				this.CalcProportionalRectFillingSize(areaWidth, areaHeight, originalSpriteWidth, originalSpriteHeight);
				break;
			case IconFillingMethod.fitting:
				this.CalcProportionalRectFittingSize(areaWidth, areaHeight, originalSpriteWidth, originalSpriteHeight);
				break;
		}
	}

	private CalcProportionalRectFillingSize(fillableWidth: number, fillableHeight: number, spriteWidth: number, spriteHeight: number): void {
		let width = 0;
		let height = 0;
		
		let aspectRatio = spriteWidth / spriteHeight;
		
		width = fillableWidth;
		height = width / aspectRatio;
		
		if (height < fillableHeight) {
			height = fillableHeight;
			width = height * aspectRatio;
		}
		
		this.iconUITransform.setContentSize(width, height);
	}

	private CalcProportionalRectFittingSize(fillableWidth: number, fillableHeight: number, spriteWidth: number, spriteHeight: number): void {
		let width = 0;
		let height = 0;
		
		let aspectRatio = spriteWidth / spriteHeight;
		
		width = fillableWidth;
		height = width / aspectRatio;
		
		if (height > fillableHeight) {
			height = fillableHeight;
			width = height * aspectRatio;
		}
		
		this.iconUITransform.setContentSize(width, height);
	}
}

Important note. The object to which the IconView is attached is a prefab for me. It is located in another prefab, where I also specify it as a link. For example:

PauseWindow
|-...
|-TimerIcon
|-...

PauseWindow - a prefab that needs a reference to the IconView (I point it to the TimerIcon prefab).

TimerIcon - a prefab of IconView.

And so, sometimes, when I open the PauseWindow prefab, do something in it (but don’t touch either the TimerIcon or the link to it in PauseWindow), save and close the prefab, then when I launch the game in the browser I discover that the connection is broken.

Could you please provide the video which can show us directly how to produce the connection broken issue.

I haven’t tried to reproduce this intentionally, but I’ll try to do it on an empty project.

I created a new project, but I couldn’t repeat the error. Unfortunately, I don’t have much time to do such experiments.

However, I have new information. I have similar errors in my main project from time to time and I did notice something.

Launched Cocos Creator. I have only one scene in the project. But it doesn’t interest me, since all the things are in prefabs. Opened the game level prefab. Inside it there are a number of prefabs (basically nested prefabs). While keeping this level prefab open, I do something in the code editor at the same time. I periodically switch to Cocos Creator. In general, back and forth. Well, I periodically launch the game in the browser. And so, in one of these cases, the game in the browser starts with an error. Based on the error result, I realized that the error with the links crashing happened again. I close the browser. Cocos Creator is open, the required prefab is open. I click inside on the object of interest, and the inspector shows that all links are normal. Strange, but okay. I launch the game in the browser again and the error occurs again. I close the browser, in Cocos Creator I close this prefab (I get to my only scene) and open this prefab again. And now, I see that the links have disappeared.

Before this, only links to my own components always crashed. Yesterday I saw for the first time how the link to Node was broken.

I don’t know, I didn’t start this project, but I’m working on it. Couldn’t it have been ruined somehow?!

In general, if it helps, sometimes warnings come up. I can’t say whether it’s right when I open the prefab or already during work (for example, compiling scripts, but it seems to be after recompiling scripts when the prefab is open in Cocos Creator). But today I got this:

 [Scene] component already added: building_button<Button>
Error: [Scene] component already added: building_button<Button>
at console.warn (C:\ProgramData\cocos\editors\Creator\3.8.6\resources\app.asar\node_modules\@sentry\src\instrument\console.ts:40:20)
at warn (C:\ProgramData\cocos\editors\Creator\3.8.6\resources\resources\3d\engine\bin\.cache\dev\editor\bundled\index.js:146866:12)
at ReusableInvoker.add (C:\ProgramData\cocos\editors\Creator\3.8.6\resources\resources\3d\engine\bin\.cache\dev\editor\bundled\index.js:49015:15)
at ComponentScheduler._scheduleImmediate (C:\ProgramData\cocos\editors\Creator\3.8.6\resources\resources\3d\engine\bin\.cache\dev\editor\bundled\index.js:49280:32)
at ComponentScheduler._onEnabled (C:\ProgramData\cocos\editors\Creator\3.8.6\resources\resources\3d\engine\bin\.cache\dev\editor\bundled\index.js:49145:18)
at OneOffInvoker.compScheduler [as _invoke] (C:\ProgramData\cocos\editors\Creator\3.8.6\resources\resources\3d\engine\bin\.cache\dev\editor\bundled\index.js:49077:29)
at OneOffInvoker.invoke (C:\ProgramData\cocos\editors\Creator\3.8.6\resources\resources\3d\engine\bin\.cache\dev\editor\bundled\index.js:48996:18)
at NodeActivator.activateNode (C:\ProgramData\cocos\editors\Creator\3.8.6\resources\resources\3d\engine\bin\.cache\dev\editor\bundled\index.js:51015:29)
at Scene._activate (C:\ProgramData\cocos\editors\Creator\3.8.6\resources\resources\3d\engine\bin\.cache\dev\editor\bundled\index.js:58797:44)
at Director.runSceneImmediate (C:\ProgramData\cocos\editors\Creator\3.8.6\resources\resources\3d\engine\bin\.cache\dev\editor\bundled\index.js:19222:17)
at C:\ProgramData\cocos\editors\Creator\3.8.6\resources\app.asar\builtin\scene\dist\script\3d\manager\scene\utils.ccc:1:1367
at eval (C:\ProgramData\cocos\editors\Creator\3.8.6\resources\resources\3d\engine\bin\.cache\dev\editor\bundled\index.js:109875:17)
at eval (C:\ProgramData\cocos\editors\Creator\3.8.6\resources\resources\3d\engine\bin\.cache\dev\editor\bundled\index.js:117467:9)
at eval (C:\ProgramData\cocos\editors\Creator\3.8.6\resources\resources\3d\engine\bin\.cache\dev\editor\bundled\index.js:151169:9)
at sentryWrapped (C:\ProgramData\cocos\editors\Creator\3.8.6\resources\app.asar\node_modules\src\helpers.ts:100:17)

And indeed, in the level prefab I have a prefab that has a building_button node, which has a Button component. Moreover, this user is marked in the hierarchy as “+”, since it was not added to the original prefab, but extends the base prefab (well, you never know, maybe it’s important).