javascript中奇怪的碰撞检测行为;游戏引擎'

strange collision detection behavior in javascript 'game engine.'

本文关键字:游戏 引擎 碰撞检测 javascript      更新时间:2023-09-26

如果你玩我设置的"游戏"一段时间,你会发现化身在跳到第二个平台上时正好掉到了第二个。我简直无法理解为什么会这样。我知道这可能比应该发布的代码多,但我束手无策,也许有人会很快发现我做错了什么。有问题的"游戏"。只需使用箭头键即可进行跑步和跳跃。

http://jsfiddle.net/QGB69/

//RequestAnimationFrame shim. 
window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;

//Initializing canvas and world
var canvas = document.getElementById('viewport');
var ctx = canvas.getContext('2d');
var keysDown = [];
var currentPlatform = 0;
//some helpful variables
var canvasHeight = $('#viewport').height();
var canvasWidth = $('#viewport').width();

//Add keycodes to 'keysDown' array
$(document).keydown(function (e) {
    if ($.inArray(e.which, keysDown) === -1) {
        keysDown.push(e.keyCode);
    }
});

//Remove keycodes from 'keysDown' array
$(document).keyup(function (e) {
    if ($.inArray(e.which, keysDown) > -1) {
        keysDown = $.grep(keysDown, function (n, i) {
            return n !== e.which;
        });
    }
});

//Avatar object, lots of attributes, great import!
var avatar = {};
avatar.xPos = 50;
avatar.yPos = 50;
avatar.accl = 0.55;
avatar.decel = 0.85;
avatar.jReduction = 1.25;
avatar.direction = null;
avatar.stopping = false;
avatar.avatarHeight = 50;
avatar.avatarWidth = 25;
avatar.fallTime = 0;
avatar.isGrounded = false;
avatar.isJumping = false;
avatar.endJump = false;
avatar.jump = 18;
avatar.j = avatar.jump;
avatar.jStrength = 0.55;
avatar.speed = 13;
avatar.prevXPos = 0;
avatar.xDelta = 0;
avatar.xVelocity = 0;
avatar.yVelocity = 0;
avatar.yBottom = 0;
avatar.xAlignment = 0;

avatar.collDetect = function (args) {
    avatar.yBottom = avatar.yPos + avatar.avatarHeight;
    avatar.xPosRight = avatar.xPos + avatar.avatarWidth;

    for (i = 0; i < arguments.length; i++) {
        if (avatar.yBottom > arguments[i].boxTop) {
            if (avatar.xPos > arguments[i].xPos) {
                if (avatar.direction === 'left' && avatar.xDelta > avatar.xPos - arguments[i].xPosRight) {
                    avatar.xPos = arguments[i].xPosRight;
                    avatar.xStop();
                } else if (avatar.direction === 'left' && avatar.xPos <= arguments[i].xPosRight) {
                    avatar.xPos = arguments[i].xPosRight;
                    avatar.xStop();
                }
            } else if (avatar.xPos < arguments[i].xPos) {
                if (avatar.direction === 'right' && avatar.xDelta > arguments[i].xPos - avatar.xPosRight) {
                    avatar.xPos = arguments[i].xPos - avatar.avatarWidth;
                    avatar.xStop();
                } else if (avatar.direction === 'right' && avatar.xPos >= arguments[i].xPos) {
                    avatar.xPos = arguments[i].xPos - avatar.avatarWidth;
                    avatar.xStop();
                }
            }
        }
        if (avatar.xPos > arguments[i].xPos - avatar.avatarWidth && avatar.xPos < arguments[i].xPos + arguments[i].boxWidth) {
            currentPlatform = arguments[i].boxHeight;
        } else {
            currentPlatform = 0;
        }
    }
};
avatar.xStop = function () {
    avatar.xVelocity = 0;
    avatar.xDelta = 0;
    avatar.stopping = false;
    avatar.direction = null;
};
//First obstacle. Good luck gettin' over this one, avatar! 
function Box(xPos, boxWidth, boxHeight, boxColor) {
    this.xPos = xPos;
    this.boxWidth = boxWidth;
    this.boxHeight = boxHeight;
    this.boxColor = boxColor;
    this.xPosRight = xPos + boxWidth;
    this.boxTop = canvasHeight - boxHeight;
}
function renderBoxes(n) {
    for (i = 0; i < arguments.length; i++) {
        ctx.fillStyle = arguments[i].boxColor;
        ctx.fillRect(arguments[i].xPos,
        canvasHeight - arguments[i].boxHeight,
        arguments[i].boxWidth,
        arguments[i].boxHeight);
    }
}
var box1 = new Box(100, 50, 100, 'gray');
var box2 = new Box(300, 50, 125, 'green');

//physics object. Properties of the world     
var physx = {};
physx.gravity = 1;
physx.colliding = false;
physx.fallTimeModifier = 0.5;

