Closed codecat closed 2 years ago
Why not use transform:translate and replaceTransform?
Because Transform
by itself does not provide a stack like love.graphics.push()
does. I can of course replicate the stack manually by implementing a new stack by hand in pure Lua, worst case scenario, but I would much rather use the actual functions provided by Love.
One of the reasons I didn't add this to begin with is I didn't really want to encourage creating new love objects every frame. I also felt like love.graphics.transformPoint
and inverseTransformPoint
would probably cover most use cases people would want to retrieve the Transform for.
What sort of use cases did you have in mind?
My use case for this was in a GUI, where I'd stack transforms for each individual widget. I haven't worked on this project for a while so I don't remember the specifics off the top of my head.
I think I'll close this for now but I'm open to revisiting it if people have some good+specific use cases.
here's a use case: i have a level editor. i want to add an object where the mouse clicked. i can't use inverseTransformPoint because the coordinate transformations are only active during the draw step. if i had getTransform, i could just save the transformation during the draw step. as is, it seems like i basically need to hack together some sort of event queue to perform the translation on the draw step, which feels ugly and wrong. there are some other possible solutions, but all of them feel flimsy and likely to go wrong.
basically, any time you want to translate a mouse event to global coordinates, you have to deal with this problem. i don't know if there's something i'm missing, but there doesn't seem to be an easy way to handle this with the current functions.
if you don't want people creating new objects every frame, you could require them to pass in an existing Transform object that would be replaced by the one on the top of the stack (sort of the opposite of love.graphics.replaceTransform).
you could require them to pass in an existing Transform object that would be replaced by the one on the top of the stack
Or do something like Transform:copyContext() --Sets the transform to the current context's transform
Or do something like Transform:copyContext() --Sets the transform to the current context's transform
That's pretty much just Transform:setTransform(love.graphics.getTransform())
, which makes more sense imo
if i had getTransform, i could just save the transformation during the draw step.
Or do something like Transform:copyContext() --Sets the transform to the current context's transform
These will have to use the previous frame's transform if they're in the current frame's event or update code, right? That seems suboptimal. love.graphics' immediate transform API is fundamentally in conflict with code like you're describing that wants to perform transformations outside of that immediate context. I don't believe they're really reconcilable.
Instead, if you keep your own Transform object and apply the transformations you want to that (or a plain Lua version of the same thing, which many camera libraries do for you), you would be able to call Transform:transformPoint
/inverseTransformPoint
whenever you want, such as your mouse example, and you can call love.graphics.applyTransform
or replaceTransform
to use that transformation during drawing.
Basically you can treat something other than love.graphics immediate mode APIs as the ground-truth, and use that other thing everywhere including in drawing code.
Instead, if you keep your own Transform object and apply the transformations you want to that (or a plain Lua version of the same thing, which many camera libraries do for you), you would be able to call Transform:transformPoint/inverseTransformPoint whenever you want, such as your mouse example, and you can call love.graphics.applyTransform or replaceTransform to use that transformation during drawing.
This usually how I do it
That's pretty much just Transform:setTransform(love.graphics.getTransform()), which makes more sense imo
Mine avoids creating an new object and an additional C call which slime was against.
if i had getTransform, i could just save the transformation during the draw step.
Or do something like Transform:copyContext() --Sets the transform to the current context's transform
These will have to use the previous frame's transform if they're in the current frame's event or update code, right? That seems suboptimal. love.graphics' immediate transform API is fundamentally in conflict with code like you're describing that wants to perform transformations outside of that immediate context. I don't believe they're really reconcilable.
yes, like every other bit of code, it can only refer to things that happened in the past. this really isn't a problem, or if it is, it's not one that is really solvable. i don't know what "fundamental conflict" there is, other than the fact that there's no way to save the current transformation.
Instead, if you keep your own Transform object and apply the transformations you want to that (or a plain Lua version of the same thing, which many camera libraries do for you), you would be able to call
Transform:transformPoint
/inverseTransformPoint
whenever you want, such as your mouse example, and you can calllove.graphics.applyTransform
orreplaceTransform
to use that transformation during drawing.Basically you can treat something other than love.graphics immediate mode APIs as the ground-truth, and use that other thing everywhere including in drawing code.
ok so, basically. instead of adding this quite basic operation, you want me to basically reimplement love2d's coordinate stack, except with the function i want, and refactor my code around that??
why even have the transformation stack then?
the current system seems to be setting up people for failure by providing a thing that looks like it does what they want, until later on they realize there is no good way to handle mouse events within their code.
Conceptually, graphics operations happen as a result of game logic and events. They're at the end of the pipeline: they're affected by game logic, rather than the other way around. Most of love.graphics is built around this idea, including the fact that love.draw is separate from love.update and doesn't contain dt
, for example (which encourages people to keep game logic earlier in the pipeline than graphics operations).
why even have the transformation stack then?
It's very useful as a way for game logic to drive graphics.
Camera systems tend to affect both game logic and graphics operations. love.graphics' transform APIs aren't a camera system. There are a few popular camera libraries that probably do something like you want (and aren't too complicated internally), if you're curious: https://github.com/love2d-community/awesome-love2d#camera
yes, like every other bit of code, it can only refer to things that happened in the past. this really isn't a problem
It's a problem because it's using the result of game logic that happened before the current frame's input events and game logic. For example if I press a button to teleport a far distance, I'd expect the game code after that button press to be based on that new position. But if the game uses the previous frame's data instead of the current frame's, everything will be out of sync for a frame. This isn't an issue with approaches that don't rely on end-of-pipeline data like you would with love.graphics transforms.
This would allow to automatically build a Transform from the current transform set using
love.graphics.translate
etc.This might also need an option to only get the top stack transformation or the full transformation of the entire stack.