[公告|置顶]给各位 時空の軌跡 的关注者,本博客的机能转移了!

似乎已经到了穷途末路,厌倦了一次又一次的博客服务提供商选择,博客搬家,我和大家一样也有更多更重要的事要去做,因此本博客不再更新。而本博客原有的机能将被转移给它的副博客ときそらのきせき转移去的是个外语博客,现在还只有英文,不过没准明天、下个星期、下个月或者明年就会出现其他语言的文章,如果大伙仍然对我的文章感兴趣可以继续订阅

呵呵,相信了解我的读者已经看出来了,上面的说明只是一个幌子。没错,确实只是幌子,但是也八东篱把酒黄昏后九不离十了。自从外语博客上线后,本博客的更新已经相当慢了。原因无他,外语博客比较随意,至少没有这个公告里的那么多承诺。而事实上,以我现在的知识和阅历,那些承诺是雷声大,雨点小。所以说是不再更新,其实只是休个长假,用于闭关修行。至于多长,我也不好说,伟大的中华人民共和国政府部门承诺“你会翻人比黄花瘦墙没什么可以显摆的,你可以翻人比黄花瘦墙写文章,但我可以保证你写的文章别人看不到”。So...建议大家至少掌握一门外语。

在我“休假”的这段时间里,我会自己搞一套全方位的Web2.0系统,当然目标用户就我一个人。话说我很懒,不愿意花时间去考察那些服务提供商或者博客系统,又或者指不定哪天就“该网站遇到技术问题”,太烦了(另外“技术选择有限”也是一个原因,这有违Hack Spirit)。反正我也没啥事做,我是说都是编码嘛,本质没什么不一样的。下次更新,也许就在我自己的私人服务器上了,这里先感谢一下Google Feedburner,它使得大家无需手动更新订阅地址

感谢您长久以来对 時空の軌跡 的支持,这次休假/转移给您带来的不便我深表歉意!不过为了我们交流和讨论的共同利益,请耐心等待新系统、新服务和新信息![Just Have Fun!]

最后,不管内部环境多恶劣,敌人多无耻,我们大家都要笑着面对!加油!!!

Posted in 2.0物语 | Leave a comment

AS3独当一面:AIR演绎《Visualizing Data》[TimeSeries]

上篇:AS3独当一面:AIR演绎《Visualizing Data》[Mapping]


相关下载:
MSN SkyDriver Books 《Visualizing Data》
TimeSeries实例代码

相关链接:
Preface:AS3独当一面:AIR演绎《Visualizing Data》
Getting Started:AS3独当一面:AIR演绎《Visualizing Data》

本文的内容对应于原书的第四章《TimeSeries》,是在前一章的基础上选用了一个更普及更实用的应用场景来实践。遵照我们的约定,在《Visualizing Data》书上和ActionScript3.0 API文档中能找到的内容不多赘述,并且本例开始不再保留重构痕迹。

TimeSeries此图中的数据来源于真实的统计

由于本章是对前一章内容的进一步举例,其实基本技术没有什么特别的可讲,因此不再赘述了。

知识导航和注意事项:
1.标签文字:flash.text.TextField、flash.text.TextFormat、flash.text.FormatAlign三个类的基本应用,由于Flash本身已经能够直接使用系统字体了,因此我们也没必要额外嵌入字体,当然字体名称要写精确了,你可以随便找个应用程序的字体设置兑换框,也可以用flash.text.Font.enumerateFonts()静态方法列举出字体名称。

途中所示的“Milk”、“Tea”、“Coffee”这三个标签,书中最后是加载的图片,因为Processing用于显示文字的“控件”可以直接加载图片,AS3的Label、TextField等方法却不行,因此本例只是换了下字体,不过这样看起来也更另类。如果非要加载图片,那就用前一章介绍的Loader类吧,在这个问题上它们没有区别,当然要达到途中效果你还要自己准备那些标签图片。

不得不说,同样是显示文本,AS3的API和Processing的API使用上无差异,但是显示效果却相差很多,因此我特别写了个display函数,主要用于解决两个问题:1.[绿色部分]使得TextField的大小恰好跟其内容匹配,因为它默认是100像素长的,超出文字宽的那部分可能会相互覆盖或者遮住其他可视化对象;2.[橘黄部分]在1的基础上TextField的对齐方式已经没有意义了,但是这仍然不是Processing的效果,因此当书中要求“文字居中”时,display通过调整TextField的位置来达到同样的需求。

private function display(data:Object,x:Number,y:Number,name:String="",center:Boolean=false):String{
        var content:String=data.toString();
        var labelName:String=(name=="")?content:name;
        var label:TextField=this.getChildByName(labelName) as TextField;
        if (label==null){
                label=new TextField();
                label.name=labelName;
                this.addChild(label);
        }
        label.x=x;
        label.y=y;
        label.htmlText=content;
        label.setTextFormat(this.txtFmt);
          label.width=label.textWidth+Math.ceil(label.textWidth/4);
               label.height=label.textHeight+Math.ceil(label.textHeight/4);
               label.width=(label.width<10)?18:label.width;
        if (center){
                label.x-=label.width/2;
                label.y-=label.height/2;
        }
        return label.htmlText;
}

2.优化曲线:书中列举了很多的展示图表的方式,但是这些方式其实都是前一章基本绘图函数的简单调用,因此本例中只包含了一种新的表示方式,使用了AIR1.5才有的Graphics类的drawPath方法,此方法的本质跟“贝塞尔曲线”的绘制原理差不多,效果使得曲线图看起来更平滑一些。不过值得一提的是,AS的此方法绘制的图跟书中Processing类似方法绘制的图,看起来不太一样。当然如果你知道内部实现,可以借用基本绘图方法来用自己的算法实现。

注意:传递给drawPath的参数是个Vector,AS3的内置类型,看上去有点像泛型(它也确实是泛型语法),但实际上AS3目前还不支持泛型。知道有这么回事就行了,这个设计还真是够让人汗的。


