مشکل در تایمر برای حرکت آبجکت روی فرم

mhabat

Member
یه آبجکت رو با تایمر روی فرم جابه جا می کنم!

خوب حالا این آبجکت یک سرعتی واسه خودش داره مثلا به صورت تساعدی 4 پوینت 4پوینت یا 6 پوینت 6 پوینت جا به جا میشن
خوب این که با چه سرعتی جا به جا میشه رو داریم
حالا چه کار کنم که دقیقا وقتی لبه ی آبجکت به فرم میرسه برگرده؟
آخه
مثلا اگر آخر فرم من در محل 660 باشه !
و مثلا آبجکت من در نقطه 333باشه و سرعت 4 پوینت بر ثانیه باشه
اون وقت نمی تونم کاری کنم که دقیقا گوشه به فرم برخورد کرد برگرده؟
حالا مثال زدم که سرعت 4 پوینت هست مگرنه سرعت رو از ورودی می گیره!
 

the_king

مدیرکل انجمن
یه آبجکت رو با تایمر روی فرم جابه جا می کنم!

خوب حالا این آبجکت یک سرعتی واسه خودش داره مثلا به صورت تساعدی 4 پوینت 4پوینت یا 6 پوینت 6 پوینت جا به جا میشن
خوب این که با چه سرعتی جا به جا میشه رو داریم
حالا چه کار کنم که دقیقا وقتی لبه ی آبجکت به فرم میرسه برگرده؟
آخه
مثلا اگر آخر فرم من در محل 660 باشه !
و مثلا آبجکت من در نقطه 333باشه و سرعت 4 پوینت بر ثانیه باشه
اون وقت نمی تونم کاری کنم که دقیقا گوشه به فرم برخورد کرد برگرده؟
حالا مثال زدم که سرعت 4 پوینت هست مگرنه سرعت رو از ورودی می گیره!

از اونجایی که شما جابجایی شیء رو نقطه به نقطه انجام نمیدید و در بعضی نقاط حرکتش رو نشون میدید و در بعضی نقاط نشون نمیدید،
حتی اگر شیء دقیقا در موقعیت مورد نظر برگرده الزاما کاربر اون رو در اون موقعیت نمی بینه، چون شما نرخ فریم هایی که نشون می دهید پایینه.
شما می خواهید این سرعت کاملا حفظ بشه؟ می توانید شرط کنید که زمانی که شیء به لبه رسید یا خواست از اون بگذره، دقیقا روی لبه قرار بگیره
و جهت حرکتش بر عکس بشه. اینطوری احتمال مکث روی لبه هست چون سرعت ثابت رعایت نمیشه ولی در عوض شیء همیشه از روی لبه
بر می گرده.

اما اگر قراره شی سرعت اش رو دقیقا حفظ کنه، باید شرط بگذارید که وقتی فاصله شیء تا لبه کمتر از اون Step (چهار پیکسل) شد، Interval اون
Timer به همون نسبت کم بشه تا زودتر فراخوانی بشه. یعنی Interval نسبت به فاصله شیء از لبه متغیر باشه.
 

mhabat

Member
سلام دوباره.

من دقیقا فرمایشات شما رو متوجه شدم!

خوب اگر بخوام به جمله ی دومتون توجه کنم ! که interval رو تغییر بدم باید بگم که انجام دادم و مشکل حل شد.

ولی مشکلی که که هست یک فرم هست که 2 تا لبه ی عمودی داره + 2 عدد آبجکت

که هر کدام از آبجکت ها به لبه ها یا آبجکت دیگه برخورد کنه باید برگرده !

حالا اینجاست که انتخاب یک interval مناسب کار رو برام سخت کرده!

حالا برای این که بیشتر متوجه بشین عکس رو هم قرار میدم
collision.PNG
 

the_king

مدیرکل انجمن
سلام دوباره.

من دقیقا فرمایشات شما رو متوجه شدم!

