যে লুপগুলির (যেমন হিউ বা রোটেশন) এর মধ্যে আমি কীভাবে ভাগ করব?


25

যৌথ অ্যানিমেশন উদাহরণ

ডেমো দেখুন

আমি ক্যানভাসের কেন্দ্রের চারপাশে, মাউস পয়েন্টারের কোণের দিকে জয়েন্টটি সহজেই ঘোরানোর চেষ্টা করছি। আমার কী কাজ আছে তবে আমি মাউসের কোণে যাওয়ার জন্য সবচেয়ে কমতম দূরত্বটি অ্যানিমেট করতে চাই। যখন সমস্যাটি অনুভূমিক লাইনে ( 3.14এবং -3.14) প্রায় কাছাকাছি চলে আসে তখন সমস্যা হয় । দিকটি কীভাবে স্যুইচ হয় এবং এটি প্রায় দীর্ঘ পথ পাবে তা দেখতে সেই অঞ্চলটি মাউসওভার করুন।

প্রাসঙ্গিক কোড

// ease the current angle to the target angle
joint.angle += ( joint.targetAngle - joint.angle ) * 0.1;

// get angle from joint to mouse
var dx = e.clientX - joint.x,
    dy = e.clientY - joint.y;  
joint.targetAngle = Math.atan2( dy, dx );

আমি কীভাবে এটিকে স্বল্পতম দূরত্বটি ঘুরতে পারি, এমনকি "ফাঁক পেরিয়ে"?


মডুলো ব্যবহার করুন। : ডি en.wikipedia.org/wiki/Modular_arithmetic
ভন Hilts

1
@ ভৌনহিল্টস নিশ্চিত নন যে আমি কীভাবে এটি আমার পরিস্থিতিটি ব্যবহার করব। আপনি আরও বিস্তারিত বলতে পারেন?
22-25

উত্তর:


11

এটি সর্বদা সর্বোত্তম পদ্ধতি নয় এবং এটি আরও গণনামূলকভাবে ব্যয়বহুল হতে পারে (যদিও এটি আপনার ডেটা কীভাবে সংরক্ষণ করবেন তার উপর নির্ভর করে) তবে আমি এই যুক্তিটি তৈরি করব যে 2D মানগুলি বেশিরভাগ ক্ষেত্রেই যুক্তিসঙ্গতভাবে কাজ করে। পছন্দসই কোণটি লিপ্পিং করার পরিবর্তে আপনি পছন্দসই স্বাভাবিক দিকনির্দেশক ভেক্টরকে লারপ করতে পারেন ।

"কোণে সবচেয়ে সংক্ষিপ্ততম পথটি বেছে নিন" পদ্ধতির উপর এই পদ্ধতির একটি সুবিধা হ'ল এটি যখন আপনি দু'বারেরও বেশি মানের মধ্যে বিভক্ত হওয়া প্রয়োজন তখন এটি কাজ করে।

হিউ মানগুলি লেখার সময়, আপনি hueকোনও [cos(hue), sin(hue)]ভেক্টরের সাথে প্রতিস্থাপন করতে পারেন ।

আপনার ক্ষেত্রে, স্বাভাবিকযুক্ত যৌথ দিকটি লিপিং করা:

// get normalised direction from joint to mouse
var dx = e.clientX - joint.x,
    dy = e.clientY - joint.y;
var len = Math.sqrt(dx * dx + dy * dy);
dx /= len ? len : 1.0; dy /= len ? len : 1.0;
// get current direction
var dirx = cos(joint.angle),
    diry = sin(joint.angle);
// ease the current direction to the target direction
dirx += (dx - dirx) * 0.1;
diry += (dy - diry) * 0.1;

joint.angle = Math.atan2(diry, dirx);

আপনি যদি 2 ডি ভেক্টর শ্রেণি ব্যবহার করতে পারেন তবে কোডটি ছোট হতে পারে। এই ক্ষেত্রে:

// get normalised direction from joint to mouse
var desired_dir = normalize(vec2(e.clientX, e.clientY) - joint);
// get current direction
var current_dir = vec2(cos(joint.angle), sin(joint.angle));
// ease the current direction to the target direction
current_dir += (desired_dir - current_dir) * 0.1;

joint.angle = Math.atan2(current_dir.y, current_dir.x);

আপনাকে ধন্যবাদ, এটি এখানে দুর্দান্ত কাজ করছে: codepen.io/jackrugile/pen/45c356f06f08ebea0e58daa4d06d204f আপনি কী করছেন তা আমি বেশিরভাগই বুঝতে পারি তবে আপনি কি স্বাভাবিকের সময় আপনার কোডের লাইন 5 এ আরও কিছুটা ব্যাখ্যা করতে পারবেন? ঠিক কী ঘটছে তা নিশ্চিত নয় dx /= len...
জ্যাক্রোগিল

1
কোনও ভেক্টরকে তার দৈর্ঘ্যের দ্বারা ভাগ করে নেওয়াকে নরমালাইজেশন বলে । এটির দৈর্ঘ্য ১ রয়েছে তা নিশ্চিত করে The len ? len : 1.0অংশটি শূন্যের দ্বারা বিভাজন এড়িয়ে চলেছে, বিরল ক্ষেত্রে মাউসটি ঠিক যৌথ অবস্থানে থাকে। এটা তোলে লিখিত হয়েছে পারে: if (len != 0) dx /= len;
সাম হোচেভার

-1। এই উত্তরটি বেশিরভাগ ক্ষেত্রে অনুকূল থেকে অনেক দূরে। যদি আপনি এবং মধ্যে interpolating হয় 180°? ভেক্টর আকারে: [1, 0]এবং [-1, 0]। ইন্টারপোলেটিং ভেক্টরগুলি আপনাকে হয় , 180°বা ক্ষেত্রে 0 টি ত্রুটি দ্বারা বিভাজন দেবে t=0.5
গুস্তাভো ম্যাকিয়েল

@ গুস্তাভোম্যাসিল এটি "বেশিরভাগ ক্ষেত্রে" নয়, এটি একটি খুব নির্দিষ্ট কোণার ক্ষেত্রে যা বাস্তবে কখনও ঘটে না। এছাড়াও, শূন্যের দ্বারা কোনও বিভাজন নেই, কোডটি পরীক্ষা করুন।
সাম হোচেভার

@ গুস্তাভোম্যাসিল আবার কোডটি পরীক্ষা করেছে, এটি আসলে অত্যন্ত সুরক্ষিত এবং ঠিক আপনার কোণার ক্ষেত্রে যেমনটি করা উচিত ঠিক তেমনই কাজ করে।
সাম হোচেভার

11

কৌশলটি মনে রাখতে হবে যে কোণগুলি (কমপক্ষে ইউক্লিডিয়ান স্পেসে) 2 * পিআই দ্বারা পর্যায়ক্রমিক হয়। যদি বর্তমান কোণ এবং লক্ষ্য কোণগুলির মধ্যে পার্থক্য খুব বেশি হয় (যেমন কার্সারটি সীমানা অতিক্রম করেছে), কেবল সেই অনুযায়ী 2 * পাই যোগ বা বিয়োগ করে বর্তমান কোণটি সামঞ্জস্য করুন।

এই ক্ষেত্রে, আপনি নিম্নলিখিতগুলি চেষ্টা করতে পারেন: (আমি জাভাস্ক্রিপ্টে এর আগে কখনও প্রোগ্রাম করি নি, তাই আমার কোডিং শৈলীটি ক্ষমা করুন))

  var dtheta = joint.targetAngle - joint.angle;
  if (dtheta > Math.PI) joint.angle += 2*Math.PI;
  else if (dtheta < -Math.PI) joint.angle -= 2*Math.PI;
  joint.angle += ( joint.targetAngle - joint.angle ) * joint.easing;

সম্পাদনা : এই বাস্তবায়নে, যৌথের কেন্দ্রের চারপাশে খুব দ্রুত কার্সারটি সরিয়ে ফেললে এটি ঝাঁকুনির কারণ হয়। এটি উদ্দেশ্যমূলক আচরণ, যেহেতু যৌথের কৌণিক বেগ সর্বদা আনুপাতিক dtheta। যদি এই আচরণটি অনাকাঙ্ক্ষিত হয় তবে জয়েন্টের কৌণিক ত্বরণে ক্যাপ রেখে সহজেই সমস্যাটি ঠিক করা যায়।

