抠腚爱揉曼 Coding Iron Man

28Dec/113

虚跟模板可以兼得?!

Posted by Jay

由某个案例引发出来...

为什么要虚

在OOP的开发过程中,往往会有大量对象的定义和使用,很多时候我们需要将这些对象持久化的保持在外部文件里面,然后再在需要的时候将其读取进来。因为对象的类型数量都很多,一个一个去实现如何写入和读取就特别冗余且笨重。常见的解决方式便是序(反序)列化,简而言之就是为对象的基类都加上serialize/deserialize的虚方法,然后子类只需要实现如何组织需要写入和读取的数据就可以了。此后我们只要有一个serializable的对象便能对其进行读写了。

为什么要模板

但是关于具体的序列化的时候,便会遇到一些跟平台相关的问题,比如不能确定这个对象最后写入到的是一个本地文件、网络流还是JSON这样的中间载体的时候该怎么办?因此我们提供给序列化接口的序列化器这时变得不那么确定了。此外我们也不希望对象的序列化接口明确的知道序列化器是什么,因为这样会让其产生依赖使得对象的使用不独立。综上,我们希望一种模板化的序列化器,只有到对象用到它的时候才会被具体的展开使用。

虚跟模板可以兼得?!

所以我们需要给对象的基类定义一个方法看上去会是:

template<typename Serializer>
virual void Serialize(Serializer& serializer);

但是遗憾的告诉你这是不可能的。为什么?
首先抛开C++的标准不说,单从编译器的实现上面就能否定这一点。虚函数具体是通过虚函数表来表现其多态性的,这个虚函数表实际上是一个函数地址的数组,在建立这个虚函数表的时候所有的虚函数都需要被实现了,这样才能找到有效的函数地址。但另一方面模板方法本身是并不存在的方法,只有在被用到的时候才被具体展开并实例化。因此模板方法的“不确定”性与虚函数的“必须确定”性在理论上便已经矛盾冲突了。

解决方案

首先,为了模板化,不得不先将virtual去掉,这样要求所有对象里面的序列号方法被重新声明为:

template<typename Serializer>
void Serialize(Serializer& serializer);

但是作为序列化方法的调用者,它所能拿到的是所有对象的基类的指针或者引用,它也不能直接调用到子类里面的具体方法。为此目前我能想出的办法就是向下转型,例如:

void Serialize(Base& base)
{
    FooSeraizlier fooSerializer;
    switch(base.GetClassId())
    {
    case DeviedA::ClassID:
        (static_cast<DeviedA*>(&base))->Serialize(fooSerializer);
        break;
    case DeviedB::ClassID:
        (static_cast<DeviedB*>(&base))->Serialize(fooSerializer);
        break;
    case DeviedC::ClassID:
        (static_cast<DeviedC*>(&base))->Serialize(fooSerializer);
        break;
    ......
    }
}

无奈的是类似这样switch case的向下转型必须要手写的,因此当类型过多的时候肯定是很悲剧的,但是如果你有类似python一类的脚本工具来帮你自动生成代码的话,那就放心的使用吧。

Tagged as: 3 Comments
15Nov/110

关于脚本

Posted by Jay

一些关于脚本的琐碎的思绪。

如果说Programming Language是对应基础框架以及底层兼容的偏硬性问题的解决方案,那Script Language便是某种意义上真正面向应用功能的解决方案。Programming Language的初衷是调用平台的各种资源与功能,为了达到最好的效果往往都是贴近底层开发的,在这方面“硬”的东西比较多,而且一旦敲定,便不会容易修改。简而言之,Hardcode大概就是这样一种意思。

太硬的东西无论是开发者还是客户都所不愿意见到的,他们不希望自己买了一辆自行车,轮子只能转到100圈,除非他们都是2B。

于是乎有一种叫Data Driven的概念出现了。大约来讲它属于已经被Hardcode编译成计算机本地指令的程序的外部配置信息。程序的代码已经实现了开发过程中能够预见的大多数功能,这些功能可以接收一定的可变的参数来达到一定程度的“动态性”。这基本上是目前绝大多数项目的一种比较统一的解决方案,无论是application,service或者game。

这种开发模式有一个比较重要的平衡点需要掌握:我们需要将多少功能给做出来,又需要将多少功能可以进行外部的配置。前者过多就会导致过多的硬编码,而后者过多就会导致过于零散的功能以及过于复杂的配置。

无论如何,硬编码是我们是在不想看到的,但是如果功能做得过于原子化,那外部的配置必然会相当复杂。要知道在一个团队里面,负责这部分数据生产的不一定都是城续猿。

有时候我们不能太明白的说明,我们到底想要一个什么样具体的功能,城续猿没办法做出一个抽象的模棱两可的功能然后再暴露一些有限的参数供团队里面其他成员使用,而后者往往也说不清楚自己到底想要什么样的东西。但就算双方能达成协议,通过修改代码来实现新功能以及调试的代价往往也是比较昂贵的。

那么,为何不让他们自己去做?

于是我们需要一种,简单,可用,且能比较高效开发的方式 —— 脚本。

理论上来讲脚本也是一种编码语言,它同样有分支,有循环,有变量,只不过其所在的层次不一样。脚本其实也是Data Driven里面的数据,只不过与单纯的贴图,动画,音效等纯数据相比它更有一种自我描述性。换言之它们能自己说明自己的目的,而不是简单的被贴到某个设置好了UV的模型上面去。

城续猿可以做好一系列的相对独立的功能,这些功能之间可以拼凑组合,相互调用,往往我们会将它们称之为API,最终它们会暴露给脚本使用。

在此基础上,如果说通过Programming Language实现的框架模型是“第一次开发”,那么通过Script Language我们可以在项目中进行“第二次开发”。这也就是一种理想的“准备阶段”往“生产阶段”过渡的方式。案例例如游戏中各种角色的AI,任务,界面UI等等。

待续…

Filed under: 抠腚 No Comments
11Apr/115

关于UI的若干纠结

Posted by Jay

话说最近哥对UI的感情那是各种的错综复杂,似乎业界使用Flash来做In Game UI逐渐成为主流。望着ScaleForm各种强大和稳健,看着自家的Homebrew的UI库,各种兴叹,无奈各种BOSS光环哥只能将错就错了。
既然说到UI不得不提到的就是走哪里都必须很吃香的PS各种版本,做UI总得画个图切个图啥的伐。不过PS各种切,Flash再各种导入,各种费时又费力。不过人家大牌厂家的东西都不是省油的灯,jsfljsx简直就是拯救哥于水火之中的各种救命草。(话说为什么我每次提到这些上面的人貌似有些漠视的感觉......)
简单来说Flash是支持只用导入一张完整的大图,然后基于这张大图创建若干小元件(Symbol,Movie Clip或者Graphic)的,原理就像CSS Spirit。不过如果你想人肉在Flash里面去切那你注定会各种杯具的,因为你会发现Flash的切图功能简直渣到几乎没有。
于是你必须需要jsfl了。我们可以通过jsfl来精确创建各种元件,所谓精切就是说你可以直接像素级别定为图块的各种上下左右而不是靠鼠标凭手感去割,于是:

// Get the document
var doc = fl.getDocumentDOM();
// Get the library
var lib = doc.library;
// Get the selected item in library
var libItem = lib.getSelectedItems()[0];
// Get current fill object
var fill = doc.getCustomFill();
// Set the style to "bitmap", available since CS4
fill.style = "bitmap";
// I don't know what is this neither
fill.bitmapIsClipped = false;
// Set the name of the selected item to the fill object
fill.bitmapPath = libItem.name;
// Well, to get some parameters about this bitmap, you have to instantiate one in the scene first
lib.addItemToDocument({x:0, y:0}, libItem.name);
// And then get the instance
var bitmap = doc.selection[0];
// Then you need to create a mystery matrix
var matrix = bitmap.matrix;
// The spirit's left bound
matrix.tx = -l;
// The spirit's top bound
matrix.ty = -t;
// God knows what does it mean
matrix.a = matrix.d = 20;
// Or Adobe either
matrix.b = matrix.c = 0;
// Set the matrix to the fill object
fill.matrix = matrix;
// And use it
doc.setCustomFill(fill);
// Then it is time to create the spirit
doc.addNewPrimitiveRectangle({left : 0, top : 0, right : r - l, bottom : b - t},0,false,true);

这随之而来就带来了第二个问题,我如何知道每个Spirit的各种上下左右边界,目测加人肉记录似乎可行,不过面对成百上千的各种图的时候,你只会喊破喉咙都没人能救你。 于是jsx这次向你伸出了上帝之手。
只要你在PSD里面将图按照每个Spirit一个图层的方式进行管理,并且每个图层之间相互没有重叠部分,那就可以通过jsx批量的将图层边界信息导出:

// Get the document
var doc = app.activeDocument;
// And prepare to store something
var code = "";
// Traverse all the layers
for(var i = 0; i < doc.layers.length; i++)
{
    // Get current layer's bounds
    var bounds = doc.layers[i].bounds;
    // Concatenate the bounds info of every layer
    if (code != "") code += "|";
    // Make the bounds info of current layer
    code += layer.name + "," + bounds[0] + "," + bounds[1] + "," + bounds[2] + "," + bounds[3];
}

然后你只需要将这生成的code里面的内容,通过各种方式整到之前的jsfl里面去,然后各种华丽你就懂得了。

Filed under: 抠腚 5 Comments
15Feb/113

Protected: 不就是过节么

Posted by Jay

This post is password protected. To view it please enter your password below:


Filed under: 扯淡 3 Comments
27Dec/102

无聊的话无聊的事

Posted by Jay

这确实是一些无聊的话,无聊的事,无聊的以至于笔者都不知道什么时候会随时写不下去。不过随便写写总还是有那么点意思的样子,就当是找不到倾诉,自己说给自己听。
其实,都是一些无聊的思想的自言片语,无聊的没人会去思考这到底有没有意义,也许自己也想不清。其实,这些东西偶尔各种推上面牢骚两句也就过去了,不过不想让太多身边的朋友知道,于是...

好吧,事情大约应该是在年初开始,不过鉴于今天才想到要写点什么,于是先把今天想记录的,给记录吧:

就在刚刚,想到了一些恐怖的事情,甚至,有点恐慌。

然后。

作为一个伪Geek-伪宅-真Coder,我承认我不了解,不熟悉,不在行的事情,确实是太多了。身边的各种人,各种事,看得到的,看不到的,都在用各种方式告诉我一些我懂或不懂的东西。说实话,我只想自己能像一个普通人一样,普普通通的人一样。不知道从什么时候开始,莫名奇妙的变得开始胡思乱想,醒着想,昏着想,做梦什么的也在想。恨不得把所有所知空间时间里面,所有各种事物,各种组合形式统统想完,当然是自己能认知的范围以内。

这,真的是一件挺无聊,又挺恐怖的事情。想到好的事,笑不出来,因为觉得不真实,虚无缥缈;想到坏的事,会不安,觉得怎么也会有可能发生。委婉的说,职业病;直接的说,神经病。

为什么总是要去想下一步会怎样,要去想别人会怎样,要去异想各种乱七八糟的上下文,以至于仍然不能确定方向,找不到办法解决各种问题。于是就被这些不知道从哪里飘来,飞来,滚来的似有非有的东西给困惑,然后仰头叹一声,我又迷茫了。摇摇头看看地,结果还是原地不动,毫无进步,任时间,人群,匆匆流过。

有一点还算明白的事情,就是自己真的不想在这样想下去了。我想过的简单一点,让脑子休息一点。说这么多,想这么多,做这么多,说白了都是在自己掩护罢了。唉...

寂寞,是种病。

Filed under: 扯淡 2 Comments
21Oct/101

0x406D1388

Posted by Jay

神奇的魔数,果断的放弃

Filed under: 抠腚 1 Comment
16Aug/103

SFINAE

Posted by Jay

什么是SFINAE

在C++中有很多的编程技巧(Trick),SFINAE就是其中一种,他的全义可以翻译为“匹配失败并不是一个错误(Substitution failure is not an error)”。简单来说他就是专门利用编译器匹配失败的一种技巧。

案例

比如我们想实现一个通用的函数叫AnyToString,他可以实现任意类型的数据转成字符串:

template<typename ValueType>
char* AnyToString(const ValueType& value);

我们更希望这个函数能检查ValueType类型自己有没有ToString方法,如果有就直接调用,没有的话就采取通用的处理方案。但是C++没有反射机制,不能像C#那样通过TypeInfo来检查,更没有像Java那样纯粹的OOP,从最基类就定义了ToString方法,下面的子类只用负责重载。
所以我们希望能有一种方法能让C++也能检查某个类型是否定义了某个成员函数,这就可以用到SFINAE。

5Aug/102

N!

Posted by Jay

template<int N>
struct Factorial
{
    static const int Result = N * Factorial<N - 1>::Result;
};

template<>
struct Factorial<0>
{
    static const int Result = 1;
};
Tagged as: , 2 Comments
26Jul/1014

曾经的年少无知

Posted by Jay


如想蛋痛请猛击这里

Filed under: 抠腚 14 Comments
21Jul/100

C++中枚举与字符串相互转换

Posted by Jay

前言

有的时候我们喜欢使用一些外部的文件保存管理一些配置信息,这些配置文件大多都是文本格式例如ini,xml等,这样方便编辑和管理。因此在使用的过程中必然会遇到各种字符串转换问题。
最常见的便是将字符串的数字转换为对应的整形(integer)或者浮点(float),如果遇到枚举类型,可能便会想当做是整形来处理,但觉得不是特别理想。如果能有办法直接转换为枚举会方便很多。