במאמר הקודם למדנו על אירועים ב-ActionScript 3 כמו לחיצת עכבר או לחיצת מקש במקלדת. עכשיו, אולי כדאי שהאירועים האלו יעשו משהו. למה שלא נגרום ל-sprite שלנו לעשות אנימציה?
באופן עקרוני, יש שתי דרכים לבצע אנימציות ב-ActionScript. הראשונה היא לעשות סוג של Interval באמצעות timer ולבקש מאובייקט להתקדם לכיוון מסוים כל שניה או חלק ממנה. הדרך השניה היא לבצע את זה באמצעות פריימים. flash היא תוכנה שעובדת לפי FPS ואם תסתכלו על הממשק של תוכנת הפלאש (שאנו לא נוגעים בו בדרך כלל) נוכל לראות שיש לו Timeline שבו יש את הפריימים. flash מתוכנן בדרך כלל ל-16 פריימים לשניה אך ניתן לשנות את זה.
אנימציה באמצעות interval
ראשית, ניצור את sprite הדוגמא שלנו. בדומה למאמרים הקודמים מדובר ב-sprite רגיל שיש לו eventListener לקליק שברגע שהוא מופעל אז מופעלת פונקצית – animateMe.
הנה האובייקט שלנו שנשמר ב animateSprite.as:
package {
import flash.display.Sprite;
import flash.events.*;
import flash.geom.*;
public class animSprite {
public var m_animSprite:Sprite = new Sprite();
public function animSprite(myStage) {
this.m_animSprite.graphics.beginFill(0x000000);
this.m_animSprite.graphics.drawRect(100, 100, 50, 50);
this.m_animSprite.graphics.endFill();
myStage.addChild(m_animSprite);
this.m_animSprite.addEventListener(MouseEvent.MOUSE_DOWN, animateMe);
}
public function animateMe(ev:Event)
{
//OUR FUNCTION
}
}
}
כמה מילים על אובייקט הדוגמא שלנו – animSprite: מדובר באובייקט רגיל שכל מי שעבר על המאמרים הקודמים אמור להכיר. האובייקט בעצם מכיל Constructor שיוצר את ה-sprite באמצעות class graphics. יש גם הוספת event listener ומתודה נוספת ששמה הוא animateMe שמופעלת כתוצאה מהאירוע ובה אנו נריץ את הפקודות שיובילו לאנימציה.
האובייקט עצמו נוצר ב-fla שבו יש רק שורה אחת שיוצרת אותו:
var m_animSprite:animSprite = new animSprite(stage);
לא לשכוח לשמור את ה-fla באותה תיקיה שבה שמרנו את animSprite.as. אחרת זה לא יעבוד. ועכשיו ליצירת האנימציה. אנו נשתמש ב-timer class. ה-timer הוא אובייקט חביב ופשוט שיש לו תכלית אחת בחיים – להריץ פונקציה אחת כל X מילישניות. אנו גם יכולים להגדיר לו כמה פעמים להריץ את הפונקציה וגם לעצור אותו. בואו ונפתח את הדוקומנטציה של פלאש ב-timer וניצור את האובייקט הזה בתוך animateMe.
package {
import flash.display.Sprite;
import flash.events.*;
import flash.geom.*;
import flash.utils.*;
public class animSprite {
public var m_animSprite:Sprite = new Sprite();
private var m_timer:Timer;
public function animSprite(myStage) {
this.m_animSprite.graphics.beginFill(0x000000);
this.m_animSprite.graphics.drawRect(100, 100, 50, 50);
this.m_animSprite.graphics.endFill();
myStage.addChild(m_animSprite);
this.m_animSprite.addEventListener(MouseEvent.MOUSE_DOWN, animateMe);
}
public function animateMe(ev:Event)
{
this.m_timer = new Timer(20, 0);
this.m_timer.start();
this.m_timer.addEventListener(TimerEvent.TIMER, this.moveMe);
}
}
}
בואו ונראה מה עשינו פה. שורה אחר שורה. ראשית, יצרתי לי משתנה שיכיל את האובייקט שלי:
private var m_timer:Timer;
אחרי כן ניגשתי ל-animateME ויצרתי את האובייקט עצמו. עלעול מהיר בדוקומנטציה מראה לי שה-constructor של timer מקבל שני פרמטרים. הראשון כל כמה מילישניות הוא רץ והשני הוא מספר הפעמים שאני מגביל אותו, כש-0 הוא בלתי מוגבל.
this.m_timer = new Timer(20, 0);
אחרי כן אני משתמש במתודת start על מנת לאתחל את ה-timer.
this.m_timer.start();
ובסוף בסוף אני מוסיף eventListener שמאזין לאירוע TimerEvent.TIMER. זה אירוע פשוט שמתרחש בכל 'קליק' של הטיימר. או כל 20 מילישניות במקרה שלנו. המאזין יורה את הפונקציה animateMe. המאזין ימשיך לירות את הפונקציה כל עוד לא ביטלנו אותו או כל הטיימר פעיל. עכשיו אנחנו צריכים לכתוב את פונקצית moveMeשהיא בסופו של דבר הפונקציה ש"עושה את העבודה" ומריצה את האובייקט. מה שהפונקציה הזו עושה הוא לשלוט או ב-x של האובייקט או ב-y שלו. לכל אובייקט גרפי שאנו יוצרים (כמו sprite או movieClip) יש x, y ו-width ו-height שעליהם אנו יכולים לשלוט או לקבל מהם מידע.
מתודת moveMe, למרות שהיא זו שעושה את העבודה היא פשוטה למדי – שינוי x ו-y ביחידת מידה אחת (לא מדובר פה בפיקסלים אלא ביחידות יחסיות). כך זה נראה:
public function moveMe(ev:Event)
{
this.m_animSprite.x +=1;
this.m_animSprite.y +=1;
}
ו… זהו! יש לנו טיימר שעובד כל 20 מילישניות ומזיז את מקומו של האובייקט. לא משהו שמאד שונה באופן עקרוני מג'אווהסקריפט למי שמכיר. כך נראה הקוד המלא:
package {
import flash.display.Sprite;
import flash.events.*;
import flash.geom.*;
import flash.utils.*;
public class animSprite {
public var m_animSprite:Sprite = new Sprite();
private var m_timer:Timer;
public function animSprite(myStage) {
this.m_animSprite.graphics.beginFill(0x000000);
this.m_animSprite.graphics.drawRect(100, 100, 50, 50);
this.m_animSprite.graphics.endFill();
myStage.addChild(m_animSprite);
this.m_animSprite.addEventListener(MouseEvent.MOUSE_DOWN, animateMe);
}
public function animateMe(ev:Event)
{
this.m_timer = new Timer(20, 0);
this.m_timer.start();
this.m_timer.addEventListener(TimerEvent.TIMER, this.moveMe);
}
public function moveMe(ev:Event)
{
this.m_animSprite.x +=1;
this.m_animSprite.y +=1;
}
}
}
וכך זה נראה במציאות, אל תשכחו להקליק על האובייקט על מנת להתחיל את האנימציה:
יצירת אנימציות באמצעות פריימים
כל מי שעסק מעט בתוכנת פלאש יודע שיש צורך להשתמש ב-TimeLine על מנת ליצור אנימציות מורכבות. אך באמצעות ActionScript 3 לא רק שאנו יכולים ליצור אנימציות מורכבות באמצעות ה-TimeLine אלא גם אפשר לייצא אנימציות שכבר נעשו בפלאש ולהשתמש בהן במסגרת ActionScript 3. מגרסת flash CS3 ומעלה אנו עושים את זה באמצעות XML או יותר נכון Motion XML.
למי שלא יודע מה זה XML, לא צריך ממש להבהל. XML דומה מאד ל-XHTML והסינטקס שלו כל כך פשוט שזה מצחיק, אני אסביר על כך בהמשך. אך בינתיים אסביר כמה מושגים בסיסיים בפלאש שיעזרו לנו ביצירת ה-Motion XML.
keyframe – אנימציה, כפי שכולנו יודעים, היא רצף של תמונות מתחלפות שמביאות עצם מסוים מנקודה א' לנקודה ב'. נקודה א' ונקודה ב' הן בעצם ה-keyframes. סוג של עוגנים של נקודות שיא באנימציה/תזוזה. למשל אם יש לנו איש שקופץ, תחילת הקפיצה תהיה keyframe, שיא הקפיצה יהיה keyframe והנחיתה תהיה keyframe.
tween – ה-tween הוא בעצם רצף התמונות המתחלפות בין ה-keyframes.
ease הוא כלי חשוב ביותר כאשר אנו רוצים ליצור אנימציה מציאותית. אם ניקח כדוגמא איש רץ, אם נשתמש רק ב-keyframe וב-tweens יהיה לנו איש שנמצא בעצירה ואז מזנק במלוא המהירות עד לעצירה מלאה בעוד שבמציאות לוקח לו זמן להאיץ ולהאט. שבירת הליניאריות הזו מכונה ease.
אז בואו ונתחיל. יש לנו את האובייקט המוכר, שבמתודת animateMe שלו אנו מריצים את הכל. לאובייקט הזה נוסיף כמה דברים: ראשית נייבא את fl.motion.Animator שזו הספריה שתסייע לנו להמיר את ה-xml שלנו. שנית, ניצור אובייקט Animator ונעביר לו שני פרמטרים – האובייקט שאותו אנו רוצים להזיז וה-XML שלנו. לבסוף, נשתמש במתודת play שיש באובייקט Animator על מנת להתניע את כל התהליך.
ככה זה נראה:
package
{
import flash.display.Sprite;
import flash.events.*;
import flash.geom.*;
import flash.utils.*;
import fl.motion.Animator;
public class animSprite
{
public var m_animSprite:Sprite = new Sprite();
private var myStage;
public var anim:Animator;
public function animSprite(myStage)
{
this.m_animSprite.graphics.beginFill(0x000000);
this.m_animSprite.graphics.drawRect(100, 100, 50, 50);
this.m_animSprite.graphics.endFill();
this.myStage = myStage;
myStage.addChild(m_animSprite);
this.m_animSprite.addEventListener(MouseEvent.MOUSE_DOWN, animateMe);
}
public function animateMe(ev:Event)
{
//var myXml = HERE WIILL BE OUR XML;
this.anim = new Animator(myXml, this.m_animSprite);
this.anim.play();
}
}
}
ועכשיו תור ה-XML. לצורך העניין אנו נכתוב את ה-XML ישירות בתוך הקובץ. כל מה שאנו רוצים זו אנימציה פשוטה שמעבירה את האובייקט מנקודה א' לנקודה ב' בדיוק כמו שעשינו עם ה-Timer. רק עם פריימים. ה-XML הזה הוא פשוט למדי בבסיסו. ראשית אראה לכם את הקוד ואז אסביר עליו.
package {
import flash.display.Sprite;
import flash.events.*;
import flash.geom.*;
import flash.utils.*;
import fl.motion.Animator;
public class animSprite {
public var m_animSprite:Sprite = new Sprite();
private var myStage;
public var anim:Animator;
public function animSprite(myStage) {
this.m_animSprite.graphics.beginFill(0x000000);
this.m_animSprite.graphics.drawRect(100, 100, 50, 50);
this.m_animSprite.graphics.endFill();
this.myStage = myStage;
myStage.addChild(m_animSprite);
this.m_animSprite.addEventListener(MouseEvent.MOUSE_DOWN, animateMe);
}
public function animateMe(ev:Event)
{
var myXml =
<Motion duration="10">
<source>
<Source>
</Source>
</source>
<Keyframe index="0">
<tweens>
<SimpleEase />
</tweens>
</Keyframe>
<Keyframe index="10" x="198" y="123"/>
</Motion>;
this.anim = new Animator(myXml, this.m_animSprite);
this.anim.play();
}
}
}
וכך זה נראה במציאות:
נראה ממש אותו הדבר אך זה מאד מאד שונה. עכשיו בואו ונדבר על ה-XML. באופן עקרוני, הסינטקס של xml אמור להיות די מובן. כל מה שסגור בתוך <> נקרא תגית. לכל תגית יש תגית שסוגרת אותה בסדר היררכי או שיש סגירה עצמית עם /. אנו מחשיבים כל תגית כזו כאובייקט בפני עצמו. כל האובייקטים שמופיעים ב-XML שלנו הם מנדטוריים למעט שלושה אובייקטים:
keyframe – יש לנו שני אובייקטים של keyframe, בשניהם יש פרמטר index המגדיר את מספר הפריים שלהם. הראשון נמצא בפריים 0 והשני בפריים 12. ב-keyframe השני, זה שבפריים 12 יש גם הגדרה של x ו-y. ה-x וה-y שבהם האנימציה נפסקת יחסית למיקום האובייקט הראשוני.
tweens – אובייקט tweens חיוני לשם השגת תנועה, אם נוריד אותו אנו נראה מעבר מיידי ללא אנימציה. יהיה לנו אובייקט בנקודה א' בפריים 0 ואובייקט בנקודה ב' בפריים 12 ללא כל 'מעבר' ביניהם.
SimpleEase – היכן שיש לנו tweens יש לנו גם ease. ניתן לבנות ease מסובכים למדי אך אנו בחרנו ב-ease פשוט ודיפולטיבי שפשוט יוצר קצב ליניארי.
הגדולה של ה-XML הזה שקל מאד להעתיק אותו ולייבא אותו לאובייקטים אחרים. אם אני יוצר אובייקט אחר בפרויקט אחר, קל מאד להעתיק את ה-XML ולהשתמש בו. בנוסף, אם יצרתי באמצעות ה-GUI איזו אנימציה ואני רוצה לייבא אותה לתוך התוכנית שלי, אפשר לעשות את זה בקלות באמצעות commands->export motion XML.