Unity大型场景程序化生成及优化技术—FPS迷宫生成和优化

1、知名游戏中的大型场景生成

场景程序化生成技术是一个广泛应用在游戏开发中的技术,较早的使用这类技术有名游戏《暗黑破坏神》系列,无论是1代、2代、3代,都使用了这个技术,如下图1-1:每次进入野外场景的时候能看到随机的场景(随机地形、随机怪物、随机藏宝点)

图1-1 《暗黑破坏神3》随机地下城
另外一个知名游戏是手游《列王的纷争(COK)》,在这个游戏里的世界地图,也是程序化生成的,如下图1-2中所示,手机窗口中只能看到这个庞大地图中的一小点,地图上面会有程序随机生成的山脉、湖泊等景观,加上资源点、怪物等等。

图1-2 《COK》中的世界地图
还有一个可能是用程序化生成场景技术的游戏中技术点最多的一个游戏,它的名字叫《我的世界》,如图1-3,这个游戏世界是由海量的方块组成的,初步看起来逻辑比较简单,实际上,为了能生成拟真度很高的场景以及能让游戏流畅的跑在各个手机上,需要不少功夫。

图1-3《我的世界》中的无限场景
我们正在制作一个FPS游戏项目,其中也用到了程序化生成场景的技术,如图1-4,本文的主要内容就是给大家介绍一下这个FPS游戏项目中我们具体是怎么用程序生成迷宫场景以及如何对迷宫场景的显示进行优化。

图1-4 FPS项目中迷宫场景

2、迷宫数据生成算法

首先,项目的策划目标提出了明确的需求,要生成一个比较复杂的迷宫,纵横通道和房间交错的地下场景,每次玩家进入到场景中进行互动的时候,场景都要有不同,但我们能控制场景的规模大小及其他一些自定义的参数。我们实验了几种常用的迷宫生成算法,分别是如下几种:

  1. Recursive division (递归分割算法)

  2. Recursive backtracker ( 递归回溯,也是深度优先算法)

  3. Randomized Prim’s algorithm(随机Prim算法)

    这几种算法实验的过程比较简单,分别用算法生成随机迷宫,然后在Unity用简单的方式根据数据绘制出整个迷宫,最后让策划去比较选择哪种算法下的迷宫是项目最想要的,本文将逐一介绍一下。

2.1、迷宫算法的基本参数

下面的图2-1,展示的是一个规模为30*20的迷宫初始化后的视图,其中白色的格子代表房间,黑色的网格线代表墙,因为还未进行任何后续迷宫算法处理,所以每个房间都是用墙隔开的,都无法通行,本文提到的三种算法,都是在这样一个初始状态下,通过不同的方式把房间打通得到最终的迷宫。

图2-1迷宫数据的基本组成和参数

2.2、递归分割算法

递归分割算法的效果如图2-2所示:

图2-2 递归分割算法的效果
递归分割算法跟其他两个算法一样,首先是继承了公共父类MazeGenerator,它定义了基本的迷宫数据结构,迷宫规模,还有根据迷宫数据绘制场景的成员函数GenMazeScene其代码如下,其中int [,]mMazeData这个三维数组是要生成的迷宫数据,前两个维度下标分别表示行和列,第三个维度

1.using UnityEngine;
2.using UnityEngine.UI;
3.
4./// <summary>
5./// 4面墙的类型
6./// </summary>
7.enum RectWallType
8.{
9.     Left = 0,
10.    Up = 1,
11.    Right = 2,
12.    Down = 3,
13.    WallNum = 4,
14.}
15.
16./// <summary>
17./// 6面墙类型
18./// </summary>
19.enum HexWallType
20.{
21.    Up = 0,         //正上方:不管奇偶c,r+1
22.    Down = 1,       //正下方:不管奇偶c,r-1
23.    LeftUp = 2,     //左上方:本列是奇数列c-1,r+1,本列是偶数列c-1,r
24.    RightUp = 3,    //右上方:本列是奇数列c+1,r+1,本列是偶数列c+1,r
25.    LeftDown = 4,   //左下方:本列是奇数列c-1,r,本列是偶数列c-1,r-1
26.    RightDown = 5,  //右下方:本列是奇数列c+1,r,本列是偶数列c+1,r-1
27.    WallNum = 6,
28.}
29.
30./// <summary>
31./// 房间坐标类型
32./// </summary>
33.public class RoomCoordinate
34.{
35.    public int row; //行
36.    public int col; //列
37.
38.    public RoomCoordinate(int r, int c)
39.    {
40.        row = r;
41.        col = c;
42.    }
43.}
44.
45./// <summary>
46./// 迷宫生成器父类
47./// </summary>
48.public abstract class MazeGenerator : MonoBehaviour
49.{
50.    protected int[,,] mMazeData;          //迷宫数据
51.
52.    protected GameObject mMazePrefab;
53.
54.    public int mRowCount = 10;          //迷宫行个数
55.
56.    public int mColCount = 10;          //迷宫列个数
57.
58.    public abstract void GenMazeData();
59.
60.    /// <summary>
61.    /// 生成场景
62.    /// </summary>
63.    public virtual void GenMazeScene()
64.    {
65.        for (int r = 0; r < mRowCount; r++)
66.        {
67.            for (int c = 0; c < mColCount; c++)
68.            {
69.                GameObject o = Instantiate<GameObject>(mMazePrefab);
70.                o.transform.position = new Vector3(c * 4, 0, r * 4);
71.
72.                if (mMazeData[r, c, (int)RectWallType.Left] == 1)
73.                {
74.                    o.transform.Find("Left").gameObject.SetActive(false);
75.                }
76.
77.                if (mMazeData[r, c, (int)RectWallType.Right] == 1)
78.                {
79.                    o.transform.Find("Right").gameObject.SetActive(false);
80.                }
81.
82.                if (mMazeData[r, c, (int)RectWallType.Up] == 1)
83.                {
84.                    o.transform.Find("Up").gameObject.SetActive(false);
85.                }
86.
87.                if (mMazeData[r, c, (int)RectWallType.Down] == 1)
88.                {
89.                    o.transform.Find("Down").gameObject.SetActive(false);
90.                }
91.            }
92.        }
93.    }
94.}

然后是递归分割算法的代码,它的主要代码是RecursiveDiv,这是一个递归函数,可以这样理解:把整个迷宫作为一个大房间,每次递归时把当前的房间递归分成4个小房间,并随机打通其中的3个房间,直到这个待分割的房间为一个基本房间单元为止。代码如下

