nixzhu

春恨秋悲皆自惹,花容月貌为谁妍?
Jul 5, 2014

今日天气炎热,傍晚时气温仍然未有明显下降。我妈准备做饭,我看了觉得热,就提议去外面吃。

下楼后拐弯就是小卖部,听到有些人聚着唧唧喳喳地议论,说最近出了一个杀人帮派,听得像是“点点帮”。我没在意,和我妈转过街头,进入地下通道,通道里有好多卖衣服的个体户,也都聚一起在讨论点点帮,说这个帮派最近在这片地区很活跃,好像专门杀开饭馆的男人,也不知有什么恩怨。

出了通道时,我还看着一个老头快步进入通道,背着扁担,嘴里念念有词:“点点帮?敢来就试试!”

我们到了东门桥小吃街,我妈说还是去上次那家饭馆吃,我没答话,跟着进去了。我妈先要了一份炒面,我在店内转了一圈,没发现有什么想吃的,就和老板说:“给我来个蛋炒饭吧。”但老板表示不做。我觉得奇怪,提议加两个蛋,怕老板嫌钱少。老板赶紧解释:“没有食材了,考虑其它的吧?”

但我是真想吃蛋炒饭,就转身准备出门,老板再欲解释,我挥手表示不必了。我让我妈就在这里吃,我出去再找一家。

出来店外我没走几步,就听到地下通道口有个女人在尖叫,还从板凳上跌倒下来,我冲了两步过去准备看个究竟。那女人口中叫着:“杀人了!杀人了!”

我心里觉得不对,没接近那女人,转身就跑,但还是太迟了。刚转身,后面就有一人抓出我衬衫一拉,我往后一倒,他就抱住我的腰。我站不稳,来不及挣扎,只感觉他快速地在我腰里捅了六、七刀。

我躺倒在地,听着那人跑远的脚步,旁边那女人的尖叫声更加凄烈,言语不清。我没感觉怎么疼痛,只觉得身下水汪汪的,眼前开始模糊,我知道是失血过多。我想我的内脏一定被破坏不少,可能肠子断了,肾脏裂了,肝脏破了,肯定活不成。

……

电视里播着突发新闻,重复着一些监控镜头的画面,后面列出了当晚遇难的十二人名单,主持人一边念着一边解说,谴责凶手。我看到了自己的名字、出生年份。算一算,不到二十岁。

Oct 7, 2013

今天想着给一个应用增加iOS 7支持,就开始修改UI,还有一个困扰我的问题:我感觉它在iOS 7绘图的性能下降了。

因为这个应用用于显示围棋棋谱,用户会不断点击“下一步”来浏览棋谱,这就涉及到刷新绘制棋盘,很久以前我就专门解决过绘制的性能问题,但我不清楚为何在iOS 7上还会有“性能问题”。

从iOS 7的第一个Beta开始,我在它所有Beta上都测试过这个应用,最开始我觉得是iOS 7本身优化不够而导致绘图性能不太好,至少没有iOS 6好。后几个Beta有所改善,体验上和iOS 6较为接近,我也就放心了,没去调试应用的代码。

但最近在iOS 7正式版里使用这个应用的过程中,我发现“性能问题”又出现了:点击“下一步”后明显感觉到反应迟钝,对棋子落到棋盘上的速度很不满意。就想趁着要更新,一并解决了。更新好UI后,我打开Instruments开始检测应用在使用过程中的CPU消耗。

分析

这个应用在用户每一次点击“下一步”后都回通过Block(类似delegate)来更新上一个页面的TableView,而此时使用了reloadRowsAtIndexPaths:withRowAnimation:来更新对应的Cell。

未优化前

可以看到,每一次点击都会造成一个CPU用时的尖峰。

未优化前CPU细节

查看细节,发现就是reloadRowsAtIndexPaths:withRowAnimation:最耗时。我很奇怪,这个方法在iOS中应该是优化得很好的才对,怎么会这个消化CPU呢?我将其改为reloadData,看看这个更加常用的方法是否也有这样的问题。刚好我有另一个基本相同TableView页面,就改了它,结果性能问题不见了。

优化后

每一个点击“下一步”都不会造成CPU的尖峰,而查看CPU消耗细节,可以看到reloadData只消耗7.1%,不到reloadRowsAtIndexPaths:withRowAnimation:的1/10。

优化后CPU细节

这样修改后,用户体验非常好,点击“下一步”立即就会有对应的棋子“落到”棋盘上。

结论

OK,虚惊一场,我不用担心棋盘刷新代码有性能问题了。这应该是iOS 7的性能bug(优化还不够),因为在iOS 6上,同样的代码是不会造成这样严重的性能问题的。

你若要频繁更新Cell数据就要注意了哦!

Aug 5, 2013

今天我继续学习AngularJS,想用它POST数据到Django后台,那么$http的方式大概就是这样:

$scope.save = function() {
    $http({
        method: 'POST',
        url: '/save/',
        data: {data: "myData"}
    }).success(function() {});
    };
$scope.save();

Django的console可以看得这个请求:

"POST /save/ HTTP/1.1" 200 5

也就是一切正常啦,现在我想换成用$resource的办法来试试,这是比$http更高层的方式,RESTful友好(这也不是我关注的,我只想两种方式都能POST数据到后台)。

首页里记得要加载angular-resource.js这个脚本好使用ngResource,然后我写出这个工厂方法:

angular.module('myApp.services', ['ngResource'])
  .factory('Save', ['$resource',function($resource){
    return $resource('/save/', 
        {}, 
        {
           create: { 
              method: 'POST', 
              params: {data:'myData'}
           }
        }
    );
  }]);

再使用 Save.create(); 发起POST请求,而Django出现500错误:

"POST /save?data=myData HTTP/1.1" 500 11743

我检查了一下,这里的URL在save后没有/,而我上面是写了的。但这也不是重点,因为Django被设置为不对URL尾部的斜杠敏感,而且就算URL错误,也只应该引起Django的404错误,不该是500错误。

我只好在Django里再定义一条路由,没有斜杠在/save后面,果然,这次用$resource的POST就没问题了:

"POST /save?data=myData HTTP/1.1" 200 5

但是再看这个响应的URL,居然格式和GET一样,参数被附在URL上了,但我搞不清楚AngularJS内部是怎么处理而发出这样的POST请求的。

我搜索了一下,这个问题 Angular trailing slash for resource 描述了我遇到的URL尾部的slash被去掉情况。根据第一个回答的说法,这是个需要研究的issue,而且肯定了$http在某些时候的不可替代性。

结语

AngularJS的坑比我想象的要多,比如上面提到的用$http来POST数据到后台,Django打印出 request.POST 是这个样子的:

<QueryDict: {u'{"data":"myData"}': [u'']}>

这明显是不对的,无法取出参数,只能用:

data = json.loads(request.body).get('data', None)

这样不好看的方式。

Jul 15, 2013

《北京法源寺》是一部历史小说,主要内容是康有为、梁启超、谭嗣同等人筹划变法维新,以及讨论中国该走改良的道路还是革命的道路。

先,改良失败了:谭嗣同能走但不走,死给人看,改良不可行。 后来,革命也失败了:袁世凯妄图复辟,康有为「保皇」要走君主立宪。

法源寺呢,依然在那里,盛着那些悲悯忠义,不言不动。

一些摘录

鱼和肉叫腥,臭菜——葱、蒜韭菜等等——叫荤,大家以为荤是鱼和肉,所以吃斋只是不吃鱼肉,而大吃臭菜,这是精神上没有了解吃素的真意,至于有的庙里大做素鸡素鸭,那简直是精神上完全在吃荤,一点儿也没有吃素的本意了。

如果变法维新是做一盘菜,做这盘菜的情况就在眼前,五爷可以看得一清二楚,也可以去全盘掌握,自然五爷说得对,要讲求准备和火候。但现在这问题太复杂,复杂得什么都纠缠在一起,整个的局面纠缠得不能动。这时候,我们的目标是先让它动起来,总不能死缠在那儿,动,才有机会、才有起点;不动,就一切都是老样,老样我们看够了、也受够了,实在也忍不下去。所以,目前是要动,准备够不够火候对不对,也顾不了那么多。何况什么样的准备才叫够,什么样的火候才叫对,因为问题太复杂,实在也很难判断。所以干脆来个动,从动中造成的新局面,来判断得失。