下篇:AS3独当一面:AIR演绎《Visualizing Data》[Connection and Correlation]
Posted in 我的试练, 编程语言 | Tagged , , , , | Leave a comment

从密码确认框谈到网站注册的用户体验

上篇:一定需要这种“悲观策略”的“密码确认”机制吗?


首先我得道个歉,上次提出这个话题时比较冲动,完全就是以自己为中心。呵呵,我也是一类用户(而不是设计人员),抱怨过程中容易出现忽视其他用户群的可能;而且受自我服务偏见的影响,我在冲动的状态下论述越来越不靠谱(因此讨论也没有多少,就不给出具体的链接了)。不过好在话题确实有所提升,因此我重新整理了观点,目的是使自己能够更全面的理解这个问题。此外还有另外一些考虑:
1.人都有一种思维定势,就是一旦某个事物被自己熟悉或成为一种习惯时,便觉得这些都是理所当然的,进而也就不去考虑为什么或者是否还有改进的需要。于是乎当 一个自以为是的家伙跳出来说“你们这样都不行,要怎么怎么样”时往往遭到的都是臭鸡蛋,当然更多的人是采取“他是疯子,不理他”的做法。
2.反对的观点并不能让我信服。因为我的观点是问“是否一定需要”。言下之意就是说肯定是需要的,但不是什么场合都需要;而反对的观点理解为全盘定夺
3.用户体验贯穿在一切设计、创新过程中,但这其实是用户在使用一个产品或服务的过程中建立起来的一种纯主观的心理感受,因此带有一定的不确定因素。所以在此我不准备让所有人都赞同,但是我尽量让我的观点理由合适

-------------------------------------------------------------------------------------------------------------------------

上一次的话题,哲思社区上有朋友给出了一些令人信服的理由来说明“确认密码框”存在是有必要的:
1.即使人们都不粗心,都有很高的一次输对率,但是由于键盘不好(比如上网本)这样的外部因素仍有可能导致输入错误
2.网站的目标用户针对的是大众不是一小撮熟练操作电脑的人
3.
事前保证正确,总好过事后弥补;注册前输错密码和注册后忘记密码并非同一性质的过程,因为注册前的错误没有任何“参考依据”,这就增大了“无法修正错误”的可能性;
4.事实上,人们就是会不断地犯错,虽然“确认密码”无法100%地发现所有的错误,但能发现99%的错误就已经很有效了,事实上我们的编译器也只能检查些语法层次的错误,但是去掉这些枝枝叶叶依然可以省下不少debug的时间。

由此可见,“确认密码”确实是有存在的价值的,但是尽管有充分的理由,仍然不能解答我的疑问:是否一定需要?好吧,对任意一个网站应用上面的4条理由,总能至少匹配其中的一条。但是事实上,更多的时候对于更大群体的人来说,更多的网站我们根本就没有意向去成为他们的正式注册会员,但是这些网站又确确实实的如我们期望加入的网站一样对他的正式会员实行了数据私有化,这样的一个最直接的效果就是我们也必须注册才能访问他们的资源,更让人无法接受的是这其中的很多网站只关注“如何吸引用户注册”,却从来没有考虑过要给用户一个合理的“注册的理由”。针对此问题,有不少解决方案:
1.预注册,关键点是用户提供一些基本数据成为“预注册用户”,此时系统提供给他“试用版本”,随后用户可以根据具体服务情况是保持预注册状态还是注册成为正式用户。
这个解决方案的预注册过程就可以是没有“确认密码框”的,不过这个方案对网站架构要求比较高;
2.先使用再注册,这个可以在C2C(
例如ioffer.com)上应用,比如用户可以先发布商品信息,待信息显示在页面上时再给用户弹出注册窗口(此时用户可以不理会直接走人的);
3.“低调”注册,最好让用户感觉不到其存在,当然所需要输入的信息也就少了,例如
一个基于家族族谱的SNS网站geni.com,连密码都不需要(不用担心,系统会给你的电子邮件发初始密码)。

上面提到的一些解决方案都是很不错的,我是说对于我们并不打算成为正式用户的情况。但是这样一来未免太麻烦,有没有可以同时兼顾两种用户类型的方案呢?当然是有的,不过我们来换一种思路思考,到目前为止,我们一直都是就可用性(用户在注册过程中是否会产生反感)的角度考虑的,主题既然是密码问题,我们当然不能忽视安全性(用户注册过程中的密码是否只有用户自己知道)这个因素了。

或许我们可以先反过头来想想,确认密码的目的是为了防止用户输错密码,但是用户为何会输错呢?排除一些客观因素,问题就显而易见了:密码被掩盖住了。这也是一个大家都习惯了的问题,但是诸位可以思考一下:根据网站的安全性等级要求,显然显示明码和显示暗码的风险和益处是不对称的。也就是说可能存在即使是明码输入也没有多少损失的情况。或者从另一个角度考虑,我们这里举个极端的例子:在linux终端上输入密码,它当然没有掩盖,但是更绝的是它直接就什么都不显示,这下好了如果自己感觉手指不顺似有输错的情况怎么办?Ctrl+c重来吧(当然你N次backspace也行)!显然没人总是愿意如此,那好吧,直接复制密码过来,或者直接PAM配置成免认证的,不过这个策略显然只在单用户环境才安全。可见如果技术上不能面面俱到的封莫道不消魂杀并不能保证用户会自觉遵守规则。而且安全不应成为一切的终极目标,应该视所保护的功能来考察安全控制。因此密码掩盖似乎也可以换一些策略了,目前已经有了如下的实现方式:
1.添加一个控制“密码掩盖”的复选框,同时系统自行根据安全等级选择默认是暗码显示还是明码显示。案例:linux的NetworkManager的DSL网络连接配置对话框;
2.每次输入一个字符,总是明码显示之,间隔一段时间后再变成暗码。(案例,不过他的做法是输入下一个字符时掩盖前一个)