1.public class RecursiveDivisionGen : MazeGenerator
2.{
3.    void Start()
4.    {
5.        if (mRowCount <= 0 || mColCount <= 0)
6.            return;
7.
8.        //初始化房间数据,默认值为0表示封闭的墙
9.        mMazeData = new int[mRowCount, mColCount, (int)RectWallType.WallNum];
10.
11.        mMazePrefab = Resources.Load<GameObject>("RectCell");
12.
13.        GenMazeData();
14.
15.        GenMazeScene();
16.    }
17.
18.    /// <summary>
19.    /// 递归分割函数
20.    /// </summary>
21.    /// <param name="r1">起始行号</param>
22.    /// <param name="r2">结束行号</param>
23.    /// <param name="c1">起始列号</param>
24.    /// <param name="c2">结束列号</param>
25.    private void RecursiveDiv(int r1, int r2, int c1, int c2)
26.    {
27.        if (r1 < r2 && c1 < c2)
28.        {
29.            int rm = Random.Range(r1, r2 - 1); //取两中间的随机,不包括两端
30.            int cm = Random.Range(c1, c2 - 1);
31.
32.            int cd1 = Random.Range(c1, cm + 1);
33.            int cd2 = Random.Range(cm + 1, c2);
34.            int rd1 = Random.Range(r1, rm + 1);
35.            int rd2 = Random.Range(rm + 1, r2);
36.
37.            int d = Random.Range(0, 4); //随机4个象限
38.
39.            if (d == 0)
40.            {
41.                mMazeData[rd2, cm, (int)RectWallType.Right] = 1;
42.                mMazeData[rd2, cm + 1, (int)RectWallType.Left] = 1;
43.                mMazeData[rm, cd1, (int)RectWallType.Up] = 1;
44.                mMazeData[rm + 1, cd1, (int)RectWallType.Down] = 1;
45.                mMazeData[rm, cd2, (int)RectWallType.Up] = 1;
46.                mMazeData[rm + 1, cd2, (int)RectWallType.Down] = 1;
47.            }
48.            else if (d == 1)
49.            {
50.                mMazeData[rd1, cm, (int)RectWallType.Right] = 1;
51.                mMazeData[rd1, cm + 1, (int)RectWallType.Left] = 1;
52.                mMazeData[rm, cd1, (int)RectWallType.Up] = 1;
53.                mMazeData[rm + 1, cd1, (int)RectWallType.Down] = 1;
54.                mMazeData[rm, cd2, (int)RectWallType.Up] = 1;
55.                mMazeData[rm + 1, cd2, (int)RectWallType.Down] = 1;
56.            }
57.            else if (d == 2)
58.            {
59.                mMazeData[rd1, cm, (int)RectWallType.Right] = 1;
60.                mMazeData[rd1, cm + 1, (int)RectWallType.Left] = 1;
61.                mMazeData[rd2, cm, (int)RectWallType.Right] = 1;
62.                mMazeData[rd2, cm + 1, (int)RectWallType.Left] = 1;
63.                mMazeData[rm, cd2, (int)RectWallType.Up] = 1;
64.                mMazeData[rm + 1, cd2, (int)RectWallType.Down] = 1;
65.            }
66.            else if (d == 3)
67.            {
68.                mMazeData[rd1, cm, (int)RectWallType.Right] = 1;
69.                mMazeData[rd1, cm + 1, (int)RectWallType.Left] = 1;
70.                mMazeData[rd2, cm, (int)RectWallType.Right] = 1;
71.                mMazeData[rd2, cm + 1, (int)RectWallType.Left] = 1;
72.                mMazeData[rm, cd1, (int)RectWallType.Up] = 1;
73.                mMazeData[rm + 1, cd1, (int)RectWallType.Down] = 1;
74.            }
75.
76.            RecursiveDiv(r1, rm, c1, cm);
77.            RecursiveDiv(r1, rm, cm + 1, c2);
78.            RecursiveDiv(rm + 1, r2, cm + 1, c2);
79.            RecursiveDiv(rm + 1, r2, c1, cm);
80.        }
81.        else if (r1 < r2) // c1 == c2
82.        {
83.            int rm = Random.Range(r1, r2);
84.
85.            mMazeData[rm, c1, (int)RectWallType.Up] = 1;
86.            mMazeData[rm + 1, c1, (int)RectWallType.Down] = 1;
87.
88.            RecursiveDiv(r1, rm, c1, c1);
89.            RecursiveDiv(rm + 1, r2, c1, c1);
90.        }
91.        else if (c1 < c2) // r1 == r2
92.        {
93.            int cm = Random.Range(c1, c2);
94.
95.            mMazeData[r1, cm, (int)RectWallType.Right] = 1;
96.            mMazeData[r1, cm + 1, (int)RectWallType.Left] = 1;
97.
98.            RecursiveDiv(r1, r1, c1, cm);
99.            RecursiveDiv(r1, r1, cm + 1, c2);
100.        }
101.    }
102.
103.    public override void GenMazeData()
104.    {
105.        int r1 = 0;
106.        int r2 = mRowCount - 1;
107.        int c1 = 0;
108.        int c2 = mColCount - 1;
109.
110.        RecursiveDiv(r1, r2, c1, c2);
111.    }
112.}

2.3、深度优先算法

深度优先算法得到的迷宫场景有一条比较明显的主路,岔路较少且浅,如下图2-3所示:

图2-3 深度优先算法效果
相对应的核心代码如下,与递归分割算法不同的是,增加了房间栈用以记录访问过的房间(访问先后的路径)。

1./// <summary>
2./// 深度优先生成算法
3./// </summary>
4.public class RecursiveBackTrackerGen : MazeGenerator
5.{
6.    const int ROOM_SIGN_IDX = (int)RectWallType.WallNum;
7.
8.    private Stack<RoomCoordinate> mVisitedRooms = new Stack<RoomCoordinate>(); //已访问的房间集合
9.
10.    void Start()
11.    {
12.        if (mRowCount <= 0 || mColCount <= 0)
13.            return;
14.
15.        mMazePrefab = Resources.Load<GameObject>("RectCell");
16.
17.        mMazeData = new int[mRowCount, mColCount, (int)RectWallType.WallNum + 1];
18.
19.        GenMazeData();
20.
21.        GenMazeScene();
22.    }
23.
24.    public override void GenMazeData()
25.    {
26.        int r = Random.Range(0, mRowCount);
27.        int c = Random.Range(0, mColCount);
28.
29.        mVisitedRooms.Push(new RoomCoordinate(r, c));
30.
31.        List<RectWallType> check = new List<RectWallType>((int)RectWallType.WallNum);
32.
33.        while (mVisitedRooms.Count > 0)
34.        {
35.            mMazeData[r, c, ROOM_SIGN_IDX] = 1;
36.
37.            check.Clear();
38.
39.            if (c > 0 && mMazeData[r, c - 1, ROOM_SIGN_IDX] == 0)
40.            {
41.                check.Add(RectWallType.Left);
42.            }
43.
44.            if (r > 0 && mMazeData[r - 1, c, ROOM_SIGN_IDX] == 0)
45.            {
46.                check.Add(RectWallType.Down);
47.            }
48.
49.            if (c < mColCount - 1 && mMazeData[r, c + 1, ROOM_SIGN_IDX] == 0)
50.            {
51.                check.Add(RectWallType.Right);
52.            }
53.
54.            if (r < mRowCount - 1 && mMazeData[r + 1, c, ROOM_SIGN_IDX] == 0)
55.            {
56.                check.Add(RectWallType.Up);
57.            }
58.
59.            //检查是否有一个有效的未访问房间可以继续探索
60.            if (check.Count > 0)
61.            {
62.                mVisitedRooms.Push(new RoomCoordinate(r, c));
63.
64.                RectWallType move_dir = check[Random.Range(0, check.Count)];
65.                if (move_dir == RectWallType.Left)
66.                {
67.                    mMazeData[r, c, (int)RectWallType.Left] = 1;
68.                    c--;
69.                    mMazeData[r, c, (int)RectWallType.Right] = 1;
70.                }
71.                else if (move_dir == RectWallType.Up)
72.                {
73.                    mMazeData[r, c, (int)RectWallType.Up] = 1;
74.                    r++;
75.                    mMazeData[r, c, (int)RectWallType.Down] = 1;
76.                }
77.                else if (move_dir == RectWallType.Right)
78.                {
79.                    mMazeData[r, c, (int)RectWallType.Right] = 1;
80.                    c++;
81.                    mMazeData[r, c, (int)RectWallType.Left] = 1;
82.                }
83.                else if (move_dir == RectWallType.Down)
84.                {
85.                    mMazeData[r, c, (int)RectWallType.Down] = 1;
86.                    r--;
87.                    mMazeData[r, c, (int)RectWallType.Up] = 1;
88.                }
89.            }
90.            else
91.            {
92.                var room = mVisitedRooms.Pop();
93.                r = room.row;
94.                c = room.col;
95.            }
96.        }
97.    }
98.}
99.

2.4、Prim算法特点

Prim算法的效果如下图2-4所示,相对前面两个算法来说,岔路较多,最难走。

图2-4 Prim算法效果
除了矩形迷宫,还可以用Prim算法实现六边形迷宫,算法思路和四边形迷宫基本一致,下面是六边形迷宫的效果图:


图2-5 Prim算法实现蜂巢(六边形)迷宫
Prim算法的具体实现代码如下,主要思路也是类似于深度优先算法,但不是找一个未访问的房间打通,而是找已访问的房间打通,如果找到死路,不是回到上一个路径点,而是随机在周围待处理房间里随机。

1./// <summary>
2./// Prim算法,迷宫的房间坐标是左下角是0,0,X正方向是右,Z正方向是上
3./// </summary>
4.public class PrimRectGen : MazeGenerator
5.{
6.    public const int ROOM_SIGN_IDX = (int)RectWallType.WallNum;    //房间标记位索引0-3为墙
7.
8.    private List<RoomCoordinate> mWaitProcRooms = new List<RoomCoordinate>();  //待处理房间集合
9.
10.    void Start()
11.    {
12.        if (mRowCount <= 0 || mColCount <= 0)
13.            return;
14.
15.        mMazeData = new int[mRowCount, mColCount, (int)RectWallType.WallNum + 1];
16.
17.        mMazePrefab = Resources.Load<GameObject>("RectCell");
18.
19.        GenMazeData();
20.
21.        GenMazeScene();
22.    }
23.
24.    /// <summary>
25.    /// 生成迷宫,标记位0表示未访问、1表示已访问,2表示已添加到待处理列表
26.    /// </summary>
27.    public override void GenMazeData()
28.    {
29.        int startRow = Random.Range(0, mRowCount);
30.        int startCol = Random.Range(0, mColCount);
31.
32.        mWaitProcRooms.Add(new RoomCoordinate(startRow, startCol));
33.
34.        List<RectWallType> check = new List<RectWallType>((int)RectWallType.WallNum);
35.
36.        while (mWaitProcRooms.Count > 0)
37.        {
38.            int i = Random.Range(0, mWaitProcRooms.Count);
39.            int r = mWaitProcRooms[i].row;
40.            int c = mWaitProcRooms[i].col;
41.
42.            mMazeData[r, c, ROOM_SIGN_IDX] = 1; //这个房间被访问
43.
44.            int lastIdx = mWaitProcRooms.Count - 1;
45.            mWaitProcRooms[i] = mWaitProcRooms[lastIdx];
46.            mWaitProcRooms.RemoveAt(lastIdx);
47.
48.            //左边
49.            if (c > 0)
50.            {
51.                if (mMazeData[r, c - 1, ROOM_SIGN_IDX] == 1)
52.                    check.Add(RectWallType.Left);
53.                else if (mMazeData[r, c - 1, ROOM_SIGN_IDX] == 0)
54.                {
55.                    mWaitProcRooms.Add(new RoomCoordinate(r, c - 1));
56.                    mMazeData[r, c - 1, ROOM_SIGN_IDX] = 2;
57.                }
58.            }
59.
60.            //上边
61.            if (r < mRowCount - 1)
62.            {
63.                if (mMazeData[r + 1, c, ROOM_SIGN_IDX] == 1)
64.                    check.Add(RectWallType.Up);
65.                else if (mMazeData[r + 1, c, ROOM_SIGN_IDX] == 0)
66.                {
67.                    mWaitProcRooms.Add(new RoomCoordinate(r + 1, c));
68.                    mMazeData[r + 1, c, ROOM_SIGN_IDX] = 2;
69.                }
70.            }
71.
72.            //右边
73.            if (c < mColCount - 1)
74.            {
75.                if (mMazeData[r, c + 1, ROOM_SIGN_IDX] == 1)
76.                    check.Add(RectWallType.Right);
77.                else if (mMazeData[r, c + 1, ROOM_SIGN_IDX] == 0)
78.                {
79.                    mWaitProcRooms.Add(new RoomCoordinate(r, c + 1));
80.                    mMazeData[r, c + 1, ROOM_SIGN_IDX] = 2;
81.                }
82.            }
83.
84.            //下边
85.            if (r > 0)
86.            {
87.                if (mMazeData[r - 1, c, ROOM_SIGN_IDX] == 1)
88.                    check.Add(RectWallType.Down);
89.                else if (mMazeData[r - 1, c, ROOM_SIGN_IDX] == 0)
90.                {
91.                    mWaitProcRooms.Add(new RoomCoordinate(r - 1, c));
92.                    mMazeData[r - 1, c, ROOM_SIGN_IDX] = 2;
93.                }
94.            }
95.
96.            if (check.Count > 0)
97.            {
98.                RectWallType move_dir = check[Random.Range(0, check.Count)];
99.                if (move_dir == RectWallType.Left)
100.                {
101.                    mMazeData[r, c, (int)RectWallType.Left] = 1;
102.                    mMazeData[r, c - 1, (int)RectWallType.Right] = 1;
103.                }
104.                else if (move_dir == RectWallType.Right)
105.                {
106.                    mMazeData[r, c, (int)RectWallType.Right] = 1;
107.                    mMazeData[r, c + 1, (int)RectWallType.Left] = 1;
108.                }
109.                else if (move_dir == RectWallType.Up)
110.                {
111.                    mMazeData[r, c, (int)RectWallType.Up] = 1;
112.                    mMazeData[r + 1, c, (int)RectWallType.Down] = 1;
113.                }
114.                else if (move_dir == RectWallType.Down)
115.                {
116.                    mMazeData[r, c, (int)RectWallType.Down] = 1;
117.                    mMazeData[r - 1, c, (int)RectWallType.Up] = 1;
118.                }
119.            }
120.
121.            check.Clear();
122.        }
123.    }
124.}

3、项目中的迷宫场景部件制作

FPS项目中的迷宫是在上面数据生成的基础上,由程序组合各种不同迷宫场景部件来完成的,我们先规划出若干的迷宫部件,例如描述4个通路的迷宫部件如图3-1所示:

图3-1 四通路迷宫部件
美术制作完这个部件后,我们把它导入Unity,然后为每个部件创建一个场景,把部件导入到场景中进行灯光设置,然后静态烘焙,计算生成物理碰撞数据,最后按照子场景部件填写场景部件配置表,在程序代码中加载并拼接显示出来。

4、迷宫物理碰撞数据生成

每个迷宫部件的碰撞信息我们是用BVH树来保存的,每个迷宫部件单独生成自己的BVH树,其目的是为了让碰撞检测更高效,下面介绍一下BVH树的算法流程:

  1. 给场景中需要添加到BVH的物体添加标签
  2. 遍历这些物体
  3. 把每个物体的三角形获取出来放入集合中
  4. 根据集合构建一个BVH根节点
  5. 开始BVH切分
  6. Shake(摇树:把未包含三角形的叶节点删除)

BVH树的切分过程如下图4-1所示,从一个根节点开始把场景等分成八个部分,然后把遍历根节点包含的三角形列表,把三角形根据自己的位置存放到各个子节点中,然后递归下去,直到达到最大深度或者没有三角形可以分配为止。


图4-1BVH树生成过程

5、迷宫场景资源管理和显示优化

因为迷宫场景是一个大型场景,那么需要考虑流畅性运行的前提下,尽可能的减少内存占用和CPU、GPU占用。
首先场景的静态碰撞数据,因为是帧同步的网络游戏,所以需要全部加载到内存中,便于保证流畅度,其中加载的过程要经历先加载原始的碰撞信息,然后旋转、平移到对应的迷宫场景处的这个过程。
然后是子场景处理,为了保证运行流畅性,还因为每个子场景的数据量并不大,先做一个预加载,避免需要显示子场景的时候产生加载延迟
对于子场景的显示优化,主要思想是尽可能不显示看不见的房间,所以有了以下算法:

  1. 获得玩家当前所在房间作为当前房间
  2. 从当前房间出发,向着4个方向遍历,把遍历到的房间加入显示列表
  3. 如果某个方向上遇到墙,则停止遍历
  4. 如果某个方向上遍历层数达到设定最大层,也停止遍历

算法的效果图如5-1所示,只显示出了玩家周围的可见房间以减少显卡负担。

图5-1 实际运行中的迷宫场景

最后是碰撞检测的优化,因为我们针对每个子场景建立了BVH树,那么进行碰撞检测的时候,就能节省比较多的CPU负荷,下面以射线碰撞检测为例展示算法流程

  1. RayCast递归检测 从根节点开始先检测射线是否和AABB相交,不相交就返回
  2. 如果和AABB相交,则查看是否有子节点,没有子节点(叶节点)就检测射线是否和三角形相交
  3. 如果有子节点,继续递归调用检测函数

6、总结和展望

大型场景生成技术是一个比较宽泛的技术,很多知名游戏都在使用,因为各个类型的游戏特色不同,所采用的具体实现方式和优化手段也有不同,本文介绍了一个FPS迷宫吃鸡游戏项目的实际制作方法和优化手段,包含了迷宫数据的生成,迷宫部件的制作,及迷宫显示的优化。
后续的将会推出其他几种典型游戏的场景生成算法和优化手段相关文章,敬请期待!

Unity大型场景程序化生成及优化技术—FPS迷宫生成和优化相关推荐

  1. 公众号seo排名优化技术,公众号名称优化排名

    春江潮水连海平,海上明月共潮生. 滟滟随波千万里,何处春江无月明! 江流宛转绕芳甸,月照花林皆似霰. 空里流霜不觉飞,汀上白沙看不见. 江天一色无纤尘,皎皎空中孤月轮. 江畔何人初见月?江月何年初照人 ...

  2. c语言随机迷宫生成方法,[原创]递归随机迷宫生成算法详解

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 #include #include #include char cw[][4]={" ","┃","━& ...

  3. 第16章 Unity中的渲染优化技术

    程序优化的第一条准则: 不要优化.程序优化的第二条准则(仅针对专家! 〉: 不要优化. 一一Michael A. Jackson 在进行程序优化的时候,人们经常会引用英国的计算机科学家Michael ...

  4. 基于SDN网络的优化技术和QoS研究分析(二)

    编者按 随着网络技术的发展,越来越多的分布式应用和不同类型的网络技术被部署到网络上,基于传统IP的网络体系结构正面临越来越多的问题,传统的优势正逐渐成为制约网络技术发展的瓶颈. 由于篇幅较长,文章将分 ...

  5. 鲸鱼优化算法与大数据:高效网站分析优化技术

    作者:禅与计算机程序设计艺术 "鲸鱼优化算法与大数据:高效网站分析优化技术" 引言 1.1. 背景介绍 随着互联网的发展,网站数量日益增长,用户访问量也不断增加.网站作为企业或个人 ...

  6. DL之DNN优化技术:利用Batch Normalization(简介、入门、使用)优化方法提高DNN模型的性能

    DL之DNN优化技术:利用Batch Normalization优化方法提高DNN模型的性能 目录 Batch Normalization简介 Batch Normalization入门 Batch ...

  7. SEO优化:网站优化之关键词优化技术

    SEO优化 网站SEO是一种在搜索引擎中获取到自然排名的一种优化技术,通常是以优化某个关键词,某一个页面整体的质量,从而能在庞大的互联网中脱颖而出的排名展现方式.布局关键词和内容,优化网站关键词和整体 ...

  8. JIT 即时编译及优化技术

    JIT 即时编译及优化技术 前言 即时编译 热点代码探测 编译优化技术 语言无关的经典优化技术之一:公共子表达式消除 语言相关的经典优化技术之一:数组范围检查消除 最重要的优化技术之一:方法内联 最前 ...

  9. JVM虚拟机中优化技术之逃逸分析

    目录 编译器优化技术之语言相关的优化技术 简介 前置问题 对象是否都被分配到了堆内存中? 为什么会发生逃逸? 什么是逃逸分析? 逃逸分析原理 什么是方法逃逸? 举例说明: 什么是线程逃逸? 举例说明: ...

最新文章

  1. html怎么查看cad文件,如何直接查看CAD格式的图纸
  2. 独立重复实验与二项分布
  3. MySQL使用可重复读作为默认隔离级别的原因
  4. 关于使用layui中的tree的一个坑
  5. Jenkins 中以构建 Tag 来实现版本管理
  6. 【OpenGL从入门到精通(七)】OpenGL中的数学
  7. 我的高中生涯大概只有一年半学的是C/C++
  8. Oracle学习笔记--导航
  9. (转)Arcgis for JS之Cluster聚类分析的实现
  10. win32 disk imager使用后u盘容量恢复
  11. [转]计算机视觉、机器学习相关领域论文和源代码大集合--持续更新
  12. spring boot first
  13. linux教学之安装python与pip环境
  14. magisk卸载内置软件_软件卸载工具的终极武器——Revo Uninstaller
  15. 设计模式之工厂模式(C++)
  16. linux的命令名必须是小写英文字母,Linux常用命令及操作
  17. uni-app动画渲染
  18. 什么是位域?位域如何定义?一般什么时候使用?
  19. 邹小强老师的个人目标管理分享课
  20. cubemx—编码器测速(其中遇到的问题分析以及部分解决办法)

热门文章

  1. wp-db.php,WordPress中的数据库操作类wp-db.php
  2. echart结合高德地图的数据可视化大数据展示平台模板
  3. Excel根据身份证号自动识别性别
  4. zookeeper数据恢复
  5. 在计算机中无符号整数和有符号整数,无符号整数和有符号整数怎么区分?
  6. cannon的英文名_卡农的作者是谁啊 此曲的赏析 英文名cannon不是大炮吗
  7. html实现艺术字体颜色,Word中将标题设置为艺术字,式样为艺术字库中的填充-红色,强调文字颜色2,粗糙棱台,...
  8. 代码详解设计模式--中介者模式
  9. 计算机歌曲数我的一个道姑朋友,同人歌|我的一个道姑朋友
  10. 给自己:得失心莫太重,功利心莫太强