不错,是稳健。可是愈是稳健的人,就愈变成稳健有余、行动不足,最后一事无成两鬓霜,也一事无败两鬓霜。所以稳健,最后竟变成不是一种做事的态度,而变成了不做事的借口。

何况从技巧上,也必须用变法维新的行动来做宣传的手段,这叫取法其上,或得其中;如果不得其中更可得其上,那不更好。

在他被打之前,有人送他蚺蛇胆,说吃了可以减少痛苦,可是他的回答是:“椒山自有胆,何必蚺蛇哉!”

在光天化日之下、在黑暗时代,他们在看我们流血。我们成功,他们会鼓掌参与;我们失败,他们会袖手旁观。我们来救他们,他们不能自救,如今有眼睁睁看着我们亦无法自救。在他们眼中,我没事失败者。但是,他们不知道失败者其实也蛮痛快,因为失败的终点,也就是另一场胜利的起点。这些可怜的同胞啊,他们不知道,他们永远不会知道。

西太后本人的文化水平是低层的,她的权势窜升到高层,文化水平却没有窜上去,结果由她点头肯定义和团由她带头纵容义和团,就上下衔接,串联成腾笑古今中外的文化大乱命。

岁月只对生命有意义,一旦物化,彭觞同庚、前后并寿,大家比赛的,不再是存在多久,二十存不存在。

你别忘了,他们是一个大集团,一个靠着压迫别人的不平等与保护自己的特权共生着、互利着的大集团。整个大集团不能改变,一个人的觉悟,闹到头来,只是一场悲剧而已。一个人带着一个大集团做坏事,坏事对大集团有好处,虽然不合正义,他会得到拥护;可是,一个人带着大集团做好事,好事对大集团有坏处,虽然合乎正义,他会得到反对。

我们要用霹雳手段去革命,提醒中国人:当一个政权从根烂掉的时候,它不能谈改良,当它肯改良的时候,都太迟了。

他们做拦路虎于先,又到处拉大便于后,他们的可恶,不做的比做出的,其实更多。他们是一块顽固的绊脚石,自己不前进,却又使别人不得前进。

Jun 28, 2013

设想这影视作品常出现的桥段,两个人花前月下,正谈着风月,情不自禁慢慢靠近。我总会幻想某一方嘴里跑出些怪味,让对方皱起眉头,哈哈,那是怎样的煞风景哟。所以,若要谈风月,其他条件不论,第一条就是不能有口臭。

我牙齿都还没有刷呢

近代以来,中国人重视口腔问题的人数理应增加不少。但在生活中,你总有机会偶尔闻到别人的口臭,如果不幸,自己的口臭给人闻见而不自知,则显得有些可悲了。遇到这样的情况,你总不好意思去提醒别人,想想若是自己被人提醒,又该是怎样的尴尬。再若是被自己心爱的人给提醒了,岂不是连在地上挖个洞钻进去的心思都会有?

每个人嘴里或多或少都有些味道,若要说没有,大概只有那那刚出生的婴儿嘴里没有味道,可它喝过几天奶,还是满身满嘴奶味了。那小说里写佳人们“呵气如兰”,我想多半也是吃了薄荷糖吧,或者就是才子们不小心闻过了太多臭男人的口臭,到了佳人这里,几不可闻,相较之下,便如兰花般美好也是有的。

没有考据过,但我想古时人们大多不刷牙,就算富贵人家有刷牙习惯的,也不过用手指抹点盐,按摩一下牙龈。更没有牙医,有点口腔方面的疾病更是难治了。我想大家都有经验,当口腔有疾病时,口气会有多么的糟糕。

宝玉跟他的小厮们说:“这女儿两个字,极尊贵,极清净的,比那阿弥陀佛、元始天尊的这两个宝号还更尊贵无比的呢!你们这浊口臭舌,万不可唐突了这两个字。要紧,要紧!但凡要说时,必须是用清水香茶漱了口才可,设若失错,便要凿牙穿腮。”,这里当然不能全算到口腔里的味道上,更多的是一种气质体现,但总在侧面说明小厮们嘴里的味道不太好。

黛玉初入贾府时,有描写贾府有饭后漱口的规矩:“寂然饭毕,各有丫鬟用小茶盘捧上茶来。当日林如海教女以惜福养身,云饭后务待饭粒咽尽,过一时再吃茶,方不伤脾胃。今黛玉见了这里许多事情不合家中之式,不得不随的,少不得一一改过来,因而接了茶。早见人又捧过漱盂来,黛玉也照样漱了口。盥手毕,又捧上茶来,这方是吃的茶。” 用茶漱口,效果算是不错的,因为茶叶里面含有的茶多酚具有抗菌作用,且茶通常都有清香味。

大家都承认或者默认,中国人什么都吃。当然,一些人不同意,会争辩道:“那是广东人好吧!”。别的我先不说,单说吃火锅。火锅的特点,不外乎,辣、麻、烫、杂,各种佐料。吃完火锅,口腔里简直都快烫掉一层皮,上皮细胞不知道死了多少,再混合各种味道,胃里也翻腾翻腾,第二天嘴里味道能好吗?

说起来早晨嘴里的味道,我想大家都有经验。若是昨晚好好刷了牙,甚至再用用牙线,第二天嘴里的味道不说是如兰花一样,至少也不会激起太严重的嗅觉反应。若是昨晚没有刷牙,甚至睡前还吃了什么东西,不论甜咸,第二天嘴里的味道都不会好到哪里去。一夜的发酵,加上厌氧细菌的繁殖,我想,要是本有个妖精住在那嘴里,它自杀的心都有了。

这就是说牙齿清洁对现代人来说十分重要,进而至每年看两次牙医,治疗口腔疾病,去除牙结石,都是必要的。这才能保证你和心爱的谈风说月时,亲近自然。和朋友同事说话交流时,畅快随性。你说呢?

May 10, 2013

你是个作家,你遇到创作瓶颈,没有话说了,你怎么办?也许你应该好好描述这种没有话说的状态,写一本《我为何没有话说》,所以作家不会没有话说。对于生活,不论是精彩还是无聊,总是有值得描述的事情。无论是为了自己看,还是给后来人一点参考。

但是否有这样的情况:你没有话说,也没有力气审视自己,更不会将从无聊中得来的胡思记录下来。时间再慢慢流逝,那些有点作用,有些价值的思维闪光点就这样默默地消失不见了。这有点像大浪淘沙,沙子很多,金子很少。很明显不是每个人都有同样多的金子,但沙子的比例都远远超过金子。如果你关注这些沙子,搞不好会有几块小水晶在里面。就算是普通的沙子,每一颗都有它丑陋的独特。

可这样丑陋的独特该怎么描述呢?最怕丑陋得还不够独特。不然搜肠刮肚半天,丑陋都快没有了,也没想好怎么描述。我把这样的情况称为悲剧,就是在你想做到但却无力实现的时候。你若明知山有虎,还要往山上跑,而且你又不是武松,最后的结果就很难看,或者除了一点残渣根本看不到你。

有个傻逼说:天空没有翅膀的痕迹,但我已经飞过。说这句话的傻逼应该已经留下了痕迹,听这句话的傻逼们有没有痕迹呢?

说到最后,还不是怕不够特别。就算你没开车,也一定看到过,那些乡间的公路上都有两条车轱辘印,中间是一丛青草,你开的车或者坐的车依着两条车轱辘印,但既不能增加的它的深度,也不能增加的宽度,不然车子就会很颠簸。只有第一个开过这条路的傻逼留下了痕迹,后面的不过是依样画葫芦。