//Big movement function. The action's in here!
function moveIt() {
    //Jump!
    if ($.inArray(38, keysDown) > -1) {
        if (avatar.j > 0) {
            avatar.isGrounded = false;
            avatar.isJumping = true;
            avatar.yPos -= avatar.j;
            avatar.yVelocity = avatar.j;
            avatar.j -= avatar.jStrength;
        } else if (avatar.j <= 0) {
            avatar.isJumping = false;
        }
    }
    //End Jump, initiated when the user lets off the jump key mid-jump.
    if (avatar.endJump === true) {
        if (avatar.j > 0) {
            avatar.j -= avatar.jReduction;
            avatar.yPos -= avatar.j;
        } else if (avatar.j <= 0) {
            avatar.isJumping = false;
            avatar.endJump = false;
        }
    }
    $(document).keyup(function (e) {
        if (e.which === 38 && avatar.isJumping === true) {
            avatar.endJump = true;
        }
    });
    //Accounting for deceleration when avatar stops.
    if (avatar.stopping === true) {
        if ((avatar.xVelocity - avatar.decel) <= 0) {
            avatar.xStop();
            return;
        }
        if (avatar.direction === 'right') {
            avatar.xPos += avatar.xVelocity;
            avatar.xVelocity -= avatar.decel;
            avatar.xDelta = avatar.xVelocity;
        }
        if (avatar.direction === 'left') {
            avatar.xPos -= avatar.xVelocity;
            avatar.xVelocity -= avatar.decel;
            avatar.xDelta = avatar.xVelocity
        }
    }

    //Correcting glitchy stopping behavior when conflicting left/right keycodes present in 'keysDown' array
    if ($.inArray(37, keysDown) > -1 && $.inArray(39, keysDown) > -1) {
        avatar.stopping = true;
    }
    //right
    if ($.inArray(39, keysDown) > -1) {
        if (avatar.stopping === false) {
            avatar.direction = 'right';
            if (avatar.xVelocity >= avatar.speed) {
                avatar.xPos += avatar.speed;
                avatar.xDelta = avatar.speed;
            } else {
                avatar.xPos += avatar.xVelocity;
                avatar.xVelocity += avatar.accl;
                avatar.xDelta = avatar.xVelocity;
            }
        }
    }

    //left
    if ($.inArray(37, keysDown) > -1) {
        if (avatar.stopping === false) {
            avatar.direction = 'left';
            if (avatar.xVelocity >= avatar.speed) {
                avatar.xPos -= avatar.speed;
                avatar.xDelta = avatar.speed;
            } else {
                avatar.xPos -= avatar.xVelocity;
                avatar.xVelocity += avatar.accl;
                avatar.xDeta = avatar.xVelocity;
            }
        }
    }

    //Set avatar.isStopping to true when 
    $(document).keyup(function (e) {
        if (e.which === 39 || e.which === 37) {
            avatar.stopping = true;
        }
    });
}
//Gravity function. Keep him on the dang ground!
function grav() {
    if (avatar.isJumping) {
        return;
    }
    if (avatar.yPos >= (canvasHeight - currentPlatform) - avatar.avatarHeight) {
        avatar.isGrounded = true;
        avatar.fallTime = 0;
    } else {
        if ((avatar.fallTime * physx.gravity) > (((canvasHeight - currentPlatform) - avatar.avatarHeight) - avatar.yPos)) {
            avatar.yPos = (canvasHeight - currentPlatform) - avatar.avatarHeight;
            avatar.isGrounded = true;
            avatar.j = avatar.jump;
            avatar.fallTime = 0;
        } else {
            avatar.yPos += avatar.fallTime * physx.gravity;
            avatar.fallTime += physx.fallTimeModifier;
        }
    }
}

//Render the dang thing, ya dingus!
function render() {
    ctx.clearRect(0, 0, canvasWidth, canvasHeight);
    renderBoxes(box1, box2);
    avatar.collDetect(box2, box1);
    grav();
    ctx.fillStyle = 'red';
    ctx.fillRect(avatar.xPos,
    avatar.yPos,
    avatar.avatarWidth,
    avatar.avatarHeight);
    moveIt();

谢谢!

我注意到您在冲突检测调用中颠倒了方框的顺序,看起来您几乎已经明白了这一点。不过,我会继续告诉你它在哪里。

if (avatar.xPos > arguments[i].xPos - avatar.avatarWidth && avatar.xPos < arguments[i].xPos + arguments[i].boxWidth) {
            currentPlatform = arguments[i].boxHeight;
        } else {
            currentPlatform = 0;
        }

这一点在一个循环中。因为您使用if/else,所以它始终只适用于第二个框。这意味着,即使第一次循环设置currentPlatform=X,第二次循环也将设置currentPlatform=0。

对于这个特定的问题,请尝试

if (avatar.xPos > arguments[i].xPos - avatar.avatarWidth && avatar.xPos < arguments[i].xPos + arguments[i].boxWidth) {
            currentPlatform = arguments[i].boxHeight;
            break; // <-- here
        } else {
            currentPlatform = 0;
        }