netcode, networkAnimations and sprite masks

i have a code to create superlasers that use masking - it works in regular setting but when i try going netcode i have few problems

i will present my code(not all of it becuse it inherits from other scripts) but my main quesion is how to us the masking of sprites when i use networkanimator sinchornizer for the sprites and not directly on sprite renderder

this is how the end result shpuld look like -

and here is my code - for now the real problem is with my main gameobject (that is parenting chunks) that need to sync its childrens with masking

any tips will help - thanx

#region Script Synopsis
//A more dynamic laser type shot which uses sprite masking along with multiple animated sections
//Learn more about laser type shots at: VariaBULLET2D Unity Tutorial - In Depth Shot Guide
#endregion
using Unity.Netcode.Components;
using UnityEngine;
using Unity.Netcode;
using Unity.Netcode.Transports;
using UnityEngine.Assertions;
using Unity.BossRoom.Infrastructure;
public class MyShotLaserMask : ShotLaserBase
{
[Header(“LaserMask Settings”)]
[Tooltip(“The CHUNK used as GAMEOBJECT.”)]
public GameObject chunkPrefab;
[Tooltip(“The sprite used as mask.”)]
public Sprite MaskingSprite;
[Tooltip(“When two or more masked lasers interact, set this to a different value on each to de-conflict.”)]
public int UniqueMaskId;
[Tooltip(“Increments the start animation frame in each successive main chunk, for progressive seamless animations.”)]
public bool StaggerAnimations;
// private BasicAnimation[ ] mainAnims;
private GameObject mainMask;
private GameObject tipMask;
[Tooltip(“Adjusts the starting point for the main section.”)]
public float MainAdjust;
[Tooltip(“Adjusts main masking scale on Y-axis”)]
public float MainMaskYScale = 1;
[Tooltip(“Adjusts the starting point for the tip section.”)]
public float TipAdjust;
[Tooltip(“Adjusts tip masking scale on Y-axis”)]
public float TipMaskYScale = 1;
[Tooltip(“Creates overlap or gaps between mid section chunks [must be set before play mode].”)]
public float ChunkAdjust;
public override void InitialSet()
{
base.InitialSet();
// FiringScript.OnStoppedFiring.AddListener(destroy_laser);
}

protected override void animate()
{
if (!IsServer)
return;
base.animate();
originGo.GetComponent().Play(“Origin”);
mainGo.GetComponentInChildren().Play(“Main”);
// This assumes mainGo is already assigned and refers to the parent GameObject
foreach (Transform child in mainGo.transform)
{
Animator animator = child.GetComponent();
if (animator != null) // If the child has an Animator component
{
animator.Play(“Main”);
}
}
tipGo.GetComponent().Play(“Tip”);
// foreach (BasicAnimation anim in mainAnims)
// anim.Animate(FrameSkip);
}
protected override void movement()
{
if (!IsServer)
return;
float speed = scaledSpeed.Value * ppu;
mainGo.transform.localPosition = new Vector2(MainAdjust * globalDirection, mainGo.transform.localPosition.y);
RaycastHit2D ray = boxCast();
Utilities.DebugDrawLine(gameObject.transform.position, ray.point); //visualizes raycast in scene view for debug purposes
if (ray.collider == null)
laserExtension(speed);
else
laserCollision(ray, speed);
tipGo.transform.localPosition = new Vector2((mainMask.transform.localScale.x / ppu * globalDirection) + ((MainAdjust + TipAdjust) * globalDirection), 0);
tipMask.transform.localPosition = new Vector2(Mathf.Abs(TipAdjust), 0);
if (collided)
sendLaserCollData(ray);
}
private void laserExtension(float speed)
{
float laserExtension = Mathf.MoveTowards(mainMask.transform.localScale.x, maxDistance * globalDirection, Time.deltaTime * speed);
mainMask.transform.localScale = new Vector3(laserExtension, mainMask.transform.localScale.y, 1);

tipGo.GetComponent().Play(“Tip”);
// tipAnim.Animate(FrameSkip);
}
private void laserCollision(RaycastHit2D ray, float speed)
{
//more accurate than ray.distance. Fixes discrepency between when fired on horizontal plane vs vertical plane when rotated
float distance = Vector2.Distance(mainGo.transform.position, ray.point);
float blastArea = BlastImg[0].bounds.size.x * ppu * CollisionTipZone;
if (mainMask.transform.localScale.x >= (distance * ppu) - blastArea)
{
mainMask.transform.localScale = new Vector3((distance * ppu) - (blastArea), mainMask.transform.localScale.y, 1);
// blastAnim.Animate(FrameSkip);
tipGo.GetComponent().Play(“Blast”);
collided = true;
}
else
{
float laserExtension = Mathf.MoveTowards(mainMask.transform.localScale.x, (distance * ppu) - blastArea, Time.deltaTime * speed);
mainMask.transform.localScale = new Vector3(laserExtension, mainMask.transform.localScale.y, 1);
float bufferZone = 0.002f;
if (laserExtension >= (distance * ppu) - blastArea - bufferZone)
{
tipGo.GetComponent().Play(“Blast”);
// blastAnim.Animate(FrameSkip);
collided = true; }
else
tipGo.GetComponent().Play(“Tip”);
//tipAnim.Animate(FrameSkip);
}
}
public override void OnNetworkSpawn()
{
// Now, the object has been taken out of the pool and spawned into the network.
// This is a good place to initialize or reset specific components.
if (!IsServer)
return;
print(“1 OnNetworkSpawn " + this);
base.OnNetworkSpawn();
if (!m_ObjectPool)
{
m_ObjectPool = GameObject.FindWithTag(s_ObjectPoolTag).GetComponent();
Assert.IsNotNull(m_ObjectPool, $”{nameof(NetworkObjectPool)} not found in scene. Did you apply the {s_ObjectPoolTag} to the GameObject?");
}
globalDirection = (PositioningEmitterScale.Value.x < 0) ? -1 : 1;
print("2 OnNetworkSpawn " + this);
UniqueMaskId = UniqueMaskId * 100;
print("3 OnNetworkSpawn " + chunkPrefab);
print("4 OnNetworkSpawn " + m_ObjectPool);
originGo = m_ObjectPool.GetNetworkObject(
chunkPrefab, transform.position, Quaternion.identity).gameObject;
originGo.name = “Origin”;
mainGo = m_ObjectPool.GetNetworkObject(
chunkPrefab, transform.position, Quaternion.identity).gameObject;
mainGo.name = “Main”;
mainGo.GetComponent().enabled = false;
mainGo.GetComponent().enabled = false;
mainGo.GetComponent().enabled = false;
tipGo = m_ObjectPool.GetNetworkObject(
chunkPrefab, transform.position, Quaternion.identity).gameObject;
tipGo.name = “Tip”;
tipGo.GetComponent().enabled = true;
originGo = initLaserEnds(originGo, new Vector2(0, 0), OriginImg, sortOrder + 1, false);
mainGo = initLaserMain(mainGo, new Vector2(MainAdjust * globalDirection, 0), MainImg, sortOrder);
tipGo = initLaserEnds(tipGo, new Vector2((MainAdjust + TipAdjust) * globalDirection, 0), TipImg, sortOrder + 2, true);
originGo.GetComponent().Spawn();
originGo.GetComponent().TrySetParent(this.transform);
tipGo.GetComponent().Spawn();
tipGo.GetComponent().TrySetParent(this.transform);
mainGo.GetComponent().Spawn();
mainGo.GetComponent().TrySetParent(this.transform);
originAnim = originGo.GetComponent();
mainAnim = originGo.GetComponent();
tipAnim = originGo.GetComponent();
//blastAnim = initAnimations(tipGo, BlastImg);
// tipAnim = initAnimations(tipGo, TipImg);
}

private GameObject initLaserEnds(GameObject part, Vector3 initPosition, Sprite[ ] sprite, int sort, bool isTip)
{
// part.transform.parent = this.transform;
// part.GetComponent().TrySetParent(this.transform);
part.transform.localPosition = initPosition;
print("Global direction: " + globalDirection);
print("Initial part x scale: " + part.transform.localScale.x);
part.transform.localScale = new Vector3(part.transform.localScale.x * globalDirection, part.transform.localScale.y);
print(“1 aaaaaaaaaa scale” + part.transform.localScale );
print(“2 aaaaaaaaaa scale” + part.transform.lossyScale );
part.transform.rotation = new Quaternion();
SpriteRenderer partSprite = part.AddComponent();
partSprite.sprite = sprite[0];
partSprite.sortingOrder = sort + UniqueMaskId;
partSprite.sortingLayerName = sortLayer;
partSprite.color = FiringScript.SpriteColor;
if (isTip)
tipMask = initMask(part, “TipMask”, new Vector2(Mathf.Abs(TipAdjust), 0), new Vector2(BlastImg[0].bounds.size.x * ppu, TipMaskYScale), sortOrder + 3, sortOrder);
else
partSprite.maskInteraction = SpriteMaskInteraction.VisibleOutsideMask;
return part;
}
private GameObject initLaserMain(GameObject main, Vector3 initPosition, Sprite[ ] sprite, int sort)
{
// main.transform.parent = this.transform;
main.transform.localPosition = initPosition;
main.transform.localScale = new Vector3(main.transform.localScale.x * globalDirection, main.transform.localScale.y);
main.transform.rotation = new Quaternion();
float chunkSize = MainImg[0].bounds.size.x + ChunkAdjust;
int chunksNeeded = (int)((CalcObject.getMaxScreenDistance() + AddedMaxDistance) / chunkSize);
// mainAnims = new BasicAnimation[chunksNeeded];
int startFrame = 0;
for (int i = 0; i < chunksNeeded; i++)
{
// GameObject chunk = new GameObject(“Chunk” + i);
GameObject p = Resources.Load(“ND_VariaBullet/EmitterPrefabs/Chunks/” + “Chunk”);
GameObject chunk = Instantiate(p);
// GameObject chunk = m_ObjectPool.GetNetworkObject(
// chunkPrefab, transform.position, Quaternion.identity).gameObject;
chunk.name = “Chunk” + i;
// chunk.GetComponent().Spawn();
chunk.transform.parent = main.transform;
// chunk.GetComponent().TrySetParent(main.transform);
chunk.transform.localPosition = new Vector2((sprite[0].bounds.size.x + ChunkAdjust) * i, 0);
chunk.transform.localRotation = new Quaternion();
chunk.transform.localScale = new Vector2(1, 1);
SpriteRenderer chunkSprite = chunk.AddComponent();
chunkSprite.sprite = sprite[startFrame];
chunkSprite.sortingOrder = sort + UniqueMaskId;
chunkSprite.sortingLayerName = sortLayer;
chunkSprite.color = FiringScript.SpriteColor;
chunkSprite.maskInteraction = SpriteMaskInteraction.VisibleInsideMask;
// mainAnims = new BasicAnimation(ref chunkSprite, ref MainImg, startFrame);
startFrame++;
if (!StaggerAnimations || startFrame > sprite.Length - 1)
startFrame = 0;
}
maxDistance = chunksNeeded * chunkSize * ppu * globalDirection;
mainMask = initMask(main, “MainMask”, new Vector2(0, 0), new Vector2(1, MainMaskYScale), sortOrder, sortOrder - 1);
return main;
}
private GameObject initMask(GameObject parentGo, string maskName, Vector2 pos, Vector2 scale, int frontSortOrder, int backSortOrder)
{

// GameObject mainMask = new GameObject(maskName);
GameObject p = Resources.Load(“ND_VariaBullet/EmitterPrefabs/Chunks/” + “Chunk”);
mainMask = Instantiate(p);
print(“111111111111111111111”);
// GameObject chunk = m_ObjectPool.GetNetworkObject(
// chunkPrefab, transform.position, Quaternion.identity).gameObject;

// mainMask = m_ObjectPool.GetNetworkObject(
// chunkPrefab, transform.position, Quaternion.identity).gameObject;
mainMask.name = maskName;
SpriteMask spriteMask = mainMask.AddComponent();
//mainMask.GetComponent().Spawn();
// mainMask.GetComponent().TrySetParent(parentGo.transform);
mainMask.transform.parent = parentGo.transform;
mainMask.transform.localPosition = pos;
mainMask.transform.localScale = scale;
mainMask.transform.localRotation = new Quaternion();
spriteMask.isCustomRangeActive = true;
spriteMask.frontSortingLayerID = SortingLayer.NameToID(sortLayer);
spriteMask.frontSortingOrder = frontSortOrder + UniqueMaskId;
spriteMask.backSortingLayerID = SortingLayer.NameToID(sortLayer);
spriteMask.backSortingOrder = backSortOrder + UniqueMaskId;
spriteMask.sprite = MaskingSprite;
return mainMask;
}
}

1 Like

same problem, mask works on not multiplayer state and do not work with netcode