前言

技能系统暂时告一段落,现在要花点时间规范一下客户端这边的资源管理以及一些流程优化,这里选择轻量高效资源管理框架xasset:xasset开源地址​github.com

版本为:https://github.com/xasset/xasset/commit/3d4983cd24ff92a63156c8078caf34b20d2d4c02​github.com

代码量很少,一天就能看个差不多,但是质量很高,如果只追求使用的话,是可以开箱即用的。

另外我对xasset底层源码做了一些修改,主要是为了迎合我们ET传统艺能await/async样式代码,所以推荐大家直接使用我项目(下文的Moba项目)中的xasset源码

想要入门此资源管理框架的可以查看:xasset入门指南 - TA养成记​www.lfzxb.top

以及视频教程:xasset4.0入门指南_哔哩哔哩 (゜-゜)つロ 干杯~-bilibili​www.bilibili.com

为了方便大家参考,可以去我对Moba项目:烟雨迷离半世殇/NKGMobaBasedOnET​gitee.com

,来查看更加具体的接入代码,目前全部功能测试通过(资源加载,普通热更新,VFS热更新,FGUI适配)

流程预演

为了流畅接入一个框架,可以先把大体流程先构思一下

xasset使用主要分为3块打包工具配置(直接拿过来改几个路径即可使用(因为要打包到我们资源服务器指定的目录下面))

本地资源服务器(使用ET自带的Web资源服务器即可)

运行时类库(非常简单的接口使用,完全不需要关心资源管理)

打包工具

xasset打包流程分ApplyRule(配置打包规则),BuildRule(自动分析依赖关系,优化资源冗余,解决资源冲突),BuildBundle(出包)三步走,具体内容可参照上文链接

本地资源服务器

这一块是ET的内容,事实上我们只需要修改代码,把资源打到文件资源服务器指定的目录就行了

运行时类库

xasset运行时接入相对于前面两块内容较为复杂,主要包括资源热更新模块接入,API封装,FGUI资源加载适配

正式开始

xasset导入

首先导入xasset到ET,主要有Editor和Runtime这两部分内容

首先是Editor部分,把Assets/XAsset/Editor文件夹放到我们ET中的Editor文件夹

Assets/XAsset/Runtime文件夹放到我们ET中的ThirdParty,注意移除UI文件夹,因为他是和xasset的官方Demo耦合的

会有一些Updater脚本的报错,但是不要怕,我们接下来解决他

它里面的报错主要是引用的Message Mono类(一个用于显示对话框的类)找不到导致的,所以我们把这部分内容改成用Debug输出或者直接删掉就行了

这里提供一个我的修改版本的

Updater.cs Author:// fjy Copyright (c) 2020 fjy Permission is hereby granted, free of charge, to any person obtaining a copy// of this software and associated documentation files (the "Software"), to deal// in the Software without restriction, including without limitation the rights// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell// copies of the Software, and to permit persons to whom the Software is// furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in// all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN// THE SOFTWARE.

using System;

using System.Collections;

using System.Collections.Generic;

using System.IO;

using UnityEngine;

using UnityEngine.Networking;

namespace libx

{

public enum Step

{

Wait,

Copy,

Coping,

Versions,

Prepared,

Download,

Completed,

}

[RequireComponent(typeof (Downloader))]

public class Updater: MonoBehaviour

{

public Step Step;

public Action ResPreparedCompleted;

public float UpdateProgress;

public bool DevelopmentMode;

public bool EnableVFS = true;

[SerializeField]

private string baseURL = "http://127.0.0.1:7888/DLC/";

private Downloader _downloader;

private string _platform;

private string _savePath;

private List _versions = new List();

public void OnMessage(string msg)

{

Debug.Log(msg);

}

public void OnProgress(float progress)

{

UpdateProgress = progress;

}

private void Awake()

{

_downloader = gameObject.GetComponent();

_downloader.onUpdate = OnUpdate;

_downloader.onFinished = OnComplete;

_savePath = string.Format("{0}/DLC/", Application.persistentDataPath);

_platform = GetPlatformForAssetBundles(Application.platform);

this.Step = Step.Wait;

Assets.updatePath = _savePath;

}

private void OnUpdate(long progress, long size, float speed)

{

OnMessage(string.Format("下载中...{0}/{1}, 速度:{2}",

Downloader.GetDisplaySize(progress),

Downloader.GetDisplaySize(size),

Downloader.GetDisplaySpeed(speed)));

OnProgress(progress * 1f / size);

}

private IEnumerator _checking;

public void StartUpdate()

{

Debug.Log("StartUpdate.Development:" + this.DevelopmentMode);

#if UNITY_EDITOR if (this.DevelopmentMode)

{

Assets.runtimeMode = false;

StartCoroutine(LoadGameScene());

return;

}

#endif

if (_checking != null)

{

StopCoroutine(_checking);

}

_checking = Checking();

StartCoroutine(_checking);

}

private void AddDownload(VFile item)

{

_downloader.AddDownload(GetDownloadURL(item.name), item.name, _savePath + item.name, item.hash, item.len);

}

private void PrepareDownloads()

{

if (this.EnableVFS)

{

var path = string.Format("{0}{1}", _savePath, Versions.Dataname);

if (!File.Exists(path))

{

AddDownload(_versions[0]);

return;

}

Versions.LoadDisk(path);

}

for (var i = 1; i < _versions.Count; i++)

{

var item = _versions[i];

if (Versions.IsNew(string.Format("{0}{1}", _savePath, item.name), item.len, item.hash))

{

AddDownload(item);

}

}

}

private static string GetPlatformForAssetBundles(RuntimePlatform target)

{

// ReSharper disable once SwitchStatementMissingSomeCases switch (target)

{

case RuntimePlatform.Android:

return "Android";

case RuntimePlatform.IPhonePlayer:

return "iOS";

case RuntimePlatform.WebGLPlayer:

return "WebGL";

case RuntimePlatform.WindowsPlayer:

case RuntimePlatform.WindowsEditor:

return "Windows";

case RuntimePlatform.OSXEditor:

case RuntimePlatform.OSXPlayer:

return "iOS"; // OSX default:

return null;

}

}

private string GetDownloadURL(string filename)

{

return string.Format("{0}{1}/{2}", baseURL, _platform, filename);

}

private IEnumerator Checking()

{

if (!Directory.Exists(_savePath))

{

Directory.CreateDirectory(_savePath);

}

this.Step = Step.Copy;

if (this.Step == Step.Copy)

{

yield return RequestCopy();

}

if (this.Step == Step.Coping)

{

var path = _savePath + Versions.Filename + ".tmp";

var versions = Versions.LoadVersions(path);

var basePath = GetStreamingAssetsPath() + "/";

yield return UpdateCopy(versions, basePath);

this.Step = Step.Versions;

}

if (this.Step == Step.Versions)

{

yield return RequestVersions();

}

if (this.Step == Step.Prepared)

{

OnMessage("正在检查版本信息...");

var totalSize = _downloader.size;

if (totalSize > 0)

{

Debug.Log($"发现内容更新,总计需要下载 {Downloader.GetDisplaySize(totalSize)} 内容");

_downloader.StartDownload();

this.Step = Step.Download;

}

else

{

OnComplete();

}

}

}

private IEnumerator RequestVersions()

{

OnMessage("正在获取版本信息...");

if (Application.internetReachability == NetworkReachability.NotReachable)

{

Debug.LogError("请检查网络连接状态");

yield break;

}

var request = UnityWebRequest.Get(GetDownloadURL(Versions.Filename));

request.downloadHandler = new DownloadHandlerFile(_savePath + Versions.Filename);

yield return request.SendWebRequest();

var error = request.error;

request.Dispose();

if (!string.IsNullOrEmpty(error))

{

Debug.LogError($"获取服务器版本失败:{error}");

yield break;

}

try

{

_versions = Versions.LoadVersions(_savePath + Versions.Filename, true);

if (_versions.Count > 0)

{

PrepareDownloads();

this.Step = Step.Prepared;

}

else

{

OnComplete();

}

}

catch (Exception e)

{

Debug.LogException(e);

Debug.LogError("版本文件加载失败");

}

}

private static string GetStreamingAssetsPath()

{

if (Application.platform == RuntimePlatform.Android)

{

return Application.streamingAssetsPath;

}

if (Application.platform == RuntimePlatform.WindowsPlayer ||

Application.platform == RuntimePlatform.WindowsEditor)

{

return "file:///" + Application.streamingAssetsPath;

}

return "file://" + Application.streamingAssetsPath;

}

private IEnumerator RequestCopy()

{

var v1 = Versions.LoadVersion(_savePath + Versions.Filename);

var basePath = GetStreamingAssetsPath() + "/";

var request = UnityWebRequest.Get(basePath + Versions.Filename);

var path = _savePath + Versions.Filename + ".tmp";

request.downloadHandler = new DownloadHandlerFile(path);

yield return request.SendWebRequest();

if (string.IsNullOrEmpty(request.error))

{

var v2 = Versions.LoadVersion(path);

if (v2 > v1)

{

Debug.Log("将资源解压到本地");

this.Step = Step.Coping;

}

else

{

Versions.LoadVersions(path);

this.Step = Step.Versions;

}

}

else

{

this.Step = Step.Versions;

}

request.Dispose();

}

private IEnumerator UpdateCopy(IList versions, string basePath)

{

var version = versions[0];

if (version.name.Equals(Versions.Dataname))

{

var request = UnityWebRequest.Get(basePath + version.name);

request.downloadHandler = new DownloadHandlerFile(_savePath + version.name);

var req = request.SendWebRequest();

while (!req.isDone)

{

OnMessage("正在复制文件");

OnProgress(req.progress);

yield return null;

}

request.Dispose();

}

else

{

for (var index = 0; index < versions.Count; index++)

{

var item = versions[index];

var request = UnityWebRequest.Get(basePath + item.name);

request.downloadHandler = new DownloadHandlerFile(_savePath + item.name);

yield return request.SendWebRequest();

request.Dispose();

OnMessage(string.Format("正在复制文件:{0}/{1}", index, versions.Count));

OnProgress(index * 1f / versions.Count);

}

}

}

private void OnComplete()

{

if (this.EnableVFS)

{

var dataPath = _savePath + Versions.Dataname;

var downloads = _downloader.downloads;

if (downloads.Count > 0 && File.Exists(dataPath))

{

OnMessage("更新本地版本信息");

var files = new List(downloads.Count);

foreach (var download in downloads)

{

files.Add(new VFile { name = download.name, hash = download.hash, len = download.len, });

}

var file = files[0];

if (!file.name.Equals(Versions.Dataname))

{

Versions.UpdateDisk(dataPath, files);

}

}

Versions.LoadDisk(dataPath);

}

OnProgress(1);

OnMessage($"更新完成,版本号:{Versions.LoadVersion(_savePath + Versions.Filename)}");

StartCoroutine(LoadGameScene());

}

private IEnumerator LoadGameScene()

{

OnMessage("正在初始化");

var init = Assets.Initialize();

yield return init;

this.Step = Step.Completed;

if (string.IsNullOrEmpty(init.error))

{

init.Release();

OnProgress(0);

OnMessage("加载游戏场景");

ResPreparedCompleted?.Invoke();

}

else

{

init.Release();

Debug.LogError($"初始化异常错误:{init.error},请联系技术支持");

}

}

}

}

