|
查看: 6037|回复: 48
|
Visual C# 简易粒子系统(Particle System)(GDI+)
[复制链接]
|
|
|
在这里和大家分享我用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:- //---系统生成,不必作任何修改---
- using System;
- using System.Collections.Generic;
- using System.ComponentModel;
- using System.Data;
- using System.Drawing;
- using System.Text;
- using System.Windows.Forms;
- //---系统生成部分完毕---
- namespace WindowsApplication1
- {
- public partial class Form1 : Form
- {
- List<particle> plist = new List<particle>();//生成用来保管粒子的List,以下会详细解释
- //---系统生成---
- public Form1()
- {
- InitializeComponent();
- }//---系统生成完毕---
- //点击Form1的Event就可以找到这个MoseMove Event
- private void Form1_MouseMove(object sender, MouseEventArgs e)
- {
- //使用 Mouse Cursor 的位子来产生粒子,并把它加入到plist里面
- //由于 Form1.MousePosition 所差生的点 (Point) 是以 Screen Space
- //作为根本的,也就是说(0,0)是Screen的(0,0)而不是 Form 的
- //(0,0),所以要使用 PointToClient 来转换成 Form Space。
- plist.Add(new particle(this.PointToClient(Form1.MousePosition)));
- }
- //Timer Event,每秒触发20次
- private void timer1_Tick(object sender, EventArgs e)
- {
- //获取 Form 的 HDC,相当于让你画图画的表面
- Graphics g = Graphics.FromHwnd(this.Handle);
- //填色,把整个Form 上面的旧画面去除掉,相当于Clear Screen
- g.FillRectangle(Brushes.Beige, this.ClientRectangle);
- g.Dispose(); // Dispose 掉之前的 HDC,不然会由 Memory leak。
-
- // 粒子管理,先更新每颗粒子的状态,然后检视粒子是否寿命已尽
- //是的话,把已死的粒子移除掉。没死的粒子就会进行画面描绘。
- for (int i = 0; i < plist.Count; i++)
- {
- plist[i].update();
- if (plist[i].isDead)
- {
- plist.RemoveAt(i);
- }
- else
- {
- plist[i].draw(this.Handle);
- }
- }
- }
- }
- //粒子物件
- public class particle
- {
- protected int lifespan; //粒子的寿命,我设定每颗粒子可存活10个触发点(0.5秒)
- protected Point location; //粒子的位置
- protected int size; //粒子的大小
- protected int speed; //粒子的移动速度,在这个例子里面,粒子会往下掉,然后消失
- public bool isDead;//决定例子是否已经死亡
- //初始化粒子
- public particle(Point loc)
- {
- lifespan = 10; //寿命,每秒触发20次,能存活10次,也就是半秒钟
- location = new Point(loc.X, loc.Y); //初始的位置,也就是鼠标(发射器)的位置
- speed = 20; //每次更新,粒子会往下掉20个pixels
- size = 20; //我用圆形代表粒子,半径20 pixels
- isDead = false; //刚开始当然还活着
- }
- //更新粒子状态
- public void update()
- {
- lifespan--; //消耗寿命
- if (lifespan <= 0) //如果寿命已尽。。。
- {
- isDead = true; //标示为已经死亡
- }
- else
- {
- //没死的话就往下掉
- location.Y += speed;
- }
- }
- //在画面上描绘出粒子
- public void draw(IntPtr hwnd)
- {
- //再一次,抓取 Form 的描绘表面,在这里我们所收到的hwnd已经是Form
- //的handle了
- Graphics g = Graphics.FromHwnd(hwnd);
- g.DrawEllipse(Pens.Red, location.X, location.Y, size, size);
- g.Dispose(); //记得Dispose!!!
- }
- }
- }
复制代码 |
|
|
|
|
|
|
|
|
|
|

楼主 |
发表于 9-6-2008 06:01 PM
|
显示全部楼层
- 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()了。- using System;
- using System.Collections.Generic;
- using System.ComponentModel;
- using System.Data;
- using System.Drawing;
- using System.Text;
- using System.Windows.Forms;
- namespace WindowsApplication1
- {
- public partial class Form1 : Form
- {
- List<particle> plist = new List<particle>();
- public Form1()
- {
- InitializeComponent();
- }
-
- private void timer1_Tick_1(object sender, EventArgs e)
- {
- Graphics g = Graphics.FromHwnd(this.Handle);
-
- g.FillRectangle(Brushes.Black, this.ClientRectangle);
- g.Dispose();
-
- for (int i = 0; i < plist.Count; i++)
- {
- plist[i].update();
- if (plist[i].isDead)
- {
- plist.RemoveAt(i);
- }
- else
- {
- plist[i].draw(this.Handle);
- }
- }
- }
- private void Form1_MouseMove_1(object sender, MouseEventArgs e)
- {
- plist.Add(new particle(new Point(e.X, e.Y)));
- }
- }
-
- public class particle
- {
- protected int lifespan;
- protected Point location;
- protected int size;
- protected int speed;
- public bool isDead;
- protected int counter;
- public particle(Point loc)
- {
- lifespan = 10;
- location = new Point(loc.X, loc.Y);
- speed = 10;
- size = 3;
- counter = 0;
- isDead = false;
- }
- public void update()
- {
- lifespan--;
- if (lifespan <= 0)
- {
- isDead = true;
- }
- else
- {
- location.Y -= speed;
- }
- counter++;
- }
-
- public void draw(IntPtr hwnd)
- {
- Graphics g = Graphics.FromHwnd(hwnd);
- int d = size + (lifespan * 5);
- SolidBrush sb = new SolidBrush(Color.FromArgb(0xFF - (25 * counter), 0xFF, 0xFF - (25 * counter), 0x00));
- g.FillEllipse(sb, location.X, location.Y, d, d);
- g.Dispose();
- }
- }
- }
复制代码 附上图片:

[ 本帖最后由 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。 |
|
|
|
|
|
|
|
|
|
|

楼主 |
发表于 25-6-2008 04:47 PM
|
显示全部楼层
|
|
|
|
|
|
|
|
|
|
发表于 28-6-2008 11:51 AM
|
显示全部楼层
其实我不清楚什么叫 managed directx。。。 如果没有 managed 的 c# directx 会是怎样的?如果 加入XNA 又会如何的方便?
有例子的话就一幕了然了。。。 |
|
|
|
|
|
|
|
|
|
|

楼主 |
发表于 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
|
显示全部楼层
|
|
|
|
|
|
|
|
|
|
发表于 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 挂钩,自然就没有进入粒子更新和描绘程序了。 |
|
|
|
|
|
|
|
|
|
|
发表于 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 编辑 ] |
|
|
|
|
|
|
|
|
| |
本周最热论坛帖子
|