说起MySpace,可能很多人对他印象很深,MySpace.com成立于2003年9月,是目前全球最大的社交网站。它为全球用户提供了一个集交友、个人信息分享、即时通讯等多种功能于一体的互动平台,同时他也是.NET应用最出色的网站之一。下面我们一起来回顾一下MySpace架构的改革之路,或许我们能从中得到一点点架构方面的经验和教训。

 

1、50万用户

 

最早myspace网站由一台数据库服务器和两台Web服务器构成,此后一段时间又加了几台web服务器。但在2004年早期,用户增加到50万时一台数据库服务器就显得力不从心了。

 

他们设计了第一代架构,在此架构中他们运行3个SQL Server2000服务器,一个为主,所有的新数据都提交给他,然后再复制给其他两个数据库服务器。另外两台服务器用来给用户提供信息浏览,也就是只做数据读取。在一段时间内效果不错,只需要增加数据库服务器,扩大硬盘,就可以应对用户数和访问量的增加了。

 

2、100万-200万用户

 

当达到这个数字时,myspace数据库服务器遇到了I/O瓶颈,即他们存取数据的速度跟不上了。而这时据他们第一个架构只要5个月。有人花5分钟都无法完成留言,很多用户认为myspace完蛋了。

 

这个时候新的架构被快速提出来了,这一次他们把数据库架构按照分割模式设计,以网站功能分出多种,如登陆、现实用户资料、博客信息、等分门别类存储在不同的数据库服务器里。这种垂直分割策略利于多个数据库分担访问压力(天涯曾经就这么做过)。后来myspace从存储设备与数据库服务器直接交互的方式SAN(用高带宽和专门设计的网络将大量磁盘存储设备链接在一起,而数据库链接到SAN)。

 

3、300万用户

 

到300万用户时,这种架构开始也不行了,因为每个数据库都必须有每个用户表副本,意识是一个用户注册后,他的信息会分别存在每个数据库中,但这种做法有可能某台数据库服务器挂掉了,用户使用一些服务可能会有问题。另一个问题是比如博客信息增长太快,专门为他服务的数据库的压力过大,而其他一些功能很少被使用又在闲置。这就好像有人忙的要死,有人闲的要死。

 

于是他们购买了更好更贵的服务器来解决管理更大数据库的问题。但专家预测他们即使昂贵专业的服务器到最后也会不堪重负,他们必须调整架构而不是掏钱买更好的服务器。于是他们的第三代架构出现了。分布式计算架构,他们分布众多服务器,但从逻辑上看成是一台服务器。拿数据库来说,不能再按功能拆分了,看成只有一个数据库服务器。数据库模型中维护一个用户表、博客信息表、等等同看作在一个数据库服务器中。

 

然后他们开始把用户按每百万一组分割,每一组的用户访问指定的数据库服务器。另外一个特殊服务器保存所有用户的帐号和密码。他们的设计师说如果按照这种模式以更小粒度划分架构是可以进一步优化负荷负担的(50万用户为一组 或者更少)。

 

4、900万-1700万用户

 

myspace在这个时候把网站代码全部改为.net语言,事实证明网站跑的比以前快了很多、执行用户的请求消耗非常少的资源,后来他们把所有的程序都改成.net了。但问题到1000万时还是出来了。

 

用户注册量太快,按每100万分割数据库的策略不是那么完美,比如他们的第7台数据库服务器上线仅仅7天就被塞满了。主要原因是佛罗里达一个乐队的歌迷疯狂注册。而且某台数据库服务器可以在任何原因 任何时候遭遇特别大的负荷。他们的解决办法是人工把崩溃的数据库里的用户迁移走。但这不是一个好办法。

 

这个时候myspace购买了3PAdata设备,他的牛逼之处是真正把所有的数据库看成一个整体。他会根据情况把负荷平均分配出去,比如当用户提交一个信息,他会看哪个数据区域空闲然后分配给他,然后会在其他多处地方留有副本,不会出现一台数据库服务器崩溃,而这台数据库里的信息没有办法读取的情况,这样做看起来好极了。

 

另外他们增加了缓存层,以前用户查询一个信息,就请求一次数据库,现在当一个用户请求数据库后,缓存层就会保留下来一个副本,当其他用户再访问时就不需要再请求数据库了,直接请求缓存就够了。

 

5、2600万用户

 

他们把服务器更换到运行64位的服务器,这样服务器上可最多挂上32G内存,这无疑有提升了网站性能,用户感觉这个网站开始稳定快起来了。但一个新问题意外出现了。他们放数据库服务中心的洛杉矶全市停电了。这导致整个系统停止运行长达12个小时。

 

这时他们实现了在地理上分布多个数据中心以防止洛杉矶事件再次出现,在几个重要城市的数据中心的部署可以防止某一处出现故障,整个系统照样提供服务,如果几个地方都出现故障,那么这就意味着国家出现了重大灾难,这种几率是非常低的。

 

6、总结

 

这个架构变化升级相当有意思,架构随着用户量的提升作仓促的变化,但又恰到好处,看来MySpace又验证了一句古话“有压力才会有动力”。同时他给我们后人的启示是要尽早发现系统的瓶颈,设计师在设计时要有前瞻思想,否则今后有可能也要这样仓促的升级你的产品。

 

SGM 2 weeks

April 21, 2010

到SGM两个礼拜了。很多感慨。却不知道如何说起。

对于IT的规模,虽曾预计过,但不可否认,仍超出我的想象。我也算终于知道张某人口中的一排监控屏是什么样子的了。

可是高度的自动化带来的IT上的风险转移。

看着这张架构图不由心里发汗,高可靠性,在这里比什么都重要。

工作上很多事情要做。。很多东西要记。。

对于记忆,我自认为从小就与众不同。

或许是形象思维过强吧,写了这么多年的code也始终没记住过api,准确的说,是我压根就没去记过。当然,代码提示真是个好东西。

记得有个技术大牛跟我说过,脑子是用来想东西的。

工作上记得东西太多,最近别指望会费大量心思去coding了

coding始终是非常有趣的事情。

在未来的一段时间内恐怕coding都和这两个项目分不开,一是一个基于browser的web os,二是一个rpg游戏。

对于前者,这已经不是我第一次做这个题材了。如果没记错是第三次,回顾过往的每次,都陪伴了我技术上的飞跃。虽然可能也伴随了某些不好的回忆。

记得第一次应该是高一高二,纯js,当时技术怎一个烂字了得。第二次大三,ajax+asp.net,压根就不懂操作系统设计和oo,也难怪被这帮老头老太ooxx。

这次决定用sl做ui,以此增加了不少ui上的可靠性。也打算像真正operating system一样分离用户态和管态,这块的技术难点我也有所眉目了。只是前阵子和以前一样,在ui上花费了我太多的时间。

或许我注定是个色弱吧。

不过说归说,这块已经么啥大问题了。也完成了cs code和xmal描述的物理分离,代码的耦合已经降低的新的境界了。

当然这只是表层。

未完待续。。。

关于后者的游戏,前阵子还有朋友问我写的啥样了。其实早在wonders的时代已经开始计划,也确实写了不少代码。

倒不是真因为dr lee的梦魇打算囧他一囧。只是游戏一直在玩都腻了,换换口味罢了,其实rpg游戏说简单不简单说难确实不难。

当初尝试性的写了一部分游戏框架,只是偶尔间看到Farseer Physics,不由有些发毛,太烂了推倒重来吧。

咳。。仍是未完待续。。要做的事情太多了。。

不知道写了些啥。。。关机睡觉。

众所周知,Twitter和中国无缘。通常我们上twitter有2个手段,要么vpn,要么通过国内某些网站同步。

最近把自己的空间 用BlogEngine重新整了一遍,看到其自带的一个用于同步Twitter的widget颇为不爽,原因很简单,我的主机在国内,根本连不上。

在国内么,Follow5 是一个不错的微博,其本身就能用于同步Twitter消息。

于是便开发了这么一个widget。

 

效果见http://www.dumuzi.cn/

右图为管理员登录 

 

 值得注意的是,由于follow5访问api需要api—key,我提供的code中使用了一些手段避免了明文显示之。

当然,dotnet写的东西是可以反编译的,这我就么啥办法了。

各位如果需要其他用处,请直接去这里 申请。

 

源代码下载

 

 

接着上回.

Dynamic也是Dotnet 4.0的一个重要的feature.

通常的动态语言,如JavaScript, Python, Ruby 都能做到动态.即其类型在编译时能不检查,而等到运行时方知.

当然,动态化通常受到了IDE,效率等影响.

.net 4.0引入 Dynamic Language Runtime (DLR),为其带来了动态语言的一些feature.

下面我们进行简单的研究.

类型动态 

            dynamic str = "string";
            Console.WriteLine(str.GetType().ToString());

 

 以上例子非常简单,输出了一个动态对象其真实的类型.其输出是System.String

或许你会认为其和var关键字起的效果是一样的.其实不然.

最明显的,你会发现,dynamic做了IDE屏蔽,其作为动态类型在编译时,是不推测其类型的. 

就以上文例子,其 GetType()方法在IDE(Vs2010)里被解释成了dynamic expression,其只有到了运行时方能被resolve

相反,var关键字是一个典型的语法糖,其在编译时已经被正确解析成自己真实的类型.

关于这点,有兴趣的朋友可以通过Reflector看看.

 

因为这个特点,我们反射时就不必再像以前那样了。

//以前的方法
Type type=...;
type.GetMethod(
"method name",...).Invoke(data,...);
//现在的方法
dynamic obj=Activator.CreateInstance(...);
obj.XXmethod()

 

Dynamic的几个实现

.net 4.0提供了几个dynamic类型或接口,如DynamicObject,ExpandoObject,IDynamicMetaObjectProvider

其都是在System.Dynamic命名空间下。

 ExpandoObject

 ExpandoObject提供了一个具有可扩充的动态对象。这里的可扩充,包括对其不同节点上的属性或是方法的扩充。

最简单的例子,通过这个对象,我们便可以实现类似json里的某些功能。

 //JS code

var test={
   name:
"test",
   func:
function(){
       alert();
   }
};

 

类似的Csharp代码如下

dynamic test = new ExpandoObject();
test.Name 
= "test";
test.Func 
= new Action(() => Console.WriteLine("alert "+test.Name));
Console.WriteLine(test.Name);
test.Func();

通过以上特点,我们在处理多节点对象(如XML)就非常方便了。

 

DynamicObject 

通常我们自定一个类去继承 DynamicObject,而当我们对某个dynamic 对象使用XX方法或属性时,其TryGetMember方法都能被拦截到。

所访问的方法或属性名是在 public virtual bool TryGetMember(GetMemberBinder binder, out object result);的GetMemberBinder.Name中得以体现。

举个例子吧

代码
    class Program
    {
        
static void Main(string[] args)
        {
            dynamic test 
= new TestDynamicObject();
            Console.WriteLine(test.Test);
            Console.ReadKey();
        }
    }

    
class TestDynamicObject : DynamicObject
    {
        
public TestDynamicObject( )
        {
           
        }
        
public override bool TryGetMember(GetMemberBinder binder,out object result)
        {
            
string nodeName = binder.Name;
            Console.WriteLine(binder.Name
+" run");
            result 
= DateTime.Now;
            
return true;
        }
    }

 

 以上代码输出类似如下:

Test run

2010-02-25 15:15:42

 

总结 

当然,DLR并非如上介绍的这么简单,其也支持了某些动态语言的binder,如IronPython等。在PDC09中,就有相关的video。 

本文也只是简单了介绍了DLR的一些皮毛罢了。 

 To Be Continue....

 

.net 4 和对应的vs2010已经出来一阵子了.从最早的beta1到如今的RC,已愈发向正式版靠拢.

最近闲来无事,又似乎有些假日综合症之嫌,于是静下心来对dotnet 4.0从头到底好好研究一下. 

首先我们来研究MEF 。

 所谓MEF (Managed Extensibility Framework)

 

