Windows 编译 AOSP 趟坑笔记

  • 首先 AOSP 是不支持在 windows 系统下直接编译的,目前官方也不再支持 Mac OS ,所以只能搞个 Ubuntu 环境。
  • 我首先尝试了 wsl ,安装 Microsoft Store 官方的 Ubuntu 。这条路实际上并没有走通,不过我还是分享一些关键点。

首先,要保证代码所在文件夹必须是大小写敏感的。使用管理员模式运行命令行,执行下面命令,开启文件夹的大小写敏感。


这里要注意一点,建议先处理好文件夹再下载代码,因为这条命令只能操作一层文件夹,如果已经下载完的代码,再想改变所有文件夹的大小写敏感状态就需要写脚本去处理了,会很慢。

其次,Ubuntu 要切换到 wsl2 ,具体方法可以搜索。

然后按照官方文档的方法安装环境和同步代码,如果访问官方代码库速度不理想也可以用国内清华或者科大的镜像,具体操作在他们镜像站都有。

访问 windows 文件夹的话,盘符在 /mnt 文件夹下。

我因为代码下载到了 windows 路径中,导致无法启动编译。会一直报系统不支持的错误:

已经提 issue
https://github.com/microsoft/WSL/issues/7283


现在切回到主线,我后来尝试了使用 docker 。可以成功的启动编译了,但是访问 windows 路径会及其慢,一次编译用了10几个小时(配置:i7 + 32G 内存)。

  • windows 下不论是 wsl 直接安装的 Ubuntu 还是用 docker,都不建议直接访问 windows 路径。
  • 在 Ubuntu 下直接同步代码,按照官方文档进行编译,可以很顺利的完成。
  • 不过如何从 docker 启动模拟器我目前还没找到好办法,可能会考虑电脑直接装双系统了。

解决 iOS 的 SSH 连接不上的问题

要用 SSH 连接 iPhone 手机,当然要越狱才行。当然越狱不是这篇的重点,就不多说了。
然后手机需要装有 OpenSSH ,通过 Cydia 就可以安装,很简单。
然后就遇到问题了,用的是一个 iPhone 6,系统 10.3.3 。发现即使都安装好了,使用 SSH 还是无法连接。

查资料的过程就不介绍了,解决方案如下:
首先需要在手机上安装一个命令行 App ,自己找 Cydia 的源就好了,只要能用就行。
使用命令行先停掉 sshd 的 plist

然后用如下命令启动 sshd

之后就可以在电脑上使用 ssh 操作我的苹果手机了。
没有在其他设备和系统测试过,不知道能不能解决所有问题,希望对看到这篇博文的你能有一点帮助。

Unity3D Custom Render Texture Shader 编译到低版本 Shader model 环境

写好的 Custom Render Texture 的 shader 在打包的时候报出了一个错误:

查资料发现 asuint 这个函数是在 Shader Model 4 (SM4) 才开始支持。
然后稍微看了一下 cginc 里边的代码,发现只有一个地方用到了这个函数——是把浮点类型的 primitive ID 转成 uint 类型。

这个 primitiveID 是 update zone 的当前正在处理的索引,而我目前并没有使用很多 update zone 来实现效果的需求,所以决定尝试一下,直接将 float 类型强转成 uint 。

操作步骤:

  • 在自己的工作目录下创建一个自己的 CustomRenderTexture 的 cginc ,也就是新建个文本文件,把后缀改成 .cginc。
  • 把 UnityCustomRenderTexture.cginc 中的代码全部复制到自己新建的 cginc 中,并且按照上面代码修改。
  • 别忘了修改一下头部的宏定义名称 #ifndef UNITY_CUSTOM_TEXTURE_INCLUDED 跟默认的做一个区分。
  • 在 shader 代码中引用我们自己的 cginc ,就可以用了。

Unity3D UGUI 基础布局

1. 轴点 (Pivot)
作为控件的“轴”,在控件状态改变时对最终布局产生影响。比如,旋转的时候控件就以轴点为中心旋转,调整尺寸或缩放的时候轴点会固定在原位置。

2. 锚点 (Anchor)
Anchor 看起来的样子是四个相对的三角形。Anchor 决定了当前控件与父控件的位置关系。控件的 Rect Transform 的坐标 Pos X 和 Pox Y 就以 Anchor 为原点。也就是 Pivot 会放在 Anchor + (Pos X, Pox Y) 的位置上。当父控件状态(旋转、缩放、移动等)发生变化时,控件会保持这个关系跟随父控件。
在 Anchor 选择面板,按住 Shift 选择会同时将 Pivot 设置在 Anchor 选择的相同的位置;按住 Alt 选择会将控件位置同时停靠在 Anchor 位置。如果要同时将 Pivot 和位置放在 Anchor 点,就同时按住 Shift 和 Alt。

3. 尺寸自适应
也可以选择自动伸展的 Anchor 比如,全屏或者 X 轴或 Y 轴方向自动伸展。这个时候在 Rect Transform 中的坐标或(和)尺寸设置会变成 Left Right Top Bottom 。这些值是相对 Anchor 自动伸展方向的距离。这个概念是非常有用的,可以实现控件相对父控件同比例缩放。具体效果看我撸的视频会比较清楚一点。

Unity3D 解决默认碰撞检测的缺陷 —— 实现理想的匀速直线运动

首先介绍一下默认碰撞检测存在的问题。先不提【我只想要你丫告诉我碰撞盒撞上了,却非得要我挂个钢体组件,还得接受重力,阻尼,角速度的影响。不是在逗我?】,那么我们手动勾选上钢体的 Is Kinematic 属性,自己来控制。
我们看下边的图:
collider1
当这个圆走到实线圆圈位置的时候,就会触发碰撞检测回调。当然,如果是在虚线圆圈的位置,回调是不会被触发的。那么问题来了,如果一帧的移动距离刚好是从虚线位置移动到实线位置,情况就是这样的,移动前一帧不会触发碰撞回调,而后一帧两个碰撞体已经嵌到一起了。
当然,如果只是小小的插入一下,也并没有什么问题,接下来看下面的图:
collider2
当一帧的移动距离超过碰撞目标的时候,奇迹发生了,这时碰撞检测的回调根本不会触发。也就是说在速度足够快的情况下,你的墙是挡不住你的球的。
为了实现一个在理想条件下的匀速直线运动,我就用 Unity 本身的射线检测功能来解决这个问题。
首先取出自己的碰撞盒
Collider 中有 Raycast 和 Cast 两个方法,参数差不多,具体的区别,还得画两张图:
ray1
图一
ray2
图二
ray3
图三
图一、图二中蓝色的线就是 Raycast 的结果,是从自己的中心射到目标碰撞盒上,如果按照这个射线的距离运动,结果就是图二的样子。
图三种的蓝色线是 Cast 方法的结果,射到目标碰撞盒上的是自己碰撞盒与目标碰撞盒的交点。这个距离就是从当前点移动到发生碰撞的点的距离。
简单一点就用 Cast 方法就够了,Raycast 比 Cast 提供了更多细节操作,比如专门针对某一层的检测等,如果需要做更细节的优化,就得自己用 Raycast 方法来处理。
RaycastHit 中比较重要的属性:
collider —— 目标碰撞盒
distance —— 到碰撞点的距离(上面图3中蓝线的长度)
normal —— 碰撞点的法线
point —— 目标碰撞点
接下来的逻辑就是:
比较本帧移动距离和 distance ,如果本帧移动距离比较小,就直接移动。
当然之后要把总距离减去这步的移动距离。
如果这一帧的距离大于剩余的距离了,就需要处理反射了。
先移动到碰撞点→方向向量反射→继续移动完。
打完收工。
当然这只是简单的描述,实际上还会遇到一些坑,比如当前自己和一个碰撞盒有重叠的时候,取到的第一个 RaycastHit 永远是有接触的这个碰撞盒,此时与方向向量无关,所以需要根据自己的逻辑处理。还有同时碰撞两个或多个碰撞盒等,都要小心处理。

libgit2使用教程(十三)git rebase

rebase 就是将制定目标分支,或者制定 commit 所在的一条路径,直接插到当前分支或目标分支。(好像有点乱,具体的东西自己去查吧)

简单点说,这不是合并。

当然,虽然不是 merge 但是也会有冲突的可能,所以中途有解决冲突的需要,所以 rebase 操作是分阶段进行的,因此逻辑会复杂一些。

首先尝试打开现有未完成的 rebase

如果存在未完成的 rebase,可以选择继续将其完成,或者把它终止掉

在 git_rebase_open 返回 -3 (也就是 GIT_ENOTFOUND ) 表示当前仓库并没有其他未完成的 rebase 可以放心大胆的从头开始搞。

接下来创建一个 rebase

init 的第三个参数是需要被操作的分支,传空表示当前分支;第四个参数和第五个参数这俩二选一,前者表示以一个 commit 为节点把到这个节点为止的一条链合并到目标分支;后者是直接选一个分支的最新一个 commit ,将这个分支的整条链合并到目标分支。

当然,这个时候工作区不会有任何变化。到 .git 文件夹里面会看到多了一个叫做 rebase-merge 的文件夹。如果这个时候程序被终止,这个文件夹会保留,在下一次启动的时候,就可以通过 git_rebase_open 打开这个 rebase 。

接下来就是实际执行 rebase 这个操作

这里可能存在遍历,但是为什么会有多个 operation 我也还没搞太明白,不过为了避免出事,还是循环调用保险一些:

接下来,不要忘记查看是否有冲突,需要将冲突解决才可以做后边的 commit 的操作:

在解决完冲突,并且 add 之后,就可以 commit 了。这里并不需要使用 commit 的 create 接口,rebase 部分提供了 rebase 专用的 commit 接口。

最后,做一下收尾 finish 掉这个 rebase 操作

示例代码:sample13

libgit2使用教程(十二)git tag

tag 的作用是作为重要节点的标记,在需要的时候可以直接切过去。所以在 git 管理系统,比如 github ,直接以带信息描述的 tag 作为 release。并且通常也习惯以版本编号作为 tag 名。

首先来看看仓库里有哪些 tag ,libgit2 提供了两个接口,它们有不同的作用。
1. list

输出的是 tag 的名字,并且没有更多的详细信息。
2. foreach

这个接口是通过回调的方式遍历所有的 tag。回调中的参数 name 返回的是 tag 的完整路径(refs/tags/tagname 这种形式,这样看实际上它也是一种 reference),这一点要特别注意;oid 是这个 tag 的 oid,如果要获取这个 tag 对象的指针,则需要使用 lookup 这个接口去获取——这个接口需要仓库指针作为参数,所以仓库指针需要通过 payload 传入。lookup 的时候有可能会出现找不到的情况,那是因为只有带描述的 tag 才能被找到,也就是说只有名字的轻量级 tag 是找不到的。

然后有必要解释一下什么是轻量级 tag 么?
轻量级 tag 是这个 tag 只有名字,没有其他信息。通过 git tag [tagname] 这个命令来创建;
除了轻量级 tag 之外还有一种就是带附注的 tag,这个 tag 要求创建的时候输入一段描述信息,通过 git tag -a [tagname] -m [message] 这个命令创建。

因此使用 libgit2 创建 tag 也有不同的接口来实现。
1. 轻量级 tag

因为 tag 是一种 reference 所以,它一定是以 commit 为节点的,所以 target 参数要找一个目标 commit。

2. 带附注的 tag

相比 lightweight 版本多了两个参数,一个是提交者的信息,另一个是这个 tag 附带的信息。这个信息在 github 的 release 中会对外显示。

下面介绍一个模糊搜索的功能,对应的是 git tag -l “*.*” 这个命令:

接下来是删除 git tag -d [tagname]

