佳礼资讯网

 找回密码
 注册

ADVERTISEMENT

查看: 6037|回复: 48

Visual C# 简易粒子系统(Particle System)(GDI+)

[复制链接]
发表于 9-6-2008 05:49 PM | 显示全部楼层 |阅读模式
在这里和大家分享我用15分钟写出来的简易粒子系统。

什么是粒子系统(Particle System)?简单来说,就是一个用来发出粒子(Particle)的系统,而粒子的定义就是,一旦发出,将自主生存一段时间,然后消失的物件,这些物件将自己管理自己的一切活动,包括更新自己的活动,状态,在画面上的表现,最后宣告死亡,自我毁灭。粒子系统通常用来表现气候现象如雪花,雨点,烟雾等,也常被用来表现火焰,废气之类的自然现象。

粒子系统通常分为两部分:发射器(Emitter)和粒子(Particle)。发射器就是用来发射出粒子的点(不一定限制为点,在天气粒子系统里面,发射器可以是整片天空),发射器的作用是产生粒子,并把粒子加入List当中,方便系统更新粒子状态(通过呼叫粒子本身的更新函式)。在一般的粒子系统里面,发射器也兼任管理和更新粒子的工作,不过在这个例子里面,我为了简化整个系统,没把发射器给做出来(至少没有一个完整的发射器),而是借用了MouseMove Event 来取巧,当作发射器的触发点(trigger)。

[制作步骤]
首先,在VC#开一个新的 Windows Application,然后把 Form Size 设定为 800x800。然后给这个Form加入一个Timer,把 timer interval 设定为 50 ms (也就是说这个 timer 每秒触发 20 下)

然后输入以下的code:
  1. //---系统生成,不必作任何修改---
  2. using System;
  3. using System.Collections.Generic;
  4. using System.ComponentModel;
  5. using System.Data;
  6. using System.Drawing;
  7. using System.Text;
  8. using System.Windows.Forms;
  9. //---系统生成部分完毕---
  10. namespace WindowsApplication1
  11. {   
  12.     public partial class Form1 : Form
  13.     {
  14.         List<particle> plist = new List<particle>();//生成用来保管粒子的List,以下会详细解释
  15.         //---系统生成---
  16.         public Form1()
  17.         {
  18.             InitializeComponent();
  19.         }//---系统生成完毕---
  20.         //点击Form1的Event就可以找到这个MoseMove Event
  21.         private void Form1_MouseMove(object sender, MouseEventArgs e)
  22.         {
  23.             //使用 Mouse Cursor 的位子来产生粒子,并把它加入到plist里面
  24.             //由于 Form1.MousePosition 所差生的点 (Point) 是以 Screen Space
  25.             //作为根本的,也就是说(0,0)是Screen的(0,0)而不是 Form 的
  26.             //(0,0),所以要使用 PointToClient 来转换成 Form Space。
  27.             plist.Add(new particle(this.PointToClient(Form1.MousePosition)));            
  28.         }
  29.         //Timer Event,每秒触发20次
  30.         private void timer1_Tick(object sender, EventArgs e)
  31.         {
  32.             //获取 Form 的 HDC,相当于让你画图画的表面
  33.             Graphics g = Graphics.FromHwnd(this.Handle);
  34.             //填色,把整个Form 上面的旧画面去除掉,相当于Clear Screen
  35.             g.FillRectangle(Brushes.Beige, this.ClientRectangle);
  36.             g.Dispose();  // Dispose 掉之前的 HDC,不然会由 Memory leak。
  37.             
  38.            // 粒子管理,先更新每颗粒子的状态,然后检视粒子是否寿命已尽
  39.            //是的话,把已死的粒子移除掉。没死的粒子就会进行画面描绘。
  40.             for (int i = 0; i < plist.Count; i++)
  41.             {
  42.                 plist[i].update();
  43.                 if (plist[i].isDead)
  44.                 {
  45.                     plist.RemoveAt(i);
  46.                 }
  47.                 else
  48.                 {
  49.                     plist[i].draw(this.Handle);
  50.                 }
  51.             }
  52.         }
  53.     }

  54.     //粒子物件
  55.     public class particle
  56.     {
  57.         protected int lifespan; //粒子的寿命,我设定每颗粒子可存活10个触发点(0.5秒)
  58.         protected Point location; //粒子的位置
  59.         protected int size; //粒子的大小
  60.         protected int speed; //粒子的移动速度,在这个例子里面,粒子会往下掉,然后消失
  61.         public bool isDead;//决定例子是否已经死亡

  62.         //初始化粒子
  63.         public particle(Point loc)
  64.         {
  65.             lifespan = 10; //寿命,每秒触发20次,能存活10次,也就是半秒钟
  66.             location = new Point(loc.X, loc.Y); //初始的位置,也就是鼠标(发射器)的位置
  67.             speed = 20; //每次更新,粒子会往下掉20个pixels
  68.             size = 20; //我用圆形代表粒子,半径20 pixels
  69.             isDead = false; //刚开始当然还活着
  70.         }
  71.         //更新粒子状态
  72.         public void update()
  73.         {
  74.             lifespan--;  //消耗寿命
  75.             if (lifespan <= 0) //如果寿命已尽。。。
  76.             {
  77.                 isDead = true; //标示为已经死亡
  78.             }
  79.             else
  80.             {
  81.                 //没死的话就往下掉
  82.                 location.Y += speed;
  83.             }
  84.         }
  85.         //在画面上描绘出粒子
  86.         public void draw(IntPtr hwnd)
  87.         {
  88.             //再一次,抓取 Form 的描绘表面,在这里我们所收到的hwnd已经是Form
  89.             //的handle了
  90.             Graphics g = Graphics.FromHwnd(hwnd);
  91.             g.DrawEllipse(Pens.Red, location.X, location.Y, size, size);
  92.             g.Dispose(); //记得Dispose!!!
  93.         }
  94.     }
  95. }
复制代码
回复

使用道具 举报


ADVERTISEMENT

 楼主| 发表于 9-6-2008 06:01 PM | 显示全部楼层
  1. List<particle> plist = new List<particle>();
复制代码
以上这段会比较难理解,其实也不是很难,List就是一个class,用来记录/储存一系列的物件的pointers的,<Type> 是用来告诉 List,它要储存的 Pointers 是什么 Data Type, 在这里,它要储存的是 particle 这个我自行设定的 class 的 pointers 的。这个 List 的名字就叫 plist,就这样。

还有,我把粒子系统的设计改了一点:

g.DrawEllipse(Pens.Red, location.X, location.Y, size+(lifespan * 3), size+(lifespan * 3));

这会让粒子从大缩小然后消失,看起来比较有趣。
回复

使用道具 举报

 楼主| 发表于 9-6-2008 07:32 PM | 显示全部楼层
再度作了些更改,现在加上了颜色,透明度,以及颜色变化,让粒子看起来有点像火焰的感觉。。。

另外,由于刚才在短时间里面做出来,没有注意到原来我可以在MouseEventArg 里面找到直接对应Form Space的鼠标位置,使用MouseEventArg里的位置就不必使用PointToClient()了。
  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.Data;
  5. using System.Drawing;
  6. using System.Text;
  7. using System.Windows.Forms;

  8. namespace WindowsApplication1
  9. {
  10.     public partial class Form1 : Form
  11.     {
  12.         List<particle> plist = new List<particle>();
  13.         public Form1()
  14.         {
  15.             InitializeComponent();
  16.         }
  17.                
  18.         private void timer1_Tick_1(object sender, EventArgs e)
  19.         {
  20.             Graphics g = Graphics.FromHwnd(this.Handle);
  21.             
  22.             g.FillRectangle(Brushes.Black, this.ClientRectangle);
  23.             g.Dispose();  
  24.            
  25.             for (int i = 0; i < plist.Count; i++)
  26.             {
  27.                 plist[i].update();
  28.                 if (plist[i].isDead)
  29.                 {
  30.                     plist.RemoveAt(i);
  31.                 }
  32.                 else
  33.                 {
  34.                     plist[i].draw(this.Handle);
  35.                 }
  36.             }
  37.         }

  38.         private void Form1_MouseMove_1(object sender, MouseEventArgs e)
  39.         {
  40.             plist.Add(new particle(new Point(e.X, e.Y)));
  41.         }
  42.     }

  43.    
  44.     public class particle
  45.     {
  46.         protected int lifespan;  
  47.         protected Point location;
  48.         protected int size;
  49.         protected int speed;
  50.         public bool isDead;
  51.         protected int counter;

  52.         public particle(Point loc)
  53.         {
  54.             lifespan = 10;
  55.             location = new Point(loc.X, loc.Y);
  56.             speed = 10;
  57.             size = 3;
  58.             counter = 0;
  59.             isDead = false;
  60.         }

  61.         public void update()
  62.         {
  63.             lifespan--;  
  64.             if (lifespan <= 0)
  65.             {
  66.                 isDead = true;
  67.             }
  68.             else
  69.             {
  70.                 location.Y -= speed;
  71.             }
  72.             counter++;
  73.         }
  74.         
  75.         public void draw(IntPtr hwnd)
  76.         {
  77.             Graphics g = Graphics.FromHwnd(hwnd);
  78.             int d = size + (lifespan * 5);
  79.             SolidBrush sb = new SolidBrush(Color.FromArgb(0xFF - (25 * counter), 0xFF, 0xFF - (25 * counter), 0x00));
  80.             g.FillEllipse(sb, location.X, location.Y, d, d);
  81.             g.Dispose();
  82.         }
  83.     }
  84. }
复制代码
附上图片:


[ 本帖最后由 geekman 于 10-6-2008 11:14 AM 编辑 ]
回复

使用道具 举报

发表于 21-6-2008 05:32 PM | 显示全部楼层
请问楼主从事什么行业的?
回复

使用道具 举报

 楼主| 发表于 22-6-2008 10:51 PM | 显示全部楼层
我是programmer/software designer(因为小公司嘛,就只好自己设计,自己编写啰)
回复

使用道具 举报

发表于 25-6-2008 02:52 PM | 显示全部楼层
期待楼主可以出 opengl 和 directx 版的 particle。
回复

使用道具 举报

Follow Us
 楼主| 发表于 25-6-2008 04:47 PM | 显示全部楼层
其实要把这个粒子系统转换成 DX 或者 OGL 并不会太困难,参考我的另一个主题:使用C# + Managed DIrectX 制作 2D 游戏
http://cforum.cari.com.my/viewth ... &extra=page%3D1

就可以做到。。。(我没有研究 OpenGL,所以无法提供 OGL 的例子)

如果有什么问题的话欢迎大家一起来讨论。
回复

使用道具 举报

发表于 28-6-2008 11:51 AM | 显示全部楼层
其实我不清楚什么叫 managed directx。。。 如果没有 managed 的 c# directx 会是怎样的?如果 加入XNA 又会如何的方便?

有例子的话就一幕了然了。。。
回复

使用道具 举报


ADVERTISEMENT

 楼主| 发表于 28-6-2008 12:11 PM | 显示全部楼层
managed DX 的特色是,它的各种资源都会被自动管理,例如在MDX里面,你不必担心所有DX interface 的 destruction/release,因为这些动作都由 Manager 自行管理。另外,在managed 的环境下,许多的DirectX 指令都被简化,例如当你需要 create 一个 D3D Device时,你只需要把PresentParameters里的数值设定好,然后new 一个Direct3D.device 就可以了,在unmanaged的环境下,你得先create一个DX object,然后通过该object query 一个D3D interface,然才能呼叫CreateDevice来制造一个D3DDevice,而且你还得照顾每一个步骤可能发生的error,十分繁复。当你关闭你的游戏时,你还得记得Release() 你所制造的所有DirectX resources(surface,texture,vertex buffer, etc...)

XNA 算是 MDX 的完整版(MDX 可以算是 XNA 的过渡时期产品),它也是以 MDX 为基础发展出来的,拥有更多的功能,更进一步的简化了各种指令(通过 XNA Framework)。XNA 的缺点是,它要求至少可以支援 Shader Model 1.1的显示卡。

我会写一个完整的 MDX 版的粒子系统的,别担心,不过得给我一点时间。。。

[ 本帖最后由 geekman 于 28-6-2008 12:13 PM 编辑 ]
回复

使用道具 举报

发表于 14-7-2008 02:24 AM | 显示全部楼层

回复 3# geekman 的帖子

不太熟悉visual studio 2005....
请问该如何triger mouse move event呢?
toolbox哪里找不到...
回复

使用道具 举报

 楼主| 发表于 14-7-2008 10:36 AM | 显示全部楼层
mouse move event 并不是 tools 来的, 它是隶属于各种 Windows Object (例如 Forms, Buttons, Text Box 之类)的一个事件处理程式(Event Handler)。在这里我使用的是隶属于Form 的 (private void Form1_MouseMove_1(object sender, MouseEventArgs e)) mouse move event。

在 Visual C# 里面,要使用Form的event,你可以先在 Solution Explorer (通常在当你开启了一个windows application 的 project 之后,会出现在画面右方)选择了你的program (在这个例子里面是 Form1.cs),然后点滑鼠右键,选择view designer,就会显示 Form 的设计界面,选择Form,点右键选择 property,solution explorer 底下就会显示 Form 的 property,在property的视窗里面的上端有一个toolbar,有一系列的button,当中第4个,呈现一个闪电状的图案的,就是Event List了,你可以在里面找到所有跟Form有关的Event。对任何一个Event的右边的空白格子double click就会开启一个空的event handler,你可以在里面输入任何你想在该event 发生时想要做的事情。

mouse event 的 triggering 是自动的,只要该object(例如 Form)上有任何滑鼠的活动,就会被触发。

[ 本帖最后由 geekman 于 14-7-2008 10:38 AM 编辑 ]
回复

使用道具 举报

发表于 9-9-2008 11:44 AM | 显示全部楼层
新手问路
我也找不到MouseMove。。。请指教
我用SharpDevelop



