Java 动画仿真中逻辑与图形的分离

Java 动画仿真中逻辑与图形的分离,java,animation,graphics,simulation,Java,Animation,Graphics,Simulation,我需要写一个程序,就像一个动画模拟的一些过程。不必详细说明,就会有蜗牛(讲师的想法)在棋盘形的表面上移动,每个矩形(我称之为单元)都有(x,y)坐标 我在将这个模拟的逻辑与图形分离时遇到了一个问题。例如: 我有一门Snail课。它存储蜗牛的坐标并计算其行为。当它确定蜗牛应该从(x,y)移动到(a,b)时,我需要设置该移动的动画,因此我还必须以像素为单位计算蜗牛的位置,并且我需要在一段时间内重复该操作,以便蜗牛流畅地移动,而不是跳跃。如果不是因为我不想在Snail类中这样做,这不会是一个问题,因为

我需要写一个程序,就像一个动画模拟的一些过程。不必详细说明,就会有蜗牛(讲师的想法)在棋盘形的表面上移动,每个矩形(我称之为单元)都有(x,y)坐标

我在将这个模拟的逻辑与图形分离时遇到了一个问题。例如:

我有一门
Snail
课。它存储蜗牛的坐标并计算其行为。当它确定蜗牛应该从(x,y)移动到(a,b)时,我需要设置该移动的动画,因此我还必须以像素为单位计算蜗牛的位置,并且我需要在一段时间内重复该操作,以便蜗牛流畅地移动,而不是跳跃。如果不是因为我不想在
Snail
类中这样做,这不会是一个问题,因为它严格地与图形相关,与逻辑没有任何关系

我不能仅仅做一个循环,根据棋盘上的坐标画出蜗牛,因为它不能反映它流畅的运动,只能反映当前的位置

我现在最好的想法是通过
GraphicSnail
扩展
Snail
,它将额外计算和存储属性,如Snail的像素位置,但对我来说,这似乎不够独立

提前感谢您的帮助。

您可能需要使用

使用中间接口进行分离:

public interface SnailObserver {
   void update(Snail snail);
}
然后让图形相关类实现这个接口。我不知道您正在使用哪个库(如果有的话)来渲染图形。如果您使用的是JavaFX之类的东西,
GraphicSnail
类也可以从
ImageView
或其他东西继承

public class GraphicSnail implements SnailObserver {

    @Overrride
    public void update(Snail snail) {
        // Use snail.getX() and snail.getY() to obtain 
        // position of the snail and perform whatever
        // graphical updates you wish to make
    }      

}
最后,这就是
Snail
类的外观。请注意另一个字段,它包含对
GraphicSnail
的引用,但通过
SnailObserver
接口进行引用。这就是分离的所在。请注意,您还可以存储此类观察者的完整列表。在任何情况下,关键的部分是在
Snail
对象的状态发生变化时,对观察者对象调用
update()
方法,从而使观察者意识到发生了变化。然后,观察者对象检查
Snail
对象的当前状态,并相应地修改其自身的状态

public class Snail {

    private double x;
    private double y;
    private SnailObserver observer;

    public void move() {
        // Move the snail and then notify the observer
        // that the snail has changed like so: 
        observer.update(this);
    }

    public void registerObserver(SnailObserver observer) {
        this.observer = observer;
        observer.update(this); // initial sync
    }

    public double getX() {
        return x;
    }

    public double getY() {
        return y;
    }

} 
最后,在创建
Snail
GraphicSnail
对象后,不要忘记注册观察者:

Snail snail = new Snail();
GraphicSnail graphicSnail = new GraphicSnail();
snail.registerObserver(graphicSnail);
希望这有帮助,
stepan

与Stephan的答案类似,在模拟的模型类中实现。创建独立的蒙皮类,这些蒙皮类负责可观察模型类的视觉表示。创建蒙皮时,为蒙皮指定对模型的引用,并在蒙皮中向模型的可侦听属性添加侦听器,以便蒙皮可以根据需要对模型状态更改作出反应

有关此方法的示例,请参见本文中的Square和SquareSkin以及Board和BoardSkin类。以下是摘录:

class Square {
  enum State { EMPTY, NOUGHT, CROSS }

  private final SquareSkin skin;

  private ReadOnlyObjectWrapper<State> state = new ReadOnlyObjectWrapper<>(State.EMPTY);
  public ReadOnlyObjectProperty<State> stateProperty() {
    return state.getReadOnlyProperty();
  }
  public State getState() {
    return state.get();
  }

  private final Game game;

  public Square(Game game) {
    this.game = game;

    skin = new SquareSkin(this);
  }

  public void pressed() {
    if (!game.isGameOver() && state.get() == State.EMPTY) {
      state.set(game.getCurrentPlayer());
      game.boardUpdated();
      game.nextTurn();
    }
  }

  public Node getSkin() {
    return skin;
  }
}

class SquareSkin extends StackPane {
  static final Image noughtImage = new Image(
      "http://icons.iconarchive.com/icons/double-j-design/origami-colored-pencil/128/green-cd-icon.png"
  );
  static final Image crossImage = new Image(
      "http://icons.iconarchive.com/icons/double-j-design/origami-colored-pencil/128/blue-cross-icon.png"
  );

  private final ImageView imageView = new ImageView();

  SquareSkin(final Square square) {
    getStyleClass().add("square");

    imageView.setMouseTransparent(true);

    getChildren().setAll(imageView);
    setPrefSize(crossImage.getHeight() + 20, crossImage.getHeight() + 20);

    setOnMousePressed(new EventHandler<MouseEvent>() {
      @Override public void handle(MouseEvent mouseEvent) {
        square.pressed();
      }
    });

    square.stateProperty().addListener(new ChangeListener<Square.State>() {
      @Override public void changed(ObservableValue<? extends Square.State> observableValue, Square.State oldState, Square.State state) {
        switch (state) {
          case EMPTY:  imageView.setImage(null);        break;
          case NOUGHT: imageView.setImage(noughtImage); break;
          case CROSS:  imageView.setImage(crossImage);  break;
        }
      }
    });
  }
}
classsquare{
枚举状态{空、零、交叉}
私人皮肤;
private ReadOnlyObjectWrapper state=新的ReadOnlyObjectWrapper(state.EMPTY);
public ReadOnlyObjectProperty stateProperty(){
返回状态。getReadOnlyProperty();
}
公共状态getState(){
返回状态get();
}
私人决赛;
公众广场(游戏){
这个游戏=游戏;
皮肤=新的方形皮肤(此);
}
公众假期({
如果(!game.isGameOver()&&state.get()==state.EMPTY){
state.set(game.getCurrentPlayer());
game.boardUpdated();
game.nextTurn();
}
}
公共节点getSkin(){
返回皮肤;
}
}
类SquareSkin扩展了StackPane{
静态最终图像noughtImage=新图像(
"http://icons.iconarchive.com/icons/double-j-design/origami-colored-pencil/128/green-cd-icon.png"
);
静态最终图像crossImage=新图像(
"http://icons.iconarchive.com/icons/double-j-design/origami-colored-pencil/128/blue-cross-icon.png"
);
private final ImageView ImageView=新建ImageView();
方形皮肤(最终方形){
getStyleClass().add(“square”);
imageView.setMouseTransparent(true);
getChildren().setAll(imageView);
setPrefSize(crossImage.getHeight()+20,crossImage.getHeight()+20);
setOnMousePressed(新的EventHandler(){
@重写公共无效句柄(MouseEvent MouseEvent){
正方形。按下();
}
});
square.stateProperty().addListener(新的ChangeListener()){
@覆盖公共无效已更改(ObservableValue