徐麟家的博客

站在巨人的肩膀上

点评

这两个工厂设计模式都体现了依赖倒置的设计原则,将具体创建产品的过程延迟到子类实现,从而为工厂提供了一定的拓展灵活性。相对而言工厂方法比较实用些,抽象工厂是在工厂方法上再多了一层对产品的抽象;抽象工厂因为多了一层产品抽象层,可能会比较臃肿,类个数会比较庞大,总是会结合反射一起使用。

点评

在6个设计模式中,我认为单一职责和合成复用,我认为是最基础的,但却是最有用的;同时也是程序设计初学者最容易出问题的设计的两个设计原则. 对此我的经验总结如下:

1.单一职责,告诉我们类的功能要单一,所以每个类存在自己的边界,必须分清楚各个类的功能划分,事件中可以一步一步来,化繁为简,逐步求精,当然也需要把握类的颗粒度。对此我的编程经验总结是:

 a.对于某些实体类,根据现实世界映射到类,往往可以得到一个直观的类划分。
 b.先划分一个初步的功能结构,讲究够用就行,等到需求要在拓展,再整理划分。
 c.“奥卡姆剃刀”-当你发现以上两条经验得出的类不是那个简单,明了,是否是你的类不够简单,可以在划分。

2.合成复用原则 ,我认为我们要成分的理解类之间的几种关系,从强到弱分别是:依赖关系,关联关系,聚集关系,组成关系。然而初学面向对象的同学可能总是在继承中不能自拔,一旦出现某种设计,首先使用继承,这是不对的。应该综合实际情况,而且组合要优先于继承,好拓展啊!

经验总结

a.认清事物之间的关系,可以画几个草图,问问自己他们是 is 关系,还是has关系。
b.当继承的子类越来越多,子类之间接近以乘数形式增加,可以想象类是否可分成两个或多个有关系的类,而不是继承。

cpu:
通常对于游戏性能最直观的感受就是流畅性,表示流畅性最直接的指标就是帧率。不同种游戏类型对于帧率的要求并不一致,比如即时战斗的动作类游戏对于帧率更高的要求。为了打成对应的帧率,需要将CPU耗时控制住一定的数值范围内,对于30帧,总耗时需要控制在33ms以下,对应的模块也需要有对应的耗时范围,如下表所示,不过不同的游戏类型会有所差异。对于60帧或者更高帧率,依次换算对于数值即可。

内存:
在我们了解了内存相关的各项参数的含义之后,知道了避免游戏闪退的重点在于控制PSS内存峰值。而PSS内存的大头又在于Reserved Total中的资源内存和Mono堆内存。对于使用Lua的项目来说,还应关注Lua内存。
PSS内存峰值控制在硬件总内存的0.5-0.6倍以下的时候,闪退风险才较低。
而对于大多数项目而言,PSS内存大约高于Reserved Total 200MB-300MB左右,故2G设备的Reserved Total应控制在700MB以下、3G设备则控制在1G以下。

渲染:
随着游戏对画质效果的要求越来越高,越来越多的项目遇到的性能瓶颈会来自于GPU的压力,GPU的压力会受到帧率,分辨率,三角形面片数,后处理,Shader复杂度,Overdraw等多方面的影响,因此我们对于不同分档的机型需要做一些对应的调整,在不同档位的机型上要做较为细致的区分,从而达到越高端的机型画质表现越好,而在低端机型上又能保证一定的流畅性。下表中可以当做一个模板,对于不同的游戏类型的需求可以再进一步调整,其中绿色的数值如果能达到的话会更优。

堆排序算法复杂度为【O(nlgn)】,分最大堆排序和最小堆排序:最大堆定义为所有父节点比左右子节点大。反之亦反,就是最小堆了!
这里我用最小堆来阐述算法。首先需要了解下完全二叉数结构,可以把一个数组看出一个完全二叉树。数组第1个元素看做为根节点,
可定义:
1、 左子树Left(i) = i * 2
2、 右子树Right(i) = i * 2 + 1
3、 父节点Parent(i) = i / 2 

关键的子程序:
a、最小堆操作:堆下降,递归使得父节点小于自己的两个子节点
b、建堆操作(循环调用最小堆操作,获得数组a中n个数据的最小值)

