王小兵 2021-07-28T23:36:23+08:00 spencer.wong@hotmail.com UE4入门Roadmap 2021-06-13T00:00:00+08:00 王小兵 https://peakcoder.com/ue4/2021/06/13/ue4-roadmap 1.UE4介绍

UE4介绍 UE4和Unity对比

2.模板介绍和工程创建

安装UE4 创建工程 模块选择, 编辑器介绍

3.Actor操作

Actor介绍(放置,变换,分组,合并) 常见放置Actor 静态网格物体 BSP画刷模式 Landscape地形系统 草地工具

4.蓝图

蓝图介绍 蓝图快速入门指南(Launchpad) 案例-横版游戏 案例-第一人称控制

5.UMG基础+案例

UI创建和显示 UI组件事件绑定 UMG UI设计快速入门指南

6.物理系统和射线

碰撞事件和触发事件 射线检测系统 APEX可破坏网格 第一人称射击游戏

7.动画系统

时间轴 Matinee Sequencer Composure

8.行为树

行为树介绍 行为树节点 行为树快速入门指南

9.材质系统+案例

材质编辑器 基本材质概念 特殊效果(法线贴图,菲涅尔,自发光,彩色半透明阴影 等) 自定义材质函数

10.Paper2D系统

创建Flipbooks 2D物理和动画 2D图集

11.C++脚本基础

C++与蓝图 标签介绍 案例:第一人称射击教程

–EOF–

]]>
URP Tilt-Shift Effect 2021-05-24T00:00:00+08:00 王小兵 https://peakcoder.com/unity3d/2021/05/24/tilt-shift 使用URP + RenderFeature 实现的 Tilt-Shift 移轴摄影效果。github

以上.

–EOF–

]]>
SSD 移动硬盘格式的坑 2021-04-13T00:00:00+08:00 王小兵 https://peakcoder.com/ssd/2021/04/13/ssd 因为穷,个人 MBP 的硬盘只有256G,故狗东买了一块闪迪 1TSSD 移动硬盘作为辅助存储.

公司用PC,回家 MacBook,初衷是两种OS可以读写. 以下是我在选择硬盘格式时候遇到的一些坑.

1] 一开始使用的 exFAT 格式

因为这格式倆 OS 都可直接读写.但在拷贝一个Unity工程到硬盘的时候,发现在 NTFS 60G 到移动硬盘里变 400 多G了,靠北!我可只有1个T而已啊. 一番google之后发现:32G 以上的 exFAT 格式硬盘默认簇大小128K,即使大小不到1K的文件也要占128K. 又Unity工程文件全是细碎小文件,故膨胀至此.

2] 使用 NTFS

NTFS簇只有4K,应该就不会浪费空间了.在使用Mounty啊之类的软件之后Mac也能写硬盘.似乎..完美了. 但当老夫发现无法在硬盘打开创建Unity工程!提示The project is on case sensitive file system.Case sensitive file systems are not supported at the moment.Please move the project folder to a case insensitive file system. 又一番google,好吧,NTFS是大小写敏感的,只是Win默认关闭了. 在Mac上大小写敏感,而且还关不掉,汗,只能放弃这格式了.

3] 再使用的 exFAT 格式

算了还是使用exFAT吧,因为之前检索发现 exFAT 直接使用4K簇大小,只要容量不超过16T也是可以的.但默认格式化的时候最小簇只能设 64K 微软太恶意了! 能不能把簇改小呢?又双叒叕一番google发现对于系统自带的format命令行格式化程序则没有对exFAT文件系统施加任何人为限制,那么解决方案自然就是:先以管理员身份运行CMD命令提示符,注意空格输入:Format X: /FS:exFAT /Q /A:4096 /Y 即可将任意类型的盘快速格式化为exFAT-4K簇,其中X:为要格式化卷的盘符,/A:参数为自行指定一个簇大小.或者直接使用DiskGenius等第三方软件来格式化为exFAT也是可以的.

似乎又完美了,都可读写,速度也没有太影响.

但天不遂人愿,又有新的问题,因为只能在 NTFS 格式硬盘上创建软连接,所以在 exFAT 格式的盘上,Unity2019package 全都会拷一份到项目内. 这也还好了,因为用Mac APFS 也一直如此,但只会拷贝一次,第二次打开就很快了.但是 WinexFAT 的硬盘,Unity每次都会拷贝!很慢啊,这绝对 Unity bug,太恶心了,但也只能 file a bugUnity 解决了,没啥办法,哎.

以上.

