ساخت انیمیشن قسمت دوم (بازیسازی با جاوا)

1395/10/12 لیلا کشاورز 1102

سلام عرض میکنم خدمت همه ی همراهان عزیز.
کشاورز هستم وبا قسمت سوم از سری آموزش های بازیسازی با جاوا در خدمتتونم.
انشاالله مطالبی که گفته میشه، مورداستفادتون قرار بگیره.

از جلسه گذشته  مبحث انیمیشن رو شروع کردیم، قرار بر این شد که یک پروژه ای رو با هم ایجاد کنیم  که  در آن یک تصویر در board حرکت کند.

کار رو تا اونجا پیش بردیم که تصویر مورد نظر، از گوشه ی سمت چپ بالای پنجره وارد و از گوشه ی سمت راست پایین  پنجره خارج میشد.

اما قصد ما این بود که  حرکت مدام تکرار بشه  و این میطلبه که  به محض خروج  تصویر از Board ، به نوعی دوباره وارد صفحه بشه .

این تنها نقص برنامه ای بود که هفته ی گذشته نوشتیم.

برای حل این مشکل باید در تابع actionPerformed این شرط رو قرار بدیم که وقتی تصویر از پنجره خارج شد (یعنی وقتیy  مکان تصویر، بیشتر از ارتفاع پنجره ی خروجی شد) x  وy   رو طوری مقداردهی کنیم ، که تصویر دوباره از گوشه ی سمت چپ پنجره به نمایش دربیاد:

if (y > 240) {
           y = -45;
            x = -45;
}


حال اگر بعد از اضافه کردن قطعه کد فوق  به متد actionPerformed  دوباره برنامه رو اجرا کنید، میبینید که حرکت مورد نظرمون به درستی انجام میشه.

اگر خاطرتون باشه ، گفتیم که برای ایجاد انیمیشن در جاوا، چندین راه وجود داره.
روشی که ذکر کردیم، برای بازیهای ساده و 2 بعدی کارآمدی خوبی دارند و جز در این موارد کاربرد چندانی نخواهند داشت .

در این قسمت، همون برنامه ی بالا رو با روش دیگه ای پیاده سازی میکنیم که میشه گفت بهترین و دقیقترین روش برای ایجاد انیمیشن هاست.

استفاده از  thread (چند نخی):

در روش قبل ، یک وظیفه (که نمایش تصویر بود) رو تعریف کردیم و در بازه های زمانی متوالی ، مدام این وظیفه
را تکرار میکردیم. اما در این روش، کل انیمیشن را در یک thread  (نخ) قرار میدهیم و تنها یکبار متد () run   را فراخوانی میکنیم .

درعوض در تابع run()   یک "حلقه ی تکرار "درنظرمیگیریم که متدهای"() cycle" که میزان افزایش مختصه های x,y  را در هر حرکت، درآن  پیاده سازی کردیم)و"() repaint"  را فراخوانی میکند.

قطعه کد کلاس  Board ، برای پیاده سازی پروژه ی  قبل، با این روش، به شکل زیر خواهد بود :

package animation;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Toolkit;
import javax.swing.ImageIcon;
import javax.swing.JPanel;

public class Board extends javax.swing.JPanel {
    private Image triangle;
    private Thread animator;
    private int x, y;

    private final int DELAY = 50;

    public Board() {
        setBackground(Color.WHITE);
        setDoubleBuffered(true);

        ImageIcon ii = new ImageIcon(this.getClass().getResource("triangle.jpg"));
       triangle = ii.getImage();

        x = y = 10;
    }

    public void addNotify() {
        super.addNotify();
        animator = new Thread(this);
        animator.start();
    }

    public void paint(Graphics g) {
        super.paint(g);

        Graphics2D g2d = (Graphics2D)g;
        g2d.drawImage(triangle, x, y, this);
        Toolkit.getDefaultToolkit().sync();
        g.dispose();
    }

    public void cycle() {

        x += 1;
        y += 1;

        if (y > 240) {
            y = -45;
            x = -45;
        }
    }

    public void run() {

        long beforeTime, timeDiff, sleep;

        beforeTime = System.currentTimeMillis();

        while (true) {

            cycle();
            repaint();

            timeDiff = System.currentTimeMillis() - beforeTime;
            sleep = DELAY - timeDiff;

            if (sleep < 0)
                sleep = 2;
            try {
                Thread.sleep(sleep);
            } catch (InterruptedException e) {
                System.out.println("interrupted");
            }

            beforeTime = System.currentTimeMillis();
        }
    }
}

 

مشاهده میکنید که در اینجا خبری از timer  نیست!
در عوض یک thread  ایجاد شده و درمتد() addNotify   ، این thread   ( که   animatorنامگذاری کرده بودیم )، فعال شده است.

 

"نکته ای که باید حتما بهش توجه بشه  اینه که، برای استفاده از thread  ها باید حتما کلاس اصلی رو از Runnable ، implement کنیم."

در واقع ما کلاسی(تحت عنوان Board) تعریف کردیم که اینترفیس Runnable را implement کند. سپس آن کلاس متد run را implement می کند. که وقتی Thread ساخته می شود، یک آرگومان پاس داده می شود و start می شود.

متد Board  و paint، همانطور که مشاهده میکنید،مطابق روش قبل پیاده سازی شده اند.

به متد run  نگاه کنید :
درون این متد ابتدا تنها یک حلقه ی while  قرار دادیم و توابع cycle و  repaint را در آن فراخوانی کردیم، یعنی به این شکل:

public void run (){                                                                                                     
while (true){                                                                                                             
               cycle();
               repaint(); }
}


اگر تا همینجای کد، برناممون رو اجرا کنیم، میبینیم که  تصویر با سرعت خیلی زیاد و به شکل نا منظمی حرکت میکنه .
اما ما می خواهیم حرکت، به آرامی و با سرعت ثابت انجام بشه .
پس نیازه  که یک زمان توقفی(استراحت) برای حرکتمان در نظر بگیریم.
برای این، باید زمان سیستم را محاسبه کنیم .اینکار را با دستورات زیر انجام میدهیم:

timeDiff = System.currentTimeMillis() - beforeTime;                 
                         sleep = DELAY – timeDiff;

متغیرbeforeTime  زمان قبل از ورود به حلقه ی تکرارمان را مشخص میکند و timeDiff زمان لازم برای اجرای متد.
متغیر sleep ، همان استراحتی خواهد بود که بین حرکت ها ، خواهیم داشت.که از کم کردن زمان لازم برای اجرای  متدها از متغیر ثابت DELAY بدست می اید.

حال کافیست  این زمان را برای توقف فراخوانی کنیم:

if (sleep < 0)
               sleep = 2;
            try {
              Thread.sleep(sleep);
          } catch (InterruptedException e) {
           " System.out.println("interrupted"));
           }
           beforeTime = System.currentTimeMillis();
  }

با اجرای برنامه ، نتیجه ای مشابه اجرای قبل خواهیم دید ، بااین تفاوت که حرکت تصویر به شکل روان تری صورت میگیرد.

در این آموزش هم مبحث انیمیشن رو ادامه دادیم. تا اینجا با دو روش برای ایجاد انیمیشن ، آشنا شدیم.