In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-17 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Internet Technology >
Share
Shulou(Shulou.com)05/31 Report--
This article mainly explains "the performance measurement method of Unity game development". The content of the article is simple and clear, and it is easy to learn and understand. Please follow the editor's train of thought to study and learn "the performance measurement method of Unity game development".
Build up the nucleus
We need a test scenario. It ideally covers both high performance and low performance situations. I suggest that we create nuclei by fusing more and more nucleons together. As the nucleus gets bigger, the performance gets worse.
The nucleon will be a simple sphere that will be attracted to the center of the scene, where they will converge into a ball. This is certainly not the correct representation of atoms, but that is not the point.
We can model nucleons using default spheres and custom Nucleon components. This component ensures that the rigid body is attached to its object, and then simply pulls it toward the origin. The strength of the tension depends on the configurable attraction and the distance from the center.
Using UnityEngine
[RequireComponent (typeof (Rigidbody))] public class Nucleon: MonoBehaviour {
Public float attractionForce
Rigidbody body
Void Awake () {body = GetComponent ();} void FixedUpdate () {body.AddForce (transform.localPosition *-attractionForce);}} missing some access modifiers?
Yes, I have now omitted the private modifiers in field and method declarations because they are private by default. Let's see how it works, whether it's confusing or not.
Use a sphere to create two nuclear preforms, one for protons and the other for neutrons. Provide different materials for each material to make them look different. It's enough for us to have only one nuclear type, but it's boring.
What is a prefabricated part?
Preforms are Unity objects (or object hierarchies) that do not exist in the scene and have not been activated. You use it as a template, create its clone, and add it to the scene. To create an object, construct an object in the scene as usual, and then drag it to the project window. The scene object becomes a prefabricated instance and can be deleted if it is no longer needed.
Nuclear prefabricated parts.
To generate these cores, we need to create another component, NucleonSpawner. It needs to know the interval between builds, how far away it is from the build center, and what to generate.
Using UnityEngine
Public class NucleonSpawner: MonoBehaviour {
Public float timeBetweenSpawns
Public float spawnDistance
Public Nucleon [] nucleonPrefabs;}
Create an empty game object, attach a NucleonSpawner component, and then configure it as needed.
Nuclear generator.
To generate periodically, we need to track the time since the last build. We can do this with a simple FixedUpdate method.
Float timeSinceLastSpawn
Void FixedUpdate () {timeSinceLastSpawn + = Time.deltaTime; if (timeSinceLastSpawn > = timeBetweenSpawns) {timeSinceLastSpawn-= timeBetweenSpawns; SpawnNucleon ();}} Why should I use FixedUpdate instead of Update?
Use FixedUpdate to make the resulting frame independent of the frame rate. If the time between configured builds is shorter than the frame time, the use of Update can cause build delays. And because the focus of this scenario is to reduce our frame rate, this will happen.
You can use a while loop instead of an if check to catch up with missed builds, but when timeSinceLastSpawn accidentally sets it to 00:00, this results in an infinite spawning cycle. It is wise to limit spawning to one step at a fixed time.
The actual generation consists of three steps. Pick a random preform, instantiate it, and give it a random location at the desired distance.
Void SpawnNucleon () {Nucleon prefab = nucleonPrefabs [Random.Range (0, nucleonPrefabs.Length)]; Nucleon spawn = Instantiate (prefab); spawn.transform.localPosition = Random.onUnitSphere * spawnDistance;} establish the nucleus by bombardment.
Playing this scene should cause the sphere to shoot toward the center. After a while, they collide with each other until they form a ball. The ball will continue to grow, physical calculations will become more complex, and at some point you will notice a decline in the frame rate.
If it takes too long to see performance degradation, you can increase the build speed. It is also possible to speed up time by increasing the proportion of time. You can find it through Edit / Project Settings / time. You can also reduce the fixed time step, which results in more physical calculations per second.
Unify the time setting. Why does the lower mark not move smoothly at low times?
When the timescale is set to a low value (for example, 0.1), time moves very slowly. Because the fixed time step is constant, this means that the physical system will be updated less frequently. As a result, the physical object remains unchanged until a fixed update occurs (only every few frames).
You can solve this problem by increasing the timescale to reduce the fixed time step. Alternatively, you can change the interpolation mode of rigid body assemblies so that they interpolate between physical steps, thus hiding the lower update frequency.
Use the profiler
Now that we have a scenario that will eventually slow down the frame rate of any machine, it's time to measure actual performance. The fastest thing you can do is to enable statistics overlay in the game view.
Game view statistics overlay layer.
However, the frame rate shown there is not accurate at all and is more like a rough guess. By opening the Unity profiler through Window / Profiler, we can do better. The profiler provides us with a lot of useful information, especially CPU usage and memory data.
Parser, showing a large amount of vsync time.
If vsync is enabled, it may initially dominate CPU graphics. To better understand how many CPU resources the scene requires, turn off vsync. You can do this through Edit / Project Settings / quality. It is located at the bottom under the heading "other".
There is no more vsync.
My frame rate is very high now!
If there is no vsync, you may get a high frame rate, or even more than 100, for simple scenarios. This puts unnecessary pressure on the hardware. You can prevent this by setting the Application.targetFrameRate property to force the use of the maximum frame rate by code. Note that this setting remains in the editor even if you exit playback mode. Setting it to-1 removes the restriction.
Now you can have a better understanding of CPU usage. In my case, physics takes the most time, followed by rendering, then my script. Even if everything slows down as the number of spheres increases, this will remain the same for a long time.
There is no vsync profiler.
We also have two unexpected discoveries. First, there is an occasional surge in CPU usage. Second, the memory graph shows frequent spikes in GC allocation, indicating that there is memory being allocated and subsequently freed. This is strange because we are just creating new objects and never throw anything away.
Both phenomena are caused by the Unity editor. CPU spikes occur whenever something is selected in the editor. Memory allocation is caused by the editor calling GameView.GetMainGameViewRenderRect. There is also additional overhead, especially if both the game view and the scene view are displayed. In short, the editor itself interferes with our measurements.
You can still get a lot of useful information from the analysis in the editor, but if you want to eliminate the editor itself from the measurement, you must build it independently. If you develop and even automatically connect to the profiler when you run the application, you can still use the profiler. You can configure it through File / build Settings.
A profiler attached to a stand-alone application.
When profiling stand-alone builds, the data looks completely different. Now, memory allocation is caused only by the resulting core, and garbage collection no longer occurs. As far as I'm concerned, rendering takes more time because I'm running the application in full-screen mode rather than in Mini Game view. In addition, scripts are so trivial that they are not even visible in the drawing.
Measure frames per second
The profiler provides us with useful information, but it is still not a good measure of the frame rate. The number of FPS displayed is only 1 divided by CPU time, which is not the actual frame rate we get. So let's measure it ourselves.
We need a simple component to tell us the current number of frames per second the application is running. One public property is enough. We set it to an integer because we don't really need decimal precision.
Using UnityEngine
Public class FPSCounter: MonoBehaviour {
Public int FPS {get; private set;}} how does this property work?
Remember that properties are methods that are disguised as fields. We provide FPS as public information, but only the component itself needs to update the value. The syntax used is an abbreviated form of automatically generated attributes, which looks like this.
Int fps; public int FPS {get {return fps;} private set {fps = value;}}
This abbreviation does not apply to Unity serialization, but this is good because we still do not need to save the FPS value.
We measure the number of frames per second per update by dividing 1 by the time increment of the current frame. We convert the result to an integer, effectively rounding it.
Void Update () {FPS = (int) (1f / Time.deltaTime);}
However, there are problems with this approach. The time increment is not the actual time spent processing the last frame, it is affected by the current time ratio. This means that unless the time scale is set to 1, our FPS will be wrong. Fortunately, we can also request an unscaled time increment from Unity.
Void Update () {FPS = (int) (1f / Time.unscaledDeltaTime);}
Some kind of UI is required to display the FPS. Let's use Unity's UI. Create a canvas with a panel inside, which in turn contains a text object. These can be added through the GameObject / UI submenu. When you add the canvas, you will also get an EventSystem object to handle user input, but we don't need it, so we can delete it.
UI object hierarchy.
I used the default canvas setting, except that I set it to pixel perfection.
A canvas with perfect pixels.
This panel is used to create a translucent black background for FPS tags. In this way, it will always be readable. I put it in the upper left corner of the window. Set its anchor to the upper-left corner so that it remains in place regardless of the size of the window. Set its pivot to (0pl 1) for easy placement.
Place the label in the panel in a similar way. Set it to white bold text centered horizontally and vertically. Design the whole content so that it fits exactly into two digits.
Build the UI.
Now we need to bind the FPS value to the tag. To do this, we create a component. It needs a FPSCounter component to retrieve the value from it and a reference to the Text tag in the UnityEngine.UI namespace to assign the value to it.
Using UnityEngine;using UnityEngine.UI
[RequireComponent (typeof (FPSCounter))] public class FPSDisplay: MonoBehaviour {
Public Text fpsLabel;}
Add this component to the panel and connect it. We attach it to the panel because this is the entire FPS display, not the label. We will include more tags later.
Set up the monitor.
The display component only needs to update the text of the label at each frame. Let's cache the reference to the counter so that we don't have to call GetComponent every time.
FPSCounter fpsCounter
Void Awake () {fpsCounter = GetComponent ();}
Void Update () {fpsLabel.text = fpsCounter.FPS.ToString ();}
The FPS tag is being updated now! However, because we designed it to be double-digit, it displays useless values as long as our frame rate exceeds 99 per second. So let's limit the values that are displayed. In any case, the performance above 99 is good enough.
Void Update () {fpsLabel.text = Mathf.Clamp (fpsCounter.FPS, 0,99). ToString ();} now we can see the frame rate.
Everything seems to be working properly now, but there is a slight problem. Now we create a new string object with each update and discard it the next time we update it. This contaminates managed memory, which triggers the garbage collector. While this is not a big problem for desktop applications, it is even more troublesome for devices with little available memory. It can also contaminate our profiler data, which is annoying when you're looking for an allocation.
A temporary string object is created for each update.
Can we get rid of these temporary strings? The value we display can be any integer between 0 and 99. This is 100 different strings. Why not create all these strings at once and reuse them instead of always recreating the same content?
Static string [] stringsFrom00To99 = {"00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23" "24", "25", "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49" "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "70", "71", "72", "73", "74" "75", "76", "77", "78", "79", "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "90", "91", "92", "93", "94", "95", "96", "97", "98", "99"} Void Update () {fpsLabel.text = stringsFrom00To99 [Mathf.Clamp (fpsCounter.FPS, 0,99)];}
By using an array of fixed string representations of each number that might be needed, we eliminate all temporary string assignments!
Average frames per second
Updating the FPS value every frame has adverse side effects. When the frame rate is unstable, the label fluctuates constantly, making it difficult to obtain useful readings. We can only update the tag once in a while, but in this way, we will no longer have any impression of the frame rate performance.
One possible solution is to average the frame rate to smooth the effects of sudden changes and produce smaller jitter values. Let's adjust the FPSCounter to do this within a configurable range of frames. Setting this value to 1 equals no averaging at all, so it's actually optional.
Public int frameRange = 60; configurable frame range.
Let's change the property name from FPS to AverageFPS, because this is a better description of the value it now represents. You can use the IDE ReFactor name, or you can manually update the display component to use the new name.
Public intAverageFPS {get; private set;}
Now we need a buffer to store the FPS values for multiple frames, as well as an index so that we know where to put the data for the next frame.
Int [] fpsBuffer; int fpsBufferIndex
When initializing this buffer, make sure that the frameRange value is at least 1 and set the index to 0.
Void InitializeBuffer () {if (frameRange = frameRange) {fpsBufferIndex = 0;}}
Calculating the average is a simple matter of adding and dividing all the values in the buffer by the amount of the value.
Void CalculateFPS () {int sum = 0; for (int I = 0; I
< frameRange; i++) { sum += fpsBuffer[i]; } AverageFPS = sum / frameRange; } 现在,我们的平均帧速率有效,并且在合理的帧范围内,轻松获得良好的阅读效果非常容易。但是我们可以做得更好。由于我们现在具有来自多个帧的数据,因此我们也可以公开此范围内的最高和最低FPS。这给了我们比平均值更多的信息。 public int HighestFPS { get; private set; } public int LowestFPS { get; private set; } 我们可以在计算总和的同时找到这些值。 void CalculateFPS () { int sum = 0; int highest = 0; int lowest = int.MaxValue; for (int i = 0; i < frameRange; i++) { int fps =fpsBuffer[i]; sum +=fps; if (fps >Highest) {highest = fps;} if (fps
< lowest) { lowest = fps; } } AverageFPS = sum / frameRange; HighestFPS = highest; LowestFPS = lowest; } 现在,我们的FPSDisplay组件可以绑定两个附加标签。 public Text highestFPSLabel, averageFPSLabel, lowestFPSLabel; void Update () { highestFPSLabel.text = stringsFrom00To99[Mathf.Clamp(fpsCounter.HighestFPS, 0, 99)]; averageFPSLabel.text = stringsFrom00To99[Mathf.Clamp(fpsCounter.AverageFPS, 0, 99)]; lowestFPSLabel.text = stringsFrom00To99[Mathf.Clamp(fpsCounter.LowestFPS, 0, 99)]; } 在用户界面中再添加两个标签,然后将它们全部连接起来。我将最高FPS放在顶部,将最低FPS放在底部,将平均FPS放在中间。 更多信息,更少抖动。 给标签上色 作为FPS标签的最后修饰,我们可以为它们着色。这可以通过将颜色与FPS值关联来完成。这样的关联可以用自定义结构表示。 [System.Serializable] private struct FPSColor { public Color color; public int minimumFPS; } 作为FPSDisplay唯一将使用此结构的东西,我们将struct定义直接放在该类内,并将其私有化,这样它就不会显示在全局名称空间中。使它可序列化,以便可以由Unity编辑器公开。 现在添加这些结构的数组,以便我们可以配置FPS标签的颜色。我们通常会为此添加一个公共字段,但是由于结构本身是私有的,因此我们不能这样做。因此,也将数组设为私有并为其赋予SerializeField属性,以便Unity在编辑器中公开并保存它。 [SerializeField] private FPSColor[] coloring; 继续添加一些颜色!确保至少有一个条目,从最高FPS到最低FPS进行排序,最后一个条目为0 FPS。 染色。 在将颜色应用于标签之前,Update通过引入一个单独的Display方法来调整方法,以调整单个标签。 void Update () { Display(highestFPSLabel,fpsCounter.HighestFPS); Display(averageFPSLabel,fpsCounter.AverageFPS); Display(lowestFPSLabel,fpsCounter.LowestFPS); } void Display (Text label, int fps) { label.text = stringsFrom00To99[Mathf.Clamp(fps, 0, 99)]; } 可以通过遍历阵列直到找到某种颜色的最低FPS来找到正确的颜色。然后设置颜色并跳出循环。 void Display (Text label, int fps) { label.text = stringsFrom00To99[Mathf.Clamp(fps, 0, 99)]; for (int i = 0; i < coloring.Length; i++) { if (fps >= coloring [I] .minimumFPS) {label.color = coloring [I] .color; break;} Why did my tag disappear?
All four channels of the default color are set to zero. This includes Alpha channels that control opacity. If you have not changed the Alpha channel, you will get a completely transparent label.
Color fps tags. Thank you for reading, the above is the content of "performance measurement of Unity game development". After the study of this article, I believe you have a deeper understanding of the performance measurement method of Unity game development, and the specific use needs to be verified in practice. Here is, the editor will push for you more related knowledge points of the article, welcome to follow!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.