通常,由于应用需求的变化频繁,导致应用软件可扩展性越做越差,很难加入新的功能.在NET4.0中,加入了MEF(托管可扩展框架)就为了解决了简化可扩展应用程序和组件的设计这个问题.

由于MEF已经包含在.net 4.0之中,我们不需要额外下载其类库,当然,如果对其实现原理感兴趣,可以在http://www.codeplex.com/MEF看到其源代码.

 

如何创建一个MEF Application

通常,创建一个MEF Application需要实现以下几个步骤.

  1. 使用[Import]属性标记可扩展的地方,并将其已接口形式表达
  2.  创建进行扩展的组件,实现(1)的接口的同时使用[Export]标记

 

我们接着用一个简单的例子来做以上2步。

首先,我们申明了一个可用于扩展的属性Message,并实现了其调用时的方法Do().

注意,Import属性是在System.ComponentModel.Composition下,需要在项目中引入System.ComponentModel 

    class MEFTest
    {
        [Import]
        
public string Message { getset; }
        
public void Compose()
        {
            CompositionContainer container 
= new CompositionContainer();
            CompositionBatch batch 
= new CompositionBatch();
            batch.AddPart(
new Extension1());
            batch.AddPart(
this);
            container.Compose(batch);
        }
        
public void Do()
        {
            Console.WriteLine(Message);
            Console.ReadKey();
        }
    }

 

 接着,我们要具体实现一个被扩展的对象。

 class Extension1
    {
        [Export]
        
public string Message
        {
            
get
            {
                
return "I am extension 1";
            }
        }
    }

  这样,在我们需要正式初始化MEFTest实例并调用其Do方法之前,自然需要一个方法将可扩展的Message对象“绑定”到Message这个接口之上。而这就是 Compose()方法的作用。在其之中,我们创建了一个CompositionContainer容器用于绑定该属性。

请注意,任何Export标记过的都必须有对应的Import在容器中,反之却可以通过 Compose()。

 关于实际应用

 通常,我们在实际应用中没有上文来的这么简单。MEF支持通过程序集或目录(DirectoryCatalog)或2者结合等方式绑定可扩展的对象。

 CompositionContainer  container = new CompositionContainer(

new DirectoryCatalog(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)));

 

 如果在新建CompositionContainer容器时,使用了DirectoryCatalog,则在该目标路径下的所有程序集都会被遍历查询,用于寻找实现Import接口的具体class。

 当然,请在标记Export时使用[Export(typeof(接口))]来精确定位。

 另外,通常使用时,我们需要使用一些特定的元数据(metadata)来进一步描述或说明某些特定接口(如果安全性,速度等)。我们可以使用PartMetadata标记class,使用ExportMetadata标记实现方法。 

 

总结

 本文主要介绍了何为MEF,以及MEF的简单应用。望广大网友指正。

 To Be Continue....

 

Code7 Contest 的杯具

February 2, 2010

咳....又想起了那个破悲剧Code7 Contest比赛...

杯具有三:

  • 棋差一招 得了个Run-Up...与PDC09失之交臂
  •  奖品还么寄到杯具先到了
  • 杯具长得很杯具
直接上PP吧...杯具一个....

 

 

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

| 戴佳顺 | msn:edwin19861218@hotmail.com | QQ:1961218 | Web:http://www.dumuzi.cn |

Tag标签: Code7,杯具
0
0
 
(请您对文章做出评价)
 
« 上一篇:XNA入门教程----(三) 简单的动

继续我们的XNA之旅...

上周,我们简单的介绍了XNA的相关控制器,并且实现了鼠标和键盘对屏幕内的某个Sprite(精灵)进行简单的位置控制.

但是,美中不足的,我们并没有实现Sprite人物的移动动作,即Animation.

现在,我们继续上周所讲的, 实现简单的动画.

 

关于Animation 

 对动画有所了解的朋友对于逐帧动画一定不会陌生.其以最简单的形式,将各动画元件相邻单位时间的动作位置记录在每个帧里.这样,当我们

一帧帧播放时,由于视觉暂留现象,我们便能看到相对"运动"的动画了.我们通常看到的GIF动画,就是基于这个原理的.

 回来说们的游戏,由于在一般游戏内Sprite的"动作"都是有限可枚举得,我们便同样可以用类似的方案来记录Sprite的所有动作. 

 

我们现在尝试用上面的GIF来实现我们的移动动画.

 

由于原生的XNA不支持直接从GIF动画中读取各帧图象,即不能直接从Content中Load到GIf动画或其相关帧图像 

虽然从技术上而言,由于有很多开源项目支持如何读取或生成GIF(有兴趣的朋友可以看看这里),我们完全可以通过之自己加载Gif动画的某帧来实现游戏动画。

但这里,我们老老实实的将各帧拆开,得到以下的图像。

 

这样,我们只要通过加载不同的同一图像的不同位置,便可实现人物的”运动“了 。

 

加载动画 

我们回到这个系列的第一篇 

在sprite的Draw时候,我们提供以下重载参数

 

 其中我们就可以通过设置sourceRectangle的值来显示texture2D的一部分了。

 

经过计算,我们得知每个单独人物的尺寸为60X110

这样,我们便用以下方法来动态显示不同“帧”的人物动画了

代码
        protected override void Update(GameTime gameTime)
        {
            KeyboardState state 
= Keyboard.GetState();
            
if (state.IsKeyDown(Keys.Up))
            {
                
this.Position.Y -= 10;
            }
            
if (state.IsKeyDown(Keys.Down))
            {
                
this.Position.Y +=10;
            }
            
if (state.IsKeyDown(Keys.Left))
            {
                
this.Position.X -= 10;
            }
            
if (state.IsKeyDown(Keys.Right))
            {
                
this.Position.X += 10;
            }
            MouseState state1 
= Mouse.GetState();
            
if (state1.LeftButton == ButtonState.Pressed)
            {
                IPathFinder pathFinder 
= new PathFinderFast(m_matrix);
                pathFinder.Formula 
= HeuristicFormula.Manhattan;
                pathFinder.SearchLimit 
= 2000;
                m_path 
= pathFinder.FindPath(new Vector2((int)this.Position.X, (int)this.Position.Y), new Vector2(state1.X , state1.Y ));
            }
            
if (this.m_path != null && this.m_path.Count() != 0)
            {
                
int index = this.m_path.Count() - 1;
                
this.Position = new Vector2(this.m_path[index].X, this.m_path[index].Y);
                
this.m_path.RemoveAt(index);
                
return;
            }
            m_frameIndex = m_frameIndex + 1 > 5 ? 0 : m_frameIndex + 1;
            
base.Update(gameTime);
        }
        
protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);
            spriteBatch.Begin();
          
  this.spriteBatch.Draw(this.img, new Vector2(this.Position.X, this.Position.Y), new Rectangle(m_frameIndex * m_frameSize.X, 0, m_frameSize.X, m_frameSize.Y), Color.White, 0, Vector2.Zero, 1f, SpriteEffects.None, 0);
            spriteBatch.End();
            
base.Draw(gameTime);
        }

 其中,m_frameSize 便是我们每个单独动作的尺寸   Point m_frameSize = new Point(60, 110);

编译后,我们 便能看到“动”的人物了。

 

调节动画的“速度” 

我们很快发现,用这个方法显示的逐帧动画很暴走。即动画帧变化速度太快了。

这其实是由于我们的动画变帧逻辑在Update方法中,而通常,update的调用是基于游戏内置循环的,即根据XNA的默认刷新率---60fps(Frame/Second)。 

如果直接用默认速度的帧循环速度,一则CPU使用率太高,二则也没有这个必要。这里,我们决心“降低”帧数。

我们知道,在XNA中,我们可以通过gameTime.ElapsedGameTime得到自从上次循环结束后到现在的时间。

因此,我们可以将我们的Update方法改成如下结构

代码
  protected override void Update(GameTime gameTime)
        {
            timeSinceLastFrame 
+= gameTime.ElapsedGameTime.Milliseconds;
            
if (timeSinceLastFrame > millisecondsPerFrame)
            {
                timeSinceLastFrame 
-= millisecondsPerFrame;
                
//TODO
            }
        }

 

 其中,int millisecondsPerFrame = 50;就是我们设定的阀值。