最后因为我们Model层会用到xasset,所以引用asmdef文件,Hotfix同理

替换ET资源管理模块

因为我们使用xasset全盘托管资源管理(资源加载,热更新),所以我们只需要对其进行封装即可

移除所有打包模块

Editor下的打包模块相关代码都可以删除

ResourceComponent

支持await/async语法

using libx;

using UnityEngine;

namespace ETModel

{

public class ResourcesComponent: Component

{

#region Assets

/// /// 加载资源,path需要是全路径 /// /// /// /// public T LoadAsset(string path) where T : UnityEngine.Object

{

AssetRequest assetRequest = Assets.LoadAsset(path, typeof (T));

return (T) assetRequest.asset;

}

/// /// 异步加载资源,path需要是全路径 /// /// /// /// public ETTask LoadAssetAsync(string path) where T : UnityEngine.Object

{

ETTaskCompletionSource tcs = new ETTaskCompletionSource();

AssetRequest assetRequest = Assets.LoadAssetAsync(path, typeof (T));

//如果已经加载完成则直接返回结果(适用于编辑器模式下的异步写法和重复加载) if (assetRequest.isDone)

{

tcs.SetResult((T) assetRequest.asset);

return tcs.Task;

}

//+=委托链,否则会导致前面完成委托被覆盖 assetRequest.completed += (arq) => { tcs.SetResult((T) arq.asset); };

return tcs.Task;

}

/// /// 卸载资源,path需要是全路径 /// /// public void UnLoadAsset(string path)

{

Assets.UnloadAsset(path);

}

#endregion

#region Scenes

/// /// 加载场景,path需要是全路径 /// /// /// public ETTask LoadSceneAsync(string path)

{

ETTaskCompletionSource tcs = new ETTaskCompletionSource();

SceneAssetRequest sceneAssetRequest = Assets.LoadSceneAsync(path, false);

sceneAssetRequest.completed = (arq) =>

{

tcs.SetResult(sceneAssetRequest);

};

return tcs.Task;

}

/// /// 卸载场景,path需要是全路径 /// /// public void UnLoadScene(string path)

{

Assets.UnloadScene(path);

}

#endregion }

}

BundleDownloaderComponent

using System;

using System.Collections.Generic;

using System.IO;

using System.Threading.Tasks;

using libx;

using UnityEngine;

namespace ETModel

{

[ObjectSystem]

public class UiBundleDownloaderComponentAwakeSystem: AwakeSystem

{

public override void Awake(BundleDownloaderComponent self)

{

self.Updater = GameObject.FindObjectOfType();

}

}

[ObjectSystem]

public class UiBundleDownloaderComponentSystem: UpdateSystem

{

public override void Update(BundleDownloaderComponent self)

{

if (self.Updater.Step == Step.Completed)

{

self.Tcs.SetResult();

}

}

}

/// /// 封装XAsset Updater /// public class BundleDownloaderComponent: Component

{

public Updater Updater;

public ETTaskCompletionSource Tcs;

public ETTask StartUpdate()

{

Tcs = new ETTaskCompletionSource();

Updater.ResPreparedCompleted = () =>

{

Tcs.SetResult();

};

Updater.StartUpdate();

return Tcs.Task;

}

}

}

ABPathUtilities

因为xasset使用全路径对资源进行加载,所以我们要提供路径拓展

//------------------------------------------------------------// Author: 烟雨迷离半世殇// Mail: 1778139321@qq.com// Data: 2020年10月14日 22:27:15//------------------------------------------------------------

namespace ETModel

{

/// /// AB实用函数集,主要是路径拼接 /// public class ABPathUtilities

{

public static string GetTexturePath(string fileName)

{

return $"Assets/Bundles/Altas/{fileName}.prefab";

}

public static string GetFGUIDesPath(string fileName)

{

return $"Assets/Bundles/FUI/{fileName}.bytes";

}

public static string GetFGUIResPath(string fileName)

{

return $"Assets/Bundles/FUI/{fileName}.png";

}

public static string GetNormalConfigPath(string fileName)

{

return $"Assets/Bundles/Independent/{fileName}.prefab";

}

public static string GetSoundPath(string fileName)

{

return $"Assets/Bundles/Sounds/{fileName}.prefab";

}

public static string GetSkillConfigPath(string fileName)

{

return $"Assets/Bundles/SkillConfigs/{fileName}.prefab";

}

public static string GetUnitPath(string fileName)

{

return $"Assets/Bundles/Unit/{fileName}.prefab";

}

public static string GetScenePath(string fileName)

{

return $"Assets/Scenes/{fileName}.unity";

}

}

}

打包配置

BuildlScript

把脚本中对应路径进行修改即可

public static class BuildScript

{

//打包AB的输出路径 public static string ABOutPutPath = c_RelativeDirPrefix + GetPlatformName();

//前缀 private const string c_RelativeDirPrefix = "../Release/";

//Rules.asset保存路径 private const string c_RulesDir = "Assets/Res/XAsset/Rules.asset";

....

}

Assets

按需求修改Manifest保存路径即可

public sealed class Assets: MonoBehaviour

{

public static readonly string ManifestAsset = "Assets/Res/XAsset/Manifest.asset";

...

}

适配FGUI

我们使用FGUI提供的自定义Package加载方式

using System;

using System.Collections.Generic;

using System.Threading.Tasks;

using FairyGUI;

using libx;

using UnityEngine;

namespace ETModel

{

/// /// 管理所有UI Package /// public class FUIPackageComponent: Component

{

public const string FUI_PACKAGE_DIR = "Assets/Bundles/FUI";

private readonly Dictionary packages = new Dictionary();

public void AddPackage(string type)

{

if (this.packages.ContainsKey(type)) return;

UIPackage uiPackage;

if (Define.ResModeIsEditor)

{

uiPackage = UIPackage.AddPackage($"{FUI_PACKAGE_DIR}/{type}");

}

else

{

ResourcesComponent resourcesComponent = Game.Scene.GetComponent();

uiPackage = UIPackage.AddPackage($"{FUI_PACKAGE_DIR}/{type}", (string name, string extension, Type type1, out DestroyMethod method) =>

{

method = DestroyMethod.Unload;

switch (extension)

{

case ".bytes":

{

var req = resourcesComponent.LoadAsset($"{name}{extension}");

return req;

}

case ".png"://如果FGUI导出时没有选择分离通明通道,会因为加载不到!a结尾的Asset而报错,但是不影响运行 {

var req = resourcesComponent.LoadAsset($"{name}{extension}");

return req;

}

}

return null;

});

}

packages.Add(type, uiPackage);

}

public void RemovePackage(string type)

{

UIPackage package;

if (packages.TryGetValue(type, out package))

{

var p = UIPackage.GetByName(package.name);

if (p != null)

{

UIPackage.RemovePackage(package.name);

}

packages.Remove(package.name);

}

if (!Define.ResModeIsEditor)

{

Game.Scene.GetComponent().UnLoadAsset(ABPathUtilities.GetFGUIDesPath($"{type}_fui"));

Game.Scene.GetComponent().UnLoadAsset(ABPathUtilities.GetFGUIResPath($"{type}_atlas0"));

}

}

}

}

热更新流程演示

打包

xasset出包流程为Apply Rule

Build Rule

Build Bundle

Build Player

根据我们ET的传统艺能,资源形式大多都是一个个prefab(但是这种做法不提倡嗷,要按正规项目那样分布)

这里以Unit为例,对Unit文件夹应用Prefab规则(各个规则代表的含义可以去前面链接里的文章查看)

对于FUI(我们的FGUI编辑器导出的文件)需要应用两次规则,因为有png和bytes两种文件

然后我们会得到一个如下所示的Rule.asset文件

其中的Scene In Build选项中需要包含我们随包发布的Scene(ET中的Init.scene)

然后我们Build Bundle,就可以出包了

运行

为Global添加Update Mono脚本

其中各个内容含义为:Step:当前热更新阶段

Update Progess:当前热更新阶段进度

Development Mode:是否开启编辑器资源模式,如果开启会使用AssetDatabase.load进行资源加载原始资源,如果关闭会模拟出包环境下的资源加载

Enable VFS:是否开启VFS(对于VFS更加详细的内容,可以去上文链接中查看)

Base URL:资源下载地址,这里我填写的是HFS的资源地址,如果我们使用ET资源文件服务器就是http://127.0.0.1:8080/

然后在脚本调用,即可进行热更新,其中对于热更新各个阶段的进度,都可对Updater的Step和UpdateProgress来取得

await bundleDownloaderComponent.StartUpdate();

资源加载

同步资源加载

以我们加载Hotfix.dll.bytes为例

GameObject code = Game.Scene.GetComponent().LoadAsset(ABPathUtilities.GetNormalConfigPath("Code"));

byte[] assBytes = code.GetTargetObjectFromRC("Hotfix.dll").bytes;

byte[] pdbBytes = code.GetTargetObjectFromRC("Hotfix.pdb").bytes;

异步资源加载

这里加载一在路径Assets/Textures/TargetTextureName.png中的贴图示例

await Game.Scene.GetComponent().LoadAssetAsync("Assets/Textures/TargetTextureName.png");

资源卸载

Game.Scene.GetComponent().UnLoadAsset("Assets/Textures/TargetTextureName.png");

资源内存释放

xasset采用惰性GC的资源内存管理方案,老版本是每帧都会检查和清理未使用的资源(称为灵敏GC),这个版本底层只会在切换场景或者主动调用Assets.RemoveUnusedAssets();的时候才会清理未使用的资源,这样用户可以按需调整资源回收的频率,在没有内存压力的时候,不回收可以获得更好的性能。

fgui的ui管理框架_ET框架FGUIxasset的梦幻联动相关推荐

  1. vue + element ui 的后台管理系统框架_从零开始搭建 VUE + Element UI后台管理系统框架...

    点击右上方红色按钮关注"web秀",让你真正秀起来 前言 后台管理系统前端框架,现在很流行的形式都是,上方和左侧都是导航菜单,中间是具体的内容.比如阿里云.七牛云.头条号.百家号等 ...

  2. 网络请求UI自动切换框架

    1. 概述与分析 在实际项目中,我们不可避免的需要网络请求数据,由于网络或请求方式等主观或客观原因,导致我们请求的结果有时会出现一些偏差,从而导致我们UI界面显示也会有所不同.一般情况下,网络请求后我 ...

  3. 【Vue 快速入门】从零开始搭建 VUE + Element UI后台管理系统框架

    [Vue 快速入门]从零开始搭建 VUE + Element UI后台管理系统框架 前言 后台管理系统前端框架,现在很流行的形式都是,上方和左侧都是导航菜单,中间是具体的内容.比如阿里云.七牛云.头条 ...

  4. Element ui+vue前端框架组件主题美化后台管理系统模板html

    最新设计了一套Element ui主题模板 演示地址:Element ui+vue前端框架组件主题美化后台管理系统模板 Element ui版本号:2.15.12        vue版本号:2.7. ...

  5. 【JavaScript UI库和框架】上海道宁与Webix为您提供用于跨平台Web应用程序开发的JS框架及UI小部件

    Webix是Javascript库 一种软件产品 用于加速Web开发的 JavaScript UI库和框架 Webix用于跨平台Web应用程序开发的JS框架,为您提供102个UI小部件和功能丰富的CS ...

  6. ue4 html ui,UE4用户UI界面核心框架完整资源

    UE4用户UI界面核心框架完整资源. 1.0版本发布日期:2019年11月21日 当前产品版本:1.1 当前版本发布日期:2020年1月2日 RPG用户界面套件提供了用户界面.蓝图.演员交互代码.道具 ...

  7. FGUI,UGUI在ET框架上的使用以及区别

    这两天把FGUI差不多学完了,今天看了点ET框架,发现如何在ET上使用FGUI的文档很少很少.就自己根据初见大佬的ET4.0的斗地主,他的一篇在ET上如何使用FGUI,和同事自己写好的一些界面,了解了 ...

  8. 2018几大主流的 UI/JS 前端框架

    2016年开始应该是互联网飞速发展的几年,同时也是Web前端开发非常火爆的一年,Web 前端技术发展速度让人感觉几乎不是继承式的迭代,而是一次次的变革和创造.这一年中有很多热门的前端开发框架,下面源码 ...

  9. APP UI自动化测试:框架选择、环境搭建、脚本编写……全总结

    首先想要说明一下,APP自动化测试可能很多公司不用,但也是大部分自动化测试工程师.高级测试工程师岗位招聘信息上要求的,所以为了更好的待遇,我们还是需要花时间去掌握的,毕竟谁也不会跟钱过不去. 接下来, ...

  10. 16个Javascript的Web UI库、框架及工具包

    目前,几乎所有的富Web应用程序都依赖一套UI管理,程序库或框架(或工具包),他们不仅简化了应用程序开发,他们还提供兼容的.可靠的及很强交互性的用户界面.除此之外您会请求哪些呢? 当前,广泛应用的We ...

最新文章

  1. 【北大微软】用于视频目标检测的记忆增强的全局-局部聚合
  2. Metasploit中aggregator插件无法使用
  3. php 判断PC 还是 telphone 访问网站
  4. 分享几段祖传的Python代码,拿来直接使用!
  5. 快速了解一门技术的基本步骤
  6. matplotlib.pyplot分区绘图
  7. 14门教程带你全面入门Linux
  8. pythonrandrange_Python3 randrange() 函数
  9. 磁盘及文件系统的管理
  10. Android: android 如何预置APK
  11. 婴幼儿体重在线计算机,宝宝身高体重标准计算器
  12. 猿创征文|一个.Net过气开发工程师的成长之路
  13. 常见硬盘接口技术:从IDE、SCSI到SATA、SAS再到M.2、PCIe
  14. ps如何把自己的图与样机结合_Ps如何套用样机图?
  15. java的jna电脑桌面背景_获取bing图片并自动设置为电脑桌面背景(使用 URLDownloadToFile API函数)...
  16. 程序员应如何提高系统分析能力(转)
  17. os-003-protected-mode
  18. mr编程实现手机流量统计和读取MySQL数据
  19. (2000-2020高精度世界人口密度地图下载分享【附下载链接】
  20. 计算机毕业设计php旅游网站的设计与实现

热门文章

  1. 贪吃蛇c加加代码_贪吃蛇 C语言源代码
  2. 《笨方法学PYTHON》——eighteenthlesson
  3. 怎么在苹果手机计算机上打字,iPhone苹果手机在打字时如何进行换行
  4. 白帽子讲web安全(一)
  5. Synergy两台电脑使用同一个鼠标和键盘
  6. dsoframer java_dsoframer控件动态加载
  7. 实战揭秘地方性社区门户站运营大法
  8. 莫兰迪紫rgb_莫兰迪色系颜色大全 莫兰迪色系适合什么人
  9. 《统计学习基础-数据挖掘、推理与…
  10. Unity 制作艺术字 BMFont