Как быстрее обновить положение спрайта?

#unity3d

Вопрос:

Для игры, которую я пытаюсь сделать, у меня есть довольно много объектов:

введите описание изображения здесь

Где все начинается с определенного спрайта, такого как Герой:

введите описание изображения здесь

За исключением того, что Герой-единственный, у которого есть Animator компонент и который следует состояниям анимации.

Другие игровые объекты получат свой спрайт и положение в зависимости от Героя, используя следующий сценарий:

 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 amp;amp; index <= 34 ||
            index >= 69 amp;amp; index <= 71 ||
            index >= 81 amp;amp; index <= 83 ||
            index >= 97 amp;amp; index <= 99 ||
            index >= 117 amp;amp; index <= 118 ||
            index >= 133 amp;amp; 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 amp;amp; 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;
  }
}
 

Который при добавлении в качестве компонента ожидает имя, индекс и значения R, G и B:

введите описание изображения здесь

Здесь Name необходимо загрузить определенные спрайты для каждого игрового объекта, в этом скрипте:

 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);
  }
}
 

и спрайты загружаются из нескольких папок в Resources папке:

введите описание изображения здесь

Все эти таблицы спрайтов имеют одинаковый размер, поэтому их можно аккуратно нарезать:

введите описание изображения здесьвведите описание изображения здесьвведите описание изображения здесь

Таким образом, имея такие спрайты, я могу просто вызвать SetSprite функцию и SetPosition функцию, основанную на Герое:

 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 amp;amp; 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;
  }
 

Это работает, и другие спрайты обновляются на основе индекса игрового объекта героя и следуют за ним, так что похоже, что у пользователя много оборудования. Однако иногда спрайты либо не успевают за ходом, либо позиция немного отстает:

введите описание изображения здесь

Также кажется, что в верхней части спрайтов есть несколько черных линий. Я предполагаю, что это происходит потому, что спрайты загружаются недостаточно быстро, и если да, то есть ли способ обеспечить более быструю загрузку этих спрайтов? Или это связано с производительностью компьютера?

Кроме того, чтобы спрайты следовали за мной, мне нужно заморозить положение X и Y на Rigidbody2D других игровых объектов. У героя есть Transform, средство визуализации спрайтов, Box Collider 2D, Rigidbody2D, два сценария (HeroMovement и HeroResources) и компоненты Аниматора, в то время как у других игровых объектов есть только Transform, средство визуализации спрайтов, Rigidbody2D и сценарий (расположение спрайтов)

Ответ №1:

Вместо того, чтобы самостоятельно размещать одежду, вы могли бы позволить компоненту Transform выполнять тяжелую работу, просто поместив игровые объекты ткани под игровым объектом героя с правильными смещениями.

Таким образом, у вас также может быть компонент HeroAnimator, который при запуске получает все компоненты одежды под героя с помощью GetComponentsInChildren, изменяет спрайты и переворачивает их в соответствии с направлением движения. Компоненты одежды могут фокусироваться

потому что спрайты загружаются недостаточно быстро

Вы загружаете все ресурсы спрайта с помощью HeroResources.Просыпайтесь, чтобы все они были загружены в память во время запуска. То же самое относится ко всем спрайтам, на которые вы ссылаетесь в своей сцене.

Строковые операции, подобные этому в методе обновления, могут генерировать удивительное количество мусора для сборщика мусора, что может повлиять на производительность в дальнейшем.

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

Приятно отметить, что реализация чего-то подобного может стать действительно сложной. Многие 2D-игры, которые позволяют вам сменить одежду, используют скелетную анимацию или имеют очень ограниченный набор минимальных анимаций.

Комментарии:

1. Я изменил строку на эту int.Parse(currentSpriteName.Substring(currentSpriteName.LastIndexOf("_") 1)); для выполнения внутри сценария героя, чтобы уменьшить количество мусора. Я попытался поместить другие игровые объекты в качестве детей Героя, но это устраняет возможность масштабирования. Кроме того, когда Герой движется/прыгает, он меняет скорость (x или y). Должен ли я изменять скорости другого игрового объекта вместе с положением? Я заметил body.velocity = heroRigidBody.velocity; , что это, похоже, не работает

2. Если вы создаете объекты ткани внутри героя, вам вообще не нужно будет их перемещать, если только они не отличаются по размеру во время анимации, в этом случае вам нужно будет настроить ось (центральную точку) каждого анимационного спрайта ткани. Единственный случай, когда вам может потребоваться их компенсировать, будет в тех случаях, когда герой присел или что-то в этом роде. Только у героя в этом случае должно быть жесткое тело, и он не может придумать никаких веских причин для того, чтобы предметы одежды имели ридигбоди. Масштабирование должно быть в порядке, если у героя есть единый масштаб, так как неравномерный масштаб может привести к множеству проблем.

3. Хорошая гибкая иерархия для чего-то подобного-иметь родительский игровой объект игрока/актера, который имеет сценарий движения и жесткое тело. Тогда, будучи детьми, вы могли бы иметь 3 игровых объекта: один для персонажа/героя-спрайта, один для коллайдера и один для одежды, в которой есть все игровые объекты одежды, как у детей.

4. Как вы думаете, было бы намного более требовательно к памяти, если бы я создал базовый лист спрайта, основанный только на голове и руках, и отдельный лист спрайта для ног и рук, чтобы при загрузке не возникало риска перекрытия?

5. Я бы предпочел избежать преждевременной оптимизации и использовать встроенный профилировщик для устранения любых проблем с производительностью. Делайте то, что вы считаете наиболее эффективным с точки зрения рабочего процесса и рабочей нагрузки, так как рисовать новый спрайт для каждого предмета одежды для каждого анимационного кадра и так непросто. Многие игры просто меняют всю таблицу спрайтов персонажей, что лишает их возможности менять отдельные предметы одежды, но требует гораздо меньше работы.