–EOF–

]]>
新应用「晴书」发布 2020-12-24T00:00:00+08:00 王小兵 https://peakcoder.com/%E7%8B%AC%E7%AB%8B%E5%BC%80%E5%8F%91/2020/12/24/new-app

虽说苹果爸爸圣诞放假暂停审核,但还是在平安夜approve了,ready for sale. 欢迎大家下载。

PS: 开发此App的动机之一是为了泡姑娘,估计追不到了…

–EOF–

]]>
Unity3d gradle 编译后处理 2020-12-14T00:00:00+08:00 王小兵 https://peakcoder.com/android/2020/12/14/gradle-copy-file Unity 可以在 Android 目录直接放置 java 源文件,加上自定义 gradle 配置文件,使得接入第三方的 SDK 变得十分优雅。 Unity 使用 gradle 编译,直接导出 apk ,会在 tmp 文件下面先导出名为 gradleOut 的 android studio 工程。 在 as 工程导出后、编译前,可以做一些文件拷贝处理等工作,例如在接入 Firebase SDK 时需要拷贝 json文件到 launcher 目录。 代码入下:

using UnityEditor;
using UnityEditor.Android;
using UnityEngine;
using System.IO;
public class AndroidPostBuildProcessor : IPostGenerateGradleAndroidProject
{
    public int callbackOrder
    {
        get
        {
            return 0;
        }
    }
    void IPostGenerateGradleAndroidProject.OnPostGenerateGradleAndroidProject(string path)
    {
        Debug.Log("Bulid path : " + path);
        path = path.Replace("unityLibrary", "launcher");
        var json = path + "/google-services.json";
        File.Copy("Assets/Plugins/Android/google-services.json", json);
    }
}

–EOF–

]]>
Android ANR 2020-10-14T00:00:00+08:00 王小兵 https://peakcoder.com/android/2020/10/14/android-anr ANR全称:Application Not Responding,也就是应用程序无响应。 logcat 定位不到具体哪个线程导致 ANR,可以通过 ANR 日志分析来定位问题。

当app出现ANR时会在data/anr/目录下生成traces.txt日志文件。每次发生ANR时都会删除旧的traces文件,重新创建新文件。Android只保留最后一次发生ANR时的信息。 可以使用adb命令导出traces文件:

adb pull /data/anr/traces.txt

默认导出文件路径在 adb 命令行运行的当前的目录。

一般trace文件顶部的线程即为ANR的原因。

–EOF–

]]>
NavMeshAgent SetDestination Error 2020-06-18T00:00:00+08:00 王小兵 https://peakcoder.com/unity3d/2020/06/18/navmesh-bug 使用 NavMeshAgentSetDestination函数,一直报错 "SetDestination" can only be called on an active agent that has been placed on a NavMesh. 导致寻路失败。

导航网格生成完好,Destination的点使用 NavMesh.SamplePosition 验证过,也定会在可行走区域内。

Debug许久未果,后在Unity Forum找到答案。

If you create a NavMeshAgent and set its position via transform.position=... and then try to SetDestination, it fails because the NavMeshAgent did not recognize the position change and does not know that it already is on the NavMesh. Use NavMeshAgent.Warp instead of transform.position to initialize the position before calling SetDestination.

开始时直接使用transfrom.poistion赋值,agent未识别位置变化,不知道自己已在导航网格之上,可在初始时调用Wrap函数来初始化agent的位置。

示例代码如下:

agent.Warp(tank.BornPosition);

–EOF–

]]>
Unity Tips (#2) 2020-06-08T00:00:00+08:00 王小兵 https://peakcoder.com/unity3d%20tips/2020/06/08/unity-tips-2 使用 ContextMenu Attribute 不运行 Unity, 执行 MonoBehavior 函数 示例代码如下:

[ContextMenu("ResizeDialogueText")]
private void ResizeDialogueTextHeight()
{
	var height = dialogueText.preferredHeight;
	var size = dialogueBgRect.sizeDelta;
	size.y = height + 60f;
	dialogueBgRect.sizeDelta = size;

	var pos = dialogueText.transform.localPosition;
	pos.y = height + 30f;
	dialogueText.transform.localPosition = pos;
}

Diagram

–EOF–

]]>
使用 MaterialPropertyBlock 2020-05-28T00:00:00+08:00 王小兵 https://peakcoder.com/unity3d/2020/05/28/material-property-block 前些天做性能优化使用 Frame Debugger 发现坦克凭空多了很多dc,每一个负重轮都需一个dc,一下多了十几个,同一材质却不能合批,这不合理。 后发现坦克涂装做decal贴图,抓出material设置贴图导致 (在材质上读取设置任何参数都会导致Instanced) 虽然是同一材质,参数也完全相同,只要Instance了,就无法合批。