能影响一个人身体或者思维的东西才是永恒的。所以,薯条是永恒的,A片也是永恒的。你们来猜猜:若没有外人告知,一个人在有了一台摄像机之后多久,他才会尝试拍一部自制A片?

Apr 21, 2013

昨天早上8点过发生了地震,当时我在重庆的某小区的16层睡觉,被晃醒了。当时我的预感是这次地震应该在重庆附近,但重庆不太可能发生地震,那就要到四川那边,这种能摇醒人的程度,传到这边,怎么也得有7级。

最初看到Twitter上有人说是四川雅安5.9级,我还想还好自己感觉错了,这级别大陆地区的房子还能顶住。但随后就被更新到7级,我想,这个级别在大陆地区是必然要死人了。

地震很难做到像预报天气那样提前一两天,这个不假。但有可能做到预警,且确定可以做到发生后及时提醒。日本的地震情报是直接显示在所有电视屏幕上,一般刚发生就显示了(看火影等动画偶尔都会看到这种提示)。 这钟提示不是某个电视台的,而是一种公共报告系统。

稍微分析一下,你就知道这种提示的巨大优势:

  • 第一,在地震发生时,人们有很大比例在看电视,这样的提示可以给他足够判断的信息,而不是先怀疑是否自己的感觉器官出了问题,这样他可以最及时的实行自保。

  • 第二,地震破坏性较大,若不在地震开头就做提示,很可能之后电力系统被破坏,通讯系统无法使用,电视看不到,电话打不出,互联网也无法使用,道路交通系统也可能被破坏,这时想提示也没机会了。民众很难知道具体发生了什么事,会更容易恐慌。

这种提示,可以最大限度的减少伤亡,所以我认为,这个地震报告系统必须要建立起来,灾后救援虽然很重要,但毕竟错过了最佳时间,只能算治标。

还有一个是大陆地区建筑的质量问题,7级地震,在日本来看都是小case,他们建筑的抗震能力应该学学吧!都说他们是小日本,但人家地震死人少。比如你修学校,总不能地震7级就倒塌吧!你修大桥,总不能地震7级就震断吧!居民楼也一样,不能地震7级就坏了。

这是一个监管的问题,以及监督这些监管的问题。你一不能做监管,二不能监督这些监管,那要你执政干什么?我们纳税干什么?你的合法性和先进性从何处体现?光靠在时代广场播几天广告?你以为你是三鹿奶粉吗?

什么是好的历史?不是说这个历史没发生过坏事,而是不要重复发生这样的坏事。大陆的朝代一代接一代,但全都在循环,看不到进步。大陆当局应该好好想想,要不要这一循环下去。

在一个互害的社会里,就算你捞着好处了,终有一天,你也会失去。

Feb 1, 2013

最近做的一个App要发布新版本,希望能让用户给应用评分。

从我自己使用各种App的经验来看,很多时候并不会主动去评分。而对于那种刚打开应用就弹出提示让用户去评分的应用来说,用户并不见得会去评,而用户下次再打开,你还是立马弹出提示,用户就会烦闷了。

我想,最好的办法是给用户一段时间的体验,再提醒用户去评分,这样评价也会中肯些。如果用户不理会评分提示,那么下次再弹出评分提示的时机就很重要了,以尽量不要打扰用户。

简单的分析:若用户对大多数应用都没有评分的习惯或者倾向,那么他在点击了“下次再评”后,他实际希望的是不要在很近的几次应用使用中再看到这个对话框;另一种,用户无暇去评分,觉得评分会打扰他使用应用。

最终的结果就是:我们希望用户评分,但是用户希望能好好使用应用,而对于不愿意评分的用户尽量不要给太多打扰。

所以我想设计一个算法来满足这样的情况。稍微想了想,我原来做通信方面的编程,了解一种算法叫做:截断二进制指数退避算法(truncated binary exponential backoff),它原本用于通信碰撞中,属于节点的一种自我调整。简单来说:若现在获取不到信道资源,就随机等待一段时间再尝试;若还是获取不到,就再随机等待一段时间;以此重复,直到获取到资源。

就像在没有主持人的座谈会中,所有的参加者都通过一个共同的媒介(空气)来相互交谈。每个参加者在讲话前,都礼貌地等待别人把话讲完。如果两个客人同时开始讲话,那么他们都停下来,分别随机等待一段时间再开始讲话。这时,如果两个参加者等待的时间不同,冲突就不会出现。如果传输失败超过一次,将采用退避指数增长时间的方法(退避的时间通过截断二进制指数退避算法(truncated binary exponential backoff)来实现)。——出自维基百科:以太网

现在的情况属于,App要获取“用户去评分”这种资源,我们假设用户会不耐烦,所以在第一次获取不到时,自我退避一段时间;第二次还获取不到,就再退避更久;…… 这样就可以了。

说回来,为什么叫做二进制指数退避呢?因为这个算法里使用了以2为底的指数来计算退避时间长度(C语言里通常用位运算实现)。原本算法里指数是随机的,我的算法里,指数是递增的,以应用不同的情况。

那么算法怎样呢?如下所示:

读取退避指数(初始为1或2)
若退避指数为0:
    return // 不再提示用户

读取应用打开次数(初始为0),再+1存储

若应用打开次数 > 2^退避指数:
    alert:提示用户去AppStore评分或评论

-------------------------------------------------
alert处理:
    若选择了是:
        退避指数设置为0(不再提示用户)
        转到AppStore的页面
    否则:
        退避指数+1存储

其中退避指数和应用打开次数需要存储在应用的文件里,怎么实现就随意了。开启App Sore的方法见此:跳转到AppStore里的应用评分页面

把这个算法写成方法,在主界面或者某个用户常去的界面里执行即可。若要体验更好,就算是当前条件满足了算法,也应该再等待一段时间,即,不立马弹出alert,等待5~10秒,给用户更好的体验,别吓着他。

从算法的效果来看,他开头几次使用应用是不会得到提示的。几次后,他第一次得到提示,若他放弃去评分,下次就会等待更多次才会得到提示了。

Jan 25, 2013

做个《Learn cocos2d 2》的学习笔记,慢慢看,就慢慢写,随时增补。

基本元素

Node的创建、添加、移除

CCNode *node = [CCNode node];
[myNode addChild:node z:0 tag:123]; // z越小,越先绘制

CCNode *reNode = [myNode getChildByTag:123];

[myNode removeChildByTag:123 cleanup:YES];
[myNode removeChild:reNode];
[myNode removeAllChildrenWithCleanup:YES];

[myNode removeFromParentAndCleanup:YES];

Node可以运行Action

CCAction *action = [CCBlank actionWithDuration: 10 blinks:20];
action.tag = 123;

[myNode runAction:action];

CCAction *reAction = [myNode getActionByTag:123];

[myNode stopActionByTag:123]; // or
[myNode stopAction:reAction];

[myNode stopActions]; // stop all actions

Node可以调度消息

若在Node的init方法中:

[self scheduleUpdate];

那么需要实现以便来做每帧都更新的事情:

-(void) update:(ccTime)delta
{
    // this method is called every frame
}

若不想每帧都调度,获得更好的灵活性 ,可以自定义间隔时间:

[self schedule:@selector(updateTenTimesPerSecond:) interval:0.1f];

相应实现方法:

-(void) updateTenTimesPerSecond:(ccTime)delta
{
    // this method is called according its interval
}

在 PROJECT -> Build Setting 里可以将selector未实现的警告打开,免得运行时崩溃了才知道。

当然,调度的方法都可以停下来:

[self unscheduleAllSelectors]; // 全部都停止调度
[self unscheduleUpdate]; // 默认的那个
[self unschedule:@selector(updateTenTimesPerSecond:)]; // 指定的那个

有时候需要只调度一次:

[self scheduleOnce:@selector(tenMinitesElapsed:)  delay:600];

或者按照常规办法,只是实现的selector里自己运行完了就自杀:

[self schedule:@selector(tenMinitesElapsed:) interval:600];

-(void) tenMinitesElapsed:(ccTime)delta
{
    // ...
    [self unschedule:_cmd]; //_cmd equal to @selector(tenMinitesElapsed:)
}

