littledemon در حال رشد
تاريخ عضويت: جمعه 31 فروردين 1386 تعداد ارسالها: 86 محل سكونت: زیر بام کلبه ای در غرب این ملک غریب
4 شنبه 25 خرداد 1390 - 18:44 |
|
|
به نام خدا که هر چه دارم از اوست
سری مقالات سیستم های ذره ای دو بعدی در XNA
قسمت اول : مقدمه ای بر سیستم ذره ای
سخنی با شما:
لازم به ذکر است تمامی تعاریف و تئوری های موجود در این سری مقالات برگرفته از نظرات و تفکرات شخصی بوده ، لذا چنانچه هرگونه نقص در نگارش یا تشریح مطلبی مشاهده کردید جهت تصحیح این مقاله ما را نیز مطلع نمائید .
شاید شما هم در طبیعت هنگام مشاهده بارش باران ،رخ دادن یک انفجار ، آتش و . . . به این فکر افتاده باشید که چگونه من میتوانم چنین چیزی را شبیه سازی کنم ؟
سیستم ذره ای یا Particle System به یکسری از دستورالعمل های یکپارچه گفته میشود که به ترتیب وظیفه تولید ، کنترل و از بین بردن ذراتی را دارد که تعداد ، نوع ، عمر، موقعیت و اندازه مشخصی دارند. برای این که سیستم ذره ای شکل بگیرد این اشیاء باید از حالت شی بیرون بیایند ، چرا که ما میخواهیم دیگر شی نداشته باشیم. حال سوال این است که چرا ما باید شی را از حالت خود بیرون بیاوریم و سیستم ذره ای را بوجود بیاوریم ؟
در جواب باید گفت ابتدا یک سیستم مانند آتش را در نظر بگیرید ، برای درک بهتر آتش را خرد کنید یعنی آتش تشکیل شده از ذرات بسیار ریزی که هم رنگ دارند و هم مکان (خصوصیات دیگر مورد بحث ما نیست) زمانی که ما میخواهیم یک سیستم ذره ای مثل انفجار را بوجود بیاوریم مسلما باید از تعداد زیادی ذره آتش استفاده کنیم این ذرات از مرکز وقوع انفجار تولید میشود و به اطراف پرتاب میشود و بعد از گذشت زمان مشخصی مثلا 5 ثانیه ذرات آتش به دلای معلوم با ذرات هوا ترکیب میشوند ، پس ما ابتدا حالتی مانند دود مشاهده میکنیم و بعد از آن دیگر ذرات آتش را نمی بینیم چرا که هر ذره تغییر رنگ میدهد و ما این تغییر رنگ را به شکل آتش و دود و در آخر محو شدن می بینیم . ما تا همین جای کار را فعلا نیاز داریم .
اگر متن بالا را چند بار با دقت بخوانیم میتوانیم موارد زیر را از آن استخراج کنیم :
- هر ذره آتش دارای رنگ است.
- هر ذره آتش دارای شکل است.
- هر ذره آتش دارای مکان است.
- هر ذره آتش دارای عمر است.(پس از چند ثانیه تمام ذرات ناپدید میشوند ، اصطلاحا میمیرند )
خوب پس خصوصیات اصلی ذرات را مشخص کردیم و به درک پایه ای از سیستم ذره ای پیدا کردیم.
البته باید توجه داشته باشیم که با در نظر گرفتن نوع سیستم ذره ای مورد نیازمان این خصوصیات را مشخص کنیم . مثلا در تعریف یک سیستم ذره ای برای بارش برف پارامترهایی که در نظر گرفتیم فرق میکند .با جمع بندی مطالب بالا میتوان به این نتیجه رسید که انواع سیستم ذره ای از نظر زمان کاری به شرح زیر است:
* سیستم های بر پایه شلیک
نحوه عمل این سیستم ها مانند شلیک اسلحه است،به این گونه که در زمان خاصی ، تعدادی ذره تولید شده و از بین میروند ، ممکن است این عمل ممکن است تنها یکبار انجام شود یا با فاصله زمانی مشخصی تکرار شود . یا حتی برای تولید ذرات ممکن است نیاز به تحریک چیزی مانند چاشنی داشته باشیم .برای مثال دود حاصل از شلیک یک توپ را در نظر بگیرید که به صورت مداوم در حال شلیک است ،بعد از هرچند دقیقه یک گلوله بارگذاری میشود و شلیکی انجام میشود .
* سیستم های بر پایه حلقه
نحوه عمل این سیستم ها به صورت یک حلقه مداوم است.تا زمانی که حلقه فعال است به صورت مداوم ذراتی تولید می شوند و در زمان معین از بین می روند و ذرات جدیدی جای آنها را می گیرند. مانند یک سیستم فواره آب یا آبشار که دائما در حال فعالیت هستند ، پس این حلقه میتواند بینهایت باشد یا با نهایت .
قسمت دوم: پیاده سازی ذره
طرحی که در ذهن خود آوردیم نیاز به یک پیاه سازی اولیه دارد، تا بتوان عیب هایش را رفع کرد و آن را از دید کاربر مورد بررسی قرار داد .بدین منظور اولین قدم ، پیاده سازی کلاسی به نام Particle برای تعریف ذره است تا بعدا بتوانیم با استفاده از یک موتور این ذرات را تولید کنیم.
ابتدا یک کلاس عمومی به نام Particle ایجاد کنید . سپس حوزه فعالیت کلاس را public (عمومی) تعریف کنید تا در کلاس های دیگر بتوان به راحتی از این کلاس استفاده کرد.
كد: |
public class Particle |
توجه داشته باشید که فضاهای نام مربوط به چارچوب Xna را برای کلاس تعریف کنید تا بتوانید از توابع آماده و بدنه اصلی xna استفاده کنید .
كد: |
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics; |
حال نوبت تعریف خاصیت های کلاس رسید ، برای این کار ما تمام خصوصیات یک ذره را به صورت زیر به سیستم معرفی می کنیم .
كد: |
public Texture2D Texture { get; set; }
public Vector2 Position { get; set; }
public Vector2 Velocity { get; set; }
public float Angle { get; set; }
public float AngularVelocity { get; set; }
public Color Color { get; set; }
public float Size { get; set; }
public int TTL { get; set; } |
Texture : تصویری که کاربر به عنوان ذره می بیند . این ذره میتواند تصویر قطره آب باشد یا یک جرقه یا هر چیز دیگری که شما میخواهید.
Position:موقعیت مکانی ذره که برای حرک ذره مورد نیاز است .
Velocity :سرعت حرکت ذره ، که میتواند در حالات مختلف متفاوت باشد .مثلا در انفجار ، سرعت پاشیدن ذرات بسیار سریع است ولی در یک فواره سرعت پرتاب ذرات به هوا معمولی است .
Angle : زاویه چرخش ذره که در مواردی (آتش بازی و . . .)نیاز به تغییر آن دارید.
AngularVelocity :سرعت چرخش ذره. شاید چندان کاربردی به نظر نرسد ولی در بعضی از جلوه ها نیاز دارید تا برای زیباتر شدن نتیجه ،سرعت چرخش ذرات را زیاد کنید.
Color :رنگ ذره که یکی از خصوصیات اصلی و پر کار برد است.
Size :اندازه ذره که در بعضی جلوه ها نیاز دارید آن را تغییر دهید .(دود، انفجار و . . .)
TTL :تقریبا میتوان گفت کلیدی ترین خصوصیت هر ذره عمر آن است .همانطور که قبلا اشاره کردیم بسته به نوع سیستمی که انتخاب میکنید عمر ذرات باید متفاوت باشد .این خصوصیت مخفف کلمات
Time To Live می باشد (اصطلاحا : مدت زندگی) .
زمانی که یک ذره تولید میشود باید خصوصیات اصلی آن مقدار دهی شود اگر نه با صحنه ای فوق العاده دور از انتظار مواجه خواهیم شد . برای مثال یک فواره را در نظر بگیرید،ذرات آب از لوله فواره بیرون می آیند (لحظه تولید ذره) و به بالا پرتاب میشوندو بر اثر گرانش زمین به سمت پائین کشیده میشوند و سرانجام وارد آن حوض آب میشوند(لحظه مرگ ذره). اگر زمان تولید ذرات موقعیت مکانی آن ها مشخص نباشد .چه چیزی پیش می آید ؟ هر ذره به جای این که از لوله فواره بیرون بیاید از گوشه صفحه سر در بیاورد . این منظره جالبی نیست ! پس خصوصیات هر ذره را موقع تولید مشخص میکنیم و این کار با مقدار دهی خصوصیات در متد سازنده کلاس particle صورت میگیرد:
كد: |
public Particle(Texture2D texture, Vector2 position, Vector2 velocity,
float angle, float angularVelocity, Color color, float size, int ttl)
{
Texture = texture;
Position = position;
Velocity = velocity;
Angle = angle;
AngularVelocity = angularVelocity;
Color = color;
Size = size;
TTL = ttl;
} |
بنابراین هر زمان که ذره ای ساخته شود بر اساس مقادیر ورودی، تمام خصوصیات آن مقدار میگیرند که مقدار دهی این خصوصیات وظیفه موتور تولید کننده ذرات است و بعدا درمورد آن بحث میکنیم.
حال نوبت متد های اصلی می رسد ، یعنی Update و Draw :
كد: |
public void Update()
{
TTL--;
Position += Velocity;
Angle += AngularVelocity;
} |
در متد Update ما موقتا کدهایی قراردادیم که بعدا با تنوع سیستم های ذره ای ممکن است مجبور ب تغییر آن ها بشویم. هر لحظه باید عمر ذره کم شود ، به سمت مشخصی و با سرعتی خاص حرکت کند و چرخشی داشته باشد که سرعت آن را نیز معین کرده ایم .
كد: |
public void Draw(SpriteBatch spriteBatch)
{
Rectangle sourceRectangle = new Rectangle(0, 0, Texture.Width, Texture.Height);
Vector2 origin = new Vector2(Texture.Width / 2, Texture.Height / 2);
spriteBatch.Draw(Texture, Position, sourceRectangle, Color,
Angle, origin, Size, SpriteEffects.None, 0f);
}
|
در متد Draw هم همانطور که پیداست ابتدا در sourceRectangle وضعیت تصویر ذره را مشخص کرده ایم . سپس با استفاده از origin نقطه ای که گردش باید دور آن انجام شود ، مشخص شده و در آخر هم که توسطspriteBatch.Draw ذره را رسم کردیم.
کد نهایی شما در کلاس Particle باید شبیه زیر باشد:
كد: |
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace ParticleSystems{
public class Particle
{
public Texture2D Texture { get; set; }
public Vector2 Position { get; set; }
public Vector2 Velocity { get; set; }
public float Angle { get; set; }
public float AngularVelocity { get; set; }
public Color Color { get; set; }
public float Size { get; set; }
public int TTL { get; set; }
public Particle(Texture2D texture, Vector2 position, Vector2 velocity,
float angle, float angularVelocity, Color color, float size, int ttl)
{
Texture = texture;
Position = position;
Velocity = velocity;
Angle = angle;
AngularVelocity = angularVelocity;
Color = color;
Size = size;
TTL = ttl;
}
public void Update()
{
TTL--;
Position += Velocity;
Angle += AngularVelocity;
}
public void Draw(SpriteBatch spriteBatch)
{
Rectangle sourceRectangle = new Rectangle(0, 0, Texture.Width, Texture.Height);
Vector2 origin = new Vector2(Texture.Width / 2, Texture.Height / 2);
spriteBatch.Draw(Texture, Position, sourceRectangle, Color,
Angle, origin, Size, SpriteEffects.None, 0f);
}
}
}
|
که البته در قسمت namespace ParticleSystems شما باید نام پروژه خود را قرار دهید .در اینجا نام پروژه ما ParticleSystems بوده است.
قسمت سوم : پیاده سازی موتور مولد
ما الان یک ذره را شبیه سازی کردیم اما آیا این برای ما میتواند در حد یک سیستم ذره ای کامل مانند باران یا آتش کار کند ؟مثل این است که ما یک قطره آب داشته باشیم و انتظارمان این باشد که بتوانیم باران از آن ایجاد کنیم .پس ما نیاز به یک موتور مولد ذره داریم ، موتوری که به صورت مداوم بتواند این ذرات را تولید کند. پس کلاس جدیدی به نام ParticleEngine ایجاد میکنیم .البته یادتان باشد که فضاهای نام زیر را قبل از نام کلاس تعریف کنید.
كد: |
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
|
کلاس را از نوع public تعریف میکنیم :
كد: |
public class ParticleEngine
{
|
خصوصیات زیر را تعریف میکنیم ، این خصوصیات در واقع بدنه اصلی کلاس را تشکیل میدهند.
كد: |
private Random random;
public Vector2 EmitterLocation { get; set; }
private List<Particle> particles;
private List<Texture2D> textures;
|
random : استفاده این متغیر تصادفی یکی از نیاز های اصلی است چرا که ما برای هر خاصیت تصادفی مانند رنگ ، موقعیت ،سرعت و . . . به این نوع داده ها نیاز داریم.
EmitterLocation : موقعیت نقطه ای که قرار است ذرات از انجا تولید شوند .موقعیت این نقطه میتواند مطلق یا نسبی باشد به این معنا که ممکن است مثلا سیستم ذره ا شما وظیفه تولید آتش موشک جت را بر عهده داشته باشد . پس موقعیت سیستم نمی تواند یک جای ثابت باشد و مسلما باید به انتهای محفظه سوخت موشک متصل باشد که به این مورد موقعیت نسبی گوئیم یعنی نسبت به یک شی دیگر موقعیت خود را تنظیم میکند . اما مثلا گدازه هایی که از دهانه یک اتش فشان بیرون می آید دارای موقعیت مطلق است که تنها به یک نقطه ثابت اشاره میکند و آن هم دهانه آتشفشان است.
Particles : لیستی از ذراتی که باید توسط موتور تولید شوند در اینجا ذخیره میشود.
Textures : لیستی از تصاویری که باید برای ذرات استفاده گردد در اینجا ذخیره میگردد.
متد سازنده موتور مول ما هم باید چیزی شبیه زیر باشد ، البته قبلا طرز کار متد سازنده اشاره کردیم.
كد: |
public ParticleEngine(List<Texture2D> textures, Vector2 location)
{
EmitterLocation = location;
this.textures = textures;
this.particles = new List<Particle>();
random = new Random();
}
|
تقریبا تمامی خطوط واضح هستند و نیازی به توضیح نیست. البته شاید در مورد آرگومان ورودی اول یعنی textures ابهامی وجود داشته باشد که توضیحی مختصر ارائه میکنیم. زمان ساخته شدن شی موتور مولد ، لیستی از چند تصویر ذره باید به شی معرفی کنیم ، مثلا 4 تصویر مختلف از یک قطره آب را فرض کنید (بیشتر برای تنوع استفاده میشود).زمانی که لیست این 4 ذره به شی موتور مول تحویل داده شد در هربار تولید ذره ، یک تصویر تصادفی از بین 4 تصویر انتخاب میکند و در نتیجه شما تعداد از ذرات با شکل متنوع را مشاهده میکنید . و اما متد update که باید به صورت زیر پیاده سازی شود .
كد: |
public void Update()
{
int total = 5;
for (int i = 0; i < total; i++)
{
particles.Add(GenerateNewParticle());
}
for (int particle = 0; particle < particles.Count; particle++)
{
particles[particle].Update();
if (particles[particle].TTL <= 0)
{
particles.RemoveAt(particle);
particle--;
}
}
}
|
به صورت کلی 2 حلقه داریم . در حلقه اول ذرات توسط تابع GenerateNewParticle تولید میشوند ، یعنی در هربار فراخوانی update حلقه اجرا شده و 5 ذره تولید میکند. حال اگر به فرض مثال متد update در هر ثانیه 30 بار صدا زده شود در هر ثانیه ما 5*30 ذره یعنی 150 ذره داریم !
تابع دوم به ازائ تمام ذرات موجود در لیست یک بار اجرا میشود و وظیفه آن صدا زدن متد update هر ذره است . در قسمت قبل اشاره کردیم که متد update یک ذره چه کاری انجام میدهد.اما فعلا جهت یادآوری باید گفت که هر بار که متد update یک ذره اجرا شود در واقع موقعیت مکانی آن تغییر میکند و مهمتر از آن از عمر ذره کم می شود . پس شرط درون حلقه چک میکند که آیا ذره فعلی عمرش تمام شده یا نه ؟ اگر عمر ذره کمتر یا برابر صفر باشد ذره از لیست حذف میشود تا دیگر پردازشی روی آن صورت نگیرد .
و اما تابع GenerateNewParticle که وظیفه ساخت یک ذره را دارد .
كد: |
private Particle GenerateNewParticle()
{
Texture2D texture = textures[random.Next(textures.Count)];
Vector2 position = EmitterLocation;
Vector2 velocity = new Vector2(
1f * (float)(random.NextDouble() * 2 - 1),
1f * (float)(random.NextDouble() * 2 - 1));
float angle = 0;
float angularVelocity = 0.1f * (float)(random.NextDouble() * 2 - 1);
Color color = new Color(255f, (float)random.NextDouble(), 0f);
float size = (float)random.NextDouble();
int ttl = 20 + random.Next(40);
return new Particle(texture, position, velocity, angle, angularVelocity, color, size, ttl);
} |
این تابع ابتدا یک تصویر تصادفی از لیست تصاویر ذرات انتخاب میکند ، سپس موقعیت مکانی ذره را در جایی تنظیم میکند که موتور مولد قرار دارد پس هر ذره از جایی تولید میشود که موتور مستقر است . بعد از آن سرعت و جهت حرکت ذره را به صورت تصادفی انتخاب میکند تا هر ذره به یک سمت رها شود . در اینجا زاویه چرخش ذرات برابر صفر بوده و سرعت تغییر زاویه هم یک مقدار تصادفی تنظیم شده و در ادامه رنگ ذره هم به صورت تصادفی انتخاب میشود ، البته براساس استانداردRGB که نگاه کنیم میبینیم که مقدار B برابر صفر است به این معنا که رنگ تصادفی ما باید فاقد رنگ آبی باشد . بعد از آن اندازه ذره به صورت تصادفی انتخاب میشود و عمر ذره هم مقدار ثابت 20 به علاوه یک مقدار تصادفی بین 1 تا 40 هست و در آخر هم که ذره ای با تمام مشخصات بالا تولید میشود و به عنوان خروجی تابع باز گردانده میشود.
و اما متد draw که متد نهایی و اصلی جهت رسم ذرات است .
كد: |
public void Draw(SpriteBatch spriteBatch)
{
spriteBatch.Begin();
for (int index = 0; index < particles.Count; index++)
{
particles[index].Draw(spriteBatch);
}
spriteBatch.End();
}
|
کد نهایی شما باید شبیه زیر باشد :
كد: |
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace ParticleSystems{
public class ParticleEngine
{
private Random random;
public Vector2 EmitterLocation { get; set; }
private List<Particle> particles;
private List<Texture2D> textures;
public ParticleEngine(List<Texture2D> textures, Vector2 location)
{
EmitterLocation = location;
this.textures = textures;
this.particles = new List<Particle>();
random = new Random();
}
public void Update()
{
int total = 5;
for (int i = 0; i < total; i++)
{
particles.Add(GenerateNewParticle());
}
for (int particle = 0; particle < particles.Count; particle++)
{
particles[particle].Update();
if (particles[particle].TTL <= 0)
{
particles.RemoveAt(particle);
particle--;
}
}
}
private Particle GenerateNewParticle()
{
Texture2D texture = textures[random.Next(textures.Count)];
Vector2 position = EmitterLocation;
Vector2 velocity = new Vector2(
1f * (float)(random.NextDouble() * 2 - 1),
1f * (float)(random.NextDouble() * 2 - 1));
float angle = 0;
float angularVelocity = 0.1f * (float)(random.NextDouble() * 2 - 1);
Color color = new Color(255f, (float)random.NextDouble(), 0f);
float size = (float)random.NextDouble();
int ttl = 20 + random.Next(40);
return new Particle(texture, position, velocity, angle, angularVelocity, color, size, ttl);
}
public void Draw(SpriteBatch spriteBatch)
{
spriteBatch.Begin();
for (int index = 0; index < particles.Count; index++)
{
particles[index].Draw(spriteBatch);
}
spriteBatch.End();
}
}
}
|
به تعداد ذرات موجود در لیست یک حلقه اجرا میشود و هر ذره جداگانه رسم میشود . تا اینجای کار تمام مواد اولیه و وسایل را آماده کردیم . حال نوبت استفاده عملی از این وسایل است .
قسمت چهارم : استفاده کاربردی از سیستم ذره ای
میخواهیم ببینیم تا الان چه کردیم . یک مثال کاربردی میتواند حاصل کار را تا به اینجا مشخص کند .
میخوام دو سیستم ذره ای را روی محور سینوسی در جهت مخالف یکدیگر حرکت دهیم .پس اهداف اصلی ما دو سیستم هستند که نقطه انتشار دهنده ذره آنها بر روی منحنی سینوسی حرکت میکند . ما نیاز به دو نقطه انتشار دهنده داریم ، دوموتور که ذرات را به صورت خودکار تولید کنند و ذرات به صورت خودکار بعد از گذشت زمانی از بین بروند . در نهایت این دو موتور بر روی یک منحنی حرکت کنند .اگر حرکت موتور ها را از کار حذف کنیم ، میبینیم که آنچه تا بحال انجام دادیم همین است که الان نیاز داریم .پس ما فقط نیاز به ایجد دو موتور و فعال کردن آنها داریم و در نتیجه حرکت دادن آنها بر روی منحنی مورد نظر .
با ایجاد موتور ها شروع میکنیم ، البته توجه داشته باشید که کدها در فایل Game1.cs قرار میگیرد .
در کلاس Game1.cs متغیر های زیر را تعریف کنید .
كد: |
ParticleEngine particleEngine1, particleEngine2;
bool Flag=true ;
float sin=0f; |
ما دوسیستم ذره ای به نام های particleEngine1 و 2 تعریف کردیم ، این دو موتور همان نقاط انتشار دهنده ما هستند . در ادامه Flag را تعریف کردیم که یک متغیر پرچم است که برای تغییر جهت حرکت موتور ها به کار میرود ، یعنی زمانی که پرچم بالا باشد جهت حرکت مثبت است و در غیر اینصورت جهت حرکت منفی است . زمان استفاده از این پرچم سعی میکنیم بیشتر بر روی آن تمرکز کنیم . و در نهایت متغیری به نام sin داریم که نگهدارنده سینوس موقعیت موتور است . که بعدا به صورت کامل توضیح خواهیم داد . در متد LoadContent کدهای زیر را اضافه میکنیم :
كد: |
List<Texture2D> textures = new List<Texture2D>();
textures.Add(Content.Load<Texture2D>("circle"));
textures.Add(Content.Load<Texture2D>("star"));
textures.Add(Content.Load<Texture2D>("4ed"));
particleEngine1 = new ParticleEngine(textures, new Vector2(400, 240));
particleEngine2 = new ParticleEngine(textures, new Vector2(400, 240)); |
textures در اینجا لیستی از تصاویر است ، تصاویری که باید در موتور تولید شوند . همانطور که میبینید در ادامه تعریف لیست ، 3 عنصر به آن اضافه کردیم ، circle ،star و 4ed که تصاویر را خودتان ميتوانيد در ابعاد 10*10 انتخاب يا رسم كنيد . در خطوط بعد دو موتور را مقدار دهی کردیم . اگه یادتان باشد هر موتور برای ساخته شدن 2 آرگومان ورودی دریافت میکرد . یکی لیست تصاویر ذراتی که باید در موتور تولید شوند و دیگری موقعیت نقطه انتشار دهنده موتور ، ما در این خطوط این دو آرگومان را مقدار دهی کرده ایم.
در متد Update کد زیر را اضافه میکنیم :
كد: |
if (Flag)
{
sin += .05f;
if (sin >= 14)
Flag = false;
}
else
{
if (sin <= -2)
Flag = true;
sin -= .04f;
} |
همانطور که مشاهده میکنید ، در صورتی که مقدارflag برابر true باشد ،مقدار متغیرsin اضافه میشود و زمانی که از مقدار خاصی تجاوز کرد مقدارflag برابرfalse میشود. این به آن خاطر است که مسیر حرکت موتور مثبت است تا زمانی که به گوشه های تصویر برسد و وقتی این اتفاق افتاد ف مسیر حرکت منفی میشود تا موتور به داخل صفحه باز گردد. در ادامه کدهای قبلی ، کدهای زیر را نیز اضافه میکنیم :
كد: |
float sin1=(-1)*sin;
particleEngine1.EmitterLocation = new Vector2(sin*70,200+(100*( (float)Math.Sin(sin))));
particleEngine1.Update();
particleEngine2.EmitterLocation = new Vector2(800+sin1 * 70, 200 + (100 * ((float)Math.Sin(sin))));
particleEngine2.Update();
|
در خط اول یک متغیر به نام sin1 داریم که مقدار آن برابر مقدار منفی شده متغیر sin است ، چرا که یکی از موتور ها به سمت جلو و دیگری به سمت عقب حرکت میکند پس ما یک مقدار منفی و یک مقدار مثبت میخواهیم که این گونه آنها را ایجاد کردیم . در خط بعد موقعیت موتور اول را مشخص کرده ایم. مقادیری که در اینجا میبینید ثابت نیستند و بسته به موقعیت نقاط و صفحه استفاده شده اند .با کمی تغییر میتوانید بفهمید که چه اتفاقی افتاده چون ما برای حرکت یک نقطه روی محور سینوس باید نقطه را در جهت محور x ها به طور ثابت جلو ببریم و از y نقطه سینوس بگیریم تا منحنی ایجاد شود .این کار را برای موتور دوم نیز تکرار میکنیم .
در آخر نیز کدهای زیر را در متد draw قرار میدهیم . که برای رسم موتور ها استفاده میشود .
كد: |
particleEngine1.Draw(spriteBatch);
particleEngine2.Draw(spriteBatch);
|
كد نهايي شما بايد شبيه زير باشد :
u
كد: |
sing System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
namespace ParticleSystems
{
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
ParticleEngine particleEngine1, particleEngine2;
bool Flag=true ;
float sin=0f;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
protected override void Initialize()
{
base.Initialize();
}
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
List<Texture2D> textures = new List<Texture2D>();
textures.Add(Content.Load<Texture2D>("circle"));
textures.Add(Content.Load<Texture2D>("star"));
textures.Add(Content.Load<Texture2D>("4ed"));
particleEngine1 = new ParticleEngine(textures, new Vector2(400, 240));
particleEngine2 = new ParticleEngine(textures, new Vector2(400, 240));
}
protected override void UnloadContent()
{
}
protected override void Update(GameTime gameTime)
{
if (Flag)
{
sin += .05f;
if (sin >= 14)
Flag = false;
}
else
{
if (sin <= -2)
Flag = true;
sin -= .04f;
}
float sin1=(-1)*sin;
particleEngine1.EmitterLocation = new Vector2(sin*70,200+(100*( (float)Math.Sin(sin))));
particleEngine1.Update();
particleEngine2.EmitterLocation = new Vector2(800+sin1 * 70, 200 + (100 * ((float)Math.Sin(sin))));
particleEngine2.Update();
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.Black );
particleEngine1.Draw(spriteBatch);
particleEngine2.Draw(spriteBatch);
base.Draw(gameTime);
}
}
}
|
|
_________________ به امید موقعیتی که لایقش باشیم !
www.MojtabaMaher.ir
0 بار اين نامه ويرايش شده است كه آخرين بار توسط 3 شنبه 7 تير 1390 - 18:56 در 8 بوده است. |
|