In my scenario, i create a ship according to player's preferences. The ship consists of the flag and the hull.
An example view
PROBLEM-1
Ship is an Arcade.Group and i want to prevent this group from going outside the borders of the world.
create(){
// Create a group for ship
this.shipGroup = this.physics.add.group()
// Add hull to shipGroup
this.shipGroup.create(400, 500, "1021")
// Add flag to shipGroup
const mainFlag = this.shipGroup.create(400, 500, "FA1")
mainFlag.setOrigin(0.5, 0.8)
// Set collision property to true of every object in shipGroup
this.shipGroup.children.each((item: any) =>
item.setCollideWorldBounds(true)
)
}
update(t: number, dt: number){
if (this.cursor.up.isDown) {
this.shipGroup.setVelocity(...)
}
else {
this.shipGroup.setVelocity(0, 0)
}
}
With this approach every object in group calculated seperately. After hitting the world boundary, the position of the objects is distorted.
PROBLEM-2
To avoid this i tried another approach. I add bounding box to group. Instead of check collision for every object, i will only check collision for bounding box.
create(){
// Create a group for ship
this.shipGroup = this.physics.add.group()
// Add bounding box for shipGroup
this.shipBox = this.shipGroup.create(400, 500, "bbox")
this.shipBox.setCollideWorldBounds(true)
this.shipBox.body.onWorldBounds = true
// Add hull to shipGroup
this.shipGroup.create(400, 500, "1021")
// Add flag to shipGroup
const mainFlag = this.shipGroup.create(400, 500, "FA1")
mainFlag.setOrigin(0.5, 0.8)
/*this.shipGroup.children.each((item: any) =>
item.setCollideWorldBounds(true)
)*/
}
update(t: number, dt: number){
if (this.cursor.up.isDown && !this.shipBox.body.checkWorldBounds()) {
this.shipGroup.setVelocity(...)
}
else {
this.shipGroup.setVelocity(0, 0)
}
}
The problem is checkWorldBounds() returns false even if shipBox hits world boundaries. But collision for shipBox is work.
checkWorldBounds()
Description: Checks for collisions between this Body and the world
boundary and separates them.
Returns: True if this Body is colliding with the world boundary.
How can i implement collision for group and world boundary?
P.S. : phaser version is 3.55.2
There are a few ways to solve/work around this issue, I personally would just use only one image why one physics-body (hull and flag combined) and just move that single image/texture, and switch the image, when needed.
That said, if you need to use the separate images, the easy way is to use a phaser container. (link to the documentation)
create a container
add the images to the container
set the size for the container (default size is width=0 height=0)
create a physics body for the container
done
A short demo:
document.body.style = 'margin:0;';
var config = {
type: Phaser.AUTO,
width: 536,
height: 183,
physics: {
default: 'arcade',
arcade: {
gravity:{ y: 0 },
debug: true
}
},
scene: {
create
},
banner: false
};
function create () {
this.add.text(10,10, 'Ship with physics')
.setScale(1.5)
.setOrigin(0)
.setStyle({fontStyle: 'bold', fontFamily: 'Arial'});
let graphics = this.make.graphics();
graphics.fillStyle(0xffffff);
graphics.fillRect(0, 0, 10, 40);
graphics.generateTexture('ship', 10, 40);
graphics.fillStyle(0xff0000);
graphics.fillRect(0, 0, 30, 10);
graphics.generateTexture('flag', 30, 10);
graphics.generateTexture('flag2', 20, 6);
let hull = this.add.image(0, 0, 'ship')
let flag = this.add.image(0, -5, 'flag')
let flag2 = this.add.image(0, 10, 'flag2')
this.ship = this.add.container(100, 80, [ hull, flag, flag2]);
this.ship.setAngle(-90)
this.ship.setSize(40, 30)
this.physics.world.enable(this.ship);
this.ship.body.setVelocity(100, 0).setBounce(1, 1).setCollideWorldBounds(true);
}
new Phaser.Game(config);
<script src="https://cdn.jsdelivr.net/npm/phaser#3.55.2/dist/phaser.js"></script>
Info: this demo is based partly from this official example
Related
Before anything, I have no idea what I'm doing but I was wondering if it was possible to use vincentgarreaus' particles with names instead of patterns?
I managed to edit particle.js and add a function that randomly picks a name and it replaces the circles for the names but my problem is that while moving the names change too fast in the same particle. If I set it to static it works as intended but I want them moving.
Is it possible?
What I did is going to VG website > inspect element > ctrl P > particle.js > Line 426 and pasted this "code" > ctrl S to save
function PlayerSelector1() {
var word = ['player1', 'player2', 'player3', 'player4', 'player5', 'player6'];
var random = word[Math.floor(Math.random() * word.length)];
return random;
}
switch(p.shape){
case "circle":
var context = pJS.canvas.ctx.canvas.getContext("2d");
var xtxx = PlayerSelector1();
context.beginPath();
context.fillStyle = "black";
context.fillText(xtxx, p.x, p.y);
context.fill();
break;
I have nothing to offer for your help but you will have my eternal gratitude
You can't create text particles using particles.js natively, but you can use tsParticles instead which support text particles.
This is how you can achieve it:
// keeping your function, it's fine
function PlayerSelector1() {
var word = ["player1", "player2", "player3", "player4", "player5", "player6"];
var random = word[Math.floor(Math.random() * word.length)];
return random;
}
// basic particles configuration, used for example
tsParticles.load("tsparticles", {
background: {
color: "#000"
},
particles: {
number: {
value: 10
},
move: {
enable: true
},
size: {
value: 30
},
shape: {
type: "text",
options: {
text: {
value: PlayerSelector1()
}
}
}
}
});
For using tsParticles you can read the instructions on the GitHub repository
This is your working demo on CodePen, using the config shown above: https://codepen.io/matteobruni/pen/oNymXmy?editors=0010
Every time you call the tsParticles.load function, the player will change.
Imagine a very simple scene:
canvas of 800x640
red circle in the center
two blue lines in a 9:00 position
private create(): void {
const circ = this.add.circle(
400, 320, 200, 0xff0000
);
const l1 = this.add.line(
0, 0, 400, 320, 400, 100, 0x0000ff
).setOrigin(0);
const l2 = this.add.line(
0, 0, 400, 320, 200, 320, 0x0000ff
).setOrigin(0);
}
So far so good.
Now I want to scale this simple figure, same config 1.5x its current size:
private create(): void {
const circ = this.add.circle(
400, 320, 200, 0xff0000
);
const l1 = this.add.line(
0, 0, 400, 320, 400, 100, 0x0000ff
).setOrigin(0);
const l2 = this.add.line(
0, 0, 400, 320, 200, 320, 0x0000ff
).setOrigin(0);
this.tweens.add({
targets: [circ, l1, l2],
scale: 1.5,
yoyo: false,
duration: 2000,
ease: 'Sine.easeInOut'
});
}
Expected behavior:
the circle expands from the center
the lines expand as well, ideally where they meet
Actual behavior:
As things stand, only #1 fit my expectations. The lines, however, translate as opposed to merely scaling. And the translation seems affected by the scale parameter passed to tweens.add. What gives? What am I missing here?
Given the various configurations for "origin" wrt lines in Phaser 3, the worst I was expecting was that the lines would emanate/grow differently than the circle (which emanates from the center/origin). But I definitely expect the lines to stay still/keep their intersection at the circle's center.
Can you explain what exactly Phaser is doing here and what might I do to get my desired effect?
There are a bunch of things that might be unintuitive with the Line GameObject in Phaser 3, all contributing to this behavior. Let's break them down.
Lines and Origins
First of all, make the distinction between line as a geometric entity and line as a Phaser 3 Game Object. They are related but the confusion stems from the subtleties.
For the Phaser 3 line Game Object, there are three ordered pairs p (x, y) you need to keep in mind:
P (X, Y), the origin of the line, relative to the game world (and we are going to define the term in a while); in the game world, (0, 0) is at the upper-left corner.
P1 (X1, Y1), the first point that defines a line, relative to P
P2 (X2, Y2), the second point that defines a line, relative to P
Another important thing is that the game world is basically a Cartesian coordinate system flipped around the x-axis. So negative x values still go to the left but negative y values go up and positive y values go down.
Now let's define the origin, which I will just paraphrase from the official docs:
An object's origin is a normalized value in the range [0, 1].
For the x-axis, 0 means the left of the Game Object and 1 means the right.
For the y-axis, 0 means the top of the Game Object and 1 means the bottom.
By default, the origin is set at 0.5, the center of the object.
Interpreting the example and the tween
Let's take the horizontal line l2 in the example and figure out how it's being drawn.
const l2 = this.add.line(
0, 0, 400, 320, 200, 320, 0x0000ff
).setOrigin(0);
The origin is to the left of the game object.
With respect to the game world, this origin lies at 0, 0 or the upper-left corner.
The line is defined by two points relative to the origin, (400, 320) and (200, 320).
The important observation here is that the origin is completely outside the geometric line defined. In effect, there is a considerable invisible space in the rectangular area between your origin (at (0, 0)), left of the game object and the right, lowermost corner (at (400, 320)).
Now, when the tween is invoked, it actually scales the game object rather than just the geometric line. And since most of the game object is "invisible" (the geometric line being the only part visible), the effect is as if the geometric line is translating!
Solution
To get the desired effect, constrain the game object to exactly the area taken up by the geometric line. One such way to do this would be:
since we want the lines to "radiate" in the same way as the circle, set P at (400, 320), the center of the circle. Additionally, set P1 to (0, 0)---remember this is relative to P, so in the game world, we just made P = P1.
let's leave the lines' origins at 0 but now we have to redefine their P2 values to (0, -100) and (0, -200) respectively.
Putting it all together,
private create(): void {
const circ = this.add.circle(
400, 320, 200, 0xff0000
);
const l1 = this.add.line(
400, 320, 0, 0, 0, -100, 0x0000ff
).setOrigin(0);
const l2 = this.add.line(
400, 320, 0, 0, -200, 0, 0x0000ff
).setOrigin(0);
this.tweens.add({
targets: [circ, l1, l2],
scale: 1.5,
yoyo: false,
duration: 2000,
ease: 'Sine.easeInOut'
});
}
So I've built a map with several different colored lines. I chopped up the lines into a string of segments at each dot.
What I'm trying to do is use javascript to change the colors of the lines and eventually move to being able to change the dynamically with a feed.
I made a color chart that I'm trying to get the line segment object to find its color and change to the proper corresponding color. The dots pulsate the fill color with greensock properly but the lines are giving me trouble. I keep getting an object isn't a function error.
I've tried a few different ways to set up the formula but I'm stuck.
var colorSet = {
'#ff0000': '#ff6666',
'#008250': '#42a680',
'#a3238f': '#bd73b2',
'#0079c2': '#42a0db',
'#ff8c00': '#ffba66',
'#96c800': '#cae087',
'#a86000': '#c28f4e',
'#999999': '#cccccc',
'#ffe000': '#fff399'
}
var animateThis = function(obj) {
var getStroke = obj.getAttribute('stroke');
TweenLite.to(obj, 1.5, {
fill: "#bbbbbb",
yoyo: true,
repeat: -1
});
};
Here is my fiddle: https://jsfiddle.net/Spiderian/hzafm6vk/3/#&togetherjs=6e0qwPovaP
To color lines you need to use stroke, not fill. And on <line> not on <g>.
getStroke is not used in you code. (And there is no getAttribute on $ by the way. There is attr())
colorSet also unused, and I don't really understand what is it for. As all of lines and points in the jsfiddle you've provided are of the same color.
Don't really understand why there are two lines for each <g>.
But nevertheless here is what you need to do:
// change all line selectors from this
// const lsSIT0001 = $("#SIT0001");
// const lsSIT0002 = $("#SIT0002");
// ...
// to this
const lsSIT0001 = $("#SIT0001 line");
const lsSIT0002 = $("#SIT0002 line");
// ...
// because you need to change stroke color of `<line>`s, not `<g>`s
// split
// var itemsToAnimate = [ lsSIT0020, ..., lsSIT0001, SIR4947, ..., SIR4629 ]
// into
const linesToAnimate = [ lsSIT0020, ..., lsSIT0001 ]
const pointsToAnimate = [ SIR4947 , ..., SIR4629 ]
// run separate animations for lines and dots
const animatePoints = obj => TweenLite.to(obj, 1.5, { fill : "#ff0000", yoyo: true, repeat: -1 })
const animateLines = obj => TweenLite.to(obj, 1.5, { stroke: "#0000ff", yoyo: true, repeat: -1 })
linesToAnimate .forEach(animateLines )
pointsToAnimate.forEach(animatePoints)
Update:
To use colorSet you need to either
set style='stroke: some_color' on each <line> and then use it like this:
const style = obj.eq(i).attr('style')
// `i` is the index of a line inside <g> the color of which you want to get
TweenLite.to(obj, 1.5, { stroke: colorSet[style], ... })
In this case colorSet should look like
const colorSet = {
"stroke: #ff0000": '#ff6666',
...
"stroke: #ffe000": '#fff399',
}
or get computed styles with obj.get(i).computedStyleMap().get("stroke").toString()
In this case colorSet should look like
const colorSet = {
"rgb(255, 0, 0)": '#ff6666',
...
"rgb(255, 224, 0)": '#fff399',
}
But it's experimental technology. So probably don't :)
Or better yet - make a map not from color to color but from ids to color:
const colorSet = {
"#SIT0001": '#ff6666',
...
"#SIT0020": '#fff399',
}
Then you won't need to get colors from DOM.
In my Phaser3 game there is a global gameTick variable that is incremented every update. I am using this to spawn in enemies in my game every 100th update.
Here is a simplifed example of what is going on in my scene class:
update () {
global.gameTick++;
if (global.gameTick % 100 === 0) {
this.spawnAlien();
}
}
This works fine but as soon as a user plays the game on a monitor with a refresh rate >60hz the update timing breaks and causes the aliens to spawn more frequently.
I have checked this.physics.world.fps and it is 60. I can also modify this.physics.world.timescale but then I would have to do a giant switch statement for every refresh rate.
Either I am missing an obvious solution or my global.gameTick method is not an effective way to accomplish this task.
This is what I have in my config so far
let config = {
type: Phaser.AUTO,
backgroundColor: "#000",
scale: {
parent: "game",
mode: Phaser.Scale.FIT,
width: 1900,
height: 600,
},
physics: {
default: "arcade",
arcade: {
debug: true,
fps: 60 // doesn't fix update frequency
},
fps: { // not sure if this is even doing anything
max: 60,
min: 20,
target: 60,
}
},
pixelArt: true,
};
You can also set the following property in your game's config object:
fps: {
target: 24,
forceSetTimeOut: true
},
Source: https://phaser.discourse.group/t/how-to-limit-fps-with-phaser-3/275/14?u=saricden
To limit the update rate, use the following method.
// The time and delta variables are passed to `update()` by Phaser
update(time, delta) {
this.frameTime += delta
if (this.frameTime > 16.5) {
this.frameTime = 0;
g.gameTick++;
// Code that relies on a consistent 60hz update
}
}
This accumulates the miiliseconds between the last frame and the current frame. It only runs the update() code if there has been 16.5ms of delay.
The example above works for 60fps, but if you want to limit the FPS to a different value use the formula: delay = 1000/fps.
In my Phaser 3 game I have a group called OBS that stores all the obstacles sprites.
OBS = this.physics.add.group
I want to set the velocity of every child created from this group to a particular value say
setVelocityY=40
So every object that is in group OBS will move at velocity 40. How do I set a configuration for the group such that every object that I add to it moves at the given velocity and shares other common properties?
There are multiple ways to do that.
using config -
You can pass PhysicsGroupConfig while creating group, and set velocityY in that config that will set velocityY of every children of that group. You can set many properties in config. Check out the docs.
using group's methods like setVelocity or setVelocityY - if you want to set velocity after creating group you can use group's methods like setvelocityY or setVelocity.
You can use a for-loop to add properties to all the child elements.
Eg. if you have 3 child elements, you can do it like this:
var OBSPositions = [
{ x: 50, y: 80 },
{ x: 50, y: 480 },
{ x: 300, y: 480 }
];
OBS = this.physics.add.group;
for (var i = 0; i < OBSPositions.length; i++) {
var OBSchild = OBS.create(
OBSPositions[i].x,
OBSPositions[i].y,
'OBSsprite'
).setScale(0.13, 0.13);
OBSchild.setVelocityY = 40;
}