由此,我们可以制造出一种特别的调度,根据具体的条件生成下次调度时间来调度:

[self schedule:@selector(irregularUpdate:) interval:1];

-(void) irregularUpdate:(ccTime)delta
{
    [self unschedule:_cmd];
    // do something ...
    float nextUpdate = CCRANDOM_0_1() *10;
    [self schedule:_cmd interval:nextUpdate];
}

最后,调度可以设置优先级,多个节点都有update:时,可能会有用:

[self scheduleUpdateWithPriority:0]; // -1, 1, 100, …

导演、场景和层

CCSence和CCLayer同CCNode一样,没有实际的界面,CCSence作为一种容器包含其他节点,而CCLayer通常将一组节点组织在一起,包含正确的绘制命令。同时接收触摸和加速度计(鼠标键盘,OS X)

导演:CCDirector

CCDirector以单件(singleton)实现,通常用 [CCDirector sharedDirector] 访问它。它为cocose2d保存全局配置,并管理场景。它主要有下面这些工作:

  • 提供访问当前场景的入口
  • 更换场景
  • 提供访问配置的入口
  • 提供访问OpenGL的view和为window的入口
  • 修改certain OpenGL projection。。。开启深度测试
  • 转化UIKit和OpenGL坐标
  • 暂停、恢复或停止游戏
  • 显示debug状态
  • 计算渲染帧数

场景:CCSence

CCSence总是场景里的第一个节点。通常有导演运行、替换或推送场景,但也可以将场景被包装进CCSenceTransiton里,加上转化时长,再从当前场景转化场景。

sence -> layer,+sence里就会给sence添加一个layer,再由导演开始第一个场景:

[[CCDirector sharedDirector] runWithSence:[HelloWorld sence]];

场景转化时,总是新场景先进入内存,旧场景之后再free。所以这有可能造成内存瞬间增加,可能引起内存警告和崩溃。只有经常测试场景转化了。

导演的pushScenepopScene方法可以在不移除就场景的情况下运行新场景。相当于新场景添加到就场景上面,所以和replace不一样。这种方式对于设置场景很合适。

这样的话,前一个场景:

[[CCDirector sharedDirector] pushScene:[Settings sence]];

到达settings场景,设置结束,再:

[[CCDirector sharedDirector] popScene];

回到原来的场景。

CCTransitionScence

为了在场景转换中获得更多定制,可以使用CCTransitionScence的各种子类。只不过增加一行代码。例如:

CCTransitionFade *tran = [CCTransitionFade transitionWithDuration:1 scene:[HelloWorld scence] withColor:ccWHITE];
[[CCDirector sharedDirector] replaceSence:tran];

就可以控制场景转换的时间长度和背景颜色。下面看看几种不同的场景转换类型:

  • CCTransitionFade:
  • CCTransitionFadeTR:
  • CCTransitionJumpZoom
  • CCTransitionMoveInL
  • CCTransitionSceneOriented
  • CCTransitionPageTurn
  • CCTransitionProgress
  • CCTransitionRotoZoom:
  • CCTransitionShrinkGrow
  • CCTransitionSlideInL
  • CCTransitionSplitCols
  • CCTransitionTurnOffTiles

层:CCLayer

有时候场景里应该要有多个层,那就将更多CCLayer添加到场景里,最常见的就是在sence方法里做:

+(id) scene {
   CCScene* scene = [CCScene node];
   CCLayer* backgroundLayer = [HelloWorldBackground node];
   [scene addChild: backgroundLayer];
   CCLayer* layer = [HelloWorld node];
   [scene addChild:layer];
   CCLayer* userInterfaceLayer = [HelloWorldUserInterface node];
   [scene addChild: userInterfaceLayer];
   return scene;
}

那这个场景就有了三个层,背景层、常规层以及最上面的用户界面层。可以想见:放在背景层的node永远绘制在其他层的下面,……

用多层结构的愿意之一可能是背景及其上的node可以轻易被移动,而不会影响上层。有时候也会有旋转一堆node的需求,用若层管理它们,可以一次全部旋转。等等层的好处。

接收触摸事件

CCLayer可以接收触摸事件,但要显式开启:

self.isTouchEnabled = YES;

这句最好在类的init方法里执行,但也可以在其他时候改变它。这个属性一旦被设置,一堆接收触摸输入的方法就该被调用了。顾名思义,这些方法大概如下:

-(void) ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
-(void) ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
-(void) ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
-(void) ccTouchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event

许多时候,你想知道触摸的位置:

-(CGPoint) locationFromTouches:(NSSet *)touches
{
   UITouch *touch = touches.anyObject;
   CGPoint touchLocation = [touch locationInView:touch.view];
   return [[CCDirector sharedDirector] convertToGL:touchLocation];
}

这个方法处理单个触摸,若要处理多点触摸,需要分别追踪每个触摸。

演员、精灵:CCSprite

最常用的一种,以一张图片的方式现实一个精灵在屏幕上。

CCSprite* sprite = [CCSprite spriteWithFile:@"Default.png"];
[self addChild:sprite];

其中图片会以精灵的位置居中,因为精灵初始化在(0,0),所以图片会有一半(3/4)看不到。所以有必要调整精灵的位置。

锚点

每个node都有一个锚点,但只有有配图的才有作用,例如CSprite或CCLabelTTF。默认,锚点是(0.5,0.5),即在配图的中心。

三种可能需要改变锚点的情况:

  • 根据屏幕边框对齐节点更容易
  • 调整标签、按钮或图像的上/下/左/右对齐。
  • 在不改变精灵位置的情况下,调整一个已经改变尺寸图像的对齐
Jan 8, 2013

By Goalay.Shi

我喜欢北方的冬天,喜欢冬天里严肃的感觉。

冬天的气息弥漫在空气中,寒冷干燥的空气让人能随时随地闻到冬天的味道。从一年的年末到第二年的年初都不会融化的冰雪在寒冷的冬天里提醒着人们,现在,这个世界,已被冬天接管。

路边的白杨光秃秃的杵在那里。树叶早已被萧瑟的秋风撕扯殆尽,细小的枝条也被寒风折断,就连稍粗一些的树杈也逃不过被大雪压折的命运。一棵棵白杨树只剩下外皮早已干褐的树干和斜刺在凛冽空气中的树枝还默默地矗立在路边,大风一吹所有的树枝都会咯咯作响。

河边的野草高一枯黄,被干燥寒冷的空气汲干了所有的水分,只剩下一片淡黄色的酥脆铺在河边。走在这一片淡黄色的草垫上,像走在蓬松的羊毛毯上,毛茸茸、软绵绵的,让人从脚底到胸口都开始温暖起来。

当白杨的枝条挂上飞雪,河边的枯草盖上厚重的棉絮,一切都被白色笼罩的时候。我只能望着皑皑白雪暗自揣摩着,白杨那粗壮的树干里是否还有水分在流动,被大雪掩盖的野草是否还能从这被冬天冻结的泥土中吸收养分。

或许寒冷的冬天早已冻结这片天地里的所有事物。耐寒如白杨、坚韧如野草也不得不在寒冬的压迫之下沉默,但也仅限于沉默。冷酷的寒冬只能用大雪与寒冷压迫这天地间的生灵,却冻不透白杨的树芯,冻不烂野草的草根。待到河流解冻冰雪融化的时候,白杨会抖掉身上的残雪,枯萎的野草会茂出新绿。

冬日呼啸的风雪带来的不仅是寒冷,还有浪漫与热情。漫天的雪花挥洒而下,被飞舞的雪花遮盖的天空更能引起遐思。飘渺的思绪穿过层层飞雪飘到九天之上,想一探雪花之上的天空此时是怎样的姿态。

雪片被寒风携裹着在空中飞舞,像一团团跳动着的银色火焰。这一团团银色火焰,总能勾出胸中最原始最强烈的热火。身体与心灵都会融入到这铺天盖地的大雪中。


此文由@Shelyscu提供,改了两个打字错误和换行,登载于此。