我想把一个球(带有一个图像)扔到一个二维场景中,当它到达一定距离时检查它是否有碰撞。但我无法正确地“飞行”。好像这已被问过一百万次,但随着我发现的越多,我得到的就越混乱...... 现在我跟着this answer,但看起来,球的表现与我预期的非常不同。事实上,它移动到我的画布的左上方并变得太快太快 - 我可以通过将vz设置为0.01或类似来调整这个,但是然后我根本看不到一个球...
这是我感兴趣的对象(简称)/链接到full source。重要的部分是update()和render()
var ball = function(x,y) {
this.x = x;
this.y = y;
this.z = 0;
this.r = 0;
this.src = 'img/ball.png';
this.gravity = -0.097;
this.scaleX = 1;
this.scaleY = 1;
this.vx = 0;
this.vy = 3.0;
this.vz = 5.0;
this.isLoaded = false;
// update is called inside window.requestAnimationFrame game loop
this.update = function() {
if(this.isLoaded) {
// ball should fly 'into' the scene
this.x += this.vx;
this.y += this.vy;
this.z += this.vz;
// do more stuff like removing it when hit the ground or check for collision
//this.r += ?
this.vz += this.gravity;
}
};
// render is called inside window.requestAnimationFrame game loop after this.update()
this.render = function() {
if(this.isLoaded) {
var x = this.x / this.z;
var y = this.y / this.z;
this.scaleX = this.scaleX / this.z;
this.scaleY = this.scaleY / this.z;
var width = this.img.width * this.scaleX;
var height = this.img.height * this.scaleY;
canvasContext.drawImage(this.img, x, y, width, height);
}
};
// load image
var self = this;
this.img = new Image();
this.img.onLoad = function() {
self.isLoaded = true;
// update offset to spawn the ball in the middle of the click
self.x = this.width/2;
self.y = this.height/2;
// set radius for collision detection because the ball is round
self.r = this.x;
};
this.img.src = this.src;
}
我也想知道,当使用requestAnimationFrame渲染~60fps的画布时,速度的哪些参数应该是合适的,以获得“自然”的飞行动画
我非常感激,如果有人能指出我正确的方向(也使用伪代码解释课程的逻辑)。
由于
答案 0 :(得分:1)
我认为最好的方法是首先在公制系统中模拟情况。
speed = 30; // 30 meters per second or 108 km/hour -- quite fast ...
angle = 30 * pi/180; // 30 degree angle, moved to radians.
speed_x = speed * cos(angle);
speed_y = speed * sin(angle); // now you have initial direction vector
x_coord = 0;
y_coord = 0; // assuming quadrant 1 of traditional cartesian coordinate system
time_step = 1.0/60.0; // every frame...
// at most 100 meters and while not below ground
while (y_coord > 0 && x_coord < 100) {
x_coord += speed_x * time_step;
y_coord += speed_y * time_step;
speed_y -= 9.81 * time_step; // in one second the speed has changed 9.81m/s
// Final stage: ball shape, mass and viscosity of air causes a counter force
// that is proportional to the speed of the object. This is a funny part:
// just multiply each speed component separately by a factor (< 1.0)
// (You can calculate the actual factor by noticing that there is a limit for speed
// speed == (speed - 9.81 * time_step)*0.99, called _terminal velocity_
// if you know or guesstimate that, you don't need to remember _rho_,
// projected Area or any other terms for the counter force.
speed_x *= 0.99; speed_y *=0.99;
}
现在你将有一个时间/位置系列,从0,0开始(你可以用Excel或OpenOffice Calc计算)
speed_x speed_y position_x position_y time
25,9807687475 14,9999885096 0 0 0
25,72096106 14,6881236245 0,4286826843 0,2448020604 1 / 60
25,4637514494 14,3793773883 0,8530785418 0,4844583502 2 / 60
25,2091139349 14,0737186144 1,2732304407 0,7190203271
...
5,9296028059 -9,0687933774 33,0844238036 0,0565651137 147 / 60
5,8703067779 -9,1399704437 33,1822622499 -0,0957677271 148 / 60
从该表中可以首先估计击球的距离和时间。 它们分别为33,08米和2.45秒(或148帧)。通过在excel中继续模拟,人们还注意到终端速度将达到~58 km / h,这并不多。
确定最终速度为60 m / s或216 km / h是正确的,正确的衰减系数为0,9972824054451614。
现在唯一剩下的任务是决定屏幕的长度(以米为单位),并将pos_x,pos_y与正确的比例因子相乘。如果1024像素的屏幕是32米,那么每个像素将对应于3.125厘米。根据应用,人们可能希望“改善”现实并使球更大。
编辑:另一件事是如何在3D上投影。我建议你将前一个算法(或excel)生成的路径作为一个可见对象(由线段组成),你可以旋转和放大。平移。
答案 1 :(得分:1)
您所看到的不良行为的起因是您使用的投影,以(0,0)为中心,而且通常过于简单,看起来不太好。
你需要一个更完整的投影,包括中心,比例,...
我使用那个添加一点3d:
projectOnScreen : function(wx,wy,wz) {
var screenX = ... real X size of your canvas here ... ;
var screenY = ... real Y size of your canvas here ... ;
var scale = ... the scale you use between world / screen coordinates ...;
var ZOffset=3000; // the bigger, the less z has effet
var k =ZOffset; // coeficient to have projected point = point for z=0
var zScale =2.0; // the bigger, the more a change in Z will have effect
var worldCenterX=screenX/(2*scale);
var worldCenterY=screenY/(2*scale);
var sizeAt = ig.system.scale*k/(ZOffset+zScale*wz);
return {
x: screenX/2 + sizeAt * (wx-worldCenterX) ,
y: screenY/2 + sizeAt * (wy-worldCenterY) ,
sizeAt : sizeAt
}
}
显然,你可以根据你的游戏进行优化。例如,如果分辨率和比例不变,您可以在该功能之外计算一次参数 sizeAt是您必须应用于图像的缩放系数(canvas.scale)。
编辑:对于你的更新/渲染代码,正如Aki Suihkonen的帖子所指出的,你需要使用'dt',两次更新之间的时间。因此,如果您稍后更改每秒帧数(fps)或者如果您在游戏中暂时减速,则可以更改dt并且所有内容的行为仍然相同。
等式变为x + = vx * dt / ... / vx + = gravity * dt;
无论屏幕大小如何,您都应该具有相对于屏幕高度计算的速度和重力,以获得相同的行为
我也会使用负z来开始。首先要有一个更大的球。
我也会分开关注:
- 分别处理图像的加载。您的游戏应在加载所有必要资产后开始。一些免费的小框架可以为您做很多事情。举个例子:crafty.js,但有很多好的
- 相对于点击位置的调整和图像大小应在渲染中完成,而x,y只是鼠标坐标。
var currWidth = this.width *scaleAt, currHeight= this.height*scaleAt;
canvasContext.drawImage(this.img, x-currWidth/2, y-currHeight/2, currWidth, currHeight);
或者您可以使用画布来进行缩放。奖金是你可以这样轻松旋转:
ctx.save();
ctx.translate(x,y);
ctx.scale(scaleAt, scaleAt); // or scaleAt * worldToScreenScale if you have
// a scaling factor
// ctx.rotate(someAngle); // if you want...
ctx.drawImage(this.img, x-this.width/2, x-this.height/2);
ctx.restore();