Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
126 views
in Technique[技术] by (71.8m points)

c# - UI prefabs are instantiated below the canvas

I am trying to copy the Zelda health system. The code looks really fine and works fine.

But the heart containers are placed wrong. They get instantiated below the canvas.

This is the important code, the heart containers are correct, just at the wrong position.

The calculation of x and y is correct, but on the canvas it is not.

  private Transform healthBar = GameObject.FindGameObjectWithTag("HealthController").transform; // container for the heartContainers

    private GameObject healthWrapperObject = Resources.Load("HealthContainer") as GameObject; // the backgroundImage and parent of the heart

    private List<Image> healthContainers = new List<Image>(); // list of hearts for later usages

    private int maxHealth = 6;
    private int currentHealth;

    private int healthPerHealthContainer = 4; // 4 lifepoints per heart
    private int healthContainersPerRow = 5; // 5 hearts per row

    private int healthContainerStartPositionX = 0; // Healthbar starts at 0 on x
    private int healthContainerStartPositionY = 0; // Healthbar starts at 0 on y

    private int healthContainerSpacingX = 10; // horizontal spacing
    private int healthContainerSpacingY = -10; // vertical spacing

    private void Start()
    {
        currentHealth = maxHealth;
        InitializeHealthBar();
    }

    public void InitializeHealthBar()
    {
        int neededHealthContainers = maxHealth % healthPerHealthContainer == 0 ? maxHealth / healthPerHealthContainer : maxHealth / healthPerHealthContainer + 1; // Calculate the needed container count
        int counter = 0; // counts the hearts per row 
        int x = healthContainerStartPositionX; // horizontal position of the heartContainer
        int y = healthContainerStartPositionY; // vertical position of the heartContainer

        for (int i = 0; i < neededHealthContainers; i++)
        {
            counter++;

            if (counter >= healthContainersPerRow) // start a new line after 5 hearts per row
            {
                x = healthContainerStartPositionX; // move back to the left
                y += healthContainerSpacingY; // go for the next line
                counter = 0; // reset the counter
            }
            else
                x += healthContainerSpacingX; // place the new container right next to the previous

            Transform newHealthContainerTransform = Instantiate(healthWrapperObject, new Vector2(x, y), healthWrapperObject.transform.rotation).transform; // create the healthContainer parent / backgroundImage
            newHealthContainerTransform.SetParent(healthBar); // take the container and make it a child of the healthBar
            healthContainers.Add(newHealthContainerTransform.GetChild(0).GetComponent<Image>()); // get the heart of the heartContainer and add it to the heartList
        }
    }

I added the transform settings for the healthBar, the healthContainer / backgroundImage and the heart ("healthfill").

On all 3 elements I pressed Strg+Alt and Shift for anchoring them.

The heartcontainter should be added to the healthbar, the heart is a child of the heartcontainer and is set to stretch (it should be the same size as its parent)

Why are the UI prefab Objects instantiated below the canvas?

Ingame

HealthBar

HealthContainer

HealthObjekt

See Question&Answers more detail:os

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

1 Reply

0 votes
by (71.8m points)

I assume you are getting something like this:

enter image description here

You fix this by pass false to the second parameter of the SetParent function. By doing this, you will make the Transform keep its local orientation rather than its global orientation.

Simply replace :

newHealthContainerTransform.SetParent(healthBar);

with:

newHealthContainerTransform.SetParent(healthBar, false)

You can also set the parent Object and make the instantiated Object's Transform keep its local orientation in the Instantiate function. The only disadvantage of this is that you now have to set the position of object in another line of code instead of the Instantiate function like before.

Transform newHealthContainerTransform = Instantiate(healthWrapperObject, healthBar, false).transform;
newHealthContainerTransform.GetComponent<RectTransform>().anchoredPosition3D = new Vector2(x, y);

When moving a UI Object you should be modifying it's RectTransform variables instead of the Transform variables.

Below are other useful variables that determines where to position the UI:

These are anchoredPosition, anchoredPosition3D, anchorMax and anchorMin which can be modified with:

yourUIObj.GetComponent<RectTransform>().anchoredPosition = ...
yourUIObj.GetComponent<RectTransform>().anchoredPosition3D = ...
yourUIObj.GetComponent<RectTransform>().anchorMax = ...
yourUIObj.GetComponent<RectTransform>().anchorMin = ...

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...