Related
I'm working on a new project in phaser and for some reason the gravity in the game is all messed up, when i attempt to jump i jump like a centimeter. if i change the values nothing changes its always glitched. how can i make it so that i jump and fall normally?
I've had some previous projects and the gravity works just fine, for this project i am using the latest stable release of phaser 3. I honestly cant see what the error is and i've been at it for a while.
there was a lot of code that wasn't relevent to the error so i removed it to make it easier for someone to review this.
game.js
const socket = io();
var config = {
type: Phaser.AUTO,
width: 1000,
height: 550,
parent: 'master',
physics: {
default: 'arcade',
arcade: {
gravity: { y: 300 },
debug: true
}
},
scene: {
preload: resources,
create: mechanics,
update: controls
}
};
const game = new Phaser.Game(config);
function resources() {
this.load.image("arena", "../resources/images/arena1.png");
this.load.image("floor", "../resources/images/floor.png");
this.load.atlas("warrior", "../resources/images/characters/warrior.png","../resources/images/characters/warrior.json");
}
var warrior;
function mechanics() {
grasslands = this.add.image(500, 225, "arena").setScale(0.7);
warrior = this.physics.add.sprite(100, 490, "warrior").setScale(2).setSize(15, 15);
floor = this.physics.add.staticGroup();
floor.create(500, 545, "floor").setVisible(false);
this.physics.add.collider(warrior, floor);
warrior.body.collideWorldBounds = true;
warrior.body.onWorldBounds = true;
}
function controls() {
key = this.input.keyboard.addKeys("W,A,S,D");
if(key.A.isDown) {
warrior.setVelocityX(-100);
warrior.flipX = true;
}else if (key.D.isDown) {
warrior.setVelocityX(100);
warrior.flipX = false;
}else if (key.W.isDown && warrior.body.touching.down) {
warrior.setVelocityY(-330);
}else{
warrior.setVelocity(0);
}
}
The problem occurs because of this line warrior.setVelocity(0);. This line, stops the gravity of working, as intended (and hinders jumping), since on scene updates, the velocity is set to 0. Remove that line and add warrior.setVelocityX(0) at the start of the controls function and everything should work fine (if you want/have to keep the if-else block). Or check out my working demo at the end of this answer.
function controls() {
key = this.input.keyboard.addKeys("W,A,S,D");
warrior.setVelocityX(0);
if(key.A.isDown) {
warrior.setVelocityX(-100);
warrior.flipX = true;
}else if (key.D.isDown) {
warrior.setVelocityX(100);
warrior.flipX = false;
}else if (key.W.isDown && warrior.body.touching.down) {
warrior.setVelocityY(-330);
}
}
I would only stop the left/right movement, when the player stops pressing the keys ( with setVelocityX(0)), and let gravity take care of stopping the jump/upwards movement.
Here a basic demo:
var config = {
type: Phaser.AUTO,
width: 400,
height: 160,
physics: {
default: 'arcade',
arcade: {
gravity: { y: 200 },
}
},
scene: {
create,
update
}
};
var cursors;
var player;
var playerStateText;
const SPEED = 250;
var isJumping = true;
function create () {
cursors = this.input.keyboard.createCursorKeys();
this.add.text(10, 10, 'Use arrow Keys to move!')
let ground = this.add.rectangle(-40, 120, 480, 50, 0xBAF0FF).setOrigin(0);
player = this.add.rectangle(20, 20, 30, 30, 0xcccccc).setOrigin(0);
ground = this.physics.add.existing(ground);
ground.body.setImmovable(true);
ground.body.allowGravity = false;
player = this.physics.add.existing(player);
this.physics.add.collider(player, ground, _ => isJumping = false);
}
function update (){
if (cursors.left.isDown){
player.body.setVelocityX(-SPEED);
} else if (cursors.right.isDown) {
player.body.setVelocityX(SPEED);
}
else {
player.body.setVelocityX(0);
}
if (!isJumping && cursors.up.isDown){
player.body.setVelocityY(-SPEED * .75);
isJumping = true;
}
}
new Phaser.Game(config);
<script src="//cdn.jsdelivr.net/npm/phaser#3.55.2/dist/phaser.js"></script>
I am trying to make a mini-game by phaser.js. In my idea. A sprite object can collided to a static sprite and continually perform desired effect if they are stick together. However, when I handle them with this.physics.add.collider, the callback function just run once.
Search for API document. I find the touching event can be judged by object.body.touching. But seen it can only return the facing. So I wonder how to get the object who are touching on the specific direction of a sprite? Or the function is need to handle by handy?
Thanks for your help.
If you need more "detailed"/"better" physics functions in phaser, you could use the matterjs physics engine.
(It is as easy as the arcade engine, atleast to setup)
With matterjs there are more options, like the "collisions events", using matter you could use the event collisionactive.
(link to the documentation)
collisionactive executes as long as the two colliding objects touch.
Like this you know the exact object(s) touching. (And if you need the direction you could find it with the x and y postions of the objects, or the velocity of the impact)
Here a demo from the offical website, showing collisionstart Event https://phaser.io/examples/v3/view/physics/matterjs/collision-event
Here a small with matter demo:
// Minor formating for stackoverflow
document.body.style = "display: flex;flex-direction: column;";
var config = {
type: Phaser.AUTO,
width: 536,
height: 153,
backgroundColor: '#1b1464',
physics: {
default: 'matter',
matter: {
debug:true,
gravity:{y:0, x:0}
}
},
scene: {
create: create
}
};
var game = new Phaser.Game(config);
var counter = 0;
var gOverlay;
function create ()
{
var blockA = this.matter.add.image(50, 80, 'block1');
var blockB = this.matter.add.image(300, 80, 'block1').setStatic(true);
var text = this.add.text(10,10, 'Not touching')
blockA.setVelocityX(3);
gOverlay = this.add.graphics();
this.matter.world.on('collisionstart', function (event, bodyA, bodyB) {
text.setText(text.text + '\nHIT')
drawDirectionArrow(bodyA, bodyB)
});
this.matter.world.on('collisionactive', function (event, bodyA, bodyB) {
text.setText(`Touching: ${counter++}`)
if(counter > 60 ) {
counter = 0;
blockA.setVelocityX(-2);
}
});
this.matter.world.on('collisionend', (function (event, bodyA, bodyB) {
text.setText(text.text + '\nNot touching');
gOverlay.clear();
this.time.delayedCall(2000, _ => blockA.setVelocityX(5));
}).bind(this));
}
function drawDirectionArrow(a, b){
gOverlay.clear();
gOverlay.fillStyle( 0xFF00FF);
gOverlay.fillTriangle(a.position.x, a.position.y, b.position.x, b.position.y - 10, b.position.x, b.position.y + 10);
}
<script src="https://cdn.jsdelivr.net/npm/phaser#3.55.2/dist/phaser.js">
</script>
Update:
If you need/want to use arcade physics, here is a similar demo, with a small workaround using a second physics-object to check for overlap.
Arcade - Physics workaround - Demo:
// Minor formating for stackoverflow
document.body.style = "display: flex;flex-direction: column;";
var config = {
type: Phaser.AUTO,
width: 536,
height: 153,
backgroundColor: '#1b1464',
physics: {
default: 'arcade',
arcade: {
debug:true,
}
},
scene: {
create: create
}
};
var game = new Phaser.Game(config);
var counter = 0;
var gOverlay;
function create () {
var blockA = this.physics.add.image(50, 80, 'block1').setOrigin(.5);
var blockB = this.physics.add.image(300, 80, 'block1').setOrigin(.5);
// Helper
var blockBx = this.add.rectangle(300, 80, 34, 34);
this.physics.add.existing(blockBx);
blockB.setImmovable();
var text = this.add.text(10,10, 'Not touching')
blockA.setVelocityX(50);
gOverlay = this.add.graphics();
this.physics.add.collider(blockA, blockB, function ( bodyA, bodyB) {
drawDirectionArrow(bodyA, bodyB);
})
this.physics.add.overlap(blockA, blockBx, function ( bodyA, bodyB) {
text.setText(`Touching: ${counter++}`);
if(counter > 60 ) {
counter = 0;
gOverlay.clear();
blockA.setVelocityX(-50);
text.setText('Not touching');
this.time.delayedCall(2000, _ => blockA.setVelocityX(50));
}
}, null, this);
}
function drawDirectionArrow(a, b){
gOverlay.clear();
gOverlay.fillStyle( 0xFF00FF);
gOverlay.fillTriangle(a.x, a.y, b.x, b.y - 10, b.x, b.y + 10);
}
<script src="https://cdn.jsdelivr.net/npm/phaser#3.55.2/dist/phaser.js">
</script>
I'm creating a large grid of divs which I plan to perform animations and effects on. I have around 90 divs creating a tiled background. Now here's the trick with this..I am creating the page to be responsive. I cannot float these divs and so they are inline-blocked with overflow hidden. So as the width of the viewport changes the amount of divs in the rows changes - essentially moving the div over and down. So I've had to dynamically populate the div text based on position(x,y). Unfortunately I am having an issue with the event listener box moving because of this.
When I try to remove the event listener I get an undefined error - even when I move it into the initial listener scope.
function menuBox(){
var allDiv = document.getElementsByTagName('div');
var menuDiv1 = document.elementFromPoint(39, 16);
var menuDiv2 = document.elementFromPoint(39, 120); //Blank Div
var menuDiv3 = document.elementFromPoint(39, 225);
var menuDiv4 = document.elementFromPoint(39, 329);
var menuDiv5 = document.elementFromPoint(39, 433);
var menuDiv6 = document.elementFromPoint(39, 538);
var menuDiv7 = document.elementFromPoint(39, 642);
var menuDiv = [
menuDiv1,
menuDiv3,
menuDiv4,
menuDiv5,
menuDiv6,
menuDiv7
]
for (var i = 0; i < allDiv.length; i++) {
allDiv[i].innerHTML = '';
// allDiv[i].removeEventListener("mouseover", menuOver, false);
// allDiv[i].removeEventListener("mouseout", menuOut, false);
};
for (var i = 0; i < menuDiv.length; i++) {
menuDiv[i].addEventListener("mouseover", function menuOver(){
TweenLite.to(this, 0.4, {backgroundColor: '#272822', color: '#fff', scale: 1.1})
}, false);
menuDiv[i].addEventListener("mouseout", function menuOut(){
TweenLite.to(this, 0.3, {backgroundColor: '#fff', color: '#000', scale: 1})
}, false);
};
menuDiv1.innerHTML = '<p>Switch<br>Menu</p>';
menuDiv3.innerHTML = '<p>Michael</p>';
menuDiv4.innerHTML = '<p>Design</p>';
menuDiv5.innerHTML = '<p>Develop</p>';
menuDiv6.innerHTML = '<p>Imaging</p>';
menuDiv7.innerHTML = '<p>Motion</p>';
console.log('menuBox function');
};
menuBox();
window.onresize = menuBox;
You should know that, to remove event handlers from DOM element, the function specified with the addEventListener() method must be an external function .
Anonymous functions, like domelement.removeEventListener("event", function(){ \\something }); will not work.
Having said that, same mistake has been done in your code, thus leading to reference type of error Uncaught ReferenceError: menuOver is not defined.
To make you code work, below are changes you should use:
for (var i = 0; i < allDiv.length; i++) {
allDiv[i].innerHTML = '';
allDiv[i].removeEventListener("mouseover", menuOver, false);
allDiv[i].removeEventListener("mouseout", menuOut, false);
};
for (var i = 0; i < menuDiv.length; i++) {
menuDiv[i].addEventListener("mouseover",menuOver, false);
menuDiv[i].addEventListener("mouseout",menuOut, false);
};
function menuOver(){
TweenLite.to(this, 0.4, {backgroundColor: '#272822', color: '#fff', scale: 1.1});
}
function menuOut(){
TweenLite.to(this, 0.3, {backgroundColor: '#fff', color: '#000', scale: 1});
}
In my game, I need some buttons that will work on mobile devices (buttons that you can press and/or hold in the game). I saw this example (note that the version of Phaser being used here is old, however, it still works) and was able to temporarily have some working buttons. Here's the source code for that example.
However, one thing bothered me about this example's code for the creation of these virtual gamepad buttons: the buttons' code wasn't DRY (Don't Repeat Yourself). You can see how these buttons keep getting created in the same fashion here over and over again:
// create our virtual game controller buttons
buttonjump = game.add.button(660, 340, 'buttonjump', null, this, 0, 1, 0, 1); //game, x, y, key, callback, callbackContext, overFrame, outFrame, downFrame, upFrame
buttonjump.anchor.setTo(0.5, 0.5);
buttonjump.fixedToCamera = true; //our buttons should stay on the same place
buttonjump.events.onInputOver.add(function(){jump=true;});
buttonjump.events.onInputOut.add(function(){jump=false;});
buttonjump.events.onInputDown.add(function(){jump=true;});
buttonjump.events.onInputUp.add(function(){jump=false;});
buttonfire = game.add.button(750, 340, 'buttonfire', null, this, 0, 1, 0, 1);
buttonfire.anchor.setTo(0.5, 0.5);
buttonfire.fixedToCamera = true;
buttonfire.events.onInputOver.add(function(){fire=true;});
buttonfire.events.onInputOut.add(function(){fire=false;});
buttonfire.events.onInputDown.add(function(){fire=true;});
buttonfire.events.onInputUp.add(function(){fire=false;});
buttonleft = game.add.button(40, 312, 'buttonhorizontal', null, this, 0, 1, 0, 1);
buttonleft.anchor.setTo(0.5, 0.5);
buttonleft.fixedToCamera = true;
buttonleft.events.onInputOver.add(function(){left=true;});
buttonleft.events.onInputOut.add(function(){left=false;});
buttonleft.events.onInputDown.add(function(){left=true;});
buttonleft.events.onInputUp.add(function(){left=false;});
buttonbottomleft = game.add.button(48, 352, 'buttondiagonal', null, this, 6, 4, 6, 4);
buttonbottomleft.anchor.setTo(0.5, 0.5);
buttonbottomleft.fixedToCamera = true;
buttonbottomleft.events.onInputOver.add(function(){left=true;duck=true;});
buttonbottomleft.events.onInputOut.add(function(){left=false;duck=false;});
buttonbottomleft.events.onInputDown.add(function(){left=true;duck=true;});
buttonbottomleft.events.onInputUp.add(function(){left=false;duck=false;});
buttonright = game.add.button(136, 312, 'buttonhorizontal', null, this, 0, 1, 0, 1);
buttonright.anchor.setTo(0.5, 0.5);
buttonright.fixedToCamera = true;
buttonright.events.onInputOver.add(function(){right=true;});
buttonright.events.onInputOut.add(function(){right=false;});
buttonright.events.onInputDown.add(function(){right=true;});
buttonright.events.onInputUp.add(function(){right=false;});
buttonbottomright = game.add.button(128, 352, 'buttondiagonal', null, this, 7, 5, 7, 5);
buttonbottomright.anchor.setTo(0.5, 0.5);
buttonbottomright.fixedToCamera = true;
buttonbottomright.events.onInputOver.add(function(){right=true;duck=true;});
buttonbottomright.events.onInputOut.add(function(){right=false;duck=false;});
buttonbottomright.events.onInputDown.add(function(){right=true;duck=true;});
buttonbottomright.events.onInputUp.add(function(){right=false;duck=false;});
buttondown = game.add.button(88, 360, 'buttonvertical', null, this, 0, 1, 0, 1);
buttondown.anchor.setTo(0.5, 0.5);
buttondown.fixedToCamera = true;
buttondown.events.onInputOver.add(function(){duck=true;});
buttondown.events.onInputOut.add(function(){duck=false;});
buttondown.events.onInputDown.add(function(){duck=true;});
buttondown.events.onInputUp.add(function(){duck=false;});
Because they were created in such a non-DRY and, what I feel to be, inefficient way, I decided that my buttons should have a gamepad button class that they all inherit from. Unfortunately, I've been running into lots of problems trying to make this button class work.
I have an example here that models what I'm trying to do in my game.
(Here's the source code for my example)
// Global constants
var GAME_WIDTH = 800;
var GAME_HEIGHT = 600;
var ORIGIN = 0;
var TEXT_X_POS = 50;
var TEXT_Y_POS = 100;
var TEXT_STYLE = { fontSize: "16px" };
var RIGHT_BUTTON_X_POS = 600;
var RIGHT_BUTTON_Y_POS = 400;
var LEFT_BUTTON_X_POS = 100;
var LEFT_BUTTON_Y_POS = 400;
var PHASER_DUDE_Y_POS = 300;
var PHASER_DUDE_GRAVITY = 300;
var PHASER_DUDE_RIGHT_VELOCITY = 100;
var PHASER_DUDE_LEFT_VELOCITY = -100;
var STOPPED = 0;
// Global variables
var background;
var rightButton;
var movingRight;
var rightButtonDown;
var leftButton;
var movingLeft;
var leftButtonDown;
var phaserDude;
var rightKey;
var leftKey;
// New instance of Phaser.Game
var game = new Phaser.Game(GAME_WIDTH, GAME_HEIGHT, Phaser.AUTO, "game", {preload: preload, create: create, update: update});
// Mobile button class
var MobileButton = function (button, movingInADirection, isTheButtonDown, pressedMethod) {
button.events.onInputOver.add(function () {
if (isTheButtonDown === true) {
movingInADirection = true;
}
});
button.events.onInputDown.add(function () {
isTheButtonDown = true;
movingInADirection = true;
});
button.events.onInputUp.add(function () {
movingInADirection = false;
});
};
function preload () {
game.load.image("background", "sprites/sky.png");
game.load.image("left arrow", "sprites/left_arrow.png");
game.load.image("right arrow", "sprites/right_arrow.png");
game.load.image("phaser dude", "sprites/phaser_dude.png");
}
function create () {
background = game.add.image(ORIGIN, ORIGIN, "background");
game.add.text(TEXT_X_POS, TEXT_Y_POS, "Use the arrow keys or the arrow buttons below to move", TEXT_STYLE);
rightButton = game.add.button(RIGHT_BUTTON_X_POS, RIGHT_BUTTON_Y_POS, "right arrow", moveRight);
leftButtonDown = game.add.button(LEFT_BUTTON_X_POS, LEFT_BUTTON_Y_POS, "left arrow", moveLeft);
phaserDude = game.add.sprite(game.world.centerX, PHASER_DUDE_Y_POS, "phaser dude");
game.physics.arcade.enable(phaserDude);
phaserDude.body.collideWorldBounds = true;
phaserDude.body.gravity.y = PHASER_DUDE_GRAVITY;
rightKey = game.input.keyboard.addKey(Phaser.Keyboard.RIGHT);
leftKey = game.input.keyboard.addKey(Phaser.Keyboard.LEFT);
}
function update () {
stopMoving();
if (leftKey.isDown || movingLeft === true) {
moveLeft();
}
if (rightKey.isDown || movingRight === true) {
moveRight();
}
}
function moveRight () {
phaserDude.body.velocity.x = PHASER_DUDE_RIGHT_VELOCITY;
}
function moveLeft () {
phaserDude.body.velocity.x = PHASER_DUDE_LEFT_VELOCITY;
}
function stopMoving () {
phaserDude.body.velocity.x = STOPPED;
}
As you can see, the arrow keys work fine for moving the sprite, but the mobile buttons do not work well; they only move the sprite for a frame, and then it stops moving again. I'm not sure why the keys work, yet the mobile buttons don't. The problem seems to be that the code in the class is not being run the way that I am thinking it should run (i.e., it seems like all of the code concerning the onInputOver, onInputDown, and onInputUp events is not being correctly run and the class is only paying attention to the method to run when a button is pressed). Can anyone figure out what the problem is with my button class?
Your problem is that the onInputDown of Phaser.Button only fires once each time the button is pressed.
What you need to do is set an isDown property on the button something like this:
button.events.onInputDown.add(function () {
button.isDown = true;
});
button.events.onInputUp.add(function () {
button.isDown = false;
});
And the in your update method check for that property:
function update () {
stopMoving();
if (leftKey.isDown || leftButton.isDown) {
moveLeft();
}
I have some icons from Raphaƫl.js, and I would like to fade them in sequentially on page load.
Here I build the icons:
function navBar() {
var icon={/* lots of path strings */},
fill = {
fill:"#666",
stroke:"none",
opacity:0 // opacity is initially 0; would like to loop through this
},
stroke = {
stroke:"#fff",
"stroke-width":3,
"stroke-linejoin":"round",
opacity:0
};
for (var name in icon) {
var r = Raphael(0, 0, 40, 40),
s = r.path(icon[name]).attr(stroke).translate(4, 4),
Icon = r.path(icon[name]).attr(fill).translate(4, 4),
Path = document.getElementById("path"),
none = {fill: "#000", opacity: 0};
(function (icon, path, s, name) {
r.rect(0, 0, 38, 38).attr(none).hover(function () {
s.stop().animate({opacity: 1}, 200);
}, function () {
s.stop().attr({opacity: 0});
});
})(Icon, icon[name], s, name);
// Here I'm able to fade all of the icons in at the same time
(function (icon) {
Icon.animate({opacity:1},200);
})(Icon);
}
}
Rather than fade all of the icons in at the same time as I've done at the end of the script, how can I loop through them and fade them each in, one by one, with a slight delay in between?
Maybe you need to animate them after create opr. is done. Here is a simulation: http://jsfiddle.net/r75hh/
Like this or so.
// creating
var icon = new Raphael ...
...
icon.attr("rel", "icon")
// the last line in navBar()
animateIcons();
// animating
function animateIcons() {
// assuming jQuery in use
var icons = $("elementTagNameOfIcon[rel='icon']");
var i = 0;
var f = function(){
var icon = icons.get(i);
if (icon) {
$(icon).animate({opacity:1}, 200, function(){
f();
});
i++;
}
};
f();
}