خوب اگر بخوام به جمله ی دومتون توجه کنم ! که interval رو تغییر بدم باید بگم که انجام دادم و مشکل حل شد.

ولی مشکلی که که هست یک فرم هست که 2 تا لبه ی عمودی داره + 2 عدد آبجکت

که هر کدام از آبجکت ها به لبه ها یا آبجکت دیگه برخورد کنه باید برگرده !

حالا اینجاست که انتخاب یک interval مناسب کار رو برام سخت کرده!

حالا برای این که بیشتر متوجه بشین عکس رو هم قرار میدم
مشاهده پیوست 107690

فرق آنچنانی نمی کنه، بجای مقدار ثابت برای موقعیت لبه از یک متغیر محلی مثل limit استفاده کنید :
اگر جهت حرکت شیء A به سمت راست باشه و موقعیت Left شیء B بزرگتر از Right شیء A است (شیء B بین لبه و A قرار داره)، برای شیء A مقدار limit موقعیت Left شیء B است.
اگر جهت حرکت شیء A به سمت راست باشه و موقعیت Right شیء B کوچکتر از Left شیء A است (شیء B پشت سر A قرار داره)، برای شیء A مقدار limit موقعیت لبه سمت راست است.
اگر جهت حرکت شیء A به سمت چپ باشه و موقعیت Right شیء B کوچکتر از Left شیء A است (شیء B بین لبه و A قرار داره)، برای شیء A مقدار limit موقعیت Right شیء B است.
اگر جهت حرکت شیء A به سمت چپ باشه و موقعیت Left شیء B بزرگتر از Right شیء A است (شیء B پشت سر A قرار داره)، برای شیء A مقدار limit موقعیت لبه سمت چپ است.
حالا که limit برای شیء A مشخص شده، بر اساس اینکه فاصله اش از limit کمتر از Step هست یا خیر، مطابق همان روال قبلی تصمیم بگیرید.
 

mhabat

Member
سلام. مجدد.
یک نمونه کد برای لبه ی سمت راست نوشتم اگر میشه یه نگاه بندازید:
کد داخل تایمر:
کد:
     timer2.Interval = Math.Abs(a.EndPoint2.X - a.StartPoint.X); 
            if (  _pos1.Location.X < 4)
            {
                a.EndPoint2 = new Point(a.StartPoint.X - (a.EndPoint2.X - a.StartPoint.X), a.EndPoint2.Y);
            }
            
         
            x1 += (a.EndPoint2.X - a.StartPoint.X);
            _pos1 = new Rectangle(new Point(x1, y1), bmp1.Size);
            a.StartPoint = new Point(x1 + (_pos1.Width / 2), _pos1.Y + (_pos1.Height / 2));
            a.EndPoint2 = new Point((x1 + (_pos1.Width / 2)) + ((int)a.Len[0]), _pos1.Y + (_pos1.Height / 2));
          
            Invalidate();
            if (797 - _pos1.Right == limit)
            {
                a.EndPoint2 = new Point(a.StartPoint.X - (a.EndPoint2.X - a.StartPoint.X), a.EndPoint2.Y);
                timer2.Interval -=  limit;
            }
        }

کد مربوط به زدن دکمه ی استارت:
کد:
  int limit;
        int masir;
        private void button6_Click(object sender, EventArgs e)
        {
             masir = 797 - _pos1.Right;
           
            int v = Math.Abs(a.EndPoint2.X - a.StartPoint.X);
            if (v > masir)
                limit = masir / v;
            else
            {
                int i = 1;
                while (i * v < masir)
                {
                    i++;
                }
                if (i * v <= masir)
                    limit = (masir - (i * v));
                else
                    limit = (masir - ((i - 1) * v));
                 
            }
            timer2.Enabled = true;
        }
         
    }

دقیقا اون فاصله ی اضافی تا لبه رو وقتی اتفاق می افته داخل کد ا مشخصه
 

mhabat

