我正在制作一个游戏,其中世界是使用cos函数生成的,它看起来像这样:
在任何任何x点我都能找到具有Game.getHeight(x)函数的表面高度;
现在我想制作一个可以射击的球,它可以从表面正常反弹。我做的方式大约有5%的弹跳效果(即其他弹射到地面,以不切实际的角度反弹等)。我怎么能改进它?我正在使用的代码:
球(子弹)课程:
package game;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.state.StateBasedGame;
public class Bullet {
private final static float speed = 400;
private final static float acc = 250;
public final static float radius = 8;
private int changed;
private float x, y, speedx, speedy;
public Bullet (float x, float y, float tx, float ty) {
this.x = x;
this.y = y;
changed = 0;
float angle;
angle = (float) ((float) Math.atan2(y - ty, tx - x) + .5 * Math.PI);
speedx = (float) (Math.sin(angle) * speed);
speedy = (float) (Math.cos(angle) * speed);
}
public void update(GameContainer gc, StateBasedGame game, int delta) {
x += speedx * (float) delta / 1000;
y += speedy * (float) delta / 1000;
speedy += acc * (float) delta / 1000;
}
public void render (Graphics g) {
g.drawOval(x-radius, y-radius, 2*radius, 2*radius);
g.drawLine(x, y, (float) (32*Math.sin(getDirection()) + x), (float) (32*Math.cos(getDirection()) + y));
}
public void setTrajectory (float angle) {
float spd = speedx + speedy;
speedx = (float) (Math.sin(angle) * spd);
speedy = (float) (Math.cos(angle) * spd);
changed += 1;
}
public float getDirection () {
return (float) ((float) Math.atan2(-speedy, speedx) + .5 * Math.PI);
}
public boolean destroy() {
return (changed >= 2); //destroy it after it has bounced off once
}
public float getX() {
return x;
}
public float getY() {
return y;
}
}
游戏更新方法的片段,其中检查了碰撞:
if (bullet != null) {
bullet.update(gc, game, delta);
for (float x = bullet.getX() - bullet.radius; x <= bullet.getX()
+ bullet.radius; x++) {
if (Point2D.distance(x, getHeight(x), bullet.getX(),
bullet.getY()) <= bullet.radius) {
float angle;
angle = (float) (Math.atan2(getHeight(x - 1)
- getHeight(x + 1), -2));
angle += (angle - bullet.getDirection());
bullet.setTrajectory(angle);
if (bullet.destroy()) {
bullet = null;
}
break;
}
}
答案 0 :(得分:2)
鉴于您的第一个问题可能是子弹与地面之间的碰撞检测,所以当我做了类似的事情时,the separating axis theorem帮助了很多。
就反作用力而言,它取决于你想要的外观。如果您想要相当准确,我建议您查找suvat运动方程,以及与coefficient of restitution
相关的事项如果你想要不那么精确,计算你击中曲面的角度,将曲面的切线作为0度,并将关于法线的向量反转到平面
答案 1 :(得分:2)
如果你需要物理模拟在数学上是准确的,你需要求解一个方程来找到球的轨迹和地面之间的交点,然后你需要找到地面曲线的导数,你可以然后通过将交点的坐标插入地面的导数来找到交点处的曲线的梯度。找到渐变后,您需要将球反射到碰撞点的法线上。你需要在碰撞时找到球的速度,并在碰撞点反射穿过地面的法线。法线垂直于梯度。如果您想要像大多数真实世界碰撞一样进行非弹性碰撞,那么您还需要一个乘数来降低弹跳后的速度。对于额外的点,你应该考虑在一次迭代/时间步骤中球碰撞后不久就会碰撞。
如果你只需要使弹跳看起来足够逼真,但不需要它在数学上是准确的,你可以通过在碰撞的左右两侧找到几个像素的曲线高度来找到渐变指出并找到差异。您再次需要找到法线并反映正常速度。
答案 2 :(得分:1)
看起来你的角度是正确的,但它不是你需要做的全部:
以下是代码:
angle += Math.PI / 2
PointF normal = new PointF(Math.cos(angle), Math.sin(angle));
float length = oldMovmentVector.x * normal.x + oldMovmentVector.y * normal.y;
newMovementVector = new PointF();
newMovementVector.x = oldMovmentVector.x - (1 + bounciness) * (length) * normal.x;
newMovementVector.y = oldMovmentVector.y - (1 + bounciness) * (length) * normal.y;
// bounciness is between 0 and 1
// newMovementVector is the new movement vector for your ball.
这应该让你开始。
答案 3 :(得分:1)
你关心的是地面的normal。当球与该切片中的世界发生碰撞时,您使用球的轨迹和曲面的法线,并使用该信息运行计算。
你应该能够提前计算出来。您可以将世界分成一组n像素宽的方框(2d数组或四叉树),每个方框都小于球的直径,并计算每个的平均法线。
切片有许多优点,如果你可以逃脱它。您需要多个单点接触来计算表面方向并准确地反弹球。对盒子进行预计算可以让您进行简单的查找,而不是对地面数据进行采样(或重新计算),并且必须计算每次碰撞的正常情况。