[转载]图书站点检索界面设计分析

mikel阅读(1166)

[转载]图书站点检索界面设计分析 – 莫文的博客 – 博客园.

无论哪个系统在检索上都需要花费很大的力气设计的东西,让我们现在来看看现在国内有名的图书站点(当当卓越京东番薯)都是如何设计检索界面的。

首先来看检索条设计

检索相比导航来说,它的重要性是要低一些的(除了搜索引擎站点),因为使用检索的用户,一般目的都很明确,在系统的逗留时间相对来说也比较短而稳 定,但使用导航的用户就不一样,他们在寻找着自己喜欢的东西,只有通过导航找不着他要找的东西的时候才会使用检索系统,所以导航系统不能占页面的很多的地 方,但必须很明显,用户需要的时候,非常方便的找到。咱们来看以下站点设计:

当当:

卓越: 

京东图书:

番薯

当当和京东采用的是黄色的色调,而卓越和番薯用的是蓝色的色调。从色调来说,没什么可挑剔的,只要符合行业,符合自己的品牌文化即可,但有一点需要注意,就是无论什么色调都要内容清晰,保证用户看起来不费劲。

当当采用的是在黄色色调环境下,使用蓝色的检索框,非常的明显,用户非常容易找到,本身检索框设计很简单,就一个文本框加一个检索按钮,后边跟了一 个不是很明显的高级检索按钮以及热搜词。检索框跟检索按钮设计成了一体,很是干净且因为文本框的边框为蓝色,很是突出,无论是在头部,还是整个页面中,都 能一眼找到检索框。高级检索并不是多数人选择,所以它的重要性不能跟检索按钮比,在这种情况下,把它设计成链接,降低它的明显度很是合理。这里还有一个很 合理的地方,就是在检索框的左边放置了一个分类下拉链接,鼠标悬停的时候能看到所有分类。若用户在看分类,他能知道检索框在哪,用户在检索的时候,他也知 道分类导航就在手边,很方便切换。

卓越的检索相对当当的来说,复杂了一点,检索框是和头部一体的,左边是切换资源分类(默认为图书),检索按钮后边有两个比检索按钮更大的按钮“购物 车”、“心愿单”,相对来说,卓越网的检索不是很突出,还有一处不合理的地方就是,将高级按钮链接放到了检索条底下,脱离了检索框,按理说,一类的东西应 该放在一组里边,这样用户才能方便查找,现在这两样东西分开了,用户想高级检索的时候,在检索条上边却找不到了。和当当的检索框相比,它没有了热搜词。

京东的图书频道和当当的设计很类似,左边是分类,检索按钮是高级检索链接,但有点区别,首先检索条也是和头部是一体的,没有特意突出检索框和检索按钮,右边是购物车,但这里又卓越有差别,它的购物车放在了右边,和检索框有一定的距离,没有给检索造成视觉上的干扰。

番薯网在检索条的设计上和前边的站点差别比较大,它把检索嵌进头部中,成为头部飘着的一部分。不过仅仅是从检索条来说,和当当相比,更简洁,就一个 文本框和一个按钮。相比页面的其他内容来说,也是比较突出的,且检索条放在导航栏的上边,在导航中找不着自己想要的东西后,就可以直接使用检索条进行检 索。

2、检索提示设计:

当当:

卓越:

京东图书:

番薯

从检索提示来看,各个站点的特点都不一样,当当网很明显后边有检索词库支持,而卓越进行的是书名检索,京东出于比较尴尬的境地,没有一个建议是和书相关的,番薯干脆就不提供此功能。

3、检索结果显示界面设计

当当:

卓越:

京东图书:

番薯

从检索结果显示来看,这个四个网站差别还是蛮大的,唯一相同的一点就是这四个站点都对用户输入的关键词在检索之后显示了出来。

1、当当在检索结果页面提供了分类帅选、检索建议、资源类型切换(四个网站中只有当当提供)、快速排序切换、详细的元数据展示、单独用户操作行、关键词反显等功能。

2、卓越在检索结果页面提供分类帅选、排序方式、消费操作单独分组等功能。

3、京东图书的检索结果页面提供分类帅选、快速排序方式切换、用户操作单独一行、关键词反显、详细的元数据信息、推荐信息等功能。

4、番薯网的检索结果提供检索历史、关键词反显、用户操作单独一组等功能。

这里当当和京东做的比较好的一点是在排序方式切换上,很是方便,卓越将元数据都堆到标题中,有点不大合适。而番薯网没有提供分类帅选很是奇怪。

再看看这几个网站对于没有检索结果的界面展示:

当当:

卓越:

京东图书: 

番薯

这里,只有京东和番薯说了抱歉之外,其他两个网站不管用户感受,当当直接展示检索关键词分词之后的检索结果,卓越和京东展示了推荐信息,番薯就只提供了检索建议。

[转载]代码生成技术--CodeDom VS T4

mikel阅读(1241)

[转载]代码生成技术–CodeDom VS T4 – 破狼 – 博客园.

在微软的自家代码生成方案中我们有两种选择方式:CodeDom 和Text Template Transformation Toolkit(T4)模板。同样我们可以利用简单的String或者StringBuilder来拼接字符串,但是那对于简单的还可以,但是对于复杂的 问题就悲伤。其实在ASP.NET MVC 3.0中有多处了一个更简洁语法的模板-Razor,我们同样可以运用于我们自己的代码生成中,我随便有一篇简单的介绍Razor Templating Engine,在以后有机会了会写Razor Demo。今天的主题不在这里,所以不多说了。

一:简介:

CodeDom:这 个类库出现在我们的.NET Framework 2.0,并且被深深的用于我们的ASP.NET项目中。CodeDom关注于一个语言独立性,以至于我们可以利用我们熟悉的语言(C#,vb等)构建一个 CodeDom Model Tree,就可以生成我们在.NET平台所支持的语言代码。对于我们的ASP.NET要求语言的独立。

T4:T4模板作为VS2008的一部分出现,他以<# #> 、<#= #>接近于ASP.NET的语言在模板中插入一段段的动态代码块,可以像asp或者ASP.NET一样简单的更让人贴切,相对于CodeDom就更 简洁,但是没有了语言层次的抽象,不具有语言独立性,我们必须为同一个功能的模板在不同的语言上写不同的模板,但是在开发中往往C#模板就足够了,以及更 简单化所以得到了更多人的青睐。

二:Code Demo:

下面我们将用CodeDom和t4分别生成一个简单的Code,根据时间输出不同的问候,如下:

1:CodeDom Code:

代码

2:T4 Code:

代码

三:总结:

CodeDom的优势:

1:具有语言层次抽象,独立性:是一个单语言开发,多语言生成的方式。

2:Framework 的支持:作为我们的.NET Framework 一部分出现的,位于System.CodeDom命名空间下。不需要想T4 模板一样引用Microsoft.VisualStudio.TextTemplating.dll

T4优势:

1:更加贴切:采用的是类似于ASP、ASP.NET的语言块,是的我们的开发更贴切,采用模板方式更加简洁,快速。

2:可维护性:由于是基于文件,不像codedom编译成为dll方式,我们可以随时修改Template文件、重构。

其实我觉得只要是不要求语言独立性,多语言生成的话,就采用T4或者Razor等模板。

代码生成技术(目前完成,还在继续,好久没写了…):

1:CodeDom系列目录

2:CodeSmith模板引擎系列-目录

3:Razor Templating Engine

[转载]ASP.NET MVC3 Webgrid Ajax查询条件绑定(Search Conditions Binding)、自动编号(Serial Number)及分页(Paging)的样式定义

mikel阅读(980)

[转载][原创] MVC3 Webgrid Ajax查询条件绑定(Search Conditions Binding)、自动编号(Serial Number)及分页(Paging)的样式定义 – 大熊的空间 – 博客园.

[原创] MVC3 Webgrid Ajax查询条件绑定(Search Conditions Binding)、自动编号(Serial Number)及分页(Paging)的样式定义

本文只代表作者在一定阶段的认识与理解。

一.写作前提

在MVC3 Bate version的时候小试了一把Webgrid ,并写了篇记录文章《[原创] MVC3 Web.Helpers – WebGrid Ajax 及查询绑定》,这篇文章在对webgrid一定阶段认识的条件下完成的,本文将就一些功能再加入说明,并把自动 编号及Paging CSS的实现加以说明,同时也希望能给更多人带来方便。

二.本文内容

  1. JQuery Ajax实现Webgrid查询条件的绑定(Search Conditions Binding),并将Webgrid公用化
  2. Webgrid自动编号(serial number)的实现
  3. Webgrid paging style的定义

三.Webgrid Ajax查询条件绑定及Webgrid公用化

注:需要加载JQuery的Script的文件,具体请参考《[原创] MVC3 Web.Helpers – WebGrid Ajax 及查询绑定》。

实现查询条件和webgrid绑定其实是通过把查询条件以Routing 的形式来传递,使生成webgrid的sorting及paging的请求路径中同样包含首次生成webgrid时候的查询条件,从而使webgrid可 以保持查询条件的前提下做paging和sorting的操作,请看现面的例子。

Search Page

1 @{ 2 View.Title = "Index"; 3 Layout = "~/Views/Shared/_Layout.cshtml"; 4 } 5  <script> 6 $(function () { 7 $("#btnSearchStudent").click(function (e) { 8 e.preventDefault(); 9 var url = "?studentName=" + $("#StudentName").val() + 10 "&studentChineseName=" + $("#StudentChineseName").val(); 11 url = "@ViewData["AppPath"]/Home/SearchStudent/" + url; 12 $('#searchformDiv').load(url, function (html) 13 { $('#searchformDiv')[0].value = html; }); 14 }); 15 }); 16  </script> 17 Student Name: @Html.TextBox("StudentName") 18  <br /> 19 Student Chinese Name : @Html.TextBox("StudentChineseName") 20  <br /> 21  <input type="button" value="Search" id="btnSearchStudent" /> 22  <div id="searchformDiv"> 23  </div>

在上面的razor代码中,有两个查询条件Student Name和 Student Chinese Name,当我们点击Search Button的时,调用jQuery的Ajax Request方法,并把结果返回到指定的Div标签中。小提一下,本来也像很多一样疯狂的喜爱razor的coding方式,使得代码相当简单。下面是 Action的实现代码。

Controller

1 namespace TOM.Custom.Controllers 2 { 3 [HandleErrors] 4 public class HomeController : BaseController 5 { 6 private IStudent _s; 7 8 public HomeController() 9 { 10 this._s = PolicyFactory.Create(); 11 } 12 13 public ActionResult Index() 14 { 15 this.ViewData.Model = this._s.SearchStudent(string.Empty, string.Empty); 16 return View("SearchStudent"); 17 } 18 19 public PartialViewResult SearchStudent(string studentName, string studentChineseName) 20 { 21 IDictionary showColumns = new Dictionary(); 22 23 this.ViewData.Model = this._s.SearchStudent(studentName, studentChineseName); 24 25 showColumns.Add("Student ID", "StudentID"); 26 showColumns.Add("Student Name", "StudentName"); 27 showColumns.Add("Student Code", "StudentCode"); 28 showColumns.Add("Sex", "Sex"); 29 showColumns.Add("NRIC/Passport No.", "NRICPassport"); 30 31 this.ViewModel.ShowColumns = showColumns; 32 return PartialView("UC/WebgridTemplate", this.ViewData.Model); 33 } 34 } 35 }

Action中,根据UI传递的参数进行查询,并返回一个PartialViewResult,然后把这个PartialView返回给 UI的div。在我的例子中这个PartialView返回的就是一个经过定制化的webgrid, 在上面的Action中,我们把查询结果以Model的形式返回,并且用变量“showColumns”列出了要显示的列名称及 绑定的字段,所以我们的webgrid现在只要显示指定结果中的指定字段即可。换言之,如果其它action中也有一个查询结果,并指定绑定,那么他就可 以使用这个webgrid的PartialView。OK,我们来看看这个PartialView如何实现webgrid的公用化。

PartialView Webgrid

1 @{ 2 var grid = new WebGrid(source: Model, canPage: true, 3 canSort: true, ajaxUpdateContainerId: "DivGridd", rowsPerPage: 20); 4 5 IList webGridcolumns = new List(); 6 IDictionary showColumns = ViewData["ShowColumns"] as IDictionary; 7 8 if (showColumns.Count &gt; 0) 9 { 10 foreach (string col in showColumns.Keys) 11 { 12 webGridcolumns.Add(grid.Column(showColumns[col], col)); 13 } 14 } 15  <div id="DivGridd"> 16  <div class="content-box"> @grid.GetHtml( 17 caption: "Search Result:", 18 mode: WebGridPagerModes.All, 19 numericLinksCount: 10, 20 fillEmptyRows: true, 21 emptyRowCellValue: " ", 22 columns: webGridcolumns 23 ) 24 <div class="webgrid-totalRow"> Total Result: @grid.TotalRowCount</div> 25 </div> 26 </div> 27 }

查询结果图

下面是webgrid生成的jQuery ajax paging 代码,我们可以看到,它会把查询条件以Routing的形式进行传递,从而保证可以在查询结果中进行paging.

代码图

四.Webgrid自动编号

在传统的gridview中可以很方便的进行自动 编号,但是似乎webgrid并没有给我们直接实现这个功能,那我们只能通过变通的方法来实现了。在生成Webgrid的column时,它提供了一个 format的方法,允许我们定制、填充这个column,在本例中,就是通过page size乘page number作为编号的开始,每一行加1,这样我们就可以实现自动编号了,我们修改上面的PartialView如下所示。

PartialView Webgrid

1@{ 2 var grid = new WebGrid(source: Model, canPage: true, 3 canSort: true, ajaxUpdateContainerId: "DivGridd", rowsPerPage: 20); 4 var i = grid.RowsPerPage * grid.PageIndex; 5 6 IList webGridcolumns = new List(); 7 IDictionary showColumns = ViewData["ShowColumns"] as IDictionary; 8 9 webGridcolumns.Add(grid.Column(format: (item) =&gt; (++i).ToString(), header: "S/N")); 10 11 if (showColumns.Count &gt; 0) 12 { 13 foreach (string col in showColumns.Keys) 14 { 15 webGridcolumns.Add(grid.Column(showColumns[col], col)); 16 } 17 } 18 19@grid.GetHtml( 20<div id="DivGridd"> 21<div class="content-box"> caption: "Search Result:", 22 mode: WebGridPagerModes.All, 23 firstText: "« First", 24 previousText: "« Previous", 25 nextText: "Next »", 26 lastText: "Last »", 27 numericLinksCount: 10, 28 fillEmptyRows: true, 29 emptyRowCellValue: " ", 30 columns: webGridcolumns 31 ) 32<div class="webgrid-totalRow"> Total Result: @grid.TotalRowCount</div> 33</div> 34</div> 35}

在上面的代码中var i = grid.RowsPerPage * grid.PageIndex;每行显示行数乘页数=当前页行号开始
webGridcolumns.Add(grid.Column(format: (item) => (++i).ToString(), header: “S/N”));
在创建每一行的时候,webgrid会以++i的值作为S/N列显示的值

五.Paging Style的定义

上面的Style相信大家看了都不太喜欢吧,好吧,那我们就来修改修改。我们在partralview的GetHtml的方法中,加了下面三个属性:

tableStyle: “webgrid-table”,

headerStyle: “webgrid-header”,

footerStyle: “paging-number”,

实现如下效果:

分页图

CSS如下:

CSS

1.webgrid-table 2{ 3 width: 100%; 4 height: 100%; 5 padding: 5px 5px 5px 5px; 6} 7 8.webgrid-table caption 9{ 10 text-align: left; 11 vertical-align: middle; 12 padding: 0px 0px 0px 5px; 13 font-size: medium; 14 font-weight: bold; 15} 16 17.webgrid-header 18{ 19 text-align: left; 20 vertical-align: middle; 21} 22 23.webgrid-totalRow 24{ 25 font-weight: bold; 26} 27 28.paging-number 29{ 30 text-align: left; 31 padding: 20px 0 5px 0; 32 font-family: Verdana, Arial, Helvetica, sans-serif; 33 font-size: 10px; 34 border-width: 1px; 35} 36 37.paging-number a 38{ 39 margin: 0 5px 0 0; 40 padding: 3px 6px; 41 border: 1px solid #ddd; 42 -moz-border-radius: 5px; 43 -khtml-border-radius: 5px; 44 -webkit-border-radius: 5px; 45 border-radius: 5px; 46}

六.总结
通过本章的学习,可以了解如下内容:

1.MVC3 中Webgrid Ajax Paging的操作

2.Webgrid中属性的使用

3.Webgrid中Paging Style的设置

[转载]flex 联机游戏开发四国军棋游戏:(二)棋盘棋子

mikel阅读(1159)

[转载]flex 联机游戏开发 – 四国军棋游戏:(二)棋盘棋子 – 博弈居 – 开放的第三方应用游戏开发测试平台 – 博客园.

玩了太多年的四国游戏,现在,我打算做个四国游戏的flex版,下面的文章与代码是边做边写的,所以,当我贴出来的时候,说不定我已经将代码进行重构了,但是,如果你也是一名开发者,我想,设计思路总是对你有参考意义的。

想知道我爱四国的多深,看看引子里的那个文章就知道了。你也可以点击这儿查看这些文章的全部。中途岛之战 深圳mm.活着viva&&冷血雅雅

老规矩,先画棋盘,一般的军棋游戏棋盘都利用的图片做背景,然后判断鼠标的点击来定位棋子,现在,我决定不用图片,直接用flex绘制棋盘,源于两 个方面的原因,一是adobe是做美术出身的,所以,用flex绘制的棋盘基本上在ui上比java,C#绘制的要好看得多,同时,你可以很方便地对棋 盘,棋子使用各种滤镜效果。二是绘制出来的棋盘,本身会减小相当多的开发工作,因为你点击的如果是棋子,他本身就可以实现事件触发。将细节处理放到了元部 件中。

一,单人棋盘 Layout

public function createBoard():void
{

for (var row:int = 0; row <=xsize; row++) {
var tempArray:Array=new Array(ysize);
gameControl.boardArray[row] =tempArray;
}
//y 轴的线条
for (var i:int=0;i<=ysize;i++)
{
var line:Line=new Line();
line.xFrom=0+padding;
line.xTo=board.width-padding;
line.y=i*(board.height-2*padding)/ysize+padding;
line.stroke=DEFAULT_STROKE;
board.addElement(line);
}
//x 轴的线条
for (var j:int=0;j<=xsize;j++)
{
var line2:Line=new Line();
line2.yFrom=0+padding;
line2.yTo=board.height-padding;
line2.x=j*(board.width-2*padding)/xsize+padding;
line2.stroke=DEFAULT_STROKE;
board.addElement(line2);
}
//营里的线条
for (var x1:int=0;x1<=xsize;x1++)
{
for (var y1:int=0;y1<=ysize;y1++)
{
if ((x1==1&&y1==1)||(x1==3&&y1==3)||(x1==2&&y1==2)||(x1==1&&y1==3)||(x1==3&&y1==1))
{
//画四个方向的线
board.addElement(drawLine(x1*(board.width-2*padding)/xsize+padding,(x1+1)*(board.width-2*padding)/xsize+padding,y1*(board.height-2*padding)/ysize+padding,(y1+1)*(board.height-2*padding)/ysize+padding));
board.addElement(drawLine(x1*(board.width-2*padding)/xsize+padding,(x1+1)*(board.width-2*padding)/xsize+padding,y1*(board.height-2*padding)/ysize+padding,(y1-1)*(board.height-2*padding)/ysize+padding));
board.addElement(drawLine(x1*(board.width-2*padding)/xsize+padding,(x1-1)*(board.width-2*padding)/xsize+padding,y1*(board.height-2*padding)/ysize+padding,(y1+1)*(board.height-2*padding)/ysize+padding));
board.addElement(drawLine(x1*(board.width-2*padding)/xsize+padding,(x1-1)*(board.width-2*padding)/xsize+padding,y1*(board.height-2*padding)/ysize+padding,(y1-1)*(board.height-2*padding)/ysize+padding));
}
}
}
//默认的棋盘填充,
for (var x:int=0;x<=xsize;x++)
{
for (var y:int=0;y<=ysize;y++)
{
if ((x==1&&y==5)||(x==3&&y==5))
//画大本营
board.addElement(drawDaBenYing(x,y));
else if ((x==1&&y==1)||(x==3&&y==3)||(x==2&&y==2)||(x==1&&y==3)||(x==3&&y==1))
//画营
board.addElement(drawYing(x,y));
else
//其它
board.addElement(drawQizhi(x,y));
}
}

}

现在我对决定在每个地方加一个默认的棋子,默认是不可见的,但在棋盘的实际情况应该是这样的。在测试的时候加入一种颜色,实际情况中应该是选择alpha为0,这样,棋子是不可见的,同样,这个函数也需要传入用户的位置,这样,每个玩家就可以选择不同颜色的棋子。

//默认的棋盘填充,
for (var x:int=0;x<=xsize;x++)
{
for (var y:int=0;y<=ysize;y++)
{
if ((x==1&&y==5)||(x==3&&y==5))
//画大本营
board.addElement(drawDaBenYing(x,y));
else if ((x==1&&y==1)||(x==3&&y==3)||(x==2&&y==2)||(x==1&&y==3)||(x==3&&y==1))
//画营
board.addElement(drawYing(x,y));
else
//其它
board.addElement(drawRect(x,y));

//放一个默认的棋子
board.addElement(drawQizhi(x,y));
}
}

//默认的棋盘填充,
for (var x:int=0;x<=xsize;x++)
{
for (var y:int=0;y<=ysize;y++)
{
if ((x==1&&y==5)||(x==3&&y==5))
//画大本营
board.addElement(drawDaBenYing(x,y));
else if ((x==1&&y==1)||(x==3&&y==3)||(x==2&&y==2)||(x==1&&y==3)||(x==3&&y==1))
//画营
board.addElement(drawYing(x,y));
else
//其它
board.addElement(drawRect(x,y));

//放一个默认的棋子
board.addElement(drawQizhi(x,y));
}
}

对不同颜色的棋子,我们再加入一个默认的数组来填充不同用户的颜色,同样,我们可以设置这个数组是可变的,当你的上家说,要吃红子时,实际上红子对不同位置的玩家是完全不一样的,有一定的迷惑作用,当然,他可以说,人吃上家或下家,总归是道高一,魔高一章啊。

//玩家填充
private const TURNARRAY:Array=[new SolidColor(0xff0000,1),new SolidColor(0x00ff00,1),new SolidColor(0x0000ff,1),new SolidColor(0xffcc00,1),new SolidColor(0xffffff,0)];

哦,差点忘了,在游戏中还有另外一种状态,那就是棋子是覆盖着的,不可见的。我们也得把这个加上。

二 四人棋盘 Board

现在,每个人的棋盘已经画好,我们现在需要四个棋盘+一个中间部分来完成整个棋盘的初始化工作,flex对效果的支持现在开始发威,我们只要把这个棋盘放上四份,一份为原始,一份旋转90,一份旋转-90,一份旋转180度,就可以完成4个棋盘的工作,

<s:Group width=”660″ height=”660″ left=”20″ top=”20″>
<ns1:Layout x=”425″ y=”220″ rotation=”180″>
</ns1:Layout>
<ns1:Layout x=”220″ y=”235″ rotation=”90″>
</ns1:Layout>
<ns1:Layout x=”235″ y=”440″>
</ns1:Layout>
<ns1:Layout x=”440″ y=”425″ rotation=”-90″>
</ns1:Layout>
<ns1:CenterGroup x=”220″ y=”220″>
</ns1:CenterGroup>
</s:Group>

下面的工作就是画中间那个部分了。

protected function init(event:FlexEvent):void
{
// TODO Auto-generated method stub
// TODO Auto-generated method stub
//y 轴的线条

BOARD_IMG.source=BoardChess;
BOARD_IMG.fillMode =  BitmapFillMode.REPEAT;

for (var i:int=0;i<=2;i++)
{
var line:Line=new Line();
line.xFrom=0;
line.xTo=center.width;
line.y=i*(center.height-2*padding)/2+padding;
line.stroke=DEFAULT_TEDAO_STROKE;
center.addElement(line);
}
//x 轴的线条
for (var j:int=0;j<=2;j++)
{
var line2:Line=new Line();
line2.yFrom=0;
line2.yTo=center.height;
line2.x=j*(center.width-2*padding)/2+padding;
line2.stroke=DEFAULT_TEDAO_STROKE;
center.addElement(line2);

}

//边角的弧
center.addElement(drawArc(-padding,-padding));
center.addElement(drawArc(-padding,2*(center.height-2*padding)/2+padding));
center.addElement(drawArc(2*(center.height-2*padding)/2+padding,-padding));
center.addElement(drawArc(2*(center.height-2*padding)/2+padding,2*(center.height-2*padding)/2+padding));

//填充,
for (var x:int=0;x<=2;x++)
{
for (var y:int=0;y<=2;y++)
{

var rect:Rect=new Rect();
rect.width=15;
rect.height=15;
rect.x=x*(center.width-2*padding)/2-15/2+padding;
rect.y=y*(center.height-2*padding)/2-15/2+padding;
rect.radiusX=3;
rect.radiusY=3;
rect.stroke=DEFAULT_STROKE;
rect.fill=BOARD_IMG;
center.addElement(rect);
}
}
}

这样,所有的部分已经完成,我们现在稍微改变一下逻辑,将底纹填充放到主类,在layout类不进行填充。生成的棋局应该就是这样子的。

三,最终布局

最后,我们将用户的信息传入棋盘,并加入一些控制键,最终形成的棋盘应该如下

<s:BorderContainer id=”board” width=”880″ height=”670″  top=”20″ left=”150″ horizontalCenter=”0″>
<ns1:CenterGroup x=”320″ y=”198″>
</ns1:CenterGroup>
<ns1:Layout x=”530″ y=”219″ rotation=”180″>
</ns1:Layout>
<ns1:Layout x=”341″ y=”218″ rotation=”90″>
</ns1:Layout>
<ns1:Layout x=”340″ y=”407″>
</ns1:Layout>
<ns1:Layout x=”529″ y=”408″ rotation=”-90″>
</ns1:Layout>
<ns1:UserInfo x=”221″ y=”17″>
</ns1:UserInfo>
<ns1:UserInfo x=”550″ y=”433″>
</ns1:UserInfo>
<ns1:UserInfo x=”10″ y=”228″>
</ns1:UserInfo>
<mx:Button id=”btnlose” label=”投降” enabled=”false”   x=”669″ y=”465″ width=”70″ height=”21″/>
<mx:Button id=”btnstart” label=”开始” enabled=”true”   x=”669″ y=”435″ width=”70″ height=”21″/>
<mx:Button id=”btnsave” label=”调入布局” enabled=”true”   x=”669″ y=”525″ width=”70″ height=”21″/>
<mx:Button id=”btnopen” label=”保存布局” enabled=”true”   x=”669″ y=”495″ width=”70″ height=”21″/>
<ns1:UserInfo x=”763″ y=”228″>
</ns1:UserInfo>

</s:BorderContainer>

[转载]爆破内存中的SWF文件

mikel阅读(1083)

[转载]爆破内存中的SWF文件 – zcsor ~~~ VB业余爱好者的窝 – 博客园.

因为最近有一点需要,所以想提取打开的网页里面的SWF文件出来,其实以前也做过,用个游戏修改器,搜索一下FWS然后挨个检查一下结果,手工复制 一下内存数据。这次的却比较多,挨个弄比较麻烦还容易出错。于是写了一小段程序。只是需要注意的是,很多浏览器并不是那个有启动窗口的进程是我们要的…… 哎……不提这个,只是提一下这个实现。其实嘿嘿,前面那个从OFFICE中提取的,也可以考虑一下用这个代替?~咕~~(╯﹏╰)b~速度,速度……慢点 点而已了

1、枚举进程,列表以供选择

2、枚举所选进程内存块,搜索FWS字样……貌似叫暴力……(其实只需稍加修改源程序就可以连同CWS一起搜索)

3、筛选,依据被定为这样几个:

A、版本(第四字节)为,7,8,9,10的

B、文件大小大于0(5-8字节)

C、文件结尾为0X40,0X00,0X00,0X00的

第一部分,枚举进程其实没什么好说的,Process类就可以了。

第二部分,其实就是核心内容了:内存检索,一般来说,应该先枚举进程所用的内存块及其属性,这样可以通过属性进行筛选,但实际在实现时,我是用ReadProcessMemory函数是否出错来决定是否继续搜索本块内存的……真是懒人啊~~~~

代码

Public Structure MEMORY_BASIC_INFORMATION
Dim BaseAddress As Integer
Dim AllocationBase As Integer
Dim AllocationProtect As Integer
Dim RegionSize As Integer
Dim State As Integer
Dim Protect As Integer
Dim lType As Integer
End Structure
‘出处http://www.cnblogs.com/zcsor/
Private Declare Function VirtualQueryEx Lib kernel32 (ByVal hProcess As Int32, ByVal lpAddress As IntPtr, ByRef lpBuffer As MEMORY_BASIC_INFORMATION, ByVal dwLength As Int32) As Int32
Private Declare Function ReadProcessMemory Lib kernel32 (ByVal hProcess As Integer, ByVal lpBaseAddress As Integer, ByVal lpBuffer() As Byte, ByVal nSize As Integer, ByRef lpNumberOfBytesWritten As Integer) As Integer
Private Shared m_phandle As Integer

这样就声明了所用的API函数,接下来是枚举过程:

代码

Private Shared Function GetMemoryInfo(ByVal pHandle As Integer) As ArrayList
Dim Infs As New ArrayList
Dim pAddr As Integer, dwTotalCommit As Integer, ret As Integer, miLen As Integer
Dim mi As New MEMORY_BASIC_INFORMATION
miLen
= Len(mi)
dwTotalCommit
= 0 这是结果
pAddr = 0 这个时查询起始地址,设为0,即进程虚拟地址开始处。
ret = VirtualQueryEx(pHandle, pAddr, mi, miLen) 从起始地址开始查询
Infs.Add(mi)
Do While (ret = miLen)
dwTotalCommit
= dwTotalCommit + mi.RegionSize
pAddr
= mi.BaseAddress + mi.RegionSize 跳过已经查询过的内存块,到未被查询的内存地址起始处
ret = VirtualQueryEx(pHandle, pAddr, mi, miLen) 再次查询,直到查询失败(所有可查询地址都已经查过了)
If mi.State = &H1000 Then Infs.Add(mi)
Loop
Return Infs
End Function

很简单,相信大家一看就懂~~~~~~~~~~接下来,就是读内存数据了,这里需要考虑这样一个问题:有些内存块大的……可怜的VB溢出了咋 办……前几天绘制一个几十万像素宽的图像就……咕~~(╯﹏╰)b,其实解决办法很简单的,分块即可,这里读写内存我们就分1024字节吧~~当然有一定 原因了~~分4096也许你喜欢?O(∩_∩)O~其实编码起来都差不多,只要你知道~~~原因

出处:http://www.cnblogs.com/zcsor/

代码


Public Shared Function Scan(ByVal pHandle As Integer) As ArrayList
m_phandle
= pHandle
If Not ToKen.ToKenPrivileges() Then MsgBox(提升权限失败,扫描结果可能有遗漏)
Dim Ret As New ArrayList
Dim int As ArrayList = GetMemoryInfo(pHandle)
For Each mi As MEMORY_BASIC_INFORMATION In int
Dim bLen As Integer = mi.RegionSize
Dim rLen As Integer
Dim mBaseAddr As Integer = mi.BaseAddress
Dim mStep As Integer = 1024
Dim test(3) As Byte
Do While bLen > 0
If bLen > mStep Then rLen = mStep Else rLen = bLen
bLen
-= mStep
Dim Bytes(rLen 1) As Byte
Dim read As Integer = ReadProcessMemory(pHandle, mBaseAddr, Bytes, rLen, 0)
If read = 0 Then Exit Do
For mIndex = 0 To mStep 3 Step 4
If Bytes(mIndex) = &H46 AndAlso Bytes(mIndex + 1) = &H57 AndAlso Bytes(mIndex + 2) = &H53 Then just FWS
Dim f As New Flash
f.addr
= mIndex + mBaseAddr
f.ver
= Bytes(mIndex + 3)
f.size
= System.BitConverter.ToInt32(Bytes, 4)
If f.size > 0 AndAlso f.ver > 6 AndAlso f.ver < 11 Then
read
= ReadProcessMemory(pHandle, mBaseAddr + mIndex + f.size 4, test, 4, 0)
If read = 0 Then
Exit Do
Else
If test(0) = &H40 AndAlso test(1) = 0 AndAlso test(2) = 0 AndAlso test(3) = 0 Then Ret.Add(f)
End If
End If
End If
Next
mBaseAddr
+= mStep
Loop
Next
Return Ret
End Function

Structure Flash
Public addr As Integer
Public ver As Integer
Public size As Integer
Public Overrides Function ToString() As String
Return addr: & Hex(addr) & vbCrLf & _
ver: & ver & vbCrLf & _
size: & Format(size, #,0.) & vbCrLf
End Function
End Structure

这样,大功告成了啊~得到了可读的内存空间内的全部貌似SWF文件的信息,接下来……保存它们吧!

代码

Shared Function Save2File(ByVal Pid As Integer, ByVal addr As Integer, ByVal size As Integer) As Boolean
Dim bs(size 1) As Byte
Dim read As Integer = ReadProcessMemory(m_phandle, addr, bs, size, 0)
If read = 0 Then
Return False
Else
My.Computer.FileSystem.WriteAllBytes(
c:\ & Hex(Pid) & _ & Hex(addr) & .swf, bs, False)
End If
End Function

呃,发现我确实很懒了吧……其实也检测了一下,当读内存失败的时候不保存(也没法保存嘛~),这是防止FWS字样出现的位置后面的字节数不够SIZE(超出内存段)时的错误,换句话说,就是如果代码没写错,那这个FWS一定不是一个FLASH文件~

’出处http://www.cnblogs.com/zcsor/

最后附上,成品~呃,其实你自己写一下修改修改我的代码,才真的是成品,这个里面不识别压缩的~~其实压缩的和这个一样的~~~~如果你愿意解压,那就解压吧~几句代码的事……不过我懒

/Files/zcsor/FLASH提取工具.7z

[转载]迅雷网站设计浅析

mikel阅读(1022)

[转载]迅雷网站设计浅析 – 莫文的博客 – 博客园.

很久之前就想写篇关于迅雷网站的设计分析文章了,迅雷看看一直是我在线看电影的网站,也是我比较喜欢的网站,所以我也比较乐于分析一下它的设计,先从结构上分析,也就是导航、搜索、详细信息展示、以及播放页面的顺序。

1、导航

全局导航

迅雷首页导航是非常清晰的,一目了然,如图:

根据人的视觉路线来看,除了阿拉伯等少数语系之外,大部分人都是从左到右,从上到下,也就是图中的红色箭头所示。这也是它能做到一目了然的原因。再来细看导航:

迅雷看看Logo是可点击的链接,用于返回首页,其实,Logo能不能点击问题不是很大,不可点击,也不会给用户带来太大的困扰,但对于一些专家用户来说,是一个返回首页的捷径,增加此处设计能增加用户体验度。

可用性导航很醒目很简洁,放在了Logo的旁边,减少了用户的记忆负担,当用户想登录的时候,他能马上把鼠标移到Logo旁边。同时也给右边的广告腾出了空的地方。

主导航放的是频道列表,内容频道的划分非常合理,上来迅雷看看的用户有一些时间是比较空闲的,可能比较喜欢看电视剧,也有一些用户时间不是很富裕的,像我就没有太多时间,我的空余时间不够我看电视连续剧,只好看电影,所以我一上来就直奔电影频道。

副导航在这里就显得不大显眼了一点,副导航采用的是专题形式,专题有个好处就是能推荐很多内容,且能抓住当前大部分用户关注的话题来推荐。

说完了首页导航再来看,频道内导航:

从各个频道来看,一致的布局,一样的颜色,每个频道最顶上的全局导航颜色要比下边 的当前主频道要浅很多,让用户的视觉聚焦到频道主导航上来。每个频道的主导航旁边都有一个检索框,方便用户进行直接查找,放在右边还有一个好处,就是尽量 不影响主导航,毕竟有具体查找目标的用户相对于根分类来查找自己喜欢的内容的用户来说还是少数。

分类导航

频道主导航右上边的热门分类,可以帮用户迅速定位到当前热门上。

从上边频道截图还可以看到,频道主导航都有一个“分类”的标签,从这个小细节能看得出来,迅雷看看比较重视使用分类来导航的。以电影频道的“分类浏览”来看,点击“分类浏览”,页面如图:

选择了一个分类,之后页面如图:


到了这个页面之后,左边仍然显示分类等筛选条件,每切换一个条件,右边的检索结果则会根据选择条件显示相应的结果。整个流程很清晰,不用用户思考,让人感觉到不到导航的存在就直奔自己想看的电影去了。

关联导航

在关联导航上,迅雷看看做得非常,在这个方面,它从没有让我失望过。在首页有一处很有意思的地方:

“猜你会喜欢”,虽然迅雷看看在这个推荐信息不一定能做到百分百猜得对,但是迅雷为每个用户都不同的推荐信息,给了用户一种被重视的感觉,而且标题标签的右边还允许用户关闭,还可以设置下次不显示,减少对用户的打扰。

2、搜索

搜索有两种情况会用到,第一种用户有明确具体的搜索关键词,第二种情况是在当前导航找不到自己想要找的资源最后不得不求助于搜索。所以从这个观点来说,搜索不用放在很重要的位置,只要我想要使用它,我能立刻知道它在哪就成。

迅雷看看首页的搜索条放在了主导航的底下:

相对于页面的主导航来说,搜索条的颜色浅很多,视觉焦点在主导航上,但检索条的位置遵守了大多数网站默认设计,放在主导航和内容区之间,所以当用户想要用搜索条的时候,找起来还是很方便的。

频道首页的搜索条,放在了频道主导航的右边:

这里也遵循占用地方不大,位置很显然的原则。

3、详细信息页设计

其实在迅雷看看,详细信息页面的地位不是很受重视,很多页面上的链接是直接链接到在线播放页面去的,只有在搜索结果页面,有详细信息页面的链接:

详细信息页面,设计也是比较清晰简洁:

标题很明显,重点链接很突出,没有太多的空白,也没有拥挤。

给用户提供推荐信息:

4、在线播放页面

在线播放页面是最终的服务页面,这个页面是整个系统里边至关重要的页面,也是决定用户体验的终极页,系统其它地方设计得再好,也不能弥补这个页面的不好。迅雷看看的在线播放页面的在线播放也和详细信息页一样,很简洁:

[转载].Net 实现游戏修改器

mikel阅读(916)

[转载].Net 实现游戏修改器 – cyclone – 博客园.

前不久玩植物大战僵尸,不停地玩啊玩,也通关了,准备开始享受一下IMBA的感觉。“玩玩小游戏”模式中有关“谁笑到最后”,一来就有5000的阳光,随你布置,布置完后开始攻击,过关挺容易。但是毕竟5000的阳光可布置的植物有限,总觉得不过瘾,于是找来《金山游侠》改阳光数量。好好享受了几次imba的感觉。

不用说,我当然不甘心用别人的工具,我要自己来。我选择.NET Framework 3.5作为该程序的实现平台。

整个过程总结如下:

一.获取具有窗体的进程集合

二.在所选进程的私有地址空间内查找数据

三.跟踪所选进程的数据修改情况,获得所要修改的数据的唯一地址

四.修改该地址中的数据内容

这就好办了,思路有了,我们就根据思路来搜集和整理相关知识:

进程

进程只是个被动容器,其中包含了很多资源。

System.Diagnostics命名空间中的 Process类表示进程。Process类中的 GetProcesses() 方法可以获取系统中所有进程。判断MainWindowHandle 是否为空可以确定进程是否包含主窗体。

我当前的宿主OSXP,在32位的 Windwos NT/2000/XP 中,进程地址空间有4GB,但却只能访问其地址空间底部的2GB,另外2GB留给内核模式相关的一些东西用。在这部分可访问的2GB空间中,最低和最高的64KB不能访问,于是可访问的地址范围是:0x00010000 0x7ffeffff

我们还需要读和写进程的内存,System.Diagnostics.Process 提供的方法不能做到。还好,Windowskernel32库中的进程相关API可以帮到:ReadProcessMemory WriteProcessMemory,可以将他们表达为如下C#语句:

[DllImport(“kernel32.dll”, SetLastError = true)]

static extern bool ReadProcessMemory(

IntPtr hProcess,

IntPtr lpBaseAddress,

[Out] byte[] lpBuffer,

int dwSize,

out int lpNumberOfBytesRead

);

[DllImport(“kernel32.dll”, SetLastError = true)]

static extern bool WriteProcessMemory(

IntPtr hProcess,

IntPtr lpBaseAddress,

byte[] lpBuffer,

uint nSize,

out int lpNumberOfBytesWritten);

名字很直观:读/写进程内存。

两个方法签名基本相同,我这里简单解释一下:

hProcess :进程句柄

lpBaseAddress:基地址,也就是起始地址(起始位置)

lpBuffer:从基地址起读取或要写入的内存值

nSize:读取或写入的数量,单位是字节

lpNumberOfBytesRead lpNumberOfBytesWritten:用作传出,表示实际读取或写入的数量

好了!开始实战吧!

创建一个C#Windows forms项目

在窗体上我这样布局:

为类添加如下几个成员:

List<Process> _windowedProcesses = new List<Process>();//存放有窗体的进程集合

private List<IntPtr> _addrList = new List<IntPtr>();//存放作为结果的地址列表

bool isFirstSearch = true;//是否是第一次搜索

Process _selectedProcess;//所选进程

还要获取有窗体的进程并列出来,让使用者选择需要的进程

private void RefreshProcessList()

{

listBox1.Items.Clear();

_windowedProcesses.Clear();

textBox2.Enabled = false;//在没得到唯一的地址前不能写入

foreach (var p in System.Diagnostics.Process.GetProcesses())

{

if (p.MainWindowHandle != IntPtr.Zero)//进程有窗口

{

if (!string.IsNullOrEmpty(p.MainWindowTitle))//窗体名不为空。因为有些时候会有一些进程如iexplorer.exe ,它有窗口,但窗口没名称且没显示。所以应该排除一下

{

listBox1.Items.Add(p.MainWindowTitle);

_windowedProcesses.Add(p);

}

}

}

}

于是可以在我们的窗体装载和单击刷新按钮时调用该方法

private void Form1_Load(object sender, EventArgs e)

{

RefreshProcessList();

}

private void btnRefreshPList_Click(object sender, EventArgs e)

{

RefreshProcessList();

}

为什么要区别是否是第一次搜索?因为第一次搜索是在整个进程可访问内存范围内查找,而之后的查找是基于第一次找到的地址。这样做不是唯一的,但是最好的方法。

下面是搜索按钮单击的事件处理代码:

private void button1_Click(object sender, EventArgs e)

{

if (_selectedProcess == null) return;

if (isFirstSearch)

{

uint baseAddr = 0x00010000;

uint endAddr = 0x7ffeffff;

for (uint i = baseAddr; i < endAddr; i += (4 * 1024))

{

var addrs = CreateAddrList(new IntPtr(i), int.Parse(textBox1.Text));

if (addrs !=null )

_addrList.AddRange( addrs);

}

isFirstSearch = false;

}

else

{

RefreshAddrList(int.Parse(textBox1.Text));

}

label2.Text = 找到结果+ _addrList.Count.ToString() + ;

if (_addrList.Count == 1)

textBox2.Enabled = true;

}

很明显CreateAddrList是第一次查找掉用的方法,RefreshAddrList是之后查找调用的方法。在第一次查找中,我们以4KB作一次跳跃。为什么查找的地址范围如此本文开始已作说明,这里就不再赘述。

好了,现在来看看CreateAddrList方法:

private List<IntPtr> CreateAddrList(IntPtr baseAddr, int value)

{

int bytesRead;

byte[] buffer = new byte[4096];

bool ok;

List<IntPtr> result = new List<IntPtr>();

ok = ReadProcessMemory(_selectedProcess.Handle, baseAddr, buffer, 4096, out bytesRead);

if (!ok)

return null ;

int currentVal;

for (int i = 0; i < 4096 – 3; i++)

{

currentVal = BitConverter.ToInt32(buffer, i);

if (currentVal == value)

{

IntPtr addr = new IntPtr(baseAddr.ToInt32() + i);

result.Add(addr);

i += 3;

}

}

return result;

}

该方法用以创建地址列表。它接受2个参数,分别是基地址和要查找的值。

我们用ReadProcessMemory 一次读取4KB的值,并把它存放在buffer中。由于buffer byte[] ,所以需要用BitConverter.ToInt32()buffer中的一部分值转成Int32以和要查找的值进行比对。

如果值匹配,则把对应地址添加到该方法的 result中以供方法返回。

接下来是RefreshAddrList方法:

. private void RefreshAddrList(int value)

{

var la = _addrList.ToList();

_addrList.Clear();

byte[] buffer = new byte[4];

int bytesRead;

foreach (var i in la)

{

ReadProcessMemory(_selectedProcess.Handle, i, buffer, 4, out bytesRead);

if (BitConverter.ToInt32(buffer, 0) == value)

_addrList.Add(i);

}

}

因为要根据第一次查找的地址结果进行查找并要更新主地址列表,所以要用addrList.ToList()得到一份主地址列表的拷贝。接下来再在作为第一次搜索结果的地址表中查找新的值。如果等于之前的值的地址中的数据现在还等于新的值,那么就添加到地址列表。

回看查找按钮的事件处理代码可以发现:反复多次,直到地址列表中只有一个地址时,就可以确定这就是我们要的地址,此时,我们就可以修改它了。

private void button2_Click(object sender, EventArgs e)

{

int value;

if (!int.TryParse(textBox2.Text, out value))

{

MessageBox.Show(输入值太大!小心溢出!请重新输入!);

return;

}

var buffer=BitConverter.GetBytes(value);

int bytesWritten;

WriteProcessMemory(_selectedProcess.Handle, _addrList[0], buffer, 4,out bytesWritten);

}

哈哈!这样就完成了。按下F5我又IMBA了一回

代码下载:ProgramMemoryEditor

该程序搜索值的数据类型是Int32 ,若我们要修改的程序的某个数据是以其他数据类型存储的,则需要小修改下我们的修改器。

笔者水平有限,若有疑问或更好的建议,务必不吝赐教。

[转载]Silverlight MMORPG网页游戏开发课程第十四课:完结篇

mikel阅读(838)

[转载]Silverlight MMORPG网页游戏开发课程[一期] 第十四课:完结篇 – 深蓝色右手 – 博客园.

引言

英雄的故事将要谢幕,似乎每段传奇都该有个华丽的结局。于是我打算用全新的魔法、炫酷的特效和再一次的重构为这期教程画上句号;虽然依旧伴随着一些客观因素导致的瑕疵,然而更让我欣慰的是这款快被敲烂了的Silverlight MMORPG客户端引擎正逐步迈向成熟。或许Silverlight 5 的新特性能给这款引擎带来质的飞跃,又或者我们更加期待Web 3D 游戏时代的来临,那望眼欲穿的公元2011年春

14.1完结篇[一期] (交叉参考:HLSL自定义渲染特效之完美攻略() HLSL自定义渲染特效之完美攻略() HLSL自定义渲染特效之完美攻略() )

上一节的魔法效果是否已足够打动你的心?或许你会感叹原来Silverlight开发的RPG可以这么美的。其实更奇妙的风景还在下面,为了演绎这场华丽的结局,全新编写的4个魔法旨在换取您的惊叹,一切源于Silverlight,因此您无须复杂的代码照样可以实现绝非简单的游戏特效。

圆月斩,附带HLSL编写的空间扭曲动画效果,技能施放时游戏场景会出现短暂的3D波动效果(空间扭曲动画),这是HLSL给我们所有Silverlight游戏开发者带来的惊喜,2KB成就超绚特效。虽然目前Silverlight 4还无法利用硬件加速进行HLSL渲染,不过令人兴奋的是Moonlight近期已实现对HLSL编写的渲染进行GPU硬件加速,相信同样的效果不久将出现在Silverlight新版本中,相当期待:

落焰斩,多次伤害附加Storyboard实现的屏幕震动模仿地震效果。其中素材由两部分动画拼接而成;一部分是落下的火焰,另一部分是着地后产生的熔岩喷发。为了轻松的处理多对象间的Z轴层次顺序,可借助切分/分离某些图片/动画部件以达到更加逼真的2.5D游戏场景效果:

碧月斩,直线穿梭类型技能,伤害范围为一个多边形,游戏中的激光等魔法同样可以类似处理,另外与圆月斩类似,附带弹开效果:

地裂斩,直线波浪发散逐环伤害,附带Storyboard实现的击飞效果,如能配上很酷的素材可呈现更加壮观的战场画面。

基于不同类型魔法都拥有各异特性,编写方式也就迥然不同。作为一款能够拓展到商业级别的Silverlight RPG游戏Demo,其动画/魔法的配置文件必须在游戏初期/初始化时被动态下载/加载(同一类的所有Info.xml归到一个文件中)。另外,基于结构的考虑,我重构了魔法控件这一大块,不再有Magic类,所有的技能/魔法依旧由Animation创建,而至今所制作的7类技能/魔法均重新继承自名为MagicBase的抽象类:

using System.Collections.Generic;
using System.Windows;
using System.Reflection;

namespace Controls.Base {

#region

public class MagicArgs {

/// <summary>
/// 获取或设置代号
/// </summary>
public int Code { get; set; }

/// <summary>
/// 获取或设置出现坐标
/// </summary>
public Point Coordinate { get; set; }

/// <summary>
/// 获取或设置目标
/// </summary>
public Point Target { get; set; }

/// <summary>
/// 获取或设置Z层次
/// </summary>
public int Z { get; set; }

/// <summary>
/// 获取或设置等级
/// </summary>
public int Level { get; set; }

/// <summary>
/// 获取或设置最小伤害
/// </summary>
public int DamageMin { get; set; }

/// <summary>
/// 获取或设置最大伤害
/// </summary>
public int DamageMax { get; set; }

/// <summary>
/// 获取或设置范围半径
/// </summary>
public int Radius { get; set; }

/// <summary>
/// 获取或设置魔法类型
/// </summary>
public MagicTypes MagicType { get; set; }

/// <summary>
/// 获取或设置装饰特效
/// </summary>
public SpecialEffects SpecialEffect { get; set; }

/// <summary>
/// 获取或设置附加效果
/// </summary>
public AdditionalEffects AdditionalEffect { get; set; }

/// <summary>
/// 获取或设置数量
/// </summary>
public int Number { get; set; }
}

#endregion

#region 枚举

/// <summary>
/// 魔法类型
/// </summary>
public enum MagicTypes {
/// <summary>
/// 圆形
/// </summary>
CircleMagic = 0,
/// <summary>
/// 扇形
/// </summary>
SectorMagic = 1,
/// <summary>
/// 十字
/// </summary>
CrossMagic = 2,
/// <summary>
/// 圆发散波浪
/// </summary>
CircleWaveMagic = 3,
/// <summary>
/// 圆多段
/// </summary>
MultipleHitCircleMagic = 4,
/// <summary>
/// 矩形穿梭
/// </summary>
LinearShuttleMagic = 5,
/// <summary>
/// 矩形波浪
/// </summary>
LinearWaveMagic = 6,
}

/// <summary>
/// 特殊效果
/// </summary>
public enum SpecialEffects {
/// <summary>
///
/// </summary>
None = 1,
/// <summary>
/// 麻痹
/// </summary>
Mine = 7,
/// <summary>
/// 风切
/// </summary>
Wind = 8,
/// <summary>
/// 灼烧
/// </summary>
Fire = 9,
/// <summary>
/// 冰冻
/// </summary>
Ice = 10,
/// <summary>
/// 爆裂
/// </summary>
Explosion = 12,
/// <summary>
/// 爆裂1
/// </summary>
Explosion1 = 20,
}

/// <summary>
/// 附加效果
/// </summary>
public enum AdditionalEffects {
/// <summary>
/// 击伤
/// </summary>
Injure = 0,
/// <summary>
/// 弹开
/// </summary>
Bounce = 1,
/// <summary>
/// 击飞
/// </summary>
BeKnock = 2,
}

#endregion

/// <summary>
/// 魔法类
/// </summary>
public abstract class MagicBase {

#region 属性

/// <summary>
/// 获取或设置参数
/// </summary>
public MagicArgs Args { get; set; }

/// <summary>
/// 获取或设置目标精灵集合
/// </summary>
public List<Sprite> Targets { get; set; }

#endregion

#region 构造

public MagicBase() {
Targets
= new List<Sprite>();
}

#endregion

#region 方法

/// <summary>
/// 触发/运行
/// </summary>
/// <param name=”caster”>施法者</param>
/// <param name=”scene”>所在场景</param>
/// <param name=”args”>魔法参数</param>
public abstract void Run(Sprite caster, Scene scene, MagicArgs args);

#endregion

}
}

剩下的仅仅是当技能/魔法被释放时,通过反射来动态加载并创建对应类型的魔法实例,短短几行代码即轻松搞定任意类型技能/魔法的运行:

//开始技能/魔法攻击
void sprite_DoCasting(object sender, DoCastingEventArgs e) {
Sprite caster
= sender as Sprite;
//通过反射来加载并实例化各类型魔法
Assembly assembly = Assembly.Load(Controls,Version=1.0.0.0);
MagicBase magic
= assembly.CreateInstance(string.Format(Controls.Magic.{0},e.MagicArgs.MagicType.ToString())) as MagicBase;
magic.Run(caster, scene, e.MagicArgs);
}

到此,整个Controls控件项目库的结构已脉络清晰,关系图如下:

技能/魔法在逻辑处理上变化很大,需要对配置参数更细的划分方能适应,比如类型、子对象个数、伤害特效、爆破时特效、是否持续伤害及间隔等等,某些特殊的魔法甚至作为单独的类存在,当然好处也是相对的,游戏的趣味性与众不同,整体可玩性将大幅度提升。

快告一段落了,至今朋友们会发现第一期几乎囊括了MMORPG单机部分的所有功能除了任务系统外。没错,我一直的主张是如果一切都透彻了那就真的失去了继续探索的价值,毕竟当前Silverlight网游开发依旧处于摸索阶段,我和朋友们长期以来都在不断的专研中前行,思索着如何改进与提升。以下也列出了一些个人意见,若想在本课程示例代码上进行更深度的修正以达到较完美境界,还需要补充及完善的内容大致如下:

1、改进及优化动态障碍物系统(包括其中的障碍物碰撞检测、以及精灵AI)。

2、重新封装游戏对象的3个坐标属性以满足各种情况需求:窗口坐标系精确值,游戏坐标系精确值,游戏坐标系整值。

3、编写更多的游戏开发辅助工具以提升效率,比如游戏资源管理器等。

4PNG图片格式在未来软件应用中显得尤为重要,包括8位和24位,甚至它的动画格式MNG,因为无论是2D还是3D;无论是SilverlightFlash还是HTML5;无论是在网页上还是客户端亦或是WP7PNG格式将在未来的2D图像呈现方面长期处于统治地位。

5、更多技巧需要相互的取长补短以习得,做游戏就得从玩游戏开始一点没错。强烈推荐开发者去参考目前已运营的商业游戏,利用浏览器缓存查看它们的资源布局、配置存储、压缩加密以及动画的处理手法等等,高手均诞生于普通人之中,只是看得多了,做的多了就成了师傅,尔耳。

[一期]结束语:一 款成功的商业游戏必须具备天时、地利、人和。优秀的美工团队意味着游戏成功了一半,这绝非危言耸听;而像我们这类做程序的则常挂嘴边最多的词不外呼“性 能”二字,回归原点,性能的关键取决于细节。完美的画面加上优秀的游戏架构和引人入胜的体验将缔造成功参数中的“人和”,假以时日在那即将载入史册的场 景,旷世之巨作莅临终将显得毫无悬念。

牢记这么一句话:成功的秘诀不在别人嘴里,而在你自己手里。

Silverlight网游开发真诚期待您的加入,行动吧!那就从今天开始。

()

本课源码[14.1]

参考资料:中游在线[WOWO世界] Silverlight C# 游戏开发:游戏开发技术

友情招聘:中游在线[北京]招聘Silverlight游戏开发者,盛情期待您的加入!

具体要求:

理解面向对象的编程思想、设计模式;

熟悉使用基于C#语言基础及常用库的开发;

熟练下述任一开发环境,Visual StudioMicrosoft Expression Studio

Silverlight开发非常有兴趣;

有责任心,学习、沟通和团队合作必不可少,能适应较强的工作压力;

简历附有作品或项目介绍,提供演示程序或截图;

待遇面议,用您的潜力见证您的价值!

联系方式:http://nxria.com/contact.html

教程Demo在线演示地址http://silverfuture.cn

[转载]使用ASP.NET MVC MvcHandler设计自定义系统权限

mikel阅读(977)

[转载]使用MvcHandler设计自定义系统权限 – RyanDing – 博客园.

本文是续上篇 使用MvcHandler设计自定义系统权限<上> 的下篇。通过本篇,我将在MVC2.0中提出一个系统权限的解决方案,如存在不足的地方,希望您能够指出。谢谢!

一、回顾上篇中的内容:


重点回顾一下上篇的3.2中的MyHandler类,该类继承MVC2.0的MvcHandler类。代码如下:

01 protected override System.IAsyncResult BeginProcessRequest(System.Web.HttpContext httpContext, System.AsyncCallback callback, object state)
02 {
03 if (1 == 1) //系统权限的入口
04 {
05 //当条件满足时可以跳转到一个自定义的httphandler,比如(Error.aspx)等。
06 httpContext.Response.Redirect("http://www.cnblogs.com/ryanding");
07 return null;
08 }
09 return base.BeginProcessRequest(httpContext, callback, state);
10 }

贴出以上代码,着重想讲解的就是我们的权限判断应该注入在第11行,这个if语句内。将权限注入在第11行,个人认为这个权限是建立在MVC整个框 架外的。效率应该会更高,而且随着未来ASP.NET MVC版本的升级换代。这种权限结构在一定程度上是不受MVC框架的制约的,总比继承AuthorizeAttribute特性来控制权限高明一些吧?否 则那些“反reflection派”又开始出来用效率说事。

二、切入正题,将权限功能注入系统


先看以下代码:

01 protected override System.IAsyncResult BeginProcessRequest(System.Web.HttpContext httpContext, System.AsyncCallback callback, object state)
02 {
03
04 //使用系统System.Security.Principal保存登录后的用户名
05 //示例代码此处userName赋值不够严谨,请注意对象空引用
06 string userName = httpContext.User.Identity.Name;
07
08 if (string.Compare(userName, "admin", true) == 0)
09 {
10 //当条件满足时可以跳转到一个自定义的httphandler,比如(Error.aspx)等。
11 httpContext.Response.Redirect("http://www.cnblogs.com/ryanding");
12 return null;
13 }
14 return base.BeginProcessRequest(httpContext, callback, state);
15 }

这里我们还是使用.NET经典的System.Security.Principal类库。从httpContext.User.Identity 获取到登录的用户名符串(从Name属性获取值)。在Login这个View内保存登录成功的用户信息,Login View 这个登录页面不需要对权限进行处理,everyone 都可以打开。后台代码如下:

1 FormsAuthenticationService auth = new FormsAuthenticationService();
2 auth.SignIn("admin", false);

假设admin就是当前用户,这里的admin硬编码了,应该从UI获取。

三、具体的实现思路


在MVC2.0中如果通过Action进行判断的话,难免要考虑到Area机制。因为在不同的Area中,可能会出现相同的Controller和 Action。所以在权限判断时还需要将Area加入到逻辑中一起判断。这样就可以精确的控制所有的MVC的URL。比如我们通过 AreaName + ControllerName + ActionName 这样判断就可以对系统具体的URL进行唯一标识,同时也方便处理权限了。我们将BeginProcessRequest方法重新改造如下:

01 protected override System.IAsyncResult BeginProcessRequest(System.Web.HttpContext httpContext, System.AsyncCallback callback, object state)
02 {
03
04 //使用系统System.Security.Principal保存登录后的用户名
05 //示例代码此处userName赋值不够严谨,请注意对象空引用
06 string userName = httpContext.User.Identity.Name;
07
08 //"admin"
09 string strAreaName = base.RequestContext.RouteData.DataTokens["area"] == null ?
10 "" : base.RequestContext.RouteData.DataTokens["area"].ToString();
11
12 //"TestMvc.Areas.admin.Controllers"
13 string strNameSpaces = base.RequestContext.RouteData.DataTokens["namespaces"] == null ?
14 "" : base.RequestContext.RouteData.DataTokens["namespaces"].ToString();
15 //"index"
16 string strAction = base.RequestContext.RouteData.Values["action"].ToString();
17 //"home1"
18 string strController = base.RequestContext.RouteData.Values["controller"].ToString();
19
20 //TODO:将area + action + controller 参与到权限判断中来
21 if (string.Compare(userName, "admin", true) == 0)
22 {
23 //当条件满足时可以跳转到一个自定义的httphandler,比如(Error.aspx)等。
24 httpContext.Response.Redirect("http://www.cnblogs.com/ryanding");
25 return null;
26 }
27
28 return base.BeginProcessRequest(httpContext, callback, state);
29 }

从MVC RouteData 的 DataTokens得到area相关信息;Values得到action与controller相关信息,最后将获取的值再参与具体的权限判断。


四、注意事项


在整个调试的过程中,发现只有一个地方让我调试了半天:MyHandler不能控制area的内部的Action。后来查阅相关资料,原来是在AreaRegistration(MVC2.0新增Area后自动生成的类)中设置问题。贴出代码,供参考:

1 public override void RegisterArea(AreaRegistrationContext context)
2 {
3 context.Routes.Add(new Route("{area}/{controller}/{action}/{id}"
4 , new RouteValueDictionary(new { area = "admin", controller = "Home1", action = "Index", id = "" })
5 , new RouteValueDictionary(new { area = "admin" })
6 , new RouteValueDictionary(new {area = "admin",  Namespaces = "TestMvc.Areas.admin.Controllers" })
7 , new HelloWorldHandler()));
8 }

新增的area结构如下:

至此,您可以在MVC2.0中加入权限机制了。我相信通过这种方式注入自定义的权限结构到MVC程序中,总比用HttpModule来的强一些,因为客户端请求CSS/JS时,不需要再过滤这类的URL。代码看起会更舒服点,判断也就更简单点。

希望本篇文章可以给您带来帮助,如有不足之处欢迎指出,谢谢!

[转载]乐易拍电子商务系统(asp.net+ extjs)

mikel阅读(1184)

[转载]乐易拍电子商务系统(asp.net+ extjs) – huhai – 博客园.

乐易拍信息平台主要为企业实现电子商务提供一个快捷的解决方案.通过本方案系统,可以加快开发速度,降低开发成本.搭建高效、稳定、可拓展的系统。
本系统运用ASP.NET 2.0开发,数据库使用SQL2000(可以放在SQL2005上),包括前台和后台两个部分。
前台主要运用JQuery+CSS 构建的展示订购平台。
后台是运用CSS+EXTJS构建的一个完整的权限管理+销售订单管理的系统。主要功能:
地区信息设置 、用户信息管理 、系统角色管理 、系统仓库配置 、物流公司配置 、数据库备份 、
系统操作日志、 品牌管理 、类型管理 、商品添加 、商品管理 、销售客户管理 、销售订单管理 。

(给初学者叁考)项目下载 http://code.google.com/p/leyipai/
后台默认帐户密码: huhai/111111

效果图