2018-10-23 16:52:11 realzuile 阅读数 5360

1.没有将vs设置为默认脚本编辑器

2.通过步骤1后设置使用VS2012做脚本编辑,但每次还是启动Monodevelop,可能原因一:没有将unity与vs关联起来

https://marketplace.visualstudio.com/items?itemName=SebastienLebreton.VisualStudio2015ToolsforUnity
你下载对应的vs版本的Tool for Unity,安装后,打开后就可以正常使用vs打开脚本了,亲测(我自己的是用vs2015的,所以这里放2015的Tool for Unity)

要是不是2015版本的vs,那么

在搜索到结果中,找到自己版本的Tool for Unity,点击打开,在弹出的窗口中点击“Download”按钮,就可以下载对于版本的Tool for Unity了,接着安装就可以了

原因2:vs出问题了,无法与unity关联

将vs修复下;既然怒控制面板->程序->程序和功能->vs->更改->修复

 

 

2013-02-01 09:50:20 yang7474020 阅读数 1452

Unity3D教程:Unity制作连连看教程

GameManager.cs  游戏的核心代码,产生图片,判断是否可以销毁等。

[color=#008ef1][font=宋体]using UnityEngine;[/font][/color]
using System.Collections;
using System.Collections.Generic;
public class GameManager : MonoBehaviour
{
public DrawLine drawLine;//画线
public GameObject tilePrefab;//tile的预制
public List<Tile> tiles;//开始实例化的时候存放tile
public List<Tile> _tiles;//存放随机摆放的tile
public List<Tile> tilesEdge;//为了边界处可以有拐点,把棋盘四周的tile放在这里,这里的tile看不到
public int x, y;//棋牌的大小,两个数必须是偶数
private Tile tileA;
private Tile tileB;
private bool destroy;
private Vector3 mousePos;
private enum stepType//控制游戏的状态
{
one,
two,
three
}
private stepType _stepType;
void Start ()
{
this.gameObject.transform.position = Vector3.zero;
Spawn ();
_stepType = stepType.one;
}private void Spawn ()//实例化tile
{
float num = (x * y - (2 * x + 2 * y - 4)) * 0.5f;
for (int i = 0; i <num; i ++) {
int idTex = Random.Range (20, 45);
GameObject obj = Instantiate (tilePrefab) as GameObject;
GameObject obj2 = Instantiate (tilePrefab) as GameObject;
Tile tile = obj.GetComponent<Tile> ();
Tile tile2 = obj2.GetComponent<Tile> ();
tile.Init (idTex);
tile2.Init (idTex);
tiles.Add (tile);
tiles.Add (tile2);
}
for (int i = 0; i<((2*x+2*y) -4); i++) {//实例化边缘的tile
GameObject obj = Instantiate (tilePrefab) as GameObject;
obj.name = "edage";
Tile tile = obj.GetComponent<Tile> ();
tilesEdge.Add (tile);
}
CreatTile ();
for (int i = 0; i < _tiles.Count; i++) {
_tiles [i].transform.name = i.ToString ();
_tiles [i].id = i;
}
this.transform.position = new Vector3 (-(x / 2.0f - 0.5f), -(y / 2.0f - 0.5f), 0);
}private void CreatTile ()//随机摆放tile,如果是边缘的就放在边缘位置
{
int idTex = 0;
float _y = 0.0f;
for (int i = 0; i < y; i ++) {
float _x = 0.0f;
for (int j = 0; j < x; j ++) {
if (i == 0 || j == 0 || i == y - 1 || j == x - 1) {
tilesEdge [0].transform.position = new Vector3 (_x, _y, 0);
tilesEdge [0].pos = new Vector2 (_x, _y);
tilesEdge [0].transform.rotation = new Quaternion (0, 0, 180, 0);
tilesEdge [0].transform.parent = this.gameObject.transform;
_tiles.Add (tilesEdge [0]);
tilesEdge [0].transform.localScale = Vector3.zero;
tilesEdge [0].type = false;
tilesEdge.RemoveAt (0);
} else {
int id = Mathf.FloorToInt (Random.Range (0, tiles.Count));
tiles [id].transform.position = new Vector3 (_x, _y, 0);
tiles [id].pos = new Vector2 (_x, _y);
tiles [id].transform.rotation = new Quaternion (0, 0, 180, 0);
tiles [id].transform.parent = this.gameObject.transform;
_tiles.Add (tiles [id]);
tiles.RemoveAt (id);
}
_x += 1;
}
_y += 1;
}
}private void SelectTile ()//开始选择图片,通过射线方式选中,如果tileA和tileB不相同,则tileA等于tileB开始下一个检测
{
Ray ray = Camera.mainCamera.ScreenPointToRay (mousePos);
RaycastHit hit;
int mask = 1 << 8;
if (Physics.Raycast (ray, out hit, mask)) {
if (tileA == null) {
tileA = hit.transform.GetComponent<Tile> ();
tileA.SetTileTexture (1);
//                print ("tileA = hit.transform.GetComponent<Tile> ();" + tileA.transform.name);
} else {
tileB = hit.transform.GetComponent<Tile> ();
tileB.SetTileTexture (1);
//                print ("tileB = hit.transform.GetComponent<Tile> ();" + tileB.transform.name);
Compare (tileA, tileB);
if (tileA == null && tileB == null) {//                    print ("a and b is null");
}
}
//            hit.transform.GetComponent
}
}

private void Compare (Tile tile1, Tile tile2)//比较两个点是否可以连接到一起
{
// same card
_stepType = stepType.one;
drawLine.waypoints.Add (tile1.transform); //第一个选择的tile是画线的起始位置,
drawLine.transform.position = tile1.transform.position;
destroy = false;
print ("compare");
if (tile1.pos.x == tile2.pos.x && tile1.pos.y == tile2.pos.y) {如果选中的是同一个图片返回
tileA.SetTileTexture (0);
//            tileB.SetTileTexture (0);
tileA = tileB;
tileB = null;
//            tileA.SetTileTexture (1);
return;
} else if (tile1.pos.x == tile2.pos.x && tile1.pos.y != tile2.pos.y) {//如果两点的x相等,竖向检测
print ("check y");
destroy = CheckY (tile1, tile2);
if (destroy)
drawLine.waypoints.Add (tile2.transform);
} else if (tile1.pos.x != tile2.pos.x && tile1.pos.y == tile2.pos.y) {//如果两点的y相等,横向检测
print ("check x");
destroy = CheckX (tile1, tile2);
if (destroy)
drawLine.waypoints.Add (tile2.transform);
}
if (!destroy) {//不符合直线连接方式的开始进行一个拐点的检测
_stepType = stepType.two;
destroy = CheckTwoStep (tile1, tile2);
//            print ("destroy = CheckTwoStep (tile1, tile1);:" + destroy);
print ("check two step");
if (!destroy) {//不符合直线和一个拐点检测的开始进行两个拐点的检测
_stepType = stepType.three;
destroy = CheckThreeStep (tile1, tile2);
print ("check three:" + destroy);
print ("tile1.idTex:" + tile1.idTex + "tile1.idTex:" + tile1.idTex);
}
}
if (destroy) {//如果符合销毁条件销毁图片,并开始画线
tile1.transform.localScale = Vector3.zero;
tile2.transform.localScale = Vector3.zero;
tile1.type = false;
tile2.type = false;
tileA = null;
tileB = null;
drawLine.MoveToWaypoint ();
} else {//不符合的话,清除画线中的路径
drawLine.ClearPath ();
tileA.SetTileTexture (0);
//            tileB.SetTileTexture (0);
tileA = tileB;
tileB = null;
return;
}
}
// one step横向检测
private bool CheckX (Tile a, Tile b)
{
bool compare = true;
int _min, _max;
if (a.idTex == b.idTex) {
CompareID (a, b, out _min, out _max);
_min += 1;
if (_min == _max)
return true;
for (int i = _min; i < _max; i++) {
if (_tiles [i].type == true) {
compare = false;
break;
}
}
return compare;
} else
return false;
}
//竖向检测
private bool CheckY (Tile a, Tile b)
{
bool compare = true;
int _min, _max;
//        int idA = (int)(a.x * x + a.y);
//        int idB = (int)(b.x * x + b.y);
//        print (_tiles [idA].id.ToString () + "idA:" + idA);
//        print (_tiles [idB].id.ToString () + "idB:" + idB);
//        print ("a.idtex:" + a.idTex + "b.idtex:" + b.idTex);
if (a.idTex == b.idTex) {
CompareID (a, b, out _min, out _max);
_min += x;
if (_min == _max)
return true;
for (int i = _min; i < _max; i+=x) {
//                print ("1step");
if (_tiles [i].type == true) {
compare = false;
break;
}
}
//            if (compare) {
//                print ("2step");
//                a.type = false;
//                b.type = false;
//            }
return compare;
} else
return false;
}
// two step一个拐点的检测
private bool CheckTwoStep (Tile a, Tile b)
{
if (a.pos.x == b.pos.x || a.pos.y == b.pos.y)
return false;
int id1 = (int)(a.pos.y * x + b.pos.x);
if (_tiles [id1].type == false) {
_tiles [id1].idTex = a.idTex;

if (CheckY (_tiles [id1], b)) {
if (CheckX (a, _tiles [id1])) {
if (_stepType == stepType.two) {
drawLine.waypoints.Add (_tiles [id1].transform);
drawLine.waypoints.Add (b.transform);
} else if (_stepType == stepType.three) {
drawLine.waypoints.Add (_tiles [id1].transform);
print ("=====================:" + 1);
}
return true;
}
//                else
//                    return false;
}
}
int id2 = (int)(b.pos.y * x + a.pos.x);
if (_tiles [id2].type == false) {
_tiles [id2].idTex = b.idTex;

if (CheckY (a, _tiles [id2])) {
if (CheckX (b, _tiles [id2])) {
if (_stepType == stepType.two) {
drawLine.waypoints.Add (_tiles [id2].transform);
drawLine.waypoints.Add (b.transform);
} else if (_stepType == stepType.three) {
drawLine.waypoints.Add (_tiles [id2].transform);
print ("=====================:" + 2);
}
return true;
}
//                else
//                    return false;
}
}
return false;
}
// three step两个拐点的检测
private bool CheckThreeStep (Tile a, Tile b)
{
print ("a:" + a.idTex + "b:" + b.idTex);
//        if (a.pos.x == b.pos.x || a.pos.y == b.pos.y) return false;
bool returnValue = false;
print ("returnValue:" + returnValue);
List<Tile> _comparrPointsB;
ComparePoints (b, out _comparrPointsB);//返回b点可以横竖直线相连的点
for (int i =0; i<_comparrPointsB.Count; i++) {
returnValue = CheckTwoStep (a, _comparrPointsB [i]);
if (returnValue) {
drawLine.waypoints.Add (_comparrPointsB [i].transform);
drawLine.waypoints.Add (b.transform);
return returnValue;
}
}
if (!returnValue) {
List<Tile> _comparrPointsA;
ComparePoints (a, out _comparrPointsA);
print (a.name);
print (b.name);
for (int i =0; i<_comparrPointsA.Count; i++) {
print ("--------------" + b.idTex);
returnValue = CheckTwoStep (b, _comparrPointsA [i]);
if (returnValue) {
drawLine.waypoints.Add (_comparrPointsA [i].transform);
drawLine.waypoints.Add (b.transform);
return returnValue;
}
}

}
return returnValue;
}
//两个拐点的时候返回可以与a横竖直线相连的点
private void ComparePoints (Tile a, out List<Tile> comparePoints)
{
print ("a.idtex" + a.idTex);
comparePoints = new List<Tile> ();
comparePoints.Clear ();
//        for (int i = 0; i < y; i ++) {
//            if (i != a.y) {
//                int id = (int)(i * x + a.pos.x);
//                if (_tiles [id].type == false) {
//                    comparePoints.Add (_tiles [id]);
//                    _tiles [id].idTex = a.idTex;
//                }
//            }
//        }
for (int i = (int)a.pos.y - 1; i >-1; i--) {
int id = (int)(i * x + a.pos.x);
//            print ("three step :" + id);
if (_tiles [id].type == false) {
comparePoints.Add (_tiles [id]);
_tiles [id].idTex = a.idTex;
print ("_tiles [id].idTex = a.idTex; " + _tiles [id].idTex);
} else
break;
}
for (int i = (int)a.pos.y + 1; i < y; i++) {
int id = (int)(i * x + a.pos.x);
//            print ("three step :" + id);
if (_tiles [id].type == false) {
comparePoints.Add (_tiles [id]);
_tiles [id].idTex = a.idTex;
print ("_tiles [id].idTex = a.idTex; " + _tiles [id].idTex);
} else
break;
}
for (int i = (int)a.pos.x -1; i >-1; i --) {
int id = (int)(a.pos.y * x + i);
if (_tiles [id].type == false) {
comparePoints.Add (_tiles [id]);
_tiles [id].idTex = a.idTex;
print ("_tiles [id].idTex = a.idTex; " + _tiles [id].idTex);
} else
break;
}
for (int i = (int)a.pos.x +1; i < x; i ++) {
int id = (int)(a.pos.y * x + i);
if (_tiles [id].type == false) {
comparePoints.Add (_tiles [id]);
_tiles [id].idTex = a.idTex;
print ("_tiles [id].idTex = a.idTex; " + _tiles [id].idTex);
} else
break;
}
//        for (int i = 0; i < x; i ++) {
//            if (i != a.x) {
//                int id = (int)(a.pos.y * x + i);
//                if (_tiles [id].type == false) {
//                    comparePoints.Add (_tiles [id]);
//                    _tiles [id].idTex = a.idTex;
//                }
//            }
//        }
}
private void CompareID (Tile a, Tile b, out int min, out int max)
{

if (a.id < b.id) {
min = a.id;
max = b.id;
} else {
min = b.id;
max = a.id;
}
}
Vector2 TexSize ()
{
Vector2 size = new Vector2 (1 / x, 1 / y);
return size;
}
Vector2 TexOffset (int _idTex)
{
int a = (int)(_idTex / x);
//        print (a + "a:" + _idTex);
int b = (int)(_idTex % x);
//        print (b + "b:" + _idTex);
Vector2 offset = new Vector2 (b / x, (y - 1 - a) / y);
return offset;
}
void Update ()
{

if (Input.GetMouseButtonUp (0)) {
mousePos = Input.mousePosition;
SelectTile ();
}

}
private void ClearTiles (List<Tile> tiles)
{

tiles.Clear ();
//         this.gameObject.transform.DetachChildren();
}
}
// ari

DrawLine.cs,画线脚本,用的itween。

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class DrawLine : MonoBehaviour
{
public List<Transform> waypoints = new List<Transform> ();
public float rate = 1;
private int currentWaypoint = 1;
public void MoveToWaypoint ()
{
print ("public void MoveToWaypoint (): " + waypoints.Count);
StartCoroutine ("move");
}
public void ClearPath ()
{
waypoints.Clear ();
print ("path.Clear ();");
}
IEnumerator move ()
{
for (int i = 0; i < waypoints.Count; i++) {
iTween.MoveTo (this.gameObject, waypoints [i].position, rate);
print ("now id:" + i);
yield return new WaitForSeconds(rate);
}
waypoints.Clear ();
}
}

Tile.cs

using UnityEngine;
using System.Collections;
public class Tile : MonoBehaviour
{
public int id;
public int idTex; //通过这个判断两个图片是否相同
public Vector2 pos ;
public bool type = true;//控制图片的状态,当销毁的时候为false,其他判断的时候可以通过该点
public float x, y;
public Texture texA, texB;//鼠标选中的时候可以换贴图
public GameObject mask;//鼠标选中的时候上边显示的框框
public void Init (int _idTex)
{
idTex = _idTex;
Vector2 offset = TexOffset (_idTex);
this.renderer.material.SetTextureOffset ("_MainTex", offset);
this.renderer.material.SetTextureScale ("_MainTex", new Vector2 (0.2f, 0.1f));}
//设置tile显示的贴图和大小
public void SetTileTexture (int i)
{
if (i == 0) {
this.renderer.material.mainTexture = texA;
mask.transform.localScale = Vector3.zero;
}if (i == 1) {
this.renderer.material.mainTexture = texB;
mask.transform.localScale = new  Vector3 (0.1380835f, 0.1380835f, 0.1380835f);
}
}
//这个就是裁剪一张大图,变成一个个小的,贴到tile上
Vector2 TexOffset (int _idTex)
{
int a = (int)(_idTex / x);
int b = (int)(_idTex % x);
Vector2 offset = new Vector2 (b / x, (y - 1 - a) / y);
return offset;
}
Vector2 TexSize ()
{
Vector2 size = new Vector2 (1 / x, 1 / y);
return size;
}
}

Menu.cs,添加两个按钮。

using UnityEngine;
using System.Collections;
public class Menu : MonoBehaviour
{
public GameManager gameManager;
private GameManager _gameManger;
private bool start = true;void OnGUI ()
{
if (start) {
if (GUI.Button (new Rect (10, 10, 100, 50), "start")) {
start = false;
_gameManger = Instantiate (gameManager) as GameManager;
}
}
if (GUI.Button (new Rect (10, 70, 100, 50), "restart")) {
if (_gameManger != null) {
Destroy (_gameManger.gameObject);
print ("Destroy(_gameManger.gameObject);");
}
_gameManger = Instantiate (gameManager) as GameManager;
}}
}
2016-12-30 09:51:20 dickysun1987 阅读数 464

创建和使用脚本

创建C#脚本Assets > Create > C# Script

初始内容

using UnityEngine;
using System.Collections;

public class MainPlayer : MonoBehaviour {

    // Use this for initialization
    void Start () {

    }

    // Update is called once per frame
    void Update () {

    }
}

要使脚本运行,需要将脚本附加到游戏对象(GameObject)上。可以通过将脚本拖拽到hierarchy视图的特定对象上进行绑定,之后在Inspector视图上会出现新添加的脚本的组件。

测试:
在Start中 添加代码

// Use this for initialization
void Start () {
// Debug.Log is a simple command that just prints a message to Unity’s console output
    Debug.Log("I am alive!");
}

点击运行,会在 console窗口中看到打印信息。

这里写图片描述

变量和Inspector

在脚本中可以设置变量,使可编辑变量出现在inspector视图中。将变量设置为public才能在Inspector视图中显示可编辑变量。

using UnityEngine;
using System.Collections;

public class MainPlayer : MonoBehaviour {
    public string myName;

    // Use this for initialization
    void Start () {
        Debug.Log("I am alive and my name is " + myName);
    }

    // Update is called once per frame
    void Update () {

    }
}

代码会在Inspector视图当前组件中创建一个名为”My Name”的可编辑域。
编辑”My Name”之后点击运行,会在console窗口中看到打印信息。
这里写图片描述

用Components控制GameObjects

用GetComponent函数获取组件对象

void Start () {
    Rigidbody rb = GetComponent<Rigidbody>();
}

一旦获取了组件对象,就可以改变其变量。

void Start () {
    Rigidbody rb = GetComponent<Rigidbody>();

    // Change the mass of the object's Rigidbody.
    rb.mass = 10f;
}

访问其它GameObject

public class Enemy : MonoBehaviour {
    public GameObject player;

    void Start() {
        // Start the enemy ten units behind the player character.
        transform.position = player.transform.position - Vector3.forward * 10f;
    }
}

用如上方式,可以通过从Hierarchy面板向 Inspector中拖拽目标的方式给变量赋值。

用GameObject.Find可以通过名字查找特定GameObject

GameObject player;

void Start() {
    player = GameObject.Find("MainHeroCharacter");
}

还可以通过GameObject.FindWithTag、FindGameObjectsWithTag获取特定tag的一个或一组GameObject。

GameObject player;
GameObject[] enemies;

void Start() {
    player = GameObject.FindWithTag("Player");
    enemies = GameObject.FindGameObjectsWithTag("Enemy");
}
2017-01-04 16:58:08 dickysun1987 阅读数 919
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class mousedrag : MonoBehaviour
{

    private Vector3 targetScreenpos;//拖拽物体的屏幕坐标
    private Vector3 targetWorldpos;//拖拽物体的世界坐标
    private Transform target;//拖拽物体
    private Vector3 mouseScreenpos;//鼠标的屏幕坐标
    private Vector3 offset;//偏移量
    Color oricolor = Color.black;//物体本来的颜色

    void Start () 
    {
        target = transform;
    }

    void OnMouseEnter()
    {
        //当鼠标在物体上时,改变物体颜色。
        GetComponent<Renderer>().material.color = Color.blue;
    }
    void OnMouseExit()
    {
        GetComponent<Renderer>().material.color = oricolor;
    }

    //被移动物体需要添加collider组件,以响应OnMouseDown()函数
    //基本思路。当鼠标点击物体时(OnMouseDown(),函数体里面代码只执行一次),记录此时鼠标坐标和物体坐标,并求得差值。如果此后用户仍然按着鼠标左键,那么保持之前的差值不变即可。
    //由于物体坐标是世界坐标,鼠标坐标是屏幕坐标,需要进行转换。具体过程如下所示。
    IEnumerator OnMouseDown()
    {
        GetComponent<Renderer> ().material.color = Color.red;
        targetScreenpos = Camera.main.WorldToScreenPoint(target.position);  
        mouseScreenpos = new Vector3(Input.mousePosition.x,Input.mousePosition.y,targetScreenpos.z);
        offset = target.position -Camera.main.ScreenToWorldPoint(mouseScreenpos);

        while(Input.GetMouseButton(0))//鼠标左键被持续按下。
        {
            GetComponent<Renderer> ().material.color = Color.yellow;
            mouseScreenpos = new Vector3(Input.mousePosition.x,Input.mousePosition.y,targetScreenpos.z);
            targetWorldpos = Camera.main.ScreenToWorldPoint(mouseScreenpos) + offset;
            target.position = targetWorldpos;
            yield return new WaitForFixedUpdate();
        }
        GetComponent<Renderer>().material.color = Color.blue;
    }
} 
2016-10-30 21:45:44 u010832643 阅读数 1002

原文地址:Unity3D中脚本的执行顺序和编译顺序

在Unity中可以同时创建很多脚本,并且可以分别绑定到不同的游戏对象上,它们各自都在自己的生命周期中运行。与脚本有关的也就是编译和执行啦,本文就来研究一下Unity中脚本的编译和执行顺序的问题。

事件函数的执行顺序

先说一下执行顺序吧。 
官方给出的脚本中事件函数的执行顺序如下图: 
exeOrder

我们可以做一个小实验来测试一下: 
在Hierarchy视图中创建三个游戏对象,在Project视图中创建三条脚本,如下图所示,然后按照顺序将脚本绑定到对应的游戏对象上: 
struct 

三条脚本的代码完全一样,只是做了一点名称上的区分: 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
using UnityEngine;
using System.Collections;
public class Scring0 : MonoBehaviour
{
    void Awake()
    {
        Debug.Log("Script0=======Awake");
    }
 
    bool isUpdate = false;
    void Update()
    {
        if(!isUpdate)
        {
            Debug.Log("Script0=======Update");
            isUpdate =true;
        }
    }
 
    bool isLateUpdate =false;
    void LateUpdate()
    {
        if(!isLateUpdate)
        {
            Debug.Log("Script0=======LateUpdate");
            isLateUpdate =true;
        }
    }
}

播放游戏,看看它们的执行顺序。如下图所示,Awake、Update、LateUpdate,无论运行游戏多少次,它们的执行顺序是完全一样的。 
exeOrderInstance01



接着我们再做一个测试,把Script0的Update方法注释掉!! 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
using UnityEngine;
using System.Collections;
public class Script0 : MonoBehaviour
{
  
    void Awake ()
    {
        Debug.Log("Script0========Awake");
    }
  
//  bool isUpdate = false;
//  void Update ()
//  {
//      if(!isUpdate)
//      {
//          Debug.Log("Script0 ======== Update");
//          isUpdate = true;
//      }
//  }
  
    bool isLateUpdate=false;
    void LateUpdate()
    {
        if(!isLateUpdate)
        {
            Debug.Log("Script0========LateUpdate");
            isLateUpdate=true;
        }
    }
}

再次运行游戏,看看它的结果。脚本的执行顺序和以前完全一样,Script0即便删除掉了Update方法,但是它也不会直接执行LateUpdate方法,而是等待Script1和Script2中的Update方法都执行完毕以后,再去执行所有的LateUpdate方法。 
exeOrderInstance02 



通过这两个例子,我们就可以很清楚地断定,Unity后台是如何执行脚本的了。每个脚本的Awake、Start、Update、LateUpdate、FixedUpdate等等,所有的方法在后台都会被汇总到一起: 

1
2
3
4
5
6
7
后台的Awake()
{
    // 这里暂时按照上图中的脚本执行顺序,后面会谈到其实可以自定义该顺序的
    脚本2中的Awake();
    脚本1中的Awake();
    脚本0中的Awake();
}

后台的方法Awake、Update、LateUpdate等等,都是按照顺序,等所有游戏对象上脚本中的Awake执行完毕之后,再去执行Start、Update、LateUpdate等方法的。 

1
2
3
4
5
6
7
后台的Update()
{
    // 这里暂时按照上图中的脚本执行顺序,后面会谈到其实可以自定义该顺序的
    脚本2中的Update();
    脚本1中的Update();
    脚本0中的Update();
}

脚本的执行顺序

然后我们来看看这样一种情况:在脚本0的Awake方法中创建一个立方体对象,然后在脚本2的Awake方法中去获取这个立方体对象。代码如下: 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Script0.cs
using UnityEngine;
using System.Collections;
public class Script0 : MonoBehaviour
{
    void Awake ()
    {
        GameObject.CreatePrimitive(PrimitiveType.Cube);
    }
}
 
// Script2.cs
using UnityEngine;
using System.Collections;
public class Script2 : MonoBehaviour
{
    void Awake ()
    {
        GameObject go = GameObject.Find("Cube");
        Debug.Log(go.name);
    }
}

如果脚本的执行顺序是先执行Script0,然后再执行Script2,那么Script2中的Awake就可以正确地获取到该立方体对象;可是如果脚本的执行顺序是先执行Script2,然后是Script0,那么Script2肯定会报空指针错误的。

那么实际项目中的脚本会非常多,它们的先后执行顺序我们谁也不知道(有人说是按照栈结构来执行的,即后绑定到游戏对象上的脚本先执行。这一点可以从上面的例子中得到,但官方并没有这么说,还得进一步深入研究)。但一般的,建议在Awake方法中创建游戏对象或Resources.Load(Prefab)对象,然后在Start方法中去获取游戏对象或者组件,因为事件函数的执行顺序是固定的,这样就可以确保万无一失了。 
另外,Unity也提供了一个方法来设置脚本的执行顺序,在Edit -> Project Settings -> Script Execution Order菜单项中,可以在Inspector面板中看到如下图所示: 
Inspector

点击右下角的"+"将弹出下拉窗口,包括游戏中的所有脚本。脚本添加完毕后,可以用鼠标拖动脚本来为脚本排序,脚本名后面的数字也越小,脚本越靠上,也就越先执行。其中的Default Time表示没有设置脚本的执行顺序的那些脚本的执行顺序。 
Inspector2

按照上面这张图的设置,我们再来看一下控制台的输出结果,来确认一下我们的设置是否起作用(注意:把Script0脚本中的Update方法取消注释): 
exeOrderInstance03

补充说明:

下面我们针对每一个方法进行详细的说明(摘自http://www.unitymanual.com/thread-14461-1-1.html):

  1.Awake:用于在游戏开始之前初始化变量或游戏状态。在脚本整个生命周期内它仅被调用一次.Awake在所有对象被初始化之后调用,所以你可以安全的与其他对象对话或用诸如GameObject.FindWithTag()这样的函数搜索它们。每个游戏物体上的Awake以随机的顺序被调用。因此,你应该用Awake来设置脚本间的引用,并用Start来传递信息Awake总是在Start之前被调用。它不能用来执行协同程序。

  2.Start:仅在Update函数第一次被调用前调用。Start在behaviour的生命周期中只被调用一次。它和Awake的不同是Start只在脚本实例被启用时调用。你可以按需调整延迟初始化代码。Awake总是在Start之前执行。这允许你协调初始化顺序。在所有脚本实例中,Start函数总是在Awake函数之后调用。
  3.FixedUpdate:固定帧更新,在Unity导航菜单栏中,点击“Edit”-->“Project Setting”-->“Time”菜单项后,右侧的Inspector视图将弹出时间管理器,其中“Fixed Timestep”选项用于设置FixedUpdate()的更新频率,更新频率默认为0.02s。
4.Update:正常帧更新,用于更新逻辑。每一帧都执行,处理Rigidbody时,需要用FixedUpdate代替Update。例如:给刚体加一个作用力时,你必须应用作用力在FixedUpdate里的固定帧,而不是Update中的帧。(两者帧长不同)FixedUpdate,每固定帧绘制时执行一次,和update不同的是FixedUpdate是渲染帧执行,如果你的渲染效率低下的时候FixedUpdate调用次数就会跟着下降。FixedUpdate比较适用于物理引擎的计算,因为是跟每帧渲染有关。Update就比较适合做控制。
  5.LateUpdate:在所有Update函数调用后被调用,和fixedupdate一样都是每一帧都被调用执行,这可用于调整脚本执行顺序。例如:当物体在Update里移动时,跟随物体的相机可以在LateUpdate里实现。LateUpdate,在每帧Update执行完毕调用,他是在所有update结束后才调用,比较适合用于命令脚本的执行。官网上例子是摄像机的跟随,都是在所有update操作完才跟进摄像机,不然就有可能出现摄像机已经推进了,但是视角里还未有角色的空帧出现。

  6.OnGUI:在渲染和处理GUI事件时调用。比如:你画一个button或label时常常用到它。这意味着OnGUI也是每帧执行一次。

  7.Reset:在用户点击检视面板的Reset按钮或者首次添加该组件时被调用。此函数只在编辑模式下被调用。Reset最常用于在检视面板中给定一个默认值。

  8.OnDisable:当物体被销毁时 OnDisable将被调用,并且可用于任意清理代码。脚本被卸载时,OnDisable将被调用,OnEnable在脚本被载入后调用。注意: OnDisable不能用于协同程序。

  9.OnDestroy:当MonoBehaviour将被销毁时,这个函数被调用。OnDestroy只会在预先已经被激活的游戏物体上被调用。注意:OnDestroy也不能用于协同程序。





脚本的编译顺序

关于脚本的编译顺序很是头疼,官方的说法有点模糊,请看官方的解释: 
phaseFour

由于脚本的编译顺序会涉及到特殊文件夹,比如上面提到的Plugins、Editor还有Standard Assets等标准的资源文件夹,所以脚本的放置位置就非常重要了。下面用一个例子来说明不同文件夹中的脚本的编译顺序:

fileStruct

实际上,如果你细心的话会发现,如果在你的项目中建立如上图所示的文件夹层次结构时,编译项目之后会在项目文件夹中生成一些文件名中包含Editor、firstpass这些字样的项目文件。比如按照上图的文件夹结构,我们打开项目文件夹来看一下产生的项目文件是什么样的? 
fileStruct01

下面就来详细探讨一下这些个字样是什么意思?它们与脚本的编译顺序有着怎样的联系?


1、首先从脚本语言类型来看,Unity3d支持3种脚本语言,都会被编译成CLI的DLL

如果项目中包含有C#脚本,那么Unity3d会产生以Assembly-CSharp为前缀的工程,名字中包含”vs”的是产生给Vistual Studio使用的,不包含”vs”的是产生给MonoDevelop使用的。 

项目中的脚本语言 工程前缀 工程后缀
C# Assembly-CSharp csproj
UnityScript Assembly-UnityScript unityproj
Boo Assembly-Boo booproj

如果项目中这三种脚本都存在,那么Unity将会生成3种前缀类型的工程。

2、对于每一种脚本语言,根据脚本放置的位置(其实也部分根据脚本的作用,比如编辑器扩展脚本,就必须放在Editor文件夹下),Unity会生成4中后缀的工程。其中的firstpass表示先编译,Editor表示放在Editor文件夹下的脚本。

在上面的示例中,我们得到了两套项目工程文件:分别被Virtual Studio和MonoDevelop使用(后缀包不包含vs),为简单起见,我们只分析vs项目。得到的文件列表如下: 
Assembly-CSharp-filepass-vs.csproj 
Assembly-CSharp-Editor-filepass-vs.csproj 
Assembly-CSharp-vs.csproj 
Assembly-CSharp-Editor-vs.csproj

根据官方的解释,它们的编译顺序如下: 
(1)所有在Standard Assets、Pro Standard Assets或者Plugins文件夹中的脚本会产生一个Assembly-CSharp-filepass-vs.csproj文件,并且先编译; 
(2)所有在Standard Assets/Editor、Pro Standard Assets/Editor或者Plugins/Editor文件夹中的脚本产生Assembly-CSharp-Editor-filepass-vs.csproj工程文件,接着编译; 
(3)所有在Assets/Editor外面的,并且不在(1),(2)中的脚本文件(一般这些脚本就是我们自己写的非编辑器扩展脚本)会产生Assembly-CSharp-vs.csproj工程文件,被编译; 
(4)所有在Assets/Editor中的脚本产生一个Assembly-CSharp-Editor-vs.csproj工程文件,被编译。

之所以按照这样建立工程并按此顺序编译,也是因为DLL间存在的依赖关系所决定的。

好了,到此为止,我们可以很容易地判断出上面举的实例中,脚本的编译顺序(实际上,我已经把顺序写在了脚本的文件名中了)

unity 3d 鼠标旋转物体脚本

博文 来自: dickysun1987
没有更多推荐了,返回首页