[转载]Flex 学习曲线图

mikel阅读(1120)

[转载]Flex 学习曲线图 – Java&Flex – ITeye技术网站.

Flex 的基础架构

关于 flex 基本上常被问到的不外乎就是“如何可以学好它?”,要了解这个问题的答案基本上只要看懂下面这个图就OK了。

Actionscript 该学的重点

从最底层看起,最下面的 actionscript 3是一切的基础,它是 flash/flex 编程使用的唯一程式语言,因此任何人想学好 flex 第一件事绝对是先摸熟 actionscript 这个语言,包含:

1. 它的基本语法与结构(array, hash, loop, if else…)
2. DisplayList (DisplayObject, DisplayObjectContainer)与 Event system(bubbling, propagating…)
3. Sound, Video, NetConnection 与 Graphics class

掌握 as3 的精华后,接下来就可以进入 flex framework。

Flex framework 的重点

基本上 flex framework 就是用 actionscript 写成的框架,因此也可以把它看成是 as3的最好示范,看着 framework source 学 actionscript 也是挺不错的,只是路会变很长。

Flex Framework 整个体系非常博大精深,通常一般人不太可能完整把它学完,只需要针对最常用到的部份熟悉就好,图中列出的那三块(component, managers, style/skin)就是我个人认为所有初学者最优先该学会的。

Component 该学些什么

Component 是整个 flex framework 的基础,几乎80% 的元素都是由 UIComponent 继承而来,例如最根本的它本身就是一个 UIComponent,因此,熟悉 component 就成为学好 flex framework 最根本也最重要的基本功

Flex 内建了 二十几个 UI controls, 例如 Button, DataGrid, HBox等,以种类来分,这些 components 可以概分为三大类:

* Controls: Button, DateChooser, Slider…
* Containers: Box, DividedBox, Panel…
* List: DataGrid, Tree, TileList…

初学者第一步至少该学会怎么用这些元件,了解每个元件的 properties, events, styles, effects…,知道怎么在手册里查它的 API 文件,以及何时该用何种元件。

进一步,则是学会怎么修改这些元件,例如继承一个 Button 下来加上不同的功能,或是写不同的 skin border 来改变它的外观。

再进一步,则是开始研究元件的生命周期,了解每个元件是何时初始化,元件内部有那些关键指令与它们个别的功能,然后可以试着建立自已的 custom component。

这一关看起来容易但实际上最困难,因为 flex 的 component framework 写的非常庞大,虽然乱中有序但要在混沌中看出隐藏的架构然后抓住重点整串提起,就非得有人带着指引正确的途径才比较可能完成。

manager 是什么

图中最上方的第二块就是 manager。

flex 里有很多的 managers,负责做各种不同的工作(废话…),几个比较重要的包含:

* SystemManager:
它是每个 flex 程序的根源,最先被下载,也最早启动,由它进行一连串的 app boot流程
* StyleManager:
它负责整支app 的 css style 套用与 skin 生成,如果想玩动态 css 载换也靠它
* DragManager:
Flex 最大的卖点就是 drag and drop(拖放),这个 manager 就是背后的英雄,初学者至少要学会怎么处理 drag 行为的五个事件,以及如何在不同元件间做拖放;进阶的玩家则应该深入研究这支 manager 是怎么写成的,详细阅读它的 source 会得到意想不到的无穷乐趣(如果你读完却没有这种感觉,呃,那代表你该再多读几次,如果还是没有,那请私下联络我 😀 )
* ModuleManager:
使用 Flex 开发大型应用程式时,往往会将程式切割成许多小的 module, 这个 manager 就是负责载入并管理所有的 module (包含它的 class partition),初心者或许用不到,但有志深入的玩家一定要很熟。
* CursorManager:
这个用到的时机不是很多,但偶尔要换一下 cursor 时还是会用到,初学者至少要知道怎么用指定的图案去换掉系统cursor。

Style/Skin 的重点

CSS style 与 skinning 是 Flex 最大的卖点之一,也是开发过程中较为麻烦也最耗时的部份。

初学者应该要彻底了解如何使用 CSS style 来打点一支 flex app 的外观,换颜色、素材,使用外部 assets 修饰介面。

中阶玩家则应该了解 skinning 的系统,包含 programmatic skinning 与 graphical skin,它们俩的差别?使用时机?如何客制化(zrong注1)?

更高阶的玩家则应该熟悉整个 Styling system 的运作模式,外加如何动态载入 css 在 runtime 换掉整个介面。

简而言之,flex app 写的好不好,外行人其实看不太出来,但一支 app UI 美不美则是一翻两瞪眼,比较漂亮的那就先加十分

(当然,有一种情况是刻意用心去美化了介面结果弄巧成拙搞的怨声载道人人喊打,但那种比较不多见,也不是每家公司都会搞到这步田地,就先不讨论)

学完基本功后下一步

在我的标准里,当一个 developer 对上图内每一块都有中等程度的了解后,就算是完成 flex 养成教育,可以迈向下一个阶段。

也就是开始熟悉 application 的制作手法,这包含

* 了解至少一种以上的开发框架,例如 Cairngorm,老实说我对这个框架没什么好感(因为手法太复杂,只适合超复杂登月计画或火星探勘时使用),但它结构设计良好,又是业界公认的圣杯, 等于是专家们共通的语言,因此至少要先了解它在做什么,将来在专业场合才好沟通(俗话说 know the rules so you know what you are breaking, 就是指这情况)
* 接着可以看看比较简单的手法,像 Riawave, Model-Glue:Flex, PureMVC…等,基本上这些框架设计方式都大同小异,每个都有不同的应用场合,可以挑一个喜欢的再自行修改。
* 了解基本的概念,例如 Value Object, DAO, MVC 等,它们在大部份的程式框架里都会出现,早点学会日子比较轻松。
* 接着就是开始实际 coding,写一个中小型规模的app,不论是单纯的 CRUD (zrong注2)程序,或是留言版、电话簿、进销存管理都可以,籍由多写来强化编程的概念,然后透过大量的 peer code review 来找出可改进的地方。

结论

结论还是老话一句:要入门 flex 超级简单,只要不是白痴应该一小时就行,但要成为可独当一面的专业开发者,路就很长,如果没有走对方向很容易就迷失甚至最后放弃。

换句话说,要能成为职场上真正需要的 professional developer,并不如表面上想象的容易(其实我想每种技术领域跟产业都一样吧),这也是我过去半年来协助很多公司做 recruiting 后的感想。

zrong注1:按客人要求不同定义
zrong注2:CRUD是指在做计算处理时的增加、查询(重新得到数据)、更新和删除(create, retrieve, update, and delete)几个单词的首字母简写。主要被用在描述软件系统中数据库或者持久层的基本操作功能

原文:http://ria.richtechmedia.com/2007/08/09/flex-%e5%ad%b8%e7%bf%92%e8%b3%87%e6%ba%90%e6%9b%b4%e6%96%b0/

[转载]微软发布Kinect for Windows7 SDK Beta开发工具包

mikel阅读(794)

[转载]微软发布Kinect for Windows7 SDK Beta开发工具包 « 岩屋(乐在其中).

微软今天发布了运行在 Windows 7 环境的 Kinect SDK Beta(开发工具包),需要 Microsoft .NET Framework 4.0、Microsoft Visual Studio 2010,使用 C++, C#, VB 等开发语言。硬件要求是双核 2.66GHz CPU,至少 2GB 内存建议 4GB,支持 DirectX 9 的显卡。

Skeleton tracking image

新闻:http://research.microsoft.com/en-us/um/redmond/projects/kinectsdk/about.aspx
下载:http://research.microsoft.com/en-us/um/redmond/projects/kinectsdk/download.aspx

[转载]不做职场橡皮人

mikel阅读(1132)

[转载]不做职场橡皮人 | GTD Life.

“橡皮人”是我这次研习会用来破题的切入点,得到了不少人的共鸣,所以在这里和更多的朋友分享一下。

据我所知,《橡皮人》是王朔1986年发明的词汇,现代人对它的解释是:“他们没有梦想,没有痛感,没有效率,没有乐趣,整个人犹如橡皮做成的”。

为什么会被“橡皮化”?

我觉得是进入了恶性循环:没有梦想,就没有痛感,就像没有爱就不会被伤害一样,没有痛感就没有效率,就像死猪不怕开水烫,没有效率就没有乐趣,就像不以结婚(乐趣)为目的的恋爱(工作效率、生活效率)都是耍流氓。

解决问题的方法就在于找到问题本身,梦想、效率、痛感、乐趣都可以当作解决问题的切入点

NewImage

梦想+效率

我和李参在郑州的麦当劳碰面的时候,曾经聊到过她遇到的恶性循环:

  • 课件:她的梦想是做一名自由职业培训师(和我一样哦~所以我们俩互为良师益友,呵呵~这种结伙攻顶的感觉很棒!),所以她的下一步行动是“开发出自己的第一个课件”
  • 时间:可是她每天早晨8:30到公司一直工作到晚上20:30,几乎天天如此,每天回到家都累的半死哪里有时间做自己的第一个课件呢?
  • 工作:可以为了梦想而辞掉工作吗?遗憾的是:不行!因为她是一个科技公司的内训师,这是她最擅长的领域,也是今后她主要的授课方向,她需要在这个行业里积累经验、积累人脉、掌握趋势。
  • 经验:而离开了宝贵的经验,“开发出自己第一个课件”的难度就加大了很多

所有值得一提的问题都不会被一下子轻易解决,必须找到一个有效的方法,每天坚持去应用,这就像是在砍树一样,拿一把锋利的斧头,找准一个地方不停的砍,再粗壮的树也伤不起啊,呵呵

1005141001eb055ae2686f936a

在这一次的研习会上,李参分享了自己是如何破解这个恶性循环的(特别感谢她赶来做我的助教组长!:)  )

她说她回去之后认真的思考了这个循环,觉得自己对管理好时间还有一些把握,所以打算从效率这点入手,她给自己的目标是“每天少工作一个小时!”

但是这是一个不能说,也不能照做的目标,你懂的。

所以她从三个方面实现“每天少工作一个小时”:

  1. 通过授权,使用番茄时间来提高效率和保持节奏,虽然没有提前一个小时下班,可是在每个工作任务之间得到了充分的休息,也减轻了自己的压力
  2. 下班之后回家不考虑太多的问题,早点休息(听说她的睡眠质量很高,羡慕ing)
  3. 每天早晨5:30-6:00起床,考虑与实现梦想相关的事情

可以看到,她虽然没有真的提前一个小时下班,但是却通过提高自己的效能、调整自己的节奏,为“创造一小时”做好了准备。

同时,我也很惊讶她能够在每天下班那么晚的情况下,坚持每天早晨早起(从西安回去的那天在火车上也是5:30起床,呵呵~),所以我相信每天叫醒她的不是闹钟,而是梦想!

“每天少工作一个小时”,其实是每天从快要窒息的工作状态中,呼吸一个小时的新鲜空气

而恶性循环其实是一种“习得性无助”,所以才会被“橡皮化”成为“橡皮人”

如果你不能破解恶性循环,就会成为橡皮人,如果能够破解恶性循环,就会成为超级赛亚人!让我们为超级赛亚人加油、喝彩!

屏幕快照 2011 07 03 下午09 10 35

痛感+乐趣

在我时间管理研习会的一个星期前,世界知名心灵导师彼尚(Bijan Anjomi)来西安做工作坊,他是一个伊朗人,前总统布什、里根、前加州州长施瓦辛格,都上过他的课程。

在他的课程里面有一个非常关键的理论,就是“分享奇迹”,他对“奇迹”的定义是:觉得快乐的事情就是奇迹,如果能够每天把奇迹分享出去,就能够获得更多的奇迹

他说:“建议你尽快在你的日志上写下你的奇迹,以免你忘记了,小我会努力的抹煞掉奇迹,让你要分享的时候记不起你要分享的奇迹是什么~”

NewImage

我对此深有感触,因为我已经坚持写成功日记大半年了,我觉得成功日记就是记录奇迹、分享奇迹。

我是这样使用成功日记的:

  • 我每天最多会写下五件让自己快乐的事情。
  • 遇到了,就立即写下来
  • 每周回顾的时候我会随便翻看,当我心情低落的时候,有时候会掉眼泪。
  • 我以前是使用笔记本每天写成功日记的,现在我用iphone上专门的软件来写(感谢开发者)

NewImage 《-笔记本   §§  iphone软件-》NewImage

用了一段时间之后发现软件要比笔记本更加适合写成功日记:

  1. 可以随时记,包括睡觉前
  2. 回顾起来比较方便
  3. 可以分享到新浪微博,可以看看别人写的成功日记
  4. 有一个“随便看看”功能我很喜欢,它可以随机取出5件以前写过的成功日记,有时候会有意外惊喜

其实写成功日记是一种“习得性快乐”,和前面“习得性无助”相对应。

每天只写下快乐的事情,实际上就是忘记让我们痛苦的事情,这样让我们对生活更加的敏感。

橡皮人的定义里有一条就是“没有痛感”,现在紧张的节奏和复杂的工作让我们疲于应付,所以我们的生活几乎是一条直线,而成功日记就是帮助我们从平淡的生活中挖掘出兴奋点,当我们感受到了快乐和开心的事情,同样也就能感觉到痛苦和沮丧的事情。

Untitled

总结

通过和很多优秀的人交流,我现在越来越感觉到时间管理是一种追求幸福的入口,而不是太过忙碌的出口,就像求神拜佛应该是思想到某种境界之后的一个入口。而不是精神寄托的出口一样。

所以这种追求往往不是一蹴而就的,需要积跬步以致千里,“每天少工作一个小时”是这样,写成功日记也是这样。

解决问题的方法就在问题本身,李参通过梦想+效率解开被橡皮化的恶性循环,成功日记通过痛感+乐趣解开平淡、无聊的状态,如果你愿意相信,和愿意坚持,那就一定可以成功!加油!

屏幕快照 2011 07 03 下午10 38 08

[转载]捕捉网页取消关闭事件

mikel阅读(950)

[转载]捕捉网页取消关闭事件 – EtherDream – 博客园.

在做Web开发时,我们经常用到页面关闭事件onbeforeunload,可以给用户一个选择放弃关闭的机会,就比如这个博客编辑器。如果用户选择了离开,那么onunload事件自然会触发;但若用户选择了取消,又该如何检测呢?

我们假定一个页面离开取消事件,叫做onunloadcancel。显然,这个事件应触发在用户按下对话框的取消按钮之后。但关闭提示对话框的触发流程并不是那么简单。我们先来回顾下这个过程:

window.onbeforeunload = function()
{
return "真的离开?";
}

当用户准备离开页面(比如按下关闭按钮,或者刷新页面等等),onbeforeunload事件触发。我们的脚本无法在这个事件里决定是否阻止页面的关 闭,唯一能做到的只有返回一个字符串,这个字符串仅作为说明文字出现在关闭选择对话框里,用户可以选择关闭,或者不关闭。但究竟选择哪个,我们无从得知。

然而仔细分析下这个问题,其实不然。 如果用户真选择了关闭页面,那么之后所有的运行代码都byebye了;而继续留在页面的话,就当什么都没发生过,除了onbeforeunload事件。 所以,我们在onbeforeunload事件里做点小花招,在此注册个几毫秒之后启动的定时器,如果页面真关闭了,那么这个定时器当然是作废了;那么页 面还在,几毫秒的延时对于这个本来就是异步的界面交互事件也没有什么误差。

<script language="JavaScript"> window.onbeforeunload = function() { setTimeout(onunloadcancel, 10); return "真的离开?"; } window.onunloadcancel = function() { alert("取消离开"); } </script>

我们使用setTimeout,延时10ms执行onunloadcancel。如果页面真关闭了,定时器当然都销毁;反之继续。但在测试中,发现FireFox有个两个BUG:

1.有时按下关闭按钮,也会执行onunloadcancel,并且有个对话框一闪而过。如果换成while(1);浏览器会一直卡死,这说明onunloadcancel确实是执行了,只是销毁了界面,但并没有暂停脚本的运行。

2.如果是通过刷新页面的方式离开,仅执行一次onbeforeunload,但点击X按钮关闭页面,会执行两次onbeforeunload。因此我们还需在完善下,以便兼容FF。

<script language="JavaScript"> var _t; window.onbeforeunload = function() { setTimeout(function(){_t = setTimeout(onunloadcancel, 0)}, 0); return "真的离开?"; } window.onunloadcancel = function() { clearTimeout(_t); alert("取消离开"); } </script>

这里使用了一种我也说不出原因的办法,应该算是hack,解决了FF下的bug。

[转载]15本Web开发人员应该拥有的免费电子书籍

mikel阅读(1084)

[转载]15本Web开发人员应该拥有的免费电子书籍_IT新闻_博客园.

当今互联网已经成为每一个人的信息知识来源。假如你想学习任何事情,可以很容易在互联网上轻松找到相关的信息,即使它是很简单的事情。在互联网上有成千上万的教程和指南可以用来学习与工作相关的技术和内容。

Web开发人员同样也经常通过互联网获取关于HTML和编码的相关知识。为了帮助这些开发人员,以下是我们收集的一些优秀电子书供大家学习。

1. HTML5 Quick Learning Guide

clip_image002

如果您想学习HTML5,这本电子书将帮助您学习所有HTML5的基本标签元素。

2. Head First HTML with CSS and XHTML

clip_image004

有了这本电子书,你可以使用HTML和CSS技术轻松地创建基于标准的网页。

3. Best Practices for Developing a Web Site

clip_image006

这是一个很好的电子书,将引导你建立一个网站和计划项目,无论你正在开发一个内部网站或外包项目。
4. HTML Wtf?

clip_image008

这本书简要概述了由HTML5带来的变化。
5. HTML And XHTML

clip_image010

一个了解HTML和XHTML的简单而全面的指南。
6. Javascript Programming for The Absolute Beginner

clip_image012

JavaScript不了解?不要担心,这本书将帮助你学习JavaScript,即使你不知道任何关于JavaScript的知识。
7. Dive into Accessibility

clip_image014

这本电子书回答了一些关于如何建设一个网站的简单问题。
8. Getting Real

clip_image016

这本电子书介绍了在建设一个网站时需要注意的一些问题。
9. Web Design in a Nutshell

clip_image018

这本书包含了CSS与XHTML编码和Web设计所有相关的信息。
10. Foundations of Ajax

clip_image020

本书介绍了Ajax技术到现有应用或新开发应用程序所需要所有工具。
11. Up to Speed on HTML5 and CSS 3

clip_image022

介绍关于新兴的HTML5和CSS3标准。
12. Learning PHP 5

clip_image024

这本将帮助你学习PHP5拥有的一些高级功能,如面向对象功能和对XML与Web Services的支持等。
13. 20 Things I Learned about Browsers and The Web

clip_image026

这本书包含了所有你想了解的关于网站、Cookie和历史记录等知识。
14. Web Style Guide: 3rd Edition

clip_image028

这本书将告诉你如何使你的网站更容易让大家使用。
15. Essential Javascript and JQuery Design Patterns

clip_image030

通过这本电子书,你将学习到如何使用Javascript和JQuery设计模式。

[转载](译)iPhone上面的现实增强(Augmented Reality )入门教程

mikel阅读(809)

[转载](译)iPhone上面的现实增强(Augmented Reality )入门教程 – 子龙山人 – 博客园.

免责申明(必读!):本博客提供的所有教程的翻译原稿均来自于互联网,仅供学习交流之用,切勿进行商业传播。同时,转载时不要移除本申明。如产生任何纠纷,均与本博客所有人、发表该翻译稿之人无任何关系。谢谢合作!

原文链接地址:http://www.raywenderlich.com/3997/introduction-to-augmented-reality-on-the-iphone

教程截图:

Create a simple augmented reality game where you can blast some aliens!

Create a simple augmented reality game where you can blast some aliens!

在这篇教程中,你将学习到如何为你的iphone和ipod touch制作一个简单的增强现实游戏。

在这个游戏中,你将使用到摄像头,陀螺仪和cocos2d框架。听起来很振奋人心吧?

在写作这篇教程的时候,探索上面提到的一些技术的过程真的是非常有趣。这里有一些数学和转换,不过不要担心—没有什么事情是很难的!

学习这篇教程的时候,你需要一个iPhone4,因为这个教程使用陀螺仪来移动游戏世界视图。

你也需要一些基本的cocos2d方面的知识,当然要安装好cocos2d。如果你对于cocos2d完全陌生的话,你可以先看看本博客上的其它cocos2d教程

你准备好爆头一些虚拟外星人了吗?跟我来吧!

Getting Started

打开Xcode,然后从File菜单中选择New\New Project。然后选择 iOS\cocos2d\cocos2d template,接着点击Next。把工程命名为ARSpaceShips,并点击Next,同时选择一个文件夹位置来保存本项目,最后点 Create。

我们将重用Space Shooter游戏的一些资源文件,所以,先下载它们并解压缩。

下载完后,把Fonts,Sounds和Spritesheet文件夹拖到Resources分组下面。同时确保 Copy items into destination group’s folder被复选中,并且ARSpaceships target也要被选中。然后点击Finish。这时,你的工程看起来应该如下图所示:

我们将在后面使用到这些资源。

玩一玩摄像头!

如果你现在运行项目,那简单太无聊了,就是一个黑屏的HelloWorld,这玩意儿谁没看过!让我们往里面添加一些非常好玩的内容吧。选择AppDelegate.h,然后在interface里面添加一个UIView成员变量。

UIView *overlay;

现在,打开AppDelegate.m文件,找到EAGLView *glView所在的代码行,把像素格式改成kEAGLColorFormatRGBA8。改完后如下图所示:

EAGLView *glView = [EAGLView viewWithFrame:[window bounds] pixelFormat:kEAGLColorFormatRGBA8 depthFormat:0];

如果你不改变像素格式的话,那么摄像头里拍摄出来的图像就显示不出来。因为我们现在正在做一个增强现实的游戏,所以必须这样做!

在 [window addSubview: viewController.view];下面,我们添加了以下代码:


// set the background color of the view
[CCDirector sharedDirector].openGLView.backgroundColor = [UIColor clearColor];
[CCDirector sharedDirector].openGLView.opaque = NO;

// set value for glClearColor
glClearColor(0.0, 0.0, 0.0, 0.0);

// prepare the overlay view and add it to the window
overlay = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
overlay.opaque = NO;
overlay.backgroundColor=[UIColor clearColor];
[window addSubview:overlay];

这里,我们把openGLView的背景颜色清除了,把视图设置为透明的,同时指定了glClearColor,最后,我们创建并添加了一个新的视图,叫做overlay。这个视图后面用来显示camera里的内容。

接下来,在你刚刚添加的代码后面增加以下代码行:

#define CAMERA_TRANSFORM 1.24299

UIImagePickerController *uip;

@try {
uip = [[[UIImagePickerController alloc] init] autorelease];
uip.sourceType = UIImagePickerControllerSourceTypeCamera;
uip.showsCameraControls = NO;
uip.toolbarHidden = YES;
uip.navigationBarHidden = YES;
uip.wantsFullScreenLayout = YES;
uip.cameraViewTransform = CGAffineTransformScale(uip.cameraViewTransform,
CAMERA_TRANSFORM, CAMERA_TRANSFORM);
}
@catch (NSException * e) {
[uip release];
uip = nil;
}
@finally {
if(uip) {
[overlay addSubview:[uip view]];
[overlay release];
}
}

[window bringSubviewToFront:viewController.view];

首先,我们了一个常量来缩放摄像头。摄像头的比率是4:3,而iphone屏幕的比例是3:4,所以,我们需要缩放一下摄像头,使之与屏幕匹配。

其次,我们创建了一个UIImagePickerController,设置了它的一些属性,具体效果就是没有控件并且缩放了的摄像头。然后,我们把它添加到了overlay视图中。

最后,我们需要把 viewController.view显示到前面来(因为它包含了cocos2d的显示内容)。这样子摄像头拍摄的内容也就会显示到前台来了。

现在,运行一下应用程序。现在,你将看到摄像头里面捕捉的画面是你的Hello World的背景了。

Shake, Rattle, and Roll…Well at Least Yaw!

因为我们现在的程序已经可以把现实捕捉并显示出来了,接下来,我们将集中精力来解决本教程中比较难的部分。

首先,我们需要把CoreMotion framework添加到项目中去。点击project文件,然后选择ARSpaceships target,再选择Build Phases标签页,展开 Link Binary With Libraries.。

点击上图中的+号,选择 CoreMotion.framework,然后点击Add按钮。现在,我们就可以在项目中使用陀螺仪啦。

打开HelloWorldLayer.h,然后导入一些头文件:

#include
#import

然后在interface里面添加一些成员变量:

CMMotionManager *motionManager;
CCLabelTTF *yawLabel;
CCLabelTTF *posIn360Label;

同时添加属性声明语句:

@property (nonatomic, retain) CMMotionManager *motionManager;

现在,重点要来了!打开HelloWorldLayer.m文件,在if ((self=[super init]))语句内部,删除原来的“Hello World”标签语句,同时添加下面的代码来设置一些新的标签.

// add and position the labels
yawLabel = [CCLabelTTF labelWithString:@"Yaw: " fontName:@"Marker Felt" fontSize:12];
posIn360Label = [CCLabelTTF labelWithString:@"360Pos: " fontName:@"Marker Felt" fontSize:12];
yawLabel.position = ccp(50, 240);
posIn360Label.position = ccp(50, 300);
[self addChild: yawLabel];
[self addChild:posIn360Label];

目前为止,并没有什么特别的。只是添加了一些标签,指明了字体和一些文字。标签的位置都是在屏幕的左边。

接下来,你需要设置motion manager,它会启动陀螺仪。

self.motionManager = [[[CMMotionManager alloc] init] autorelease];
motionManager.deviceMotionUpdateInterval = 1.0/60.0;
if (motionManager.isDeviceMotionAvailable) {
[motionManager startDeviceMotionUpdates];
}

[self scheduleUpdate];

这里,我们分配并初始化了 motion manager对象。同时,我们还设置了更新间隔为每秒60次。如果设置支持陀螺仪的话,那么就启动更新。最后,我们触发一个update定时器。

不要忘了在.m文件中添加synthesize语句,如下所示:

@synthesize motionManager;

因为我们触发了update定时器,所以我们需要添加一个update方法。同时,在init方法的下面增加下面一个方法:

-(void)update:(ccTime)delta {
CMDeviceMotion *currentDeviceMotion = motionManager.deviceMotion;
CMAttitude *currentAttitude = currentDeviceMotion.attitude;

// 1: Convert the radians yaw value to degrees then round up/down
float yaw = roundf((float)(CC_RADIANS_TO_DEGREES(currentAttitude.yaw)));

// 2: Convert the degrees value to float and use Math function to round the value
[yawLabel setString:[NSString stringWithFormat:@"Yaw: %.0f", yaw]];

// 3: Convert the yaw value to a value in the range of 0 to 360
int positionIn360 = yaw;
if (positionIn360 &lt; 0) {
positionIn360 = 360 + positionIn360;
}

[posIn360Label setString:[NSString stringWithFormat:@"360Pos: %d", positionIn360]];

}

现在,你可以运行应用程序了。你将会看到Yaw和positionIn360的对应标签值在改变。

那玩意儿究竟如何工作?

尽管可以跑起来了,但是你可能会问,它究竟是如何工作的呢?让我们花几分钟时间来一步步解释上面的代码是如何工作的。

首先,下载iTunes app store上面的免费程序 Gyrosocope app。安装并运行它,当你移动iphone的时候,就可以看到每个值究竟是怎么变化的。

我们关心的值是Yaw。这个值代表往左或往右移动。在Gyroscope程序中,它的单位是度,然而 motion manager获取的值却是弧度。这就是为什么我们需要使用内置的函数CC_RADIANS_TO_DEGREES来把弧度转换成角度的原因啦。

因此,在第一部分中,我们得到了yaw的弧度值,并且把它转换成角度,最后赋值给yaw变量。第二部分,我们只是把yaw的值显示到屏幕上去。如果你运行工程,你会看到yaw值的变化范围是从0~180,然后又从-180回到0.

现在看看第三部分,你可能会奇怪positionIn360的值倒底是什么啊?好吧,这里只是一个手段,目的是使得放置飞碟的过程变得容易。

这里的逻辑其实非常简单。如果yaw值是正的,那么我们什么也不做。如果是负的,那么就减去360.(加上一个负值和减去一个正值是一样的)。最后一行代码只是在屏幕上显示那个值。

如果你还没完全理解,没关系,运行一下程序,看看具体值是怎么变化的吧。

灯光,摄像机,Action!

现在,我们为陀螺仪的使用奠定基础了,让我们往里面添加一些太空飞船吧!我们将创建一个新的文件。首先,左键点ARSpaceships工程文 件,然后选择New File。接着选 iOS\Cocoa Touch\Objective-C class,然后点击Next。确保NSObject被选中基类,然后点Next。把文件命名为EnemyShip.m,最后点Save。

把 EnemyShip.h里的内容换成下面的代码:

#import "cocos2d.h"

@interface EnemyShip : CCSprite {
int yawPosition;
int timeToLive;
}

@property (readwrite) int yawPosition;
@property (readwrite) int timeToLive;

@end

同时修改EnemyShip.m:

#import "EnemyShip.h"

@implementation EnemyShip

@synthesize yawPosition, timeToLive;

-(id)init {
self = [super init];
if (self){
yawPosition = 0;
timeToLive = 0;
}
return self;
}

@end

现在,回到HelloWorldLayer.h。在顶部导入EnemyShip类的头文件,如下所示:

#import "EnemyShip.h"

在interface里面声明以下成员变量:

NSMutableArray *enemySprites;
int enemyCount;
CCSpriteBatchNode *batchNode;

最后,在interface声明下面,添加enemyCount属性,同时定义一些方法,具体如下图所示:

@property (readwrite) int enemyCount;

-(EnemyShip *)addEnemyShip:(int)shipTag;
-(void)checkEnemyShipPosition:(EnemyShip *)enemyShip withYaw:(float)yawPosition;
-(void)updateEnemyShipPosition:(int)positionIn360 withEnemy:(EnemyShip *)enemyShip;
-(void)runStandardPositionCheck:(int)positionIn360 withDiff:(int)difference withEnemy:(EnemyShip *)enemyShip;

打开 HelloWorldLayer.m文件,同时作以下修改:

// Place after the #import statement
#include

// Place after the other @synthesize statement
@synthesize enemyCount;
#define kXPositionMultiplier 15
#define kTimeToLive 100

// Add to the bottom of init
batchNode = [CCSpriteBatchNode batchNodeWithFile:@"Sprites.pvr.ccz"];
[self addChild:batchNode];
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:@"Sprites.plist"];

这里,我们加载了spritesheet,它的资源文件在一开始的时候,我们就把它拖到项目中来了。

接下来,我们将添加一个新的方法来创建新的敌人太空飞船。在dealloc方法的上面添加下列方法:

-(EnemyShip *)addEnemyShip:(int)shipTag {

EnemyShip *enemyShip = [EnemyShip spriteWithSpriteFrameName:@"enemy_spaceship.png"];

// Set position of the space ship randomly
int x = arc4random() % 360;
enemyShip.yawPosition = x;

// Set the position of the space ship off the screen, but in the center of the y axis
// we will update it in another method
[enemyShip setPosition:ccp(5000, 160)];

// Set time to live on the space ship
enemyShip.timeToLive = kTimeToLive;
enemyShip.visible = true;

[batchNode addChild:enemyShip z:3 tag:shipTag];

return enemyShip;
}

这个方法接收一个整数值作为tag,并且返回一个EnemyShip CCSprite。下面一行代码,我们从精灵表单中创建一个EnemyShip精灵。接着,我们使用arc4random方法来得到一个0~360的随机数。最后,我们设置了飞船的位置,并把timeToLive的值设置为100,把飞船添加到batchNode里面,最后返回飞船精灵对象。

在addEnemyShip下面,我们添加一个checkEnemyShipPosition方法:

-(void)checkEnemyShipPosition:(EnemyShip *)enemyShip withYaw:(float)yawPosition {
// Convert the yaw value to a value in the range of 0 to 360
int positionIn360 = yawPosition;
if (positionIn360 &lt; 0) {
positionIn360 = 360 + positionIn360;
}

BOOL checkAlternateRange = false;

// Determine the minimum position for enemy ship
int rangeMin = positionIn360 - 23;
if (rangeMin &lt; 0) { rangeMin = 360 + rangeMin; checkAlternateRange = true; } // Determine the maximum position for the enemy ship int rangeMax = positionIn360 + 23; if (rangeMax &gt; 360) {
rangeMax = rangeMax - 360;
checkAlternateRange = true;
}

if (checkAlternateRange) {
if ((enemyShip.yawPosition &lt; rangeMax || enemyShip.yawPosition &gt; rangeMin ) || (enemyShip.yawPosition &gt; rangeMin || enemyShip.yawPosition &lt; rangeMax)) { [self updateEnemyShipPosition:positionIn360 withEnemy:enemyShip]; } } else { if (enemyShip.yawPosition &gt; rangeMin &amp;&amp; enemyShip.yawPosition &lt; rangeMax) {
[self updateEnemyShipPosition:positionIn360 withEnemy:enemyShip];
}
}
}

这个方法看起来似乎有点让人摸不着头脑,一会最大值,一会儿最小值,一会候选值。不过,不要担心,其实非常简单。首先,我们检查设置的yaw坐标址(positionIn360),然后把此值限制在0~360之间。

因为,我们有两端范围都是0~360,所以需要检查一下设置的positionIn360具体属于哪一端。我们使用一个任意数23来代表将在屏幕一半处显示的度数。

因为,我们只需要关心变化范围是0~23和337~360的空间了。因为,另一端的线将会包过来。(这里相像成3维空间的一个圆)

最后,如果敌人飞船在屏幕46度的范围之内的话,那么就要更新敌人飞船。checkAlternateRange判断语句只是用来决定什么时候来更新敌人飞船。

如果checkAlternateRange为真的话,那么我们就检查敌船的位置是否落在min和max的范围之内。

positionIn360 = 20
rangeMin = 357
rangeMax = 20
enemyShip.yawPosition = 359

因为我们要考虑线段的两端,我们的min范围比max范围要大一些。现在,我们做一个判断,看敌船的位置是不是大于rangeMin,如果是,则显示在屏幕上。

if语句中的else就更加明了了。他检查敌船的位置是不是大于min且小于max。

多么复杂的一个update方法啊!我们在checkEnemyShipPosition方法下面添加以下代码:

