Hi please help me to find out how trully responsive game can be created with Phaser3.
Respnsiveness is critical because game (representation layer of Blockly workspace) should be able to be exapanded to larger portion on screen and shrinked back many times during the session.
The question is How I can change dimentions of the game in runtime?
--- edited ---
It turns out there is pure css solution, canvas can be ajusted with css zoom property. In browser works well (no noticeable effect on performance), in cordova app (android) works too.
Here is Richard Davey's answer if css zoom can break things:
I've never actually tried it to be honest. Give it a go and see what
happens! It might break input, or it may carry on working. That (and
possibly the Scale Manager) are the only things it would break,
though, nothing else is likely to care much.
// is size of html element size that needed to fit
let props = { w: 1195, h: 612, elementId: 'myGame' };
// need to know game fixed size
let gameW = 1000, gameH = 750;
// detect zoom ratio from width or height
let isScaleWidth = gameW / gameH > props.w / props.h ? true : false;
// find zoom ratio
let zoom = isScaleWidth ? props.w / gameW : props.h / gameH;
// get DOM element, props.elementId is parent prop from Phaser game config
let el = document.getElementById(props.elementId);
// set css zoom of canvas element
el.style.zoom = zoom;
Resize the renderer as you're doing, but you also need to update the world bounds, as well as possibly the camera's bounds.
// the x,y, width and height of the games world
// the true, true, true, true, is setting which side of the world bounding box
// to check for collisions
this.physics.world.setBounds(x, y, width, height, true, true, true, true);
// setting the camera bound will set where the camera can scroll to
// I use the 'main' camera here fro simplicity, use whichever camera you use
this.cameras.main.setBounds(0, 0, width, height);
and that's how you can set the world boundary dynamically.
window.addEventListener('resize', () => {
game.resize(window.innerWidth, window.innerHeight);
});
There is some builtin support for resizing that can be configured in the Game Config. Check out the ScaleManager options. You have a number of options you can specify in the scale property, based on your needs.
I ended up using the following:
var config = {
type: Phaser.AUTO,
parent: 'game',
width: 1280, // initial width that determines the scaled size
height: 690,
scale: {
mode: Phaser.Scale.WIDTH_CONTROLS_HEIGHT ,
autoCenter: Phaser.Scale.CENTER_BOTH
},
physics: {
default: 'arcade',
arcade: {
gravity: {y: 0, x: 0},
debug: true
}
},
scene: {
key: 'main',
preload: preload,
create: this.create,
update: this.update
},
banner: false,
};
Just in case anybody else still has this problem, I found that just resizing the game didn't work for me and physics didn't do anything in my case.
To get it to work I needed to resize the game and also the scene's viewport (you can get the scene via the scenemanager which is a property of the game):
game.resize(width,height)
scene.cameras.main.setViewport(0,0,width,height)
For Phaser 3 the resize of the game now live inside the scale like this:
window.addEventListener('resize', () => {
game.scale.resize(window.innerWidth, window.innerHeight);
});
But if you need the entire game scale up and down only need this config:
const gameConfig: Phaser.Types.Core.GameConfig = {
...
scale: {
mode: Phaser.Scale.WIDTH_CONTROLS_HEIGHT,
},
...
};
export const game = new Phaser.Game(gameConfig);
Take a look at this article.
It explains how to dynamically resize the canvas while maintaining game ratio.
Note: All the code below is from the link above. I did not right any of this, it is sourced from the article linked above, but I am posting it here in case the link breaks in the future.
It uses CSS to center the canvas:
canvas{
display:block;
margin: 0;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
It listens to the window 'resize' event and calls a function to resize the game.
Listening to the event:
window.onload = function(){
var gameConfig = {
//config here
};
var game = new Phaser.Game(gameConfig);
resize();
window.addEventListener("resize", resize, false);
}
Resizing the game:
function resize() {
var canvas = document.querySelector("canvas");
var windowWidth = window.innerWidth;
var windowHeight = window.innerHeight;
var windowRatio = windowWidth / windowHeight;
var gameRatio = game.config.width / game.config.height;
if(windowRatio < gameRatio){
canvas.style.width = windowWidth + "px";
canvas.style.height = (windowWidth / gameRatio) + "px";
}
else{
canvas.style.width = (windowHeight * gameRatio) + "px";
canvas.style.height = windowHeight + "px";
}
}
I have used and modified this code in my phaser 3 project and it works great.
If you want to see other ways to resize dynamically check here
Put this in your create function:
const resize = ()=>{
game.scale.resize(window.innerWidth, window.innerHeight)
}
window.addEventListener("resize", resize, false);
Important! Make sure you have phaser 3.16+
Or else this won't work!!!
Use the game.resize function:
// when the page is loaded, create our game instance
window.onload = () => {
var game = new Game(config);
game.resize(400,700);
};
Note this only changes the canvas size, nothing else.
Related
My code is here please look it. I have try my option but stuck in this.
https://www.codepile.net/pile/7mPpA9m0
What i do and please share me best solution. I'm stuck from last 2 weeks. in this problem please help me
As mention in a previews comment (I couldn't answer because the question was closed), the problem is the bounding-box of the physics objects.
It could be the player or the platform, depending on the images.
To know for sure turn on the debug information(debug: true) in the config of the Phaser Game Object. (a nice demo can be found here: https://phaser.io/examples/v3/view/physics/arcade/custom-debug-colors)
And than adjust the size of the bounding box. _(a nice demo can be found here: https://phaser.io/examples/v3/view/physics/arcade/smaller-bounding-box)
I here you can see what I mean in action.
The red and white player would have the same bounding box, but I made the box of the white player smaller with the function setSize and adjusted the position of the bounding box with the function setOffset.
Here a working Demo:
var config = {
type: Phaser.AUTO,
width: 536,
height: 183,
scene: {
create: create
},
physics: {
default: 'arcade',
arcade: {
gravity:{y:100},
//show debug information
debug: true
}
},
banner: false
};
function create () {
// JUST FOR DEMO START
let graphics = this.make.graphics();
graphics.fillStyle(0xffffff);
graphics.fillRect(10,5,10,25);
graphics.generateTexture('player', 30, 30);
// JUST FOR DEMO END
let platform = this.add.rectangle(150, 120, 100, 20, 0x00ff00)
.setOrigin(0, 1);
this.physics.add.existing(platform)
.body
.setImmovable(true)
.setAllowGravity(false);
let player = this.add.image(136, 50, 'player')
.setOrigin(.5, 1)
.setTint(0xff0000);
this.physics.add.existing(player);
let player2 = this.add.image(136, 50, 'player')
.setOrigin(.5, 1);
this.physics.add.existing(player2)
.body
// CHANGE SIZE OFF PHYSICS BOX
.setSize(14, 25, false)
.setOffset(8, 5)
.setAllowGravity(false)
.setVelocityY(10)
this.physics.add.collider(player, platform);
this.physics.add.collider(player2, platform);
}
var game = new Phaser.Game(config);
<script src="https://cdn.jsdelivr.net/npm/phaser#3.55.2/dist/phaser.js">
</script>
Depending on, if you player's or your platforms bounding-box is to big you will have to adjust it, or make images, that have less transparent borders.
In my Phaser 3 game I'm using Phaser.GameObjects.Image 's as things that a user can click on. When a user's mouse hovers over an image I would like a tooltip with text to fade in and appear. When the mouse moves off the image, I'd like the tooltip to disappear.
How can I implement this behavior in Phaser? I'm new to Phaser and I don't see a ToolTip class in the framework.
You could:
use the pointer events for detecting, that the pointer is over an object
and animate/tween the alpha property on the over event
you can alter the speed with the tween duration
and hide the toolTip on the out event
Here the docs to the Input events: https://photonstorm.github.io/phaser3-docs/Phaser.Input.Events.html
Here a mini example:
var config = {
type: Phaser.WEBGL,
parent: 'phaser-example',
width: 800,
height: 600,
scene: {
create: create
}
};
var game = new Phaser.Game(config);
var toolTip;
var toolTipText;
function create ()
{
let objectWithToolTip = this.add.rectangle( 100, 100, 100, 100, 0xffffff).setInteractive();
toolTip = this.add.rectangle( 0, 0, 250, 50, 0xff0000).setOrigin(0);
toolTipText = this.add.text( 0, 0, 'This is a white rectangle', { fontFamily: 'Arial', color: '#000' }).setOrigin(0);
toolTip.alpha = 0;
this.input.setPollOnMove();
this.input.on('gameobjectover', function (pointer, gameObject) {
this.tweens.add({
targets: [toolTip, toolTipText],
alpha: {from:0, to:1},
repeat: 0,
duration: 500
});
}, this);
this.input.on('gameobjectout', function (pointer, gameObject) {
toolTip.alpha = 0;
toolTipText.alpha = 0;
});
objectWithToolTip.on('pointermove', function (pointer) {
toolTip.x = pointer.x;
toolTip.y = pointer.y;
toolTipText.x = pointer.x + 5;
toolTipText.y = pointer.y + 5;
});
}
<script src="//cdn.jsdelivr.net/npm/phaser#3.55.2/dist/phaser.min.js"></script>
Info: if you want to dig deeper into the phaser events you can checkout some examples on the offical home page: https://phaser.io/examples/v3/category/input/mouse are really good, and explore may use cases.
Extra Info: If you don't want to re-invent the wheel, there are several plugins, that can be loaded into phaser(that add special functionality to phaser).
It is always good to check this page(https://phaserplugins.com/), before implementing so common feature/function. There is even on specially for tooltips https://github.com/netgfx/Phaser-tooltip
Please take a look at this fiddle:
http://jsfiddle.net/2ktvyk4e/5/
var imgUrl, snapshotCanvas;
imgUrl = 'http://cdn-development.wecora.com/boards/backgrounds/000/001/388/cover/ocean-background.jpg';
snapshotCanvas = new fabric.StaticCanvas('snapshotCanvas', {
backgroundColor: '#e0e0e0',
width: 1000,
height: 1500
});
fabric.Image.fromURL(imgUrl, function(img) {
img.set({
width: 1000,
left: 0,
top: 0,
clipTo: function(ctx) {
return ctx.rect(0, 0, 1000, 400);
}
});
return snapshotCanvas.add(img).renderAll();
}, {
crossOrigin: 'Anonymous'
});
It's pretty simple. I'm loading an image and then trying to clip it so that the full width of the canvas but clipped so only the top 400 pixels are showing. For some reason, the clipTo causes the image to move and resize inexplicably:
As you can see, when the clipping path is applied the image is repositioned on the canvas inexplicably. If I remove the clipTo, then the image loads full canvas width no problem (of course its also full height, which we don't want).
I have no idea what is happening here or why this is occuring so any help is appreciated.
Make sure you have originX and originY left to top and left on both the canvas and your image. Also - you don't really need to clip anything to the canvas. The only real use I've found for clipTo was clipping collage images to their bounding shapes. If you want to restrict the image from being dragged above that 400px, I would recommend rendering a rectangle below it (evented = false, selectable = false) and then clipping to that rectangle.
I put this together without clipTo (and changed some numbers so I wasn't scrolling sideways). It renders the image half way down the canvas.
http://jsfiddle.net/2ktvyk4e/6/
Edit:
I dug through some source code to find the clipByName method and the two helper methods for finding stuff. I use this for keeping track of collage images and their bounding rectanlges ("images" and "clips"). I store them in an object:
imageObjects: {
'collage_0': http://some.tld/to/image.ext,
'collage_1': http://some.tld/to/image2.ext
}
Helper methods for finding either the clip or image:
findClipByClipName: function (clipName) {
var clip = _(canvas.getObjects()).where({ clipFor: clipName }).first();
return clip;
},
findImageByClipFor: function (clipFor) {
var image = _(canvas.getObjects()).where({ clipName: clipFor }).first();
return image;
},
Actual clipping method:
clipByName: function (ctx) {
this.setCoords();
var clipRect, scaleXTo1, scaleYTo1;
clipRect = collage.findClipByClipName(this.clipName);
scaleXTo1 = (1 / this.scaleX);
scaleYTo1 = (1 / this.scaleY);
ctx.save();
var ctxLeft, ctxTop;
ctxLeft = -(this.width / 2) + clipRect.strokeWidth;
ctxTop = -(this.height / 2) + clipRect.strokeWidth;
ctx.translate(ctxLeft, ctxTop);
ctx.rotate(degToRad(this.angle * -1));
ctx.scale(scaleXTo1, scaleYTo1);
ctx.beginPath();
ctx.rect(
clipRect.left - this.oCoords.tl.x,
clipRect.top - this.oCoords.tl.y,
clipRect.width,
clipRect.height
);
ctx.closePath();
ctx.restore();
function degToRad(degrees) {
return degrees * (Math.PI / 180);
}
},
And finally adding images to canvas where all of this comes together:
var clipName, clip, image;
clipName = helpers.findKeyByValue(url, this.imageObjects);
clip = this.findClipByClipName(clipName);
image = new Image();
image.onload = function () {
var collageImage = new fabric.Image(image, $.extend({}, collage.commonImageProps, {
left: clip.left,
top: clip.top,
clipName: clipName,
clipTo: function (ctx) {
return _.bind(collage.clipByName, collageImage)(ctx);
}
}));
collage.scaleImagesToClip(collageImage);
canvas.add(collageImage);
};
I have a canvas built using fabricJS with the dimension of 600x500. I have added an image to this canvas which is of size 200x300 and also a text element just below it.
$canvasObj.toDataURL();
exports the whole canvas area including the white spaces surrounding the design on the canvas.
Is there a way to get the cropped output of the design on the canvas alone instead of all the whitespace?
This can be done by cloning objects to a group, getting the group boundingRect, and then passing the boundingRect parameters to toDataUrl() function (see fiddle).
e.g.
// make a new group
var myGroup = new fabric.Group();
canvas.add(myGroup);
// ensure originX/Y 'center' is being used, as text uses left/top by default.
myGroup.set({ originX: 'center', originY: 'center' });
// put canvas things in new group
var i = canvas.getObjects().length;
while (i--) {
var objType = canvas.item(i).get('type');
if (objType==="image" || objType==="text" || objType==="itext" || objType==="rect") {
var clone = fabric.util.object.clone(canvas.item(i));
myGroup.addWithUpdate(clone).setCoords();
// remove original lone object
canvas.remove(canvas.item(i));
}
}
canvas.renderAll();
// get bounding rect for new group
var i = canvas.getObjects().length;
while (i--) {
var objType = canvas.item(i).get('type');
if (objType==="group") {
var br = canvas.item(i).getBoundingRect();
}
}
fabric.log('cropped png dataURL: ', canvas.toDataURL({
format: 'png',
left: br.left,
top: br.top,
width: br.width,
height: br.height
}));
p.s. I should probably mention that i've not worked with image types, so i just guessed that it's called 'image'..
I am testing the <webview> element in Chrome and I have gone through the documentation but I cannot figure out why the Webview is not resizing when the parent window does.
Index.Html
<body>
<webview src="http://website.com" style="width:1010px; height:700px" minwidth="1010" minheight="700" autosize="on""></webview>
<script src="index.js"></script>
background.js
chrome.app.runtime.onLaunched.addListener(function() {
chrome.app.window.create('index.html', {
'bounds': {
'width': 1010,
'height': 700
}});});
Index.js
$(function() {
function resizeWebView() {
$('webview').get(0).width = $(window).width();
$('webview').get(0).height = $(window).height();
}
$(window).resize(resizeWebView);
resizeWebView();
});
I tried also removing the autosize=on as recommended but it does not work.
Another question how to disable the main window (Embedder) from resizing.
Thanks
You should use the chrome.app.window.onBoundsChanged API on the window object returned by chrome.app.window.create. Simple implementation:
background.js
var appWin = null;
chrome.app.runtime.onLaunched.addListener(function() {
chrome.app.window.create(
'index.html',
{'bounds': {'width': 1010, 'height': 700}},
onWindowCreated
);
});
function onWindowCreated(win) {
appWin = win;
// Resize the webview when the window resizes.
appWin.onBoundsChanged.addListener(onBoundsChanged);
// Initialize the webview size once on launch.
onBoundsChanged();
}
function onBoundsChanged() {
var webview = document.querySelector('webview');
var bounds = appWin.getBounds();
webview.style.height = bounds.height + 'px';
webview.style.width = bounds.width + 'px';
}
index.js
// Nothing here
See a much more elaborate object-oriented example with multiple windows and persisting/reusing the last window size set by the user here: https://github.com/GoogleChrome/chrome-app-samples/tree/master/url-handler.
As to your second question, it would be good if you asked it separately, but nevertheless: just set the resizable parameter of chrome.app.window.create to false:
...
chrome.app.window.create(
'index.html',
{
'bounds': {'width': 1010, 'height': 700},
'resizable': false
},
...
I set out to achieve something similar and the following approach is how I implemented it.
index.html
<webview src="http://website.com" style="width:1010px; height:700px"></webview>
background.js
The code you have here looks correct.
index.js
I approached this in a slightly different way by using the window.onresize event to handle the resize. Please keep in mind that resize event is fired after the screen has been resized and may seem to create a slightly laggy feel.
window.onresize = setWebview;
function setWebview() {
var webview = document.querySelector('webview');
var webviewWidth = document.documentElement.clientWidth;
var webviewHeight = document.documentElement.clientHeight;
webview.style.width = webviewWidth + 'px';
webview.style.height = webviewHeight + 'px';
}
I also call the setWebview(); function in the onLoad or something of the sorts for the initial setting of the webview.
So that is my approach, looking at your index.js I think you may be having an issue in that you are trying to set the width and height of the <webview> when in fact you need to be setting the style width and height, so maybe the following would have worked;
$('webview').get(0).style.width = $(window).width();
$('webview').get(0).style.height = $(window).height();
Notice that I added in .style before .width and .height
Hope that helps..
I don't know if this is a new thing but I did exactly this with CSS:
webview {
height: 100%;
width: 100%;
}
Easy as pie!