[ 本帖最后由 cskoom 于 9-9-2008 12:05 PM 编辑 ]
回复

使用道具 举报

 楼主| 发表于 9-9-2008 01:16 PM | 显示全部楼层
你的图太小了,也不是连接,我看不清楚你的图片。。。

我用的是Visual C# 2005 Express。请参考下图:


[ 本帖最后由 geekman 于 9-9-2008 01:18 PM 编辑 ]
回复

使用道具 举报

发表于 11-9-2008 12:29 AM | 显示全部楼层
不好意思 刚学习编程,我不是做IT这行,之前学习HTML然后到ASP。NET,得知C# 可以做很多东西,有兴趣学,得知SharpDevelop是免费的IDE,才刚学到HelloWorld ,有很多东西都不明白。。抱歉

我找到了MouseMove
我把private void Form1_MouseMove(object sender, MouseEventArgs e以下CODE全都COPY进去,DEBUG没问题,跑出的画面上空白的,请指教。。
回复

使用道具 举报

发表于 11-9-2008 12:38 AM | 显示全部楼层
补充。。我学习的是最顶那个源码。。

还有个问题。。。我在网上找到的C#源码直接COPY进去都很难实现,都出错。。
是不是要如你所说到要经过event————》出了XXXXXX{  },然后把CODE放进括弧里,请指教
回复

使用道具 举报

 楼主| 发表于 11-9-2008 01:33 AM | 显示全部楼层
Visual C# 2005 Express 也是免费的,写出来的游戏/软件也都可以自由的用作商业用途,而且更能和XNA Game Studio (同样也是免费的游戏开发框架/工具)配合,令得游戏开发的工作更轻松一些。如果你还没决定要使用哪个IDE,不妨去下载 VC# 2005 Express 试试看,毕竟是 Microsoft 出品,和 Windows 以及 DirectX 的配合度也比较优良。XNA Game Studio 更是能够开发游戏在 XBox 360 上游玩的呢。如果你打算使用 VC# 2005 Express,我可以把我的project source code 寄给你作测试用。

至于你的program无法显示。。。你是copy我的哪一个版本的source code? 如果是第一个版本(有华文注解的那个),在MouseMove event之后还有一个Timer event,你必须从 toolbox 里面 drag & drop 一个 timer tool,然后再如同 MouseMove event 那样从那个timer 的event list 里面创建你的 private void timer1_Tick(object sender, EventArgs e),而不是直接 copy & paste,不然 C# 的 Manager 并不知道那个function 是对应 Timer 的。这是 Managed code 的一点小麻烦。既然 Manager 并没有把那个 function 和 timer event 挂钩,自然就没有进入粒子更新和描绘程序了。
回复

使用道具 举报


ADVERTISEMENT

发表于 11-9-2008 12:30 PM | 显示全部楼层
timer tool我直接点2下生成了Timer event ...
MouseMove event我是按照你的图,按闪电-----》MouseMove点2下生成的,是这样吗?

顶端的using XXXXX , copy & paste

List<particle> plist = new List<particle>();//生成用来保管粒子的List,不是系统生成的吗??这段我直接copy & paste

public class particle 和 public void draw(IntPtr hwnd)怎生成出来的

我刚安装了Visual C# 2005 Express。。研究中。。

project source code 麻烦你了,我的cskoom@gmail.com
回复

使用道具 举报

 楼主| 发表于 11-9-2008 01:13 PM | 显示全部楼层
source code 已经寄给你了。这个 project 必须放在你的 My Documents\Visual Studio 2005\Projects 里面的。

你生成event functions的方法是正确的。

public class particle 和 public void draw(IntPtr hwnd) 都是自己写出来的,这些部分应该可以使用 copy & paste。

要注意 public void draw(IntPtr hwnd) 是 particle class 的 method,是 particle class 的一份子。

[ 本帖最后由 geekman 于 11-9-2008 01:21 PM 编辑 ]
回复

使用道具 举报

发表于 11-9-2008 04:49 PM | 显示全部楼层
没收到。。麻烦你了,。。。。
回复

使用道具 举报

 楼主| 发表于 11-9-2008 05:25 PM | 显示全部楼层
已经发出第二次电邮。

p/s: hotmail 回复说无法发出 email 到你提供的地址,请确定你提供的地址正确。

[ 本帖最后由 geekman 于 11-9-2008 05:28 PM 编辑 ]
回复

使用道具 举报

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

本版积分规则

 

ADVERTISEMENT



ADVERTISEMENT



ADVERTISEMENT

ADVERTISEMENT


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

GMT+8, 7-12-2025 04:16 AM , Processed in 0.138596 second(s), 24 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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