佳礼资讯网

 找回密码
 注册

ADVERTISEMENT

查看: 6370|回复: 16

浅谈Isometric Tile Engine的原理(VC# + XNAGS)

[复制链接]
发表于 7-2-2011 02:24 PM | 显示全部楼层 |阅读模式
本帖最后由 geekman 于 7-2-2011 03:10 PM 编辑

最近有没有玩CityVille?是否好奇这种类型的游戏是如何编写的呢?是的话就继续看下去吧。

首先,这不是完整的游戏设计教学,这篇文章只是讨论基本的Isometric tile engine (简称Iso Engine)的设计原理。

何为Isometric?全名为Isometric Projection(斜角视图)是指一个物体,旋转45度角度,45度角的高度,并无视视角扭曲,纯粹以平行线来描绘物体同一个方向的边缘线的一种绘图方式。这种视角可以提供整齐划一的描绘方式,同时更清楚地展现一个物体的立体性。简单的说就是以2D画面表现出近似3D的画面,所以Isometric Projection也被称为2.5D。

参考网站:http://bbs.66rpg.com/thread-79139-1-1.html

一般上Iso引擎都是采用1:2的纵横比例,也就是说一个正方形,在Iso视角下,会变成一个宽2:高1的菱形。当然,如果视角高度不是45度(你可以选择任何视角高度)的话,这个比例就不适用。但是2x1的比例是最容易计算的,所以这里就采用2x1的比例。

使用工具:Visual C# 2008 Express Edition,XNA Game Studio 3.1,Paint.Net

首先,用Paint.Net开启一张新的画像,尺寸设定为宽64高32,因为XNA的图像渲染引擎在图像尺寸为2的倍数时才会以最高效率运作,所以才选择这个尺寸,别为了贪方便而用10或5的倍数,这只会拖慢XNA的渲染引擎的速度。

花一个如下图一般的图形,这就是最基本的1x1地板图块(floor tile)


在XNA Game Studio里开启一个新的Windows Game Project,或者在安装了XNA Game Studio的情形下,启动Visual C# Express Edition 2008里,开启一个新的Project,选择XNA Game Studio 3.1-〉Windows Game(3.1)。把Project Name设定为Iso_Engine(或者任何你喜欢的名字)。



滑鼠右键点击Solution Explorer里的 Project的Content部分,选择Add-〉Existing Item,把刚才所制作的图像加入在Content里面。记得选择Add As Link,这样的话VC#会自动检测你的图画是否有改动过,并自动更新你改动过的部分。





以下是XNA Game Studio预设的代码,已经包括启动DirectX和泛用的基本游戏框架 - 游戏逻辑更新函式和游戏画面渲染函式。
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using Microsoft.Xna.Framework;
  5. using Microsoft.Xna.Framework.Audio;
  6. using Microsoft.Xna.Framework.Content;
  7. using Microsoft.Xna.Framework.GamerServices;
  8. using Microsoft.Xna.Framework.Graphics;
  9. using Microsoft.Xna.Framework.Input;
  10. using Microsoft.Xna.Framework.Media;
  11. using Microsoft.Xna.Framework.Net;
  12. using Microsoft.Xna.Framework.Storage;

  13. namespace Iso_Engine
  14. {
  15.     /// <summary>
  16.     /// This is the main type for your game
  17.     /// </summary>
  18.     public class Game1 : Microsoft.Xna.Framework.Game
  19.     {
  20.         GraphicsDeviceManager graphics;
  21.         SpriteBatch spriteBatch;

  22.         public Game1()
  23.         {
  24.             graphics = new GraphicsDeviceManager(this);
  25.             Content.RootDirectory = "Content";
  26.         }

  27.         /// <summary>
  28.         /// Allows the game to perform any initialization it needs to before starting to run.
  29.         /// This is where it can query for any required services and load any non-graphic
  30.         /// related content.  Calling base.Initialize will enumerate through any components
  31.         /// and initialize them as well.
  32.         /// </summary>
  33.         protected override void Initialize()
  34.         {
  35.             // TODO: Add your initialization logic here

  36.             base.Initialize();
  37.         }

  38.         /// <summary>
  39.         /// LoadContent will be called once per game and is the place to load
  40.         /// all of your content.
  41.         /// </summary>
  42.         protected override void LoadContent()
  43.         {
  44.             // Create a new SpriteBatch, which can be used to draw textures.
  45.             spriteBatch = new SpriteBatch(GraphicsDevice);

  46.             // TODO: use this.Content to load your game content here
  47.         }

  48.         /// <summary>
  49.         /// UnloadContent will be called once per game and is the place to unload
  50.         /// all content.
  51.         /// </summary>
  52.         protected override void UnloadContent()
  53.         {
  54.             // TODO: Unload any non ContentManager content here
  55.         }

  56.         /// <summary>
  57.         /// Allows the game to run logic such as updating the world,
  58.         /// checking for collisions, gathering input, and playing audio.
  59.         /// </summary>
  60.         /// <param name="gameTime">Provides a snapshot of timing values.</param>
  61.         protected override void Update(GameTime gameTime)
  62.         {
  63.             // Allows the game to exit
  64.             if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
  65.                 this.Exit();

  66.             // TODO: Add your update logic here

  67.             base.Update(gameTime);
  68.         }

  69.         /// <summary>
  70.         /// This is called when the game should draw itself.
  71.         /// </summary>
  72.         /// <param name="gameTime">Provides a snapshot of timing values.</param>
  73.         protected override void Draw(GameTime gameTime)
  74.         {
  75.             GraphicsDevice.Clear(Color.CornflowerBlue);

  76.             // TODO: Add your drawing code here

  77.             base.Draw(gameTime);
  78.         }
  79.     }
  80. }
复制代码
我们要做的是在开头的SpriteBatch的宣告之后加入一个Texture2D和一个Vector2的宣告:
  1. public class Game1 : Microsoft.Xna.Framework.Game
  2.     {
  3.         GraphicsDeviceManager graphics;
  4.         SpriteBatch spriteBatch;
  5.         Texture2D txt_floor;
  6.         Vector2 position;
复制代码
Texture2D顾名思义,是一个2D贴图材质,我们用它来载入刚才加入的那个地面的图像。Vector2则是一个2D向量标,用来在渲染图像时,标识图画的所在位置的。

《待续》

评分

参与人数 1人气 +5 收起 理由
arios + 5

查看全部评分

回复

使用道具 举报


ADVERTISEMENT

 楼主| 发表于 7-2-2011 03:08 PM | 显示全部楼层
接下来是在 Initialize() 里面设定图像的起始位置:
  1. protected override void Initialize()
  2.         {
  3.             // TODO: Add your initialization logic here
  4.             position = Vector2.Zero;

  5.             base.Initialize();
  6.         }
复制代码
position只是用Vector2.Zero来把其内容初始化为X=0,Y=0。没什么特别的。
  1. protected override void LoadContent()
  2.         {
  3.             // Create a new SpriteBatch, which can be used to draw textures.
  4.             spriteBatch = new SpriteBatch(GraphicsDevice);
  5.             txt_floor = Content.Load<Texture2D>("1x1_floor_green");

  6.             // TODO: use this.Content to load your game content here
  7.         }
复制代码
由于我们已经把地板的图像加入Content里面,这个图像就会由Content Pipeline Manager进行处理,将之转换为 Content Pipeline Object。Content可以是图画(Graphics/Texture),3D模型(Model/Mesh),影片(Video Stream),音效/音乐(Audio Stream)诸如此类,所以当我们呼叫 Content.Load ();的时候,要进行dynamic casting,以便Content Pipeline Manager 知道你要载入的是什么类型的Object。这就是Load后面的那个<>的作用,也就是将这个图像Asset dynamic casting为Texture2D。后面的括弧里的是 Asset Name,而不是File Name,这点要注意。Asset Name可以在Solution Explorer里面,选择了Content里的Item(例如我们刚刚加入的那个地板图像)然后在Solution Explorer底下的Properties里面找到。你可以更改Asset Name,这并不会影响CPM(Content Pipeline Manager)辨认那个档案的所在位置。

接下来是大家都喜欢的,也就是画面输出。
  1. protected override void Draw(GameTime gameTime)
  2.         {
  3.             GraphicsDevice.Clear(Color.CornflowerBlue);

  4.             // TODO: Add your drawing code here
  5.             spriteBatch.Begin(SpriteBlendMode.AlphaBlend);
  6.             {
  7.                 spriteBatch.Draw(txt_floor, position, Color.White);
  8.             }
  9.             spriteBatch.End();
  10.             base.Draw(gameTime);
  11.         }
复制代码
在2D模式下,所有的图像都将由spriteBatch来描绘。Sprite是XNA对游戏画面里的构成元素的称呼,简单来讲就是指任何可以在画面里移动(或者不移动,例如背景)的图像模块。而Sprite(这不是汽水而是有气XX水。。。呃,抱歉,最近听这个讲鬼话的广告听到有点烦了,有气又有水还说不是汽水 )的图像则由Texture(以及其家族成员,例如Texture2D)提供。简而言之,SpriteBatch 就是批次量的把Texture里的图画贴在游戏画面里,就酱。

在使用spriteBatch描绘图像前,要记得呼叫SpriteBatch.Begin(),这是为了锁定游戏画面(显示卡内存)以免被其他程式介入,造成冲突和搅乱,毕竟这是Windows OS,多工处理环境,大家都要用显示卡内存来输出画面,不锁定的话就会大乱了。Begin()里面标明SpriteBelndMode.AlphaBlend是告诉显示卡,所有的Sprite都要使用Alpha Channel来达成去底效果,也就是透明底色。

接下来就是用SpriteBatch.Draw()来描绘Sprite。这个Method有很多个变化,我采用的是最简单的那个。Texture自然是图像来源,也就是我们的那个地板图像的Texture。Vector2 是我们的图像位子,也就是(0,0),画面的左上角。Color 是渲染色调。Color.White是没有变化,换成其他颜色的话就会把你的图像染上该颜色,自己玩玩吧。

输出:



要注意的是,图像是以其左上角对准position的X和Y。有时你会想以图像中心来对position,这点并不难达到,只需在设定position时往右下角移动适当的像素距离就行了。

在描绘完之后,记得用SpriteBatch.End()来解放被锁定的显卡内存。

但是这只是单单一个地板图像而已,要如何描绘更多的地板呢?

且看下回分解。

《待续》
回复

使用道具 举报

 楼主| 发表于 7-2-2011 03:52 PM | 显示全部楼层
本帖最后由 geekman 于 7-2-2011 03:54 PM 编辑

要描绘更多的地板,基本上只是简单的Nested For Loop,内圈从X=0,Y=0-〉X=N-1,Y=0,外圈从Y=0-〉Y=N-1。就酱简单。

可是由于Iso视角是要旋转45度,所以我们要为每个地板计算其需要偏移的距离。经过无数的尝试和验证(呼呼呼!),这是计算方式:
  1. spriteBatch.Begin(SpriteBlendMode.AlphaBlend);
  2. {
  3.     for (int x = 0; x < max_x; x++)
  4.     {
  5.         for (int y = 0; y < max_y; y++)
  6.         {
  7.             pos.X = (GraphicsDevice.Viewport.Width / 2) + ((x - y) * 32) - 32;
  8.             pos.Y = (GraphicsDevice.Viewport.Height / 3) + ((y + x) * 16) - 16;
  9.             spriteBatch.Draw(txt_floor, pos, Color.White);                       
  10.         }
  11.     }
  12. }
  13. spriteBatch.End();
复制代码
上面的代码做了些跨越,就是说原本是应该由几个步骤来完成的东西,我浓缩了直接贴上去。

首先,我们可以通过GraphicsDevice.Viewport.Width和Height来获取你的游戏画面的尺寸。GraphicsDevice就是你的显示卡,Viewport就是你在显示卡里用来描绘画面的内存窗口。上面的代码里的运算只是把第一个地板移到画面宽度的中央/高度的2/3的位置。因为旋转了45度角,所以你的左上角变成了中央上方。



从上面的画面来看,Iso视角是很败家的画面描绘方式,因为画面的4个角都被浪费掉了。

计算原理是横向每一行里面,X轴每向右边前进一格,x向位置就会偏移为x+32 (地板宽度的一半),而y向会偏移y+16(地板高度的一半),而纵向每一排(Y轴往下前进)则会往x向偏移-32像素,y向偏移另一个16像素。

换句话说,每向Y轴前进一步,其X方向就会倒退一个单位(半个地板宽度),所以-> (X-Y)*32;至于Y方向,则存粹的继续向下发展。

max_x和max_y只是两个const int,用来限定x和y的地板数量。

至于每一行后面的-32和-16只是把描绘的position从每个地板图像的Origin从左上角移动到图像中心罢了。

画面输出:



《待续》
回复

使用道具 举报

 楼主| 发表于 7-2-2011 04:15 PM | 显示全部楼层
单单只是描画地板很枯燥。我们来画屋子~

再次用Paint.Net画出这个简单的屋子(或者直接在这帖子里save下那些图画),依据上面所描述的方式,把这个屋子加入在Content 里面。然后再加入相关的Texture2D的宣告。



很可爱吧?

再次参考上面的步骤,载入这个图像于相关的Texture2D里面。

接下来是在每个地板上面加上屋子:
  1. for (int y = 0; y < max_y; y++)
  2. {
  3.     pos.X = (GraphicsDevice.Viewport.Width / 2) + ((x - y) * 32) - 32;
  4.     pos.Y = (GraphicsDevice.Viewport.Height / 3) + ((y + x) * 16) - 16;
  5.     spriteBatch.Draw(txt_floor, pos, Color.White);
  6.     pos.Y -= 32;  //加入这行
  7.     spriteBatch.Draw(txt_building, pos, Color.White); //还有这行
  8. }
复制代码
之所以要把Y位置减去32像素,是因为这个屋子的高度,因为屋子图像的尺寸是64x64,比地板高了32像素,所以要矫正一下位置。

再来,就是显示鼠标。你可以选择硬体鼠标(也就是Windows提供的那个)或者是软体鼠标。因为XNAGS预设是把鼠标给隐藏起来的,我也懒得去找如何显示鼠标的方式,就干脆使用软体鼠标,也就是说,自己画鼠标。

画出这个鼠标(或者任何你喜欢的图画,但是记得别选太大的图像):



再次重复之前的步骤,把这个鼠标图案加入在Content里面,宣告Texture2D,载入图像,顺便宣告另一个Vector2,作为鼠标位置的寄存。

然后在游戏的Update()函式里读取鼠标位置,存进鼠标位置的Vector2里面。

最后在完成渲染地板和屋子的程序后,根据鼠标位置画上鼠标图像。

完整的代码:
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using Microsoft.Xna.Framework;
  5. using Microsoft.Xna.Framework.Audio;
  6. using Microsoft.Xna.Framework.Content;
  7. using Microsoft.Xna.Framework.GamerServices;
  8. using Microsoft.Xna.Framework.Graphics;
  9. using Microsoft.Xna.Framework.Input;
  10. using Microsoft.Xna.Framework.Media;
  11. using Microsoft.Xna.Framework.Net;
  12. using Microsoft.Xna.Framework.Storage;

  13. namespace ISO_Engine
  14. {
  15.     /// <summary>
  16.     /// This is the main type for your game
  17.     /// </summary>
  18.     public class Game1 : Microsoft.Xna.Framework.Game
  19.     {
  20.         GraphicsDeviceManager graphics;
  21.         SpriteBatch spriteBatch;
  22.         Texture2D txt_floor, txt_building, txt_cursor;
  23.         Vector2 pos = Vector2.Zero, cur_pos = Vector2.Zero;
  24.         const int max_x = 12, max_y = 12;

  25.         public Game1()
  26.         {
  27.             graphics = new GraphicsDeviceManager(this);
  28.             Content.RootDirectory = "Content";
  29.         }

  30.         /// <summary>
  31.         /// Allows the game to perform any initialization it needs to before starting to run.
  32.         /// This is where it can query for any required services and load any non-graphic
  33.         /// related content.  Calling base.Initialize will enumerate through any components
  34.         /// and initialize them as well.
  35.         /// </summary>
  36.         protected override void Initialize()
  37.         {
  38.             // TODO: Add your initialization logic here      
  39.    
  40.             base.Initialize();
  41.         }

  42.         /// <summary>
  43.         /// LoadContent will be called once per game and is the place to load
  44.         /// all of your content.
  45.         /// </summary>
  46.         protected override void LoadContent()
  47.         {
  48.             // Create a new SpriteBatch, which can be used to draw textures.
  49.             spriteBatch = new SpriteBatch(GraphicsDevice);

  50.             // TODO: use this.Content to load your game content here
  51.             txt_floor = Content.Load<Texture2D>("floor_green");
  52.             txt_building = Content.Load<Texture2D>("house");
  53.             txt_cursor = Content.Load<Texture2D>("cursor");
  54.         }

  55.         /// <summary>
  56.         /// UnloadContent will be called once per game and is the place to unload
  57.         /// all content.
  58.         /// </summary>
  59.         protected override void UnloadContent()
  60.         {
  61.             // TODO: Unload any non ContentManager content here
  62.         }

  63.         /// <summary>
  64.         /// Allows the game to run logic such as updating the world,
  65.         /// checking for collisions, gathering input, and playing audio.
  66.         /// </summary>
  67.         /// <param name="gameTime">Provides a snapshot of timing values.</param>
  68.         protected override void Update(GameTime gameTime)
  69.         {
  70.             // Allows the game to exit
  71.             if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
  72.                 this.Exit();

  73.             // TODO: Add your update logic here
  74.             cur_pos.X = Mouse.GetState().X;
  75.             cur_pos.Y = Mouse.GetState().Y;
  76.             base.Update(gameTime);
  77.         }

  78.         /// <summary>
  79.         /// This is called when the game should draw itself.
  80.         /// </summary>
  81.         /// <param name="gameTime">Provides a snapshot of timing values.</param>
  82.         protected override void Draw(GameTime gameTime)
  83.         {
  84.             GraphicsDevice.Clear(Color.CornflowerBlue);

  85.             // TODO: Add your drawing code here
  86.             spriteBatch.Begin(SpriteBlendMode.AlphaBlend);
  87.             {
  88.                 for (int x = 0; x < max_x; x++)
  89.                 {
  90.                     for (int y = 0; y < max_y; y++)
  91.                     {
  92.                         pos.X = (GraphicsDevice.Viewport.Width / 2) + ((x - y) * 32) - 32;
  93.                         pos.Y = (GraphicsDevice.Viewport.Height / 3) + ((y + x) * 16) - 16;
  94.                         spriteBatch.Draw(txt_floor, pos, Color.White);
  95.                         pos.Y -= 32;
  96.                         spriteBatch.Draw(txt_building, pos, Color.White);
  97.                     }
  98.                 }
  99.                 spriteBatch.Draw(txt_cursor, cur_pos, Color.White);
  100.             }
  101.             spriteBatch.End();

  102.             base.Draw(gameTime);
  103.         }
  104.     }
  105. }
复制代码
另外我还在这里加多一个侦测键盘ESC键的动作,如果玩家按下ESC键,就结束游戏。

《还要继续吗?》
回复

使用道具 举报

发表于 9-2-2011 10:38 PM | 显示全部楼层
本帖最后由 megablue 于 9-2-2011 10:40 PM 编辑

有研究过 我有使用Javascript来写的简单的Isometric引擎

不过碰到了一些难题就搁着了

之前想到的问题是,

问题1,就是layers之间重叠的问题,如一个人(高1x 宽1 长x1) 走到 一间 (高2x 宽1 长x1) 的建筑物的后面时,该如何实现?
问题2, 一个人穿越一道中间有空洞的门栏,又该如何实现?
回复

使用道具 举报

 楼主| 发表于 10-2-2011 02:48 AM | 显示全部楼层
本帖最后由 geekman 于 10-2-2011 03:04 AM 编辑

1)如果是根据我上面所说的tile based ISO 设计,每个单位都占据一个四方格(1x1 floor),那么站在地上的人就会被当作和建筑物一般处理(参开上面程式码里的方式),也就是先画地板,然后画建筑物/人物/汽车/其他占据该格子的物体,那么遮盖的问题就不应该存在。或者你可以尝试把有问题情况描述和画面贴上来,我试试看能否帮你解决。如果你的问题指的是在格子与格子间穿行(smooth transition from tile to tile)那就另当别论了。事实上,就算是smooth transition也不会受建筑物的高度影响,反而是非正方形物体(1x2或者2x3之类的)才是比较麻烦的。

2)将拱门分拆。

分拆成如下:

(请原谅图片的简陋,已经半夜了,而画Iso图片真的很耗时。。。)

每一个地板将容纳3层的物体(以红线标示),画完地板接着画最底下一层的柱子/物体,然后第二层,。。。然后第N层。中间那个格子(地板)就是人物所站的地方,也就是说横梁会覆盖在人物的头上。

----分割线----
到目前为止,我采用的都是以地板为主的描绘方式,也就是说,先画地板,然后画第一层的物体,然后再画第二层的物体。。。画第N层的物体,然后画第二片地板。。。第N片地板。这种做法并不适合让人物在地板间流畅的移动,而是会从一格跳到另一格地板。如果要做到流畅的移动动画,旧的采取由下到上的分层描绘方式,也就是先描绘所有的地板,然后描绘所有的第二层物体,然后描绘所有的第三层物体。。。以此类推。两者之间的差别在于如何储存整个世界的物件,以适应所采用的描绘方式。

网上有几个挺著名的Iso Engine Project,就是模仿Fallout 1 & 2的Iso Engine的,你可以向谷歌大神查询一下,里面应该有更详细的解说smooth tile transition的。
回复

使用道具 举报

Follow Us
 楼主| 发表于 31-5-2011 03:20 PM | 显示全部楼层
本帖最后由 geekman 于 31-5-2011 03:54 PM 编辑

目前正在改善这个ISO引擎,增加了Layers支援,并且加入了Cell class以提供在每个Cell里面放置物体(可重叠)的功能,测试完毕后将把结果和代码贴上来。

预览:
回复

使用道具 举报

 楼主| 发表于 2-6-2011 10:35 AM | 显示全部楼层
本帖最后由 geekman 于 2-6-2011 10:52 AM 编辑

我可是已经累积了十多年的经验的。想当初在 C++ Builder 1.0 的年代,我也是望着 C++ Builder 的 IDE 干瞪眼,无从下手。读书时花了一个学期每天一下课就泡在Computer Lab到晚上9点,足足四个月从不间断,这其中的血汗又有谁人知?结果从DOS时代迁移到Win32时代,一切归零,从新回到什么也不懂的状态。过后死抱着Documentation和参考书天天刨到半夜,才有今天的成果。台前风光,幕后清光啊。
回复

使用道具 举报


ADVERTISEMENT

 楼主| 发表于 7-6-2011 02:25 PM | 显示全部楼层
本帖最后由 geekman 于 7-6-2011 02:30 PM 编辑

到目前为止的代码,接下来要做一个Map Editor/Tile Manager的软件,用来设计地图用的。
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using Microsoft.Xna.Framework;
  5. using Microsoft.Xna.Framework.Audio;
  6. using Microsoft.Xna.Framework.Content;
  7. using Microsoft.Xna.Framework.GamerServices;
  8. using Microsoft.Xna.Framework.Graphics;
  9. using Microsoft.Xna.Framework.Input;
  10. using Microsoft.Xna.Framework.Media;
  11. using Microsoft.Xna.Framework.Net;
  12. using Microsoft.Xna.Framework.Storage;

  13. namespace ISO_Engine
  14. {
  15.     /// <summary>
  16.     /// This is the main type for your game
  17.     /// </summary>
  18.     public class Game1 : Microsoft.Xna.Framework.Game
  19.     {
  20.         GraphicsDeviceManager graphics;
  21.         SpriteBatch spriteBatch;
  22.         Vector2 cur_pos;
  23.         Texture2D cur_txt, flr_txt;
  24.         layer layers;

  25.         public Game1()
  26.         {
  27.             graphics = new GraphicsDeviceManager(this);
  28.             Content.RootDirectory = "Content";
  29.         }

  30.         /// <summary>
  31.         /// Allows the game to perform any initialization it needs to before starting to run.
  32.         /// This is where it can query for any required services and load any non-graphic
  33.         /// related content.  Calling base.Initialize will enumerate through any components
  34.         /// and initialize them as well.
  35.         /// </summary>
  36.         protected override void Initialize()
  37.         {
  38.             // TODO: Add your initialization logic here                  
  39.             layers = new layer();            
  40.             base.Initialize();
  41.         }

  42.         /// <summary>
  43.         /// LoadContent will be called once per game and is the place to load
  44.         /// all of your content.
  45.         /// </summary>
  46.         protected override void LoadContent()
  47.         {
  48.             // Create a new SpriteBatch, which can be used to draw textures.
  49.             spriteBatch = new SpriteBatch(GraphicsDevice);

  50.             // TODO: use this.Content to load your game content here            
  51.             flr_txt = Content.Load<Texture2D>("cube");            
  52.             layers.init(12, 12, 3, flr_txt, spriteBatch, GraphicsDevice.Viewport.Width, GraphicsDevice.Viewport.Height);            
  53.             cur_txt = Content.Load<Texture2D>("cursor");            
  54.         }

  55.         /// <summary>
  56.         /// UnloadContent will be called once per game and is the place to unload
  57.         /// all content.
  58.         /// </summary>
  59.         protected override void UnloadContent()
  60.         {
  61.             // TODO: Unload any non ContentManager content here
  62.         }

  63.         /// <summary>
  64.         /// Allows the game to run logic such as updating the world,
  65.         /// checking for collisions, gathering input, and playing audio.
  66.         /// </summary>
  67.         /// <param name="gameTime">Provides a snapshot of timing values.</param>
  68.         protected override void Update(GameTime gameTime)
  69.         {
  70.             // Allows the game to exit
  71.             if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
  72.                 this.Exit();

  73.             // TODO: Add your update logic here
  74.             cur_pos.X = Mouse.GetState().X;
  75.             cur_pos.Y = Mouse.GetState().Y;
  76.             base.Update(gameTime);
  77.         }

  78.         /// <summary>
  79.         /// This is called when the game should draw itself.
  80.         /// </summary>
  81.         /// <param name="gameTime">Provides a snapshot of timing values.</param>
  82.         protected override void Draw(GameTime gameTime)
  83.         {
  84.             GraphicsDevice.Clear(Color.CornflowerBlue);

  85.             // TODO: Add your drawing code here
  86.             spriteBatch.Begin(SpriteBlendMode.AlphaBlend);
  87.             {               
  88.                 layers.draw();               
  89.                 spriteBatch.Draw(cur_txt, cur_pos, Color.White);
  90.             }
  91.             spriteBatch.End();

  92.             base.Draw(gameTime);
  93.         }        
  94.     }
  95.     public class cell
  96.     {
  97.         Vector2 pos;
  98.         Texture2D txt;
  99.         SpriteBatch sb;
  100.         Color tint;

  101.         public cell()
  102.         {            
  103.             txt = null;
  104.             sb  = null;
  105.         }        
  106.         public void init(int xval, int yval, Texture2D txt_name, SpriteBatch sprb, Color Tint)
  107.         {
  108.             pos = new Vector2(xval, yval);
  109.             txt = txt_name;
  110.             sb = sprb;
  111.             tint = Tint;
  112.         }        
  113.         public void draw()
  114.         {
  115.             if (txt == null)
  116.                 return; //nothing to draw if cell is empty

  117.             sb.Draw(txt, pos, tint);
  118.         }
  119.     }
  120.     public class layer
  121.     {
  122.         cell[,,] cells;
  123.         int xsize, ysize, lcount, layer_height = 32;
  124.         Color[] shade = new Color[] { Color.Gray, Color.LightGray, Color.White };

  125.         public layer()
  126.         {
  127.             cells = null;
  128.             xsize = 0;
  129.             ysize = 0;
  130.             lcount = 0;
  131.         }
  132.         public void init(int xSize, int ySize, int lCount, Texture2D texture, SpriteBatch sprb, int width, int height)
  133.         {
  134.             xsize  = xSize;
  135.             ysize  = ySize;
  136.             lcount = lCount;           

  137.             cells = new cell[lcount, xsize, ysize];
  138.             for (int l = 0; l < lcount; l++)
  139.             {
  140.                 for (int x = 0; x < xsize; x++)
  141.                 {
  142.                     for (int y = 0; y < ysize; y++)
  143.                     {
  144.                         cells[l, x, y] = new cell();
  145.                         cells[l, x, y].init((width / 2) + ((x - y) * 32) - 32,
  146.                                            ((height / 3) + ((y + x) * 16) - 16) - (l * layer_height),
  147.                                              texture,
  148.                                              sprb, shade[l]);                        
  149.                     }
  150.                 }
  151.             }
  152.         }
  153.         public void draw()
  154.         {
  155.             for(int l=0; l<lcount; l++)
  156.             {
  157.                 for (int x = 0; x < xsize; x++)
  158.                 {
  159.                     for (int y = 0; y < ysize; y++)
  160.                     {
  161.                         cells[l, x, y].draw();
  162.                     }
  163.                 }
  164.             }
  165.         }
  166.     }
  167. }
复制代码
效果:


材质:
回复

使用道具 举报

 楼主| 发表于 13-6-2011 01:24 PM | 显示全部楼层
别崇拜叔,叔只是传说。。。

我也是在摸索罢了,学无止境啊。
回复

使用道具 举报

 楼主| 发表于 13-6-2011 04:34 PM | 显示全部楼层
那就提醒你最重要的一点:记着防毒,Computer Lab绝对是万毒魔窟,功课要多储存几个副本,不然一旦中毒导致档案被删,那就欲哭无泪了。我曾经在提交Yearly Project的前一晚才赶完整个Project,匆忙存档后没有检查,结果第二天presentation时把磁碟插入电脑,找了老半天没有档案,后来证实Computer Lab大规模爆发病毒潮,我是受害者之一。幸好这次事件有迹可循,所以导师额外给予受影响的人两天时间来recover,不然那次我就顶多得 A  D(Just a ‘D’),而不是AD(‘A’ Distinction)了。
回复

使用道具 举报

 楼主| 发表于 14-6-2011 12:23 AM | 显示全部楼层
开头的时候的确是很难看的懂那些参考书,毕竟你没有足够的知识去理解那些技术名词和原理。坚持下去吧,总有一天你会忽然开窍的。只要你用心的去看Lecture Notes(聊胜于无)和参考书,随着对那些技术的理解你会渐渐的明白的。不过当你开始懂得那些东西,你就会开始发现学的越多,懂得越少。。。

当我还是个电脑白痴时,我只知道我不懂得电脑。
当我开始懂得电脑时,我发觉我不懂得DOS。
当我开始懂得DOS时,我发觉我不懂得什么是电脑语言。
当我开始懂得电脑语言时,我揭开了潘多拉的盒子。
我不懂如何打破64KB的框框。
我不懂如何在荧幕上画线条。
我不懂如何播放音效。
我不懂如何读取键盘数据。
我不懂如何截取鼠标的位置。
我不懂什么是Interrupt。
我不懂如何读写640KB以上的记忆体空间。
我不懂很多很多。
我不懂什么是Windows。

当我学会什么是Windows,潘多拉再次找上门跟我推销另一个更大的箱子。。。

所以别想太多,因为你已经有太多不明白的东西了。。。哈哈哈
回复

使用道具 举报

发表于 18-11-2011 12:53 PM | 显示全部楼层
非常感谢!!大大的分享...

小弟最近也学习XNA....

你的教学...让我收益很多...哈!!哈!!哈!!

请继续你的教学...T。T
回复

使用道具 举报

 楼主| 发表于 18-11-2011 02:37 PM | 显示全部楼层
欢迎讨论~
回复

使用道具 举报

发表于 1-2-2012 09:32 AM | 显示全部楼层
geekman大大,你好像很久没有上来了啊,是不是厌倦人少的这里了啊?哈哈。
回复

使用道具 举报

 楼主| 发表于 4-2-2012 11:42 AM | 显示全部楼层
回复 20# Friedman


    偷偷回复你:因为某些原因,我不打算再在佳礼混了。有兴趣的话可以到我的个人网页去交流:http://webbit.zxq.net/phpbb/index.php
回复

使用道具 举报


ADVERTISEMENT

发表于 4-2-2012 01:24 PM | 显示全部楼层
回复 21# geekman


    原来是这样?到底会是什么样的原因啊?哈哈。有机会说说看啊。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

 

ADVERTISEMENT



ADVERTISEMENT



ADVERTISEMENT

ADVERTISEMENT


版权所有 © 1996-2023 Cari Internet Sdn Bhd (483575-W)|IPSERVERONE 提供云主机|广告刊登|关于我们|私隐权|免控|投诉|联络|脸书|佳礼资讯网

GMT+8, 7-12-2025 06:37 PM , Processed in 0.150977 second(s), 23 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表