Skip to content

Timer Component #1439

@AlmasB

Description

@AlmasB

Discussed in #1434

Originally posted by LifeDrainingCoding September 17, 2025
I wonder , why FXGL doesn't have Timer Component that will schedule task by given interval?

This will allows devs to execute tasks to each entity individually at individually calculated interval.

My use case: I have a Marble that's shoots projectiles , and when this projectile collides with another Marble that will lower it's radius or disappear. Shoot rate of Marble depends on it's radius, so i need update interval every radius change.

import com.almasb.fxgl.dsl.FXGL;
import com.almasb.fxgl.entity.Entity;
import com.almasb.fxgl.entity.SpawnData;
import com.almasb.fxgl.entity.components.BoundingBoxComponent;
import com.almasb.fxgl.entity.components.CollidableComponent;
import com.almasb.fxgl.entity.components.ViewComponent;
import com.almasb.fxgl.physics.BoundingShape;
import com.almasb.fxgl.physics.HitBox;
import com.almasb.fxgl.physics.PhysicsComponent;
import com.almasb.fxgl.physics.box2d.dynamics.BodyDef;
import com.almasb.fxgl.physics.box2d.dynamics.BodyType;
import com.almasb.fxgl.physics.box2d.dynamics.FixtureDef;
import com.lifedrained.fxglphysicstest.FXGLTest;
import com.lifedrained.fxglphysicstest.components.TimerComponent;
import com.lifedrained.fxglphysicstest.types.TypeOf;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.util.Duration;

import java.util.UUID;

import static com.lifedrained.fxglphysicstest.SpawnDataKeys.*;

public class Marble extends Entity {

    protected boolean isShooting =false;
    protected PhysicsComponent physics;
    protected BodyDef bodyDef;
    protected FixtureDef fixtureDef;
    protected ViewComponent view;
    protected BoundingBoxComponent bbox;
    protected TimerComponent timer = new TimerComponent();
    protected double radius, mass,area , density = 0;

    public Marble(SpawnData data) {
        super();
        setX(data.getX());
        setY(data.getY());
        setType(TypeOf.MARBLE_TYPE);
        radius = data.get(RADIUS);
        mass = data.get(MASS);

        bodyDef = new BodyDef();
        bodyDef.setUserData(UUID.randomUUID().toString());
        fixtureDef = new FixtureDef();
        fixtureDef.setFriction(0.1f);
        fixtureDef.setRestitution(0.1f);
        updateMassRelated(0);

        view = getComponent(ViewComponent.class);
        view.clearChildren();
        view.addChild(new Circle(radius, Color.RED));

        physics = new PhysicsComponent();
        physics.setBodyDef(bodyDef);
        physics.setFixtureDef(fixtureDef);
        physics.setBodyType(BodyType.DYNAMIC);

        bbox = getBoundingBoxComponent();
        bbox.addHitBox(new HitBox(BoundingShape.circle(radius)));

        addComponent(new CollidableComponent(true));
        addComponent(physics);
        addComponent(timer);
    }

    public void setRadius(double radius) {
        this.radius = radius;

        updateBody();
    }

    public void updateBody(){
        view.clearChildren();
        view.addChild(new Circle(radius, Color.RED));
        bbox.clearHitBoxes();
        bbox.addHitBox(new HitBox(BoundingShape.circle(radius)));
        setupTimer();
    }

    public void startShooting(){
        if (!isShooting){
            isShooting = true;
           setupTimer();
        }

    }

    public void  shoot(){
        if (isActive()) {

            double x, y;
            SpawnData spawnData =   new SpawnData(0, 0);
            spawnData.put(OWNER, this);
            Bullet bullet = new Bullet(spawnData);
            x = radius * Math.cos(bullet.getRadiansDirection())+getX();
            y = radius * Math.sin(bullet.getRadiansDirection())+getY();
            bullet.setPosition(x, y);

            FXGL.getGameWorld().addEntity(bullet);
        }
    }
    private void setupTimer(){
        timer.clear();
        timer.runAtInterval(this::shoot, Duration.millis(2000*(FXGLTest.DEFAULT_RADIUS / radius)));
    }
}

And simple realization of TimerComponent :

import com.almasb.fxgl.entity.component.Component;
import com.almasb.fxgl.time.Timer;
import javafx.util.Duration;

public class TimerComponent extends Component {

    private Timer timer = new Timer();

    @Override
    public void onUpdate(double tpf) {
        super.onUpdate(tpf);
        timer.update(tpf);
    }

    public void runAtInterval(Runnable action, Duration duration) {
        timer.runAtInterval(action,duration );
    }

    public void runOnceAfter(Runnable action, Duration duration) {
        timer.runOnceAfter(action,duration);
    }

    public void clear(){
        timer.clear();
    }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions