2

我正在尝试制作一个 java 2d 游戏,而且总体上看起来效果很好。唯一的问题是,我不知道如何设置我的“增量”时间,以使运动在 30 FPS 和 1000 FPS 上移动相同。

这是我的实体类代码:

import java.awt.Rectangle;

import map.Tile;
import graphics.Sprite;

public class Entity {
private String name;
private float positionx, positiony; // Current coordinate
private int targetx,targety; // Target coordinate
private double vx, vy; // Current vector
private double lx, ly; // Last vector

private float speed;
private Sprite sprite;

public Entity(String name, int x, int y, Sprite sprite){
    this.name = name;
    this.speed = 1f;

    this.positionx = x;
    this.positiony = y;

    this.sprite = sprite;

    main.Main.e.addEntity(this); // These kind of calls are ugly, and should be fixed.
}

public void remove(){
    main.Main.e.removeEntity(this);
    sprite.remove();
}

public void setVector(double vx, double vy){
    this.vx = vx;
    this.vy = vy;
}

public void update(long delta){
    //Multiply modifier to make it act the same on 30 fps as 1000 fps.
    vx = vx*delta;
    vy = vy*delta;

    // Calculate vector
    double distance = Math.sqrt((vx * vx) + (vy * vy));

    if(distance > 0){ // Have we reached the target yet?
        vx = ((vx / distance));
        vy = ((vy / distance));
    }else{
        vx = 0;
        vy = 0;
    }

    //Check collision with objects:
    Rectangle rx = new Rectangle((int) (vx+positionx), (int)positiony, 32, 32);
    Rectangle ry = new Rectangle((int) positionx, (int)(vy+positiony), 32, 32);

    for(Entity e : main.Main.e.getEntities()){
        if(this != e){
            if(isIntersecting(rx, e.getBounds())){
                vx = 0; // Disallow x direction.
            }
            if(isIntersecting(ry, e.getBounds())){
                vy = 0; // Disallow y direction.
            }
        }
    }

    //Check tiles:
    for(Tile t : main.Main.m.getNeighbours(positionx,positiony)){
        if(t.isBlocking()){
            if(isIntersecting(rx, t.getBounds())){
                vx = 0;
            }
            if(isIntersecting(ry, t.getBounds())){
                vy = 0;
            }
        }
    }

    //Update the position:
    positionx += vx*speed;
    positiony += vy*speed;

    //Animate:
    animate(vx, vy);
}

public boolean isIntersecting(Rectangle r1, Rectangle r2){
    return r1.intersects(r2);
}

public Rectangle getBounds(){
    return new Rectangle((int) positionx,(int) positiony,32,32);
}

public void setMoveTo(int x, int y){
    this.targetx = x;
    this.targety = y;
}

//This function is used by the bots, and not on players (they are setting the vector and use update directly):
public void moveTo(long delta){
    setVector((targetx-positionx),(targety-positiony));
    update(delta);
}

public void animate(double dx, double dy){
    sprite.setPosition((int)positionx, (int)positiony);

    if(dx > 0){
        sprite.setAnimation(0, 7, 100); // Walk right.
    }else if(dx < 0){
        sprite.setAnimation(1, 7, 100); // Walk left.
    }else{
        if(lx > 0){
            sprite.setAnimation(2, 3, 200); // Stand right.
        }else if(lx < 0){
            sprite.setAnimation(3, 3, 200); // Stand left.
        }
    }

    lx = dx;
    ly = dy;
}
}

我经常遇到的两个问题:

1# 游戏在 60FPS 和 500FPS 上的运行方式不同。

2# 游戏在 60FPS 和 500FPS 上运行相同,但是我的碰撞搞砸了,我不能移动到距离另一个对象超过 15px 的地方。我想我需要实现类似的东西:如果我不能靠近 10px,那么将它移近 10px,但我不知道如何实现它。

如何正确实施?那会有很大帮助!

4

1 回答 1

2

最简单的方法是考虑,如果您想要一个恒定速度的恒定位移,您可以相对于 30 FPS 的增量缩放所有增量,如下所示:

在此处输入图像描述

因此,如果您以 60 FPS 运行但想要与 30FPS alpha 相同的位移,则(1/30)/(1/60) = 2

所以删除vx = vx*delta; vy = vy*delta;

并将您的位置更新更改为

alpha = (1.0/30)*delta;
positionx += alpha*vx*speed;
positiony += alpha*vy*speed;

这只是一个粗略的解决方案,让我知道它是如何工作的,我稍后会加入并更新以获得更正确的解决方案(考虑到由于渲染和计算需要一些时间,delta 并不总是 1/FPS)。

编辑:变量 delta 稍后会出现。但是一些优化:

  1. 您不需要在 Y 和 X 中都构造一个矩形来进行碰撞检测,尝试绘制两个矩形,如果它们相交,它们会在两个轴上这样做。只需从中创建一个矩形vx + posx, vy+posy并检查是否与此相交。
  2. 以上应该是你的碰撞检查的一半。如需进一步优化,请考虑使用QuadTree可在此处找到的实现。

  3. 对于“块状”碰撞测试的问题(从块状对象停止 X 像素)。您可以使用伪代码执行以下操作:

     if(new position will make this colide)
       while(this.position is more than one pixel away from blocking object)
         keep moving one pixel
    

这将使您在距离目标 1px 处停止。

于 2013-08-09T15:09:40.283 回答