佳礼资讯网

 找回密码
 注册

ADVERTISEMENT

查看: 3491|回复: 21

C# XNA 粒子系统应用:雪花

[复制链接]
发表于 19-6-2009 12:04 PM | 显示全部楼层 |阅读模式
之前发表过GDI+的粒子系统,之后就因为忙碌没有跟进发表MDX版的粒子系统,现在发一个XNA版的玩玩。

[使用工具]
1)Visual C# 2005 Express Edition。
2)XNA Game Studio 2.0。
3)绘图软件,个人推荐 Paint.NET,功能比 MS Paint 强很多的绘图软件,免费的。

[概念]
这个粒子系统和之前的GDI+版本没多大差别,事实上只要把之前的那个版本稍作修改就可以了。不过既然使用XNA,我就干脆使用了XNA里面的GameComponent class来制作粒子。

[目标]
1)使用 XNA GS 来制作粒子系统。
2)熟悉 2D 游戏的制作(主要在于[更新][读取输入]和[绘画游戏画面]方面)
3)熟悉 SpriteBatch 的运用。

-----------------------------------
首先开启一个新的 XNA Game Project: File->New Project...->Windows Game 2.0,把Project命名为ParticleSnow。

以下是XNA GS 2.0所产生的空白Project:
  1. using System;
  2. using System.Collections.Generic;
  3. using Microsoft.Xna.Framework;
  4. using Microsoft.Xna.Framework.Audio;
  5. using Microsoft.Xna.Framework.Content;
  6. using Microsoft.Xna.Framework.GamerServices;
  7. using Microsoft.Xna.Framework.Graphics;
  8. using Microsoft.Xna.Framework.Input;
  9. using Microsoft.Xna.Framework.Net;
  10. using Microsoft.Xna.Framework.Storage;

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

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

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

  34.             base.Initialize();
  35.         }

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

  44.             // TODO: use this.Content to load your game content here
  45.         }

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

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

  64.             // TODO: Add your update logic here

  65.             base.Update(gameTime);
  66.         }

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

  74.             // TODO: Add your drawing code here

  75.             base.Draw(gameTime);
  76.         }
  77.     }
  78. }
复制代码
暂时我们不必理会这部分,待会儿再倒回来加入游戏逻辑。关于XNA GS的基本知识,请各位善用Google学习。

接下来是加入Particle class。基于好奇和学习理由,我决定使用XNA GS的GameComponent class来取代自己写的Generic class。在现有的Project里面加入一个新的GameComponent:
Project->Add New Item...->Game Component.
把Game Component命名为Particle。

这个Game Component事实上就是一个class file,它和一般的 Generic Class 不同的是,它多了一些对游戏程式主体的认知(Game-Aware),和已经内建自己的Update函式。
  1. namespace ParticleSnow
  2. {
  3.     /// <summary>
  4.     /// This is a game component that implements IUpdateable.
  5.     /// </summary>
  6.     public class Particles : Microsoft.Xna.Framework.GameComponent
  7.     {
  8.         public Vector2 position, origin;               
  9.         public int lifespan;
  10.         public float decay;
  11.         public bool isDead;
  12.         public Color color;
  13.         public int age;
  14.         byte value;
  15.         public int depth;
  16.         public float scale;
  17.         float rotate;
  18.         public float angle;
  19.         
  20.         public Particles(Game game)
  21.             : base(game)
  22.         {
  23.             // TODO: Construct any child components here
  24.             Random rnum = new Random();

  25.             position = new Vector2(rnum.Next(0, 1024), 0);
  26.             origin = new Vector2(64, 64);
  27.             depth = rnum.Next(1, 70);
  28.             scale = depth / 100f;
  29.             lifespan = 720;
  30.             decay = depth / 10;
  31.             if (decay < 1)
  32.                 decay = 1;
  33.             age = 0;
  34.             isDead = false;
  35.             value = 255;            
  36.             color = new Color(value, value, 255);
  37.             rotate = ((float)rnum.Next(1, 100)) / 1000f;
  38.             angle = 0;
  39.         }

  40.         /// <summary>
  41.         /// Allows the game component to perform any initialization it needs to before starting
  42.         /// to run.  This is where it can query for any required services and load content.
  43.         /// </summary>
  44.         public override void Initialize()
  45.         {
  46.             // TODO: Add your initialization code here
  47.             
  48.             base.Initialize();
  49.         }

  50.         /// <summary>
  51.         /// Allows the game component to update itself.
  52.         /// </summary>
  53.         /// <param name="gameTime">Provides a snapshot of timing values.</param>
  54.         public override void Update(GameTime gameTime)
  55.         {
  56.             // TODO: Add your update code here
  57.             if (!isDead)
  58.             {               
  59.                 age += (int)decay;
  60.                 angle += rotate;

  61.                 position.Y = age;

  62.                 if (age >= lifespan)
  63.                     isDead = true;
  64.             }

  65.             base.Update(gameTime);
  66.         }
  67.     }
  68. }
