Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

I have an object structure like this:

enter image description here

Which I have to create a player with several different equipment. For this, I am using a custom animation controller which changes an index that would determine sprites out of a few spritesheets:

AnimationController.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class AnimationController : MonoBehaviour {
  private float waitTime = 0.06f;
  private float timer = 0.0f;

  private bool grounded;
  private bool falling;
  private bool jumping;
  private bool running;
  private bool horizontalCollision;

  private HeroMovement heroMovementScript;
  private GameObject hero;

  private int[] runningSprites = { 2, 3, 4, 5, 6, 7, 8, 13, 14, 15, 16, 17, 18, 19 };
  private int animationIndex = 0;

  public int currentHeroSpriteIndex;

  // Start is called before the first frame update
  void Start() {
    hero = GameObject.Find("Hero");
    heroMovementScript = hero.GetComponent<HeroMovement>();
  }

  // Update is called based on timer and waitTime
  void Update() {
    timer += Time.deltaTime;

    if (timer > waitTime) {
      grounded = heroMovementScript.isGrounded;
      falling = heroMovementScript.isFalling;
      jumping = heroMovementScript.isJumping;
      running = heroMovementScript.isRunning;
      horizontalCollision = heroMovementScript.horizontalCollision;

      if (running) {
        currentHeroSpriteIndex = runningSprites[animationIndex % 14];
        animationIndex++;
      } else {
        currentHeroSpriteIndex = 0;
        animationIndex = 0;
      }

      timer = timer - waitTime;
    }
  }
}

So here I run the update function a set wait time, and based on variables from a HeroMovement script do I update the booleans to determine how to update the index based on declared arrays:

HeroMovement.cs

using UnityEngine;

public class HeroMovement : MonoBehaviour {
  [SerializeField] private float speed;
  [SerializeField] private float jumpHeight;
  private Rigidbody2D body;
  private Animator anim;
  private AnimationController animationControllerScript;
  private int currentHeroSpriteIndex;
  private SpriteRenderer heroRenderer;
  private HeroResources heroResourcesScript;
  private Sprite[] heroSprites;
  public bool isGrounded;
  public bool isFalling;
  public bool isJumping;
  public bool isFacingLeft;
  public bool isRunning;

  public bool horizontalCollision;

  public int collisionCounter = 0;

  // called when script is loaded
  private void Awake() {
    body = GetComponent<Rigidbody2D>();
    anim = GetComponent<Animator>();
    animationControllerScript = GetComponent<AnimationController>();
    heroRenderer = GetComponent<SpriteRenderer>();
    heroResourcesScript = GetComponent<HeroResources>();

    currentHeroSpriteIndex = animationControllerScript.currentHeroSpriteIndex;
    heroSprites = heroResourcesScript.heroSprites;
  }

  // called on every frame of the game
  private void Update() {
    float horizontalInput = Input.GetAxis("Horizontal");
    float verticalSpeed = body.velocity.y;

    // x axis movement
    if (!horizontalCollision) {
      body.velocity = new Vector2(horizontalInput * speed, body.velocity.y);

      // flip player when moving left
      if (horizontalInput > 0.01f && isGrounded) {
        transform.localScale = Vector3.one;
        isFacingLeft = false;
      }
      // flip player when moving right
      else if (horizontalInput < -0.01f && isGrounded) {
        transform.localScale = new Vector3(-1, 1, 1);
        isFacingLeft = true;
      }
    }

    // jumping
    if (Input.GetKey(KeyCode.Space) && isGrounded) {
      Jump();
    }

    isRunning = horizontalInput != 0 && !isJumping && !isFalling;

    // set animator parameters
    // anim.SetBool("isRunning", horizontalInput != 0 && !isJumping && !isFalling);
    // anim.SetBool("isGrounded", isGrounded);
    // anim.SetBool("isFalling", isFalling);
    // anim.SetBool("isJumping", isJumping);
    // anim.SetBool("horizontalCollision", horizontalCollision);

    if (!isGrounded && verticalSpeed < -1) {
      Fall();
    }
  }

  private void Fall() {
    isFalling = true;
  }

  private void Jump() {
    body.velocity = new Vector2(body.velocity.x, jumpHeight);
    isJumping = true;
    isGrounded = false;
  }

  private void OnCollisionEnter2D(Collision2D collision) {
    Collider2D collider = collision.collider;
    Collider2D otherCollider = collision.otherCollider;

    if (collision.gameObject.tag == "Ground") {
      if (otherCollider.tag == "Hero") {
        if (!isHorizontalCollision(otherCollider, collider)) {
          isGrounded = true;
          isFalling = false;
          isJumping = false;
          horizontalCollision = false;
        } else {          
          horizontalCollision = true;

          if (isBottomCollision(otherCollider, collider)) {
            horizontalCollision = false;
          }
        }
      }      
    }

    collisionCounter++;
  }

  private bool isBottomCollision(Collider2D collider1, Collider2D collider2) {
    int c1BottomEdge = (int) collider1.bounds.max.y;
    int c2TopEdge = (int) collider2.bounds.min.y;

    return c1BottomEdge == c2TopEdge;
  }