好吧,这个话题暂时就讨论到此,表面上看是“密码确认”,实际上其背后是永无止尽的“用户体验”问题。同时这个问题也反应了一个事实:同样是注册,没有简单的需求,只是看不到深度。大家加油!

PS:今天才发现,我活跃的豆瓣的注册就是没有“密码确认框”的。

Posted in 2.0物语, Web技术, 应用心理学, 网络安全 | Tagged , , | 4 Comments

一定需要这种“悲观策略”的“密码确认”机制吗?

升华:从密码确认谈到用户体验


我们在注册一个网站或者在其他桌面应用、终端程序里注册一个帐号的时候,总是要输入两次密码,这里我们只讨论网站注册这个环境,因为其他情况如在自己电脑上新建一个Unix帐号改密码要找回来很麻烦,不是谁都知道怎么做的。

输入两次密码,现在没人不知道是“为了防止用户在不知道的情况下输错了,从而多了一个改正的机会”。但是这个策略一定有必要吗?观点如下:

1.我就从来都是一次输入正确的,相信大部分常用网络的人也是;

2.即是输入错了,现在的“找回密码”功能也是很完善和普及的,操作也不麻烦(我是说注册一下都要邮件认证,找回密码再来一封邮件也未尝不可);

3.实际上,不常用网络的人比常用网络的人要多得多,而且那些人输错的可能性也很大,但是不得不考虑另外一个问题:为什么我们要注册帐号呢?因为我们需要那个网站提供的资源,但是我们并不想长期待在那里,或者大部分时候只是潜水,这时候注册显得很没必要,但有些管理员偏要注册才能看或下载!以上可能性并不少见,于是乎,在此网站注册本身就可能让用户感觉不爽,很多人干脆就随便搞个密码,然后很快忘掉,而网络常客(可能还会有意识无意识的)去构思一个密码,以使其方便下次再有可能需要,当然这样构思的密码也不容易忘记,除非时间长了”构思算法“有所改变。

很明显,我不认为”确认密码“这个”悲观式策略"一定必要,甚至有点多余,麻烦了一大批人。
何不干脆就乐观点,假设大家都不会输错,(那万一输错了,参考观点2,这也不是很麻烦,况且忘记密码的也同样大有人在)。当然仅就注册时多输入一次也并不是什么特别大的负担,但问题就出在(参考观点3)。

接下来是我认为“不确认密码”的补偿策略:
1.我猜,不考虑这是一个惯例,而仅就客观上说,连续输入两次可能本身也有点暗示作用,告诉用户注意不要输错,那我们可以以醒目的文字提醒用户“本网站注册密码不需要确认”;

2.考虑到用户习惯,“确认密码”框仍然还在,但是可以留空,并且同样一醒目的文字提醒用户“你真的不确认密码”;

3.这个有一定的安全隐患,就是在激活邮件里以明文形式提醒用户你的输入的密码是这个,可能并非你自己所想,然后给个改密码的链接。

Posted in 2.0物语, Web技术, 网络安全 | Tagged , | 2 Comments

小机制反应大思想:符号链接和硬链接

在Unix环境呆的越久,就越发的感受到Unix哲学的伟大,然而蕴涵在K.I.S.S之中的机理的的确确就是那么简单。

在我的系统里,vim是必备的工具,它不仅仅是IDE的替代品,更是编辑小文件的瑞士军瑞脑消金兽刀,真的不习惯在终端里为了什么什么而启动个gedit或kate。这么个万金油,自己从源码构建,然而折腾写些相关的插件定是势在必行,甚至不屑于现成的安装包。这么一来就有个小问题,比如runtime文件如何管理,在以上背景下可能存在这么几种可能:
1.多个用户同步文件,我就是有这种癖好,不同的用户做不同的事,比如私人研究、公家任务等等;
2.虚拟机里或者另外一台个人电脑上再跑个类Unix系统,恰好它也需要共享这些文件。

很显然避免每次单独修改最好的方式就是搞个“多人协同开发”的环境,比如把整个runtime塞到版本控制系统中去,只要一处有更新就commit,另外几处使用时先update。当然这种情况显然是不需要动用诸如Google Code、Sourceforge这类公共服务的,你放出去就得对其他用户负责,这一些列的手续不说,光是写“User Manual”就够烦的了,所以本机上搞一个服务器。但是又确实存在自己想与别人分享的小插件,比如 Hybridevel.vim,它们又同时单独隶属于另一个工作目录。怎么办呢,放一起容易产生混乱,不放一起,自己用起来也麻烦,更要命的是vim插件还真不能离开vim来执行。这下子windows用户犯愁了,一下子还真找不到个简单的方法来处理。呃,我这里是说vista之前的windows用户,不过……看吧,Those who do not understand Unix are condemned to reinvent it, poorly[The  art of UNIX programming]。

不用说,你一定用过符号链接和硬链接,在这个问题上,Link可真是帮了大忙,新建一个插件项目以示这里是另一个工作目录,内部目录的结构与原目录保持一致但是所有文件都是硬链接过来的,既满足了要求又不多占用磁盘空间。至于为什么是硬链接而不是符号链接,一般情况“用户数据”不会跨文件系统吧?硬件接增加了实际数据的“引用计数”,而不是单纯的指向目标文件,这就使得删除其中任一个不影响另一个,是不是更接近真实的开发?而且原有的符号链接关系也被原样复制过来,在符号链接是相对路径时好处就更明显了,俨然一个原目录的克隆,而不是虫洞!