Member
سلام.
آقا ما یه تغییر ایجاد کردیم!
اومدم 2 تا متغیر به کلای اضافه کردم که یکش مشخصه سرعت هست و اون یکی وزن!
یعنی هنگام تعریف شی از کلاس ما میتونیم سرعت و وزن توپ رو هم مشخص کنیم.
حالا تابعlimit رو به این شکل زیر تغییر دادم که مطابق با فرمول برخورد عمودی در لینک زیر
http://fa.wikipedia.org/wiki/برخورد
سرعت هر کردوم از توپ ها بعد از برخورد با هم متناسب با وزنشون و همچنین سرعت قبلیشون تغییر کنه.

کد:
کد:
private void Form1_Load(object sender, EventArgs e)
        {
            _ballA.Weight = 100;
            _ballB.Weight = 200;
            _ballA.Speed = 10;
            _ballB.Speed = 12;
            _bmp = new Bitmap(pictureBox1.Width, pictureBox1.Height);
            backgroundWorker1.RunWorkerAsync();
        }
 private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            
            CheckLimit(_ballA, _ballB, _ballA.Speed * (_ballA.Direction == Ball.Directions.LeftToRight ? 1 : -1));
            CheckLimit(_ballB, _ballA, _ballB.Speed * (_ballB.Direction == Ball.Directions.LeftToRight ? 1 : -1));
            pictureBox1.Invalidate();
        }
private void CheckLimit(Ball a, Ball b, float x)
        {
            float limit;
            switch (a.Direction)
            {
                case Ball.Directions.LeftToRight:
                    if (b.Right >= a.Left)
                    {
                        limit = b.Left;
                    }
                    else
                    {
                        limit = pictureBox1.Width;
                    }
                    if (a.Right + x >= limit)
                    {
                        if (limit == b.Left)
                        {
                            [FONT=Consolas][SIZE=2][FONT=Consolas][SIZE=2]a.Speed = ((a.Weight - b.Weight) * a.Speed) / (a.Weight + b.Weight);
[/SIZE][/FONT][/SIZE][/FONT]
                           
                        }
                        a.Right = limit - 1;
                        a.Direction = Ball.Directions.RightToLeft;
                    }
                    else
                    {
                        a.Offset(x, 0);
                       
                    }
                    break;
                default:
                    if (b.Left <= a.Right)
                    {
                        limit = b.Right;
                    }
                    else
                    {
                        limit = 0;
                    }
                    if (a.Left + x <= limit)
                    {
                        if (limit == b.Right)
                        {
                          [FONT=Consolas][SIZE=2][FONT=Consolas][SIZE=2]a.Speed = ((a.Weight - b.Weight) * a.Speed) / (a.Weight + b.Weight);
[/SIZE][/FONT][/SIZE][/FONT]

                        }
                        a.Left = limit + 1;
                        a.Direction = Ball.Directions.LeftToRight;
                    }
                    else
                    {
                        a.Offset(x, 0);
                    }
                    break;
            }
        }

فرمول :
کد:
[FONT=Consolas][SIZE=2][FONT=Consolas][SIZE=2]a.Speed = ((a.Weight - b.Weight) * a.Speed) / (a.Weight + b.Weight);
[/SIZE][/FONT][/SIZE][/FONT]

به نظر خودم که کد درست هستش چون ما فقط داخل تابع limit متغیر a رو سرعتش رو تغییر میدیم که متناسب با 2 بار هراخوانی این تابع باید سرعت اولین پرارمتر این تابع تغییر بکنه ولی مشکلی که هست
2توپ که بهم میرسن سرعتشون میشه صفر!
مممون
 
آخرین ویرایش:

the_king

مدیرکل انجمن
فرمول :
کد:
[FONT=Consolas][SIZE=2][FONT=Consolas][SIZE=2]a.Speed = ((a.Weight - b.Weight) * a.Speed) / (a.Weight + b.Weight);
[/SIZE][/FONT][/SIZE][/FONT]

به نظر خودم که کد درست هستش چون ما فقط داخل تابع limit متغیر a رو سرعتش رو تغییر میدیم که متناسب با 2 بار هراخوانی این تابع باید سرعت اولین پرارمتر این تابع تغییر بکنه ولی مشکلی که هست
2توپ که بهم میرسن سرعتشون میشه صفر!
مممون

این فرمول تون کاری به سرعت b نداره؟ اگه می خواهید یک مساله فیزیکی واقعی رو شبیه سازی کنید از فرمول های واقعی استفاده کنید.
 

mhabat

Member
سلام.
خوب حق با شماست باید از فرمول های اثبات شده استفاده کرد.
این که b رو سرعتش رو تغییر ندادم چون یه تصور غلطی از روند کار داشتم.
در حال حاظر تابع limit رو باز نویسی کردم به این شکل که در برخورد دو جسم با هم سرعت هریک از اجسام بعد از برخورد به درستی محسابه بشه.
فرمول رو میشه از لینک زیر مشاهده و بررسی کرد:
http://www.convertalot.com/elastic_collision_calculator.html
(البته لینک رو قرار دادم شاید یه روزی چنین کاری رو افراد دیگه هم خواستن انجام بدن)
چون حرکت راست به چپ و چپ به راست در فرمول با تفاوت داشته باشه واسه همین منم داخل تابع از شرط هایی برای این کار استفاده کردم.

من از 2 تا متغیر برای وزن و 2 متغیر برای سرعت استفاده کردم:
کد:
 float VA = 1, VB = 1;
        float WA = 2, WB = 1.5F;
 private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            CheckLimit(_ballA, _ballB, VA * (_ballA.Direction == Ball.Directions.LeftToRight ? 1 : -1));
            CheckLimit(_ballB, _ballA, VB* (_ballB.Direction == Ball.Directions.LeftToRight ? 1 : -1));
            pictureBox1.Invalidate();
        }

خوب حالا کافیه ما بیام بگیم که وقتی 2 گلوله بهم برخورد کردند مطابق با فرمول بالا سرعتشون چغدر میشه!
:
کد:
 private void CheckLimit(Ball a, Ball b, float x)
        {
            float v1, v2;
            float limit;
            switch (a.Direction)
            {
                case Ball.Directions.LeftToRight:
                    if (b.Right >= a.Left)
                    {
                        limit = b.Left;
                    }
                    else
                    {
                        limit = pictureBox1.Width;
                    }
                    if (a.Right + x >= limit)
                    {
                        if(limit==b.Left)
                        {
                            v1 = VA;
                            v2 = VB;
                            if (a == _ballA && b.Direction==Ball.Directions.RightToLeft)
                            {
                                VA = (((WA - WB) * v1) + (-1 * 2 * WB * v2)) / (WA + WB);
                                VB = ((2 * WA * v1) - ((WA - WB) * v2 * -1)) / (WA + WB);
                            }
                            if (a == _ballA && b.Direction == Ball.Directions.LeftToRight)
                            {
                                VA = (((WA - WB) * v1) + (2 * WB * v2)) / (WA + WB);
                                VB = ((2 * WA * v1) - ((WA - WB) * v2)) / (WA + WB);
                            }
                            if (a == _ballB && b.Direction == Ball.Directions.RightToLeft)
                            {
                                VA = (((WA - WB) * v1 * -1) + (2 * WB * v2)) / (WA + WB);
                                VB = ((2 * WA * v1 * -1) - ((WA - WB) * v2)) / (WA + WB);
                            }
                            if (a == _ballB && b.Direction == Ball.Directions.LeftToRight)
                            {
                                VA = (((WA - WB) * v1 * +1) + (2 * WB * v2)) / (WA + WB);
                                VB = ((2 * WA * v1 * +1) - ((WA - WB) * v2)) / (WA + WB);
                            }
                            MessageBox.Show(VA+","+VB);
                            backgroundWorker1.CancelAsync();
                            
                        }
                        a.Right = limit - 1;
                        a.Direction = Ball.Directions.RightToLeft;
                    }
                    else
                    {
                        a.Offset(x, 0);
                    }
                    break;
                default:
                    if (b.Left <= a.Right)
                    {
                        limit = b.Right;
                    }
                    else
                    {
                        limit = 0;
                    }
                    if (a.Left + x <= limit)
                    {
                        if (limit == b.Right)
                        {
                            v1 = VA;
                            v2 = VB;
                            if (a == _ballA && b.Direction == Ball.Directions.RightToLeft)
                            {
                                VA = (((WA - WB) * v1 * -1) + (-1 * 2 * WB * v2)) / (WA + WB);
                                VB = ((2 * WA * v1 * -1) - ((WA - WB) * v2 * -1)) / (WA + WB);
                            }
                            if (a == _ballA && b.Direction == Ball.Directions.LeftToRight)
                            {
                                VA = (((WA - WB) * v1 * -1) + (2 * WB * v2)) / (WA + WB);
                                VB = ((2 * WA * v1 * -1) - ((WA - WB) * v2)) / (WA + WB);
                            }
                            if (a == _ballB && b.Direction == Ball.Directions.RightToLeft)
                            {
                                VA = (((WA - WB) * v1 * -1) + (2 * WB * v2 * -1)) / (WA + WB);
                                VB = ((2 * WA * v1 * -1) - ((WA - WB) * v2 * -1)) / (WA + WB);
                            }
                            if (a == _ballB && b.Direction == Ball.Directions.LeftToRight)
                            {
                                VA = (((WA - WB) * v1 * +1) + (2 * WB * v2 * -1)) / (WA + WB);
                                VB = ((2 * WA * v1 * +1) - ((WA - WB) * v2 * -1)) / (WA + WB);
                            }
                            MessageBox.Show(VA + "," + VB);
                            MessageBox.Show(a.Bounds.Location.ToString());
                    
                            backgroundWorker1.CancelAsync();
                        }
                        a.Left = limit + 1;
                        a.Direction = Ball.Directions.LeftToRight;
                    }
                    else
                    {
                        a.Offset(x, 0);
                    }
                    break;
            }
        }
خوب حالا برنامه رو که اجرا می کنم بهش گفتم که موقعه برخورد از طریقmessagebox سرعت گلوله رو بعد از بخورد نمایش بده و بعدش backgroundworker غیر فعال بشه.
که اتفاقی که می افته این هست که هنگام برخورد اعداد نا درستی رو نمایش میده
ولی خوب چون ما از async استفاده کردیم یک بار دیگه پیغام نمایش داده میشه که میشه فهمید این پیغام از ترد قبلی بوده که جاری شده
و داخل پیغام دومیه دقیقا سرعت درست بعد از برخورد دو گلوله رو نمایش میده.

من میدونم async چطور کار می کنه روالش چطوره ولی سوالی که دارم این هستش که
چطور هنوز برخورد اول محاسبه نشده برخورد دوم محاسبه شده؟(برخورد دوم فکر کنم همون اعداد نادرستی ه که گفتم نمایش داد بار اول)

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

حالا برنامه رو که اجرا می کنید بار اول همون محاسبه برخورد دوم نمایش داده میشه + محلش برخورد دوم که در محلی خارج از فرم هستش
بعدش پیغام دیگه نمایش داده میشه که محاسبه برخورد اول رو نمایش میده که سرعت ها بعد از برخود به درتس نشون میده + محل برخورد که در محلی داخل فرم هستش.

مطمئن هستم که متوجه شدین چی گفتم

با تشکر فراوان.

پروژه رو هم ضمیمه کردم که دیگه تمام و کمال توضیح داده باشم.

یورول ما
 

پیوست ها

  • Balls1.rar
    74.8 کیلوبایت · بازدیدها: 0

جدیدترین ارسال ها

بالا