经过以上修改,我们就会发现帧速明显下降,当然,CPU使用率也下去了。 

 

一些细节 

或许大家发现,人物不动时还在原地跑步,只需我们加上类似下文逻辑即可。 

 

代码
 if (isActive)
                {
                    m_frameIndex 
= m_frameIndex + 1 > 5 ? 0 : m_frameIndex + 1;
                }
                
else
                {
                    m_frameIndex 
= 0;
                }

 

总结 

本文主要简单介绍了逐帧动画及其在XNA中的实现。并且引入了帧速的控制。相信各位看完本文,便会对XNA的简单2D动画有了一个全面的了解。 

本例源代码

To Be Continue....

 

本系列列表 

 

  1. XNA入门教程----(一) 

  2. XNA入门教程----(二) 移动控制

  3. XNA入门教程----(三) 简单的动画

 

上一章,我们主要介绍了

何谓XNA

并且做了第一个XNA HelloWorld

本文,将紧接着上文. 继续我们的XNA之旅.

 

上篇的最后,我们通过

 

MouseState ms=Mouse.GetState();//获得鼠标相关信息 

 

获得了鼠标的Point位置,让自己的图像显示在鼠标的位置,因此做到了自定义光标的功能.

现在,我们对XNA的控制器作全面阐述

 

XNA的控制器

在XNA的控制器,主要定义在 Microsoft.Xna.Framework.Input下,

分为以下几种

 

  1.  Mouse 鼠标
  2. Keyboard 键盘
  3. GamePad 手柄

 

 在默认模板建立的HelloWorld中,我们会发现

 if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                
this.Exit();

 

 这其实就是判断如果按下了游戏手柄的Back按钮时,自动退出。

  同样,我们可以通过以上Input的相关state来获得相关输入端的操作,如按钮,位置等等。

 

 一个简单的例子

 

现在,我们尝试在界面中绘制一个简单的 sprite(精灵),并实现鼠标和键盘的同时控制

 

代码
 protected override void Update(GameTime gameTime)
        {
            KeyboardState state 
= Keyboard.GetState();
            
if (state.IsKeyDown(Keys.Up))
            {
                
this.Position.Y -= 10;
            }
            
if (state.IsKeyDown(Keys.Down))
            {
                
this.Position.Y +=10;

            }
            
if (state.IsKeyDown(Keys.Left))
            {
                
this.Position.X -= 10;

            }
            
if (state.IsKeyDown(Keys.Right))
            {
                
this.Position.X += 10;

            }
            
base.Update(gameTime);
        }

 

 

通过以上代码,我们实现了对键盘的简单监听。 

这样,当我们按下键盘上下左右后,我们的sprite就随之移动了。

代码
  protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);
            spriteBatch.Begin();
            spriteBatch.Draw(img, new Rectangle((int)Position.X,(int) Position.Y, 5080), Color.White);
            spriteBatch.End();
            
base.Draw(gameTime);
        }

 

如何操作鼠标呢?

 根据上面相似的逻辑,我们当然能得到鼠标的当前位置,但是,如果这样做,我们的sprite会直接从原先点瞬间移动到目标点,而没有中间的过渡过程。

 在正常的游戏中,以上逻辑往往比较复杂,中间充满着障碍物,怪物等。因此不可简单的通过point操作。

为了未来的可扩展性,这里我们最终使用a-star算法来处理这个问题。

至于何谓a-star,请关注这里 

这里,我们使用了Franco, Gustavo 写的a-star类库,来算出最短路径。

当然,我们这里的构建的障碍的空的。未来在真实的地图中,我们需要构建出相关的障碍。

首先,我们构建一个空的无障碍的点集用作描绘障碍

代码
 private byte[,] m_matrix;
  m_matrix 
= new byte[1024,1024];
            
// FIXME: in matrix ,the obstacle is 0 else is 1
            for (int y = 0; y < m_matrix.GetUpperBound(1); y++)
            {
                
for (int x = 0; x < m_matrix.GetUpperBound(0); x++)
                {
                    m_matrix[x, y] 
= 1;
                }
            }

 

 这里,如果有障碍则为0否则为1

代码
//在update中加入鼠标位置的最短路径计算
  MouseState state1 = Mouse.GetState();
            
if (state1.LeftButton == ButtonState.Pressed)
            {
                IPathFinder pathFinder 
= new PathFinderFast(m_matrix);
                pathFinder.Formula 
= HeuristicFormula.Manhattan;
                pathFinder.SearchLimit 
= 2000;
                m_path 
= pathFinder.FindPath(new Vector2((int)this.Position.X, (int)this.Position.Y), new Vector2(state1.X , state1.Y ));
            }
            
if (this.m_path != null && this.m_path.Count() != 0)
            {
                
int index = this.m_path.Count() - 1;
                
this.Position = new Vector2(this.m_path[index].X, this.m_path[index].Y);
                
this.m_path.RemoveAt(index);
                
return;
            }

 

以上代码,我们先获得了当前鼠标的位置,并计算出当前点到目标点的最短路径。

之后将其存储,这样,如果没有动作我们每次循环便可自动继续进行相关的自动移动了。

 

通过以上代码,我们实现了用鼠标或键盘的简单控制。

 

但是 

或许大家发现了,这里的sprite是单帧的移动并非动画,如何让人物运动起来?请关注下回分解。

 源文件下载

 To Be Continue.... 

 

XNA入门教程----(一)

January 15, 2010

 

写在前面:

前阵子看到cnblogs上的牛人用silverlight/WPF写游戏,不由心血来潮也尝试写个。

众所周知,虽说从silverlight2开始ms就提供了Isolated Storage的支持,但是,对于游戏这种大玩意儿,1M空间明显是不够的。于是乎,在silverlight中做游戏,别的不说,其资源的调度就严重依赖于服务器及网络了。

其次,WPF,由于其渲染本身基于directX,比起win32年代的winform更适合做form内的变换和刷新,但终究不非游戏框架,其本质是缺乏对界面消息循环的源生支持。虽说这对于windows app是简化了不少,但对游戏恐怕还不行。

偶然之间,发现ms的XNA,一款为dotnet爱好者准备的游戏开发框架。

 

何谓XNA:

 

XNA Framework 是建置于 .NET Framework 2.0上,另外还加入了一些专注于游戏开发上类别库,在指定的平台上使程式码重用达之最大效果。XNA Framework 在一个特别为有管理式游戏运行的通用语言执行层(Common Language Runtime)版本上运行。这个执行层支援Windows XP、Windows Vista和XBox 360。由于游戏是开发在执行层上,使得游戏能在所有支持XNA Framework 的平台上只要很少甚至不需要任何更改便能运行。现时所有在XNA Framework 上的游戏必需使用C #和XNA Game Studio Express IDE开发。

XNA Framework 把所有用作游戏编程的低阶技术包起来,由此,游戏开发员就可以专注于游戏内容开发而不用关心游戏移植至不同平台上的问题,游戏只要开发于XNA 的平台上,所有硬件只要支援XNA都能运行。XNA Framework 还内置一些工具,例如XACT以帮助游戏内容开发。这些工具还能帮助开发视觉和听觉效果和和像真度很高的模型制作。

XNA Framework 同时支持2D 和3D 的游戏开发也支援XBox 360 的控制器和震动效果。Xbox Live卖场可以升级开发者的XNA Game Studio Express 以使能把他们开发的游戏用于Xbox 360上。

由于XNA基于.NET 2.0,其开发效率比C++直接调用DirectX API 快了不少,对于我等业余的游戏开发者,是件好事。

 

 

XNA相关:

现在XNA最高版本已升级至3.1,并且加入了对Video的支持,并且需要在VS2008或其Express版本中安装。(至少理论上是,本人本地VS2008+2010+Win7屡次安装遭自动回滚,请教老外结果仍不理想,最终只能硬着头皮再安装个Express用于XNA)

其次,国内现在XNA的相关资源非常之少,严重依赖于相关英文参考。建议大家去看 Learning XNA 3.0-XNA 3.0 Game Development for the PC, Xbox 360, and Zune;XNA 3.0 Game Programming Recipes-A Problem-Solution Approach 和 Beginning XNA 3.0 Game Programming-From Novice to Professional相关的书,我看下来还是非常不错的。

貌似国内不少人正翻译或已经部分翻译此类文。

下载XNA

 

第一个XNA程序

 安装完XNA Framework后,在VS中即出现XNA的相关项目Template. 我们随便创建一个名曰 WindowsGame1的XNA项目。

编译后,出现蓝天白云一片
 

 现在,我们开始尝试在蓝天白云中,增加一些我们自己的元素。

插一句 

习惯于windows编程的朋友往往比较习惯对事件的监听或触发。其实,windows本身是基于消息队列的,事件就是不断监听消息并做出相应的处理。 不熟悉的朋友可以在dotnet winForm中重载WndProc即可知晓。

 在每个游戏中,都存在消息循环,即不断计算和控制界面不断刷新的循环。XNA也不例外,其Microsoft.Xna.Framework.Game以及我们以后说到的GameComponent都存在着自己的消息循环。

其中,

Initialize用于对游戏界面内一些对象的初始化。

LoadContent用于加载游戏内的一些资源,如图像,文件,声音等。

紧接着,Update和Draw 便开始游戏内的消息循环,前者主要用于相关计算,后者用于绘制。

XNA的默认刷新率是60fps(Frame/Second) 

 

我们首先在Content Directory下添加一张一存在的图片old english sheepdog.png 将其Content Importer和Processor 改为Texture - XNA Framework

请注意,XNA源生的API不支持GIF,如果需要导入则需要自己实现相应的Importer和Processor 

插一句

 Mircosoft.Xna.Framework.Content.Pipeline 是专门用于处理游戏资源的管道,无论是图像还是声音,3D资源或是XML的处理和编译都是在其之下处理编译的。

 编译后,任意资源都会被编译成.XNB(XNA二进制文件)。请注意,虽说XNA能同吃Windows和XBox,但其资源文件编译过程是不同的,不可通用,须重新编译。 

 

紧接着,导入图片后,我们可以紧接着LoadContent以及Update和Draw了

 

//新建变量img;用于存储图像
Texture2D img;

 

//在LoadContent中增加
 this.img = this.Content.Load<Texture2D>("old english sheepdog");

 

 其中"old english sheepdog"就是刚才加载的图像的Name属性。

这样,我们便可以直接在Draw方法中,让XNA的每次刷新都画出这样图片了 

代码
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);
            spriteBatch.Begin();
            spriteBatch.Draw(img, 
new Rectangle(005050), Color.White);
            spriteBatch.End();
            
base.Draw(gameTime);
        }

 简单解释下以上代码,

GraphicsDevice.Clear(Color.CornflowerBlue);用于擦除当前屏幕内的图像

 spriteBatch 通常用于绘制Sprite,即精灵。

请注意,在XNA的一个Game里请避免使用多个 spriteBatch ,并且所有图像的绘制都必须在spriteBatch 的Begin和End之间。

至于 spriteBatch.Draw 具有多个重载, 具体参考

 

 这样,我们第一个图像就诞生了

 

细心的你发现了吧,游戏界面内没有鼠标光标,那多简单,自己画个。

//声明全局变量p
Point p;
//在Update方法中增加
 MouseState ms=Mouse.GetState();
 p 
= new Point(ms.X, ms.Y);
//修改Draw方法
 spriteBatch.Draw(img, new Rectangle(p.X, p.Y, 5050), Color.White);

 

 这样,鼠标光标,便变成当前图像了。

当然,如果你只需要默认windows鼠标光标作为游戏光标,则不需要这么麻烦

//在Initialize中
 this.IsMouseVisible = true;

 即可。

 

这样,我们第一个XNA程序便完成了。 

 

所谓算法

January 14, 2010

所谓算法,除非脑残者写出的,否则对其的优化无非就是空间换时间时间换空间

----By Edwin Tai