gfcf14
gfcf14

Reputation: 350

How to update sprite position faster?

For the game I'm trying to do, I have quite a few objects:

enter image description here

Where all start with a specific sprite, like the Hero:

enter image description here

Except that the Hero is the only one which has an Animator component and follows animation states.

The other game objects would get their sprite and position based on the Hero, using the following script:

using UnityEngine;

public class SpritePosition : MonoBehaviour {
  [SerializeField] private string objectName;
  [SerializeField] private int objectIndex;
  [SerializeField] private int objectR;
  [SerializeField] private int objectG;
  [SerializeField] private int objectB;
  private Rigidbody2D body;
  private SpriteRenderer objectRenderer;
  private GameObject hero;
  private Rigidbody2D heroRigidBody;
  private SpriteRenderer heroRenderer;
  private Sprite currentHeroSprite;
  private HeroResources heroResourcesScript;
  private HeroMovement heroMovementScript;
  private Sprite[] spriteGroup;

  private void Start() {
    body = GetComponent<Rigidbody2D>();
    objectRenderer = GetComponent<SpriteRenderer>();

    hero = GameObject.Find("Hero");
    heroRigidBody = hero.GetComponent<Rigidbody2D>();
    heroRenderer = hero.GetComponent<SpriteRenderer>();
    currentHeroSprite = heroRenderer.sprite;

    heroResourcesScript = hero.GetComponent<HeroResources>();
    Debug.Log(objectName + "(" + objectR + ", " + objectG + ", " + objectB + ")");
    spriteGroup = heroResourcesScript.spriteGroup[objectName];
  }

  private void Update() {
    heroMovementScript = hero.GetComponent<HeroMovement>();

    if (currentHeroSprite != heroRenderer.sprite) {
      currentHeroSprite = heroRenderer.sprite;
    }

    SetSprite();
    SetPosition();
  }

  private bool shouldMirrorSprite(int index) { 
    return index >= 0 && index <= 34 ||
            index >= 69 && index <= 71 ||
            index >= 81 && index <= 83 ||
            index >= 97 && index <= 99 ||
            index >= 117 && index <= 118 ||
            index >= 133 && index <= 175;
  }
  private void SetSprite() {
    int currentSpriteIndex = int.Parse(currentHeroSprite.name.Replace("hero-body_", ""));
    objectRenderer.sprite = spriteGroup[currentSpriteIndex];

    objectRenderer.color = new Color32((byte)objectR, (byte)objectG, (byte)objectB, 255);

    if (heroMovementScript.isFacingLeft && shouldMirrorSprite(currentSpriteIndex)) {
      transform.localScale = new Vector3(-1, 1, 1);
    } else {
      transform.localScale = Vector3.one;
    }
  }

  // for this to work, the game object must have a
  // RigidBody2D component with Freeze Position active
  // for X and Y axis
  private void SetPosition() {
    Vector2 currentHeroPosition = heroRigidBody.position;
    transform.position = currentHeroPosition;
  }
}

Which, when added as a component, expects a name, index, and R, G, and B values:

enter image description here

Here, the Name is necessary to load specific sprites for each game object, in this script:

using System.Collections.Generic;
using UnityEngine;

public class HeroResources : MonoBehaviour
{
  public Dictionary<string, Sprite[]> spriteGroup = new Dictionary<string, Sprite[]>();
  void Awake () {
    spriteGroup.Add("pants", Resources.LoadAll<Sprite>("Spritesheets/pants"));
    spriteGroup.Add("boots", Resources.LoadAll<Sprite>("Spritesheets/boots"));
    spriteGroup.Add("shirt", Resources.LoadAll<Sprite>("Spritesheets/shirt"));
    spriteGroup.Add("tunic", Resources.LoadAll<Sprite>("Spritesheets/tunic"));
    spriteGroup.Add("belt", Resources.LoadAll<Sprite>("Spritesheets/belt"));

    Debug.Log(spriteGroup.Count);
  }
}

and the sprites are loaded from several folders in the Resources folder:

enter image description here

These spritesheets are all of the same size, so they can be cleanly sliced:

enter image description hereenter image description hereenter image description here

Thus, having the sprites like this, I can simply call the SetSprite function and the SetPosition function based on the Hero:

private void SetSprite() {
    int currentSpriteIndex = int.Parse(currentHeroSprite.name.Replace("hero-body_", ""));
    objectRenderer.sprite = spriteGroup[currentSpriteIndex];

    objectRenderer.color = new Color32((byte)objectR, (byte)objectG, (byte)objectB, 255);

    if (heroMovementScript.isFacingLeft && shouldMirrorSprite(currentSpriteIndex)) {
      transform.localScale = new Vector3(-1, 1, 1);
    } else {
      transform.localScale = Vector3.one;
    }
  }
// for this to work, the game object must have a
  // RigidBody2D component with Freeze Position active
  // for X and Y axis
  private void SetPosition() {
    Vector2 currentHeroPosition = heroRigidBody.position;
    transform.position = currentHeroPosition;
  }

This works, and the other sprites update based on the index of the Hero game object and follow it, so it looks like the user has a lot of equipment. However, sometimes the sprites either fail to keep up or the position falls behind a bit:

enter image description here

There also seems to be some black lines toward the top of the sprites. I assume this happens because the sprites do not load fast enough, and if so, is there a way to ensure these sprites load faster? Or does this have to do with a computer's performance?

Also, to have the sprites follow, I need to freeze X and Y position on the other game objects' RigidBody2D. The Hero has Transform, Sprite Renderer, Box Collider 2D, RigidBody2D, two scripts (HeroMovement and HeroResources), and Animator components, while the other game objects only have Transform, Sprite Renderer, RigidBody2D, and a script (SpritePosition)

Upvotes: 0

Views: 294

Answers (1)

Pasi &#214;sterman
Pasi &#214;sterman

Reputation: 2197

Instead of positioning the clothes yourself you could let Transform component do the heavy lifting by just placing the cloth GameObjects under the hero GameObject with correct offsets.

This way you could also have HeroAnimator component that gets all the clothing components under the hero with GetComponentsInChildren at startup and changes the sprites and flip them to match movement direction. Clothing components could focus

because the sprites do not load fast enough

You're loading all the sprite resources with HeroResources.Awake so they should all be loaded to memory during startup. Same goes for all sprites you're referencing in your scene.

String operations like this in update method can generate surprising amount of garbage for the garbage collector which can affect performance down the line.

int currentSpriteIndex = int.Parse(currentHeroSprite.name.Replace("hero-body_", ""));

Its good to note that implementing something like this can get really tricky. Many 2D games that let you change outfit use skeletal animation or have very limited set of minimal animations.

Upvotes: 0

Related Questions