复制代码
这个和GDI+版本的不同的是,这只是几个纯粹的粒子状态记录式的粒子系统,它并不包括描绘自己的影像的部分,因为描绘影像的工作将统一由Game Object (也就是这个Program的Main Object)负责。

上面的是我的最终版本,其实我是经过几个阶段才达到这个状态的,接下来我将逐步讲解整个粒子雪花系统的发展史。
[待续]

[ 本帖最后由 geekman 于 19-6-2009 02:08 PM 编辑 ]
回复

使用道具 举报


ADVERTISEMENT

 楼主| 发表于 19-6-2009 01:07 PM | 显示全部楼层
首先我们来探讨这个粒子雪花系统的一些概念:
1)粒子需要一个定位数值,我采用了Vector2D来记录这个数据,命名为position,也就是粒子在画面上的(X,Y)值。

2)粒子需要一个前进方位,在雪花的情形下,这个数值就是单纯的向下坠落。这个我偷懒一点,直接使用粒子的年龄来代表。因为随着时间的经过,粒子会衰老,年龄增长,这正好可以用来当作粒子的Y座标。

3)粒子需要一个寿命值,这件决定粒子的存在的时限。因为我用了粒子的寿命为其Y座标,这也变相的把我的粒子寿命特性和粒子在画面的位置挂钩了,换句话说,我完全可以把粒子的存在条件设定为:当它坠落到画面的某个高度就会死去(消失)。

4)粒子的年龄 - 这个数值和粒子的衰老度挂钩,每一个循环,粒子的年龄将随着该粒子的衰老度增长(每个粒子的衰老度都不一样),直到打到寿命极限(画面里的Y座标极限),就会死去。

5)粒子的衰老度,这个数值只是为了让每个粒子都有不同存活时限,免得整个粒子系统显得太过呆板,规律化。

6)isDead 这个开关只是为了方便检测粒子是否已经死亡。

以上是粒子的基本数值,接下来是为了增加趣味性才在后来加入的数值:

7)origin 这个 Vector2D,是为了旋转和放大缩小雪花才加进去的,它是使用于旋转和缩放功能的中心点,也就是说,在选转雪花影像时,这个店就是旋转的中心点,在缩放时,这个点就是放大和缩小的中心。

8)scale负责雪花影像的放大缩小,scale=0.5f表示雪花被缩小50%,scale=2.50f代表雪花被放大250%。

9)rotate和angle代表了雪花的旋转角度和角度的速度。

10)value只是我用来调整雪花的颜色的一个数值,原本的雪花是五彩缤纷的,现在我只是用它来设定雪花的红和青色的数值。

11)depth是用来模拟雪花和镜头的距离,用来模拟景深的。

接下来是各个数值的设定和原因:
  1. Random rnum = new Random();

  2. rnum 是 Random number object,用来提取随机数值的。
复制代码
  1. position = new Vector2(rnum.Next(0, 1024), 0);

  2. 这里我使用随机数值来初始化雪花的X座标,雪花的初始Y座标永远是0。

  3. Random.Next(min, max)是用来产生随机整数,而min,max这是这个整数的最小和最大可能性,使用0,1024是因为我的画面设定为1024x768,所以雪花的X座标可以是画面宽度的任何一个位置。
复制代码
  1. origin = new Vector2(64, 64);

  2. origin是雪花影像的中心点,我采用的雪花影像大小是128x128,所以其中心点就是(64,64)
复制代码
  1. depth = rnum.Next(1, 70);

  2. 雪花的深度,随机产生。原本理想的深度为0到100,经过运算,它会化为0%~100%,并使用于缩放雪花影像的scale factor。但是为了避免0%(雪花完全看不见)以及太过巨大的雪花(128x128的确是太大了一点),所以我把min设定为1,max设定为70(也就是说雪花最大状态会被缩小30%)
复制代码
  1. scale = depth / 100f;

  2. 简单,就是把深度换成缩放的百分比。
复制代码
  1. lifespan = 720;

  2. 粒子的寿命,也就是说等雪花坠落到 Y >= 720 时就会死亡。
复制代码
  1. decay = depth / 10;

  2. 粒子的衰老度,同时也是雪花坠落的速度。远处的会坠落的比较慢,近处的会坠落的比较快,造成景深的幻像。
复制代码
  1. if (decay < 1)
  2.     decay = 1;

  3. 因为四舍五入的关系,小于0.5的衰老度会变成0,所以要预防粒子不会衰老的情况。
复制代码
  1. age = 0;

  2. 初始化粒子的年龄为0。
复制代码
  1. isDead = false;

  2. 这个很简单吧?关系着粒子的死活。
复制代码
  1. value = 255;            
  2. color = new Color(value, value, 255);

  3. 雪花的色调,改变value的数值可以改变雪花的蓝色调(蓝色被我固定在最高值)
复制代码
  1. rotate = ((float)rnum.Next(1, 100)) / 1000f;
  2. angle = 0;

  3. rotate是雪花的旋转速度,angle是雪花的当前旋转角度。这两个数值都是以raidan来计算的,而不是度数(degree)。
复制代码
[待续]
回复

使用道具 举报

 楼主| 发表于 19-6-2009 01:32 PM | 显示全部楼层
  1. public override void Update(GameTime gameTime)
  2.         {
  3.             // TODO: Add your update code here
  4.             if (!isDead)
  5.             {               
  6.                 age += (int)decay;
  7.                 angle += rotate;

  8.                 position.Y = age;

  9.                 if (age >= lifespan)
  10.                     isDead = true;
  11.             }

  12.             base.Update(gameTime);
  13.         }
复制代码
这是粒子的更新程式,很简单,首先检测粒子是否还活着,死了当然就不用理它了。还活着的话,就增加它的Y座标和旋转的角度,以及增加它的年龄。然后检测粒子是否已经老死。

粒子本身就这么简单。

接下来是Game本身:

首先,你得在Solution Explorer里面加入需要用到的图像和其他物件。

1)加入2个图像(雪花和背景)在Content项目里面(在Solution Explorer 里面 right click Content 项目,然后选择 Add Existing。。。,选择想要加入的图像,然后选择Add As Link会比较好,这样以来你在图像里面所做的修改都会直接反映在你的Content Pipeline 里面)。雪花图像我用绘图软件去底然后储存在PNG格式,因为PNG可以支援透明底色。

2)增加一个 SpriteFont 物件。方法和增加图像差不多。详情可以参考XNA说明书里关于SpriteFont的使用方法。
  1. using System;
  2. using System.Collections.Generic;
  3. using Microsoft.Xna.Framework;
  4. using Microsoft.Xna.Framework.Audio;
  5. using Microsoft.Xna.Framework.Content;
  6. using Microsoft.Xna.Framework.GamerServices;
  7. using Microsoft.Xna.Framework.Graphics;
  8. using Microsoft.Xna.Framework.Input;
  9. using Microsoft.Xna.Framework.Net;
  10. using Microsoft.Xna.Framework.Storage;

  11. namespace ParticleSnow
  12. {
  13.     /// <summary>
  14.     /// This is the main type for your game
  15.     /// </summary>
  16.     public class Game1 : Microsoft.Xna.Framework.Game
  17.     {
  18.         GraphicsDeviceManager graphics;
  19.         SpriteBatch spriteBatch;
  20.         Texture2D snowflakes, bg;
  21.         SpriteFont sfont;
  22.         List<Particles> plist;
  23.         int maxCount = 200;
  24.                         
  25.         public Game1()
  26.         {
  27.             graphics = new GraphicsDeviceManager(this);
  28.             this.graphics.PreferredBackBufferWidth = 1024;
  29.             this.graphics.PreferredBackBufferHeight = 768;
  30.             this.graphics.IsFullScreen = false;
  31.             Content.RootDirectory = "Content";
  32.         }

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

  43.             base.Initialize();
  44.         }

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

  53.             // TODO: use this.Content to load your game content here
  54.             snowflakes = Content.Load<Texture2D>("snowflake");
  55.             bg = Content.Load<Texture2D>("eratap_beach");
  56.             sfont = Content.Load<SpriteFont>("SpriteFont1");
  57.         }

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

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

  76.             // TODO: Add your update logic here
  77.             if (plist.Count < maxCount)
  78.                 plist.Add(new Particles(this));
  79.                         

  80.             for (int i = 0; i < plist.Count; i++)
  81.             {
  82.                 plist[i].Update(gameTime);
  83.                 if (plist[i].isDead)
  84.                     plist.RemoveAt(i);
  85.             }
  86.             
  87.             if(Keyboard.GetState().IsKeyDown(Keys.Escape))
  88.                 this.Exit();
  89.             base.Update(gameTime);
  90.         }

  91.         /// <summary>
  92.         /// This is called when the game should draw itself.
  93.         /// </summary>
  94.         /// <param name="gameTime">Provides a snapshot of timing values.</param>
  95.         protected override void Draw(GameTime gameTime)
  96.         {
  97.             graphics.GraphicsDevice.Clear(Color.Black);

  98.             // TODO: Add your drawing code here
  99.             
  100.             spriteBatch.Begin(SpriteBlendMode.AlphaBlend);
  101.             spriteBatch.Draw(bg, new Rectangle(0, 0, 1024, 768), Color.White);
  102.             foreach (Particles p in plist)
  103.             {
  104.                 spriteBatch.Draw(snowflakes, p.position, null, p.color, p.angle, p.origin, p.scale, SpriteEffects.None, 0);            
  105.             }
  106.             spriteBatch.DrawString(sfont, "Press ESC to Quit", Vector2.Zero, Color.White);
  107.             spriteBatch.End();            

  108.             base.Draw(gameTime);
  109.         }
  110.     }
  111. }
复制代码
以下是各个程式码的解析:
  1. Texture2D snowflakes, bg;
  2. Texture2D 是使用于 2D 状况下的图像,基本上就和Bitmap差不多。这两个Texture,一个是雪花的图像,一个是背景,背景我使用了一个从网上找来的阳光明媚的海滩照片,有点恶搞的意味,嘿嘿嘿。。。

  3. SpriteFont sfont;
  4. SpriteFont 是用来在DX画面上书写文字的,你必须在Windows的Control Panel 里面确认你的Font Type Face 的名字,然后打开SpriteFont得档案,把Font Type Face Name 填进去。

  5. List<Particles> plist;
  6. 老朋友了,用来记录粒子的list。详情请参考我的另一篇文章:http://cforum2.cari.com.my/viewthread.php?tid=1242667&extra=page%3D1

  7. int maxCount = 200;
  8. 用来限制粒子最大数量。更大的数量就会占据更多的运算资源,看着自己的电脑能力来加减吧。
复制代码
[待续]
回复

使用道具 举报

 楼主| 发表于 19-6-2009 01:47 PM | 显示全部楼层
  1. this.graphics.PreferredBackBufferWidth = 1024;
  2. this.graphics.PreferredBackBufferHeight = 768;
  3. this.graphics.IsFullScreen = false;
复制代码
接下来在Game class的constructor里面这三行只是用来设定游戏画面的大小(1024x768)。如果想要换成全画面模式,只要把IsFullScreen设定为true就可以了。
  1. protected override void LoadContent()
  2. {
  3.     // Create a new SpriteBatch, which can be used to draw textures.
  4.     spriteBatch = new SpriteBatch(GraphicsDevice);

  5.     // TODO: use this.Content to load your game content here
  6.     snowflakes = Content.Load<Texture2D>("snowflake");
  7.     bg = Content.Load<Texture2D>("eratap_beach");
  8.     sfont = Content.Load<SpriteFont>("SpriteFont1");
  9. }
复制代码
我增加的就只是后面的三行,分别是载入雪花图像,背景图像以及用来写字的文字资源。
  1. if (plist.Count < maxCount)
  2.     plist.Add(new Particles(this));
  3.                         

  4. for (int i = 0; i < plist.Count; i++)
  5. {
  6.     plist[i].Update(gameTime);
  7.     if (plist[i].isDead)
  8.         plist.RemoveAt(i);
  9. }
  10.             
  11. if(Keyboard.GetState().IsKeyDown(Keys.Escape))
  12.     this.Exit();
复制代码
游戏的Update()程式,里面首先检测粒子数量是否打到限制了?是的话就不再增加粒子,否的话就增加新的粒子。然后就是呼叫每个粒子的Update()程式,然后再检测每个粒子的状态,把死了的拖出去埋了,就这样。然后就是检测玩家是否按下了Escape键,是的话就结束游戏。

最后是Game.Draw()程式:
  1. protected override void Draw(GameTime gameTime)
  2. {
  3.     graphics.GraphicsDevice.Clear(Color.Black);

  4.     // TODO: Add your drawing code here
  5.             
  6.     spriteBatch.Begin(SpriteBlendMode.AlphaBlend);
  7.     spriteBatch.Draw(bg, new Rectangle(0, 0, 1024, 768), Color.White);
  8.     foreach (Particles p in plist)
  9.     {
  10.         spriteBatch.Draw(snowflakes, p.position, null, p.color, p.angle, p.origin, p.scale, SpriteEffects.None, 0);            
  11.     }
  12.     spriteBatch.DrawString(sfont, "Press ESC to Quit", Vector2.Zero, Color.White);
  13.     spriteBatch.End();            

  14.     base.Draw(gameTime);
  15. }
复制代码
根据每颗粒子(雪花)的状态画出相对应的影像,然后写字,就酱。
回复

使用道具 举报

 楼主| 发表于 19-6-2009 02:01 PM | 显示全部楼层
后记:由于某些不知名的原因(可能是因为Random是以时间作为种子的关系),雪花坠落会有一个特定的pattern。雪花的数量越多就越明显。

最后给大家看看:六月飞雪窦娥冤。。。

回复

使用道具 举报

发表于 22-6-2009 06:11 PM | 显示全部楼层
谢谢楼主。。我迟点试试看

最近刚开始接触XNA+C#。。才刚开始学习oop学校assignment就要制作游戏。。有点难度
回复

使用道具 举报

Follow Us
 楼主| 发表于 23-6-2009 01:31 PM | 显示全部楼层

回复 6# 千年刹 的帖子

任何关于 C#+XNA 的问题欢迎提出讨论。
回复

使用道具 举报

 楼主| 发表于 23-6-2009 01:31 PM | 显示全部楼层

回复 6# 千年刹 的帖子

任何关于 C#+XNA 的问题欢迎提出讨论。
回复

使用道具 举报


ADVERTISEMENT

 楼主| 发表于 23-6-2009 01:33 PM | 显示全部楼层
原帖由 千年刹 于 22-6-2009 06:11 PM 发表
谢谢楼主。。我迟点试试看

最近刚开始接触XNA+C#。。才刚开始学习oop学校assignment就要制作游戏。。有点难度


任何关于 C#+XNA 的问题欢迎提出讨论。
回复

使用道具 举报

 楼主| 发表于 23-6-2009 01:33 PM | 显示全部楼层
呃,严重卡贴。。。
回复

使用道具 举报

发表于 26-6-2009 12:33 AM | 显示全部楼层

回复 7# geekman 的帖子

我会的。。而且肯定会有很多

可是现在还在design阶段。。high concept都还没想好
回复

使用道具 举报

 楼主| 发表于 26-6-2009 10:30 AM | 显示全部楼层
哦,对了,如果是与粒子系统无关的,你可以另外开个[C# + XNA 问题讨论区]主题,以免跑题了。

这里我还要继续讨论粒子系统应用的(下一次更新会是3D环境的雪花/天气现象)。
回复

使用道具 举报

发表于 12-7-2009 08:59 PM | 显示全部楼层
成功~



没有font没有background,懒得弄
回复

使用道具 举报

发表于 12-7-2009 09:28 PM | 显示全部楼层
想问下lz,你的雪是直落的吗?

我把origin换去比snow图大的数值时,snow下落时会像在左右飘动,

但其实是在围着中心旋转,比较好看
回复

使用道具 举报

 楼主| 发表于 12-7-2009 10:53 PM | 显示全部楼层
恭喜成功

如果你把origin设定为大过图形,就会导致雪花围绕着图形的右下角旋转,这样一来雪花就会以螺旋状的路径向下坠落。
回复

使用道具 举报

发表于 8-9-2009 01:07 PM | 显示全部楼层
成功了



为什么我的图不能放大?


[ 本帖最后由 杨老 于 8-9-2009 01:34 PM 编辑 ]
回复

使用道具 举报


ADVERTISEMENT

 楼主| 发表于 8-9-2009 04:34 PM | 显示全部楼层
原帖由 杨老 于 8-9-2009 01:07 PM 发表
为什么我的图不能放大?
http://smg.photobucket.com/albums/v137/hxyeoh_4/Flash%20Animation ...


请详述你的问题。
回复

使用道具 举报

发表于 8-9-2009 04:46 PM | 显示全部楼层

回复 17# geekman 的帖子

我的意思是问为什么我 upload 的图不能像你们 upload 的图那样放大(在论坛里放大)。
回复

使用道具 举报

 楼主| 发表于 8-9-2009 05:24 PM | 显示全部楼层
这个论坛本身会把过大的图片缩小到适合论坛版面格局的大小的。
回复

使用道具 举报

发表于 8-9-2009 05:37 PM | 显示全部楼层
原来如此, 刚要进入你的教学 ”使用C# + Managed DIrectX 制作 2D 游戏“  
希望不会太难懂
回复

使用道具 举报

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

本版积分规则

 

ADVERTISEMENT



ADVERTISEMENT



ADVERTISEMENT

ADVERTISEMENT


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

GMT+8, 11-12-2025 09:00 AM , Processed in 0.155620 second(s), 24 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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