标题: unity虚拟视觉效果创建(一) [打印本页] 作者: 会飞的鱼 时间: 2012-6-11 14:56 标题: unity虚拟视觉效果创建(一) unity虚拟视觉效果创建
Introduction,介绍
In this tutorial we'll write some C# scripts to display increasingly complex graphs. You'll learn to
create graphs, from a single line to animated volumes;
control a particle system;
write various mathematical functions;
change behavior while in play mode;
use the Start and GetComponent methods;
write loops, both single and nested;
use arrays, enumerations, and delegates.
You're assumed to know your way around Unity's editor and know the basics of creating C# scripts. If you've completed the Clock tutorial you're good to go.
Preparations
We start by opening a new project without any packages. We'll be creating our graphs inside a unit cube, placed between (0, 0, 0) and (1, 1, 1). Let's set up the editor so we get a good view of this area. The 4 Split is a handy predefined view configuration so let's select that. It's in Window / Layout / 4 Spit or in the dropdown list at the top right of the screen. Set the view mode of all of them to Textured. Also flip the perspective view around by clicking on the box at the center of the compass.
Now create a particle system via GameObject / Create Other / Particle System and set its position to (0, 0, 0). This gives us a reference point for calibrating our views. Now zoom and pan the views so they're focused on the unit cube.
Finally, select the Main Camera and make it match the perpective view via GameObject / Align With View (we did the opposite in the Clock tutorial). If that doesn't work right, make sure the correct view is active by clicking in it and then try again.
The particle system is currently producing animated particles at random, which we don't need. Disable the Ellipsoid Particle Emitter by unchecking the Emit field. This causes the preview particles to die out. Also remove the Particle Animator component, because we don't need it. This leaves us with an inert particle system that we can use to visualize graph data.
Creating the first graph
The first graph we'll create will be a simple line graph where the value of Y depends on the value of X. We'll use the positions of the particles to visualize this.
Rename the particle system object to Graph 1, create a C# script named Grapher1 as a minimal GameObject class, and drag it to our object so it gets added as a component.
using UnityEngine;
public class Grapher1 : MonoBehaviour {
}
To get to the particles, we need access to the particle emitter component first. We could do this by adding a public ParticleEmitter variable and then dragging the component to the corresponding field, like we did in the Clock tutorial. However, there's a different way that doesn't require manual configuration.
We use the GetComponent method to retrieve our particle emitter component. We do this inside the special Start method, which is called once before updates start happening. This allows us to keep out variable private, so it doesn't show up in the inspector view.
Not EllipsoidParticleEmitter?How does GetComponent work?When is Start called exactly?
using UnityEngine;
public class Grapher1 : MonoBehaviour {
private ParticleEmitter emitter;
void Start () {
emitter = GetComponent<articleEmitter>();
}
}
Using the emitter, we can produce an array of Particle s***cts which will serve as the points of our graph. To get them, we first need to ins***ct the emitter to generate a certain number of particles, then fetch those particles and store them for later use.
How much particles should we create? The more particles, the higher the sample resolution of our graph. Let's make it customizable, with a default resolution of 10.
How do arrays work?
using UnityEngine;
public class Grapher1 : MonoBehaviour {
public int resolution = 10;
private Particle[] points;
private ParticleEmitter emitter;
void Start () {
emitter = GetComponent<articleEmitter>();
emitter.Emit(resolution);
points = emitter.particles;
}
}
Now that we have the points it's time to position them along the X axis. The first point should be placed at 0 and the last should be placed at 1. All other points should be placed in between. So the distance – or X increment – between two points is 1 / (resolution - 1).
Besides the position, we can also use color to provide the same information. We'll make the red color component of the points equals to their position along the X axis.
We'll use a for loop to iterate over all points and set both their position and color, which are s***ct values of type Vector3 and Color.
How does a for loop work?What does i++ do?What does new do?
using UnityEngine;
public class Grapher1 : MonoBehaviour {
public int resolution = 10;
private Particle[] points;
private ParticleEmitter emitter;
void Start () {
emitter = GetComponent<articleEmitter>();
emitter.Emit(resolution);
points = emitter.particles;
float increment = 1f / (resolution - 1);
for(int i = 0; i < resolution; i++){
float x = i * increment;
points.position = new Vector3(x, 0f, 0f);
points.color = new Color(x, 0f, 0f);
}
}
}
So far, it doesn't work. When playing, we now get some random white particles instead of what we want. This is because the particle array that we got out of the emitter is a copy. Changing this copy doesn't affect the particles in the particle system at all. We need to assign the particle array back to the emitter. That will copy our data back into the particle system, replacing its old particles. We will do this in the Update method.
using UnityEngine;
public class Grapher1 : MonoBehaviour {
public int resolution = 10;
private Particle[] points;
private ParticleEmitter emitter;
void Start () {
emitter = GetComponent<articleEmitter>();
emitter.Emit(resolution);
points = emitter.particles;
float increment = 1f / (resolution - 1);
for(int i = 0; i < resolution; i++){
float x = i * increment;
points.position = new Vector3(x, 0f, 0f);
points.color = new Color(x, 0f, 0f);
}
}
void Update () {
emitter.particles = points;
}
}
That's it! We now get a nice black to red line of points along the X axis. How many points are shown depends on what we put in the resolution field before entering play mode.
Right now, resolution is only taken into account when the graph is initialized. Updating its value while in play mode doesn't do anything. Let's change that.
A simple way to detect a change of resolution is by storing it twice and then constantly checking whether both values are still the same. If at some point they're different, we need to rebuild the graph. We'll create a private variable currentResolution for this purpose.
using UnityEngine;
public class Grapher1 : MonoBehaviour {
public int resolution = 10;
private int currentResolution;
private Particle[] points;
private ParticleEmitter emitter;
void Start () {
emitter = GetComponent<articleEmitter>();
currentResolution = resolution;
emitter.Emit(resolution);
points = emitter.particles;
float increment = 1f / (resolution - 1);
for(int i = 0; i < resolution; i++){
float x = i * increment;
points.position = new Vector3(x, 0f, 0f);
points.color = new Color(x, 0f, 0f);
}
}
void Update () {
if(currentResolution != resolution){
// currently do nothing
}
emitter.particles = points;
}
}
Because rebuilding is almost the same as the first initialization, let's move that code into a new private method which we name CreatePoints. That way we can reuse the code. The only thing we need to add clearing any previous particles before emitting new ones.
using UnityEngine;
public class Grapher1 : MonoBehaviour {
public int resolution = 10;
private int currentResolution;
private Particle[] points;
private ParticleEmitter emitter;
void Start () {
emitter = GetComponent<articleEmitter>();
CreatePoints();
}
private void CreatePoints () {
currentResolution = resolution;
emitter.ClearParticles();
emitter.Emit(resolution);
points = emitter.particles;
float increment = 1f / (resolution - 1);
for(int i = 0; i < resolution; i++){
float x = i * increment;
points.position = new Vector3(x, 0f, 0f);
points.color = new Color(x, 0f, 0f);
}
}
void Update () {
if(currentResolution != resolution){
CreatePoints();
}
emitter.particles = points;
}
}
Now the graph is recreated as soon as we change the value of resolution. However, you'll notice that the console will spit out errors whenever resolution is equal to 1, even while typing. This is because the caluculation of increment will result in a division by zero. One way to prevent this is to make sure that resolution is always at least 2.
using UnityEngine;
public class Grapher1 : MonoBehaviour {
public int resolution = 10;
private int currentResolution;
private Particle[] points;
private ParticleEmitter emitter;
void Start () {
emitter = GetComponent<articleEmitter>();
CreatePoints();
}
private void CreatePoints () {
if(resolution < 2){
resolution = 2;
}
currentResolution = resolution;
emitter.ClearParticles();
emitter.Emit(resolution);
points = emitter.particles;
float increment = 1f / (resolution - 1);
for(int i = 0; i < resolution; i++){
float x = i * increment;
points.position = new Vector3(x, 0f, 0f);
points.color = new Color(x, 0f, 0f);
}
}
void Update () {
if(currentResolution != resolution){
CreatePoints();
}
emitter.particles = points;
}
}
Now it's time to set the Y position of the points. Let's start simple and make Y equal to X. In other words, we're visualizing the mathematical equation y = x or the function f(x) = x. To do this, we need to loop over all points, get their position, use the X value to compute the Y value, then set their new position. Once again we use a for loop, which we'll***cute each update. (The code snippet below only contains the Update method, everything else stays the same.)Why not directly set position.y?
void Update () {
if(currentResolution != resolution){
CreatePoints();
}
for(int i = 0; i < resolution; i++){
Vector3 p = points.position;
p.y = p.x;
points.position = p;
}
emitter.particles = points;
}
The next step is to make the point's green color component the same as its Y position. As red plus green becomes yellow, this will cause the line to go from black to yellow.