【问题标题】:How to update sprite position faster?如何更快地更新精灵位置?
【发布时间】:2021-09-28 10:42:24
【问题描述】:

对于我正在尝试做的游戏,我有很多对象:

一切都从特定的精灵开始,比如英雄:

除了 Hero 是唯一一个拥有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 && 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;
  }
}

当作为组件添加时,需要名称、索引以及 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文件夹中的几个文件夹加载的:

这些 spritesheet 的大小都相同,因此可以干净利落地切割:

因此,有了这样的精灵,我可以简单地调用基于英雄的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 && 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;
  }

这样可行,其他精灵会根据英雄游戏对象的索引更新并跟随它,所以看起来用户有很多装备。但是,有时精灵要么跟不上,要么位置有点落后:

精灵顶部似乎还有一些黑线。我认为这是因为精灵加载速度不够快,如果是这样,有没有办法确保这些精灵加载得更快?还是这与计算机的性能有关?

另外,为了让精灵跟随,我需要在其他游戏对象的 RigidBody2D 上冻结 X 和 Y 位置。 Hero 有 Transform、Sprite Renderer、Box Collider 2D、RigidBody2D、两个脚本(HeroMovement 和 HeroResources)和 Animator 组件,而其他游戏对象只有 Transform、Sprite Renderer、RigidBody2D 和一个脚本(SpritePosition)

【问题讨论】:

    标签: unity3d


    【解决方案1】:

    您可以让 Transform 组件完成繁重的工作,而不是自己定位衣服,只需将布料 GameObjects 放置在具有正确偏移的英雄 GameObject 下方。

    这样你也可以拥有 HeroAnimator 组件,它在启动时使用 GetComponentsInChildren 获取英雄下的所有服装组件,并更改精灵并翻转它们以匹配运动方向。服装组件可以集中

    因为精灵加载速度不够快

    您正在使用 HeroResources.Awake 加载所有精灵资源,因此它们应该在启动期间全部加载到内存中。您在场景中引用的所有精灵也是如此。

    update 方法 中的此类字符串操作可能会为垃圾收集器生成数量惊人的垃圾,从而影响性能。

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

    值得注意的是,实现这样的事情可能会变得非常棘手。许多可以让你换装的 2D 游戏都使用skeletal animation 或者只有非常有限的一组极简动画。

    【讨论】:

    • 我将这一行更改为 int.Parse(currentSpriteName.Substring(currentSpriteName.LastIndexOf("_") + 1)); 以在 Hero 脚本中执行,这样可以减少一些垃圾。我尝试将其他游戏对象作为 Hero 的子对象,但这会删除缩放功能。此外,当英雄移动/跳跃时,它会改变速度(x 或 y)。我应该修改其他游戏对象的速度以及位置吗?我注意到body.velocity = heroRigidBody.velocity; 似乎不起作用
    • 如果你将布料对象放在英雄内部,你根本不需要移动它们,除非它们在动画期间大小不同,在这种情况下你需要配置枢轴(中心点)每个布料动画精灵。只有在英雄蹲伏或其他情况下,您可能需要抵消它们。在这种情况下只有英雄应该有刚体,想不出任何正当理由让服装对象有刚体。如果英雄具有统一的比例,缩放应该没问题,因为不统一的比例会产生过多的问题。
    • 对于这样的事情来说,良好的灵活层次结构是拥有具有运动脚本和刚体的父 Player/Actor GameObject。然后作为孩子,您可以拥有 3 个游戏对象,一个用于角色/英雄精灵,一个用于对撞机,一个用于服装,其中所有服装游戏对象都作为孩子。
    • 如果我要创建一个仅基于头部和手的基本 spritesheet,并为腿和手臂创建一个单独的 spritesheet,你是​​否认为对内存的要求会更高,这样当加载有没有重叠的风险?
    • 我会避免过早优化并使用the build in profiler 来解决任何性能问题。就工作流程和工作量而言,做你认为最有效的事情,因为必须为每个动画帧的每件衣服绘制新的精灵,这本身就是一项艰巨的任务。许多游戏只是换掉了整个角色精灵表,这消除了更换单个服装的能力,但工作量要少得多。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多