יום ראשון, 5 ביוני 2011

Particle System


היי,

הפוסט הזה יעסוק במערכות חלקיקים וההיגיון מאחוריהן, הפוסט נכתב בעיקר למשתמשי XNA אך ניתן לממש קוד כזה גם בסביבות עבודה אחרות.
מערכת חלקיקים היא אחד האפקטים הויזואליים החשובים והמרהיבים ביותר במשחקי מחשב.
הרעיון במערכת הזאת הוא הגדרת אבטיפוס (Prototype, דגם ראשוני) של חלקיק באמצעות מאפיינים רבים.
לאחר מכן יוצרים מספר רב של חלקיקים בעלי התכונות של האבטיפוס.
עם קצת ניסיון ומשחק בפרמטרים תוכלו ליצור אפקטים רבים במספר דקות.

תמונה מה-Particle System שלי
ההתחלה
קודם כל צריך להחליט מה יהיה במערכת. 
לדוגמה:
  • מה יהיו המאפיינים של כל חלקיק.
  • מה יהיו המאפיינים של כל מערכת חלקיקים.
  • כיצד תעבוד ההיררכיה בין מערכת חלקיקים לחלקיקים שבה.
  • האם יהיו אינטרקציות בין חלקיקים.
כדי להחליט על כל אלה מומלץ, כמו בכל דבר, לקחת השראה מאחרים. 
מערכות שאני ממליץ להסתכל עליהן:
למחולל המשחקים Game Maker יש מערכת חלקיקים דו מימדית מאוד גמישה ובעלת אפשרויות רבות.
למנוע Unity יש מערכת חלקיקים תלת מימדית מצויינת.
וכמובן שתוכלו לחפש ביוטיוב ולקבל מאות אם לא אלפי תוצאות.


מתחילים לכתוב:
נתחיל מלכתוב מחלקה לחלקיק. המחלקה תכיל את תכונות החלקיק שהוא יורש (הכוונה בהורשה היא לא הורשה של מחלקת אב למחלקת בן, אלא פשוט הורשה) מהאבטיפוס והמידע על מצבו.

אם לדוגמה החלטתם על המאפיינים: ספרייט, צבעים (שמתחלפים לאורך הזמן), גודל (גודל הספרייט), סיבוב (סיבוב הספרייט), מהירות וכיוון, המחלקה צריכה להראות בערך כך:
מחלקת Particle

אבל גם לחלק מהמאפיינים אפשר לקבוע מאפיינים משלהם. למהירות, גודל, כיוון וסיבוב ניתן לקבוע מינימום, מקסימום, קצב גדילה וכו'...
במקום לחשב בנפרד עבור כל מאפיין את הערך שלו, ניתן ליצור מחלקה שתטפל במאפיין.
מחלקת Property תחזיק מידע על מאפיין. במערכת שלי קיימים מינימום, מקסימום, קצב גדילה וגידול ודעיכה מחזוריים של הערך. אתם כמובן יכולים להוסיף על זה, יש אינסוף אפשרויות.
מחלקת Property

המחלקה Property אצלי נועדה רק להחזיק מידע ולא לטפל בו, על מנת לטפל במידע יצרתי מחלקה נוספת שמקבלת Property ומחשבת את הערך של המאפיין עפ"י הזמן שעבר ותכונות ה-Property.
רוב החישובים במחלקה שמעדכנת ומחשבת את הערך של המאפיין הם על ה-wiggle (גדילה ודעיכה מחזוריים), אם אתם לא מבינים משהו מהחישובים תגיבו על הפוסט ואסביר לכם בפירוט.
מחלקת PropertyUpdater



עכשיו יש לנו מחלקה שאחראית על החזקת מידע על מאפייני של החלקיק ומחלקה שמעדכנת אותם.
מה שנשאר הוא לעדכן את מחלקת Particle עם המאפיינים מהסוג החדש, ולהוסיף מאפיינים חדשים במידת הצורך.
למחלקה המעודכנת הוספתי מספר מאפיינים כמו זמן החיים של החלקיק והאם הסיבוב של הספרייט יהיה ביחס לכיוון התנועה של החלקיק. בנוסף צריך SpriteBatch שעליו נצייר את כל החלקיקים ו-GraphicsDeviceManager על מנת שתהיה גישה למידות חלון המשחק. כך נדע מתי חלקיקים יוצאים מהמסך ולכן לא נצייר אותם.
מחלקת Particle החדשה

עכשיו יש לנו חלקיק עם מאפיינים, והרבה. אבל הוא בעצם לא עושה כלום מעבר לאחסון נתונים, לכן צריך להוסיף לו פונקציות שיבצעו את כל הפעולות שחלקיק עושה.
נתחיל מ-Ctor (פונקציית בנאי, Constructor), אין הרבה מה לעשות מלבד קליטת מאפיינים (תסלחו לי על השימוש ב-keyword this):
Particle Constructor

לאחר שיצרנו את החלקיק נצטרך לעדכן אותו בכל פריים, לכן נכתוב פונקציה שתטפל בזה.
הפונקציה מעדכנת את הטיימר, בודקת האם החלקיק צריך להעלם, ומעדכנת את המאפיינים (באמצעות קריאה לפונקציה Update של PropertyUpdater.
לאחר מכן נעשה חישוב של המיקום החדש של שהחלקיק בהתאם לזווית ולמהירות שלו.
החישוב הוא המרה מגודל (מהירות) וזווית (כיוון התנועה) לרכיבי X ו-Y של וקטור המהירות (למי שלא הבין אני ממליץ לקרוא על המרת וקטורים: ויקיפדיה), וקטור המהירות נוסף למיקום וכך מתקבל המיקום החדש.

Update Particle

 יש לנו את כל המידע שאנחנו צריכים כדי לצייר את החלקיק, אז מה שנשאר בעצם זה רק לצייר אותו.
פונקציית הציור בודקת האם החלקיק נמצא בגבולות המסך (מבלי להתחשב בסיבוב או הגדלה, במקרה של ספרייטים קטנים כמעט שלא מבחינים בהבדל). אם כן היא מציירת אותו בהתאם לנתוני המחלקה ופונקציה נוספת שמחשבת איזה צבע צריך לקחת ממערך הצבעים עפ"י הזמן שהחלקיק קיים.
Draw Particle
Get Color

בשלב הזה תוכלו לצייר חלקיקים באמצעות יצירה ועדכון באופן "ידני". אם תחזיקו אותם ברשימה או רשימה מקושרת, תרעננו את החלקיקים מתי שצריך ותציירו אותם תהיה לכם מערכת שעובדת.
לעבוד כך זה מסורבל ובכלל לא מומלץ, עם זאת תוכלו כבר עכשיו ליצור אפקטים כאלה:


לכן ניצור מחלקות חדשות שיטפלו ביצירה ועדכון של מספר חלקיקים.


Emitter - פולט חלקיקים:
אנחנו צריכים מחלקה שתדע לעשות 3 דברים:
  • ליצור כמות חלקיקים מסויימת ביחידת זמן (כלומר X חלקיקים בשנייה).
  • להחזיק את כל החלקיקים בזיכרון.
  • לעדכן ולצייר אותם.
כדי שנוכל ליצור חלקיקים בצורה נוחה, ניצור מחלקה שמכילה נתונים על חלקיק ופונקציה כדי ליצור חלקיק מסוג זה.
המחלקה ParticleData שכתבתי מטפלת בנושא הזה ואין הרבה מה להרחיב עליו:
מחלקת ParticleData

בנוסף יש צורך לתת לפולט מידע על זמן היצירה בין חלקיק לחלקיק, לכן ניצור מחלקה נוספת ופשוטה, שפשוט מכילה מידע על איזה חלקיק ליצור וכמה ליצור ממנו בשנייה:
מחלקת ParticleCreationData

עכשיו הגיע הזמן ליצור את מחלקת פולט החלקיקים.
מאפייני הפולט:
הפולט יהיה נקודתי (כלומר כל החלקיקים יווצרו באותה נקודה) ולכן נצטרך נקודת התחלה (Vector2).
רשימת החלקיקים שהפולט ייצור (מערך של ParticleCreationData).
טיימרים שיבדקו כמה זמן עבר מיצירת החלקיק האחרון מסוג מסויים כדי לדעת מתי ליצור את החלקיק הבא (מערך של double).
והחלקיקים עצמם יהיו ברשימה מקושרת של חלקיקים (למה רשימה מקושרת? קראו כאן).
במידה ונרצה שתהיה גישה למספר החלקיקים הקיימים, נוסיף גם מאפיין שיחזיר את מספר האיברים ברשימה המקושרת.
המחלקה צריכה להראות כך בינתיים: מחלקת Emitter.

למחלקה יהיו 3 פונקציות: אתחול (Ctor), עדכון וציור.
בפונקציית האתחול יוצרים את הרשימה המקושרת ואת מערך הטיימרים (טיימר לכל סוג חלקיק) ומאפסים את הטיימרים.
פונקציית האתחול

פונקציית העדכון היא בעצם הפונקציה הכי מורכבת במחלקה ויש בה שני חלקים: עדכון החלקיקים הקיימים ויצירת חלקיקים חדשים.
החלק הראשון של הפונקציה עובר על כל החלקיקים, מעדכן אותם ואם הם צריכים להעלם אז הקוד דואג להסיר אותם.
החלק השני בודק עבור כל אחד מהטיימרים אם הוא עבר את זמן היצור של החלקיק עליו הוא "אחראי" במידה והוא עבר נוצרים חלקיקים בהתאם לכמה פעמים הוא עבר את זמן היצור. כדי שלא יקרה מצב לדוגמה שהעדכון הקודם היה לפני שנייה אבל אמורים להווצר 5 חלקיקים בשנייה, ויווצר רק חלקיק אחד.
פונקציית הציור פשוטה ביותר, מעבר על כל החלקיקים ברשימה וציורם:

פונקציות עדכון וציור החלקיקים

כל מה שנותר הוא ליצור מערכת חלקיקים כזאת, לאחר שתשחקו קצת עם הפרמטרים תראו שזה ממש פשוט.
האפקט בתמונה הראשונה בפוסט נוצר כך: הקוד

הערה: לקבלת האפקט המרשים ביותר, בדרך כלל תצטרכו להשתמש ב-SpriteBlendMode.Additive בפקודה Begin של ה-SpriteBatch עליו אתם מציירים.

אם תשקיעו אף יותר תוכלו להוסיף עוד אפשרויות למערכת (תגובות בין חלקיקים, כוח כבידה, שינוי ספרייט, תנועה מורכבת יותר וכו'), להתאים אותה למנוע / שפה / סביבת עבודה אחרים ואפילו להוסיף עורך אפקטים ויזואלי.

לעוד פרטים והורדה של ה-Particle System שלי:
http://www.fxp.co.il/showthread.php?t=6745484

סרטון הדגמה:


ועידכון קטן בנוגע ל-Minecraft שלי ב-XNA, הוספתי יום עם שמש ולילה עם ירח:

זה סוף הפוסט, מקווה שהפוסט הבא יפורסם בקרוב. אני לא יכול להבטיח את זה מפני שאין לי הרבה זמן לכתוב בתקופת הבגרויות.

ומוזיקה! להקה מצויינת שלא אכזבה באף אלבום שהיא הוציאה:



לילה טוב :)
ד"א אם מישהו מעוניין שאכתוב על נושא מסויים, תגיבו על הפוסט עם הנושא ובמידה והוא יתאים לבלוג אני אדאג לכתוב עליו פוסט.