算法思想主要分为2个步骤:
(1)构建堆,使得a[0](第一个数为最小值)
(2)交换a[0]<=>a[n-1](a[n-1] 不计入堆中,只有a[0]是不满足最小堆的,其他元素都不变)
(3)a[0]执行最小堆操作,回到(2)步骤直到数组长度为0
*C#的数组是从0开始的,二叉树映射数组是从1开始的。于是所有访问数组的时候都-1
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
public static class SortingSupport
{
public delegate bool CompareLess<T>(T t1, T t2);

static void Swap<T>(T[] a, int i, int j)
{
T temp = a[i];
a[i] = a[j];
a[j] = temp;
}

#region 堆排序
public static void HeapSort<T>(T[] a, CompareLess<T> cp)
{
int heapsize = a.Length;
BuildMinHeap(a, cp);
for (int i = a.Length; i > 1; i--)
{
Swap(a, 0, i - 1);
heapsize--;
MinHeapify(a, 1, heapsize, cp);
}
}

//建堆
static void BuildMinHeap<T>(T[] a, CompareLess<T> cp)
{
int heapSize = a.Length;
for (int i = a.Length / 2; i > 0; i--)
MinHeapify(a, i, heapSize, cp);
}

//堆下降
static void MinHeapify<T>(T[] a, int i, int heapSize, CompareLess<T> cp)
{
int l = i << 1;
int r = (i << 1) + 1;
int min_pos = -1;
if (l <= heapSize && cp(a[l - 1], a[i - 1]))
min_pos = l;
else
min_pos = i;
if (r <= heapSize && cp(a[r - 1], a[min_pos - 1]))
min_pos = r;
if (min_pos != i)
{
Swap(a, min_pos - 1, i - 1);
MinHeapify(a, min_pos, heapSize, cp);
}
}
#endregion
}

关于排序算法,不得不说排序算法。排序算法被认为是冒泡算法的优化,以优异的平均算法复杂度【O(nlgn)】而著名。
算法思想主要分为2个步骤:
(1)划分数组a[p..r]为两个子数组a[p,i-1],a[i+1,r],使得a[p,i-1]中的数都小于等于a[i],a[i+1,r]中的数都大于等于a[i] 
(2)递归对a[p,i-1],a[i+1,r]分别排序
*算法核心是如何分割为比基准数小的数组和比基准数大的数组,对应一下Partition方法
下面是代码部分:
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
public static class SortingSupport 
{
public delegate bool CompareLess<T>(T t1, T t2);

static void Swap<T>(T[] a, int i, int j)
{
T temp = a[i];
a[i] = a[j];
a[j] = temp;
}

#region 快速排序
public static void QuickSort<T>(T[] a, CompareLess<T> cp)
{
QSort(a, 0, a.Length - 1, cp);
}

/// <summary>
/// *递归保证子数组有序
/// </summary>
static void QSort<T>(T[] a,int p,int r, CompareLess<T> cp)
{
if (p >= r)
return;
int i = Partition(a, p, r, cp);
QSort(a, p, i - 1, cp);
QSort(a, i + 1, r, cp);
}

/// <summary>
/// *分割数字保证所有 a[p..i-1] <= p[i] && a[i+1..r] >= p[i]
/// </summary>
static int Partition<T>(T[] a,int p, int r, CompareLess<T> cp)
{
T x = a[r];
int i = p - 1;
for(int j = p;j < r ;j ++)
{
if(cp(a[j],x))
{
i++;
Swap(a, i, j);
}
}
Swap(a, i + 1, r);
return i + 1;
}
#endregion
}

EventSystem作为控制类操作BaseInput,BaseRaycaster

BaseInput 相当于一个unity Input 和 UGUI BaseInputModule 的媒介类,BaseInputModule只跟BaseInput交互。
StandaloneInputModule->PointerInputModule,TouchInputModule->PointerInputModule,PointerInputModule->BaseInputModule
EventSystem 调用 BaseInputModule.Process() 处理来自unity的所有功能

RaycasterManager静态类管理所有的GraphicRaycaster,BaseRaycaster 激活是注册自己到RaycasterManager
GraphicRaycaster -> BaseRaycaster,Physics2DRaycaster->PhysicsRaycaster->BaseRaycaster

BaseRaycaster的关键,Raycast方法获得当前节点下Canvas的所有Graphic,并且进行检测触发排序(最终是Graphic.Raycast())。
最总EventSystem.RaycastComparer方法比较,排序获得第一个graphic作为pointerCurrentRaycast。

0%