unity示例学习2

飞行射击游戏3D中发现的问题

  1. c#的字串==为直接判断值而java的字串==对于new俩个一样的是不等!为比地址
  2. 旋转基于中心!赋予上层对象时小心中心不一定在子对象中心
  3. 物理的velocity表速度,在start给予一次时一直保持

关于yield return XXXX 的原理:

划重点:IEnumerale和IEnumerator 枚举器和迭代器

一. 首先明白c#中的foreach遍历枚举器

这个东西初见源自枚举集合————为了自己实现的数据结构类能使用foreach遍历。而实现foreach首先需要本类实现IEnumerable接(其实现等同于可枚举集合)查看该接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
using System.Runtime.CompilerServices;
namespace System.Collections.Generic
{
//
// 摘要:
// 公开枚举数,该枚举数支持在指定类型的集合上进行简单迭代。若要浏览此类型的.NET Framework 源代码,请参阅参考源。
//
// 类型参数:
// T:
// 要枚举的对象的类型。此类型参数是协变。即可以使用指定的类型或派生程度更高的类型。有关协变和逆变的详细信息,请参阅 泛型中的协变和逆变。
[TypeDependencyAttribute("System.SZArrayHelper")]
public interface IEnumerable<out T> : IEnumerable
{
//
// 摘要:
// 返回一个循环访问集合的枚举器。
//
// 返回结果:
// 用于循环访问集合的枚举数。
IEnumerator<T> GetEnumerator();
}
}

PS:针对类前加[]语法还没深究,估计和UI显示有关

好的,就一个方法IEnumerator GetEnumerator();这个方法用来返回一个实现了IEnumerator 枚举器 (自建类) 在自己的类中该枚举器需要自己创建一个类继承接口以实现。直接放个示例代码演示一般方法:

  1. 创建Tree(一个自己写的树数据结构类)的枚举器:
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
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
class TreeEnumerator<TItem> : IEnumerator<TItem> where TItem : IComparable<TItem>
{
private Tree<TItem> currentData = null;
private TItem currentItem = default(TItem);
private Queue<TItem> enumData = null;
public TreeEnumerator(Tree<TItem> data)
{
this.currentData = data;
}
object IEnumerator.Current
{
get
{
throw new NotImplementedException();
}
}
TItem IEnumerator<TItem>.Current
{
get
{
if(this.enumData == null)
{
throw new InvalidOperationException("Use MoveNext before calling Current");
}
return this.currentItem;
}
}
void IDisposable.Dispose()
{
// throw new NotImplementedException();
}
bool IEnumerator.MoveNext()
{
if(this.enumData == null)
{
this.enumData = new Queue<TItem>();
populate(this.enumData, this.currentData);
}
if(this.enumData.Count > 0)
{
this.currentItem = this.enumData.Dequeue();
return true;
}
return false;
}
private void populate(Queue<TItem> enumQueue, Tree<TItem> tree)
{
if (tree.LeftTree != null)
{
populate(enumQueue, tree.LeftTree);
}
enumQueue.Enqueue(tree.NodeData);
if (tree.RightTree != null)
{
populate(enumQueue, tree.RightTree);
}
}
void IEnumerator.Reset()
{
throw new NotImplementedException();
}
}
  1. 在Tree中使用:

    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
    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
    public class Tree<TItem> : IEnumerable<TItem> where TItem : IComparable<TItem>
    {
    public TItem NodeData { get; set; }
    public Tree<TItem> LeftTree { get; set; }
    public Tree<TItem> RightTree { get; set; }
    public Tree(TItem nodeValue)
    {
    this.NodeData = nodeValue;
    this.LeftTree = null;
    this.RightTree = null;
    }
    public void Insert(TItem newItem)
    {
    TItem currentNodeValue = this.NodeData;
    if (currentNodeValue.CompareTo(newItem) > 0)
    {
    // Insert the item into the left subtree
    if (this.LeftTree == null)
    {
    this.LeftTree = new Tree<TItem>(newItem);
    }
    else
    {
    this.LeftTree.Insert(newItem);
    }
    }
    else
    {
    // Insert the new item into the right subtree
    if (this.RightTree == null)
    {
    this.RightTree = new Tree<TItem>(newItem);
    }
    else
    {
    this.RightTree.Insert(newItem);
    }
    }
    }
    public string WalkTree()
    {
    string result = "";
    if (this.LeftTree != null)
    {
    result = this.LeftTree.WalkTree();
    }
    result += String.Format($" {this.NodeData.ToString()} ");
    if (this.RightTree != null)
    {
    result += this.RightTree.WalkTree();
    }
    return result;
    }
    IEnumerator<TItem> IEnumerable<TItem>.GetEnumerator()
    {
    return new TreeEnumerator<TItem>(this);
    }
    IEnumerator IEnumerable.GetEnumerator()
    {
    throw new NotImplementedException();
    }
    }
  2. 这样就可以用了

    1
    2
    3
    4
    5
    Tree<int> tree1 = new Tree<int>(10);
    foreach(int item in tree1)
    {
    Console.WriteLine(item);
    }

以上就是实现foreach遍历可枚举集合的创建方法,但是…..太复杂了!所以有了一种 迭代器 实现IEnumeratorGetEnumerator()的简便方法,而迭代器就和 yield 关系大了!

二.迭代器与yield

官方定义:迭代器是能生成已排序值序列的代码块。也就是说,迭代器是对序列的一种描述C#用它自动生成枚举器
实现方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
IEnumerator<TItem> IEnumerable<TItem>.GetEnumerator()
{
if(this.LeftTree!=null)
{
foreach(TItem item in this.LeftTree)
{
yield return item;
}
}
yield return this.NodeData;
if(this.RightTree!=null)
{
foreach(TItem item in this.RightTree)
{
yield return item;
}
}
}

这段代码是对上面提到的一般枚举器实现方式的重写,只需关注yield即可:它不反回IEnumerator对象,而是 每次将方法叫停,讲一个值传回调用者,当调用者需要下一个值时,就从上次停止处继续,数据耗尽时停止

三.unity中的用法

  1. WWW - after Updates happen for all game objects; check the isDone flag. If true, call the IEnumerator’s MoveNext() function;
  2. WaitForSeconds - after Updates happen for all game objects; check if the time has elapsed, if it has, call MoveNext();
  3. null or some unknown value - after Updates happen for all game objects; Call MoveNext();
  4. WaitForEndOfFrame - after Render happens for all cameras; Call MoveNext().
    有一篇博客写的很实践:点这里
    在这里我简单总结本示例需要用到的:
    Unity使用 StartCoroutine(routine: IEnumerator): Coroutine 启动协程,参数必须是 IEnumerator 对象。在函数内使用前面介绍 yield 关键字返回 IEnumerator 对象,Unity 中实现了 YieldInstruction 作为 yield 返回的基类,有 Cortoutine, WaitForSecondes, WaitForEndOfFrame, WaitForFixedUpdate, WWW 几个子类实现。StartCoroutine 将 传入的 IEnumerator 封装为 Coroutine 返回,引擎会对 Corountines 存储和检查 IEnumerator 的 Current值。

总之,使用是就是用StartCoroutine调用一个带yield返回的函数,这个函数会按照yield后的规定慢慢一段一段执行完,该过程位于Update之后,永远产生一波波敌人时只用在start调用StartCoroutine启动迭代器让其在update后面默默运行就好了