How do I adjust Player's physical bounds? - phaser-framework

Object Boundary
I'm a new Phaser developer, in the beginning stages of my first game. My problem is, when my player hits the ground, the bounding box defining his bounds is way bigger than he is (see pic). So in the example shown, the player will bounce when the borders touch, not his feet.
In action
Physics Matter
Do I have to change from ARCADE physics to a more robust physics engine, in order to accomplish my goal?
Code
There's really not much to show, but here is what I have:
/// <reference path="../defs/phaser.d.ts" />
class MainGame extends Phaser.Scene {
constructor() {
super("MainGame");
}
preload() {
this.load.image("bg1", "assets/bg-level1.png");
this.load.image('ground', 'assets/platform.png');
this.load.spritesheet('dude', 'assets/final-jump.png', {
frameWidth: 118,
frameHeight: 118
});
}
create() {
this.setupBackground();
this.setupPlayer();
this.add.text(0, 0, 'Use Cursors to scroll camera.\nQ / E to zoom in and out', {
font: '18px Courier',
fill: 'black'
});
this.physics.add.collider(this.player, this.platforms);
}
update() {
// Set Player Animations
this.setPlayerAnimation();
if (this.player.body.onFloor()) {
console.log("FLOOR");
this.player.body.setAccelerationX(31);
this.player.body.setAccelerationY(31);
}
}
//---------------------------------
// CREATE RELATED FUNCTIONS
//---------------------------------
setupBackground() {
this.background = this.add.image(0, 0, "bg1");
this.background.setOrigin(0, 0);
//this.background.setInteractive();
this.background.setAlpha(.2, .2, .2, .2);
// The platforms group contains the ground and the 2 ledges we can jump on
this.platforms = this.physics.add.staticGroup();
// Here we create the ground.
// Scale it to fit the width of the game (the original sprite is 400x32 in size)
this.platforms.create(400, 568, 'ground').setScale(2).refreshBody();
}
setupPlayer() {
this.player = this.physics.add.sprite(100, 283, 'dude');
console.log(this.player.body.touching.down);
this.player.setVelocity(200, 100).setBounce(.8, .8).setCollideWorldBounds(true);
// Re-Size Player Size
this.player.setDisplaySize(220, 210);
// Collision Handler
this.physics.add.overlap(this.player, this.platforms, this.showJump, null, this);
// ANIMATIONS
this.anims.create({
key: 'jump-up',
frames: this.anims.generateFrameNumbers('dude', {
start: 1,
end: 2
}),
frameRate: 5,
repeat: 1
});
this.anims.create({
key: 'falling',
frames: this.anims.generateFrameNumbers('dude', {
start: 0,
end: 1
}),
frameRate: 5,
repeat: 1
});
this.anims.create({
key: 'onGround',
frames: this.anims.generateFrameNumbers('dude', {
start: 4,
end: 4
}),
frameRate: 24,
repeat: 1
});
}
showJump() {
this.player.anims.play('jump-up', true);
}
//---------------------------------
// UPDATE RELATED FUNCTIONS
//---------------------------------
setPlayerAnimation() {
//this.player.anims.play('jump-up', true);
if (this.player.body.deltaY() > 0 && this.player.body.onFloor()) {
this.player.anims.play('falling', true);
}
}
}
Oh, Yeah...
I am very impressed with the functionality of the local documentation package I downloaded and installed, however, I'm confused on where to begin looking for something. For instance, let's say I am struggling with making my player jump and play an animation when in the air, and change back when he hits the ground. Given that information, how do I find the correct class or function I need???

You do not have to change from arcade physics. Try something like this:
// Change the size of the bounding box.
this.player.setSize(32, 32);
// Change the location of the bounding box.
this.player.setOffset(0, 28);
// If that doesn't work, try this instead:
// this.player.body.setOffset(0, 28);
Obviously you need to adjust the width/height and offsets for your scenario.
References:
Sprite setSize method
Sprite setOffset method
Body setOffset method

Related

Is there a way to avoid this lighting effect using tile-maps in Phaser?

Is there a way to get smooth gradient from the lighting?
I'm creating a survival game using PhaserJs 3. I'm using the LightsManager and a tileset I modified from one of the example tilesets included in the examples.
Currently the tile grid is visible because of how the lighting is working with the tiles.
If possible, I would like to have the lighting be smooth similar to the picture below:
My grass tile is 32x32:
and the normal map is the following image
which was generated using this normal map generator site.
var config = {
type: Phaser.WEBGL,
width: 800,
height: 600,
pixelArt: true,
backgroundColor: '#000000',
scene: {
preload: preload,
create: create,
update: update,
},
physics: {
default: 'arcade',
arcade: {
gravity: { y: 0, x:0 },
debug: false
}
},
};
var light;
var layer;
var game = new Phaser.Game(config);
function preload ()
{
this.load.image('tiles', ['assets/tilemap.png','assets/tilemap_n.png']);
...
}
function create ()
{
var level = [ // The grass tile is #29 in the tileset
[29,29,29,29,29,29],
[29,29,29,29,29,29],
[29,29,29,29,29,29],
[29,29,29,29,29,29],
[29,29,29,29,29,29],
[29,29,29,29,29,29]]
var map = this.make.tilemap({ data: level, tileWidth: 32, tileHeight: 32 });
var tiles = map.addTilesetImage('tiles');
layer = map.createStaticLayer(0, tiles, 0, 0).setScale(2)
layer.setPipeline('Light2D');
light = this.lights.addLight(390,223.3, 150).setIntensity(.8);
}
I'm not sure if this is the best (or most performant) way, but you could use this plugin to create a post pipeline blur.
here a working demo of this plugin: https://codepen.io/rexrainbow/pen/zYzzYVw
Basically you would have to load the plugin, in preload:
function preload (){
...
this.load.plugin('rexkawaseblurpipelineplugin', 'https://raw.githubusercontent.com/rexrainbow/phaser3-rex-notes/master/dist/rexkawaseblurpipelineplugin.min.js', true);
}
And apply the plugin in to the specific camera, or gameobject:
function create (){
var postFxPlugin = this.plugins.get('rexkawaseblurpipelineplugin');
var postFxPipeline = postFxPlugin.add(this.cameras.main, {
blur: 1,
quality: 3
});
...
}
Or you could fake, the gradient with multiple lights and may be add a specific ambientColor:
light1 = this.lights.addLight(390,300, 150, 0xffffff, .80);
light2 = this.lights.addLight(390,300, 250, 0xffffff, .75);
light3 = this.lights.addLight(390,300, 350, 0xffffff, .70);
this.lights.enable().setAmbientColor(0x555555);

How to make a tooltip appear when hovering mouse over a Phaser.GameObject.Image?

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

How to make card flip animation in Phaser 3

I'm trying to make a card flip when is touched (working on mobile). So far I've only been able to change one frame for another (front to back), without any transition, so it feels unnatural. The idea is that you touch the screen, a card shows up, then ou touch it again and the card slowly flips, so you can see something in the back of the card.
I'm using the latest iteration of Phaser 3.
I have a working example, but it's made in Phaser 2, so I'm having a really hard time trying to update the code to Phaser 3.
var config = {
type: Phaser.AUTO,
width: 800,
height: 600,
scene: {
preload: preload,
create: create
}
};
var game = new Phaser.Game(config);
function preload() {
this.load.image('fondo', 'img/backgroundhome.png');
this.load.spritesheet('carta', 'img/spritesheet.png', { frameWidth: 196, frameHeight: 339 });
this.load.image('reverso', 'img/reversecard.png');
}
function create() {
this.add.image(400, 300, 'fondo');
let cartaObj = this.add.image(75, 100, 'carta').setOrigin(0, 0).setInteractive();
this.anims.create({
key: 'frente',
frames: this.anims.generateFrameNumbers('carta', { start: 0, end: 0 }),
frameRate: 1,
repeat: -1
});
this.anims.create({
key: 'atras',
frames: this.anims.generateFrameNumbers('carta', { start: 1, end: 1 }),
frameRate: 1,
repeat: -1
});
var tween1 = this.scene.tweens.add({
targets: cartaObj,
scaleX: 10,
scaleY: 10,
ease: 'Linear',
duration: 300,
repeat: 0,
yoyo: false
});
cartaObj.once('pointerup', cargaAnim, this);
}
function cargaAnim() {
tween.start();
}
Tap the screen, show up a card (in this case a have a spritesheet with 2 frames, front and back), tap the card again and it flips slowly to show the back of the card.
I'm not very familiar with Phaser3 yet, but in Phaser2 I made something like this, see the level icons animation in this game. Basically the idea is to:
add a tween to 'fold' the card sprite (scale width to 0.0)
add onComplete function to tween
in onComplete function change sprite frame to show the card and..
..start another tween to 'unfold' the card (scale width to 1.0)
So something like:
// scale horizontally to disappear
var tween1 = this.scene.tweens.add({
targets: cartaObj,
scaleX: 0.01,
ease: 'Linear',
duration: 300,
repeat: 0,
yoyo: false
});
tween1.onComplete.add(function(){this.onTurnCard(cartaObj);}, this);
onTurnCard: function(card) {
// set card face somehow
card.frameName = 'HeartQueen'; // ?
// scale horizontally to re-appear
var twn = this.scene.tweens.add({
targets: card,
scaleX: 1.0,
ease: 'Linear',
duration: 300,
repeat: 0,
yoyo: false
});
// do something on complete
twn.onComplete.add(function(){this.onTurnCardCompleted(card);}, this);
}
onTurnCardCompleted: function(card) {
// do something, show text, add score etc.
if (card.frameName == 'HeartQueen') {
// ?
};
}

Phaser 3. Change dimensions of the game during runtime

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.

How to set top-center position for pnotify

How to set non pixels position?
I try this
var stack = { "dir1": "down", "dir2": "right", "firstpos1": 50, "firstpos2": 50 };
But I this it is bad because of different screen resolution.
Necroposting, but may help to other searchers. My solution without js recalculations:
js:
new PNotify({
...
addclass: 'pnotify-center'
});
css:
.pnotify-center {
right: calc(50% - 150px) !important;
}
there's a similar question with an answer here. As per the first example in the stacks documentation, you can center the initial position of the notification by setting the top/left css propreties in before_open. You also need to reposition the notification everytime the window is resized.
function get_center_pos(width, top) {
// top is empty when creating a new notification and is set when recentering
if (!top) {
top = 30;
// this part is needed to avoid notification stacking on top of each other
$('.ui-pnotify').each(function() {
top += $(this).outerHeight() + 20;
});
}
return {
"top": top,
"left": ($(window).width() / 2) - (width / 2)
}
}
$(document).ready(function() {
new PNotify({
title: "this is center",
text: "blablabla",
opacity: 0.90,
type: "info",
width: "390px",
before_open: function(PNotify) {
PNotify.get().css(get_center_pos(PNotify.get().width()));
}
});
$(window).resize(function() {
$(".ui-pnotify").each(function() {
$(this).css(get_center_pos($(this).width(), $(this).position().top))
});
});
});

Resources