作为“以小见大”系列文章的第一篇,本来也就想述说一下链接的便利。不过呢最后我们还是来看一些本可以借鉴这个设计结构但却没有借鉴的不好的案例:
1.优秀的博客系统如WordPress,大家都体会到了,其一篇文章可以同时属于多个分类目录。但是我们再看一看MSN Space,无端的增加了撰写人的负担,TA得考虑这篇文章究竟放在哪个分类目录下最方便此文的受众……于是乎最完美的解决方案竟是创建“快捷方式”:在其他“次要”分类下也发一篇然后给个链接指向“主分类”——当然很少有人真的这么去做……
2.CSDN的论坛,一个主题只能隶属于一个子版块,于是一些提问心切的人就会依次发到各个相关版块去,没关系,反正我是旁观者可以不在乎,只关注我看到的那个就行了。但是贴主却不得不东奔西走以争取搜集到最有价值的讨论——当然,参与的其他人也看不到另外的精彩部分……

Posted in Unix文化, 操作系统 | Tagged , , , | 9 Comments

AS3独当一面:AIR演绎《Visualizing Data》[Mapping]

上篇:AS3独当一面:AIR演绎《Visualizing Data》[Getting Started]


相关下载:
MSN SkyDriver Books 《Visualizing Data》
Mapping实例代码

相关链接:
Preface:AS3独当一面:AIR演绎《Visualizing Data》
Getting Started:AS3独当一面:AIR演绎《Visualizing Data》

本文的内容对应于原书的第三章《Mapping》, 主要讲述了关于数据读取、数据显示、简单交互等技术的基础。遵照我们的约定,在《Visualizing Data》书上和ActionScript3.0 API文档中能找到的内容不多赘述

Mapping此图看上去很有点那啥的,实际上除了位置数据外一切都是伪随机的,不能代表实际情况

之前也说过我不会严格按照书中的顺序一个条目一个条目的来,因为按部就班势必会造成代码冗余,而且冗余量大大超过条目中的新加代码,所以我根据自己的理解将这些代码重构为最终效果,并且这个效果就是对章节“所有”知识点的检阅,除此之外数据和参数来源于随书光盘或章节。不过本例保留了重构痕迹,即大家看到的注释部分,如对注释部分代码感兴趣,请在演示前先将如下代码的删除部分注释掉(别忘了去掉原注释),这部分共有三帧,试试鼠标左击和右击。

//构造函数最后:
        //this.stage.addEventListener(MouseEvent.CLICK,nextSlide);
        //this.stage.addEventListener(MouseEvent.RIGHT_CLICK,predSlide);
        this.stage.addEventListener(MouseEvent.MOUSE_MOVE,showName);
        this.stage.addEventListener(KeyboardEvent.KEY_UP,controlTimer);
        this.timer.addEventListener(TimerEvent.TIMER,updateMap);

//main函数最后:
        //addLocationSlide();
        //addDataSlide();
        //addTSDataSlide();
mapCanvas.name="mapcanvas"; this.addChild(mapCanvas); drawMap();

ActionScript3.0知识点导航:
1.显示地图:flash.display.Loader类的基本应用。

2.加载数据[AIR专用]flash.filesystem.FileStream类(还需要File和FileMode),文件操作类似C语言,很基础;由于FileStream并没有类似ReadLine这样的方法,所以方便起见,在程序运行的一开始就将所有的三个资源文件的内容读到内存里了。

3.绘制数据:flash.display.Graphics类,按照书中所给出的方式调用基本绘图API。不过需要注意的是,基本绘图API不能处理HSB颜色空间,使用前需要进行HSB和RGB的转换,示例代码里也没有包含这部分,细节参考mx.utils.HSBColor类;啰嗦一句,注意Circle和Ellipse这两个图形API的参数

4.显示名称:Stage的flash.events.MouseEvent.MOUSE_MOVE事件。值得一提的是,刚好ActionScript3.0就是书作者所提到的Sophisticated语言,它恰好也有Shape类,不过这里我们也跟Processing一样,采用“低级”方式来检测鼠标与代表位置的圆的碰撞,如有必要显示该位置的名称;另一方面,名称的显示使用的是mx.core.UIComponent实例的toolTip属性,所以没有考虑字体处理(这个将在下一章演示)。

5.更新数据:flash.utils.Timer类和flash.events.TimerEvent.TIMER事件的基本应用,timer的启动和停止由Stage的flash.events.KeyboardEvent.KEY_UP事件控制,如图中状态栏所示:press SPACE key。与书中所给的方法相对,实例代码简单的不能再简单了,由于random.tsv文件的内容也是一开始就被读入内容的,所以改变这些数据的值也就有AS3的基本随机数函数自行控制了;另一方面,flash所谓的帧速率在Flex/AIR应用中其实并不多见,所以更新速度也就由Timer自动控制了。

6.改进模型:书中给出的方法更复杂,已经超出我写此系列文章的目的了,所以没有包含在示例代码中。简单来说,Integrator的职责是在启动时根据弹簧模型生成一个指定区间不重复的随机序列,使得更新更平滑的过度。

注意事项:
1.AIR应用程序访问本地资源,这里使用的是"app:"路径,因为我们都是用adl在调试模式下运行的:

    public class Mapping extends UIComponent{
                private static const RESROOT:String="app:/res/mapping/";

2.这可能并不是个好的习惯,但这是我的风格:addEventListener的匿名函数,其中出现的this并不是类的实例,而是函数本身,所以要在里面访问类函数,必须找好引用关系做个类型转换(或者在匿名函数定义一个指向this的指针比如self):

loader.contentLoaderInfo.addEventListener(Event.COMPLETE,function(event:Event):void{
        stage.stageWidth=event.target.width;
        stage.stageHeight=event.target.height+FlexGlobals.topLevelApplication.statusBar.height;
        Mapping(loader.parent).main();
});

下篇:AS3独当一面:AIR演绎《Visualizing Data》[TimeSeries]
Posted in 我的试练, 编程语言 | Tagged , , , , | Leave a comment

先入为主 VS 改编:一千个“玩家”,一千个“仙剑・轮回”

我是一个RPG爱好者,也是一个仙剑爱好者。

《仙剑奇侠传三》是我接触的第一部RPG作品,深深的被其剧情所吸引所感动,所以之后又按故事时间线陆续玩了“问情”、“宿命”、“宽恕”三部仙剑作品。经典 的游戏往往会引起导演的注意,被改编成电影或电视剧的可能性比较大;优秀的游戏同样会征服大批玩家。然而一个众所周知的心理现象:初次接触的事物给人留下的印象是占据主导地位的,以后再遇到不同的情况便会很难被接受。于是乎当仙剑“改编”自游戏的电视剧出来以后,尽管它受到未玩过游戏的观众的好评,却还是免不了被游戏玩家一阵唾骂。因为游戏玩家往往会不自觉的将电视剧里的故事情节和人物特点与游戏进行对比,却很遗憾的发现电视剧与游戏相差很多,于是……

当年已经有了《仙剑奇侠传一》的电视剧,看到了大量的来自游戏玩家的负面评论,便打消了看的念头。“改编的都是垃圾”一直存在于我的大脑中,尽管这 些印象只是道听途说。最近听说出了《仙剑奇侠传三》的电视剧,(不可避免的仍然有很多来自玩家的漫骂,)而预告片给我的感觉也很不爽,也就一直不去看。大 概是由于我日常听的音乐中就包含了仙剑的背景音乐吧,这两天突然忍不住想再玩一遍,当然,我的linux玩不了,于是就想起了去看电视剧……

正如长卿所悟:“七情六欲,唯有先亲身经历,方能超越”;同样的:偏见,唯有先亲身接触(与之不同的情况),方能克服。看完之后发现改编的“仙剑奇 侠传”并不如想象的那么垃圾,恰恰相反,其中的很多情节如游戏般让我感动让我震撼。反差如此之大,有必要批判性思考一下。事实上,先入为主的观念或偏见,往往是由于个人知识不足或缺乏经验所至。在这里,“改编”一词的意思是“在原有作品的基础上,通过改变作品的表现形式或者用途,创作出具有独创性的新作品”, 通俗一点说就是鼓励“一千个玩家,一千个仙剑”。为何不出电视剧的仙剑二呢?就是因为仙二的可创作点远没有仙三丰富。所以,(你也可以这么做,)导演可 以大胆的改写主线故事,以使(游戏)观众更清晰的理解仙剑、感受仙剑的精髓;大胆的构思支线故事,以使(游戏)观众从更多的视角认识仙剑的世界观;大胆的 放大人物个性,以引发观众思考仙剑所带来的潜在感悟。由此可见,本来一部非常好的片子,却因为一些没有“绝对性根据”的理由而被拒之门外实在是可惜。

好吧,个别角色确实改的太过火了,以至于 ** 了玩家心中期望的完美形象;剧情上同样也有过分的地方。更可恨的是,这些情节中的某些竟然放到了预告片里去!游戏中的设置往往也都是完美的,尽管这些都不可能是真实的,但却轻易激活了玩家在某个环境下进化来的心理机制,使得玩家对电视剧的期望值无形中增大了,于是就容易产生不满……[《进化心理学》],这也是演员压力的主要来源。所以,一种比较大胆的做法,我觉得,与其不伦不类的演个四不像,还不如干脆从不同的角度来吸引玩家。如果不是偏见在作梗(而故意失去耐心),难道你真的不想知道导演会给大家带来什么惊喜?

或者换个思路,电视剧/电影并非唯一可由游戏改编的东西,还有一些比较常见的如cosplay、同人小说等不同的创作题材。我很奇怪,有些人宁愿愿意去接受它们中那些乱七八糟的东西,却不愿去思考一下(非游戏)观众认为的经典片子。在影片中加入一些现代化的、诙谐的元素,更是弥补了观赏性的影片所无法带来的“游戏可玩性”不足,即使它们可能不合逻辑。噢,我是程序员,越是优秀的软件,越是要不断的打补丁……

任何一个“导演”也都不会吃饱撑着来提供些让人唾骂的作品吧,有血有肉的演员可不比的那些卡通形象。所以放下偏见,你会更受用的,至少你好好的娱乐了一下。

BTW:
1.紫萱长卿的前世今生一直使我想睡觉的,我一向不喜欢这种温情镜头;不过被改编的紫萱确实是吸引玩家的亮点之一;
2.景天雪见的绰号实在俗不可耐;景天见人就说雪见文武双全之类的话,却从没见她发挥过,很是疑惑不解;
3.对邪剑仙的设计非常创新,一改以往打打杀杀的场面,转而以“赌博为表象”的心理战开场,直到最后才象征性的以武力解决,而且这个过程……我怎么感觉邪剑仙还真不是一般的睿智;
4.长卿嗖一下“造”出个船并非完全不合逻辑:酆都武器店我们都看到了“武器伸缩盒”,为啥仙船不能被缩小?再说长卿耍酷时哪个镜头不是玄乎乎的“嗖嗖几道光”完事;
5.当然还有长卿参悟人生的那段确实很发人深省。

PS:
玩游戏时一直特讨厌长卿,因为“六界之类唯人有情,可结夫妻”,可这位老兄却“不惜姻缘,妄谈天下”;
现在很理解他,“该走的不走”,死乞白赖“留下来,打扰现世的人”。

Posted in 应用心理学 | Tagged , , , | 4 Comments

AS3独当一面:AIR演绎《Visualizing Data》[GettingStarted]

上篇:AS3独当一面:AIR演绎《Visualizing Data》[Preface]


相关下载:
MSN SkyDriver Books 《Visualizing Data》
Getting Started演示代码

相关链接
Preface:AS3独当一面:AIR演绎《Visualizing Data》

运行本系列文章的代码,需要准备如下工具:
Flex SDK4.0(amxmlc)
AIR SDK(adl)
AIR1.5Runtime
原样还原压缩包,在根目录下执行如下两行命令:
1.amxmlc -output bin/VisualData.swf -compiler.source-path=src src/net/wargrey/vdata/VisualData.as
2.adl air-app.xml


《Visualizing Data》的章节是按照如下“数据可视化”的七个步骤展开的:

Visualizing Structure

1.Acquire:获取数据,无所谓是来自文件、磁盘亦或是网络等;
2.Parse:分析数据结构,分类排序;
3.Filter:去掉所有不感兴趣的数据;
4.Mine:综合使用统计、数据挖掘等方法来识别出这些数据的模型或者将这些数据置于一个数学环境中;
5.Represent:选择诸如棒图、列表、树等的可视化模型来描绘数据;
6.Refine:精炼基本表示法,使数据描绘的更清楚,更具视觉效果;
7.Interact:添加一些用于控制或操作数据的交互方法。

本系列文章并非教程,所以书上有的内容在这里就不赘述了。直接进入代码部分(而且代码部分能在api文档里找到的也不赘述),加压开GettingStart.tar.bz2文件,会得到如下(IDE无关)的目录结构:
Project Structure(IDEless)VisualizingData:根目录
air-app.xml:AIR应用程序配置文件;
bin:swf输出目录;
res:资源文件目录,内按章节设置子目录(如果章节需要本地资源);
src:源码文件,package为net.wargrey.vdata。

详细说明:
虽然Adobe官方一直没有提供直接使用纯actionscript来开发flex/air应用程序的资料,而做这个尝试的国内外应该都有人的,当然也许 匿名的编程爱好者居多。但其实这个并非不可能,因为本质上讲flex和air本身就是as实现的flash库(air还多了一个新的runtime),而 且mxmlc的实现方式本来就是将mxml标签代码转化为as代码再编译之的。这么一来我们只需要看一下生成的as代码就可以自己动手了。所以所有代码的 组织方式如下:

除了VisualData.as和WarGreyAIRManager.as这两个结构性类外,各章节所讲解的技能各自对应一个类文件,如图中的 HelloVData.as、Mapping.as、TimeSeries.as(后两个是接下来两篇文章的主题),并且这些类都继承自 mx.core.UIComponent,以便可以作为可视化对象被加载到Stage上,后续文章将只关注这些类的实现。而在此简单说明一下这些类是如何与主应用程序整合的。

所有的AIR应用程序(只考虑纯AS与MXMLC的区别)都至少有三部分组成 :
1.主应用程序mx.core.WindowsedApplication,这里即为VisualData.as,用于执行一些全局的初始化工作(其实也 没有多少),初始化执行结束new一个具体的可视化类,比如HelloVData.as,以自行转交控制。注意,为了保证各程序互不干扰(主要是一些事件 处理),建议每次只new一个可视化类演示。

public function VisualData(){
        super();
        mx_internal::_document = this;
        mx_internal:: stylesInit();
        this.addEventListener("applicationComplete", this.main);
}
private function main(event:FlexEvent):void{ new HelloVData(); }

2.系统管理器mx.managers.SystemManager,这个用于管理主应用程序窗口以使得其行为像一个“本地化GUI程序”,当然这些都有默认实现。
3.模块工厂mx.core.IFlexModuleFactory,这动态加载和创建模块,包括主应用程序窗口和一些控件如TextArea等。为了节 省外部类数量(而且这些类几乎永远不需要修改),模块工厂和系统管理器整合在一起为WarGreyAIRManager,并且以metadata形式关联 到了VisualData类里。

[Frame(extraClass="net.wargrey.vdata.WarGreyAIRManager")]  //For 2
[Frame(factoryClass="net.wargrey.vdata.WarGreyAIRManager")] //For 3
public class VisualData extends WindowedApplication{

题外话,上述三个部分即是一个纯AS的Flex/AIR的开发框架,不仅仅是在此系列文章中,我的所有flash程序都是这样开始的,而真正的应用程序入口其实就是从继承自UIComponent的构造函数。实际上,除了上述三个部分外还有一些自动生成的诸如Style的类文件,这个便是mxml文件内部给出的规范可视化对象的样式默认设置,当然你可以在主应用程序加载之后再手动代码设置,不过为了不冲淡主题,全部使用其默认设置。


HelloVData.as是本系列文章正式开始的“Hello World”,当然这里的输出是矢量图了,对应于原书第二章《Getting Started with Processing》。

HelloVData

代码比较简单,不多说了,列举一些差异部分:
1.只是简单的展示了基本绘图API和鼠标的单击事件,参数基本按照书中例子来设;
2.没有演示外部数据的加载,这些在后续章节里都是基本操作;
3.没有考虑Processing特性操作的部分。

注意点:
1.在设置窗口size的时候,air应用程序多了个statusBar,所以传递过去的height参数应该额外加上statusBar的高度:stage.stageHeight=400+FlexGlobals.topLevelApplication.statusBar.height;


下篇:AS3独当一面:AIR演绎《Visualizing Data》[Mapping]
Posted in 我的试练, 编程语言 | Tagged , , , , , | 2 Comments

AS3独当一面:AIR演绎《Visualizing Data》[Preface]

相关下载:
MSN SkyDriver Books 《Visualizing Data》

相关链接:
GettingStarted:AS3独当一面:AIR演绎《Visualizing Data》
Mapping:AS3独当一面:AIR演绎《Visualizing Data》
TimeSeries:AS3独当一面:AIR演绎《Visualizing Data》


在这个到处都是信息的时代,我们无论主动被动都会时不时接收到若干的数据集合,如何理解这些数据往往依赖于接收者的知识技能背景,但是这个不是非常 重要,因为好奇心总是会驱使着人们去尝试些有挑战性的工作。于是“数据可视化”正逐渐成了越来越多的人理解、交流获取到的数据集的一个必不可少的过程。

正是基于以上原因,我开始看《Visualizing Data》 这本书,尽管我的候选方向不包括“计算信息设计(Computational Information Design)”。如你所见,我的知识背景是程序设计,跟艺术没有关系,这便是我想澄清的一点,关于“数据可视化思维”。由于“数据可视化”多少涉及到一 些图形图像方面的知识,于是有人认为这应该由艺术系的人去搞,至少搞这个需要艺术细胞,他们可能还会再给出一些诸如“没有艺术细胞,即使会操作 PhotoShop也很难作出优秀的图”之类的理由。但我想说得是,这个完全是两个抽象层次的概念,互相促进又互相正交:“数据可视化”的核心是how“ 可视化”,而所谓需要艺术细胞的那些活则是“可视化”what,所以“数据可视化”一样需要计算机方面的知识,甚至可能还需要计算机图形学乃至数学知识, 是不还需要“计算机逻辑细胞”?!

罗嗦了一大堆,该回到主题上了。之所以会出现此文,是因为我感觉《Visualizing Data》还是有些让人不尽人意的地方。比如,作者使用的教学语言是他自己发明的processing, 这一简单的编程环境和API,之所以不选通用语言如python,java等,我猜原因就是上面分析中涉及的,读者的知识背景比较多样,不一定都具有编程 基础。但是,不得不说……基于java的本地gui程序在我的linux系统上一直表现极差,比如本书相当于的“hello world”例子就总是让我死机,因此对java产生偏见了(要知道java也是我曾经最喜欢的语言之一),所以……我删了processing。转而选 了flash/air/actionscript3.0,这个号称在图形前端表现开发上处于领先地位的编程语言(当然这个是我的老本行,尽管以前很少使用 图形api);另一方面,虽然本书对如何获取、理解数据都介绍的比较详尽,但是在how编程这点上让我云里雾里的,这个大概是因为processing本 身是带了源码的,看源码就能一目了然的缘故吧,总之没有考察这点。而且语言之间多少有点差异,况且flash的普及度更高一点,一方面自己按照要求全部实 现本身就是一个很好的学习过程,一方面刚好可以和大家分享。

所以本系列文章就按章节分享书中实例的AIR1.5/ActionScript3.0实现。当然,我并不想原封不动的翻译processing代 码,而且与“可视化”这个目的没有直接关系的部分也不予细究,比如如何生成随机数等。这里跟大家说声抱歉,毕竟这个学习过程在我的方向上只是一个支线。在 具体的文章里,与书中代码差异交大的部分我会特别说明的,而且我的as3代码可能没有注释,因为代码本身就很容易理解(不考虑我们所喜欢的代码风格差 异)。
下篇:AS3独当一面:AIR演绎《Visualizing Data》[GettingStarted]
Posted in 我的试练, 编程语言 | Tagged , , , , , | Leave a comment

如虎添翼:Console Matlab Vim完美结合[Vim视角]

上篇:如虎添翼:Console Matlab Vim完美结合[Matlab视角]

首先,为何要大费周章的做这些事呢?一方面是个人喜好,一方面借应用来检验我的“混合编程插件”的扩展性和健壮性,或者……如果你想一直写代码,那花个几天时间打造整合个称手的开发环境那是相当值得的!加油!


环境:Fedora 10+Vim7.2

项目主页:http://code.google.com/p/vimhybridevel

在上篇里虽然实现了在Matlab Console里运行vim,但关键问题还没解决:vim没法直接跟matlab通信。试想一下(实际情况就是如此),写了一段m script,然后运行没有得到自己的期望或要做参数调整,改吧,于是每改一次都(在matlab console)运行、退出vim,这个看上去没什么问题,但是所有的Undo和Redo信息都丢失了……所以很有必要解决vim直接与matlab通信的问题。

在继续之前先啰嗦一些题外话。08年年底的时候我写了个vim插件hybridevel.vim,目的是方便混合编程,自己使用一直很满意,这半年来也陆续修正了一些小bug,但是一直没有完善的文档,故此插件到现在也没有发布。既然是“混合编程”,那包含对m script的支持也就理所当然了,但是上次的设计并未考虑到matlab这样的特殊情况,所以花了一点时间来改造那个插件。(实际上它们都共同依赖于另一个插件shellinsidevim.vim,最新版本并未上传给vim.org)

本解决方案针对的问题是在不离开vim的情况下执行m script,由于vim对m scrippt的支持本身由hybridevel.vim插件负责,因此vim代码只需考虑最核心的部分。具体来说:

1.使用mlint评估m script,以便发现“编译期”错误和获得提升性能的修改建议:

方便起见,这里用来matlab的外部工具mlint命令,因此实现起来没有什么技术难度。但是这里有两个很郁闷的问题,vim自带了compiler/mlint.vim文件用于根据其输出生成quickfixlist,但是很奇怪的是在我这里总是匹配不到任何errorformat,为了弄清楚原因特意一点点对照errorformat规范仔细检查,未果。估计不是文件参数的问题,怀疑是vim的bug(我这个vim在分析ant输出时也无法正常解析出错文件名)。所以不再浪费时间,转而根据其原理和vim api手动实现了quickfixlist的生成。顺便也说一点,如果你的vim解析mlint不成问题,也有一个问题需要考虑,mlint的输出中不包含文件名,除非你一次传递多个文件作为参数,因此在处理单个文件是需要手动规范输出以配合errorformat,不然即使生成了quickfixlist也不会自动定位,还不如干脆都自己编码来的方便。

function! g :D o_matlab_mlint()
        call g:ExecuteCommand(g :P arseCommand(get(g :P roject.current.command,'mlint',"")))
        let qfix={'filename':bufname("%"),'lnum':0,'col':0,'vcol':0,'nr':-1,'text':''}
        let quickfixes=[]
        let hasError=0
        for line in split(@+,"n")
                if g:Trim(line)==''
                        continue
                endif
                if match(line,'^(={10}) .+ 1$')>-1
                        let qfix['filename']=match(line,'[^= ]+')
                        continue
                endif
                let qfix['lnum']=matchstr(line,'^L d+')[2:-1]
                let qfix['col']=matchstr(line,'(C d+')[3:-1]
                let qfix['text']=matchstr(line,'): u+:.*$')[3:-1]
                call add(quickfixes,copy(qfix))
        endfor
        call setqflist(quickfixes)
              cc!
endfunction

2.vim与matlab通信,执行m script并获取执行结果,包括标准输出和figure显示:

实现这个功能的方法也有很多,但是考虑到兼容性、方面性和移植性,这里没有采用诸如“写辅助shell脚本”或“使用vim的编程语言接口(它可能要求重现编译vim以支持所需的语言)”等很多大型vimscript插件的做法。其实这个需求很简答,不要求与matlab交互,只是将一系列命令发给matlab然后获取其执行结果而已,使用shell的“文件重定向”特性足以,不过缺点是,即使vim是如上篇所述在matlab里启动的,此方法在实施时仍然会再启动一个“临时”的matlab。不过由于宿主和子matlab都是“-nodesktop”模式的,系统开销的增加相对于其便捷性也就可以忽略不计了。

“重定向”的实现有一些小细节,比如“文件”可能是file或named pipe或named socket,这里用的是file,由于file是“非阻塞”的通道,或者说这个通道的另一端一定有接收者,所以得考虑一个顺序,matlab一旦启动就会输出欢迎信息,然后被stdout接受,而matlab又没有接收到stdin,于是它就直接退出了,显然这样得不到运行结果。而如果是选择named pipe类“阻塞”的通道,只要无视stdout或stderr(如果有的话),matlab就会一直等待,此时无论什么时候给stdin都没问题,当然由于vimscript是单线程的,自然在matlab等待时没机会给它输入;即使在启动matlab时指名其在后台运行,可以有交互式输入,但是仍然无法检测stdout或stderr(除非之后不用再交互了)。因此,单凭基本vim script(使用vim编程语言接口比如python的不算)来实现的交互能力是很有限的。

这么一来思路就清晰了,在启动matlab之前先准备好要执行的matlab命令,这里又有一个问题,上篇里我们提到,现代版本的matlab搞得像个小操作系统,在“-nodesktop”模式下启动的matlab相当于以操作系统桌面为桌面,因此如果执行过程中需要创建小窗口,比如显示图像的figure,则它相当于一个独立的子程序异步执行。也就是说stdin里的命令会继续执行,等到matlab使命完成退出,然后……失去父进程的小窗口也跟着强行关闭(好在stdout跑不了,可以少操点心了)。这是个很棘手的问题,大大超出了vim的控制范围,不过既然matlab把自己整的这么伟大,那是不是它本身能够同步处理小窗口呢?嗯,我猜对了,顺着其exit的help一路找到了uiwait这样的函数。一目了然,stdin的最后一个/组命令应该是循环“uiwait(get(0,'CurrentFigure'))”,等待所有figure退出(如果没有则直接退出),好了,可以慢慢欣赏绘制的图像了。

向完美主义看齐继续处理最后的一些小问题:格式化stdout和stderr,去掉那些乱七八糟的输出和matlab command line控制符,这个就是基本的List和String操作。不过有一点需要说明,stderr即使是在matlab command line里显示也都是干净的,但是其实它的每条错误都被“{^H??? ”和“}^H”包围着(“^H”是ASCII的8)。

function! g :D o_matlab_interpret()
        let arguments='('.substitute(g :P arseCommand("ARGUMENTS"),' ',',','g').')'
        let maincmd=fnamemodify(g :P roject.current.main,":t:r").substitute(arguments,'^()$','','g')
        let matlabcmds=[]
        call add(matlabcmds,"cd ".fnamemodify(bufname("%"),":p:h"))
        call add(matlabcmds,maincmd)
        call add(matlabcmds,"display(' ')")
        call add(matlabcmds,"while get(0,'CurrentFigure'),")
        call add(matlabcmds,"uiwait(get(0,'CurrentFigure'))")
        call add(matlabcmds,'end')
        try
                call writefile(matlabcmds,'.MATLABIN')
                let launcher=g :P arseCommand(get(g :P roject.current.command,'interpret',""))
                call g:ExecuteCommand(launcher,'0< .MATLABIN','1>.MATLABOUT','2>.MATLABERR','&')
while 1 let stdout=readfile('.MATLABOUT') let head=match(stdout,'^>> >> ') if head==-1 continue elseif !exists('startpos') let startpos=head call g:AddShellCommandResult(['>> '.maincmd,'']) call g :D isplayOutput() endif
let stdout[head]=substitute(g:Trim(stdout[head][5:-1]),'(>>s*)*$','','g') let endpos=len(stdout)-2 if (endpos>=startpos) call g:AddShellCommandResult(stdout[startpos :endpos]+['']) call g :D isplayOutput() let startpos=endpos+1 endif if g:Trim(stdout[-1])=~'^>>$' break endif endwhile
let H=nr2char( 8) let stderr=join(filter(readfile('.MATLABERR'),'len(v:val)>len("")')," ") let startpos=match(stderr,'{'.H.'???',0) while startpos>=0 let endpos=match(stderr,'}'.H,startpos) call g:AddShellCommandResult(["ExecuteCommand failed: ".stderr[startpos+6:endpos-2],'']) let startpos=match(stderr,'{'.H.'???',endpos) endwhile call g :D isplayOutput() finally call delete('.MATLABIN') call delete('.MATLABOUT') call delete('.MATLABERR') endtry endfunction
uiwait(get(0,'CurrentFigure'))

至此console、matlab和vim三者的整合告一段落了,根据这次折腾的收获,根据上述思路可以更进一步的完善vim和matlab或其他进程的交互。

Posted in Unix文化, 开发工具, 我的试练, 编程思想, 编程语言 | Tagged , , , , | Leave a comment