আপনার পদার্থবিজ্ঞানের সিমুলেশন লুপটি উন্নত করার জন্য প্রয়োজনীয় পদক্ষেপ এখানে।
1. টাইমসটেপ
আপনার কোড সহ আমি যে প্রধান সমস্যাটি দেখতে পাচ্ছি তা হ'ল এটি পদার্থবিজ্ঞানের পদক্ষেপের জন্য অ্যাকাউন্ট করে না। এটি সুস্পষ্ট হওয়া উচিত যে এর সাথে কিছু ভুল আছে Position += Velocity;
কারণ ইউনিটগুলি মেলে না। হয় Velocity
প্রকৃতপক্ষে গতি নয়, বা কিছু অনুপস্থিত।
এমনকি যদি আপনার গতিবেগ এবং মাধ্যাকর্ষণ মানগুলি এমনভাবে পরিমাপ করা হয় যা প্রতিটি ফ্রেম এক সময় ইউনিটে ঘটে থাকে 1
(যার অর্থ উদাহরণস্বরূপ Velocity
আসলে দূরত্বটি এক সেকেন্ডে ভ্রমণ করা হয়েছিল), সময় অবশ্যই আপনার কোডের কোথাও উপস্থিত হওয়া উচিত , হয় স্পষ্টতই (ভেরিয়েবলগুলি স্থির করে যাতে করে) তাদের নামগুলি তারা কী আসলে সঞ্চয় করে) বা স্পষ্টতই (টাইমস্টেপ প্রবর্তন করে ) প্রতিফলিত করে। আমি বিশ্বাস করি সবচেয়ে সহজ কাজটি হচ্ছে সময় ইউনিট ঘোষণা করা:
float TimeStep = 1.0;
এবং যে মানটি এটি প্রয়োজন সেখানেই ব্যবহার করুন:
Velocity += Physics.Gravity.Force * TimeStep;
Position += Velocity * TimeStep;
...
মনে রাখবেন যে কোনও শালীন সংকলক এর দ্বারা গুণাগুলি সহজ করে দেবে 1.0
, যাতে অংশটি জিনিসগুলিকে ধীর করে না।
এখন Position += Velocity * TimeStep
এখনও ঠিক সঠিক নয় ( কেন এই সমস্যাটি বোঝার জন্য এই প্রশ্নটি দেখুন ) তবে এটি সম্ভবত আপাতত করবে।
এছাড়াও, এই অ্যাকাউন্টে সময় নেওয়া প্রয়োজন:
Velocity *= Physics.Air.Resistance;
এটি ঠিক করতে কিছুটা কৌশলযুক্ত; একটি সম্ভাব্য উপায় হ'ল:
Velocity -= Vector2(Math.Pow(Physics.Air.Resistance.X, TimeStep),
Math.Pow(Physics.Air.Resistance.Y, TimeStep))
* Velocity;
2. ডাবল আপডেট
বাউন্স করার সময় আপনি কী করেন তা পরীক্ষা করুন (কেবলমাত্র প্রাসঙ্গিক কোড দেখানো হয়েছে):
Position += Velocity * TimeStep;
if (Position.Y < 0)
{
Velocity.Y = -Velocity.Y * Physics.Surfaces.Grass;
Position.Y = Position.Y + Velocity.Y * TimeStep;
}
আপনি দেখতে পাচ্ছেন যে TimeStep
বাউন্সের সময় দুবার ব্যবহৃত হয়। এটি মূলত বলটিকে নিজের আপডেট করার জন্য দ্বিগুণ সময় দেয়। পরিবর্তে এটি হওয়া উচিত:
Position += Velocity * TimeStep;
if (Position.Y < 0)
{
/* First, stop at Y = 0 and count how much time is left */
float RemainingTime = -Position.Y / Velocity.Y;
Position.Y = 0;
/* Then, start from Y = 0 and only use how much time was left */
Velocity.Y = -Velocity.Y * Physics.Surfaces.Grass;
Position.Y = Velocity.Y * RemainingTime;
}
3. মাধ্যাকর্ষণ
কোডের এই অংশটি এখনই পরীক্ষা করুন:
if(Position.Y < GraphicsViewport.Height - Texture.Height)
{
Velocity += Physics.Gravity.Force * TimeStep;
}
আপনি ফ্রেমের পুরো সময়কালের জন্য মাধ্যাকর্ষণ যোগ করুন। কিন্তু বলটি যদি সেই ফ্রেমের সময় আসলে বাউন্স করে? তারপরে গতিবেগটি উল্টে যাবে, তবে যে মাধ্যাকর্ষণটি যুক্ত হয়েছিল তা বলটিকে মাটি থেকে দূরে সরিয়ে দেবে! সুতরাং বাউন্স করার সময় অতিরিক্ত মাধ্যাকর্ষণ অপসারণ করতে হবে , তারপরে সঠিক দিকে পুনরায় যুক্ত করতে হবে।
এটি ঘটতে পারে এমনকি সঠিক দিকটিতে মাধ্যাকর্ষণ পুনরায় যুক্ত করাও বেগটি খুব বেশি গতিবেগ ঘটাবে। এড়াতে, আপনি হয় অভিকর্ষজ সংযোজন এড়িয়ে যেতে পারেন (সর্বোপরি, এটি এতটা নয় এবং এটি কেবল একটি ফ্রেম স্থায়ী করে) বা বাতা বেগ শূন্যে।
4. স্থির কোড
এবং এখানে সম্পূর্ণ আপডেট হওয়া কোডটি রয়েছে:
public void Update()
{
float TimeStep = 1.0;
Update(TimeStep);
}
public void Update(float TimeStep)
{
float RemainingTime;
// Apply gravity if we're not already on the ground
if(Position.Y < GraphicsViewport.Height - Texture.Height)
{
Velocity += Physics.Gravity.Force * TimeStep;
}
Velocity -= Vector2(Math.Pow(Physics.Air.Resistance.X, RemainingTime),
Math.Pow(Physics.Air.Resistance.Y, RemainingTime))
* Velocity;
Position += Velocity * TimeStep;
if (Position.X < 0 || Position.X > GraphicsViewport.Width - Texture.Width)
{
// We've hit a vertical (side) boundary
if (Position.X < 0)
{
RemainingTime = -Position.X / Velocity.X;
Position.X = 0;
}
else
{
RemainingTime = (Position.X - (GraphicsViewport.Width - Texture.Width)) / Velocity.X;
Position.X = GraphicsViewport.Width - Texture.Width;
}
// Apply friction
Velocity -= Vector2(Math.Pow(Physics.Surfaces.Concrete.X, RemainingTime),
Math.Pow(Physics.Surfaces.Concrete.Y, RemainingTime))
* Velocity;
// Invert velocity
Velocity.X = -Velocity.X;
Position.X = Position.X + Velocity.X * RemainingTime;
}
if (Position.Y < 0 || Position.Y > GraphicsViewport.Height - Texture.Height)
{
// We've hit a horizontal boundary
if (Position.Y < 0)
{
RemainingTime = -Position.Y / Velocity.Y;
Position.Y = 0;
}
else
{
RemainingTime = (Position.Y - (GraphicsViewport.Height - Texture.Height)) / Velocity.Y;
Position.Y = GraphicsViewport.Height - Texture.Height;
}
// Remove excess gravity
Velocity.Y -= RemainingTime * Physics.Gravity.Force;
// Apply friction
Velocity -= Vector2(Math.Pow(Physics.Surfaces.Grass.X, RemainingTime),
Math.Pow(Physics.Surfaces.Grass.Y, RemainingTime))
* Velocity;
// Invert velocity
Velocity.Y = -Velocity.Y;
// Re-add excess gravity
float OldVelocityY = Velocity.Y;
Velocity.Y += RemainingTime * Physics.Gravity.Force;
// If velocity changed sign again, clamp it to zero
if (Velocity.Y * OldVelocityY <= 0)
Velocity.Y = 0;
Position.Y = Position.Y + Velocity.Y * RemainingTime;
}
}
5. আরও সংযোজন
এমনকি উন্নত সিমুলেশন স্থায়িত্বের জন্য, আপনি উচ্চতর ফ্রিকোয়েন্সিতে আপনার পদার্থবিজ্ঞানের সিমুলেশন চালানোর সিদ্ধান্ত নিতে পারেন। এই তৈরি করা হয় তুচ্ছ উপরে জড়িত পরিবর্তন দ্বারা TimeStep
, কারণ আপনি শুধু অনেক খন্ডে হিসাবে আপনার ফ্রেম বিভক্ত করতে হিসাবে আপনি ইচ্ছুক প্রয়োজন। এই ক্ষেত্রে:
public void Update()
{
float TimeStep = 1.0;
Update(TimeStep / 4);
Update(TimeStep / 4);
Update(TimeStep / 4);
Update(TimeStep / 4);
}