如果您要使用类似 Doom 的视角,您应该将查看区域想象为平行四边形,而不是矩形。想象一下,在你的角色背后是一个有自己的位置和角度的摄影师。
敌人的屏幕位置与摄像机和敌人的角度有关。
//indicates where on the screen an enemy should be drawn.
//-1 represents the leftmost part of the screen,
//and 1 is the rightmost.
//Anything larger or smaller is off the edge of the screen and should not be drawn.
float calculateXPosition(camera, enemy){
//the camera man can see anything 30 degrees to the left or right of its line of sight.
//This number is arbitrary; adjust to your own tastes.
frustumWidth = 60;
//the angle between the enemy and the camera, in relation to the x axis.
angle = atan2(enemy.y - camera.y, enemy.x - camera.x);
//the angle of the enemy, in relation to the camera's line of sight. If the enemy is on-camera, this should be less than frustumWidth/2.
objectiveAngle = camera.angle - angle;
//scale down from [-frustumWidth/2, frustumWidth/2] to [-1, 1]
return objectiveAngle / (frustrumWidth / 2);
}
这些图表可视化了我在这里使用的变量所代表的含义:
一旦在 [-1, 1] 范围内有了“X 位置”,就应该很容易将其转换为像素坐标。例如,如果你的屏幕是 500 像素宽,你可以这样做((calculateXPosition(camera, enemy) + 1) / 2) * 500;
编辑:
您可以根据点的高度和与相机的距离执行类似的操作来查找点的 y 坐标。
(我不确定你应该如何定义敌人和相机的高度 - 任何数字都可以,只要它们与笛卡尔网格的 x 和 y 尺寸设置的比例相匹配。)
//this gives you a number between -1 and 1, just as calculateXPosition does.
//-1 is the bottom of the screen, 1 is the top.
float getYPosition(pointHeight, cameraHeight, distanceFromCamera){
frustrumWidth = 60;
relativeHeight = pointHeight - cameraHeight;
angle = atan2(relativeHeight, distanceFromCamera);
return angle / (frustrumWidth / 2);
}
你可以调用该方法两次来确定敌人顶部和底部的y位置:
distanceFromCamera = sqrt((enemy.x - camera.x)^2 + (enemy.y - camera.y)^2);
topBoundary = convertToPixels(getYPosition(enemy.height, camera.height, distanceFromCamera));
bottomBoundary = convertToPixels(getYPosition(0, camera.height, distanceFromCamera));
这应该为您提供足够的信息来正确缩放和定位敌人的精灵。
(除此之外:两种方法中的 frustrumWidths 不需要相同 - 实际上,如果您要绘制的屏幕是矩形,它们应该是不同的。x frustrum 和 y frustrum 的比率应该相等屏幕宽度和高度的比例。)