Epoch Dev Blog 4.5

 • 

Unity 的 编辑器 bug 仍在继续,5.3.4f1 及 5.4.0 beta 确认。
所以我没有对核心的东西继续开发(因为一旦调试 Editor 就崩溃啊摔),而是做两件事情。
一个是写 SceneKit 版本的 Epoch,Github 地址 https://github.com/FinGameWorks/Epoch-Remastered,当然这个东西是不可能很漂亮的,毕竟 SceneKit 本身没有提供很有力的工具,不像 Unity 那么容易做出漂亮的效果,只是作为没法继续 Unity 的无聊的替代品。

一个是给 Unity 的 Epoch 添加了 Cardboard 支持,效果真的非常震撼。
以下是两段在 iPhone 6S 上录制的视频
https://www.youtube.com/watch?v=W-R63Cd05bM
https://www.youtube.com/watch?v=NEUoIn9SnlQ

如果有人想要尝试一番的话,请 Twitter/E-mail 联系我告知我您的 Apple ID 邮箱,我会把你加入 Epoch 的 testflight public beta program。
以上。

2016.3.16

 • 


新学期新气象 重修科目要缴费,一个学分八十块,一门课四点五个学分,立马就是几百块。
Apple Developer 账号开始提醒我续期了,这么算下来又是几百块。
手机 Home 按键坏了,换了手机,这么算下来....
穷。以后就是为了省钱,也是要去听听课的....
上周 去看了 疯狂动物城,Zootopia --> Utopia,很明显是有一些寓意的,不同人看的内容和观点也不一样,不细说了。
昨天给 Epoch 加了 Cardboard 支持,感觉很不一样 —— 以往很难表现出太空船的体积,但在 Cardboard 里面转动下头,整个空间感就出来了。

2016.3.13

 • 

....我才发现原来 WWDC Scholarship 是不需要写什么特殊的 app 的,之前没注意看 Guidelines,现在才发现原来就是要写一个“个人简介”的软件。
WTF?

最近的情况大致就是,Epoch 在 Unity Editor 里 Crash 的问题仍然严重,从 5.1.3f1 到 5.3.3p3 都试了,没有一个可用,只能说要么是 Unity 的这个问题太隐蔽了,要么就是我的项目文件彻底地 corrupt 了
(顺便 敬请关注推特账号 @Unity 今天崩溃了么,欢迎 DM 投稿)

于是我闲的无聊,开始把 Epoch 的一些东西往 SceneKit 上搬。大致就是 用 libnoise 生成各种图,Height,Normal,Color 一类的,然后做一个可以根据 Height Map 更新 Mesh 的星球,过程中槽点挺多:

  • SceneKit 作为一个游戏引擎,竟然基本的运算符重载都不给,比如 SCNVector3 * float 都不行
  • WWDC 里面一个 Session 工程师说的大致意思就是 Model I/O 和 SceneKit 无缝结合,但是实际情况是可以互相转化,但是基本没用,因为该不支持的还是不支持,比如带相机全屏特效的 MDLCamera 转换成 SCNCamera 就没特效了,PBR 管道的 MDLMaterial 转换成 SCNMaterial 还是只能用非 PBR 的 Light Model
  • GLSL to SCNProgram / shaderModifier 这块文档略少,我还本来就不会写 Shader,更是看不懂(摊手

然后除去 这些事情 最近还做了

  • 投了腾讯的实习申请
  • 睡觉

Create a Cube Sphere in SceneKit

 • 

前言

Cube Sphere 是一种奇特的球体,不同于 Geodesic Sphere 和一般 Sphere,更像是一个 Cube 通过某种变换得到的 Sphere。

Cube Sphere 有个好处,就是更适合来做过程生成的星球,因为每个面都可以用 QuadTree 无限细分(详见:https://acko.net/blog/making-worlds-4-the-devils-in-the-details/

过程

1.新建一个 SCNBox
2.对其修改顶点(vertices)
3.重新计算 Normal
4.刷新 SCNode 的 Geometry

1.新建 SCNBox

SCNBox *SCNBoxToSphereMapping = [SCNBox boxWithWidth:60.0f height:60.0f length:60.0f chamferRadius:0.0f];
    SCNBoxToSphereMapping.widthSegmentCount = 16;
    SCNBoxToSphereMapping.heightSegmentCount = 16;
    SCNBoxToSphereMapping.lengthSegmentCount = 16;
    
    SCNNode *PlanetNode = [SCNNode nodeWithGeometry:SCNBoxToSphereMapping];
    [PlanetSceneKitView.scene.rootNode addChildNode:PlanetNode];
    
    [SCNTransaction flush];
  • SegmentCount 是 2 的 n 次方 因为后面(当然不是这篇文章)要做 QuadTree
  • [SCNTransaction flush]; 是很关键的一步,(详见http://stackoverflow.com/questions/17760275/geometry-from-scenekit-primitives?lq=1 简单说来,就是不 flush 的话,获取的 geometry data 就是 SegmentCount = 1 的默认 SCNBox 数据,而默认的 SCNBox 顶点只有八个,不足以变换成球体)

2.对其修改顶点

// Get the vertex sources
    NSArray *vertexSources = [PlanetNode.geometry geometrySourcesForSemantic:SCNGeometrySourceSemanticVertex];
    
    // Get the first source
    SCNGeometrySource *vertexSource = vertexSources[0]; // TODO: Parse all the sources
    
    long stride = vertexSource.dataStride; // in bytes
    long offset = vertexSource.dataOffset; // in bytes
    
    long componentsPerVector = vertexSource.componentsPerVector;
    long bytesPerVector = componentsPerVector * vertexSource.bytesPerComponent;
    long vectorCount = (long)vertexSource.vectorCount;
    
    
    SCNVector3 vertices[vectorCount]; // A new array for vertices
    
    // for each vector, read the bytes
    for (long i=0; i<vectorCount; i++)
    {
        
        // Assuming that bytes per component is 4 (a float)
        // If it was 8 then it would be a double (aka CGFloat)
        
        //xyz 3 componet
        float vectorData[componentsPerVector];
        
        // The range of bytes for this vector
        NSRange byteRange = NSMakeRange(i*stride + offset, // Start at current stride + offset
                                        bytesPerVector);   // and read the lenght of one vector
        
        // Read into the vector data buffer
        [vertexSource.data getBytes:&vectorData range:byteRange];
        
        // At this point you can read the data from the float array
        
        //
        float x = vectorData[0] / SCNBoxToSphereMapping.width * 2.0f;
        float y = vectorData[1] / SCNBoxToSphereMapping.width * 2.0f;
        float z = vectorData[2] / SCNBoxToSphereMapping.width * 2.0f;
        
        float SphereX = x*sqrt(1-pow(y,2)/2.0f-pow(z,2)/2.0f + pow(y*z,2)/3.0f) * SCNBoxToSphereMapping.width / 2.0f;
        float SphereY = y*sqrt(1-pow(z,2)/2.0f-pow(x,2)/2.0f + pow(x*z,2)/3.0f) * SCNBoxToSphereMapping.width / 2.0f;

        float SphereZ = z*sqrt(1-pow(x,2)/2.0f-pow(y,2)/2.0f + pow(y*x,2)/3.0f) * SCNBoxToSphereMapping.width / 2.0f;

        
        // ... Maybe even save it as an SCNVector3 for later use ...
        vertices[i] = SCNVector3Make(SphereX, SphereY, SphereZ);
        
        // ... or just log it
        NSLog(@"x:%f, y:%f, z:%f", x, y, z);
        NSLog(@"SphereX:%f, SphereY:%f, SphereX:%f", SphereX, SphereY, SphereZ);
    }

3.重新计算 Normal

SCNGeometrySource *DeformedGeometrySource = [SCNGeometrySource geometrySourceWithVertices:vertices count:vectorCount];
    NSArray *SCNGeometrySourceArray = [NSArray arrayWithObject:DeformedGeometrySource];
    NSArray *DeformGeometryElement = [PlanetNode.geometry geometryElements];
    SCNGeometry *DeformedGeometry = [SCNGeometry geometryWithSources:SCNGeometrySourceArray elements:DeformGeometryElement];
    
    
    MDLMesh *DeformedGeometryUsingMDL = [MDLMesh meshWithSCNGeometry:DeformedGeometry];
    [DeformedGeometryUsingMDL addNormalsWithAttributeNamed:MDLVertexAttributeNormal creaseThreshold:1.0f];
  • 一个自定义 SCNGeometry 需要两样东西,SCNGeometrySource 和 SCNGeometryElements,SCNGeometryElements 在我的理解中就是用来描述顶点之间连接成三角形的顺序。当然实际肯定并非如此简单,只是便于理解我们只是修改了顶点的位置,但连接顺序并没有改变,因此 SCNBox 的 SCNGeometryElements 是可以继续用在新的 Sphere 上的
  • MDLMesh 是 Model I/O 的东西,用的时候和 SceneKit 一起记得导入以下头文件:
#import <SceneKit/SceneKit.h>
#import <ModelIO/ModelIO.h>
#import <SceneKit/ModelIO.h>

4.刷新 Geometry

DeformedGeometry = [SCNGeometry geometryWithMDLMesh:DeformedGeometryUsingMDL];
    PlanetNode.geometry = DeformedGeometry;

记得打开 Debug 的线框预览功能

PlanetSceneKitView.debugOptions = SCNDebugOptionShowWireframe;

效果如下:

Build libnoise on iOS!

 • 

libnoise 是一个 Cpp 的噪声库 写了一个简易的教程 怎么把这个东西做成一个可以 iOS 用的 lib

1.Download libnoise from http://libnoise.sourceforge.net/
2.Navigate to libnoisesrc-1.0.0/noise/src, and select files like this:

No makefile, just .h and .cpp files, and don't select win32 folder, you are building in Xcode.

3.On your current Xcode project menu, Select File/New/Target/iOS/Frameworks & Library/Cocoa Touch Static Library, hit next and name it "libnoise" or whatever :-)
4.Now, Drag files you selected in Finder to Xcode like this:

Things to remember to check:

  • Copy items if needed
  • Create Groups
  • Add to targets, just select the new library we have created.

And now our project will looks like this: (I made a group from these files so you will see a top folder named libnoise)

5.Switch your target to libnoise (or whatever you named it when creating library target), we will check something.

6.Navigate to Build Phases/Compile Sources, see if all .cpp files are in there.

7.Jump back the target to your app, make sure Build Phases/Compile Sources have these:

  • Xcode's target are on your app
  • Target Dependencies have libnoise
  • Link Binary With Libraries have libnoise.a

8.Add libc++.tbd in General Tab, or in Build Phases.
9.Create a UIViewController (like NoiseDebuggerViewController), and change NoiseDebuggerViewController.m to NoiseDebuggerViewController.mm
10.In NoiseDebuggerViewController.mm:

  • import "noise.h"

  • paste these codes to viewDidLoad():
noise::module::Perlin myModule;
double value = myModule.GetValue(1.25, 0.75, 0.5);
NSLog(@"Value : %f",value);

and Press Run, make sure you have real iDevice plugged in instead of Simulator.
See the Log!

Value : 0.686347

Now you have libnoise on iOS! We are ready to use it for procedural planet generation on iOS!

Epoch Dev Blog 4

 • 


👆用来提交崩溃的 Bug Report 都崩溃

Unity 关于 PrepareShadowMaps 还有 Occlusion Culling 崩溃的 bug 在5.3的最新 Patch 版本上仍然存在 让我的进度延缓了不少 因为没办法实时 Editor 预览了 只能自己写一堆东西 试着 build 到 iPhone 上看
所以最近没做什么事情 大致有这几个
1.加了God Ray(Sun Shafts)

2.让游戏根据不同机型调整分辨率 比如 iPad mini 2 这种 A7+大屏Retina 的组合 就运行在 0.75倍率原始分辨率上 然后更老的机型就使用Non-Retina
3.改进了SpeedTree的树木,准备了很多花花草草还有大树

4.研究了下 Noise 图生成 HeightMap 后怎么在 Runtime 生成 Splatmap 留给地面贴图用
5.试着重写树木生成的脚本(但失败了),等脑子清醒并且 Unity 不会那么容易崩溃的时候再试着重写一遍 主要是准备直接从 HeightMap 中 GetPixels 后 把根据 Alpha 值随机出的树木样式数据固化到某个地方 然后做一个 Pool Manager 来管理 Spawn 和 Destroy

多说几句 因为 Unity 工作不正常的原因 最近又看了会 WWDC 学习用了下 ModelIO里面的 Voxel 感觉可以试着用 ModelIO 和 SceneKit 写一个类似的东西(根据噪声的过程星球生成) 如果能写出来的话倒是有个东西可以提交 WWDC 奖学金了

晚安

2016.3.1

 • 

二月底开学 回到了长沙 热得冒汗 感觉都要到了夏天
EPOCH 放在电脑里面总觉得不放心 而且现在两台电脑也不好协作 于是想着用 git 传到服务器上去 但是文件数目太多 即使是用 git shell 也会在 commmit 的时候卡死 于是作罢 改为买 Dropbox 空间 扔到上面自动同步 好处是全自动 坏处是没办法做特定文件的同步规则 以及潜在的被墙风险
生日也过去了 19岁 早晨醒来看到 Twitter 客户端上飘着满屏幕的生日气球 觉得挺好玩 于是多按了几次
Fin GameWorks 算是有了一个小小的网站 http://fingameworks.github.io/ 花了一晚上乱改模板 CSS 做出来的 惨不忍睹 看来有必要学习下前端
最近在看 Vimeo 上面的一些 VFX 视频 比如特效的 breakdown 或者 工作室的 ShowReel 当然也有一些惊人的 SpeedArt 看的人心痒痒 于是跑去申请 Octane Render 的学生证书 但是 OTOY 并不支持国内教育邮箱 对方也一直没有回复

Epoch Dev Blog 3

 • 

最近在做星球的部分东西
昨天和陈叔说 我终于知道我在做什么了
Epoch将会做成一个手机版的 No Man‘s sky,但是并不具备无限的地图过程生成,也没有动物等等,除此以外,Epoch目前在做的功能和No Man‘s sky几乎一样:无缝登陆,过程生成的地表,太空战斗。
无缝登陆这块目前是没有问题,但是在Visual上表现力太差,因为登陆过程要经过一个大气层,这个大气层略高,不好处理地面的物体。No Man Sky的处理方式是加上了一个缓冲用的云层,等云散去,离地表只有一千个单位左右,做大面积的植被也不会有很多性能上的问题。但是我目前没有做这个云层,所以地面树木的生成会被很明显地看到,这个和LOD没有什么关系,就是单纯的突然冒了出来这种效果,因此估计后面我也会做这么一个trick,用云层来阻隔玩家视线,同时生成树木。
地表植被目前只做了树木和草,种类也只有四五种,我也在收集各种SpeedTree,留着后面使用。目前植被生成不是太好,代码也写的很烂,有很多可以优化的地方,主要问题有这几个:
1.草一般是做成billboard,这种模式在第一人称在地面的时候非常有用,可以用很少的物体大面积填充视线。但是这不适用于飞行游戏,飞机是要从天上飞下来的,这个时候billboard的效果就不是很好,因为billboard会让贴图一直面向摄像机所以看上去草角度有问题。解决这个问题我估计只能从改进地面Shader入手,地面贴图先用草,用2K分辨率都不过分,只要能把地面的草给模拟了就好,等到降落后,地面贴图再改成纯泥土,配合很多billboard的草,看起来应该会好些。
2.草不知为什么,并没有被Dynamic Batch掉,结果就是很多draw call,在iPad上运行的效果不是很好。但是我没法做Static Batching,因为我的草并不是真正的静态物体,一旦摄像机掠过草,草就会被移动到前方更远的地方(我还没做pool),保证摄像机看到的是很多草,这样Unity没有办法合并草的mesh,因为都是动态的。
(#Edit : 找到没有 Dynamic Batch 的原因了:我在飞船上加了一个 WindZone 模拟声速飞过的效果,导致 Speed Tree 不能 Batch(对,我的草都是SpeedTree...),关掉风就能看到几乎所有batch都被save了...我一直以为风没有关系)

3.目前树木的生成还不是依据星球的height map,因此是随机分布的,这个其实不难,但我还没写到。
4.海洋的模拟。一般海洋放一个Plane就可以了,但这是星球,是有弧度的,一般的海洋Shader搭配Sphere都会有些问题,我估计解决方式就是,把Sphere拆成一个1/4的弧面,甚至更少,在登陆过程中不断更改mesh,1/4弧面的时候Shader只有反射,保证不是很卡,然后着陆到地平线后再用很小的弧面配合带折射的Shader来做到海洋的效果。
5.天气。这个再说吧,也不是很难做的样子,和树差不多,反正是手机游戏,要求不是很高,没法做很真实,突出风格化就好。

Epoch Dev Blog 02

 • 

回家后战斗力暴跌,基本上是什么也没干。
脑子里有许许多多的计划和项目,但都只存在于我想象中。而每天最多的事情就是发呆,想着这些未竟的计划,真要开始做起来应该用什么方式。
Stillness 的 SpriteKit 重写计划往后推了一段时间 因为我发现 SpriteKit 的光影效果实在是太弱了(当然也有可能是我没有仔细研究 API) 灯光的 FallOff 聊胜于无 而 Stillness 最大的卖点就在于光影,去除光影就没什么了。但我一直在想因为用 SpriteKit 写的话 就可以提交给 WWDC 的奖学金项目,这么一说还是有动力的,只不过我需要花点时间研究下了。
Epoch 倒是开始写起来了,而且进展不错,不过项目一旦发展起来就发现,原来我以前对 Unity 不吃机器的印象也是错的,Unity 是不怎么占用系统资源,但那是相对于虚幻引擎而言。现在每次打开 Unity Editor 都要卡上十秒钟,每次修改完 Script 后预编译也是。
Epoch
而且我遇到了很多奇奇怪怪的问题,比如更新到 5.3.1 后 一个 Shader 彻底不工作,渲染的 Mesh 变粉红色了,我又不会改 Shader 这种东西,只能先替换着。还有各种 Unity 自己的 Bug,以及今天刚遇到的内存泄漏问题(现在 loadLevel 突然就爆内存),然后每次 import assets 漫长的等待(以及假死)让我明白还是不能用 Macbook 做 3D 游戏,等下学期开学还是要买工作站。

先更新到这
放点图