The game I'm creating doesn't require any physics, however you are able to interact when hovering over/clicking on the sprite by using sprite.setInteractive({cursor: "pointer"});, sprite.on('pointermove', function(activePointer) {...}); and similar.
I ran into some issues with the interactive area and wanted to debug it by showing the "area" that is interactable. However I could only find ways to do that that are related to Arcade Physics. Is there any way to get something like a debug outline around my interactable area without Physics?
Out-Of-The-Box, without physics, I don't know any way, but one could get this function/feature with a small helper-function. (but maybe there is something, since phaser is a really extensive framework. But I also couldn't find anything).
Something like this, could do the trick, and is reuseable:
function debugSpriteArea(scene, sprite){
let debugRect = scene.add.rectangle(
sprite.x, sprite.y,
sprite.displayWidth, sprite.displayHeight,
0xff0000).setOrigin(sprite.originX,
sprite.originY);
debugRect.setDepth(-1);
}
Here the help-function in action:
let Scene = {
preload ()
{
this.load.spritesheet('brawler', 'https://labs.phaser.io/assets/animations/brawler48x48.png', { frameWidth: 48, frameHeight: 48 });
},
create ()
{
// Animation set
this.anims.create({
key: 'walk',
frames: this.anims.generateFrameNumbers('brawler', { frames: [ 0, 1, 2, 3 ] }),
frameRate: 8,
repeat: -1
});
const cody = this.add.sprite(200, 100, 'brawler')
.setOrigin(0.5);
debugSpriteArea(this, cody);
cody.play('walk');
cody.setInteractive();
this.mytext = this.add.text(10, 10, 'No Hit', { fontFamily: 'Arial' });
cody.on('pointerdown', function (pointer) {
let originXOffset = cody.displayWidth * cody.originX;
let originYOffset = cody.displayHeight * cody.originY;
let x = (pointer.x - cody.x + originXOffset ) / (cody.displayWidth / cody.width)
let y = (pointer.y - cody.y + originYOffset) / (cody.displayHeight / cody.height);
if(cody.anims && cody.anims.currentFrame){
let currentFrame = cody.anims.currentFrame;
let pixelColor = this.textures.getPixel(x, y, currentFrame.textureKey, currentFrame.textureFrame);
if(pixelColor.a > 0) {
this.mytext.text = 'hit';
} else {
this.mytext.text = 'No hit';
}
}
}, this);
}
};
function debugSpriteArea(scene, sprite){
let debugRect = scene.add.rectangle(
sprite.x, sprite.y,
sprite.displayWidth, sprite.displayHeight,
0xff0000).setOrigin(sprite.originX,
sprite.originY);
debugRect.setDepth(-1);
}
const config = {
type: Phaser.AUTO,
width: 400,
height: 200,
scene: Scene
};
const game = new Phaser.Game(config);
<script src="https://cdn.jsdelivr.net/npm/phaser#3.55.2/dist/phaser.js"></script>
Related
I've been working on a project found here -> https://paperambi.glitch.me/
as seen in console, all my tiles are recorded null, and therefore i cant get their index property, what is wrong with my tiles, and is there anyway to fix it?
Heres an example, when you click on tiles to get properties, its null
and if you add index, it responds with cannot find null of index.
Am I using the wrong function, or have something wrong with my tiles?
code sandbox -> https://codesandbox.io/s/31xpvv85om?hidenavigation=1&module=/js/index.js&moduleview=1&file=/js/index.js:0-3327
all code for the example
/**
* Author: Michael Hadley, mikewesthad.com
* Asset Credits:
* - Tileset by 0x72 under CC-0, https://0x72.itch.io/16x16-industrial-tileset
*/
import Phaser from "phaser";
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
parent: "game-container",
backgroundColor: "#1d212d",
pixelArt: true,
scene: {
preload: preload,
create: create,
update: update
}
};
const game = new Phaser.Game(config);
let controls;
let marker;
let shiftKey;
let groundLayer;
function preload() {
this.load.image(
"tiles",
"../assets/tilesets/0x72-industrial-tileset-32px-extruded.png"
);
this.load.tilemapTiledJSON(
"map",
"../assets/tilemaps/platformer-simple.json"
);
}
function create() {
const map = this.make.tilemap({ key: "map" });
const tiles = map.addTilesetImage(
"0x72-industrial-tileset-32px-extruded",
"tiles"
);
// Same setup as static layers
map.createLayer("Background", tiles);
groundLayer = map.createLayer("Ground", tiles);
map.createLayer("Foreground", tiles);
shiftKey = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.SHIFT);
// Set up the arrows to control the camera
const cursors = this.input.keyboard.createCursorKeys();
const controlConfig = {
camera: this.cameras.main,
left: cursors.left,
right: cursors.right,
up: cursors.up,
down: cursors.down,
speed: 0.5
};
controls = new Phaser.Cameras.Controls.FixedKeyControl(controlConfig);
// Limit the camera to the map size
this.cameras.main.setBounds(0, 0, map.widthInPixels, map.heightInPixels);
// Create a simple graphic that can be used to show which tile the mouse is over
marker = this.add.graphics();
marker.lineStyle(5, 0xffffff, 1);
marker.strokeRect(0, 0, map.tileWidth, map.tileHeight);
marker.lineStyle(3, 0xff4f78, 1);
marker.strokeRect(0, 0, map.tileWidth, map.tileHeight);
// Help text that has a "fixed" position on the screen
this.add
.text(
16,
16,
"Arrow keys to scroll\nLeft-click to draw tiles\nShift + left-click to erase",
{
font: "18px monospace",
fill: "#000000",
padding: { x: 20, y: 10 },
backgroundColor: "#ffffff"
}
)
.setScrollFactor(0);
}
function update(time, delta) {
controls.update(delta);
// Convert the mouse position to world position within the camera
const worldPoint = this.input.activePointer.positionToCamera(
this.cameras.main
);
// Place the marker in world space, but snap it to the tile grid. If we convert world -> tile and
// then tile -> world, we end up with the position of the tile under the pointer
const pointerTileXY = groundLayer.worldToTileXY(worldPoint.x, worldPoint.y);
const snappedWorldPoint = groundLayer.tileToWorldXY(
pointerTileXY.x,
pointerTileXY.y
);
marker.setPosition(snappedWorldPoint.x, snappedWorldPoint.y);
// Draw or erase tiles (only within the groundLayer)
if (this.input.manager.activePointer.isDown) {
if (shiftKey.isDown) {
groundLayer.removeTileAtWorldXY(worldPoint.x, worldPoint.y);
} else {
groundLayer.putTileAtWorldXY(353, worldPoint.x, worldPoint.y);
var tile = groundLayer.getTileAt(
worldPoint.x,
worldPoint.y
); //.index;
console.log(tile);
}
}
}
The problem is the function you are using getTileAt (link to the documentation), with world coordinates, there are two easy solutions:
you could use your variable pointerTileXY like:
var tile = groundLayer.getTileAt(
pointerTileXY.x,
pointerTileXY.y
);
Or you could use the function getTileAtWorldXY (link to the documentation)
var tile = groundLayer.getTileAtWorldXY(
worldPoint.x,
worldPoint.y
);
I would like to randomly spawn a sprite when an enemy dies.
Example: There is a 1 in 5 chance that when an enemy dies, it drops an object (sprites that increase your HP).
Any idea how this can be done?
I did some research, but I didn't find much.
For randomness in a Phaser application, I would use the Phaser's Math helper function Between (here is the link to the documentation).
It creates a random number (whole number) from the first number to the last one (including the last number, perfect for dice).
So for 1 in 5, you just need to select one number from the interval like 5and compare it with a call to the Between function. And only if it matches, you drop/create the sprite.
Just like this:
if(Phaser.Math.Between(1, 5) === 5){
// .. drop "loot" / health-object
}
Here a small Demo:
(In this demo something could be dropped or not, depending on your luck. 20% is pretty low)
document.body.style = 'margin:0;';
var config = {
type: Phaser.AUTO,
width: 536,
height: 183,
scene: {
create
},
banner: false
};
function create () {
this.add.text(10, 10, 'Click to red Boxes')
let graphics = this.make.graphics({x: 0, y: 0, add: false});
graphics.fillStyle(0xFF0000);
graphics.fillRect(0, 0, 20, 20);
graphics.generateTexture('enemy', 20, 20)
let enemiesGroup = this.add.group({
defaultKey: 'enemy',
maxSize: 10
});
let maxEnemiesToShow = 10
for(let idx = 0; idx < maxEnemiesToShow; idx++){
// here the function is used to spawn enemies randomly on screen
const x = Phaser.Math.Between(20, config.width - 20);
const y = Phaser.Math.Between(40, config.height /2 );
let enemy = enemiesGroup.get(x, y);
enemy.setInteractive()
.on('pointerdown', () => {
// 1 in 5
if(Phaser.Math.Between(1, 5) === 5){
// Drop object
this.add.rectangle(enemy.x, enemy.y, 10, 10, 0xFFFF00);
}
enemy.destroy();
})
}
}
new Phaser.Game(config);
<script src="https://cdn.jsdelivr.net/npm/phaser#3.55.2/dist/phaser.js"></script>
Bonus (because I find this Phaser function especially useful):
If you want to select different loot/outcome in phaser you, could even let phaser select from a selected Array, with the function Phaser.Math.RNG.pick(...) (link to documentation)
Bonus Demo:
document.body.style = 'margin:0;';
var config = {
type: Phaser.AUTO,
width: 536,
height: 183,
scene: {
create
},
banner: false
};
function create () {
this.add.text(10, 10, 'Click to red Boxes')
let graphics = this.make.graphics({x: 0, y: 0, add: false});
graphics.fillStyle(0xFF0000);
graphics.fillRect(0, 0, 20, 20);
graphics.generateTexture('enemy', 20, 20)
let enemiesGroup = this.add.group({
defaultKey: 'enemy',
maxSize: 10
});
let maxEnemiesToShow = 10
for(let idx = 0; idx < maxEnemiesToShow; idx++){
const x = Phaser.Math.Between(20, config.width - 20);
const y = Phaser.Math.Between(40, config.height /2 );
let enemy = enemiesGroup.get(x, y);
let loot = [0x00ff00, 0xffff00, 0x0000ff, 0x0, 0x0];
enemy
.setInteractive()
.on('pointerdown', () => {
// Select Colro from an Array of possibilities
let color = Phaser.Math.RND.pick(loot);
// only drop item if color is not black
if(color > 0){
this.add.rectangle(enemy.x, enemy.y, 10, 10, color);
}
enemy.destroy();
})
}
}
new Phaser.Game(config);
<script src="https://cdn.jsdelivr.net/npm/phaser#3.55.2/dist/phaser.js"></script>
Phaser Random functions, have the added bonus that you can create your own RandomDataGenerator with a specific seed if you want, that the random numbers, that are created, are generated in the same sequence. Great for testing and so.
For a 1/5 chance, you can use JavaScript's Math.random.
Math.random() will return a float between 0 and 1.
To not hard code this, you can use a function like the following which will return true or false given an odds (in your case 1/5)
function rollRandom(odds) {
return Math.random() < odds;
}
console.log(rollRandom(1/5))
I'm trying to make a button with cool-down animation with Phaser 3. Here is the code.
class BootScene extends Phaser.Scene {
constructor() {
super();
}
preload() {
this.load.path = 'https://raw.githubusercontent.com/liyi93319/phaser3_rpg/main/part1/assets/cooldown/';
for (var i = 0; i < 16; i++) {
this.load.image("cooldown" + i, "cooldown" + i + ".png");
}
this.load.image('magicAttack', 'magicAttack.png');
}
create() {
this.add.image(100, 100, 'magicAttack').setScale(1)
var cd = this.add.sprite(100, 100, 'cooldown1').setFlipX(false).setScale(1.5)
let ani_frames = [];
for (var i = 0; i < 16; i++) {
ani_frames.push({ key: "cooldown" + i })
}
this.anims.create({
key: 'right',
frames: ani_frames,
frameRate: 11,
repeat: 0
});
cd.play('right');
let circle2 = this.add.circle(100, 100, 150, 0x000000, 0).setScale(.2)
cd.on('animationcomplete', () => {
cd.setVisible(false)
});
// circle.lineStyle(10, 0xffffff, 0.9);
circle2.setStrokeStyle(50, 0x000000, 1);
}
}
var config = {
width: 400,
height: 300,
physics: {
default: 'arcade',
arcade: {
gravity: {
y: 0
},
debug: false // set to true to view zones
}
},
backgroundColor: 0x000000,
scene: [BootScene]
}
var game = new Phaser.Game(config);
<script src="https://cdn.jsdelivr.net/npm/phaser#3.55.2/dist/phaser.js"></script>
The code above works as expected but may be a little bit heavy-weight.
I know I can make a sprite-sheet but it's still in the image way. Is it feasible to use crop or tweens in Phaser 3 to do the job?
I know I could also put a DOM on the scene as using div to make the animation appears easier. However, it seems less Phaser style, right?
The code in this solution, is not much shorter, but a bit more cleaner/reuseable.
I would use the Phaser Masks (here is the link to the documentation)
And create a helper class, to abstract the whole masking and tweening logic out of the scene.
This helper class makes, the button also reuseable (the name for the helper class is maybe not the best).
The main idea is:
Create a mask the "masks" part of the image
Alter the mask with the tween, the tween alters only the percentage of the circle that is visible / hidden.
on the Tween Update Methode the Phaser Graphics Object (the mask) is rerendered.
Minor Update: corrected the mask starting angle, so that it matches with your example.
class Button {
constructor(scene, gameObject){
this.scene = scene;
this.mask = scene.add.graphics().setVisible(false);
this.mask.x = gameObject.x;
this.mask.y = gameObject.y;
gameObject.mask = new Phaser.Display.Masks.BitmapMask(scene, this.mask);
}
play(){
this.scene.tweens.add({
targets: this,
hiddenPercent: { from: 0, to: 1 },
ease: 'Power0',
repeat: 0,
duraton: 300,
onUpdate: _ => this.render()
});
}
render(){
this.mask.clear();
this.mask.fillStyle(0, 1);
this.mask.beginPath();
this.mask.slice(0, 0, 24, -Math.PI/2, (2 * Math.PI * this.hiddenPercent) -Math.PI/2 , false);
this.mask.fillPath();
}
}
class BootScene extends Phaser.Scene {
preload() {
this.load.image('magicAttack', 'https://raw.githubusercontent.com/liyi93319/phaser3_rpg/main/part1/assets/cooldown/magicAttack.png');
}
create() {
let hand = this.add.image(100, 100, 'magicAttack').setScale(1);
let button = new Button(this, hand);
button.play();
}
}
var config = {
width: 400,
height: 300,
physics: {
default: 'arcade',
arcade: {
gravity: {
y: 0
},
debug: false // set to true to view zones
}
},
backgroundColor: 0x000000,
scene: [BootScene]
}
var game = new Phaser.Game(config);
<script src="https://cdn.jsdelivr.net/npm/phaser#3.55.2/dist/phaser.js"></script>
I am new to Phaser Framework and I wanted to try making some prototype of 2D pool game from top down perspective. The problem that I have right now is detecting if all balls have stopped moving before restarting.
I use Physics.Matter and here is the source code when create so far:
this.matter.world.setBounds(0, 0, 720, 1280, 32, false, false, false, true);
this.add.image(400, 300, 'sky');
var ball = this.matter.add.image(360, 1000, 'ball');
ball.setCircle();
ball.setVelocity(-5, -20);
ball.setBounce(0.5);
for (var i = 1; i < 10; i++) {
var target = this.matter.add.image(Phaser.Math.Between(400,450), Phaser.Math.Between(400,450), 'target');
target.setCircle();
target.setVelocity(0, 0);
target.setBounce(0.7);
target.setFriction(0, 0.01);
target.setSleepEvents(true, true);
}
this.matter.world.on('sleepstart', function() {console.log('sleepstart');});
this.matter.world.on('sleepend', function() {console.log('sleepend');});
This would detect if each target has slept but I need to detect if ALL of them stopped moving. I cannot count how many has slept because sometimes when a target has entered sleep state, there is a chance some other body will bounce off it and woke it up again.
Is there any way to globally detect them?
EDIT: As a fallback plan I add a basic JS function to be called whenever update is called and count the sleeping bodies, which looks like it should not be a proper way:
var isActive = false;
// Some commands here that changes isActive = true
function onupdate() {
if (isActive) {
var bodyCount = this.matter.world.getAllBodies().filter(o => o.isSleeping === true).length;
console.log(bodyCount);
if (bodyCount >= 11) {
isActive = false;
}
}
}
I would put all objects you want to track into a phaser group (https://photonstorm.github.io/phaser3-docs/Phaser.GameObjects.Group.html) and iterate over the items in the group, to see if all have the property isSleeping set to true;
Warning: I can't say how performant this solution is, youre use case. If it is too slow, I would setup a counter variable, and count it down / up on sleepstart and sleepend. And when the counter is 0 all are sleeping.
Here a working demo, how I would do it:
(explaination are in the code, as comments)
// fix to prevent 'Warnings' in stackoverflow console
console.warn = _ => _
var config = {
type: Phaser.AUTO,
width: 400,
height: 100,
scene: { create },
physics: {
default: 'matter',
matter: {
debug: true,
setBounds: {
x: 0,
y: 0,
width: 400,
height: 100
},
enableSleeping: true
}
}
};
function create(){
// create the Phaser Group
this.targets = this.add.group();
for (var i = 1; i < 10; i++) {
var target = this.matter.add.image(200, 0, 10, 10, 'target');
target.setCircle();
target.setBounce(0.7);
target.setFriction(0, 0.01);
target.setSleepEvents(true, true);
// Add Item to the Group
this.targets.add(target);
}
this.matter.world.on('sleepstart', function(event, item){
// Check all targets are sleeping
if(!this.targets.getChildren().some( target => !target.body.isSleeping)){
console.log('all are sleeping');
}
}, this); // <- pass the scene as context
this.matter.world.on('sleepend', function() {console.log('sleepend');});
}
var game = new Phaser.Game(config);
<script src="https://cdn.jsdelivr.net/npm/phaser#3.55.2/dist/phaser.js"></script>
I am a new web dev and is currently working with Vue.js framework.
Decided to try out making a game with Phaser 2, and I am currently loving it!
Although i ran in to some problems with mouse events, i have no problems with key down events ;)
currently I am trying to click on an image, just to console log it out. (will want to drag later on.)
but first thing is first, I cant even get my console log out.
here is my code, and I am using it with vue.js!
Any help will be great ! Thank you!
Below are some snippets of my code that i think will be important
mounted () {
let self = this
if (this.game == null) {
this.game = new Phaser.Game(window.innerWidth, window.innerHeight, Phaser.AUTO, this.$el, {
preload: function preload () {
self.preload(this)
},
create: function create () {
self.create(this)
},
update: function update () {
self.update(this)
},
render: function redner () {
self.render(this)
}
})
}
}
preload () {
this.game.load.image('pink', require(`#/assets/images/packing/pink-bg.png`))
this.game.load.image('cat', require(`#/assets/images/packing/cat-head.png`))
this.game.load.image('table', require(`#/assets/images/packing/table.png`))
this.game.load.image('plate', require(`#/assets/images/packing/plate.png`))
this.game.load.spritesheet('container', require(`#/assets/images/packing/container-set.png`), 310, 270)
this.game.load.spritesheet('scorePlate', require(`#/assets/images/packing/kueh-set.png`), 313, 270)
}
let cat = this.game.add.sprite(0, 0, 'cat')
cat.height = this.game.world.height * 0.25
cat.width = this.game.world.width * 0.15
let table = this.game.add.sprite(0, 0, 'table')
table.height = this.game.height
table.width = this.game.width
this.game.physics.startSystem(Phaser.Physics.ARCADE)
let containerBullu = this.game.add.sprite(this.game.world.width - (this.game.world.centerX + this.game.world.centerX), this.game.world.height / 7, 'container')
containerBullu.height = this.game.world.height * 0.55
containerBullu.width = this.game.world.width * 0.35
containerBullu.frame = 4
containerBullu.inputEnabled = true
containerBullu.events.onInputDown.add(this.listener, containerBullu)
let containerCornflake = this.game.add.sprite(this.game.world.width - (this.game.world.centerX + this.game.world.centerX / 1.8), -50, 'container')
containerCornflake.height = this.game.world.height * 0.6
containerCornflake.width = this.game.world.width * 0.35
containerCornflake.frame = 9
render (phaser) {
this.game.debug.spriteInfo(this.containerBullu, 100, 100)
}
listener (containerBullu) {
console.log(containerBullu)
},
destroyed () {
this.game.destroy()
}
I hope to hear from anyone soon!
Thanks!
sincerely,
desperate and crying new front end web developer. (I was on it for 4hours. as of now. still going... )