हल अप टेलीपोर्ट, अंतिम एल्गोरिथ्म के लिए पोस्ट के निचले देख नहीं करना चाहिएटकराव का पता लगाने वस्तु
पृष्ठभूमि: मैं जे एस और एचटीएमएल कैनवास तत्व का उपयोग कर एक 2 डी प्लेटफ़ॉर्मर पर काम कर रहा हूँ। स्तर का नक्शा टाइल-आधारित है, लेकिन खिलाड़ी टाइल्स पर क्लैंप नहीं है। मैं "Tiny Platformer" on Code inComplete में उल्लिखित टक्कर पहचान एल्गोरिदम का उपयोग कर रहा हूं। यह मोटे तौर पर एक किनारे के मामले (या '' लेज 'केस) को छोड़कर काम करता है।
समस्या:
खिलाड़ी नीचे गिर रही है और यह भी सही चलती, दीवार में। जैसे ही यह गिरता है, यह आधार की ऊंचाई तक पहुंचता है। इसके बजाय खिलाड़ी को टेलीपोर्टिंग के बिना सामान्य रूप से गिरना चाहिए।
क्या इस व्यवहार को रोकने के लिए एल्गोरिदम बदलने का कोई तरीका है? यदि नहीं, तो क्या आप एक वैकल्पिक टक्कर पहचान एल्गोरिदम का सुझाव दे सकते हैं? आदर्श रूप से कोई भी फिक्स खिलाड़ी को हमेशा गिरने का अनुमान नहीं लगाएगा, क्योंकि खेल में खिलाड़ी की गिरावट की दिशा ऊपर/नीचे/बाएं/दाएं के बीच स्विच होती है।
एल्गोरिथ्म:
खिलाड़ी की नई स्थिति कोई टकराव मानते हुए गणना की जाती है। (नीचे दिए गए कोड में नहीं दिखाया गया है)
getBorderTiles
नामक एक फ़ंक्शन एक ऑब्जेक्ट (प्लेयर) लेता है और प्रत्येक खिलाड़ी के 4 कोनों को स्पर्श करने वाली टाइल्स देता है। चूंकि खिलाड़ी टाइल से बड़ा नहीं है, इसलिए उन सीमाओं की टाइलें आवश्यक रूप से एकमात्र टाइल्स हैं जो खिलाड़ी स्पर्श कर रही हैं। ध्यान दें कि इनमें से कुछ टाइल्स समान हो सकते हैं। उदाहरण के लिए यदि खिलाड़ी केवल एक कॉलम पर कब्जा कर रहा है, तो बाएं-दाएं/दाएं-नीचे की टाइलें बाएं-नीचे/दाएं-नीचे की टाइलें समान होंगी। यदि ऐसा होता है,getBorderTiles
अभी भी सभी चार टाइल्स लौटाता है, लेकिन कुछ समान होंगे।यह यह देखने के लिए कि क्या वे ठोस हैं, इन स्तर सीमाओं (2 डी सरणी) में इन सीमाओं की जांच करता है। यदि एक टाइल ठोस है, तो वस्तु उस टाइल के साथ टकराने जा रही है।
यह ऊपर/नीचे/बाएं/दाएं टकराव का परीक्षण करता है। यदि खिलाड़ी नीचे गिर रहा है और नीचे टाइल के साथ टकरा रहा है लेकिन संबंधित टाइल के साथ टकरा नहीं रहा है, तो खिलाड़ी नीचे गिर रहा है। यदि खिलाड़ी बाएं टाइल के साथ बाएं और टकराने जा रहा है लेकिन संबंधित दाएं टाइल से टकरा नहीं रहा है, तो यह बायीं तरफ है। आदि ऊपर/नीचे चेक बाएं/दाएं चेक से पहले किए जाते हैं। बाएं/दाएं चेक करने से पहले ऊपर/नीचे टकराव होने पर वेरिएबल टाइल स्टोर करने वाले चर समायोजित किए जाते हैं। उदाहरण के लिए यदि खिलाड़ी नीचे गिर जाता है, तो उसे ऊपर की टाइल में धकेल दिया जाएगा, इसलिए बीएल/बीआर टाइल्स अब टीएल/टीआर टाइल्स के समान हैं।
खिलाड़ी का एक्स, वाई, और गति समायोजित दिशाओं के आधार पर समायोजित की जाती है।
क्यों एल्गोरिथ्म विफल रहता है:
नीचे दाईं टाइल ठोस है लेकिन शीर्ष ठीक है, तो (4 कदम) खिलाड़ी नीचे टकरा और (5 कदम नहीं है) यह धक्का दिया गया है। इसके अलावा, यह बीआर टाइल के साथ टकराता है लेकिन बीएल नहीं, इसलिए यह सही हो जाता है और बाएं धक्का दिया जाता है। अंत में, खिलाड़ी को ऊपर और बाईं ओर बाईं ओर प्रस्तुत किया जाता है। असल में यह teleported है।
समाधान पर प्रयास: मैंने इसे ठीक करने का प्रयास किया, लेकिन यह केवल एक और मुद्दा बनाया। मैंने एक चेक जोड़ा ताकि खिलाड़ी केवल टाइल के साथ टकराए, अगर वह उस टाइल के भीतर कुछ दूरी है (3px कहें)। यदि खिलाड़ी केवल बीआर टाइल में मुश्किल से था, तो एल्गोरिदम नीचे टक्कर दर्ज नहीं करेगा, इसलिए खिलाड़ी टेलीपोर्ट नहीं करेगा। हालांकि, अगर खिलाड़ी किसी अन्य परिदृश्य में जमीन पर गिर गया, तो टक्कर को तब तक स्वीकार नहीं किया जब तक कि खिलाड़ी जमीन पर बहुत दूर नहीं हो जाता। खिलाड़ी जमीन पर थोड़ा सा गिरने पर चिल्लाया, जमीन के शीर्ष पर वापस धकेल दिया, फिर गिर गया, आदि
इसे पढ़ने के लिए धन्यवाद। मेरे लिए आपका फ़ीडबैक वाकई सराहनीय है।
वर्तमान एल्गोरिथ्म कोड:
var borderTiles = getBorderTiles(object), //returns 0 (a falsy value) for a tile if it does not fall within the level
tileTL = borderTiles.topLeft,
tileTR = borderTiles.topRight,
tileBL = borderTiles.bottomLeft,
tileBR = borderTiles.bottomRight,
coordsBR = getTopLeftXYCoordinateOfTile(tileBR), //(x, y) coordinates refer to top left corner of tile
xRight = coordsBR.x, //x of the right tile(s) (useful for adjusting object's position since it falls in middle of 4 tiles)
yBottom = coordsBR.y, //y of the bottom tile(s) (useful for adjusting object's position since it falls in middle of 4 tiles)
typeTL = tileTL ? level.map[tileTL.row][tileTL.col] : -1, //if tileTL is in the level, gets its type, otherwise -1
typeTR = tileTR ? level.map[tileTR.row][tileTR.col] : -1,
typeBL = tileBL ? level.map[tileBL.row][tileBL.col] : -1,
typeBR = tileBR ? level.map[tileBR.row][tileBR.col] : -1,
collidesTL = typeTL == TILETYPE.SOLID, //true if the tile is solid
collidesTR = typeTR == TILETYPE.SOLID,
collidesBL = typeBL == TILETYPE.SOLID,
collidesBR = typeBR == TILETYPE.SOLID,
collidesUp = false,
collidesDown = false,
collidesLeft = false,
collidesRight = false;
//down and up
if (object.vy < 0 && ((collidesTL && !collidesBL) || (collidesTR && !collidesBR))) {
collidesUp = true;
/*The object is pushed out of the bottom row, so the bottom row is now the top row. Change the collides__
variables as this affects collision testing, but is it not necessary to change the tile__ variables. */
collidesTL = collidesBL;
collidesTR = collidesBR;
} else if (object.vy > 0 && ((collidesBL && !collidesTL) || (collidesBR && !collidesTR))) {
collidesDown = true;
/*The object is pushed out of the bottom row, so the bottom row is now the top row. Change the collides__
variables as this affects collision testing, but is it not necessary to change the tile__ variables. */
collidesBL = collidesTL;
collidesBR = collidesTR;
}
//left and right
if (object.vx < 0 && ((collidesTL && !collidesTR) || (collidesBL && !collidesBR))) {
collidesLeft = true;
} else if (object.vx > 0 && ((collidesTR && !collidesTL) || (collidesBR && !collidesBL))) {
collidesRight = true;
}
if (collidesUp) {
object.vy = 0;
object.y = yBottom;
}
if (collidesDown) {
object.vy = 0;
object.y = yBottom - object.height;
}
if (collidesLeft) {
object.vx = 0;
object.x = xRight;
}
if (collidesRight) {
object.vx = 0;
object.x = xRight - object.width;
}
अद्यतन: maraca समाधान के साथ हल। एल्गोरिदम नीचे है। असल में यह परीक्षण करता है (एक्स तब वाई) और हल टकराव, और फिर यह परीक्षण (वाई तब एक्स) परीक्षण करता है और इस तरह टकराव हल करता है। एक छोटी दूरी पर चलने वाले खिलाड़ी में जो भी टेस्ट परिणाम होता है वह वह होता है जो इस्तेमाल होने वाला होता है।
दिलचस्प बात यह है कि जब खिलाड़ी शीर्ष और बाएं दिशा दोनों में टकरा रहा है तो इसके लिए एक विशेष मामला आवश्यक है। शायद यह इस तथ्य से संबंधित है कि खिलाड़ी (x, y) समन्वय अपने शीर्ष बाएं कोने पर है। इस मामले में, परीक्षण जो खिलाड़ी को एक लंबी दूरी पर ले जाने वाले परिणाम में उपयोग किया जाना चाहिए।
खिलाड़ी ब्लैक बॉक्स और पीले बॉक्स का प्रतिनिधित्व करता है जहां खिलाड़ी अगर यह अन्य परीक्षण (परीक्षण है कि खिलाड़ी के परिणामस्वरूप का इस्तेमाल किया था एक लंबे समय तक चलती होगा: यह इस gif में स्पष्ट है दूरी)। आदर्श रूप से खिलाड़ी को दीवार में नहीं जाना चाहिए, और इसके बजाय यह होना चाहिए जहां पीला बॉक्स है। इस प्रकार इस परिदृश्य में लंबी दूरी की परीक्षा का उपयोग किया जाना चाहिए।
यहां त्वरित और गंदे कार्यान्वयन है। यह बिल्कुल अनुकूलित नहीं है लेकिन उम्मीद है कि यह एल्गोरिदम के चरणों को स्पष्ट रूप से दिखाता है।
function handleCollision(object) {
var borderTiles = getBorderTiles(object), //returns 0 (a falsy value) for a tile if it does not fall within the level
tileTL = borderTiles.topLeft,
tileTR = borderTiles.topRight,
tileBL = borderTiles.bottomLeft,
tileBR = borderTiles.bottomRight,
coordsBR = getTopLeftXYCoordinateOfTile(tileBR), //(x, y) coordinates refer to top left corner of tile
xRight = coordsBR.x, //x of the right tile(s) (useful for adjusting object's position since it falls in middle of 4 tiles)
yBottom = coordsBR.y, //y of the bottom tile(s) (useful for adjusting object's position since it falls in middle of 4 tiles)
typeTL = tileTL ? level.map[tileTL.row][tileTL.col] : -1, //if tileTL is in the level, gets its type, otherwise -1
typeTR = tileTR ? level.map[tileTR.row][tileTR.col] : -1,
typeBL = tileBL ? level.map[tileBL.row][tileBL.col] : -1,
typeBR = tileBR ? level.map[tileBR.row][tileBR.col] : -1,
collidesTL = typeTL == TILETYPE.SOLID, //true if the tile is solid
collidesTR = typeTR == TILETYPE.SOLID,
collidesBL = typeBL == TILETYPE.SOLID,
collidesBR = typeBR == TILETYPE.SOLID,
collidesUp = false,
collidesDown = false,
collidesLeft = false,
collidesRight = false,
originalX = object.x, //the object's coordinates have already been adjusted according to its velocity, but not according to collisions
originalY = object.y,
px1 = originalX,
px2 = originalX,
py1 = originalY,
py2 = originalY,
vx1 = object.vx,
vx2 = object.vx,
vy1 = object.vy,
vy2 = object.vy,
d1 = 0,
d2 = 0,
conflict1 = false,
conflict2 = false,
tempCollidesTL = collidesTL,
tempCollidesTR = collidesTR,
tempCollidesBL = collidesBL,
tempCollidesBR = collidesBR;
//left and right
//step 1.1
if (object.vx > 0) {
if (collidesTR || collidesBR) {
vx1 = 0;
px1 = xRight - object.width;
conflict1 = true;
tempCollidesTR = false;
tempCollidesBR = false;
}
}
if (object.vx < 0) {
if (collidesTL || collidesBL) {
vx1 = 0;
px1 = xRight;
conflict1 = true;
tempCollidesTL = false;
tempCollidesBL = false;
collidesLeft = true;
}
}
//step 2.1
if (object.vy > 0) {
if (tempCollidesBL || tempCollidesBR) {
vy1 = 0;
py1 = yBottom - object.height;
}
}
if (object.vy < 0) {
if (tempCollidesTL || tempCollidesTR) {
vy1 = 0;
py1 = yBottom;
collidesUp = true;
}
}
//step 3.1
if (conflict1) {
d1 = Math.abs(px1 - originalX) + Math.abs(py1 - originalY);
} else {
object.x = px1;
object.y = py1;
object.vx = vx1;
object.vy = vy1;
return; //(the player's x and y position already correspond to its non-colliding values)
}
//reset the tempCollides variables for another runthrough
tempCollidesTL = collidesTL;
tempCollidesTR = collidesTR;
tempCollidesBL = collidesBL;
tempCollidesBR = collidesBR;
//step 1.2
if (object.vy > 0) {
if (collidesBL || collidesBR) {
vy2 = 0;
py2 = yBottom - object.height;
conflict2 = true;
tempCollidesBL = false;
tempCollidesBR = false;
}
}
if (object.vy < 0) {
if (collidesTL || collidesTR) {
vy2 = 0;
py2 = yBottom;
conflict2 = true;
tempCollidesTL = false;
tempCollidesTR = false;
}
}
//step 2.2
if (object.vx > 0) {
if (tempCollidesTR || tempCollidesBR) {
vx2 = 0;
px2 = xRight - object.width;
conflict2 = true;
}
}
if (object.vx < 0) {
if (tempCollidesTL || tempCollidesTL) {
vx2 = 0;
px2 = xRight;
conflict2 = true;
}
}
//step 3.2
if (conflict2) {
d2 = Math.abs(px2 - originalX) + Math.abs(py2 - originalY);
console.log("d1: " + d1 + "; d2: " + d2);
} else {
object.x = px1;
object.y = py1;
object.vx = vx1;
object.vy = vy1;
return;
}
//step 5
//special case: when colliding with the ceiling and left side (in which case the top right and bottom left tiles are solid)
if (collidesTR && collidesBL) {
if (d1 <= d2) {
object.x = px2;
object.y = py2;
object.vx = vx2;
object.vy = vy2;
} else {
object.x = px1;
object.y = py1;
object.vx = vx1;
object.vy = vy1;
}
return;
}
if (d1 <= d2) {
object.x = px1;
object.y = py1;
object.vx = vx1;
object.vy = vy1;
} else {
object.x = px2;
object.y = py2;
object.vx = vx2;
object.vy = vy2;
}
}
यह भी ध्यान रखें नीचे की ओर जब टाइल ऊंचाई के निचले आधे में हो रहा teleports देखते हैं कि। यदि आप उदाहरण में लैंडिंग कर रहे हैं तो समस्या और भी गंभीर होगी, तो आपको गति 0 के साथ मध्य हवा में एक स्थिति में नीचे भेज दिया जाएगा और फिर उस गुरुत्वाकर्षण के बाद थोड़ा सा बाएं स्थानांतरित हो जाएगा फिर से मुझे लगता है। – maraca