如果 error 返回的是 -3 也就是 GIT_ENOTFOUND 这个宏,表示并没有找到输入的这个 tag。

接下来,tag 作为一个标记,就是为了作为一个目标点可以直接切过去。使用命令行工具的命令是 git checkout [tagname],它实现的是将当前 head 切成一个空分支,这个分支指向的就是目标 tag。
然而,在代码中这个操作并不是通过 checkout 系列的接口实现,我们的目标是把对象换到 head 所以,需要的是设置当前的 head。

这里的重点是第二个参数,需要这个 tag 的 reference 形式的全名,在 foreach 的回调中可以获取到,在文件目录里也可以找得到。

最后,要将 tag 上传到远端仓库,需要在 push 的时候显示的指出。就像命令行:git push origin –tags。在代码中的重点就是 push 接口的 refspecs 参数,由这个参数指定 push 什么东西。

示例代码:sample12

libgit2使用教程(十一)git push

将本地分支上传到远端,如果前面的各个功能都掌握了的话,这个接口可以算是非常简单的了。

需要注意的细节就是 1. 身份验证;2. refspecs

验证证书的回调,是通过 git_push_options 这个结构传递的,与之前一样的 callbacks 参数。详情可以参考前面的

refspecs 是用来指定这个 push 操作的本地分支和远端分支。它的格式是本地分支远端目标分支中间以冒号隔开。

比如我要把本地的 master 推到远端,碰巧远端对应的分支也叫 master,那好,它就是这个样子的:
refs/heads/master:refs/heads/master

当然,我们必须制定远端目标,因为它不一定仅仅是 origin:

那么最终,push 操作的代码就是:

示例代码:sample11

libgit2使用教程(十)git clone

这是一个非常重要的操作。
这是一个非常简单的接口。
这是一个没多少内容又不得不提的主题。

直接上代码

需要注意的就是不要忘记身份验证,然后就开始 clone 操作了。

最后就简单介绍一下 git_clone_options 的主要参数:

checkout_opts
checkout 的配置,如果需要的话

fetch_opts
fetch 的配置,如果需要的话

bare
0 表示标准仓库,1 表示 bare 仓库

local
决定是否拷贝目标数据库而不是 fetch 。(如果目标是在本地,直接拷贝会更快一些)

checkout_branch
切到哪个分支,如果是空使用远端默认的分支

repository_cb
在这个回调里可以自己创建一个 repository

remote_cb
在这个回调里可以自己创建一个 remote

关于 bare 这个参数,不仅仅在 clone 这个操作用到。在 init 或 open 也是同样的意思。
表示只有版本记录,没有实际的工程里的代码和文件那些东西。更具体一点就是除了 .git 这个文件夹以外啥都没有。

示例代码:sample10

libgit2使用教程(九)git fetch & pull

目前 libgit2 对 pull 操作的支持还不是太好,所以目前能找到的资料都指出 pull 操作就是先 fetch 然后再 merge 目标分支。比如要实现 git pull origin master ,就先 fetch 然后将 master 切换到 head 再将 origin master 合并到 head。

那么就先 fetch

git_remote_fetch 这个接口的后三个参数都是可以传 null 的,但是如果报了下面这个错:
authentication required but no callback set
说明远端地址需要验证身份,所以我们要设置 ssh 证书的回调。

这样就完成了 fetch 操作。so easy

prune 也是 fetch 操作的一个重要参数。表示:清理掉远端已经删除的分支对应的本地分支,有点绕,不过应该还好理解吧。具体信息可以查一下下面这个命令。
git fetch -p (或 git fetch –prune)
在代码里实现只需要在 option 参数里做一个设置:

这样就强制使用 prune 参数了。另外几个可选枚举值可以去读注释。

接下来为了实现类似 pull 的效果,就是蛋疼的合并操作了,顺便可以复习一下之前的内容。
首先将本地 master 设为 head

然后去拿 origin master 的 commit

合并

解决冲突

add 和 commit

清空状态

打完收工。

示例代码:sample9