Phaser 3 - Sprite linear interpolation using movement queue - javascript

I'm currently using Phaser 3 to represent my server's state.
Every x amount of time, I am sent the server's game state, this is what the client looks like:
var t1 = Date.now();
var serverUpdateDelta = 0;
Client.socket.on('usersPool', usersPool => {
// usersPool is an object containing all the user data of sockets connected on the server. Looks something like this:
/*
usersPool = {
"user1234": { x: 0, y: 0, direction: "right", moving: true },
"testuser": { x: 200, y: 250, direction: "down", moving: false }
}
*/
// keeping count of milliseconds between updates (usually around 500m)
serverUpdateDelta = Date.now() - t1;
// for every user connected on the server...
for(id in usersPool) {
let data = usersPool[id]; // this is the user's data
if(/* the player exists as a sprite in the game...*/) {
// THIS IS WHERE THE MAGIC SHOULD HAPPEN
} else {
genSprite(player);
}
}
});
The player's data contains a movementQueue, which is just an array of coordinates the user has been at. It might look a little like this:
[
{ x: 0, y: 0, direction: 'down', moving: false },
{ x: 5, y: 0, direction: 'right', moving: true },
{ x: 6, y: 0, direction: 'right', moving: false }
]
This is calculated on the server, but each movementStack (item in the movementQueue`) is generated every 25 milliseconds or so on the server.
The job now is, when receiving this movementQueue, to interpolate the values and move the sprite accordingly...
Attempt 1
I first tried making a function which would interpolate once the update was received, like so:
// THIS IS WHERE THE MAGIC SHOULD HAPPEN
// user's state on the client is set to an interpolated version of the one on the server
player.movementQueue = buffer(data.movementQueue);
The buffer will simply generate an interpolated array based on the serverUpdateDelta and game.loop.actualFps
then, in the Game.update function I ran the following:
for(id in spawnedPlayers) {
// this will remove the first movementStack from the queue and returns in
movementStack = spawnedPlayers[id].movementQueue.shift();
// we then take this movementStack and update the user to that position (and play the walking animation)
spawnedPlayers[id].update(movementStack);
}
So every game loop, we would remove a stack from the queue and set the user to it.
This did NOT work. The game loop seemed to run Way more times than there were frames in the queue, making the player look like they were moving a small distance very slowly...*:
player.movementQueue = player.movementQueue.concat(buffer(data.movementQueue));
But then something weird happened, where the game loop could not keep up with the movementQueue and the player would move extremely slowly...
Attempt 2
I then tried using tweens which would be really easy to implement, simply run:
// THIS IS WHERE THE MAGIC SHOULD HAPPEN
_this.tweens.timeline({
targets: player.sprite,
tweens: data.movementQueue, // [{x, y}, {x, y}, {x, y}]
duration: serverDeltaTime/movementQueue.length, // duration between each tween in ms
});
This worked ALMOST perfectly, except for one small detail:
Before, we would run a method for the player on each movementStack: player.update(movementStack), this method would take the direction of the user and animate the sprite accordingly. Now we have no way of doing this...
SO
What methods or techniques could I use? What am I missing? What could I implement? I ask this because I'm stuck at this point.
Thank you in advance for the help.

Related

How to get value in onPanResponderRelease?

I'm new to using PanResponder in React Native. I want to make draggable component (only ups and down, so I use Y value only). In onPanResponderMove I can get live value of Y by getting gestureState.dy.
Next, I made helper function that called in onPanResponderRelease to reset the position of dragged component when the touch is off. Here is the code:
resetPosition() {
Animated.timing(
// Animate value over time
this.position, // The value to drive
{
toValue: { x: 0, y: 0 }, // Animate to final value of 1
duration: 350
},
).start();
}
My question is; how can I get the live Y value when the component from it last position to reset position?

Plotly: How to discard older points?

TL;DR I want to display a long-running strip chart with Plotly.js. I don't know how to discard old points.
Details
The following updater from my CodePen at https://codepen.io/Michael-F-Ellis/pen/QvXPQr does almost what I want. It shows a set of 20 samples in 2 traces that update continuously at 500 msec intervals. At the end of the demo, it plots all the points to show they still exist.
var cnt = 0;
var interval = setInterval(function() {
// Add next point to each trace
Plotly.extendTraces('graph', {
y: [[rand()], [rand()]]
}, [0, 1])
// Display only 20 most recent points
Plotly.relayout('graph', { 'xaxis.range': [cnt-20, cnt]})
cnt = cnt+1;
if(cnt === 100) {
// Before ending the run, show all points
// to demonstrate they still exist in Plotly.
Plotly.relayout('graph', { 'xaxis.range': [0, cnt]});
clearInterval(interval);
}
}, 500);
The problem is that I do want to delete older points. The real application needs to run essentially forever on a system with limited memory. I'm looking for a Plotly call that will drop the oldest N trace points. It needs to be reasonably efficient as performance of the target system is also limited.
Thanks!
https://codepen.io/Michael-F-Ellis/pen/YxeEwm
The above seems workable from a behavioral standpoint. Here's the revised updating routine:
Plotly.plot('graph', data);
var cnt = 0;
var max = 20;
var interval = setInterval(function() {
// Add next point to each trace
Plotly.extendTraces('graph', {
y: [[rand()], [rand()]]
}, [0, 1])
// Keep only 'max' most recent points
if(cnt > max) {
data[0].y.shift();
data[1].y.shift();
}
cnt = cnt+1;
if(cnt === 100) {
// Before ending the run, show all points
// to demonstrate that only 'max' points
// still exist in Plotly.
Plotly.relayout('graph', { 'xaxis.range': [0, cnt]});
clearInterval(interval);
}
}, 500);
The solution is to keep the data object in a var outside of Plotly and use shift() to drop old points from the beginning of the array as new points are added.
I'm open to another solution, especially if there are known memory or performance problems with this approach.

physics.js attractors, zero gravity and slowing down velocity

I've been experimenting with attractors in physics.js, rigging up a simple object in zero gravity, with an attractor at a point. This creates a great little 'gravity well' as can be seen here.
Where the simple square vector attracts towards a point, at 'x':200,'y':200, and then orbits around it. I'm looking for a way to turn this attractor into more of a gravity well, so that the objects attracted to it slow down over time and eventually settle static and stationary at the point of the attractor, until it was collided with or dragged with the mouse again. Is this a possibility?
Currently the object is created with:
var bodies = [Physics.body('convex-polygon', {
// place the center of the square at (0, 0)
x: 150,
treatment : 'dynamic',
cof: 0.01,
mass : 1,
y: 100,
vertices: [
{ x: 0, y: 0 },
{ x: 0, y: 200 },
{ x: 200, y: 200 },
{ x: 200, y: 0 }
]
})];
The attractor is created thusly:
var attractor = Physics.behavior('attractor', {
order: 0,
strength: 0.0005
}).applyTo(bodies);
attractor.position({'x':200,'y':200});
Affecting the strength of the attractor doesn't appear to help, it just changes the speed of the attraction and subsequent orbit.
I'm looking for a way, in effect, to add friction to the entire space, which I think will do the job in naturally slowing down the object so it ends up stationary at the attractor point. Not sure how to go about this with PhysicsJS.
There is the possibility to create your own Attractor-Behaviour: See this documentation.
If you don't want to that, you can set the option min of the attractor to the size of the polygon, so the forces stops, when the body is at the center of the attractor. Strength and order are optional options, so you don't need to specify them (According to the API). For example this should work for you:
world.add(Physics.behavior("attractor", {
min: 200,
pos: {"x": 200, "y": 200}
}));

How to create ad-hoc tweens sequence without recursion in KineticJS?

is it possible to create tweens sequence in KineticJS according to data saved in array without recursion?
This is my current solution including recursion:
function tweenFunc( image )
{
setGlobalCoordinates();
tween = new Kinetic.Tween({
node: image,
duration: 1,
x: x,
y: y,
rotation: rot,
onFinish: function(){
if ( counter++ < arr.length )
{
tweenFunc( image );
}
}
});
tween.play();
}
I would like to have solution which creates tweens before first animation and following tweens start from final position of predecessor.
This project includes animation of 4 images in the same time for many position sequence.

Multiple shape fadout with Kinetic js produces a stack overflow

Hello Stackoverflow community
As I am trying to build a small game, i came to a problem.
Somehow, when i try to fade out multiple shapes, respectivly a group with the shapes in it, some of the shapes will not fade out or browser gets a stack overflow.
So as i tried out several hours to fix the problem i need your help.
Heres the link to a little fiddle i made: http://jsfiddle.net/hnBPT/
As you can see theres a function newFadeShapesOut() which needs the nodes that should be fade out and also the layer of the nodes.
It moves the nodes into a group and fades the group out. Somehow and sometimes, one or more shapes will not fade out or there occurs a fatal error.
Function for fadeout:
function newFadeShapesOut(shapes, layer, callback, speed){
if(typeof(speed) == 'undefined'){
speed = 1;
}
var g = new Kinetic.Group();
console.log(layer.getChildren().length);
console.log(shapes.length);
layer.add(g);
shapes.each(function(shape){
shape.moveTo(g);
});
console.log(layer.getChildren().length);
console.log(shapes.length);
var tween = new Kinetic.Tween({
node: g,
opacity: 0,
duration: speed,
onFinish: function(){
if(typeof(callback) != 'undefined'){
callback();
tween.destroy();
}
}
}).play();
}
PS: Google Chrome is recommend, firefox tends to crash.
Thank you for your help.
EDIT: Sorry i forgot about that, you can activate the script by clicking the red square.
There's some strange behaviour going on here. Look at my comments as I tried to rewrite your function:
function fadeShapesOut(layer, callback, speed) {
var children = layer.children;
//The layer here already shows some children have moved.
//2 children remain, 1 text and 1 rect.
console.log("LAYER");
console.log(layer);
//Again, children shows that there are only 2 children of layer at this point: Test 2 and Button Rect
console.log('CHILDREN');
console.log(children);
if(typeof(speed) == 'undefined'){
speed = 1;
}
var group = new Kinetic.Group();
layer.add(group);
children.each(function(child) {
console.log("CHILD");
console.log(child); //This spits out Test 1, Test 3 and the newly added Group. (Strange order???
child.moveTo(group);
});
//Since group is already added to the layer, you're all of layer's children to group, including group itself. Which is causing a never ending loop of references to group including itself - causing the stack overflow.
var tween = new Kinetic.Tween({
node: group,
opacity: 0,
duration: speed,
onFinish: function(){
if(typeof(callback) != 'undefined'){
callback();
tween.destroy();
}
}
}).play();
}
What's messing you up is that the group is being considered a child of layer (Even though it hasn't been added yet in the order of function calls, which is strange behaviour to me). So when you loop through the children of layer in the each function, you're trying to move group --> group which screws up the reference in a never ending loop.
I logged a bunch of things in my fiddle, so go ahead and take a look to see some of the strange behaviour I was talking about above.
Anyways, if your callback is going to destroy the layer, what is the point of moving everything to a new group in the function? That Group is messing your code up and I don't see the point of it if you're just going to destroy the layer.
Instead you can achieve the effect you want by just tweening the layer itself:
function fadeLayer(layer, callback, speed) {
var tween = new Kinetic.Tween({
node: layer,
opacity: 0,
duration: 2,
onFinish: function(){
layer.destroy();
tween.destroy();
}
}).play();
}
If you must stick with your original function format, then you can grab children by using names:
newsobj[n] = new Kinetic.Text({
nid: n,
x: 140,
y: ((n == 0) ? 294.5 : 304.5 ),
text: news[n],
fill: 'white',
fontFamily: 'Corbel W01 Regular',
fontSize: 11.26,
name: 'fadeThisAway'
});
button = new Kinetic.Rect({
x: 10,
y: 10,
width: 100,
height: 100,
fill: 'red',
name: 'fadeThisAway'
});
In my example, I used the name fadeThisAway. And then, using your old function:
function newFadeShapesOut(layer, callback, speed){
var shapes = layer.get('.fadeThisAway');
if(typeof(speed) == 'undefined'){
speed = 1;
}
var g = new Kinetic.Group();
console.log(layer.getChildren().length);
console.log(shapes.length);
layer.add(g);
shapes.each(function(shape){
shape.moveTo(g);
});
console.log(layer.getChildren().length);
console.log(shapes.length);
var tween = new Kinetic.Tween({
node: g,
opacity: 0,
duration: speed,
onFinish: function(){
if(typeof(callback) != 'undefined'){
callback();
tween.destroy();
}
}
}).play();
}
Instead of passing shapes through the function, just call
var shapes = layer.get('.fadeThisAway');
at the beginning of the function (you're passing layer through the function already anyways) to grab the children that are named fadeThisAway. (Note: This works because the group is not named fadeThisAway)
Working example and comments inside: JSFIDDLE
UPDATE
Okay so I made a basic example of the issue with layer.children
2nd JSFIDDLE
And it looks like that's just how the children of layer works. This proves that you definitely have to distinguish between shapes and group, because the group will always be considered a child of layer.
The naming method works to distinguish your shapes between layers by giving all shapes a common name that excludes groups.
After several attempts to bend projeqht's function to my way i finally did it!
Somehow, the collection shapes just updates itself when adding the group to the layer!
If i use an array instead, it works.
Hope it helps someone!
So here my solution which works like a charm.
function fadeShapesOut(shapes, callback, speed){
layer = shapes[0].getLayer();
if(typeof(speed) == 'undefined'){
speed = 1;
}
var g = new Kinetic.Group();
layer.add(g);
for(i in shapes){
shapes[i].moveTo(g);
}
var tween = new Kinetic.Tween({
node: g,
opacity: 0,
duration: speed,
onFinish: function(){
if(typeof(callback) != 'undefined'){
callback();
}
tween.destroy();
}
}).play();
}
If you have further questions, don't mind contacting me.

Categories

Resources