-
Notifications
You must be signed in to change notification settings - Fork 145
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Split updating into updatePhysics, update and interpolate to make using a fixed timestep easier #216
base: master
Are you sure you want to change the base?
Conversation
Thanks for the PR! I don't think we should introduce the concept of physics into Ashley. The framework is meant to be generic, potentially suitable for all games. Some games do not need physics at all whilst some physics engines (such as Bullet), implement a fixed time step on their own. You could always extend What do you think about this? |
I was also thinking about something like this, when multiple IntervalSystem's need to coordinate so that the Physics and Game Logic intervals will pass by both systems per interval not have their intervals be isolated. Of course we could just extend Engine to coordinate this for us or wrap both systems in an IntervalSystem. This reminds me, i think it would be kind of nice to have a limit option on the IntervalSystem, limiting the amount of loops in each update. Seeing as how IntervalSystem has a final update method. |
Well, to get rid of "physics" we could just rename the class and the method to moething like "FixedStepSystem" and "updateFixedStep()", but that's probably not what you are going for. Sadly it's currently not possible to get this behavior by extending Engine in your own project, since the most logic changes are done in SystemManager, which is package private. |
How about a solution similar to what I proposed in #215? Let's have an public interface Interpolable {
public void interpolate(float delta, float physicsStep, float inAccumulator);
} And then a public class FixedTimeStepSystem<T extends EntitySystem & Interpolable> {
private final T system;
private final float timeStep;
private float accumulator = 0.0f;
public FixedTimeStepSystem(T system, float timeStep) {
this.system = system;
this.timeStep = timeStep;
}
public void update(float deltaTime) {
accumulator += deltaTime;
while (accumulator > timeStep) {
system.update(timeStep);
accumulator -= timeStep;
}
system.interpolate(deltaTime, timeStep, accumulator);
}
} You would then use it like this: engine.addSystem(new FixedTimeStepSystem(new PhysicsSystem(), PHYSICS_STEP)); Would this work for you? Obviously, you could make the fixed time step system take several systems and update/interpolate them all with a fixed timestep. The systems themselves only need to implement |
I am not too sure about about how correct it is to have systems depend on other systems but if I would to have a FixedTimeStepSystem, this decorator/wrapper class would break how my systems depend on each other, engine.getClass(EntitySystem.class). It would be nice to still be able to do engine.getSystem(PhysicsSystem.class) even though it was added by engine.addSystem(new FixedTimeStepSystem(new PhysicsSystem(), PHYSICS_STEP)). This probably is the only complaint I have against a decorator/wrapper system, otherwise I think this is perfectly suitable. |
I haven't looked at ashley's sources in a while, but wouldn't porting SystemInvocationStrategy (default impl) over to ashley solve these issues? It would also make things like per-system profiling easier, without having to modify existing code/systems. |
The idea with the wrapper system might work, but then all systems that do physics stuff need to implement interpolable (and they are not the systems to be interpolated) and have an empty interpolate method. Systems that draw physics bodies need to be interpolated and will be wrapped into FixedTimeSystems which they actually are not... |
Hmm. Adding to this, I often find myself "splitting" systems up into groups. For example, to separate updating and rendering, I will remove all non-updating systems, update the engine, remove all non-rendering systems, update the engine, etc. Maybe a solution to both issues could be to introduce a concept of groups to Ashley. For example, a physics group, update group, interpolation group and render group could be called like this:
|
Only one system per Class is allowed, so then all systems (possibly with different time steps, e.g. physics, logic and network) would have to be crammed into the same FixedTimeStepSystem. Not very clean. |
Perhaps I'm stating the obvious here, but I also had a requirement for fixed time step systems (though I wasn't aware it was known as fixed step at that time) and I simply created an abstract class that my other systems extended should they need this functionality. It looks like I used IteratingSystem as a base, but you could of course lean on anything you like. public abstract class InfrequentIteratingSystem extends IteratingSystem implements ComponentRetriever {
protected final float frequencyInSeconds;
private float timeElapsedSinceLastUpdate;
public InfrequentIteratingSystem(float frequencyInSeconds, Family family) {
super(family);
this.frequencyInSeconds = frequencyInSeconds;
}
@Override
public void update(float deltaTime) {
timeElapsedSinceLastUpdate += deltaTime;
if (timeElapsedSinceLastUpdate < frequencyInSeconds) {
return;
}
deltaTime = timeElapsedSinceLastUpdate;
timeElapsedSinceLastUpdate = 0;
try {
super.update(deltaTime);
} catch (Exception e) {
log.error("Error running system", e);
}
}
} You can then have as many different systems as you like, with differing "fixed time steps" as you see fit, that are called with the delta since their last execution. I use this on my game server where I have tasks that are somewhat expensive that I might want to run every 30 seconds or so (think checking the entire world for dead bodies to expire, that sort of thing), and also for other things like AI that I might want to run say twice a second, or spell casting progress 10 times a second etc... any system I can think of that does not need to run at 60fps uses this in an effort to claw back server performance wherever I can. My use case is certainly different to a degree, but at it's core the requirement is the same. I don't see why this couldn't be used to execute your physics or whatever else at whatever step you like. I think if a pull request is to be accepted into Ashley to facilitate fixed-step logic, a low complexity abstract System such as this makes much more sense. You can of course adapt my simple approach above to facilitate whatever fields you need for interpolation or otherwise. |
I tired to enhance Ashley to make it easier to apply the technique mentioned here: http://gafferongames.com/game-physics/fix-your-timestep/.
Now you can have this in your main loop:
Then you can use the PhysicsSystem and InterpolatingSystem interfaces to implement fixed timestep and interpolating behaviour.