  private bool isHorizontalCollision(Collider2D collider1, Collider2D collider2) {
    int c1RightEdge = (int) collider1.bounds.max.x;
    int c1LeftEdge = (int) collider1.bounds.min.x;

    int c2RightEdge = (int) collider2.bounds.max.x;
    int c2LeftEdge = (int) collider2.bounds.min.x;

    return (c1RightEdge == c2LeftEdge) || (c1LeftEdge == c2RightEdge);
  }

  private void OnCollisionExit2D(Collision2D collision) {
    collisionCounter--;

    if (collisionCounter == 0) {
      isGrounded = false;
    }
  }

  // private bool isGrounded() {
  //   RaycastHit2D raycastHit = Physics2D.BoxCast(boxCollider.bounds.center, boxCollider.bounds.size, 0, Vector2.down, 0.1f, groundLayer);
  //   return raycastHit.collider != null;
  // }
}

Each of the child game objects also make use of the currentHeroSpriteIndex to update their sprites, their color based on serialized fields, and their position based on the parent's:

SpritePosition.cs

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 AnimationController animationControllerScript;
  private int currentHeroSpriteIndex;
  private HeroResources heroResourcesScript;
  private Sprite[] spriteGroup;

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

    hero = GameObject.Find("Hero");
    heroRigidBody = hero.GetComponent<Rigidbody2D>();
    animationControllerScript = hero.GetComponent<AnimationController>();
    currentHeroSpriteIndex = animationControllerScript.currentHeroSpriteIndex;

    heroResourcesScript = hero.GetComponent<HeroResources>();
    spriteGroup = heroResourcesScript.spriteGroup[objectName][objectIndex];

    float floatR = objectR / 255f;
    float floatG = objectG / 255f;
    float floatB = objectB / 255f;

    if (objectName != "body") {
      objectRenderer.color = new Color(floatR, floatG, floatB, 1);
    }
  }

  private void Update() {
    SetSprite();
    SetPosition();
  }
  
  private void SetSprite() {
    if (currentHeroSpriteIndex != animationControllerScript.currentHeroSpriteIndex) {
      currentHeroSpriteIndex = animationControllerScript.currentHeroSpriteIndex;
      objectRenderer.sprite = spriteGroup[currentHeroSpriteIndex];
    }

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

And for this, I use another script, HeroResources which loads all of the sprites on Dictionaries:

HeroResources.cs

using System.Collections.Generic;
using UnityEngine;

public class HeroResources : MonoBehaviour {
  const int PANTS_LIMIT = 1;
  const int BOOTS_LIMIT = 1;
  const int SHIRT_LIMIT = 1;
  const int TUNIC_LIMIT = 2;
  const int BELT_LIMIT = 1;

  public Dictionary<string, Dictionary<int, Sprite[]>> spriteGroup = new Dictionary<string, Dictionary<int, Sprite[]>>();
  public Sprite[] heroSprites = new Sprite[180];

  public Dictionary<int, Sprite[]> getAllSprites(string name, int limit) {
    Dictionary<int, Sprite[]> spriteList = new Dictionary<int, Sprite[]>();

    if (name == "hero") {
      spriteList.Add(0, Resources.LoadAll<Sprite>("Spritesheets/hero/hero-body"));
    } else {
      for (int i = 0; i < limit; i++) {
        spriteList.Add(i, Resources.LoadAll<Sprite>("Spritesheets/" + name + "/" + (i + 1)));
      }
    }

    return spriteList;
  }

  void Awake() {
    spriteGroup.Add("body", getAllSprites("hero", 1));
    spriteGroup.Add("pants", getAllSprites("pants", PANTS_LIMIT));
    spriteGroup.Add("boots", getAllSprites("boots", BOOTS_LIMIT));
    spriteGroup.Add("shirt", getAllSprites("shirt", SHIRT_LIMIT));
    spriteGroup.Add("tunic", getAllSprites("tunic", TUNIC_LIMIT));
    spriteGroup.Add("belt", getAllSprites("belt", BELT_LIMIT));

    heroSprites = Resources.LoadAll<Sprite>("Spritesheets/hero/hero-body");
  }
}

So, to clarify: the Hero game object has the scripts AnimationController, HeroMovement, and HeroResources attached to it, while the child game objects only have the SpritePosition script attached.

The idea is to load the sprites, then based on the logic in the movement script, decide on the booleans to use for the animator, check which is active (currently I only have running working), then based on which one is true determine a sprite index to use. All sprites have the same name and dimensions which is why a single index works to change all. I'm basically doing it this way to avoid having hundreds of animations for each different equipment in use.

enter image description here

So, while the sprites update this way in sync, I'm not sure if I'm not optimizing the sprite changing (and redrawing in color) process, because when I press arrow keys to run, I get the following:

<img s


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
108 views
Welcome To Ask or Share your Answers For Others

1 Answer

If you ever want to discover where lag or high memory usage is coming from use the unity profiler, if possible use a 3rd party profiler because unity's is kinda meh, also use deep profiling (unity will profile every method call, good to see what changes are causing lag, remember that using the profiler will reduce performance and increase memory usage as long as you have it on). There is a lot of stuff that you NEED to improve, but all of the issues I can think off will be obvious with deep profiling.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share

548k questions

547k answers

4 comments

86.3k users

...