#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. Я бы предпочел избежать преждевременной оптимизации и использовать встроенный профилировщик для устранения любых проблем с производительностью. Делайте то, что вы считаете наиболее эффективным с точки зрения рабочего процесса и рабочей нагрузки, так как рисовать новый спрайт для каждого предмета одежды для каждого анимационного кадра и так непросто. Многие игры просто меняют всю таблицу спрайтов персонажей, что лишает их возможности менять отдельные предметы одежды, но требует гораздо меньше работы.