-(void)updateEnemyShipPosition:(int)positionIn360 withEnemy:(EnemyShip *)enemyShip {
int difference = 0;
if (positionIn360 &lt; 23) { // Run 1 if (enemyShip.yawPosition &gt; 337) {
difference = (360 - enemyShip.yawPosition) + positionIn360;
int xPosition = 240 + (difference * kXPositionMultiplier);
[enemyShip setPosition:ccp(xPosition, enemyShip.position.y)];
} else {
// Run Standard Position Check
[self runStandardPositionCheck:positionIn360 withDiff:difference withEnemy:enemyShip];
}
} else if(positionIn360 &gt; 337) {
// Run 2
if (enemyShip.yawPosition &lt; 23) { difference = enemyShip.yawPosition + (360 - positionIn360); int xPosition = 240 - (difference * kXPositionMultiplier); [enemyShip setPosition:ccp(xPosition, enemyShip.position.y)]; } else { // Run Standard Position Check [self runStandardPositionCheck:positionIn360 withDiff:difference withEnemy:enemyShip]; } } else { // Run Standard Position Check [self runStandardPositionCheck:positionIn360 withDiff:difference withEnemy:enemyShip]; } } 

  在这个方法中,我们测试,看是否设备的positionIn360是不是在讨论的3个范围内。在第一个测试中,我们检测positionIn360是不理小于23,如果是,我们就看看是不是有一些敌船在线的另一端(大于337)。   第二部分测试,看是否positionIn360大于337.如果是的话,就再检测它是否小于23.   第二部分测试,看敌船是否在23和337之间。如果是,则调用runStandardPositionCheck方法。这个方法的定义如下所示:

 -(void)runStandardPositionCheck:(int)positionIn360 withDiff:(int)difference withEnemy:(EnemyShip *)enemyShip { if (enemyShip.yawPosition &gt; positionIn360) {
difference = enemyShip.yawPosition - positionIn360;
int xPosition = 240 - (difference * kXPositionMultiplier);
[enemyShip setPosition:ccp(xPosition, enemyShip.position.y)];
} else {
difference = positionIn360 - enemyShip.yawPosition;
int xPosition = 240 + (difference * kXPositionMultiplier);
[enemyShip setPosition:ccp(xPosition, enemyShip.position.y)];
}
}

在这个方法中,我们检查看是否enemyShip是否在设备的positionIn360的左边还是右边。当enemyShip的位置小于positionIn360时,它将出现在屏幕的左边。当enemyShip的位置大于positionIn360,那么它将出现在屏幕的右边。

现在,你会说,请等一下!你忘了描述这些变量的作用了!好吧,接下来就解释一下。

如果敌船的yaw坐标值在屏幕的范围之内(从 positionIn360 – 23到 positionIn360 + 23),然后,首先我们计算它位于屏幕的哪一边。如果大于positionIn360,那么就在屏幕的右边,否则就在屏幕的左边。

difference变量的作用是保存设备的positionIn360和敌船的 yaw位置的角度差值。一旦计算出来后,我们就把这个差值乘以一个任意的倍数。这个倍数代表每一度的像素个数。这个里,我们选择15.

基于它位于于当前屏幕的位置,我们将把这个计算出来的值增加240或者减去240。

现在,所有需要的方法已经全部准备就绪啦。

在init方法的底部,添加下面的代码,在屏幕中增加5个飞船:

// Loop through 1 - 5 and add space ships
enemySprites = [[NSMutableArray alloc] init];
for(int i = 0; i &lt; 5; ++i) {
EnemyShip *enemyShip = [self addEnemyShip:i];
[enemySprites addObject:enemyShip];
enemyCount += 1;
}

因为,我们添加了敌船到屏幕中了,我们确保它们的位置被更新。在udpate方法的底部添加下面方法:

// Loop through array of Space Ships and check the position
for (EnemyShip *enemyShip in enemySprites) {
[self checkEnemyShipPosition:enemyShip withYaw:yaw];
}

在我们忘记之前,在dealloc里面添加清理代码来清理我们之前创建的enemySpritesArray数组:

[enemySprites release];

现在,编译并运行工程吧!当你旋转设备的时候,你将会看到5个飞船在不同的地方。


<img src="http://pic002.cnblogs.com/images/2011/283130/2011070222393336.jpg" alt="" />
<h2>免费的激光和爆炸</h2>
目前为止,这个现实增加的游戏完成的差不多了。不过,还有一个很严重的问题:这里飞船打中后没什么感觉。

很明显,我们并不想这样,所以,让我们添加一些很酷的激光和爆炸效果吧。

在开始之前,让我们移除屏幕上的label--他们只是作为调试时用的。因此,找开 HelloWorldLayer.m中关于labels的代码,并把它们注释掉。完成之后,编译并运行,保证没有错误。

现在,看看有趣的部分---让我们往游戏中添加一些火力吧!首先,我们将添加一个方法用来判断玩家的开火区域是否击中了飞船。在HelloWorldLayer.h文件中,在@end之前声明下列方法:

- (BOOL) circle:(CGPoint) circlePoint withRadius:(float) radius collisionWithCircle:(CGPoint) circlePointTwo collisionCircleRadius:(float) radiusTwo;

打开HelloWorldLayer.m,然后在dealloc方法上面实现上述方法:

- (BOOL) circle:(CGPoint) circlePoint withRadius:(float) radius collisionWithCircle:(CGPoint) circlePointTwo collisionCircleRadius:(float) radiusTwo {
float xdif = circlePoint.x - circlePointTwo.x;
float ydif = circlePoint.y - circlePointTwo.y;

float distance = sqrt(xdif*xdif+ydif*ydif);
if(distance &lt;= radius+radiusTwo) return YES;

return NO;
}

这个方法用来检测是否两个点的半径有交集。输入的参数是敌方飞船位置和屏幕的中心点位置。两个点的半径都设置为50.

首先,我们计算两个点X和Y值的差。接下来计算两点的距离。这个在高中就学过的,叫勾股定理。你可以从这里得到更多的信息。

接下来,我们往屏幕中添加一个区域,用作火力瞄准器。下载这些资源文件,解压缩,然后把scope.png拖到Resouces文件夹下去。确保“Copy items into destination group’s folder”被复选中,然后点Finish。

打开HelloWorldLayer.m的init方法,然后在 [self scheduleUpdate]方法之前,添加以下代码:

// Add the scope crosshairs
CCSprite *scope = [CCSprite spriteWithFile:@"scope.png"];
scope.position = ccp(240, 160);
[self addChild:scope z:15];

// Allow touches with the layer
[self registerWithTouchDispatcher];

如果你现在运行程序,你将看到一个瞄准器出现在屏幕的中间。

非常好,现在让我们添加一些爆炸效果,在玩家点击屏幕的时候就触发。我们将按照添加scope.png的方法一样,来添加Explision.plist。先找到之前下载的本项目资源文件。把Explosion.plist拖到资源文件夹中,确保“Copy items into destination group’s folder”被复选上,然后点击Finish。

你可能会奇怪这个文件到底是什么?我使用一个很酷的软件制作的,你可能之前也听说过了,叫做 Particle Designer,它是由71 Squared的工程师所开发的。我不会在这里讲解如何使用此软件来制作这样的粒子效果文件,但是,实际上这个过程是非常简单的。选择一种粒子效果,然后调节一些参数,最后导出plist文件就可以了。

现在,在dealloc方法之前,添加下列代码:

-(void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
CGPoint location = CGPointMake(240,160);

// 1
for (EnemyShip *enemyShip in enemySprites) {
if (enemyShip.timeToLive &gt; 0) {
// Check to see if yaw position is in range
BOOL wasTouched = [self circle:location withRadius:50 collisionWithCircle:enemyShip.position collisionCircleRadius:50];

if (wasTouched) {
enemyShip.timeToLive = 0;
enemyShip.visible = false;
enemyCount -= 1;
}
}
}

// 2
CCParticleSystemQuad *particle = [CCParticleSystemQuad particleWithFile:@"Explosion.plist"];
particle.position = ccp(240,160);
[self addChild:particle z:20];
particle.autoRemoveOnFinish = YES;

// 3
if (enemyCount == 0) {
// Show end game
CGSize winSize = [CCDirector sharedDirector].winSize;
CCLabelBMFont *label = [CCLabelBMFont labelWithString:@"You win!" fntFile:@"Arial.fnt"];
label.scale = 2.0;
label.position = ccp(winSize.width/2, winSize.height/2);
[self addChild:label z:30];
}
}

这段代码的第一部分用来做碰撞检测,用来测试是否飞船在瞄准器范围之内。如果其中一个飞船被击中了,我们就飞船的属性来隐藏它,同时把 enemyCount计数减1.第二部分代码,往屏幕中心添加了一个爆炸粒子系统。第二部分代码,也是最后一部分代码,它用来检查enemyCount是 否为0,如果是的话,就显示一个label,告知玩家游戏结束了。

这个游戏如果就这样的话,有点无聊。所以,让我们往游戏中添加一些基本的AI吧。其实也很简单的,就是随着时间的推移,我们会改变一下飞船的位置。所以,在update方法底部添加下列代码:

// Loop through array of Space Ships and if the timeToLive is zero
// change the yawPosition of the sprite
for (EnemyShip *enemyShip in enemySprites) {
enemyShip.timeToLive--;
if (enemyShip.timeToLive == 0) {
int x = arc4random() % 360;
[enemyShip setPosition:ccp(5000, 160)];
enemyShip.yawPosition = x;
enemyShip.timeToLive = kTimeToLive;
}
}

这段代码将会遍历所有的enemySprites,然后更新timeToLive属性。然后,检查这个timeToLive属性是否等于0,如 果是的话,那么就给飞船的yawPositon设置一个随机值,同时重置timeToLive属性。编译并运行工程吧,现在你想要打中飞船的话就有一些难 度了,开火!

Pump up the Volume!

游戏如果没有声音的话,那就太没意思了!所以,让我们添加一些音乐吧!

在HellowWorldLayer.m文件顶部包含Simple Audio Engine所需的头文件,具体如下所示:

#import "SimpleAudioEngine.h"

然后在init方法的最后添加下列代码,记得添加在 if ((self=[super init]))语句内部:

[[SimpleAudioEngine sharedEngine] playBackgroundMusic:@"SpaceGame.caf" loop:YES];
[[SimpleAudioEngine sharedEngine] preloadEffect:@"explosion_large.caf"];
[[SimpleAudioEngine sharedEngine] preloadEffect:@"laser_ship.caf"];

这里会加载背景音乐,同时预加载音效。

现在,找到ccTouchesBegan,然后在这个方法的开头添加下列代码:

[[SimpleAudioEngine sharedEngine] playEffect:@"laser_ship.caf"];

这里会在你点击屏幕的时候播放一个发射激光的音效。

还是在ccTouchesBegan方法里面,打开遍历enemySprites那个循环,然后在 (wasTouched)的if判断句内添加下列代码:

[[SimpleAudioEngine sharedEngine] playEffect:@"explosion_large.caf"];

当飞船被打中的时候,将会播放爆炸的音效!

编译并运行代码,尽情玩吧!

何去何从?

这里有本教程的完整源代码

如果你想学习更多有关制作增强现实的游戏的话,下面有一些比较好的资源:

我希望你在学习这个教程的时候会得到许多快乐!如果有什么建议或意见,请留言,谢谢!

[转载]MemCached:第三节 MemcachedProviders之SesstionStateProvider(关于Session的讨论)

mikel阅读(1074)

[转载]第三节 MemcachedProviders之SesstionStateProvider(关于Session的讨论) – 小城岁月 – 博客园.

本节讨论问题Memcached缓存有效期及SesstionStateProvider管理Session。
  • DefaultExpireTime 和 对象序列化存储
  • SesstionStateProvider
MemcachedProvider是如何控制存储数据的有效期的
一、DefaultExpireTime 和 对象序列化存储
配置文件方式
<!--?xml version="1.0" encoding="utf-8" ?-->

<!-- put your own server(s) here-->

<!-- Define some output appenders -->

<!--
<threshold value="OFF" />-->

<!-- Setup the root category, add the appenders and set the default priority -->

在配置文件中可以看到使用了DistCache使用用的缓存策略是MemcachedCacheProvider,如果不需要分布式缓存我们利用WebCache可以继承CacheProvider重写一个缓存Provider注入到Discache中。
keySuffix代表 Key后缀 即给你的Key加后缀,以方便使用MemcachedCacheProvider的不同客户端区分各自的缓存数据
DefaultExpireTime 缓存数据有效期 毫秒为单位,虽然匹配了但默认不使用的,必须通过指定的方法实现
看一下测试代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MemcachedProviders.Cache;
using MemcachedProviders.Session;
using System.Threading;

namespace DemoTest
{
class Program
{
static void Main(string[] args)
{
//服务端缓存通行证数据10秒
DistCache.DefaultExpireTime = 10000;

//假如用户登录成功
User user = new User();
user.Name = "小怜香";
user.Age = 20;
user.Birthday = Convert.ToDateTime("1991-1-1");
user.Sex = false;
user.IsLoginSucess = true;

//生成通过证
string passCode = Guid.NewGuid().ToString();
Console.WriteLine("首次缓存数据-------------------8秒后读取");
//缓存用户信息(10秒) ,并将PassCode保存到Cookie中
DistCache.Add(passCode, user, true);

//如何让用户在操作期间 缓存不过期呢?
Thread.Sleep(8000);
Console.WriteLine(DistCache.Get(passCode).Name);

Console.WriteLine("重新缓存--------------9秒后再读取");
//从Cookie中获取PassCode ,通过PassCode从缓存中读取用户数据后重新缓存
//User newUser = DistCache.Get(passCode);
//DistCache.Remove(passCode);
DistCache.Add(passCode, user, DistCache.DefaultExpireTime); //Set方式重新缓存 (MemcacheProvider封装不是很好)用第一方式设置还是Add

Thread.Sleep(8000); //如果上面的代码没有刷新缓存至10秒 那下面的代码肯定会超时
if(DistCache.Get(passCode)!=null)
Console.WriteLine(DistCache.Get(passCode).Name);
else
Console.WriteLine("因为没有操作,缓存未刷新,导致数据过期");

Console.WriteLine("3秒后再次读取数据----------");
Thread.Sleep(3000);
if (DistCache.Get(passCode) != null)
Console.WriteLine(DistCache.Get(passCode).Name);
else
Console.WriteLine("因为没有操作,缓存未刷新,导致数据过期");

//以上服务端的过程 即服务端用户登录数据先过期,PassCode取不到用户登录数据,可以判定为登录过期
//还有一种情况客户端Cookie先过期 导致无法取到PassCode,因此服务端就无法获取用户数据,可以判定为登录过期。
Console.ReadLine();
}

[Serializable]
public class User
{
public string Name { set; get; }
public bool Sex { set; get; }
public int Age { set; get; }
public DateTime Birthday { set; get; }
public bool IsLoginSucess { set; get; }
}

}
}

结果
可以看到通过DistCache.Add(“Name2”, “小凤仙我不认识”, true); 存储的数据在3秒后再取时已经过期清除了。
另外我们在使用MemcachedCacheProvider方法存储对象时,该对象一定要标记为可序列化Serializable。当然你也 可以不使用MemcachedCacheProvider,自己先将对象先序列化再存储,取出来的时候自己在反序列化。(没有压缩功能)
既然MemcachedCacheProvider有缓存过期功能,是不是可以利用这一点+Cookie实现一个自定义Session功能呢
当用户登录了系统,这个时候生成一个GUID的PassCode通过行证做为缓存Key,并将用户登录信息保存到缓存中,同时PassCode 根据请求输出到客户端Cookie中保存。此时Cookie也设置了有效期,服务端缓存也设置了有效期,因此可通过这两种情况来验证用户是否登录过期。
当Cookie中PassCode未过期,服务端可以从Cookie中获取PassCode 去缓存中读取用户登录数据,如果用户登录数据有效,刷重新设置用户数据有效期,并充许执行相关操作。当用户数据过期时则认为用户登录过期需重新登录。
当Cookie中PassCode过期,则服务端无法读取PassCode,则认为用户登录过期。 看一下服务端示例代码,因为客户端只要判断一下PassCode是否存在就可以了。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MemcachedProviders.Cache;
using MemcachedProviders.Session;
using System.Threading;

namespace DemoTest
{
class Program
{
static void Main(string[] args)
{
//服务端缓存通行证数据10秒
DistCache.DefaultExpireTime = 10000;

//假如用户登录成功
User user = new User();
user.Name = "小怜香";
user.Age = 20;
user.Birthday = Convert.ToDateTime("1991-1-1");
user.Sex = false;
user.IsLoginSucess = true;

//生成通过证
string passCode = Guid.NewGuid().ToString();
Console.WriteLine("首次缓存数据-------------------9秒后读取");
//缓存用户信息(10秒) ,并将PassCode保存到Cookie中
DistCache.Add(passCode, user, true);

//如何让用户在操作期间 缓存不过期呢?当用户每一次操作或访问就刷新一下服务端的缓存
Thread.Sleep(9000);
Console.WriteLine(DistCache.Get(passCode).Name);

Console.WriteLine("取出数据重新缓存--------------9秒后再读取");
//从Cookie中获取PassCode ,通过PassCode从缓存中读取用户数据后重新缓存
User newUser = DistCache.Get(passCode);
DistCache.Remove(passCode);
DistCache.Add(passCode, newUser, true);

Thread.Sleep(9000); //如果上面的代码没有刷新缓存至10秒 那下面的代码肯定会超时
if(DistCache.Get(passCode)!=null)
Console.WriteLine(DistCache.Get(passCode).Name);
else
Console.WriteLine("因为没有操作,缓存未刷新,导致数据过期");

Console.WriteLine("持续11秒没有刷新操作,再次读取数据----------");
Thread.Sleep(2000);
if (DistCache.Get(passCode) != null)
Console.WriteLine(DistCache.Get(passCode).Name);
else
Console.WriteLine("因为没有操作,缓存未刷新,导致数据过期");

//以上服务端的过程 即服务端用户登录数据先过期,PassCode取不到用户登录数据,可以判定为登录过期
//还有一种情况客户端Cookie先过期 导致无法取到PassCode,因此服务端就无法获取用户数据,可以判定为登录过期。
Console.ReadLine();
}

[Serializable]
public class User
{
public string Name { set; get; }
public bool Sex { set; get; }
public int Age { set; get; }
public DateTime Birthday { set; get; }
public bool IsLoginSucess { set; get; }
}

}
}

上面处理实现Session会话(不是ASP.NET的Session)的优点:
1.解决跨域,跨窗口的会话认证(针对ASP.NET的Session不跨窗口),解决A站点转B站点或重新打开浏览器访问的Session认证问题。
2.分布式缓存Session或PassCode,实现认证服务器负载均横。
3.效率要高于数据库。
缺点:
1.一台缓存服务器Down掉后,该服务器上所有Session或PassCode丢失,导致用户访问过期。
2.受分布式缓存读取数据命中率影响,一旦未命中则导致用户访问过期。
3.分布式缓存效率要低于传统缓存。
4.Cookie欺骗,伪造在有效期内的PassCode去骗取服务器认证。(这个问题无法避免,即使你用ASP.NET Session)
缺点的第1,2两条,我们可以通过重写MemcachedCacheProvider,增加写入数据库功能,同时这样做了也会因反复读写更新数据库而导致性能降低。当用户Cookie的PassCode还在有效期内,通过PassCode去找查缓存服务器中的用户登录信息,如果未查到(数据已过期或未命中亦或服务器Down掉),则去数据库中查找并确认是否已过期,如果未过期则读取并写入缓存中(针对服务器Down掉和丢失两种情况)。第3条是没有办法,实现分布式总是损失部分性能的。至于第4条可以通过加密解密的方式处理。 个人觉得在不是特别的情况下没必要增加数据库备份功能,因为你要反复读写更新数据库中这个缓存的有效期,以及过期后删除等操作。如果一定要保证Session不丢失,那建议还是直接保存数据库中,并在每次操作时更新数据的有效期。
Cookies跨域:正常的cookie只能在一个应用中共享,即一个cookie只能由创建它的应用获得。关于如何跨域读取Cookie方法很多,这里不介绍了。
URL后缀跨域:可将PassCode或SessionID通过URL后缀转到其它站点。(相比Cookie就暴露了,虽然可以加密弥补但是还是无法防止别人借用)
下面来看一下Memcahed实现Asp.net的Session缓存功能,这个是即实现了写入缓存中,又写入数据库中并实时更新有效期。
二、SesstionStateProvider
Session是由应用服务器维持的一个服务器端的存储空间,用户在连接服务器时,会由服务器生成一个唯一的SessionID,用该SessionID 为标识符来存取服务器端的Session存储空间。而SessionID这一数据则是保存到客户端,用Cookie保存的,用户提交页面时,会将这一 SessionID提交到服务器端,来存取Session数据。这一过程,是不用开发人员干预的。所以一旦客户端禁用Cookie,那么Session可能会失效,也可能会传Url传递即xxx.aspx?SessionID=xxxxxxxxx的形式。

这么看来,Session的原理其实和我们前面的设计的Cookie+PassCode+缓存功能是一致的。即打开浏览器请求后会产生一个Session会话,该会话有一个唯一标识SessionID(相当于PassCode),当用户登录后成功后,将用户登录信息缓存至对应用SessionID的Session存储中(相当于缓存)。当响应返回客户端后,SessionID被保存在Cookie中。当下一次请求回来时,服务端会根据Cookie中的SessionID从Session存储中取出对应的Session,绑到上下文中。然后从上下文中的Session获取相应的用户信息。
既然原理相同,那不可避免的都会碰到同样类似的问题:
1.Session丢失
2.Cookie或URL后缀参数SessionID一样可以欺骗
3.跨站点,跨浏览器,跨窗口等等访问一样无法通行。
有人说利用Cookie+Session ,即Session存储的用户信息及其它可能更多的数据保存到Cookie中。
1.在Cookie有效期内Session丢失了,即从Cookie中取回所有数据绑回HttpContext的Session中。
2.跨站点,跨浏览器,跨窗口时,在Cookie有效期内将Session绑至其它站点。
3.加密解决欺骗问题
这个时候Cookie除了传递认证标识,还兼具了弥补Session丢失和跨域的一个存储载体了,即取代了Session备份数据库的功能。那谁主导数据有效期控制呢?Session本身过期了,而Cookie没有过期,这样导致了客户端一直有权限访问站点。并且大量的信息被保存在客户端中,虽然加密了,但仍可借用骗取通行。因此不提倡这种做法,Cookie本身是轻量型的,最好只担当SessionID或PassCode等标识或一些客户端自定义性的数据的保存和传递工作。所归根到底,关于会话凭据及会话信息的备份工作是由谁来做?无疑这一块还是交给数据库来做最放心了,但实时的验证工作还是交给缓存或Session存储本身来处理。

当然你可以选择使用Cookie+asp.net Session+SQL备份兼跨域,或直接使用Cookie+PassCode+分布式缓存跨域+SQL备份的方案。
甚至也可以两者接合或 Cookie+asp.net Session+分布式缓存跨域+SQL备份。终于扯到了下面的内容了:
Asp.net 本身提供了sessionState的策略注入方案,看一下web.config的配置文件


通过继承SessionStateStoreProviderBase重写Provider可以实现对Session的进一步处理。即HttpApplication管线进入HttpHandler的时候,SessionStateStoreProviderBase会重新创建Session绑定到HttpContext中,并根据SessionID从对应的存储体读取数据初始化这个Session,当HttpHandler结束请求时,又会调用SessionStateStoreProviderBase将从HttpContext中读取新进Session的数据保存到对应的存储体(字典集合,缓存,数据库,分布式缓存等),并且清除掉过期的数据,至此Session清除,整个过程是循环往复的,也就是说Session只存在HttpHandler请求期间。
MemcachedProviders/SessionStateProvider在HttpHandler处理请求结束前,从HttpContext读取Session并写入或更新到Memcahed缓存服务器中,同时清除掉Memcahed中过期的数据,同时也触发了数据库相应的操作(更新缓存有效期,删除过期数据)。当下次请求至HttpHandler阶段,Memcached又根SessionID读取缓存中的Session绑回到HttpContext中。所以我们从HttpContext读取的Session,都是从Memcahed缓存中读取的。
可以看到providers的节点中MemcachedSessionProvider的属性connectionStringName=”SqlSessionServices” 指向了一个数据库连接。


MemcachedProvider提供该数据库的脚本,一张表tblSessions和相关操作存储过程
proc_CleanExpiredData是清除过期数据的存储过程。由于过期的数据或者直接关闭浏览器导致Session没有清除,而 MemcachedSessionProvider也没有自动清除这些Session,所以只能通过调用此存储过程定期清除掉一些过期的会话。我们也可以 通过SQL代理实现这一功能。
看一下Web访问之后的数据表结果
当同一个Session会话持续不断,Expires和LockDate会不断的刷新,以防止过期。Timeout为默认或设置的过期时间(分钟)。
规则是这样,当存储的数据过了有效期后,当有访问这些数据发生时,则MemcahedSesstionProvider会从表里清除这条对应的 SessionID记录。同时系统弹出登录过期,要求用户重新登录,这个时候只要用户不是关闭浏览器登录进来的,还是会用之前的SessionID再写入 tblSessions表中。关闭浏览器IE会自动清除Cookie上的SessionID,所以这个时候再打开浏览器就新产生了一个 SessionID。通过SessionStateProvider在Memcahed中保存了Session,同时也会实时更新至数据库。
既然Session都写入数据库了,完全可以在跨域时或者丢失从数据库里读取在有效期内的Session,而没必要通过分布式缓存读取。当不是跨域时访问时,又可以直接利用Session存储而没有必要通过Memcached读取。 (以上是误解)事实是这样的吗? 通过对SessionStateProvider源码分析,SessionStateProvider继承重写了 SessionStateStoreProviderBase,使用Memcached已经替代了原先Session的存储机制,而数据库则是充当 Memcahed的备份。SessionStateProvider并没有画蛇添足,并且确实考虑了所有情况,确实是个很完美的方案。
相反另一个问题就来了,为什么说Session会丢失,Session是在什么情况下丢失的?高负载,高并发?我没有碰到这些情况,假如这些情况容易发 生,相信放到Memcached中也会碰到这种问题,如果是这样那在不注重效率的情况下,看来只有直接利数据库存储Session是最安全的了。 web.config SessionState默认支持存入数据库存中的,只要配置一下就可以了。

另外微软也提供了一种会话分布式缓存解决方案:
Windows Server AppFabric 为 ASP.NET Web 应用程序提供了自定义的会话状态提供程序,Web 应用程序可以分散缓存群集中的会话对象,从而提供可伸缩性。鉴于 AppFabric 缓存功能的性质,您放入会话中的对象必须可序列化。有时间在写一篇关于Windows Server AppFabric

[转载]在IIS7上部署ASP.NET MVC3网站应用程序遇到的错误及解决方案

mikel阅读(770)

[转载]在IIS7上部署ASP.NET MVC3网站应用程序遇到的错误及解决方案 – 翟士丹 Jason Dan – 博客园.

系统环境:Windows2008R2,安装IIS,安装.NET 4,安装SQL Server, 安装ASP.NET MVC3服务器独立安装版本。

接下来将Web应用程序Deploy到服务器,运行网站出现如下错误:HTTP错误 403.14-Forbidden Web 服务器被配置为不列出此目录的内容及Login on failed for “IIS APPPOOL\ASP.NET v4.0″,解决方案:定位到.NET 的安装目录C:\Windows\Microsoft.NET\Framework\v4.0.30319,启动cmd控制台,将当前目录切换到.NET的安装目录,执行ASP.NET_regiis -i,将ASP.NET重新注册到IIS即可解决此错误。

运行网站时又遇到如下错误:用户 ‘IIS APPPOOL\RwindRhine’ 登录失败。 这是登录SQL Server时遇到的错误。RwindRhine是我的网站的应用程序池,解决方案如下:开始==>inetmgr==>定位到网站对应的应用程序池,选择高级设置,如下图所示:

接下来找到标识选择,将其属性值设置为:Network Service,如下图所示:

此外,还应将Network Service设置为SQL Server的管理员账户。设置方法:启动MSSMS,安全性,登录名,右击,新建登录名,如下图所示:

在接下来的对话框中,选择搜索,高级,立即查找,定位到Network Service,添加用户,在服务器角色选项卡中为Network Service添加sysadmin角色。这样便可解决登录SQL Server错误的问题。

希望这篇文章能对在IIS7下部署ASP.NET MVC网站的朋友有所帮助。

[转载]百度地图API详解之地图坐标系统

mikel阅读(1213)

[转载]百度地图API详解之地图坐标系统 – jz1108 – 博客园.

我们都知道地球是圆的,电脑显示器是平的,要想让位于球面的形状显示在平面的显示器上就必然需要一个转换过程,这个过程就叫做投影(Projection)。在地球上我们通过经纬度来描述某个位置,而经过投影之后的地图也有自己的坐标系统,本篇文章就来详细介绍在百度地图API中涉及的各种坐标体系。

在百度地图API中,你需要了解如下坐标系:

  • 经纬度:通过经度(longitude)和纬度(latitude)描述的地球上的某个位置。
  • 平面坐标:投影之后的坐标(用x和y描述),用于在平面上标识某个位置。
  • 像素坐标:描述不同级别下地图上某点的位置。
  • 图块坐标:地图图块编号(用x和y描述)。
  • 可视区域坐标:地图可视区域的坐标系(用x和y描述)。
  • 覆盖物坐标:覆盖物相对于容器的坐标(用x和y描述)。

别被这么多的坐标系吓着,看完了后面的讲解相信你会逐渐理解它们。


经纬度

这个就不多说了,不熟悉的可以翻翻地理书。但需要注意的是即便同是经纬度坐标也可 能属于不同的坐标体系。一般GPS设备获取的经纬度属于WGS84坐标系,这是一个比较通用的坐标体系。由于某些原因国内不能直接使用WGS84坐标,因 此百度地图API的经纬度是经过加密偏移的。


平面坐标

前面说过,球面上的形状需要经过投影才能变换为平面上的形状,变换后就需要有一个平面坐标系统来描述地图上某个位置。百度地图API默认使用墨卡托投影(Mercator Projection),同样需要注意的是由于投影参数不同,同样是墨卡托投影也会有所差别。

平面坐标系的原点与经纬度的原点一致,即赤道与0度经线相交的位置:

在百度地图API中,平面坐标是以最大级别18级为基准的。就是说在18级下,平 面坐标的一个单位就代表了屏幕上的1个像素。平面坐标与地图所展示的级别没有关系,也就是说在1级和18级下,天安门位置的平面坐标都是一致的。那么如何 知道某个位置的平面坐标呢?可通过BMap.MercatorProjection类来完成,该类提供经纬度与平面坐标互相转换的方法。例如天安门的经纬 度大约为116.404, 39.915,经过转换即可得到平面坐标:

var projection = new BMap.MercatorProjection();
var point = projection.lngLatToPoint(new BMap.Point(116.404, 39.915));
alert(point.x + ", " + point.y);

var projection = new BMap.MercatorProjection();
var point = projection.lngLatToPoint(new BMap.Point(116.404, 39.915));
alert(point.x + “, ” + point.y);

结果如下:

这个就是平面坐标。你可以这样理解它的含义:第18级下,天安门距离坐标原点的位置差为:12958175, 4825923.77,单位为像素。

像素坐标

在第18级下,我们直接将平面坐标向下取整就得到了像素坐标,而在其他级别下可以通过如下公式进行换算(这里取整为向下取整):

像素坐标 = |平面坐标 × 2 zoom - 18|

比如经过计算,在第4级天安门位置的像素坐标是:790, 294


不同级别下,同一个地理位置的像素坐标是不一样的,它与当前地图的级别相关。


图块坐标

百度地图API在展示地图时是将整个地图图片切割成若干图块来显示的,当地图初始化或是地图级别、中心点位置发生变化时,地图API会根据当前像素坐标计算出视野内需要的图块坐标(也叫图块编号),从而加载对应的图块用以显示地图。

百度地图的图块坐标原点与平面坐标一致,从原点向右上方开始编号为0, 0:

如何知道某个位置的图块坐标呢?通过如下公式计算即可(这里为向下取整):

图块坐标 = |像素坐标 ÷ 256|

256实际上是每个图块的宽度和高度,我们用像素坐标除以这个数就知道图块坐标了。还以天安门为例,在第4级下天安门所在的图块编号为:3, 1,而在第18级下,图块编号为:50617, 18851


可视区域坐标

地图都是显示在确定大小的矩形框中的,这个矩形框通常是开发者在初始化地图传入的某个容器元素。这个矩形框也有自己的坐标系,在百度地图API中称之为可视区域坐标系,它的原点位于矩形的左上角。

通过Map类的pointToPixel和pixelToPoint方法可以相互转换经纬度坐标与可视区域坐标。


覆盖物坐标

覆盖物在实现上就是若干DOM元素,这些元素会被放在若干覆盖物容器内(具体请参 考地图API开发指南),那么覆盖物的坐标实际上就是相对于这些覆盖物容器的坐标。在地图初始化完成后,覆盖物容器的左上角与地图可视区域左上角位置相 同,一旦地图被移动、缩放,覆盖物容器位置就会发生变化。在自定义覆盖物的时候API提供经纬度信息,而开发者需要自行将经纬度转换为覆盖物的像素坐标, 从而覆盖物才能显示在正确的位置上。这个转换过程可以通过Map的pointToOverlayPixel和overlayPixelToPoint两个 方法来实现。

讲这么多都快晕了吧,我们最后通过一个完整的代码示例来回顾上面所提到的坐标系概念:





地图坐标概念
<script src="http://api.map.baidu.com/api?v=1.2"></script>
 <script type="text/javascript">// <!&#91;CDATA&#91;
var map = new BMap.Map('map_container', {defaultCursor: 'default'});
map.centerAndZoom(new BMap.Point(116.404, 39.915), 11);

var TILE_SIZE = 256;

map.addEventListener('click', function(e){
var info = new BMap.InfoWindow('', {width: 260});
var projection = this.getMapType().getProjection();

var lngLat = e.point;
var lngLatStr = "经纬度:" + lngLat.lng + ", " + lngLat.lat;

var worldCoordinate = projection.lngLatToPoint(lngLat);
var worldCoordStr = "
平面坐标:" + worldCoordinate.x + ", " + worldCoordinate.y;

var pixelCoordinate = new BMap.Pixel(Math.floor(worldCoordinate.x * Math.pow(2, this.getZoom() - 18)),
Math.floor(worldCoordinate.y * Math.pow(2, this.getZoom() - 18)));
var pixelCoordStr = "
像素坐标:" + pixelCoordinate.x + ", " + pixelCoordinate.y;

var tileCoordinate = new BMap.Pixel(Math.floor(pixelCoordinate.x / 256),
Math.floor(pixelCoordinate.y / 256));
var tileCoordStr = "
图块坐标:" + tileCoordinate.x + ", " + tileCoordinate.y;

var viewportCoordinate = map.pointToPixel(lngLat);
var viewportCoordStr = "
可视区域坐标:" + viewportCoordinate.x + ", " + viewportCoordinate.y;

var overlayCoordinate = map.pointToOverlayPixel(lngLat);
var overlayCoordStr = "
覆盖物坐标:" + overlayCoordinate.x + ", " + overlayCoordinate.y;

info.setContent(lngLatStr + worldCoordStr + pixelCoordStr + tileCoordStr +
viewportCoordStr + overlayCoordStr);
map.openInfoWindow(info, lngLat);
});
// &#93;&#93;></script>

效果如图:

[转载]40个高质量免费Flash网站模板下载

mikel阅读(1059)

[转载]40个高质量免费Flash网站模板下载 – 梦想天空(山边小溪) – 博客园.

Flash被大量应用于网页的矢量动画设计,是网页的重要组成部分,Flash让网站拥有更丰富的媒体内容,更流畅的用户交互。本文收集了40个高质量免费Flash网站模板,您可以免费下载使用,希望这些网站模板能帮助到您。

1. Business Corporate

flash web templates

2. Photographer’s Portfolio

flash web templates

3. The Gray Hotel Template

flash web templates

4. Smartco Business Company

flash web templates

5. The Ultimate Museum Site
flash web templates

6. The Yellow Business Site

flash web templates

7. European Restaurant

flash web templates

8. Auto Company

flash web templates

9. Your Own Band Template

flash web templates

10. Our Wedding Template

flash web templates

11. The Car Dealer Template

flash web templates

12. Business Constructions 2

flash web templates

13. Car Online

flash web templates

14. Books Company 2

flash web templates

15. The Blue Family Page

flash web templates

16. My Brown Personal Site

flash web templates

17. GlaModels

flash web templates

18. Animal Official Site

flash web templates

19. The Boy Scout Green Template

flash web templates

20. New Collection

flash web templates

21. Corporate Center

flash web templates

22. Global Digital Solutions Online

flash web templates

23. Communication Company

flash web templates

24. Global Business Ideas

flash web templates

25. Online Creative Solutions

flash web templates

26. Super Bike Club

flash web templates

27. Consulting Group

flash web templates

28. Create Your Style

flash web templates

29. Disco Club

flash web templates

30. Fashion Portfolio

flash web templates

31. S-Shape

flash web templates

32. Pure HD Touch

flash web templates

33. Chocolate Website

flash web templates

34. Design Studio

flash web templates

35. Microphone company

flash web templates

36. Movie

flash web templates

37. Elastic

flash web templates

38. Design

flash web templates

39. Studio Design

flash web templates

40. Film

flash web templates