I am making a platform game using phaser 3 and typescript. I am using a state machine and player controller to handle the logic for player movement but cant seem to figure out a way to play a falling animation ('pablo_fall') when the player is falling. I thought of maybe checking velocity.y and if its less than zero to enter the falling state but I cannot use the < operator on this.sprite.body.velocity.y. Here is the code from my playercontroller.ts file. Any help is appreciated!
import Phaser from 'phaser'
import StateMachine from '../statemachine/StateMachine'
export default class playercontroller {
private sprite: Phaser.Physics.Matter.Sprite
private StateMachine: StateMachine
private cursors: Phaser.Types.Input.Keyboard.CursorKeys
constructor(sprite: Phaser.Physics.Matter.Sprite, cursors: Phaser.Types.Input.Keyboard.CursorKeys) {
this.sprite = sprite
this.cursors = cursors
this.createAnimations()
this.StateMachine = new StateMachine(this, 'player')
this.StateMachine.addState('idle', {
onEnter: this.idleOnEnter,
onUpdate: this.idleOnUpdate
})
.addState('walk', {
onEnter: this.walkOnEnter,
onUpdate: this.walkOnUpdate
})
.addState('jump', {
onEnter: this.jumpOnEnter,
onUpdate: this.jumpOnUpdate
})
.addState('doubleJump',{
onEnter: this.doubleJumpOnEnter,
onUpdate: this.doubleJumpOnUpdate
})
.setState('idle')
this.sprite.setOnCollide((data: MatterJS.ICollisionPair) => {
if (this.StateMachine.isCurrentState('jump')||this.StateMachine.isCurrentState('doubleJump')) {
this.StateMachine.setState('idle')
}
})
}
update(dt: number) {
this.StateMachine.update(dt)
}
private idleOnEnter() {
this.sprite.play('pablo_idle')
}
private idleOnUpdate() {
if (this.cursors.left.isDown || this.cursors.right.isDown) {
this.StateMachine.setState('walk')
}
const spaceJustPressed = Phaser.Input.Keyboard.JustDown(this.cursors.space)
if (spaceJustPressed) {
this.StateMachine.setState('jump')
}
}
private walkOnEnter() {
this.sprite.play('pablo_run')
}
private walkOnUpdate() {
const speed = 5;
if (this.cursors.left.isDown) {
this.sprite.flipX = true
this.sprite.setVelocityX(-speed)
}
else if (this.cursors.right.isDown) {
this.sprite.flipX = false
this.sprite.setVelocityX(speed)
}
else {
this.sprite.setVelocityX(0)
this.StateMachine.setState('idle')
}
const spaceJustPressed = Phaser.Input.Keyboard.JustDown(this.cursors.space)
if (spaceJustPressed) {
this.StateMachine.setState('jump')
}
}
private jumpOnEnter() {
this.sprite.setVelocityY(-6)
this.sprite.play('pablo_jump')
}
private jumpOnUpdate() {
const speed = 5;
let canDoubleJump = true;
const spaceJustPressed = Phaser.Input.Keyboard.JustDown(this.cursors.space)
if (spaceJustPressed) {
this.StateMachine.setState('doubleJump')
}
if (this.cursors.left.isDown) {
this.sprite.flipX = true
this.sprite.setVelocityX(-speed)
}
else if (this.cursors.right.isDown) {
this.sprite.flipX = false
this.sprite.setVelocityX(speed)
}
}
private doubleJumpOnEnter(){
this.sprite.setVelocityY(-6)
this.sprite.play('pablo_double_jump')
}
private doubleJumpOnUpdate() {
const speed = 5;
if (this.cursors.left.isDown) {
this.sprite.flipX = true
this.sprite.setVelocityX(-speed)
}
else if (this.cursors.right.isDown) {
this.sprite.flipX = false
this.sprite.setVelocityX(speed)
}
}
private createAnimations() {
this.sprite.anims.create({
key: 'pablo_idle',
frameRate: 20,
frames: this.sprite.anims.generateFrameNames('pablo_idle', {
start: 0,
end: 10,
prefix: 'Idle (32x32)-',
suffix: '.png'
}),
repeat: -1
})
this.sprite.anims.create({
key: 'pablo_run',
frameRate: 20,
frames: this.sprite.anims.generateFrameNames('pablo_run', {
start: 0,
end: 10,
prefix: 'Run (32x32)-',
suffix: '.png'
}),
repeat: -1
})
this.sprite.anims.create({
key: 'pablo_jump',
frameRate: 20,
frames: 'pablo_jump',
repeat: -1
})
this.sprite.anims.create({
key: 'pablo_fall',
frameRate: 20,
frames: 'pablo_fall',
repeat: -1
})
this.sprite.anims.create({
key: 'pablo_double_jump',
frameRate: 20,
frames: this.sprite.anims.generateFrameNames('pablo_double_jump', {
start: 0,
end: 5,
prefix: 'Double Jump (32x32)-',
suffix: '.png',
})
})
}
}```
I have no idea why i couldnt use the < operator before, it said something about being undefined. Anyway this is the code.
if (this.sprite.body.velocity.y> 0){
this.sprite.anims.play('pablo_fall')
}
Related
So I've been trying to replicate the tile chebyshev lab outside of the phaser lab area, in glitch, and I've been Using the variable solution I got in this answer about using Phaser.gamestate for my variable that need to be update in many locations
And my Console hits me with Cannot read properties of undefined (reading 'gameState')
I've tried the this context setting too, but it continues this error, what can I do to fix it?
My Code:
import Phaser from "../lib/phaser.js";
export default class Game extends Phaser.Scene {
constructor() {
super("game1");
}
preload() {
this.load.image(
"Tileset",
"https://cdn.glitch.global/cc90578e-c3d0-47c5-bb0d-f5a81263b5b6/pixil-frame-0%20(17).png?v=1675985219390"
);
this.load.tilemapTiledJSON(
"map",
"https://cdn.glitch.global/cc90578e-c3d0-47c5-bb0d-f5a81263b5b6/Map.tmj?v=1675985261369"
);
this.load.spritesheet(
"player",
"https://cdn.glitch.global/cc90578e-c3d0-47c5-bb0d-f5a81263b5b6/pixil-frame-0%20(13).png?v=1675904194091",
{ frameWidth: 32, frameHeight: 32 }
);
}
create() {
this.gameState = {
map: "",
cursors: "",
};
this.gameState.map = this.make.tilemap({ key: "map" });
this.tiles = this.gameState.map.addTilesetImage("tileSet", "Tileset");
this.worldLayer = this.gameState.map.createLayer(
"Block Layer",
this.tiles,
0,
0
);
this.player = this.physics.add.sprite(0, 0, "player", 1);
this.cameras.main.setBounds(
0,
0,
this.gameState.map.widthInPixels,
this.gameState.map.heightInPixels
);
this.cameras.main.startFollow(this.player);
this.gameState.cursors = this.input.keyboard.createCursorKeys();
}
update() {
function updateMap() {
console.log(this.gameState)
var origin = this.gameState.map.getTileAtWorldXY(
this.player.x,
this.player.y
);
this.gameState.map.forEachTile(function (tile) {
var dist = Phaser.Math.Distance.Chebyshev(
origin.x,
origin.y,
tile.x,
tile.y
);
tile.setAlpha(1 - 0.1 * dist);
}, this);
}
if (this.gameState.cursors.left.isDown) {
this.player.setVelocityX(-50);
this.player.anims.play("left", true);
} else if (this.gameState.cursors.right.isDown) {
this.player.setVelocityX(50);
this.player.anims.play("right", true);
} else if (this.gameState.cursors.up.isDown) {
this.player.setVelocityY(-50);
} else if (this.gameState.cursors.down.isDown) {
this.player.setVelocityY(-50);
} else {
this.player.setVelocityX(0);
}
updateMap();
}
}
Update
I've put the function outside the scene, no change whatsoever
new code
import Phaser from "../lib/phaser.js";
export default class Game extends Phaser.Scene {
constructor() {
super("game1");
}
preload() {
this.load.image(
"Tileset",
"https://cdn.glitch.global/cc90578e-c3d0-47c5-bb0d-f5a81263b5b6/pixil-frame-0%20(17).png?v=1675985219390"
);
this.load.tilemapTiledJSON(
"map",
"https://cdn.glitch.global/cc90578e-c3d0-47c5-bb0d-f5a81263b5b6/Map.tmj?v=1675985261369"
);
this.load.spritesheet(
"player",
"https://cdn.glitch.global/cc90578e-c3d0-47c5-bb0d-f5a81263b5b6/pixil-frame-0%20(13).png?v=1675904194091",
{ frameWidth: 32, frameHeight: 32 }
);
}
create() {
this.gameState = {
map: "",
cursors: "",
};
this.gameState.map = this.make.tilemap({ key: "map" });
this.tiles = this.gameState.map.addTilesetImage("tileSet", "Tileset");
this.worldLayer = this.gameState.map.createLayer(
"Block Layer",
this.tiles,
0,
0
);
this.player = this.physics.add.sprite(0, 0, "player", 1);
this.cameras.main.setBounds(
0,
0,
this.gameState.map.widthInPixels,
this.gameState.map.heightInPixels
);
this.cameras.main.startFollow(this.player);
this.gameState.cursors = this.input.keyboard.createCursorKeys();
}
update() {
if (this.gameState.cursors.left.isDown) {
this.player.setVelocityX(-50);
this.player.anims.play("left", true);
} else if (this.gameState.cursors.right.isDown) {
this.player.setVelocityX(50);
this.player.anims.play("right", true);
} else if (this.gameState.cursors.up.isDown) {
this.player.setVelocityY(-50);
} else if (this.gameState.cursors.down.isDown) {
this.player.setVelocityY(-50);
} else {
this.player.setVelocityX(0);
}
updateMap();
}
}
function updateMap() {
console.log(this.gameState);
var origin = this.gameState.map.getTileAtWorldXY(
this.player.x,
this.player.y
);
this.gameState.map.forEachTile(function (tile) {
var dist = Phaser.Math.Distance.Chebyshev(
origin.x,
origin.y,
tile.x,
tile.y
);
tile.setAlpha(1 - 0.1 * dist);
}, this);
}
I think the problem can be solved by moving the function updateMap out of the update function/method. Making updateMap a function/mehtod of the Game class.
Like this the this inside of the updateMap function should be pointing to the correct object.
Sample Code snippet (updated):
update() {
if (this.gameState.cursors.left.isDown) {
...
}
...
// call the method
this.updateMap();
}
// the function is now a method of the class
updateMap() {
console.log(this.gameState)
var origin = this.gameState.map.getTileAtWorldXY(
this.player.x,
this.player.y
);
...
}
...
I have 2 scenes, a gameplay scene and a result screen scene, when I click on the retry button within the result screen scene everything restarts as intended but the text is no longer following the player. It is instead positioned in relation to where the player touched the goal flag that leads to the result screen.
below is all of the code involved with the text within the game and the respective functions.
class gameScene extends Phaser.Scene{
preload(){
gameScene.seconds = 0
this.interval = setInterval(counter, 1000);
}
create(){
gameScene.Text = this.add.text(this.player.x+50, this.player.y-50, { fontSize: '16px', fill: '#fff' });
gameScene.Text.setText(0);
}
update(){
gameScene.Text.x=this.player.x+50
gameScene.Text.y=this.player.y-50
}
}
function counter(){
gameScene.seconds = gameScene.seconds+1
gameScene.Text.setText(gameScene.seconds)
}
any and all help will be appreciated.
thanks a lot :)
edit: below is the entire current code of the game.
class gameScene extends Phaser.Scene {
preload(){
gameScene.seconds = 0
this.k=0
this.velocity = 0
this.endReached = false
this.facingLeft=false
this.facingRight=false
this.interval = setInterval(counter, 1000);
//load stage and tileset
this.load.image('tiles', 'assets/skyTileset.png');
this.load.image('goalFlag','assets/goalFlag.png')
this.load.tilemapTiledJSON('sky', 'assets/bouncy_shrooms.json');
//loading assets into game
this.load.spritesheet('player', 'assets/player.png',
{ frameWidth: 16, frameHeight: 16 }
);
};
create(){
//create assets within the game
this.skyMap = this.make.tilemap({key: 'sky'});
this.tileset = this.skyMap.addTilesetImage('skyShrooms','tiles');
this.background = this.skyMap.createLayer('sky', this.tileset,);
this.platforms = this.skyMap.createLayer('platforms', this.tileset,);
this.stems = this.skyMap.createLayer('stems', this.tileset,);
this.platforms.setCollisionByExclusion([-1]);
this.goal = this.physics.add.sprite(1200, 87, 'goalFlag')
this.goal.setScale(0.1)
this.goal.body.setAllowGravity(false);
this.goal.setImmovable(true)
this.physics.world.setBounds(0,0,1280,960)
//loading the player sprite as a physics based sprite.
//this.player = this.physics.add.sprite(50,850,"player");
this.player = this.physics.add.sprite(1000,50,"player");
this.player.setScale(1.75)
this.player.setCollideWorldBounds(true)
this.physics.add.collider(this.player, this.platforms);
Text = this.add.text(this.player.x+50, this.player.y-50, { fontSize: '16px', fill: '#fff' });
Text.setText(0);
//player animations
this.anims.create({
key: "left-idle",
frames: this.anims.generateFrameNumbers('player', { start: 1, end: 1 }),
frameRate: 0,
repeat: -1
})
this.anims.create({
key: "right-idle",
frames: this.anims.generateFrameNumbers('player', {start: 0, end: 0}),
frameRate: 0,
repeat: -1
})
this.anims.create({
key:"left-walk",
frames: this.anims.generateFrameNumbers('player', {start: 6, end: 9}),
frameRate: 8,
repeat: -1
})
this.anims.create({
key:"right-walk",
frames: this.anims.generateFrameNumbers('player', {start: 2, end: 5}),
frameRate: 8,
repeat: -1
})
this.anims.create({
key:"right-jump",
frames: this.anims.generateFrameNumbers('player', {start: 10, end: 10}),
frameRate: 0,
repeat: -1
})
this.anims.create({
key:"right-fall",
frames: this.anims.generateFrameNumbers('player', {start: 11, end: 11}),
frameRate: 0,
repeat: -1
})
this.anims.create({
key:"left-jump",
frames: this.anims.generateFrameNumbers('player', {start: 12, end: 12}),
frameRate: 0,
repeat: -1
})
this.anims.create({
key:"left-fall",
frames: this.anims.generateFrameNumbers('player', {start: 13, end: 13}),
frameRate: 0,
repeat: -1
})
//camera
this.cameras.main.startFollow(this.player);
this.cameras.main.setBounds(0,0,1280,960);
};
update(){
//text always within player view
this.cameras.main.zoom = 2
Text.x=this.player.x+50
Text.y=this.player.y-50
//get variable for player velocity
this.velocity=this.player.body.velocity.x;
//creating arrow key input
this.cursors = this.input.keyboard.createCursorKeys();
//creating key input
this.keyW = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.W);
this.keyA = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.A);
this.keyS = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.S);
this.keyD = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.D);
//player movement
if (this.endReached == false){
if (this.cursors.left.isDown || this.keyA.isDown){
this.player.setVelocityX(-90)
console.log("left")
this.player.anims.play('left-walk', true)
}
else if (this.cursors.right.isDown || this.keyD.isDown){
this.player.setVelocityX(90)
console.log("right")
this.player.anims.play('right-walk', true)
}
else{
this.player.setVelocityX(0)
if (this.facingLeft == true){
this.player.anims.play('right-idle',true);
}
else if (this.facingRight == true){
this.player.anims.play('left-idle',true);
};
}
if ((this.cursors.up.isDown || this.keyW.isDown) && this.player.body.onFloor()){
this.player.setVelocityY(-180)
console.log("up")
}}
if (this.velocity < 0){
this.facingRight = true;
this.facingLeft = false;
}
else if(this.velocity > 0){
this.facingLeft = true;
this.facingRight = false;
};
this.physics.add.collider(this.player, this.goal, endCollision,null,this);
};
}
class timeTrialEnd extends Phaser.Scene{
preload(){
this.load.image('finished','assets/finishedBackground.png' )
this.load.image('retryButton','assets/retryButton.png')
}
create(){
this.add.image(640,480,'finished')
this.add.text(100, 500, { fontSize: '16px', fill: '#000' })
.setText('You completed the stage in '+gameScene.seconds+' seconds!').setScale(3);
console.log("loaded")
const retryButton = this.add.image(100,800,'retryButton').setInteractive()
retryButton.on('pointerdown', () => {game.scene.add('gameScene', gameScene, true, { x: 1280, y: 960 });
game.scene.remove('timeTrialEnd');
console.log('loaded')
});
}
}
// timer function
function counter(){
gameScene.seconds = gameScene.seconds+1
this.Text.setText(gameScene.seconds)
}
function endCollision(){
while (this.k == 0){
clearInterval(this.interval)
this.endReached = true
this.player.anims.stop()
game.scene.add('timeTrialEnd', timeTrialEnd, true, { x: 1280, y: 960 });
game.scene.remove('gameScene');
this.k+=1
}}
var config = {
type: Phaser.AUTO,
pixelArt: true,
width: 1280,
height: 960,
physics: {
default: 'arcade',
arcade: {
gravity: { y: 200 },
debug: true
}
},
scene: gameScene
};
var game = new Phaser.Game(config);
ANSWERED
thanks to a kind coder I have gotten a solution to the problem.
this.seconds = 0;
this.interval = setInterval(
() => { // <- this is a inline arrow function see mdn link for details
this.seconds = this.seconds + 1
this.timerText.setText(this.seconds)
},1000);
the main part of the solution is to add this code snippet into the create function instead of establishing an interval that leads to an external function. This, as stated in their answer will put the text variable within the interval into context of the scene. After I deleted the establishing of an interval in preload() and the external function. change every involved variable or object to have this. before it and it now works (for me anyway). thanks so much for the help.
There are a some issues, that I can identify at first glance (Check the final "Update" for the full solution) :
Why is the Text bound to the class (gameScene) and not the instance (this). This is probably the main cause of the error.
btw.: I would name classes in javascript pascal case, so class GameScene ... just to avoid confusion.
Where is the this.player set? I can't see it in the create function, where one would usually find it. The Text shouldn't be displayed since this.player is likely undefined. Check the browser console for errors
optional: setInterval I personally would always use the phaser builtin function this.time.addEvent, over core javascript other libraries, when possible. Checkout this official example for a nice alternative
So to solve the problem, I would recommend:
attach the Text to the class instance , but rename it to something like this.playerInfoText to avoid naimg collisions
ensure the the player (this.player) is create in the create function of the scene, so that the x and y properties can be read and set correct
finally: check the browser console for other related errors
Update, after question was updated:
You should change the Text variable:
From this:
create(){
...
Text = this.add.text(this.player.x+50, this.player.y-50, { fontSize: '16px', fill: '#fff' });
Text.setText(0);
...
}
To this (and any other place you are using the Text):
create(){
...
this.playerInfoText = this.add.text(this.player.x+50, this.player.y-50, { fontSize: '16px', fill: '#fff' });
this.playerInfoText.setText(0);
...
}
Update 2:
The reason why the counter throws an error is that the context ist not more correct. (details mdn article)
Solution, to comment
delete this lines from the preloadfunction:
gameScene.seconds = 0
this.interval = setInterval(counter, 1000);
and after the creation of the this.playerand this.playerInfoText( as mentioned above) just add the counter creation and inline the function:
this.seconds = 0;
this.interval = setInterval(
() => { // <- this is a inline arrow function see mdn link for details
this.seconds = this.seconds + 1
this.playerInfoText.setText(this.seconds)
});
or using with the builtin addEvent function instead of setInterval (also in the create function):
this.seconds = 0;
this.interval = scene.time.addEvent({
delay: 1000,
callback: counter,
callbackScope: this,
loop: true
});
...
// some tweaks have to be done on the counter function, for this to work
function counter(){
this.seconds = this.seconds+1
this.playerInfoText.setText(this.seconds)
}
Very new to Phaser so I think I might be fundamentally misunderstanding something.
My game is supposed to be a clone of 'Jetpack Joyride' where the player jumps to avoid obstacles and collect coins etc.
I am currently trying to create a powerup which makes all of the coins on screen zoom towards the player (and therefore collect all of the coins).
I have used the this.physics.moveToObject function but it always gives me the error: 'Uncaught TypeError: Cannot read properties of undefined (reading 'velocity')'.
My thinking is that this function can't pull multiple objects at once - or it isn't able to 'locate' all instances of 'coins'(perhaps because of the way I have set up the random generation of them).
This could also be something to do with a basic syntax error on my end.
One issue I have noticed is, I need to actually destroy the sprites as they go off the screen - but not sure this is related to the above.
Anyway, any help is appreciated!
const gameState = {
score: 0,
endGame: 0,
timer: 0,
text1: '',
text2: '',
music:'',
coinscore: 0,
coincollect:'',
speed: -400,
coins: '',
};
class GameScene extends Phaser.Scene {
constructor() {
super({ key: 'GameScene' })
}
create() {
//Background
this.createParalaxBackgrounds();
//Add Timer
gameState.timer = this.time.addEvent({
delay: 999999,
paused: false
});
//Add Player
var playerAnimation = this.anims.create({
key: 'run',
frames: [{
key: 'player',
frame: "sprite6"
}, {
key: 'player',
frame: "sprite16"
}],
frameRate: 5,
repeat: -1
});
this.anims.create(playerAnimation);
this.player = this.physics.add.sprite(320, 300, 'Player').setScale(3).setDepth(11).play('run');
this.player.setSize(15, 16, false);
//Add Music
//gameState.music = this.sound.add('BackgroundMusic', { loop: true});
gameState.coincollect = this.sound.add('coin_collect', {loop : false});
//Add World Physics
this.physics.world.setBounds(0, 0, 800, 600);
this.player.setCollideWorldBounds(true);
this.player.setImmovable(true);
//Add Covid Physics
const covid = this.physics.add.group({immovable: true,
allowGravity: false});
covid.setDepth(11);
gameState.coins = this.physics.add.group({immovable: true, allowGravity: false});
gameState.coins.setDepth(11);
const magnets = this.physics.add.group({immovable: true, allowGravity: false})
magnets.setDepth(11);
//Add Test Text
gameState.text1 = this.add.text(700, 10, `Score = ${gameState.score}`);
gameState.text1.setOrigin(0.5, 0.5).setDepth(11);
gameState.text2 = this.add.text(400, 50, `Coins Collected = ${gameState.coinscore}`, { fontSize: '15px', fill: '#000000' });
gameState.text2.setOrigin(0.5, 0.5).setDepth(11)
//Random Score Used for Coin Spawn Generation
const CoinNumb = 500;
//Random Score Used for Enemy Spawn Generation
const RandomCovidGenNumb = 2000;
//Random Scored used for Magnet
const MagnetSpawnNumb = 4000;
// Enemy Spawn
function CovidGen () {
const yCoord = Math.random() * 600;
covid.create(800, yCoord, 'Covid').setDepth(11);
covid.setVelocityX(gameState.speed);
}
// Power Up Spawn
function MagnetGen() {
const yCoord = Math.random() * 600;
magnets.create(800, yCoord, 'coin_magnet').setDepth(11);
magnets.setVelocityX(gameState.speed);
}
// Adding Enemy Spawn Loop
const CovidGenLoop = this.time.addEvent({
delay: RandomCovidGenNumb,
callback: CovidGen,
callbackScope: this,
loop: true,
});
// Adding Coin Spawn
function CoinGen () {
const yCoord = Math.random() * 600;
gameState.coins.create(800, yCoord, 'coin').setDepth(11).setScale(0.25);
gameState.coins.setVelocityX(gameState.speed);
}
// Adding Coin Spawn Loop
const CoinGenLoop = this.time.addEvent({
delay: CoinNumb,
callback: CoinGen,
callbackScope: this,
loop: true,
});
const MagnetGenLoop = this.time.addEvent({
delay: MagnetSpawnNumb,
callback: MagnetGen,
callbackScope: this,
loop: true,
});
// Add Keyboard Input
const SpaceBar = this.input.keyboard.addKey('SPACE');
//Setting Enemy Spawn Velocity
//covid.setVelocityX(-300);
//Adding Collider between enemy and player + scene restart
this.physics.add.collider(this.player, covid, () => {
gameState.score += gameState.coinscore;
this.add.text(400, 300, `Game Over! \n Total Distance Travelled = ${gameState.score - gameState.coinscore} \n Total Coins Collected = ${gameState.coinscore} \n Total Score = ${gameState.score}`, { fontSize: '15px', fill: '#000000' }).setOrigin(0.5, 0.5).setDepth(11);
CovidGenLoop.destroy();
CoinGenLoop.destroy();
this.physics.pause();
gameState.timer.paused = true;
gameState.endGame += 1;
this.anims.pauseAll();
this.input.on('pointerup', () => {
gameState.endGame -= 1;
this.scene.restart();
gameState.timer.remove();
gameState.coinscore = 0;
this.anims.resumeAll();
});
})
//Adding Collider between player and coin
this.physics.add.collider(this.player, gameState.coins, (player, coin) => {
coin.destroy();
gameState.coinscore += 1;
gameState.coincollect.play();
})
//Adding Collider between player and magnet power up
this.physics.add.collider(this.player, magnets, (player, magnet, coin) => {
magnet.destroy();
this.physics.moveToObject(gameState.coins, this.player, 200);
})
}
Well the error is caused, because you are passing a group and not a gameobject to the function (details can befound in the documentation).
You could loop over all children/conis in the group, a call the function moveToObjecton each of them.
here a short demo (based on your code):
class GameScene extends Phaser.Scene {
constructor() {
super({ key: 'GameScene' });
}
loadCoins(coins){
for(let i = 0; i < 5; i++){
let yCoord = Math.random() * 200;
let coin = this.add.rectangle(400, yCoord, 15, 15, 0xFFFF00);
coin = this.physics.add.existing(coin);
coins.add(coin);
}
coins.setVelocityX(-100);
}
create() {
this.running = true;
this.message = this.add.text(10, 10, 'Click to activate Magnet');
this.player = this.add.rectangle(200, 100, 20, 20, 0xffffff);
this.physics.add.existing(this.player);
//Add World Physics
this.physics.world.setBounds(0, 0, 400, 200);
this.player.body.setCollideWorldBounds(true);
this.player.body.setImmovable(true);
let coins = this.physics.add.group({immovable: true, allowGravity: false});
this.loadCoins(coins);
this.input.on('pointerdown', _ => {
if(this.running){
coins.children.each(coin => {
// pull only coins infronz of the player
if(coin.x >= this.player.x){
this.physics.moveToObject(coin, this.player, 1500);
}
});
this.message.text = 'Click to reset';
} else {
this.loadCoins(coins);
this.message.text = 'Click to activate Magnet';
}
this.running = !this.running;
});
this.physics.add.collider(this.player, coins,
(player, coin) => {
coin.destroy();
});
}
}
const config = {
type: Phaser.AUTO,
width: 400,
height: 200,
scene: [ GameScene ],
physics: {
default: 'arcade',
}
};
const game = new Phaser.Game(config);
<script src="https://cdn.jsdelivr.net/npm/phaser#3.55.2/dist/phaser.js"></script>
Update:
I updated the code, so that "coins", that are behind the player won't be fulled. This can be tweeked to:
only pull coins in a specific distance
only pull coins that are visible
and so on
I need to achieve the tree view (Go JS Tree View). The respective tree view sample source code without React JS is at (Tree View Source Code). I'm trying to do the same using React JS and have the following code written. But somehow I'm missing something and the diagram/tree view is not rendering. Can you please help me to figure out the issue?
import React from 'react';
import * as go from 'gojs';
import { ReactDiagram } from 'gojs-react';
import '../../../App.css';
go.Shape.defineFigureGenerator("ExpandedLine", function(shape, w, h) {
return new go.Geometry()
.add(new go.PathFigure(0, 0.25*h, false)
.add(new go.PathSegment(go.PathSegment.Line, .5 * w, 0.75*h))
.add(new go.PathSegment(go.PathSegment.Line, w, 0.25*h)));
});
// use a sideways V figure instead of PlusLine in the TreeExpanderButton
go.Shape.defineFigureGenerator("CollapsedLine", function(shape, w, h) {
return new go.Geometry()
.add(new go.PathFigure(0.25*w, 0, false)
.add(new go.PathSegment(go.PathSegment.Line, 0.75*w, .5 * h))
.add(new go.PathSegment(go.PathSegment.Line, 0.25*w, h)));
});
let nodeDataArray = [{ key: 0 }];
const initDiagram = () => {
let $ = go.GraphObject.make;
const diagram =
$(go.Diagram, "myDiagramDiv",
{
allowMove: false,
allowCopy: false,
allowDelete: false,
allowHorizontalScroll: false,
layout:
$(go.TreeLayout,
{
alignment: go.TreeLayout.AlignmentStart,
angle: 0,
compaction: go.TreeLayout.CompactionNone,
layerSpacing: 16,
layerSpacingParentOverlap: 1,
nodeIndentPastParent: 1.0,
nodeSpacing: 0,
setsPortSpot: false,
setsChildPortSpot: false
})
});
diagram.nodeTemplate =
$(go.Node,
{ // no Adornment: instead change panel background color by binding to Node.isSelected
selectionAdorned: false,
// a custom function to allow expanding/collapsing on double-click
// this uses similar logic to a TreeExpanderButton
doubleClick: function(e, node) {
let cmd = diagram.commandHandler;
if (node.isTreeExpanded) {
if (!cmd.canCollapseTree(node)) return;
} else {
if (!cmd.canExpandTree(node)) return;
}
e.handled = true;
if (node.isTreeExpanded) {
cmd.collapseTree(node);
} else {
cmd.expandTree(node);
}
}
},
$("TreeExpanderButton",
{ // customize the button's appearance
"_treeExpandedFigure": "ExpandedLine",
"_treeCollapsedFigure": "CollapsedLine",
"ButtonBorder.fill": "whitesmoke",
"ButtonBorder.stroke": null,
"_buttonFillOver": "rgba(0,128,255,0.25)",
"_buttonStrokeOver": null
}),
$(go.Panel, "Horizontal",
{ position: new go.Point(18, 0) },
new go.Binding("background", "isSelected",
s => (s ? 'lightblue' : 'white')).ofObject(),
$(go.Picture,
{
width: 18, height: 18,
margin: new go.Margin(0, 4, 0, 0),
imageStretch: go.GraphObject.Uniform
},
// bind the picture source on two properties of the Node
// to display open folder, closed folder, or document
new go.Binding("source", "isTreeExpanded", imageConverter).ofObject(),
new go.Binding("source", "isTreeLeaf", imageConverter).ofObject()),
$(go.TextBlock,
{ font: '9pt Verdana, sans-serif' },
new go.Binding("text", "key", function(s) { return "item " + s; }))
) // end Horizontal Panel
); // end Node
diagram.linkTemplate = $(go.Link);
let max = 499;
let count = 0;
while (count < max) {
count = makeTree(3, count, max, nodeDataArray, nodeDataArray[0]);
}
diagram.model = new go.TreeModel(nodeDataArray);
return diagram;
}
function makeTree(level, count, max, nodeDataArray, parentData) {
let numChildren = Math.floor(Math.random() * 10);
for (let i = 0; i < numChildren; i++) {
if (count >= max) return count;
count++;
let childData = { key: count, parent: parentData.key };
nodeDataArray.push(childData);
if (level > 0 && Math.random() > 0.5) {
count = makeTree(level - 1, count, max, nodeDataArray, childData);
}
}
return count;
}
function imageConverter(prop, picture) {
let node = picture.part;
if (node.isTreeLeaf) {
return "images/document.svg";
} else {
if (node.isTreeExpanded) {
return "images/openFolder.svg";
} else {
return "images/closedFolder.svg";
}
}
}
window.addEventListener('DOMContentLoaded', initDiagram);
const TreeView = () => {
return (
<>
GO JS
<div id="myDiagramDiv">
<ReactDiagram
initDiagram={initDiagram}
divClassName='diagram-component'
nodeDataArray={nodeDataArray}
skipsDiagramUpdate={false}
/>
</div>
</>
);
}
export default TreeView;
When React start executing, the DOMContentLoaded event have already been fired. Instead try to call initDiagram in a useEffect hook
const TreeView = () => {
useEffect(initDiagram);
return (
<>
GO JS
<div id="myDiagramDiv">
<ReactDiagram
initDiagram={initDiagram}
divClassName='diagram-component'
nodeDataArray={nodeDataArray}
skipsDiagramUpdate={false}
/>
</div>
</>
);
}
i get the image from the service class via this code
this.cameraService.getImage(this.width, this.height, this.quality).subscribe(data => this.image = data, error =>
I would want to pass this code to one of the getVision() function so that i am able to use the Google API.May i know how do i do it? I tried declaring a string variable and try to put the above code inside the variable however it does not work.Below are the codes
Camera.TS class
export class CameraPage {
width: number;
height: number;
cropper: Cropper;*/
image:string;
width:number = 500;
height:number = 500;
quality:number = 90;
picture:string;
labels: Array<any> = [];
//translation
scanning: Array<any> = [];
choseLang: boolean = false;
loading: boolean = false;
constructor(public navCtrl: NavController, public navParams: NavParams,public testService: TestService,public cameraService: CameraService,public toastCtrl: ToastController) {
}
addPhoto(){ //take picture & return image ***
this.cameraService.getImage(this.width, this.height, this.quality).subscribe(data => this.image = data, error =>
{
this.getVision(this.image);
// Toast errot and return DEFAULT_PHOTO from Constants
this.toast(error);
});
}
toast(message: string) {
let toast = this.toastCtrl.create({
message: message,
duration: 2500,
showCloseButton: false
});
toast.present();
}
getVision(image64:string) {
this.testService.getVisionLabels(image64:string)
.subscribe((sub) => {
this.labels = sub.responses[0].textAnnotations;
this.getText();
});
}
getText() {
this.labels.forEach((label) => {
let translation = {search: label.description, result: ''};
console.log(label.description);
});
}
}
camera service class
export class CameraService {
public base64Image: string;
constructor(public platform: Platform, public alertCtrl: AlertController, public modalCtrl: ModalController, private http: Http) {
}
getImage(width: number, height: number, quality: number) {
return Observable.create(observer => {
//Set default options for taking an image with the camera
let imageOptions: any = {
quality: quality,
destinationType: Camera.DestinationType.DATA_URL,
sourceType: Camera.PictureSourceType.CAMERA,
encodingType: Camera.EncodingType.JPEG,
correctOrientation: 1,
saveToPhotoAlbum: false,
mediaType: Camera.MediaType.PICTURE,
cameraDirection: 1
};
let selectAlert = this.alertCtrl.create({
title: 'Let\'s add a picture!',
message: "Select how you would like to add the picture",
enableBackdropDismiss: false,
buttons: [{
text: 'Albums',
handler: data => {
//Change sourceType to PHOTOLIBRARY
imageOptions.sourceType = Camera.PictureSourceType.PHOTOLIBRARY;
selectAlert.dismiss();
}
}, {
text: 'Camera',
handler: data => {
selectAlert.dismiss();
}
}]
});
selectAlert.onDidDismiss(() => {
this.getCameraImage(imageOptions).subscribe(image => { //image options are either album or camera**
let cropModal = this.modalCtrl.create(ScannerPage, { "imageBase64": image, "width": 500, "height": 500 });
cropModal.onDidDismiss((croppedImage: any) => {
if (!croppedImage)
observer.error("Canceled while cropping.")
else {
observer.next(croppedImage);
observer.complete();
}
});
cropModal.present();
}, error => observer.error(error));
});
selectAlert.present();
});
}
getCameraImage(options: any) { //get base64 image
return Observable.create(observer => {
this.platform.ready().then(() => {
Camera.getPicture(options).then((imageData: any) => {
// imageData is a base64 encoded string as per options set above
let base64Image: string = "data:image/jpeg;base64," + imageData;
observer.next(base64Image);
observer.complete();
}, error => {
observer.error(error);
});
});
});
}
}
You need to understand that the data returned by an Observable service can be accessed in .subscribe() callback 1 and not in callback 2. Read this for more clarification.
this.cameraService.getImage(this.width,this.height,this.quality)
.subscribe( (data) => {
this.image = data;
this.getVision(this.image);
},(error) => {
// Toast errot and return DEFAULT_PHOTO from Constants
this.toast(error);
}
);