Related
I'm trying to create a snake game in Phaser 3. The arrow.(key).isDown is acting strangely and I can't figure out why. If you press a key weather it is up, down, left or right it will not change directions if you press the opposite, it just keeps moving. like if I press down it keeps going down even after I press up. I have been trying to debug it for hours. All the examples pretty much have it set up the same way.
This is my code so far
class mainScene {
preload() {
this.load.image("fruit", "images/fruit.png");
this.load.image("snake", "images/snake.png");
}
create() {
this.score = 0;
this.snake = this.physics.add.sprite(20, 20, "snake");
this.fruit = this.physics.add.sprite(
Phaser.Math.Between(10, 590),
Phaser.Math.Between(10, 590),
"fruit"
);
this.scoreText = this.add.text(500, 5, `Score: ${this.score}`);
this.arrow = this.input.keyboard.createCursorKeys();
//debug
}
update() {
if (this.physics.overlap(this.snake, this.fruit)) {
this.hit();
}
this.snake.setVelocity(0);
if (this.arrow.right.isDown) {
this.snake.setVelocityX(50);
if (this.snake.x > 600) {
this.outOfBounds();
}
} else if (this.arrow.left.isDown) {
this.snake.setVelocityX(-50);
if (this.snake.x < 0) {
this.outOfBounds();
}
}
if (this.arrow.down.isDown) {
this.snake.setVelocityY(50);
if (this.snake.y > 600) {
this.outOfBounds();
}
} else if (this.arrow.up.isDown) {
this.snake.setVelocityY(-50);
if (this.snake.y < 0) {
this.outOfBounds();
}
}
}
hit() {
this.fruit.x = Phaser.Math.Between(10, 590);
this.fruit.y = Phaser.Math.Between(10, 590);
this.score += 1;
this.scoreText.setText(`Score: ${this.score}`);
this.tweens.add({
targets: this.snake,
duration: 200,
scaleX: 1.2,
scaleY: 1.2,
yoyo: true,
});
}
outOfBounds() {
this.score = 0;
this.scoreText.setText(`Score: ${this.score}`);
this.gameOverText = this.add.text(
100,
250,
"You went out of bounds.\nGame Over\nPress Space to continue.",
{ fontSize: "24px" }
);
if (this.arrow.space.isDown) {
this.gameOverText.destroy();
this.scene.restart();
}
}
}
const config = {
type: Phaser.CANVAS,
width: 600,
height: 600,
backgroundColor: 0x000040,
scene: mainScene,
physics: {
default: "arcade",
debug: true,
setBounds: { x: 0, y: 0, width: 600, height: 600 },
},
parent: "game",
};
new Phaser.Game(config);
I'm using barba.js to fade new pages in. Code must be placed between the barba wrapper div. When I click a link to a new page though, the new page fades in at the same position as the previous page was in. So if the user clicks a link, the new page is loading in near , which I don't want.
How can I get the new page to load in at the top?
I found the answer on stack about scrollRestoration function but I still don't understand how to make this work.
Could somebody help me to compile the js code below, add some css or html to fix this issue?
$(window).scrollTop(0);
if ('scrollRestoration' in history) {
history.scrollRestoration = 'manual';
}
function delay(n) {
n = n || 2000;
return new Promise((done) => {
setTimeout(() => {
done();
}, n);
});
}
function pageTransition() {
var tl = gsap.timeline();
tl.to(".animate-this", {
duration: .2, opacity: 0, delay: 0, y: 30, //scale:0, delay: 0.2,
});
tl.to(".animate-this", {
duration: 0, opacity: 0, delay: 0, scale:0, //scale:0, delay: 0.2,
});
tl.fromTo(".loading-screen", {width: 0, left: 0}, {
duration: 1,
width: "100%",
left: "0%",
ease: "expo.inOut",
delay: 0.3,
});
tl.to("#svg-1", {
duration: 1, opacity: 0, y: 30, stagger: 0.4, delay: 0.2,
});
tl.to(".loading-screen", {
duration: 1,
width: "100%",
left: "100%",
ease: "expo.inOut",
delay: 0, //CHANGE TIME HERE END TRANS
});
tl.set(".loading-screen", { left: "-100%", });
tl.set("#svg-1", { opacity: 1, y: 0 });
}
function contentAnimation() {
var tl = gsap.timeline();
tl.from(".animate-this", { duration: .5, y: 30, opacity: 0, stagger: 0.4, delay: 0.2 });
}
$(function () {
barba.init({
sync: true,
transitions: [
{
async leave(data) {
const done = this.async();
pageTransition();
await delay(2500);
done();
},
async enter(data) {
contentAnimation();
},
async once(data) {
contentAnimation();
},
},
],
});
});
Have a look:
https://pasteboard.co/Ja33erZ.gif
Here also a codepen to check my issue (html,css):
https://codepen.io/cat999/project/editor/AEeEdg
scrollRestoration shouldn't be necessary, this can be done by resetting scroll position during the transition:
tl.fromTo(".loading-screen", {width: 0, left: 0}, {
duration: 1,
width: "100%",
left: "0%",
ease: "expo.inOut",
delay: 0.3,
onComplete: function() {
window.scrollTo(0, 0);
}
});
Working example here: https://codepen.io/durchanek/project/editor/ZWOyjE
This should be what you're looking for:
barba.hooks.enter(() => {
window.scrollTo(0, 0);
});
(can be found in the docs)
I'm working on a game in Phaser 3 and I need to use some sort of scrollable panel, so I chose to use Rex UI (if you know any alternatives, please tell me. At first I wanted to use phaser-list-view from npm but it's only in phaser 2). It seems like these plugins do not have much documentation. The docs are on this site: Notes of Phaser 3.
So I have my game configuration and I'm loading like this (oversimplified):
import UIPlugin from '../plugins/ui-plugin.js';
const config = {
// ...
plugins: {
scene: [{
key: 'rexUI',
plugin: UIPlugin,
mapping: 'rexUI'
}]
}
// ...
};
const game = new Phaser.Game(config);
And in a scene I try to use it:
export default class MyScene extends Phaser.Scene {
create() {
this.rexUI.add.scrollablePanel({
x: 0, y: 0,
width: innerWidth,
height: innerHeight/2,
scrollMode: 'horizontal',
panel: {
child: this.add.container().setSize(2 * innerWidth, innerHeight/2)
.add(this.itemImage(1))
.add(this.itemImage(2))
// ...
// (I'm actually using for-loop and save this container in a
// separate variable, but I'm over simplifying this snippet)
mask: false
},
slider: {
track: this.add.graphics({x: 0, y: innerHeight/2 + 10})
.fillRect(0, 0, innerWidth, 30).fillStyle(SOME_LIGHT_COLOR)
.setInteractive(
new Phaser.Geom.Rectangle(0, 0, innerWidth, 30),
Phaser.Geom.Rectangle.Contains
),
thumb: this.add.graphics({x: 0, y: innerWidth/2 + 10})
.fillRect(0, 0, 50, 30).fillStyle(SOME_DARK_COLOR)
.setInteractive(
new Phaser.Geom.Rectangle(0, 0, 50, 30),
Phaser.Geom.Rectangle.Contains
)
}
}).layout()
}
itemImage(n) {
return this.add.image((innerHeight/2 + 30) * (n-1), 0, 'item' + n)
.setDisplaySize(innerHeight/2, innerHeight/2)
}
}
There are many problems. Firstly with the above code I get the error:
Uncaught TypeError: this.child.getAllChildren is not a function
at e.Xo [as resetChildPosition] (<anonymous>:1:205731)
at e.layout (<anonymous>:1:206243)
at e.layout (<anonymous>:1:126859)
at e.layout (<anonymous>:1:126859)
at e.value (<anonymous>:1:172299)
at MyScene.create (MyScene.js:117)
at initialize.create (phaser.min.js:1)
at initialize.loadComplete (phaser.min.js:1)
at initialize.h.emit (phaser.min.js:1)
at initialize.loadComplete (phaser.min.js:1)
The error goes away if I just remove .layout(). But however, the thumb on the scroller is not anywhere in the scene and I can't even scroll the container.
The docs don't say what exacly should go in panel.child, scrolller.track and scroller.thumb
Can someone help me out of this?
try this, just call createTable():
me.createTable({
x: 390,
y: 410,
width: 350,
height: 220,
rank: [{"score":1520,"userID":1,"userName":"Augustus Nico"},{"score":360,"userID":"_2hzxb91byxw","userName":"lipão"},{"score":250,"userID":3,"userName":"Sarão"},{"score":200,"userID":5,"userName":"Bruna Santini"},{"score":160,"userID":4,"userName":"Paulo Junior"},{"score":100,"userID":2,"userName":"Vilasboas"}]
});
const COLOR_PRIMARY = 0x4e342e;
const COLOR_LIGHT = 0x7b5e57;
const COLOR_DARK = 0x260e04;
const COLOR_WHITE = 0xffffff;
export const createTable = ({ x, y, width, height, rank }) => {
var scrollablePanel = this.rexUI.add
.scrollablePanel({
x: x,
y: y,
width: width,
height: height,
scrollMode: 0,
background: this.rexUI.add.roundRectangle(0, 0, 2, 2, 10, COLOR_WHITE),
panel: {
child: createGrid(this, rank),
mask: {
mask: true,
padding: 1
}
},
slider: {
track: this.rexUI.add.roundRectangle(0, 0, 20, 10, 10, COLOR_LIGHT),
thumb: this.rexUI.add.roundRectangle(0, 0, 0, 0, 13, COLOR_DARK)
},
space: {
left: 10,
right: 10,
top: 10,
bottom: 10,
panel: 10,
header: 10,
footer: 10
}
})
.layout();
};
const createGrid = (scene, rank) => {
var sizer = scene.rexUI.add.gridSizer({
column: 2,
row: rank.length,
columnProportions: 1
});
rank.forEach((player, index) => {
sizer.add(
scene.createItem(scene, 0, index, player.userName), // child
0, // columnIndex
index, // rowIndex
"center", // align
0, // paddingConfig
true // expand
);
sizer.add(
scene.createItem(scene, 1, index, player.score), // child
1, // columnIndex
index, // rowIndex
"center", // align
0, // paddingConfig
true // expand
);
});
return sizer;
};
const createItem = (scene, colIdx, rowIdx, text) => {
var item = scene.rexUI.add
.label({
background: scene.rexUI.add
.roundRectangle(0, 0, 0, 0, 0, undefined)
.setStrokeStyle(2, COLOR_DARK, 1),
text: scene.add.text(0, 0, text, {
fontSize: 18,
fill: "#000"
}),
space: {
left: 10,
right: 10,
top: 10,
bottom: 10,
icon: 10
}
})
.setDepth(3);
var press = scene.rexUI.add.press(item).on("pressstart", function() {
console.log(`press ${text}`);
});
return item;
};
I am trying to combine reduce() with map() to reduce multiple box art objects to a single value: the url of the largest box art.
It gives me the following error :
VM272:20 Uncaught TypeError: boxarts.reduce(...).map is not a function at <anonymous>:20:4
Not sure what exactly is wrong
const boxarts = [
{ width: 200, height: 200, url: "http://cdn-0.nflximg.com/images/2891/Fracture200.jpg" },
{ width: 150, height: 200, url: "http://cdn-0.nflximg.com/images/2891/Fracture150.jpg" },
{ width: 300, height: 200, url: "http://cdn-0.nflximg.com/images/2891/Fracture300.jpg" },
{ width: 425, height: 150, url: "http://cdn-0.nflximg.com/images/2891/Fracture425.jpg" }
]
let newReduce = boxarts.reduce(function(accumulator, currentValue) {
if (accumulator.width * accumulator.height > currentValue.width * currentValue.height) {
return accumulator
} else {
return currentValue
}
}).map(function(item) {
return item.url
})
console.log(newReduce)
SOLUTION to the initial question. It returns the link as an array of object.
const boxarts = [{
width: 600,
height: 700,
url: "http://cdn-0.nflximg.com/images/2891/Fracture600.jpg"
},
{
width: 500,
height: 500,
url: "http://cdn-0.nflximg.com/images/2891/Fracture500.jpg"
},
{
width: 425,
height: 150,
url: "http://cdn-0.nflximg.com/images/2891/Fracture425.jpg"
},
{
width: 200,
height: 200,
url: "http://cdn-0.nflximg.com/images/2891/Fracture200.jpg"
}
]
let newReduce = [boxarts.reduce(function(acc, currentValue) {
if (acc.width * acc.height > currentValue.width * currentValue.height) {
return acc
} else {
return currentValue
}
})].map(function(item) {
return {
url:item.url}
})
console.log(newReduce)
const boxarts = [
{ width: 200, height: 200, url: "http://cdn-0.nflximg.com/images/2891/Fracture200.jpg" },
{ width: 150, height: 200, url: "http://cdn-0.nflximg.com/images/2891/Fracture150.jpg" },
{ width: 300, height: 200, url: "http://cdn-0.nflximg.com/images/2891/Fracture300.jpg" },
{ width: 425, height: 150, url: "http://cdn-0.nflximg.com/images/2891/Fracture425.jpg" }
]
let newReduce = boxarts.reduce(function (accumulator, currentValue) {
if (accumulator.width * accumulator.height > currentValue.width * currentValue.height) {
return accumulator
} else {
return currentValue
}
})
console.log(newReduce.url)
From your mapping function it seems that you're trying to get the url, the problem is newReduce is an object and there is no map function. You can easily get the url simply using newReduce.url
The result of your reduce call is a boxart object, not an array. Don't use map, just access the url property:
const boxarts = [
{ width: 200, height: 200, url: "http://cdn-0.nflximg.com/images/2891/Fracture200.jpg" },
{ width: 150, height: 200, url: "http://cdn-0.nflximg.com/images/2891/Fracture150.jpg" },
{ width: 300, height: 200, url: "http://cdn-0.nflximg.com/images/2891/Fracture300.jpg" },
{ width: 425, height: 150, url: "http://cdn-0.nflximg.com/images/2891/Fracture425.jpg" }
]
let newReduce = boxarts.reduce(function(accumulator, currentValue) {
if (accumulator.width * accumulator.height > currentValue.width * currentValue.height) {
return accumulator
} else {
return currentValue
}
}).url
console.log(newReduce)
map works on an array but in your code the reduce method is returning an object, so map will not work. Since it is already returning an object you can get the url just by doing object.url
const boxarts = [{
width: 200,
height: 200,
url: "http://cdn-0.nflximg.com/images/2891/Fracture200.jpg"
},
{
width: 150,
height: 200,
url: "http://cdn-0.nflximg.com/images/2891/Fracture150.jpg"
},
{
width: 300,
height: 200,
url: "http://cdn-0.nflximg.com/images/2891/Fracture300.jpg"
},
{
width: 425,
height: 150,
url: "http://cdn-0.nflximg.com/images/2891/Fracture425.jpg"
}
]
let newReduce = boxarts.reduce(function(accumulator, currentValue) {
if (accumulator.width * accumulator.height > currentValue.width * currentValue.height) {
return accumulator
} else {
return currentValue
}
}).url
console.log(newReduce)
If you still want to use the map function put the return of reduce in an array. Then you can use map method on this. Further the map method will again return an array
const boxarts = [{
width: 150,
height: 200,
url: "http://cdn-0.nflximg.com/images/2891/Fracture150.jpg"
},
{
width: 300,
height: 200,
url: "http://cdn-0.nflximg.com/images/2891/Fracture300.jpg"
},
{
width: 425,
height: 150,
url: "http://cdn-0.nflximg.com/images/2891/Fracture425.jpg"
},
{
width: 200,
height: 200,
url: "http://cdn-0.nflximg.com/images/2891/Fracture200.jpg"
}
]
let newReduce = [boxarts.reduce(function(acc, currentValue) {
if (acc.width * acc.height > currentValue.width * currentValue.height) {
return acc
} else {
return currentValue
}
}, boxarts[0])].map(function(item) {
return item.url
})
console.log(newReduce)
I use TweenMax to animate divs with multiples .mouseover but I'd like one to complete before starting another one.
In this JSFiddle, you can see the divs overlap if going to fast on the links.
Is there an easy solution for this ?
$(document).ready(function() {
var blocPrototypo = $("#wrap-image-menu");
$("#prototypo").mouseover(function() {
TweenLite.to(blocPrototypo, 1.4, {
backgroundColor: "#24d390",
ease: Circ.easeInOut
});
TweenMax.to(blocPrototypo, 0.5, {
width: "39vw",
ease: Circ.easeInOut,
repeat: 1,
yoyo: true
});
var allExcept = $(".all-img-menu").not(document.getElementById("img-prototypo"));
TweenMax.to(allExcept, 0.9, {
left: "0px",
opacity: 0
});
TweenMax.to($("#img-prototypo"), 0.7, {
opacity: "1",
width: "55vw",
left: "-90px",
ease: Expo.easeOut,
delay: "0.65"
});
TweenMax.to($("#line-pagination"), 0.5, {
width: "76px",
ease: Circ.easeInOut,
repeat: 1,
yoyo: true
});
$("#current-page").fadeOut(function() {
$(this).text("01").fadeIn(1000);
});
});
$("#esadvalence").mouseover(function() {
TweenLite.to(blocPrototypo, 1.5, {
backgroundColor: "#e12a1c",
ease: Power1.easeOut
});
TweenMax.to(blocPrototypo, 0.5, {
width: "39vw",
ease: Circ.easeInOut,
repeat: 1,
yoyo: true
});
var allExcept = $(".all-img-menu").not(document.getElementById("img-esadvalence"));
TweenMax.to(allExcept, 0.9, {
left: "0px",
opacity: 0
});
TweenMax.to($("#img-esadvalence"), 0.7, {
opacity: "1",
width: "55vw",
left: "-90px",
ease: Expo.easeOut,
delay: "0.65"
});
TweenMax.to($("#line-pagination"), 0.5, {
width: "76px",
ease: Circ.easeInOut,
repeat: 1,
yoyo: true
});
$("#current-page").fadeOut(function() {
$(this).text("02").fadeIn(1000);
});
});
});
TweenLite and TweenMax have onComplete callbacks that can be used to await completion prior to beginning the next operation:
TweenLite.to(blocPrototypo, 1.5, {
backgroundColor: "#e12a1c",
ease: Power1.easeOut,
onComplete: function() {
// perform next operation
}
});
There is also params you can pass via onCompleteParams, which you could perhaps use to indicate to some generic function the next operation you'd want to execute.
TweenLite.to(blocPrototypo, 1.5, {
backgroundColor: "#e12a1c",
ease: Power1.easeOut,
onCompleteParams: [{ prop1: value1, prop2: value2 }],
onComplete: function(someParams) {
// perform next operation using passed params
}
});
Another approach could be using Promise or jQuery Deferred in combination with the onComplete event callback such as:
function foo() {
return new Promise(function(resolve, reject) {
TweenLite.to(blocPrototypo, 1.5, {
backgroundColor: "#e12a1c",
ease: Power1.easeOut,
onComplete: function() {
return resolve(true);
}
});
});
}
foo().then(function() {
// perform next operation
});
Hopefully that helps!