এটি করার জন্য, আমাদের যৌথ গতিবেগের উপর নজর রাখতে হবে এবং সর্বাধিক ত্বরণ চাপিয়ে দিতে হবে:

  joint = {
    // snip
    velocity: 0,
    maxAccel: 0.01
  },

তারপরে, আমাদের সুবিধার জন্য, আমরা একটি ক্লিপিং ফাংশন চালু করব:

function clip(x, min, max) {
  return x < min ? min : x > max ? max : x
}

এখন, আমাদের মুভমেন্ট কোডটি এর মতো দেখাচ্ছে। প্রথমত, আমরা প্রয়োজনীয় হিসাবে dthetaসামঞ্জস্য করে আগের মতো গণনা করি joint.angle:

  var dtheta = joint.targetAngle - joint.angle;
  if (dtheta > Math.PI) joint.angle += 2*Math.PI;
  else if (dtheta < -Math.PI) joint.angle -= 2*Math.PI;

তারপরে, জয়েন্টটি তাত্ক্ষণিকভাবে সরানোর পরিবর্তে, আমরা একটি লক্ষ্য বেগ গণনা করি এবং clipএটি আমাদের গ্রহণযোগ্য সীমার মধ্যে জোর করে ব্যবহার করি।

  var targetVel = ( joint.targetAngle - joint.angle ) * joint.easing;
  joint.velocity = clip(targetVel,
                        joint.velocity - joint.maxAccel,
                        joint.velocity + joint.maxAccel);
  joint.angle += joint.velocity;

এটি কেবলমাত্র একটি মাত্রায় গণনা সম্পাদন করার সময় দিকনির্দেশ স্যুইচ করার সময়ও মসৃণ গতি উত্পাদন করে। তদ্ব্যতীত, এটি যৌথ গতি এবং ত্বরণকে স্বাধীনভাবে সামঞ্জস্য করতে দেয়। ডেমোটি এখানে দেখুন: http://codepen.io/anon/pen/HGnDF/


এই পদ্ধতিটি সত্যিই কাছাকাছি আসে, তবে আমি যদি আমার মাউসটি খুব দ্রুত করি তবে এটি ভুল উপায়ে কিছুটা লাফিয়ে উঠতে শুরু করে। ডেমো এখানে, আমাকে যদি আমি এটি সঠিকভাবে প্রয়োগ না করি তবে আমাকে জানান: codepen.io/jackrugile/pen/db40aee91e1c0b693346e6cec4546e98
jackrugile

1
আপনি ভুল পথে কিছুটা লাফিয়ে কী বলতে চাইছেন তা আমি নিশ্চিত নই; আমার জন্য, যৌথ সর্বদা সঠিক পথে চলে। অবশ্যই, আপনি যদি খুব দ্রুত আপনার মাউসটিকে কেন্দ্রের চারপাশে সরিয়ে নিয়ে যান তবে আপনি জয়েন্টটি চালিয়ে যান এবং এটি এক দিক থেকে অন্য দিকে যেতে সরে যেতেই ঝাঁকুনি দেয়। এটিই উদ্দেশ্যমূলক আচরণ, যেহেতু ঘোরার গতি সর্বদা আনুপাতিক dtheta। আপনি কি যৌথ কিছুটা গতি পেতে চান?
ডেভিড ঝাং

যৌথ ছাড়িয়ে তৈরি জারকি গতি দূর করার জন্য আমার শেষ সম্পাদনাটি দেখুন।
ডেভিড জাং

আরে, আপনার ডেমো লিঙ্কটি চেষ্টা করে দেখুন তবে দেখে মনে হচ্ছে এটি কেবল আমার মূলটির দিকে ইঙ্গিত করছে। আপনি একটি নতুন সংরক্ষণ করেছেন? যদি তা ঠিক না থাকে তবে আমার আজকের রাতের পরের এই প্রয়োগ করতে এবং এটি কীভাবে সম্পাদন করে তা দেখতে সক্ষম হওয়া উচিত। আমি মনে করি আপনি নিজের সম্পাদনায় যা বর্ণনা করেছেন তার চেয়ে বেশি আমি যা খুঁজছিলাম is আপনার কাছে ফিরে আসবে, ধন্যবাদ!
jackrugile

1
ওহ, হ্যাঁ, আপনি ঠিক বলেছেন। দুঃখিত আমার ভুল. আমি এটা ঠিক করব।
ডেভিড ঝাং

3

আমি দেওয়া অন্যান্য উত্তর ভালবাসে। খুব প্রযুক্তিগত!

আপনি যদি চান তবে এটি সম্পাদন করার আমার কাছে খুব সহজ পদ্ধতি রয়েছে। আমরা এই উদাহরণগুলির জন্য কোণ অনুমান করব। ধারণাটি রঙের মতো অন্যান্য মান ধরণের ক্ষেত্রেও এক্সট্রপোলেট করা যেতে পারে।

double MAX_ANGLE = 360.0;
double startAngle = 300.0;
double endAngle = 15.0;
double distanceForward = 0.0;  // Clockwise
double distanceBackward = 0.0; // Counter-Clockwise

// Calculate both distances, forward and backward:
distanceForward = endAngle - startAngle;    // -285.00
distanceBackward = startAngle - endAngle;   // +285.00

// Which direction is shortest?
// Forward? (normalized to 75)
if (NormalizeAngle(distanceForward) < NormalizeAngle(distanceBackward)) {
    // Adjust for 360/0 degree wrap
    if (endAngle < startAngle) endAngle += MAX_ANGLE; // Will be above 360
}

// Backward? (normalized to 285)
else {
    // Adjust for 360/0 degree wrap
    if (endAngle > startAngle) endAngle -= MAX_ANGLE; // Will be below 0
}

// Now, Lerp between startAngle and endAngle. 

// EndAngle can be above 360 if wrapping clockwise past 0, or
// EndAngle can be below 0 if wrapping counter-clockwise before 0.
// Normalize each returned Lerp value to bring angle in range of 0 to 360 if required.  Most engines will automatically do this for you.


double NormalizeAngle(double angle) {
    while (angle < 0) 
        angle += MAX_ANGLE;
    while (angle >= MAX_ANGLE) 
        angle -= MAX_ANGLE;
    return angle;
}

আমি সবেমাত্র এটি ব্রাউজারে তৈরি করেছি এবং এটি কখনও পরীক্ষা করা হয়নি। আমি আশা করি প্রথম যুক্তিটিই সঠিকভাবে পেয়েছি।

[সম্পাদনা] 2017/06/02 - যুক্তিটি কিছুটা স্পষ্ট করেছেন।

দূরত্বের ফরোয়ার্ড এবং ডিস্টেন্সব্যাকওয়ার্ড গণনা করে শুরু করুন এবং ফলাফলকে (0-360) সীমা ছাড়িয়ে যাওয়ার অনুমতি দিন।

কোণগুলিকে সাধারণকরণগুলি সেই মানগুলিকে (0-360) এর মধ্যে ফিরিয়ে আনে। এটি করার জন্য, আপনি মানটি শূন্যের উপরে না হওয়া পর্যন্ত 360 যোগ করুন এবং মান 360 এর উপরে থাকলে 360 বিয়োগ করুন resulting ফলে শুরু / শেষ কোণগুলি সমতুল্য হবে (-285 75 এর সমান)

এরপরে আপনি দূরত্বের ফরোয়ার্ড বা ডিস্টেনব্যাকওয়ার্ডের মধ্যে সর্বনিম্ন নরমালাইজড কোণটি খুঁজে পাবেন। উদাহরণে দূরত্বের ফরওয়ার্ড 75 হয়, যা দূরত্ববাকওয়ার্ডের (300) এর স্বাভাবিক মানের চেয়ে ছোট।

যদি দূরত্বের ফরোয়ার্ডটি সর্বনিম্ন এবং শেষের এঙ্গেল <স্টার্টআঙ্গল হয় তবে 360 যোগ করে 360 এরও বেশি প্রান্তটি প্রসারিত করুন ((উদাহরণটিতে এটি 375 হয়ে যায়)।

যদি ডিস্টেন্সব্যাকওয়ার্ডটি সর্বনিম্ন এবং এন্ডএঙ্গল> স্টার্টআঙ্গেল হয় তবে 360 বিয়োগ করে এন্ডএঙ্গল 0 এর নীচে প্রসারিত করুন।

আপনি এখন সূচনাআঙ্গল (300) থেকে নতুন এন্ডল (375) তে লার্প করবেন। ইঞ্জিনটি আপনার জন্য 360 বিয়োগ করে স্বয়ংক্রিয়ভাবে 360 এর উপরে মানগুলি সমন্বয় করা উচিত। অন্যথায় আপনাকে ইঞ্জিনটি যদি আপনার জন্য মানগুলি স্বাভাবিক না করে তবে আপনাকে 300 থেকে 360 পর্যন্ত লার্প করতে হবে, 0 থেকে 15 পর্যন্ত লার্প করতে হবে।


ঠিক আছে, যদিও atan2ভিত্তিক ধারণাটি সোজা , তবুও এটি কিছুটা জায়গা এবং কিছুটা পারফরম্যান্স বাঁচাতে পারে (এক্স এবং ওয়াই সংরক্ষণ করার দরকার নেই, পাপ, কোস এবং অ্যাটান 2 খুব ঘন ঘন গণনা করার প্রয়োজন নেই)। আমি মনে করি কেন এই সমাধানটি সঠিক (এটি যেমন একটি বৃত্ত বা ক্ষেত্রের মধ্যে সবচেয়ে ছোট পথ বেছে নেওয়া যেমন এসএলআরপি কোয়েটারিয়ান্সের মতো করে) তার কিছুটা প্রসারিত করার উপযুক্ত। এই প্রশ্ন এবং উত্তরগুলি সম্প্রদায় উইকিতে রাখা উচিত কারণ এটি বেশিরভাগ গেমপ্লে প্রোগ্রামারদের মুখোমুখি really
টয়োড্রন

দুর্দান্ত উত্তর। @ ডেভিড জাং দ্বারা দেওয়া অন্যান্য উত্তরগুলিও আমি পছন্দ করি। তবে আমি যখনই দ্রুত পাল্টে ফেলি তখন আমি তার সম্পাদিত সমাধানটি ব্যবহার করে সবসময়ই একটি অদ্ভুত রসিকতা পাই। তবে আপনার উত্তরটি আমার গেমটিতে পুরোপুরি ফিট করে। আমি আপনার উত্তর পিছনে গণিত তত্ত্ব আগ্রহী। যদিও এটি দেখতে সহজ দেখাচ্ছে তবে এটি কেন স্পষ্ট নয় যে আমাদের কেন বিভিন্ন দিকের স্বাভাবিক কোণের দূরত্বের তুলনা করা উচিত।
newguy

আমি খুশি যে আমার কোড কাজ করে। যেমনটি উল্লেখ করা হয়েছে, এটি কেবলমাত্র ব্রাউজারে টাইপ করা হয়েছিল, আসল প্রকল্পে পরীক্ষা করা হয়নি। আমি এই প্রশ্নের উত্তরও মনে করতে পারি না (তিন বছর আগে)! তবে, এটির দিকে তাকিয়ে দেখে মনে হচ্ছে যে আমি মোট ডিগ্রির পার্থক্য তুলনা করতে এবং সংক্ষিপ্ততম পার্থক্যটি গ্রহণের জন্য পরীক্ষার মানগুলি এই সীমাটির বাইরে / আগে যাওয়ার অনুমতি দিতে মাত্র সীমা (0-360) প্রসারিত করছি। সাধারণকরণ কেবল সেই মানগুলি আবার (0-360) এর মধ্যে নিয়ে আসে। সুতরাং দূরত্ব ফরোয়ার্ড 75 (-285 + 360) হয়ে যায়, যা দূরত্ব ব্যাকওয়ার্ডের (285) এর চেয়ে ছোট, তাই এটিই সবচেয়ে কম দূরত্ব।
ডগ .এমসিফারলেন

যেহেতু দূরত্ব ফরোয়ার্ডটি সবচেয়ে কম দূরত্ব, তাই আইএফ ক্লজের প্রথম অংশটি ব্যবহৃত হয়। যেহেতু এন্ডএঙ্গল (15) স্টার্টএঙ্গল (300) এর চেয়ে কম, তাই আমরা 375 পেতে 360 যোগ করি So ইঞ্জিনটি আপনার জন্য 360 বিয়োগ করে স্বয়ংক্রিয়ভাবে 360 এর উপরে মানগুলি সামঞ্জস্য করবে।
ডগ .এমসিফারলেন 18

আমি আরও স্পষ্টতা যুক্ত করতে উত্তর সম্পাদনা করেছি।
ডগ .এমসিফার্লেন
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.