Following the phaser documentation, the scene restart is not working.
What I'm doing is the following, there are three scenes (start, play and end.). When it gets to the end, when clicking on the screen it gives this.scene.start('play'). I tried several ways, researched a lot and tried several things but the ones that worked in my case, the game for some events and some settings are not reset.
For example, in this game you will create enemies and some packs. When it is restarted or started again, these builds are no longer executed.
Start
import Phaser from "phaser";
class Start extends Phaser.Scene{
constructor ()
{
super({ key: 'start', active: true });
}
init(){
console.log("- ./node_modules/babel-loader/lib??ref--2-0!./node_modules/vue-loader/lib??vue-loader-options!./pages/game/index.vue?vue&type=script&lang=js&")
console.log("initiated 0418971")
console.log("Unexpected console statement no-console")
console.log("bundle 'client' has 1 warnings")
console.log("Caching create all")
this.cameras.main.setBackgroundColor("#24252A");
}
preload(){
}
create() {
this.helloWorld = this.add.text(
this.cameras.main.centerX,
this.cameras.main.centerY,
"Clique para jogar",
{ font: "40px Arial", fill: "#ffffff" }
);
this.helloWorld.setOrigin(0.5);
this.input.on('pointerup', function (pointer) {
this.scene.start('play');
}, this);
}
update() {
}
}
export default Start;
Play
import Phaser from "phaser";
// import Pack from '~/assets/game/Components/Pack.js'
class Play extends Phaser.Scene {
constructor() {
super({
key: 'play',
active: false
});
}
index = 0;
packs = Phaser.GameObjects.Group;
enemys = Phaser.GameObjects.Group;
spriteLife = [];
config = {
speed: 0,
speedVelocity: 0.3,
maxSpeed: 15,
pack: {
created: 0,
limitcreate: 1,
value: 4.5,
},
enemy: {
speed: 5,
created: 0,
limitcreate: 1,
x: 0,
},
engine: {
temperature: 0,
}
};
init() {
}
preload() {
// Images
this.load.image('grama', require('~/assets/game/assets/grama.jpg'));
this.load.image('road', require('~/assets/game/assets/road-2.png'))
this.load.image('pack', require('~/assets/game/assets/pack.png'));
this.load.image('life', require('~/assets/game/assets/life.png'));
// Sprite sheet
this.load.spritesheet('player', require('~/assets/game/assets/car-sprite.png'), {
frameWidth: 404,
frameHeight: 1009
})
}
createGram() {
let a = 0;
let b = 0;
const totalA = 8;
const totalB = 4;
for (a = 0; a <= totalA; a++) {
for (b = 0; b <= totalB; b++) {
const grama = this.add.sprite(0, 0, 'grama').setScale(0.2);
grama.setPosition(grama.displayWidth * b, grama.displayHeight * a);
}
}
}
createScore() {
this.score = 0;
this.scoreText = this.add.text(15, 45,
`SCORE: 0`, {
font: "20px Arial",
fill: "#ffffff"
}
);
}
createLife() {
this.numberLifes = 3;
this.add.text(15, 15,
`LIFE:`, {
font: "20px Arial",
fill: "#ffffff"
}
);
for (let i = 0; i < this.numberLifes; i++) {
this.spriteLife.push(this.add.image(80 + 30 * i, 25, 'life').setScale(0.09))
}
}
createPlayer() {
this.player = this.physics.add.sprite(0, 0, 'player');
this.player.setOrigin(0.5)
this.player.setScale(0.2);
this.player.setPosition(this.cameras.main.centerX, this.cameras.main.displayHeight - 120);
this.player.setCollideWorldBounds(true);
}
createPack() {
if(this.config.pack.created < this.config.pack.limitcreate){
this.packs.create(Phaser.Math.Between(60, 330), 0, 'pack').setScale(.15).setOrigin(.5);
this.config.pack.created += 1;
}
}
createEnemy() {
if(this.config.enemy.created < this.config.enemy.limitcreate){
this.enemy = this.enemys.create(Phaser.Math.Between(70, 320), -200, 'player').setScale(.2).setOrigin(.5);
this.enemy.anims.play('default', true);
this.config.enemy.created += 1;
this.config.enemy.x = this.enemys.children.entries[0].x;
}
}
createEngineTemperatureBar() {
this.engineTemperatureBar = this.add.graphics();
this.engineTemperatureBar.fillStyle(0xffffff, 1);
this.engineTemperatureBar.fillRect(0, 0, 130, 30);
// Position
this.engineTemperatureBar.x = 350;
this.engineTemperatureBar.y = 830;
this.engineTemperatureBar.angle = -90;
this.engineTemperatureBar.scaleX = 0;
}
myAnims() {
this.anims.create({
key: 'left',
frames: this.anims.generateFrameNumbers('player', {
start: 4,
end: 4
}),
frameRate: 3,
repeat: 0
})
this.anims.create({
key: 'right',
frames: this.anims.generateFrameNumbers('player', {
start: 0,
end: 1
}),
frameRate: 3,
repeat: 0
})
this.anims.create({
key: 'default',
frames: [{
key: 'player',
frame: 2
}],
frameRate: 20
});
}
addPhysics(){
// Add
this.packs = this.physics.add.group();
this.enemys = this.physics.add.group();
// Collider
this.physics.add.collider(this.player, this.packs, this.collectPack, null, this);
this.physics.add.collider(this.player, this.enemys, this.collideEnemy, null, this, this.enemys);
}
create() {
// Camera
this.cameras.main.setBackgroundColor("#24252A");
// Gram
this.createGram();
// Roads
this.roads = this.add.tileSprite((this.game.canvas.clientWidth / 2), 0, 500, 1210, 'road').setOrigin(0.5, 0).setScale(0.7);
// Functions
this.createScore();
this.createLife();
this.createPlayer();
this.myAnims();
this.addPhysics();
this.createEngineTemperatureBar();
// Body
this.packs.enableBody = true;
this.enemys.enableBody = true;
// Cursors
this.cursors = this.input.keyboard.createCursorKeys();
}
update(time) {
this.roads.tilePositionY -= this.config.speed;
this.movePlayerManager();
this.createPack();
this.createEnemy();
this.movePacks();
this.moveEnemy()
this.updateEngineTemperatureBar()
}
movePlayerManager() {
// Left
if (this.cursors.left.isDown) {
this.player.x -= 5;
this.player.anims.play('left', false);
}
// Right
else if (this.cursors.right.isDown) {
this.player.x += 5;
this.player.anims.play('right', false);
}
else {
this.player.anims.play('default', true);
}
// Up
if (this.cursors.up.isDown) {
// Increase speed
if (this.config.speed <= this.config.maxSpeed){
this.config.speed += this.config.speedVelocity;
}
}
if (this.cursors.up.isUp) {
if (this.config.speed > 0){
this.config.speed -= 0.5;
}
else
this.config.speed = 0
}
}
movePacks() {
this.packs.children.each((e) => {
e.y += this.config.speed;
if(e.y > this.game.config.height){
e.destroy();
this.config.pack.created -= 1;
}
})
}
moveEnemy() {
this.enemys.children.each((e) => {
if(this.cursors.up.isDown){
if(this.config.speed < this.config.maxSpeed-1){
e.y -= this.config.maxSpeed;
return
};
e.y += this.config.enemy.speed;
// Move left or right
if(this.config.enemy.x < 150 && e.x < 290){
e.x += 1;
this.enemy.anims.play('right', true);
}
else if(e.x > 90){
e.x -= 1;
this.enemy.anims.play('left', true);
}else{
this.enemy.anims.play('default', true);
}
}else{
if(e.y < -1000) return;
e.y -= this.config.maxSpeed;
}
if(e.y - 200 > this.game.config.height){
e.destroy();
this.config.enemy.created -= 1;
}
})
}
updateEngineTemperatureBar(){
if(this.cursors.up.isDown && this.config.engine.temperature <= 100){
this.config.engine.temperature += 0.1;
}
else if(this.cursors.up.isUp && this.config.engine.temperature < 100){
this.config.engine.temperature += 0.2;
}else{
this.scene.start('end', { motiveText: "O motor do seu carro explodiu." });
}
// change the size of the bar
this.engineTemperatureBar.scaleX = this.config.engine.temperature/100;
}
updateLifes(){
if(this.numberLifes <= 1){
this.scene.start('end', { motiveText: "Suas vidas acabaram." });
}else{
this.spriteLife[this.numberLifes-1].destroy();
this.numberLifes = this.numberLifes -1;
}
}
collectPack(player, pack){
// Destroy pack
pack.disableBody(true, true);
pack.destroy();
// Config
this.config.pack.created -= 1;
// Score
this.score++;
this.scoreText.setText("SCORE: " + this.score);
// Engien temperature bar
if(this.config.engine.temperature > 1)
this.config.engine.temperature -= this.config.pack.value;
}
collideEnemy(player, enemy){
// Life
this.updateLifes();
// Destroy
enemy.destroy();
// Config
this.config.speed = 1;
this.config.enemy.created -= 1;
}
}
export default Play;
End
import Phaser from "phaser";
class End extends Phaser.Scene{
constructor ()
{
super({ key: 'end', active: false });
}
init(data){
this.motiveText = data.motiveText;
}
preload(){
}
create() {
this.helloWorld = this.add.text(
this.cameras.main.centerX,
this.cameras.main.centerY,
"Perdeu!",
{ font: "40px Arial", fill: "#ffffff" }
);
this.helloWorld.setOrigin(0.5);
this.helloWorld = this.add.text(
this.cameras.main.centerX,
this.cameras.main.centerY + 40,
this.motiveText,
{ font: "20px Arial", fill: "#ffffff" }
);
this.helloWorld.setOrigin(0.5);
this.helloWorld = this.add.text(
this.cameras.main.centerX,
this.cameras.main.centerY + 70,
"Clique para jogar novamente.",
{ font: "20px Arial", fill: "#ffffff" }
);
this.helloWorld.setOrigin(0.5);
this.input.on('pointerup', function (pointer) {
this.scene.start("start");
}, this);
}
update() {
}
}
export default End;
Again, the problem is that he doesn't completely rest. Even using the 'restart' methods, destroying the events and restarting them.
The issue is, that your are setting properties on the Scenes classes, like the property config on the Play class. Since these properties are not set in the create or the init function, they will never be reset, on start/
restart.
When the Scenes are restarted, scenes are not recreated, so properties are not reset and the constructor is also not called.
If you need to define / set properties on the Scenes, the best solution would be to always (re)set them in the init or create function, since these functions are called on start or restart.
I would recomend, simply moving the properties from the Play class into the init method like this:
class Play extends Phaser.Scene {
...
init() {
this.index = 0;
this.packs = Phaser.GameObjects.Group;
this.enemys = Phaser.GameObjects.Group;
this.spriteLife = [];
this.config = {
speed: 0,
speedVelocity: 0.3,
maxSpeed: 15,
pack: {
...
},
enemy: {
...
},
engine: {
temperature: 0,
}
};
}
...
}
Related
I built a TensorflowJS model that learns how to play a snake game that I made. I've followed everything I found online, but I can get my model to improve even after hundreds of games.
Can anyone show me what I'm doing wrong? Here's my code:
async function main(currentTime) {
window.requestAnimationFrame(main)
aIModelChoice(snakeBody, applePosition)
updateGameState()
draw(gameBoard)
checkIfLost()
if (gameOver) {
await updateAIModel(snakeBody, applePosition, -1, gameOver);
console.log("UPDATING MODEL WITH REWARD -1")
} else if (appleEaten){
await updateAIModel(snakeBody, applePosition, +1, gameOver);
console.log("UPDATING MODEL WITH REWARD +1")
} else {
await updateAIModel(snakeBody, applePosition, -0.1, gameOver);
console.log("UPDATING MODEL WITH REWARD 0")
}
appleEaten = false
if (gameOver) {
reset()
}
}
export function initiateAIModel() {
window.model = tf.sequential();
window.model.add(tf.layers.dense({ units: 64, inputShape: [100], activation: "relu" }));
window.model.add(tf.layers.dense({ units: 32, activation: "relu" }));
window.model.add(tf.layers.dense({ units: 4, activation: "softmax" }));
window.model.compile({
optimizer: tf.train.adam(),
loss: "categoricalCrossentropy",
metrics: ["accuracy"]
});
window.gamma = 0.9;
window.epsilon = 1.0;
let done = false;
console.log("Model initiated");
}
function getState(snakeBody, applePosition) {
const state = [];
let xValues = [];
let yValues = [];
for (let i = 0; i < snakeBody.length; i++) {
xValues.push(snakeBody[i].x);
yValues.push(snakeBody[i].y);
}
for (let i = 0; i < window.gridsize; i++) {
for (let j = 0; j < window.gridsize; j++) {
if (xValues.includes(j) && yValues.includes(i)) {
state.push(1);
} else if (applePosition.x === j && applePosition.y === i) {
state.push(2);
} else {
state.push(0);
}
}
}
return state;
}
export function aIModelChoice(snakeBody, applePosition) {
window.state = getState(snakeBody, applePosition);
let selectedAction
if (Math.random() < window.epsilon) {
// Choose a random action
selectedAction = ["UP", "DOWN", "LEFT", "RIGHT"][Math.floor(Math.random() * 4)];
} else {
// Choose the action with the highest predicted reward
const actionProbs = window.model.predict(tf.tensor2d([window.state])).dataSync();
selectedAction = ["UP", "DOWN", "LEFT", "RIGHT"][tf.argMax(actionProbs).dataSync()[0]];
}
if (selectedAction == "UP" && window.inputDirection.x !== 0) {
window.inputDirection = { x: 0, y: 1 }
} else if (selectedAction == "DOWN" && window.inputDirection.x !== 0) {
window.inputDirection = { x: 0, y: -1 }
} else if (selectedAction == "LEFT" && window.inputDirection.y !== 0) {
window.inputDirection = { x: -1, y: 0 }
} else if (selectedAction == "RIGHT" && window.inputDirection.y !== 0) {
window.inputDirection = { x: 1, y: 0 }
}
}
export async function updateAIModel(snakeBody, applePosition, reward, done) {
const nextState = getState(snakeBody, applePosition);
let target = reward;
if (!done) {
// Predict the future discounted reward
const futureReward = tf.max(window.model.predict(tf.tensor2d([window.state]))).dataSync()[0];
target = reward + window.gamma * futureReward;
}
// Update the model
const targetVec = window.model.predict(tf.tensor2d([window.state])).dataSync();
targetVec[["UP", "DOWN", "LEFT", "RIGHT"].indexOf(window.inputDirection)] = target;
await window.model.fit(tf.tensor2d([window.state]), tf.tensor2d([targetVec]));
if (!done) {
window.state = nextState;
}
window.epsilon *= 0.995;
}
You aren't updating which grid space it can go to, you're updating which direction it turns. This makes it spin in circles instead of checking different places for the apple.
I tried to make a canva which can scroll on mobile and desktop.
I have succeeded to scroll on desktop and even with touchpad, but i can't to find a way to scroll on my mobile.
I have already manage the scroll with mouse wheel but i haven't succeeded with touchscreen.
Do you have an idea to do work this below:
this.container = new PIXI.Container();
this.app.stage.addChild(this.container);
this.images = [t1, t2, t3, t4];
this.WHOLEWIDTH = this.images.length*(this.width + this.margin)
loadImages(this.images, (images)=>{
this.loadImages = images;
this.add()
this.render()
this.scrollEvent();
})
}
scrollEvent() {
document.addEventListener(
'mousewheel',
(e) => {
this.scrollTarget = e.wheelDelta / 3;
},
{ passive: true },
);
}
add() {
let parent = {
w: this.width,
h: this.height,
};
this.thumbs = [];
this.loadImages.forEach((img, i) => {
var nameEnum = {
0: { name: 'Ecocampus3', value: '/Ecocampus3' },
1: { name: 'Hoppin World', value: '/HoppinWorld' },
2: { name: 'Distrame', value: '/Distrame' },
3: { name: 'Club les Piranhas du Nord', value: '/CAPN' },
};
let texture = PIXI.Texture.from(img.img);
texture.title = nameEnum[i];
const sprite = new PIXI.Sprite(texture);
let container = new PIXI.Container();
let spriteContainer = new PIXI.Container();
let mask = new PIXI.Sprite(PIXI.Texture.WHITE);
mask.width = this.width;
mask.height = this.height;
sprite.mask = mask;
sprite.anchor.set(0.5);
sprite.position.set(sprite.texture.orig.width / 2, sprite.texture.orig.height / 2);
let image = {
w: sprite.texture.orig.width,
h: sprite.texture.orig.height,
};
let cover = fit(image, parent);
spriteContainer.position.set(cover.left, cover.top);
spriteContainer.scale.set(cover.scale, cover.scale);
container.x = (this.margin + this.width) * i;
container.y = this.height / 10;
spriteContainer.addChild(sprite);
container.interactive = true;
container.on('mouseover', this.mouseOn, { passive: true });
container.on('mouseout', this.mouseOut, { passive: true });
container.on('click', this.redirect, { passive: true });
container.on('touchstart', this.redirect, { passive: true });
container.on('touchmove', this.redirect, { passive: true });
container.addChild(spriteContainer);
container.addChild(mask);
this.container.addChild(container);
this.thumbs.push(container);
});
}
redirect(e) {
let e1 = e.currentTarget.children[0].children[0];
let e2 = e1.texture.title.value;
window.location.replace(e2);
}
mouseOut(e) {
let e1 = e.currentTarget.children[0].children[0];
gsap.to(e1.scale, {
duration: 1,
x: 1,
y: 1,
});
e1.children[0].text = '';
}
mouseOn(e) {
let e1 = e.target.children[0].children[0];
const skewStyle = new PIXI.TextStyle({
fontFamily: 'Raleway',
fontSize: '60px',
dropShadowColor: '#fff',
fill: '#fff',
fontWeight: 'bold',
});
let e2 = e1.texture.title.name;
const basicText1 = new PIXI.Text(e2, skewStyle);
basicText1.x = -400;
basicText1.y = 100;
gsap.to(e1.scale, {
duration: 1,
x: 1.1,
y: 1.1,
});
if (e1.children[0]) {
e1.children[0].text = e2;
} else {
e1.addChild(basicText1);
}
}
calcPos(scr, pos) {
let temp = ((scr + pos + this.WHOLEWIDTH + this.width + this.margin) % this.WHOLEWIDTH) - this.width - this.margin;
return temp;
}
render() {
this.app.ticker.add(() => {
this.app.renderer.render(this.container);
this.scroll -= (this.scroll - this.scrollTarget) * 0.1;
this.scrollTarget *= 0.9;
this.thumbs.forEach((th) => {
th.position.x = this.calcPos(this.scroll, th.position.x);
});
});
}
Thank you in advance :D
I have a 2D array, which contains either true or false.
I made a function that returns the number of neighbors (in all 8 directions) in that array, which are true.
But for some unknown reason, it does not work corectly (returns wrong number of neighbors).
(Yes, I'm making Conway's Game of Life.)
function neighbors(seq, x, y) {
var cnt = 0;
try {
if (seq[y-1][x]){
cnt++;
}
}
catch(err) {
}
try {
if (seq[y][x-1]){
cnt++;
}
}
catch(err) {
}
try {
if (seq[y][x+1]){
cnt++;
}
}
catch(err) {
}
try {
if (seq[y+1][x]){
cnt++;
}
}
catch(err) {
}
try {
if (seq[y-1][x+1]){
cnt++;
}
}
catch(err) {
}
try {
if (seq[y-1][x-1]){
cnt++;
}
}
catch(err) {
}
try {
if (seq[y+1][x-1]){
cnt++;
}
}
catch(err) {
}
try {
if (seq[y+1][x+1]){
cnt++;
}
}
catch(err) {
}
return cnt;
}
This code was basically translated from my Python code, that works.
You could take an object with all needed directions for counting.
Then check if the index is a key of the array and check the nested level. Add all and return that value.
function neighbors(seq, i, j) {
var directions = [{ x: 0, y: -1 }, { x: -1, y: -1 }, { x: -1, y: 0 }, { x: -1, y: 1 }, { x: 0, y: 1 }, { x: 1, y: 1 }, { x: 1, y: 0 }, { x: 1, y: -1 }];
return directions.reduce((r, { x, y }) => {
x += i;
y += j;
return r + (x in seq && seq[x][y] || 0);
}, 0);
}
You can simplify function like below.
var seq = [[false,false,false,false],
[false,true,false,false],
[false,true,false,false],
[false,true,false,false]]
function neighbors(seq, x, y) {
var cnt = 0;
var controls = [{x:0,y:1},{x:0,y:-1},
{x:1,y:0},{x:1,y:1},{x:1,y:-1},
{x:-1,y:0},{x:-1,y:1},{x:-1,y:-1}]
for (var index in controls) {
var newX = x+controls[index].x;
var newY = y+controls[index].y;
if(newX >= 0 && newY >= 0 && newX < seq.length && newY < seq[newX].length
&& seq[newX][newY]) {
cnt++;
}
}
return cnt;
}
console.log(neighbors(seq, 0,1))
console.log(neighbors(seq, 1,0))
console.log(neighbors(seq, 2,2))
I'm integrating FabricJS into my angular application. I've managed to get the canvas down and automatically adding a figure into the canvas.
My goals are to make the figure 8inches by 8inches and to add a grid that has dynamic grid height and width (lets say 1/16th inch x 1/16inch).
Here are my problems:
1. When adding the figure, I cannot set the figure height and width in inches. I can only use a number. How can I use a different unit for this? I used cssOnly: true for the canvas but I cant seem to figure it out for the figure.
addFigure(figure) {
let add: any;
switch (figure) {
case 'rectangle':
add = new fabric.Rect({
width: 500, height: 500, left: 150, top: 10, angle: 0, opacity: .3,
fill: '#3f51b5'
});
break;
case 'square':
add = new fabric.Rect({
width: 200, height: 200, left: 0, top: 0, angle: 0, opacity: .15,
fill: '#4caf50'
});
break;
case 'triangle':
add = new fabric.Triangle({
width: 100, height: 100, left: 10, top: 10, fill: '#2196f3'
});
break;
case 'circle':
add = new fabric.Circle({
radius: 50, left: 10, top: 10, fill: '#ff5722'
});
break;
}
this.extend(add, this.randomId());
console.log(add);
this.canvas.add(add);
// this.selectItemAfterAdded(add);
}
The figure height displays 200 but it appears incorrect. Here is an example of the above figure as a square. Notice the 200 width is correct but the height does not seem right seeing as the canvas is 6inches by 6inches. The height extends pass the bottom of the canvas.
Notice the grid above. Im having the same problem. Here is my code for grid which I pulled from Adding grid over Fabric.js canvas . The problem I'm having is I have no idea how to make this grid evenly 1/16th inch 1/16th inch given this is an 8in by 8in canvas.
onst gridGroup = new fabric.Group([], {left: 0, top: 0});
const gridSize = 16;
const gridWidth = 400;
const gridHeight = 400;
const lineOption = {stroke: 'rgba(0,0,0,.4)', strokeWidth: 1, selectable: false, strokeDashArray: [3, 3]};
for (let i = Math.ceil(gridWidth / gridSize); i--;) {
this.canvas.add( new fabric.Line([gridSize * i, 0, gridSize * i, gridHeight], lineOption) );
}
for (let i = Math.ceil(gridHeight / gridSize); i--;) {
this.canvas.add( new fabric.Line([0, gridSize * i, gridWidth, gridSize * i], lineOption) );
}
this.canvas.add(gridGroup);
Here is the entire component for review. Is there something I'm doing wrong in the setup?
// private canvas: any;
uploading = false;
uploaded = false;
pdfUrl = '';
canvas: any;
props: any = {
canvasFill: '#ffffff',
canvasImage: '',
id: null,
opacity: null,
fill: null,
fontSize: null,
lineHeight: null,
charSpacing: null,
fontWeight: null,
fontStyle: null,
textAlign: null,
fontFamily: null,
TextDecoration: ''
};
textString: string;
url = '';
size: any = {
width: '6in',
height: '6in'
};
json: any;
globalEditor = false;
textEditor = false;
imageEditor = false;
figureEditor = false;
selected: any;
constructor(
private router: Router,
private userService: UserService,
private fileService: FileService
) { }
ngOnInit() {
this.getActive();
// this.activateCanvas();
//setup front side canvas
this.canvas = new fabric.Canvas('canvas', {
hoverCursor: 'pointer',
selection: true,
selectionBorderColor: 'blue'
});
this.canvas.on({
'object:moving': (e) => { },
'object:modified': (e) => { },
'object:selected': (e) => {
const selectedObject = e.target;
this.selected = selectedObject
selectedObject.hasRotatingPoint = true;
selectedObject.transparentCorners = false;
// selectedObject.cornerColor = 'rgba(255, 87, 34, 0.7)';
this.resetPanels();
if (selectedObject.type !== 'group' && selectedObject) {
this.getId();
this.getOpacity();
switch (selectedObject.type) {
case 'rect':
case 'circle':
case 'triangle':
this.figureEditor = true;
this.getFill();
break;
case 'i-text':
this.textEditor = true;
this.getLineHeight();
this.getCharSpacing();
this.getBold();
this.getFontStyle();
this.getFill();
this.getTextDecoration();
this.getTextAlign();
this.getFontFamily();
break;
case 'image':
break;
}
}
},
'selection:cleared': (e) => {
this.selected = null;
this.resetPanels();
}
});
this.canvas.setWidth(this.size.width, {cssOnly: true});
this.canvas.setHeight(this.size.height, {cssOnly: true});
this.addFigure('square');
const gridGroup = new fabric.Group([], {left: 0, top: 0});
const gridSize = 16;
const gridWidth = 400;
const gridHeight = 400;
const lineOption = {stroke: 'rgba(0,0,0,.4)', strokeWidth: 1, selectable: false, strokeDashArray: [3, 3]};
for (let i = Math.ceil(gridWidth / gridSize); i--;) {
this.canvas.add( new fabric.Line([gridSize * i, 0, gridSize * i, gridHeight], lineOption) );
}
for (let i = Math.ceil(gridHeight / gridSize); i--;) {
this.canvas.add( new fabric.Line([0, gridSize * i, gridWidth, gridSize * i], lineOption) );
}
this.canvas.add(gridGroup);
// for ( let x = 1; x < ( this.canvas.width / gridsize); x++) {
// this.canvas.add(new fabric.Line([cellWidth * x, 0, cellWidth * x, 800],
// { stroke: '#000000', strokeWidth: 1, selectable: false}));
// this.canvas.add(new fabric.Line([0, cellWidth * x, 800, cellWidth * x],
// { stroke: '#000000', strokeWidth: 1, selectable: false}));
// }
// get references to the html canvas element & its context
// this.canvas.on('mouse:down', (e) => {
// let canvasElement: any = document.getElementById('canvas');
// console.log(canvasElement)
// });
}
/*------------------------Block elements------------------------*/
//Block "Size"
changeSize(event: any) {
this.canvas.setWidth(this.size.width);
this.canvas.setHeight(this.size.height);
}
//Block "Add text"
addText() {
let textString = this.textString;
let text = new fabric.IText(textString, {
left: 10,
top: 10,
fontFamily: 'helvetica',
angle: 0,
fill: '#000000',
scaleX: 0.5,
scaleY: 0.5,
fontWeight: '',
hasRotatingPoint: true
});
this.extend(text, this.randomId());
this.canvas.add(text);
this.selectItemAfterAdded(text);
this.textString = '';
}
//Block "Add images"
getImgPolaroid(event: any) {
let el = event.target;
fabric.Image.fromURL(el.src, (image) => {
image.set({
left: 10,
top: 10,
angle: 0,
padding: 10,
cornersize: 10,
hasRotatingPoint: true,
peloas: 12
});
// image.setWidth(150);
// image.setHeight(150);
this.extend(image, this.randomId());
this.canvas.add(image);
this.selectItemAfterAdded(image);
});
}
//Block "Upload Image"
addImageOnCanvas(url) {
if (url) {
fabric.Image.fromURL(url, (image) => {
image.set({
left: 10,
top: 10,
angle: 0,
// scaleX: 200,
// scaleY: 200,
padding: 10,
cornersize: 10,
hasRotatingPoint: true
});
image.scale(0.2);
// image.setWidth(200);
// image.height = 200;
// image.width = 200;
this.extend(image, this.randomId());
this.canvas.add(image);
this.selectItemAfterAdded(image);
});
}
}
readUrl(event) {
if (event.target.files && event.target.files[0]) {
const reader = new FileReader();
reader.onload = (e) => {
this.url = e.target['result'];
};
reader.readAsDataURL(event.target.files[0]);
}
}
removeWhite(url) {
this.url = '';
}
//Block "Add figure"
addFigure(figure) {
let add: any;
switch (figure) {
case 'rectangle':
add = new fabric.Rect({
width: 500, height: 500, left: 150, top: 10, angle: 0, opacity: .3,
fill: '#3f51b5'
});
break;
case 'square':
add = new fabric.Rect({
width: 200, height: 200, left: 0, top: 0, angle: 0, opacity: .4,
fill: '#4caf50'
});
break;
case 'triangle':
add = new fabric.Triangle({
width: 100, height: 100, left: 10, top: 10, fill: '#2196f3'
});
break;
case 'circle':
add = new fabric.Circle({
radius: 50, left: 10, top: 10, fill: '#ff5722'
});
break;
}
this.extend(add, this.randomId());
console.log(add);
this.canvas.add(add);
// this.selectItemAfterAdded(add);
}
/*Canvas*/
cleanSelect() {
this.canvas.discardActiveObject();
}
selectItemAfterAdded(obj) {
this.canvas.discardActiveObject();
this.canvas.setActiveObject(obj);
}
setCanvasFill() {
if (!this.props.canvasImage) {
this.canvas.backgroundColor = this.props.canvasFill;
this.canvas.renderAll();
}
}
extend(obj, id) {
obj.toObject = (function (toObject) {
return function () {
return fabric.util.object.extend(toObject.call(this), {
id: id
});
};
})(obj.toObject);
}
setCanvasImage() {
let self = this;
if (this.props.canvasImage) {
this.canvas.setBackgroundColor({ source: this.props.canvasImage, repeat: 'repeat' }, function () {
// self.props.canvasFill = '';
self.canvas.renderAll();
});
}
}
randomId() {
return Math.floor(Math.random() * 999999) + 1;
}
/*------------------------Global actions for element------------------------*/
getActiveStyle(styleName, object) {
object = object || this.canvas.getActiveObject();
if (!object) {return '';}
return (object.getSelectionStyles && object.isEditing)
? (object.getSelectionStyles()[styleName] || '')
: (object[styleName] || '');
}
setActiveStyle(styleName, value, object) {
object = object || this.canvas.getActiveObject();
if (!object) {return '';}
if (object.setSelectionStyles && object.isEditing) {
let style = {};
style[styleName] = value;
object.setSelectionStyles(style);
object.setCoords();
} else {
object.set(styleName, value);
}
object.setCoords();
this.canvas.renderAll();
}
getActiveProp(name) {
let object = this.canvas.getActiveObject();
if (!object) {return '';}
return object[name] || '';
}
setActiveProp(name, value) {
const object = this.canvas.getActiveObject();
if (!object) {return ''};
object.set(name, value).setCoords();
this.canvas.renderAll();
}
clone() {
const activeObject = this.canvas.getActiveObject(),
activeGroup = this.canvas.getActiveGroup();
if (activeObject) {
let clone;
switch (activeObject.type) {
case 'rect':
clone = new fabric.Rect(activeObject.toObject());
break;
case 'circle':
clone = new fabric.Circle(activeObject.toObject());
break;
case 'triangle':
clone = new fabric.Triangle(activeObject.toObject());
break;
case 'i-text':
clone = new fabric.IText('', activeObject.toObject());
break;
case 'image':
clone = fabric.util.object.clone(activeObject);
break;
}
if (clone) {
clone.set({ left: 10, top: 10 });
this.canvas.add(clone);
this.selectItemAfterAdded(clone);
}
}
}
getId() {
this.props.id = this.canvas.getActiveObject().toObject().id;
}
setId() {
const val = this.props.id;
const complete = this.canvas.getActiveObject().toObject();
console.log(complete);
this.canvas.getActiveObject().toObject = () => {
complete.id = val;
return complete;
};
}
getOpacity() {
this.props.opacity = this.getActiveStyle('opacity', null) * 100;
}
setOpacity() {
this.setActiveStyle('opacity', +this.props.opacity / 100, null);
}
getFill() {
this.props.fill = this.getActiveStyle('fill', null);
}
setFill() {
this.setActiveStyle('fill', this.props.fill, null);
}
getLineHeight() {
this.props.lineHeight = this.getActiveStyle('lineHeight', null);
}
setLineHeight() {
this.setActiveStyle('lineHeight', parseFloat(this.props.lineHeight), null);
}
getCharSpacing() {
this.props.charSpacing = this.getActiveStyle('charSpacing', null);
}
setCharSpacing() {
this.setActiveStyle('charSpacing', this.props.charSpacing, null);
}
getFontSize() {
this.props.fontSize = this.getActiveStyle('fontSize', null);
}
setFontSize() {
this.setActiveStyle('fontSize', +this.props.fontSize, null);
}
getBold() {
this.props.fontWeight = this.getActiveStyle('fontWeight', null);
}
setBold() {
this.props.fontWeight = !this.props.fontWeight;
this.setActiveStyle('fontWeight', this.props.fontWeight ? 'bold' : '', null);
}
getFontStyle() {
this.props.fontStyle = this.getActiveStyle('fontStyle', null);
}
setFontStyle() {
this.props.fontStyle = !this.props.fontStyle;
this.setActiveStyle('fontStyle', this.props.fontStyle ? 'italic' : '', null);
}
getTextDecoration() {
this.props.TextDecoration = this.getActiveStyle('textDecoration', null);
}
setTextDecoration(value) {
let iclass = this.props.TextDecoration;
if (iclass.includes(value)) {
iclass = iclass.replace(RegExp(value, 'g'), '');
} else {
iclass += ` ${value}`
}
this.props.TextDecoration = iclass;
this.setActiveStyle('textDecoration', this.props.TextDecoration, null);
}
hasTextDecoration(value) {
return this.props.TextDecoration.includes(value);
}
getTextAlign() {
this.props.textAlign = this.getActiveProp('textAlign');
}
setTextAlign(value) {
this.props.textAlign = value;
this.setActiveProp('textAlign', this.props.textAlign);
}
getFontFamily() {
this.props.fontFamily = this.getActiveProp('fontFamily');
}
setFontFamily() {
this.setActiveProp('fontFamily', this.props.fontFamily);
}
/*System*/
removeSelected() {
let activeObject = this.canvas.getActiveObject(),
activeGroup = this.canvas.getActiveGroup();
if (activeObject) {
this.canvas.remove(activeObject);
// this.textString = '';
} else if (activeGroup) {
let objectsInGroup = activeGroup.getObjects();
this.canvas.discardActiveGroup();
let self = this;
objectsInGroup.forEach(function (object) {
self.canvas.remove(object);
});
}
}
bringToFront() {
let activeObject = this.canvas.getActiveObject(),
activeGroup = this.canvas.getActiveGroup();
if (activeObject) {
activeObject.bringToFront();
// activeObject.opacity = 1;
}
else if (activeGroup) {
let objectsInGroup = activeGroup.getObjects();
this.canvas.discardActiveGroup();
objectsInGroup.forEach((object) => {
object.bringToFront();
});
}
}
sendToBack() {
const activeObject = this.canvas.getActiveObject(),
activeGroup = this.canvas.getActiveGroup();
if (activeObject) {
activeObject.sendToBack();
// activeObject.opacity = 1;
} else if (activeGroup) {
const objectsInGroup = activeGroup.getObjects();
this.canvas.discardActiveGroup();
objectsInGroup.forEach((object) => {
object.sendToBack();
});
}
}
confirmClear() {
if (confirm('Are you sure?')) {
this.canvas.clear();
}
}
rasterize() {
if (!fabric.Canvas.supports('toDataURL')) {
alert('This browser doesn\'t provide means to serialize canvas to an image');
} else {
console.log(this.canvas.toDataURL('png'))
//window.open(this.canvas.toDataURL('png'));
let image = new Image();
image.src = this.canvas.toDataURL('png')
let w = window.open('');
w.document.write(image.outerHTML);
}
}
rasterizeSVG() {
console.log(this.canvas.toSVG())
// window.open(
// 'data:image/svg+xml;utf8,' +
// encodeURIComponent(this.canvas.toSVG()));
// console.log(this.canvas.toSVG())
// let image = new Image();
// image.src = this.canvas.toSVG()
const w = window.open('');
w.document.write(this.canvas.toSVG());
}
saveCanvasToJSON() {
const json = JSON.stringify(this.canvas);
localStorage.setItem('Kanvas', json);
console.log('json');
console.log(json);
}
loadCanvasFromJSON() {
const CANVAS = localStorage.getItem('Kanvas');
console.log('CANVAS');
console.log(CANVAS);
// and load everything from the same json
this.canvas.loadFromJSON(CANVAS, () => {
console.log('CANVAS untar');
console.log(CANVAS);
// making sure to render canvas at the end
this.canvas.renderAll();
// and checking if object's "name" is preserved
console.log('this.canvas.item(0).name');
console.log(this.canvas);
});
}
rasterizeJSON() {
this.json = JSON.stringify(this.canvas, null, 2);
}
resetPanels() {
this.textEditor = false;
this.imageEditor = false;
this.figureEditor = false;
}
In summary, I'm looking to use a 1/16th inch x 1/16th inch (possibly dynamic) grid on an 8inch by 8inch canvas and add a figure that is also an even square inside this canvas.
For anyone having this same problem. It seems that the cssOnly boolean param option Im passing is not a solution for actually scaling the canvas to inches.
Here's how I resolved.
1) I now have an input that allows the user to manually enter in their PPI given the information on this url - http://dpi.lv/
2) I save that PPI
3) I created a method calcPixels(inches: number) which uses pixels = inches * ppi to calculate the number of pixels needed
4) I use that method across the application for all widths, heights, grid lines and etc.
This gives me a to scale 7in by 7inch grid line. Its a little less automated but if you save the PPI, the user only has to enter it once!
New result
I created a little game with Phaser for presentation purposes. After you've won or lost you can restart the game. This is done with states. When I try to fire a bullet after the game has been restarted, a null reference error occurs and the game freezes. It seems the null reference occurs because the this.game property is not set correctly in the Weapon classes after the state is restarted.
var PhaserGame = function () {
this.background = null;
this.stars = null;
this.player = null;
this.enemies = null;
this.cursors = null;
this.speed = 300;
this.weapons = [];
this.currentWeapon = 0;
this.weaponName = null;
this.score = 0;
};
PhaserGame.prototype = {
init: function () {
this.game.renderer.renderSession.roundPixels = true;
this.physics.startSystem(Phaser.Physics.ARCADE);
},
preload: function () {
this.game.time.advancedTiming = true;
},
create: function () {
this.background = this.add.tileSprite(0, 0, this.game.width, this.game.height, 'background');
this.background.autoScroll(-40, 0);
this.stars = this.add.tileSprite(0, 0, this.game.width, this.game.height, 'stars');
this.stars.autoScroll(-60, 0);
this.weapons.push(new Weapon.SingleBullet(this.game));
//this.weapons.push(new Weapon.FrontAndBack(this.game));
this.weapons.push(new Weapon.ThreeWay(this.game));
//this.weapons.push(new Weapon.EightWay(this.game));
this.weapons.push(new Weapon.ScatterShot(this.game));
this.weapons.push(new Weapon.Beam(this.game));
this.weapons.push(new Weapon.SplitShot(this.game));
//this.weapons.push(new Weapon.Pattern(this.game));
this.weapons.push(new Weapon.Rockets(this.game));
this.weapons.push(new Weapon.ScaleBullet(this.game));
//this.weapons.push(new Weapon.Combo1(this.game));
//this.weapons.push(new Weapon.Combo2(this.game));
this.currentWeapon = 0;
for (var i = 1; i < this.weapons.length; i++)
{
this.weapons[i].visible = false;
}
this.player = this.add.existing(new Spaceship(this.game, 100, 200, 'player'));
this.player.events.onKilled.add(this.toGameOver, this);
this.physics.arcade.enable(this.player);
this.player.body.collideWorldBounds = true;
this.player.animations.add('flame', [0, 1, 2, 3], 10, true);
this.player.animations.play('flame');
//Enemies
this.enemies = this.add.group();
//Enable Physics for Enemies
//this.physics.arcade.enable(this.enemies);
this.enemies.enableBody = true;
for (var i = 0; i < 24; i++) {
//create a star inside the group
var enemy = this.enemies.add(new Enemy(this.game, 1000 + (i * 50), 10 + Math.random() * 300, 'enemy'));
enemy.events.onKilled.add(this.raiseCounter, this);
}
//this.weaponName = this.add.bitmapText(8, 364, 'shmupfont', "ENTER = Next Weapon", 24);
// Cursor keys to fly + space to fire
this.cursors = this.input.keyboard.createCursorKeys();
this.input.keyboard.addKeyCapture([ Phaser.Keyboard.SPACEBAR ]);
var changeKey = this.input.keyboard.addKey(Phaser.Keyboard.ENTER);
changeKey.onDown.add(this.nextWeapon, this);
},
nextWeapon: function () {
// Tidy-up the current weapon
this.weapons[this.currentWeapon].visible = false;
this.weapons[this.currentWeapon].callAll('reset', null, 0, 0);
this.weapons[this.currentWeapon].setAll('exists', false);
// Activate the new one
this.currentWeapon++;
if (this.currentWeapon === this.weapons.length)
{
this.currentWeapon = 0;
}
this.weapons[this.currentWeapon].visible = true;
//this.weaponName.text = this.weapons[this.currentWeapon].name;
},
enemyHit: function (bullet, enemy) {
bullet.kill();
enemy.dealDamage(2);
},
playerHit: function (player, enemy) {
player.dealDamage(10);
enemy.dealDamage(1);
},
raiseCounter: function () {
this.score++;
},
toGameOver: function () {
this.game.state.start('GameOver', true, false, this.score);
},
update: function () {
//Framerate
this.game.debug.text(this.time.fps || '--', 2, 14, "#00ff00");
this.game.debug.text('Health: ' + this.player.health || 'Health: ---', 2, 30, "#00ff00");
this.game.debug.text('Counter: ' + this.score || 'Counter: ---', 2, 44, "#00ff00");
this.game.physics.arcade.overlap(this.weapons[this.currentWeapon], this.enemies, this.enemyHit, null, this);
this.game.physics.arcade.overlap(this.player, this.enemies, this.playerHit, null, this);
this.player.body.velocity.set(0);
this.enemies.setAll('body.velocity.x', -50);
if (this.cursors.left.isDown)
{
this.player.body.velocity.x = -this.speed;
}
else if (this.cursors.right.isDown)
{
this.player.body.velocity.x = this.speed;
}
if (this.cursors.up.isDown)
{
this.player.body.velocity.y = -this.speed;
}
else if (this.cursors.down.isDown)
{
this.player.body.velocity.y = this.speed;
}
if (this.input.keyboard.isDown(Phaser.Keyboard.SPACEBAR))
{
this.weapons[this.currentWeapon].fire(this.player);
}
}
};
The weapon-classes were taken from Phaser-Coding-Tips 7:
Weapon.SingleBullet = function (game) {
console.log(game);
Phaser.Group.call(this, game, game.world, 'Single Bullet', false, true, Phaser.Physics.ARCADE);
this.nextFire = 0;
this.bulletSpeed = 600;
this.fireRate = 200;
for (var i = 0; i < 64; i++)
{
this.add(new Bullet(game, 'bullet5'), true);
}
return this;
};
Weapon.SingleBullet.prototype = Object.create(Phaser.Group.prototype);
Weapon.SingleBullet.prototype.constructor = Weapon.SingleBullet;
Weapon.SingleBullet.prototype.fire = function (source) {
//Here occurs the problem, because this.game is null after restarting the state
if (this.game.time.time < this.nextFire) { return; }
var x = source.x + 50;
var y = source.y + 15;
this.getFirstExists(false).fire(x, y, 0, this.bulletSpeed, 0, 0);
this.nextFire = this.game.time.time + this.fireRate;
};
The problem occurs consistent in all Weapon classes after restarting the state.
In the beginning of the create-method before weapons is filled, i forgot to empty the array. The funny thing is: I tried emptying the array before and it didn't work. When i logged the length of weapons it suddenly started to work as expected. Maybe the cause was a strange optimization made by the javascript-engine.