-
-
Notifications
You must be signed in to change notification settings - Fork 191
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
[proposal] Make the screen/viewport capable of using layers #3134
Comments
@jwx Thanks for the proposal! I'm pretty sold on the concept, I've spoken to other maintainers and we're excited. This definitely fits into our desire to support HTML based UI. Questions:
Potential implementation thoughts:
@mattjennings @kamranayub @jedeen Let me know what you think as well |
Great to hear!
Not 100% sure with what you mean by this but in
Yeah, that's basically what a DOM element layer is.
Me too and this is exactly how it is in
Layers are drawn in a specific order and what's drawn within a layer and how it's z-ordered is up to the layer. In
In
In public async setLevel(name: string) {
// Create a fade layer and run a fade in effect on the created element
const fade = this.viewport.addLayers({ name: 'fade', before: this.hudLayer })[0];
await this.fadeIn(fade.element, 300);
// Remove layers with the same name as current level
this.viewport.removeLayers(this.viewport.getLayers(this.level));
// Update the insertion point for the local level background and foreground data (layers)
this._backgrounds[name].forEach(layer => layer.before = this.worldLayer);
this._foregrounds[name].forEach(layer => layer.before = fade);
// Add the background and foreground layers for the new level
this.viewport.addLayers([...this._backgrounds[name], ...this._foregrounds[name]]);
this.level = name;
// Fade out the fade layer and remove it
await this.fadeOut(fade.element, 300);
this.viewport.removeLayers(fade);
}
Yeah, sounds reasonable.
Is the drawing context(s) on the Screen or Scene? Feels like that might be a clue to where layers should be.
In
Yeah, layers can definitely be used to get around some coordinate transformations. |
To add some insight to this, we've had needs in the past to manage layers at a scene level (y-sorted z-indexes for isometric games, where you might have verticality and want to ensure it's always above the layer below). I thought it would be a good idea to have layers as entities because in its simplest form a layer can just be an entity with children (children are positioned relative to their parent, so if layer is at 0,0 then children's coordinates are unchanged). To make it parallax, you add the I think I see this proposal was more about having literal DOM node layers as each view port, such that you could even have multiple canvases? I believe it would be better if the layers conceptually existed within the one canvas, i.e the game, and additional HTML layers would be created as a side effect of an Some pseudocode implementations: class Layer extends ex.Entity {}
class BackgroundLayer extends ex.Entity {
constructor({ x, y, image, parallax }) {
super()
// (init transform and graphics components)
this.graphics.use(image)
if (parallax) {
this.addComponent(new ex.ParallaxComponent(...))
}
}
} Here's what an HTMLLayer could look like (using some code I've used in the past to create an HTML element that overlays the canvas): class HTMLLayer extends ex.Entity {
constructor({
tag = 'div',
id,
}: {
tag?: string
id?: string
} = {}) {
super()
this.htmlElement = document.createElement(tag)
this.htmlElement.style.position = 'absolute'
this.htmlElement.style.pointerEvents = 'none'
if (id) {
this.htmlElement.id = id
}
this.resizeObserver = new ResizeObserver(() => {
this.resizeToCanvas()
})
this.resizeObserver.observe(document.body)
}
onInitialize(engine: Engine<any>): void {
this.engine = engine
this.parentElement = engine.canvas.parentElement!
this.parentElement.appendChild(this.htmlElement)
this.resizeToCanvas()
const scene = this.scene!
scene.on('activate', () => {
this.show()
})
scene.on('deactivate', () => {
this.hide()
})
}
show() {
this.htmlElement.removeAttribute('hidden')
}
hide() {
this.htmlElement.setAttribute('hidden', '')
}
resizeToCanvas() {
if (this.htmlElement && this.engine?.canvas) {
this.emit('resize')
const { width, height, left, top, bottom, right } =
this.engine.canvas.getBoundingClientRect()
const scaledWidth = width / this.engine.drawWidth
const scaledHeight = height / this.engine.drawHeight
this.scale.x = scaledWidth
this.scale.y = scaledHeight
this.htmlElement.style.top = `${top}px`
this.htmlElement.style.left = `${left}px`
this.htmlElement.style.bottom = `${bottom}px`
this.htmlElement.style.right = `${right}px`
this.htmlElement.style.overflow = 'hidden'
this.htmlElement.style.width = `${this.engine.drawWidth}px`
this.htmlElement.style.height = `${this.engine.drawHeight}px`
this.htmlElement.style.transform = `scale(${scaledWidth}, ${scaledHeight})`
this.htmlElement.style.transformOrigin = '0 0'
}
}
} |
I don't know Excalibur that well yet, but I think this is two different use cases. (Which I think you're getting at.) The first use case is where you've got Excalibur Actors/Entities in the
How do you get fully functional HTML layers into the |
Gotcha, yeah, my gut feeling is we don't want to introduce layering as a concept outside of the canvas, with the exception being the HTMLLayer.
It'd just be a way to create and expose an overlaaying html element, so that you insert your own HTML into (framework or vanilla html or whatever). So there'd have to be some additional API for a user to hook into it, as even though it's called an |
(but I'll defer to Erik on this idea of layers outside of canvas, just giving my 2c) |
In this proposal I'm primarily talking about HTML layers being the usage. Outside the |
Great discussion! I think there are maybe 2 independent ideas forming:
HTML OverlaysIdeally I still would love an easier way to handle building HTML UI's that can be integrated with Excalibur more tightly. In this concept perhaps it does make sense to have these HTML overlays exist on the Current problems in excalibur I'd love to solve with HTML overlays:
|
Yeah.
Sounds good. Just so that I understand, this is for sorting in-game layering challenges like for example the bridge problem? If so, they can mostly be solved with parenting and/or additional z-ordering. (Here's a proof of concept from a DOM only, non-
I think that's a good idea.
Great! That keeps this proposal alive. I think the layers belong with the Viewport/Screen (but I might just be biased). Where is the canvas drawing context, on
Will be resolved.
Can be resolved.
Can probably be resolved (I need to understand it more before a more definitive answer).
Will be resolved.
Yes, but I think, at least right now, that HTML manipulation should be outside of Excalibur (in line with previous point). Any preferences on how we proceed? |
Good questions, currently the graphics drawing context abstraction lives on the engine and screen. Screen is basically responsible for coordinating layout and positioning in the DOM, and handling any fullscreen/resize/resolution change/viewport change. Only 1
Totally fair, I think I agree we should stay out of this for now
@jwx Let's sketch out a possible Excalibur API for the HTML Overlays, maybe a small low effort prototype to prove out the concept? @mattjennings anything you'd like to see? |
I'd say we want to support adding layers both when initializing and during the game. For initialization, I see two options (or a mix of them): a) instantiated objects const game = new ex.Engine(
...,
layers: [ new ex.CanvasLayer(canvasLayerOptions), new ex.HTMLayer(htmlLayerOptions) ],
...
); b) configurations const game = new ex.Engine(
...,
layers: [ { canvasLayerOptions }, { htmlLayerOptions } ],
...
]; During the game, maybe just stick to the instantiated game.add(new HTMLLayer(htmlLayerOptions,) { before: existingLayer }); And of course, also game.remove(layer);
game.getLayer(layerName); Thoughts? |
I'm interested in this feature (the canvas layer) as well, but let me ask the same question that I asked myself when I first ran into the "lack" of multiple drawing layers in EX: What would this feature add for the average developer using EX? I couldn't answer it in a satisfactory way myself, so I chose to create a plugin-like setup for it, using existing features of EX. 100% personal opinion: The idea to add HTML layers as well feels even more out of scope for a core EX feature with all the alternative options available in existing 3th party libraries. |
This issue hasn't had any recent activity lately and is being marked as stale automatically. |
Context
The
canvas
provides limited help when it comes to creating UI such as buttons, input fields, text and other things used in menus, HUDs and similar. At the same time, the browser provides a great set of tools for this with html+css in the DOM. It'd be beneficial to be able to easily select which tool,canvas
or DOM, to use for which task.Proposal
Make the screen/viewport capable of having different layers of different types and help keeping them in sync.
The concept comes from
peasy-viewport
in which providing the configurationresults in this
In this example,
peasy-viewport
automatically manages and synchronizes layers based on the position and zoom of the camera used in the canvas layer.While the above example might be a bit overkill for Excalibur to implement, it would be nice to have some support for layers. Syntax could be similar, but as an optional
layers
option when creating the Engine. Management of the Layers would be the responsibility of the Screen.The text was updated successfully, but these errors were encountered: