-
Notifications
You must be signed in to change notification settings - Fork 454
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
Changing the way FlxStates are called #2565
Conversation
this is a good start, but there's more to it than just that. The whole point is to call the state constructor after the old state is destroyed so that we don't need the create() method anymore Also, this feature will probably get merged last |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is going to be a pretty massive PR. I don't recommend making all of the changes directly in github on a web browser. it should be made locally, and we'll need to change Flixel-ui, flixel-addons, AAAAAALLLL of the demos, some snippets, the tutorial, and probably more, and we'll need to test them all together before anything is merged
{ | ||
if (state.switchTo(nextState)) | ||
game._requestedState = nextState; | ||
if (state.switchTo(nextState())) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
state.switchTo() should take a ()->FlxState
rather than a FlxState instance, it should store the function in _requestedState
and wait until the new stuff is all set up to call it, creating the state
flixel/FlxG.hx
Outdated
@@ -379,7 +379,7 @@ class FlxG | |||
*/ | |||
public static inline function resetState():Void | |||
{ | |||
switchState(Type.createInstance(Type.getClass(state), [])); | |||
switchState(() -> Type.createInstance(Type.getClass(state), [])); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
once we start storing the state creator function we can just call that to create a new one, rather than needing to use Type.createInstance.
with Type.createInstance FlxStates can't have constructor args, by passing in functions we can do things like
FlxG.switchState(()->{ return new MyState(arg1, arg2); });
and we'll know how to switch to that state AND reset it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, so like a function? I like it, I like it
flixel/system/FlxSplash.hx
Outdated
@@ -199,7 +199,7 @@ class FlxSplash extends FlxState | |||
#end | |||
FlxG.stage.removeChild(_sprite); | |||
FlxG.stage.removeChild(_text); | |||
FlxG.switchState(Type.createInstance(nextState, [])); | |||
FlxG.switchState(() -> Type.createInstance(nextState, [])); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nextState should also be a ()->FlxState
, and the FlxGame constructor should take a function instead of a Class Ref
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm So like making the PlayState.new also in FlxGame?
I liked that you didn't need to add anything, just the name but ok
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
where before you had
addChild(new FlxGame(400, 300, MenuState);
now you can just do
addChild(new FlxGame(400, 300, MenuState.new);
the reason for this is to allow constuctor args, like
addChild(new FlxGame(400, 300, MenuState.new.bind(arg));
@Geokureli wdym with "doing it locally?" you want me to make like a small fork introducing that or some shit like that? |
Yeah, typically I make changes to flixel using my forked repos. I set haxelib to use my local git versions via I do this for addons, ui, demos, snippets, templates and tools. when you make a change to flixel, Github Actions test your new version of flixel against the dev branch of all of these additional repos, and in this case it's definitely going to fail, so we test them all locally and make branches for each repo that will merged at the same time, once all our local tests work well together. NGL, this is a big task, and you'll likely have to learn a lot to get it done, but time is on your side, and I'm feeling pretty patient right now. especially because Austin East is gonna try to rewrite the flixel rendering system for flixel 5.0.0 |
Hm... |
you can check out Cheemsandfriends:patch-5 in VSCode via its github ui: once that is set up changes you commit and push will be added to the patch-5 branch and will show up here, triggering a new round of github actions you can also ditch this branch and PR and make your own locally, publish it and then you'll be able to start a new PR using that branch when you go to https://github.com/Cheemsandfriends/flixel |
I've been trying to add this for some days but idk why flixel-addons seems to work weird with my implementation? |
I've got a question @Geokureli, what is this code supposed to do exactly? |
For full context, here's the full code: if (FlxG.vcr.paused)
{
if (FlxG.vcr.stepRequested)
{
FlxG.vcr.stepRequested = false;
}
else if (_state == _requestedState)
{
// some unrelated debug code
return;
}
} you can pause the game using the debug menu's VCR, if you do, |
hey @Cheemsandfriends! I'm planning on merging this for 6.0.0, I just want it to be the last thing merged before releasing 6.0.0 because it's such a breaking change, like, it breaks everything! Also I never mentioned this but I actually made this change in my own branch in June, before you finalized this branch. I also made branches in flixel-addons and all the others, that will coincide. I'll be using this branch over mine, in the end, so you get the contribution stats, but I'll cross-check both our branches to see if either one of us missed something. but when I last looked it all seemed good |
Seen this too late to make a proposal, but anyway - why not use Either ? I believe with this pattern it wouldn't be a breaking change. |
I believe it still would be a breaking change, since Either needs the enum value constructor i.e.: However we could use OneOfTwo, or perhaps The problem with this is that we wouldn't gain any of the benefits of this change, though, The whole point of the new system is to delay the state constructor call until after the old state is destroyed. I'd have to think about it to be sure but it might not be possible to have it both ways. Good suggestion though, I'll get back to you |
@EliteMasterEric came up with a way to give a deprecation warning on the old type function main()
{
output(new FlxState("a")); // Warning : use ()->FlxState)
output(FlxState.new.bind("b"));
output(()->new FlxState("c"));
}
function output(state:NextState)
{
if (state is FlxState)
trace("received state instance");
else
trace("received state maker");
}
abstract NextState(Dynamic) to FlxState to ()->FlxState
{
@:from
@:deprecated("use ()->FlxState)")
static public function fromState(state:FlxState):NextState
{
return cast state;
}
@:from
static public function fromMaker(func:()->FlxState):NextState
{
return cast func;
}
}
class FlxState
{
public var foo:String;
public function new (foo:String) this.foo = foo;
} |
I was originally not a fan of this until I realized it lets you reset with constructor arguments by using Would this change essentially obsolete the create() function as well? |
that's one of the big features, here. State constructor arguments are extremely powerful, but no one uses them
We would keep create for backwards compatibility, but the main reason for this change is to prevent issues from creating Flixel objects in the state constructor, (since, in current flixel, the next state's constructor is called before the previous state is destroyed) however we will likely always keep the create() function in all future versions because FlxG.state will be always be null in your state's constructor. so if you make objects in your state constructor that reference FlxG.state, or if global utils reference FlxG.state, you'll have a bad time |
Ah, so Once the OneOfTwo change is implemented (could we make a generic |
looks like its doable, #2733 assuming this passes CI. feel free to try out the branch, just know that this isn't a real pr Edit: it didn't work, this will be a breaking change no matter what, but most people don't override switchTo, so this still might make people's transition smoother |
Checking back in, the new |
Is the "cost" of it actually perceivable in any of it's cases? When it comes to optimizing code its easy to say code A is more efficient than code B, but what that often actually means is "there will be noticeable slowdown if done 1,000 times a frame". Calling |
I guess I'm just brainstorming use cases for newly available language features. I do think that creating an abstract which convert a class instance to a supplier at compile time is better for code readability, and is easier to phase out later on. Here's what my proposed code would look like for context: function main()
{
output(new FlxState("a")); // Warning : use ()->FlxState)
output(FlxState.new.bind("b"));
output(()->new FlxState("c"));
}
function output(buildState:StateSupplier)
{
var state:FlxState = buildState();
}
abstract StateSupplier(()->FlxState) to FlxState to
{
@:op(a())
public function build():FlxState
{
return this();
}
@:from
@:deprecated("use ()->FlxState)")
static public function fromState(state:FlxState):StateSupplier
{
return () -> state;
}
@:from
static public function fromMaker(func:()->FlxState):StateSupplier
{
return cast func;
}
}
class FlxState
{
public var foo:String;
public function new (foo:String) this.foo = foo;
} The major benefit here is that |
How new is this feature? I'm hesitant to raise HaxeFlixel's minimum version, too much, too fast, and for this case the () op has very little benefit over having a create() method, especially since this is only used on the backend. The noteworthy differences of your approach are:
Your method is more simple but unfortunately doesn't work with |
good news, it worked! Gonna close this PR in light of #2733 which is a more frictionless way of introducing this. I appreciate everyone's help on this! |
I was looking into the issues tab from flixel when I saw #2541 and I felt like it looked pretty damn cool so I thought, why not try to make a pr about it?
Fixes #2541