从《LOL》谈游戏中的随机动作优化

游戏开发人员面对的往往是一个长期持续演进的软件产品,如”英雄联盟”(译者注:League of Legends,以下简称LOL,是美国Riot游戏公司开发的3D大型竞技场网游),这些游戏产品在演进过程中会随着剧情的发展在内部的有限状态机上不断地添加越来越多的内容。新加入的内容不仅导致愈加复杂的逻辑实现,同时由于需要更多的纹理绘制,仿真计算,也带来了内存消耗的增加和性能的下降。如果我们忽视这些问题(或者由于实现出现错误)的话,那么必然会损害游戏的运行性能,导致游戏的体验感下降。游戏动画中产生的任何延迟,阻塞都会直接导致玩家感到异常沮丧。

我所在团队的使命是致力于改善LOL这款游戏的性能。通过测试游戏的客户端和服务器端,找到影响性能的代码,并改进它们。同时我们也会把研究成果反馈给其他的团队,并为他们提供分析工具和测量指标,让他们能尽早发现性能上的问题。我们持续地提升LOL的性能表现,为游戏情节设计师和艺术家们腾出更多的空间来添加更酷的新东西:我们的任务是让游戏运行得更快,而他们的工作是让游戏变得更有趣,更好玩。

这篇文章是一个系列中的第一篇,介绍我们的团队是如何在游戏内容不断添加的情况下对游戏的运行速度进行优化的。这是一件非常有挑战性的任务。本文将和各位深入探讨一下在面对如何提高游戏中的粒子系统(particle system)的性能中所遇到的一些有趣的问题,正如下面的动画,其展现了粒子系统在游戏软件中所扮演的举足轻重的作用。


LOL游戏中高密度粒子运动动画场景的例子

优化不是简单地重写一段代码,尽管有时候的确需要这样。我们对代码的修改,其目的不仅是要提升性能,更要保证其正确性,并尽可能地提高代码的可维护性。最后这一点是至关重要的:任何代码,如果可读性和可维护性不好的话,都会在未来给项目带来潜在的问题。

在优化现有的代码时,我们会采取三个基本步骤:识别,理解和迭代。

第一步: 识别

在优化前,我们首先需要确认该代码的确需要优化。有些代码咋看上去运行效率是比较低,但如果它们对系统的整体性能的影响本身就微乎其微的话,那还是不要花精力去改动它(有这时间和精力还不如用在其他的地方)。我们用代码分析工具和测试样本来帮助识别低效的代码。

第二步: 理解

一旦我们识别出低效的代码,就可以更深入地研究这些代码来真正了解发生了什么事情。我们需要知道代码在干什么,以及原来实现的真实意图。然后,我们就可以明白为什么这里的代码是一个瓶颈。

第三步: 迭代

当我们明白为什么这些代码是低效的,以及它的目的是什么,我们就有足够的信息来设计并实现一个可能的解决方案。使用第一步识别步骤所提到的分析工具和样本数据,我们可以对比新代码和老代码在性能上孰优孰劣。如果新的解决方案足够的快,并且经过全面的测试确保没有引入任何新的错误,那么赶紧庆祝一下,至此我们已经为进一步的内部测试做好准备。但大部分情况下,如果新的改动并没有带来性能上的显著提高,那么我们就不断地迭代解决方案,直到它足够地快。

最近我针对LOL的粒子系统做了一次优化工作,就让我们基于这次优化一步一步地来看看这个过程是怎么实现的吧。

第一步: 识别

Riot公司(译者注:Riot直译为拳头,即著名的Riot Games,美国网游开发商,成立于2006年,代表作品LOL《英雄联盟》,2011年被腾讯收购)的工程师们使用各种分析工具检查游戏的客户端和服务器的性能。首先让我们来看看一款叫做Waffles的内部分析工具,通过有选择地测试某些指定的函数,它可以帮助我们检查客户端的帧刷新速度并提供图形化的分析数据。 (Waffles也可被用于许多其他的事情,如在测试中触发调试事件,检查游戏数据,譬如导航网格,还可以暂停或减慢游戏的运行。)


Waffles

Waffles可以动态地显示详细的性能信息。上图是一个客户端性能数据展示的例子。图上方的那些绿色的竖条表示采样点上的帧速(单位为毫秒),竖条越长,表示该采样点的帧速越慢。在游戏中那些异常慢的帧速点被称之为“结点”(hitches)。在绿色竖条的下方是对应每个采样点(即绿色竖条)的函数调用的详细信息,以函数调用的层次结构方式展现。点击任意一个绿色竖条,就可以看到该采样点的详细信息。通过这种方式,我们可以形象地深入了解哪些代码块对整体运行效率有拖累。

在代码中(译者注,本文作者的编程语言应该是C++)我们定义一个简单的宏来帮助我们提取代码里的重要函数在运行期间的性能相关参数。对于正式发布的版本,这个宏不起作用,但对于内部调试的版本,这个宏会构造一个很小的类,在函数被调用时会创建一个该类的对象,该对象会生成一条消息,并将该条消息输出到一个缓存里保存起来用于事后的分析。这条消息中一般会包含一个字符串标识符,一个线程号,一个时间戳,还有其他所有被认为有必要保存的信息(例如,在该函数执行过程中分配的内存大小的值)。当该对象超出其作用范围后其析构函数会根据该对象的构造函数被调用的时间点计算出该对象生存的时长并将该时长信息也更新到缓存中和该对象所对应的消息中。在稍后的一个时间点,缓存中的数据可以被导出并用于进一步的分析,导出的动作一般由另外一个进程执行,以尽量不影响游戏本身的运行。


基于Chrome的可视化跟踪分析插件

在我们的例子中,缓存中的内容将被导出并保存为一个文件(保存为json格式),该文件可以被导入到一个可视化的跟踪工具里,而这个工具软件可以以插件的方式集成在Chrome浏览器中方便使用。 (你可以在这里找到有关这个跟踪工具的更多信息,并通过在Chrome浏览器里键入“chrome://tracing/”试用它。这个插件可以方便我们直接在网页浏览器里查看分析结果。)该图形化的分析工具可以帮助我们找到那些运行较慢的函数,或者发现哪里存在大量的小函数被连续调用的情况(译者注:连续的大量小函数被调用会导致频繁的函数压栈和出栈发生,这会导致CPU运行效率降低),这两种情况都意味着需要优化。

让我来告诉你如何使用这个工具:上面的图是一个Chrome里显示的跟踪视图,它显示了客户端上运行的两个线程。最上面的一个是主线程,主要的处理逻辑由它完成,下面的是粒子线程,用于粒子的处理。每个彩条对应一个函数,彩条的长度指示了函数执行的时间。函数间的调用关系通过彩条的上下位置关系表示,调用函数显示在被调用函数的上面。这种安排很好地展现了函数执行调用之间的复杂关系以及函数的执行时间。当我们感觉某段区域存在不合理的代码时,可以将特定的区域放大,这样我们就可以观察到更多的细节了。


放大后的跟踪显示

让我们放大图的中间部分。观察上方的主线程,我们可以看到一个非常漫长的等待“Wait”过程,其结束的位置对应于下方线程中的Simulate函数的结束点位置。Simulate函数中含有大量的不同的函数调用(见Simulate色带下方的彩色栏)。这些函数都是粒子系统的更新函数用于更新每个粒子对象的位置,方向,以及其他需要显示的特征点。一个明显的优化措施是用多线程来实现Simulate函数,但在本文中我们将主要着眼于优化Simulate函数的代码本身。

在找到了会影响性能的大致地方后,我们可以启用采样分析的方法来更准确地定位性能改进点。这种分析方法周期性地读取 程序计数器(program counter)的内容并记录下来,甚至会访问运行中的进程栈。经过一段时间运行后,此类统计信息大致可以为我们勾勒出代码中指令运行的随机过程状态(译者注:即通过查看PC来统计程序运行过程中某些经常被执行的指令的执行频度,或通过查看程序栈了解函数的调用情况等)。对于那些运行较慢的函数,由于在这些函数里停留的时间较长,会导致采样的结果更多地发生在这些函数中,进一步地,该方法也适用于指令级别,可以检测出那些消耗时间较长的指令段。这样,我们不仅可以发现那些执行最慢的函数,还可以发现执行最慢的代码。有很多很好的采样分析软件,从免费的像 Very Sleepy到功能更全的商业软件,像Intel的 VTune。

通过在游戏客户端上运行VTune检查粒子线程,我们可以得到运行最慢的函数的列表如下。


VTune中显示的运行最慢的函数列表

上表列出了一些和粒子处理相关的函数。最上面的两个函数花费的时间最多,但它们实现的功能也多,主要用于更新和每个粒子的位置和方向都相关的各种矩阵信息和状态。在这里我将特别关注列表的第三项和第九项,即AnimatedVariableWithRandomFactor<>这个类的成员函数Evaluate,因为较于前述的两个函数,这个函数并不大(因此很容易理解),但运行时花费的时间却不短。

第二步:理解

现在,我们已经选择了一个需要优化的函数,我们需要了解它做了什么,以及为什么这么做。在上述例子里,AnimatedVariables这个类是LOL游戏中的情节设计师(artists)用来定义粒子的特征点是如何随时间变化而变化用的。设计师首先为某个特定的粒子对象定义好所有关键帧中的位置数据,然后通过代码对这些数据插值拟合出一条运动轨迹曲线。插值方法可以用线性插值法,或者一阶,甚至二阶积分插值法。在游戏中动态曲线被大量使用 - 譬如在象“Summoner’s Rift”(译者注:LOL中一处著名的游戏场景的地名)里,光一个这样的场景中就要计算近40,000处 - 包括所有粒子的颜色,尺寸到运动速度。在一次游戏中Evaluate这个函数被调用高达数亿次。要知道,在LOL中,粒子的定义是游戏中的关键部分,它们的行为是不能随意改变的。

经过优化,这个类使用了一个查找表(以数组的形式),针对每个关键帧存放了一个预先计算好的值。在运行期间直接读取即可,避免了频繁的计算。这是一个合理的选择,因为采用一阶和二阶积分计算曲线轨迹是一个非常消耗CPU的过程。如果在每个系统中对每个粒子的每个动画都执行类似计算将导致游戏程序的运行速度显著放缓。


动态变化曲线查询表

当查看性能问题时,我们经常会在一些极端条件下进行测试和验证。为了模拟粒子运行的慢动作我新建了一个单人游戏,包括九个中等技能水平的自动机器人(bots),并在在游戏中的下路(bottom lane)上策动了一次大规模的团战(teamfight)。在战斗的同时我在客户端运行VTune,记录下各种数据。对Evaluate这个函数的代码给出了如下分析结果(如下图所示)。

Evaluate函数中第90行涉及到一个函数调用,为了说明方便,我把函数调用中的代码复制出来放在第91-95行,同时注释掉了第90行。


VTune中的分析例子

让我来给你们详细解释一下VTune显示的内容含义,上图以图形化的方式表明了该函数在采样分析运行过程中收集到的结果。右边的红色条状表示采样命中的频度,红色条越长,说明命中的频度越高,频度越高,说明这行代码运行得越慢。条状物的右边显示的时间是CPU运行该行代码大致花费的时间。你也可以选择以汇编的方式查看每个函数,那样就可以在机器指令级别识别具体是哪条指令拖慢了整个函数的执行。

不出意外,第95行就是问题所在了。但是,这行语句所做的事情无非就是将一个Vertor3f对象从查找表中拷贝出来,为何它会成为该函数中执行最慢的部分呢,为何拷贝一个12字节长度的数据会如此之慢?

答案在于现代CPU访问内存的方式。处理器的处理速度相当可靠地遵守着摩尔定律,按照每年60%左右的比例增长,而内存的增长速度则相对缓慢得多,每年只有10%左右。


CPU和内存处理速度的走势图,由John L. Hennessy, David A. Patterson, Andrea C. Arpaci-Dusseau提供

缓存(Cache)可以弥补这种性能上的差距。大部分LOL游戏跑在具有三级缓存的CPU上:一级缓存最快,容量最小,三级缓存最慢,但容量也最大。从一级缓存读取一条数据大约需要4个周期,而从主存(三级缓存)读出可能需要大约300个周期以上。要知道300个周期足够CPU做很多事情了。

优化前的查找算法的问题是,如果是顺序地从查询表读取数据速度是相当快的(基于硬件预取的功能),但游戏中所处理的粒子数据并不是按照时间顺序存储的,所以导致查找总是按照随机方式进行。这造成CPU不得不经常停下来等待数据从主存储器上被读进来。虽然300个周期可能比一阶或二阶积分计算所花费的机器周期要少,我们还是需要知道在游戏中这种延迟发生的频率怎样。

为了找到答案,我们增加了一些辅助代码用于统计AnimatedVariables对象被创建的次数的数量和每次创建对应的类型。我们发现在将近38000次AnimatedVariables对象的创建和销毁过程中:

  • 有37500次是线性插值; 100次一阶积分插值,400次是二阶积分插值。
  • 有31500次查询表中只有一项; 2500次有3项; 1500次有2项或4项。

所以,大部分情况处理的是单项。但代码中每次都会创建一个查找表,显而易见大部分情况该表只包含一项。而每次建表查找(它总是返回相同的值)一般都会重新刷新高速缓存,从而导致了内存和CPU处理周期的浪费。

很多时候,以下四个原因之会造成代码中的效率瓶颈:

  • 函数调用过于频繁。
  • 选择了错误的算法:例如:可以用O(n),却使用了O(n^2)。
  • 做不必要的工作,或者虽然工作是必需的要但执行过于频繁了。
  • 数据的问题:要么数据太多或者数据存储方式和访问方式不好。

这里的问题并不是由于设计或编码造成的。解决方案本身没有问题。问题在于有些事情在程序开发阶段是无法知道的,就像这里,游戏软件的情节经过设计师的不断修改后,造成了在大部分场景下某些用于计算的数据变成了一个单一值(不需要建表查询了)。

顺便说一句,作为一个程序员我所领悟到的最重要的事情之一就是要尊重你所面对的代码。有些代码可能看起来让你很抓狂,但它被写成这样可能是有原因的。不深入研究就放弃并重构现有的代码是愚蠢的行为,修改代码之前一定要保证你完全理解它是如何被使用的,以及它为什么被设计成这样。


来源: http://codesoftly.com/2010/03/ha-code-entropy-explained.html

第三步:迭代

现在知道了问题症结所在,它​​应该做的是什么以及为什么它那么慢。现在可以开始制定解决方案了。首先,我们发现程序最常见的执行路径对应的是单变量,在重新设计时需要考虑到这一点。其次,如果插值点很少,那么采用线性插值将非常快(因为这仅涉及读取少量的高速缓存中的数据并进行简单的计算),这也是我们需要考虑的一点。最后,我们可以认为只有很少的情况才会需要为计算积分曲线执行建表操作并进行查找。

由于大部分情况我们是不需要查表的(译者注:对应上面所说的前两点),所以并不需要每次计算时都执行创建查询表的动作,这样我们可以释放相当数量的内存(绝大多数的表有至少256项甚至更多,每项最多12个字节。这相当于大约每张表占3K字节)。释放的内存中的一小部分可以被另外用来作为缓存存放少量的表项(译者注:对应上面的第二点)或者单一变量(译者注:对应上面的第一点)。

原来的代码如下:

template <typename T>
class AnimatedVariable
{
    // <snip>
private:
    std::vector<float> mTimes;
    std::vector<T>     mValues;
};

template <typename T>
class AnimatedVariablePrecomputed
{
    // <snip>
private:
    std::vector<T> mPrecomutedValues;
};

AnimatedVariablePrecomputed对象由AnimatedVariable对象构造,用于内插和建立一个指定大小的表。Evaluate()函数只会被AnimatedVariablePrecomputed对象调用。

We changed the AnimatedVariable class to look like this:

改动后的AnimatedVariable类如下所示:

template <typename T>
class AnimatedVariable
{
    // <snip>
private:
    int mNumValues;
    T mSingleValue;

    struct Key
    {
        float mTime;
        T     mvalue;
    };
    std::vector<Key> mKeys;
    AnimatedVariablePrecomputed<T> *mPrecomputed;
};

我们增加了一个缓存值mSingleValue,同时增加了一个变量mNumValues用来告诉我们何时使用mSingleValue。如果mNumValues的值是1(单键的情况),Evaluate()函数就立即返回mSingleValue而不需要进一步的处理。同时我们还把分开定义的两个数组mTimes和mValues组合成一个新的结构体数组mKeys,避免了实际匹配的Time和Value在各自数组中位置不对应,这样可以减少缓存不命中的概率。

不考虑数组mKeys(实质是指针数组)所指向的实际数据的大小,由于模版T(mSingleValue的类型)的实际类型不定,这个类现在的大小在24至36个字节之间(当然具体还取决于CPU架构类型,以及数组mKeys的元素的个数多少)。

最初的Evaluate()函数是这样的:

template <typename T>
T AnimatedVariablePrecomputed<T>::Evaluate(float time) const
{
    in numValues = mPrecomputedValues.size();
    RIOT_ASSERT(numValues > 1);

    int index = static_cast<int>(time * numValues);
    // clamp to valid table entry to handle the 1.0 boundary or out of bounds input
    index = Clamp(index, 0, numValues - 1);
    return mPrecomputedValues[index];
}

而经过改造的新的Evaluate()函数如下,通过运行VTune,你可以看到三种情况下运行的情况:单值(红色),线性插值(蓝色),积分查表插值(绿色)。


VTune改进后的代码示例

改进后的代码运行速度大致提高了三倍:在运行最慢的函数列表中从原来的排名第3降到了第22位!改进不仅仅体现在更快,改进后使用的内存也减少了,少了大约750KB。这还没完!不仅速度更快,更省内存,执行线性插值也更准确了!

在本文中由于篇幅的原因我没有介绍改进中所经历的曲折过程。我曾经尝试过基于粒子的生命周期减少插值点表的大小。该方法效果也不错,但较小的表会导致一些快速移动的粒子的显示出现锯齿状条纹。幸运的是这个问题被及早发现,所以最终我采用了本文介绍的解决方案。还有一些其他的数据和代码的修改,但这些修改并没有对运行效率有太大的提升。有些修改甚至带来副作用导致运行速度还变慢了。

总结

我们在这里经历了一次典型的对LOL代码库中的一段代码的优化过程。这些简单的改变为我们节省了750KB的内存,并且使计算粒子运动轨迹的线程速度快了一到两秒,从而使主线程的执行也变得更快了。

文中提到的三个阶段,虽然看似明显,但在程序员寻求优化代码的过程中却常常被忽视,所以再重申一下:

  1. 识别:分析应用程序,并确定表现最差的部分。
  2. 理解:理解代码为什么这么写,以及为何它这么慢。
  3. 迭代:根据第2步的分析修改代码,然后测试检查效果。这样的操作可能需要不断重复,直到性能问题得到解决。

上面的解决方案还不是最快的,但它的思路和方向应该是正确的,只有沿着正确的方向进行迭代才能保证最终的成功。

小白WEB
关注 关注
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ym——android源码大放送(实战开发必备)
陈宇明
08-10 4万+
文件夹 PATH 列表 卷序列号为 000A-8F50 E:. │  javaapk.com文件列表生成工具.bat │  使用说明.txt │  免费下载更多源码.url │  目录列表.txt │   ├─android web应用 │      jqmDemo_static.zip │      jqmMobileDemo-master.zip │      jqmMo
Image Enhance (图像增强:论文)
最新发布
qq_45858803的博客
05-19 1432
摘要本文提出了一种基于传统直方图均衡(HE)算法的智能对比度增强技术。这种动态直方图均衡化(DHE)技术控制了传统直方图均衡化的效果,从而在增强图像效果的同时不会丢失任何细节。DHE 根据局部最小值对图像直方图进行分区,并为每个分区分配特定的灰度级范围,然后再分别对它们进行均衡。这些分区还要经过重新分区测试,以确保不存在任何占主导地位的部分。这种方法能很好地增强对比度,而不会带来严重的副作用,如冲淡外观、棋盘格效果等,也不会产生不良的人工痕迹,因此优于其他现有方法。
Unity一个低成本优化游戏动作的方案探究
u013617851的博客
12-30 2603
本篇博客主要探究一个低成本运用动作捕捉,动态骨骼,以及一个动作标准来提升游戏角色动作表现的方案。 一.什么是游戏动作 顾名思义,就是游戏,人物角色行为举止,走路,跑步,战斗等等。 二.为什么游戏动作如此重要 在一位被卢浮宫邀请过的日本漫画家写的一本书,《荒木飞吕彦的漫画术》,荒木老师认为,漫画有四大基本元素,分别是:角色、剧情、世界观、主题。而其,角色,是终极顶梁柱,如果角色塑造的好,其余剧情世界观主题等元素,甚至可以被忽略。我个人比较认同,因为就像是樱桃小丸子,蜡笔小新,大家都不记得这俩动漫
动作游戏老是卡?试试从这些方面提升流畅度
wubaohu1314的博客
09-15 474
https://www.fgba.net/sitemap.xml
基于keybd_event函数在window系统下的模拟键盘输入源码
11-22
一个基于keybd_event函数在window系统下的模拟键盘输入源码,可以实现在winodw系统下输入任何字符串
我们是如何优化英雄联盟的代码的
u010536615的专栏
05-08 7318
原文地址:http://www.cocoachina.com/game/20160125/15092.html 对于像英雄联盟这样不断演进的产品的开发者而言,需要不断的致力于与系统的熵作斗争,因为他们将越来越多的内容添加到资源有限的服务器。新的内容带了新的隐性成本——不仅是更多的实施成本,同时也包括由于创造了更多的纹理、仿真和处理造成的内存和性能成本。如果我们忽略(或者错误估算)了
使用PowerApps制作请假系统(二)--在Flow生成请假审批流
weixin_44669829的博客
08-26 7832
之前我们创建了一个可以想SharePoint列表进行写入的PowerApps小程序,现在我们在Flow去创建这样的审批流: 1.SharePoint Online列表新添加了请假申请 2.向申请者的直接汇报领导发送邮件 3.领导在邮件通过或拒绝请假 4.更新SharePoint Online列表的请假审批状态并通知申请人 注:PowerApps现有的请假系统做的较为完善,分为员工登陆和经理...
2017年最牛逼的分类Android项目源码免费一次性打包下载!
热门推荐
源码分享的博客
03-29 2万+
之前发过一个帖子,但是那个帖子有点问题我就重新发一个吧,下面的源码是我从今年开始不断整理源码区和其他网站上的安卓例子源码,目前总共有810套左右,根据实现的功能被我分成了100多个类,总共接近2.5G,还在不断更新。初学者可以快速方便的找到自己想要的例子,大神也可以看一下别人的方法实现。虽然的例子都是我一个人辛辛苦苦花了很多时间和精力整理的,但是既然这些例子是来自于社区那就让他们免费回归社区吧,(...
强化学习实验
Gary的博客
01-07 5435
一.实验标题 强化学习实验 二.实验目的 了解并学习与强化学习有关的基本知识与实验方法。 三.强化学习的原理和思想 一.Reinforcement Learning的概念 机器学习可以分为三类,分别是 supervised learning,unsupervised learning 和reinforcement learning。而强化学习与其他机器学习不同之处为: 没有教
(开源)计算机视觉+深度学习+英雄联盟-Python开发
05-25
DeepLeague-在英雄联盟迷你地图上使用计算机视觉和深度学习+超过100,000个带有标签的图像的数据集,可在电竞领域进行进一步的AI研究。 请在此处阅读博客文章。 此回购协议仅解释了DeepLeague-利用英雄联盟迷你地图上的计算机视觉和深度学习+超过100,000个带有标签的图像的数据集在电子竞技进行进一步的AI研究。 请在此处阅读博客文章。 此仓库仅说明了如何进行设置。 该博客将解释这实际上是什么! 非常感谢YAD2K出色的开发人员。 DeepLeague建立在我自定义的回购分叉基础上,如果没有出色的工作就不可能实现。 免责声明我写了大部分
(pywin32)解决keybd_event在游戏失效
练练大脑,有何不好
09-18 1万+
pywin32是python调用win32完成windows系统功能调用的第三方库,实质就是调用的win32,so这个方案也可以解决C++的相同问题。 为什么keybd_event在notepad++等普通软件上可用而游戏不行 http://zhidao.baidu.com/link?url=0bSo71kDyJIvKfUVvhnjKvPd7yd2z6BkJ6qM6onsNciXKNyV
NPC的随机运动
崔小妹儿Jessica的博客
09-02 4116
游戏有很多自由走动的怪兽,以下脚本可以控制怪兽的运动方向与速度。但是运动方向是随机产生的using UnityEngine; using System.Collections; public class MoveControl : MonoBehaviour { public float speed = 4; private float timer = 0; priv
深度学习小白入门教程(一):以英雄联盟为例,爬取你感兴趣图片,构建你自己的数据集
CN_Thirteen的博客
07-01 1104
爬取英雄联盟英雄图片建立数据集 本教程来自DUT515深度学习实验室 小白在练习的时候,在教程里都会有各种现成的经典的数据集作为例子当你,比如鸢尾花啊: 手写数字啊: 而当你学会了这些内容,想要跃跃欲试的时候,你想要爬取一些图片作为自己的数据集的时候又苦于自己没学过爬虫吧!反正我有过这种困惑,在这里通过十来行不是很精美的代码,便可以帮你批量下载英雄联盟的英雄图片,建立属于你的数据集。 1.打开你想爬取的图片页面 今天打开英雄联盟资料库发现又鸡儿更新了,网上有很多现成的教程可能都要改改,不过换汤不换药,我
简单做好游戏“攻击动作”的3个窍门
It's time
04-29 2536
转自:http://www.gamelook.com.cn/2015/04/213246/ 随着手游市场的竞争日趋激烈,产品重度化的倾向也是越来越明显,而说到重度手游的话那就不得不提到“动作”。但由于市场发展的特殊性,国内的开发者大多精于数字方面的计算而不善于制作人物动作,这也是国内手游行业很少出现精品动作类手游的原因之一。 最近GameLook看到一篇日本格斗游戏制作者分享的游戏制作心...
Java基础打造的LOL风格文字游戏
资源摘要信息:"这是一个模仿LOL的文字游戏,涉及java所有基础知识.zip" 知识概述: 1. Java编程语言基础: Java是一种广泛应用于企业级开发、移动应用开发和游戏开发的高级编程语言。文字游戏项目将涵盖Java基础...
写文章

热门文章

  • Maya粒子-水滴表面流动效果 4631
  • HotOcean for Maya快速安装指南: 4388
  • 三维电影特效魔术师软件Sidefx Houdini FX v15.0.224 (Win/Mac) 1652
  • VS批量删注释,代码保护第一步 1596
  • 游戏AI之行为树(中) 1237

分类专栏

  • MAYA 2篇
  • 游戏开发 12篇
  • web 6篇

最新评论

  • 用distinct在MySQL中查询多条不重复记录值

    书书书呆子: 你好,关于您的Simple LWRP Reflective Shaders(个人高价官网购买).rar我已经下载购买了,可否请您提供下最新版本?可以继续付积分购买。谢谢~~

  • Maya粒子-水滴表面流动效果

    sinat_35790978: 挺好的

最新文章

  • 用distinct在MySQL中查询多条不重复记录值
  • mysql视图学习总结
  • mysql教程导出数据库教程几种方法
2016年31篇
2015年12篇

目录

目录

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值

深圳坪山网站建设公司凡科网站seo优化吗长葛口碑好网站优化价格如何计算许昌网站推广优化哪家专业菜鸟网站如何优化盱眙网站优化费用网站优化体验报告青岛网站优化电话多少个人网站如何优化关键词快速优化网站公司新沂盐城网站优化企业兰州网站seo优化工具建设网站如何利用外链优化深圳网站建设推广优化西安技术好网站关键词优化南通互联网网站优化信息中心网站单词优化按天计费山东企业网站优化公司网站关键词优化排名培训济宁质量好网站优化价格白城网站优化公司价格南京小企业网站优化推广伊宁网站搜索优化网站优化技术小鸭seo驻马店百度网站优化漯河优化网站售价黔江网站seo优化产品网站优化方式商品网站优化效果不好揭阳网站关键优化南京企业网站优化公司香港通过《维护国家安全条例》两大学生合买彩票中奖一人不认账让美丽中国“从细节出发”19岁小伙救下5人后溺亡 多方发声卫健委通报少年有偿捐血浆16次猝死汪小菲曝离婚始末何赛飞追着代拍打雅江山火三名扑火人员牺牲系谣言男子被猫抓伤后确诊“猫抓病”周杰伦一审败诉网易中国拥有亿元资产的家庭达13.3万户315晚会后胖东来又人满为患了高校汽车撞人致3死16伤 司机系学生张家界的山上“长”满了韩国人?张立群任西安交通大学校长手机成瘾是影响睡眠质量重要因素网友洛杉矶偶遇贾玲“重生之我在北大当嫡校长”单亲妈妈陷入热恋 14岁儿子报警倪萍分享减重40斤方法杨倩无缘巴黎奥运考生莫言也上北大硕士复试名单了许家印被限制高消费奥巴马现身唐宁街 黑色着装引猜测专访95后高颜值猪保姆男孩8年未见母亲被告知被遗忘七年后宇文玥被薅头发捞上岸郑州一火锅店爆改成麻辣烫店西双版纳热带植物园回应蜉蝣大爆发沉迷短剧的人就像掉进了杀猪盘当地回应沈阳致3死车祸车主疑毒驾开除党籍5年后 原水城县长再被查凯特王妃现身!外出购物视频曝光初中生遭15人围殴自卫刺伤3人判无罪事业单位女子向同事水杯投不明物质男子被流浪猫绊倒 投喂者赔24万外国人感慨凌晨的中国很安全路边卖淀粉肠阿姨主动出示声明书胖东来员工每周单休无小长假王树国卸任西安交大校长 师生送别小米汽车超级工厂正式揭幕黑马情侣提车了妈妈回应孩子在校撞护栏坠楼校方回应护栏损坏小学生课间坠楼房客欠租失踪 房东直发愁专家建议不必谈骨泥色变老人退休金被冒领16年 金额超20万西藏招商引资投资者子女可当地高考特朗普无法缴纳4.54亿美元罚金浙江一高校内汽车冲撞行人 多人受伤

深圳坪山网站建设公司 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化