嗯,使用 MaterailPropertyBlock 可以阻止材质Instance,继而能够合批,有效降低dc数目。

示例代码如下:

private MaterialPropertyBlock decalMPB;

void Start()
{
	decalMPB = new MaterialPropertyBlock();
}

public void SetExclusiveDecoration(string path)
{
	if (allRenderTexDic.Count == 0) 
		InitializeMaterialDic();

	var tankKeyname = tank.ProtoKeyname;
	foreach (var k in allRenderTexDic.Keys.ToList())
	{
		var tex = Resources.Load(path) as Texture;
		if (tex == null) continue;
		
		k.GetPropertyBlock(decalMPB);
		decalMPB.SetTexture("_CMap", tex);
		k.SetPropertyBlock(decalMPB);
		allRenderTexDic[k] = tex;
	}
}

–EOF–

]]>
查找 Unity ManagedStaticReferences 2018-06-11T00:00:00+08:00 王小兵 https://peakcoder.com/unity3d/2018/06/11/untiy-static-ref 当我们使用 Unity Profiler 查看内存时,经常有些贴图等资源的引用只有一个 ManagedStaticReferences() 引用,怎么都卸载不掉。 使用 Memory Profiler 也查找不到谁引用的。现在终于找到方法,开心,来分享给大家。

具体思路:

  1. 维护一个 key 是 物件 Hierarchy 路径, value 是 WeakReference 的字典,收集所有可能会泄漏的组件
  2. 在需要 Check 的物件挂靠的脚本里 添加该 Componet 的 弱引用到字典
  3. 查看 Alive 状态,Alive 为 true ,但 target 为空的即为泄漏者,打印他的路径
  4. 如果单纯查找 UI 贴图的内存泄漏,例如 NGUI 可以只在 UIWidget 的 Awake 里添加弱引用到字典, UGUI 的话需要下载 UGUI 源码 在 Graphic 构造里添加引用,再打包 dll 来测试。这样就可以覆盖所有 UI 贴图
  5. 其他的,怀疑那个脚本,就把他的引用添加到字典里

代码如下:

public class ComponentReferenceManager
{
	public static ComponentReferenceManager Instance = new ComponentReferenceManager();
    Dictionary<string, WeakReference> refs = new Dictionary<string, WeakReference>();

	private int count = 0;
	public void AddRef(Component c)
	{
		var key = string.Format("Index:<color=red>{0}</color> ComponentType:<color=red>{1}</color> GameObject:<color=red>{2}</color>", 
				  count,  c.GetType().ToString(), GetGameObjectPath(c));

		refs[key] = new WeakReference(c);
		++count;
	}

	private string GetGameObjectPath(Component c)
    {
		var obj = c.gameObject;
        string path = "/" + obj.name;
        while (obj.transform.parent != null)
        {
            obj = obj.transform.parent.gameObject;
            path = "/" + obj.name + path;
        }
        return path;
    }
	
	public void PrintLog()
	{
		GC.Collect();
		Debug.LogError("打印销毁了但还被引用的物件:");
		foreach(var kv in refs)
		{
			if (kv.Value.IsAlive)
			{
				if (kv.Value.Target == null)
				{
					Debug.LogErrorFormat("Target is null {0}", kv.Key);
					continue;
				}

				var w = kv.Value.Target as Component;
				if (w == null)
				{
					Debug.LogErrorFormat("Component is null {0}", kv.Key);
					continue;
				}

				if (w.gameObject == null)
				{
					Debug.LogErrorFormat("Component attached game object is null {0}", kv.Key);
				}
			}
		}
	}
}

UIWidget 的 Awake 里 或者你怀疑的其他脚本里添加调用

	#if UNITY_EDITOR
		ComponentReferenceManager.Instance.AddRef(this);
	#endif

编辑器脚本

public partial class ReportEditor
{
	[MenuItem("Jinggle/打印销毁了但还被引用的物件")]
	static void ReportUIRef () 
	{
		ComponentReferenceManager.Instance.PrintLog();
	}
}

这样就会找到所有在场景里已经销毁了,但还是被引用到的物件了。 下一步就是要查为什么明明被销毁了,还会被引用的原因了。

经过我排查我们游戏,发现导致 ManagedStaticReferences() 绝大部分原因都是 static 变量的使用不当。 真真儿是 静态变量一时爽,内存泄漏火葬场。

–EOF–

]]>