All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.almasb.fxgl.pathfinding.CellMoveComponent Maven / Gradle / Ivy

There is a newer version: 21.1
Show newest version
/*
 * FXGL - JavaFX Game Library. The MIT License (MIT).
 * Copyright (c) AlmasB ([email protected]).
 * See LICENSE for details.
 */

package com.almasb.fxgl.pathfinding;

import com.almasb.fxgl.core.collection.grid.Cell;
import com.almasb.fxgl.entity.component.Component;
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.ReadOnlyBooleanWrapper;

import static java.lang.Math.abs;

/**
 * Enables cell based movement for an entity.
 * Can potentially be used for any top-down movement, though may not be as effective.
 * For all entity position calculations, entity's anchored position is used.
 *
 * @author Almas Baimagambetov ([email protected])
 */
public final class CellMoveComponent extends Component {

    private int nextCellX;
    private int nextCellY;

    private int cellWidth;
    private int cellHeight;
    private double speed;
    private boolean isAllowRotation = false;

    private ReadOnlyBooleanWrapper isAtDestinationProp = new ReadOnlyBooleanWrapper(true);

    private boolean isMovingUp = false;
    private boolean isMovingDown = false;
    private boolean isMovingLeft = false;
    private boolean isMovingRight = false;

    public CellMoveComponent(int cellWidth, int cellHeight, double speed) {
        this.cellWidth = cellWidth;
        this.cellHeight = cellHeight;
        this.speed = speed;
    }

    public ReadOnlyBooleanProperty atDestinationProperty() {
        return isAtDestinationProp.getReadOnlyProperty();
    }

    /**
     * @return true if the entity has reached the destination cell and is no longer moving
     */
    public boolean isAtDestination() {
        return isAtDestinationProp.getValue();
    }

    /**
     * @return true if the entity has not yet reached its destination
     */
    public boolean isMoving() {
        return !isAtDestination();
    }

    public boolean isMovingUp() {
        return isMovingUp;
    }

    public boolean isMovingDown() {
        return isMovingDown;
    }

    public boolean isMovingLeft() {
        return isMovingLeft;
    }

    public boolean isMovingRight() {
        return isMovingRight;
    }

    public int getCellWidth() {
        return cellWidth;
    }

    public int getCellHeight() {
        return cellHeight;
    }

    public void setCellWidth(int cellWidth) {
        this.cellWidth = cellWidth;
    }

    public void setCellHeight(int cellHeight) {
        this.cellHeight = cellHeight;
    }

    public double getSpeed() {
        return speed;
    }

    public void setSpeed(double speed) {
        this.speed = speed;
    }

    /**
     * Note: entity's anchored position is used to compute this.
     *
     * @return the cell x where entity is currently at
     */
    public int getCellX() {
        return (int) (entity.getAnchoredPosition().getX() / cellWidth);
    }

    /**
     * Note: entity's anchored position is used to compute this.
     *
     * @return the cell y where entity is currently at
     */
    public int getCellY() {
        return (int) (entity.getAnchoredPosition().getY() / cellHeight);
    }

    /**
     * Sets (anchored) position of the entity to given cell.
     */
    public void setPositionToCell(Cell cell) {
        setPositionToCell(cell.getX(), cell.getY());
    }

    /**
     * Sets (anchored) position of the entity to given cell, identified using given cell X, Y.
     */
    public void setPositionToCell(int cellX, int cellY) {
        // cell center
        int cx = cellX * cellWidth + cellWidth / 2;
        int cy = cellY * cellHeight + cellHeight / 2;

        entity.setAnchoredPosition(cx, cy);

        isAtDestinationProp.setValue(true);
        isMovingLeft = false;
        isMovingRight = false;
        isMovingUp = false;
        isMovingDown = false;
    }

    public void moveToCell(Cell cell) {
        moveToCell(cell.getX(), cell.getY());
    }

    public void moveToCell(int cellX, int cellY) {
        nextCellX = cellX;
        nextCellY = cellY;

        isAtDestinationProp.set(false);
    }

    /**
     * Allows entity to rotate (only 4 directions) based on its velocity.
     */
    public CellMoveComponent allowRotation(boolean isAllowRotation) {
        this.isAllowRotation = isAllowRotation;
        return this;
    }

    @Override
    public void onUpdate(double tpf) {
        if (isAtDestination())
            return;

        double tpfSpeed = tpf * speed;

        // cell center
        int cx = nextCellX * cellWidth + cellWidth / 2;
        int cy = nextCellY * cellHeight + cellHeight / 2;

        var entityAnchoredPosition = entity.getAnchoredPosition();

        // move in x and y per frame
        double dx = cx - entityAnchoredPosition.getX();
        double dy = cy - entityAnchoredPosition.getY();

        if (isAllowRotation)
            updateRotation(dx, dy);

        int offsetX = (int) (entityAnchoredPosition.getX() - entity.getX());
        int offsetY = (int) (entityAnchoredPosition.getY() - entity.getY());

        if (abs(dx) <= tpfSpeed) {
            isMovingLeft = false;
            isMovingRight = false;
            entity.setX(cx - offsetX);
        } else {
            isMovingLeft = Math.signum(dx) < 0;
            isMovingRight = Math.signum(dx) > 0;
            entity.translateX(tpfSpeed * Math.signum(dx));
        }

        if (abs(dy) <= tpfSpeed) {
            isMovingUp = false;
            isMovingDown = false;
            entity.setY(cy - offsetY);
        } else {
            isMovingUp = Math.signum(dy) < 0;
            isMovingDown = Math.signum(dy) > 0;
            entity.translateY(tpfSpeed * Math.signum(dy));
        }

        // get position after movement
        entityAnchoredPosition = entity.getAnchoredPosition();

        if ((int) entityAnchoredPosition.getX() == cx && (int) entityAnchoredPosition.getY() == cy) {
            setPositionToCell(nextCellX, nextCellY);
        }
    }

    /**
     * @param dx move distance in X
     * @param dy move distance in Y
     */
    private void updateRotation(double dx, double dy) {
        if (dx > 0) {
            entity.setRotation(0);
        } else if (dx < 0) {
            entity.setRotation(180);
        } else if (dy > 0) {
            entity.setRotation(90);
        } else if (dy < 0) {
            entity.setRotation(270);
        }
    }

    @Override
    public boolean isComponentInjectionRequired() {
        return false;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy