I am creating a circular motion simulation, where a box of mass m hangs freely on a string. A force will then be applied to the box which will either put it in circular motion, or put it in weird motion depending on the strength of the force applied.
The rope physics works fine - at least it looks ropelike, and I used a Verlet algorithm based upon suggestions from other threads on stackoverflow and various YouTube videos after failing to apply a forces and angles based approach. This is the first time I have used this method.
The problem is that the time dependency doesn't seem to work correctly, its far too slow. I have created a stack blitz with the base code for this part of the sim. Without timeSimSpeed it looks ropey and like I would expect in real life, except far too fast.
I assumed it is something to do how I applied the force and time dependency to the sim:
processPoints2() {
for(var i = 0; i < this.points.length; i++) {
var p = this.points[i];
var timeSimSpeed = ((this.elapsedSinceFrame + this.previousElapsedSinceFrame) / 2000) * this.simulationSpeed;
if(!p.fixed) {
var vx = (p.x - p.oldx);
var vy = (p.y - p.oldy);
p.oldx = p.x;
p.oldy = p.y;
p.x += vx * timeSimSpeed;
p.y += (this.gravity + vy) * timeSimSpeed;
}
}
}
But this, logically at least, seems to make sense to me. Elasped and previousElasped are in ms and averaged which is why I have 2000 there, but even using only the current frame speed its the same outcome. I'm not familiar enough with Verlet methods to be able to work through the mathematics of whether the sticks could be restricting the motion and creating this speed issue.
Any help is much appreciated, even if it is a link to somewhere this has been addressed before that I have failed to see.
EDIT:
OK so after a bunch of comments on this I have gotten around to make some modifications. I have dropped the weird way of calculating velocity and am explicit with that now, and have also been explicit with the distance units (before it was 1 pixel per meter, but this is now defined, and I changed it to 10meters per pixel. I have also updated my stackblitz.
You will notice there are still some errors (the rope has no maximum extension and will keep stretching!) but the major one for me is that it still doesnt fall under gravity correctly...
The processing looks a lot simpler now:
processPoints2() {
for(var i = 0; i < this.points.length; i++) {
var p: point = this.points[i];
var timeSimSpeed: number = (this.elapsedSinceFrame / 1000) * this.simulationSpeed;
if(!p.fixed) {
p.vx += p.ax * timeSimSpeed;
p.vy += p.ay * timeSimSpeed;
p.x += p.vx * this.pixelsPerMeter * timeSimSpeed;
p.y += p.vy * this.pixelsPerMeter * timeSimSpeed;
}
}
}
As it just uses a classical method of calculating velocity and position.
Any help would continue to be appreciated. It has been pointed out that this isnt verlet so I have amended the title and tags to appropriately represent the question.
Related
In this codePen demo you can move "player" square with arrows, place a light with space and are supposed to be stopped going over blue lines from any direction by being pushed to the opposite direction. "player" uses x and y velocity variables to create movement and multiply them by -1 (+some value) if collision detected.
The problem is that after being pushed away from the wall "player" gets stuck in a position where only moving backward from the wall is possible while appearing stuck on a perpendicular axis to that. (for example - if the wall is on top of player you can move only to bottom and not left or right after hitting the wall)
Theoretically, I would want a smooth sliding collision detection where player stuck at the wall would slowly slide down the left or right side
depending if left or right arrow pressed. (playing around I am able to achieve this but always one direction would "flow" making player slide down certain direction) I thought about using rays or some others way to detect hits, but they seem to require more computational time than just plain approach. Would appreciate any input and any recommendations of building scalable collision detections,
Here is my basic code for movement and collision detection from the demo:
let xVelocity = 0;
let yVelocity = 0;
var blockedMapGrid = [[0,30],[0,50],[0,100],[0,150],[0,200],[0,250],
[50,0],[100,0],[150,0],[200,0],[250,0],[300,0]];
var animate = function() {
if (keyState[37]) {
xVelocity -= 1;
}
if (keyState[38]) {
yVelocity += 1;
}
if (keyState[39]) {
xVelocity += 1;
}
if (keyState[40]) {
yVelocity -= 1;
}
for (var i = 0; i < blockedMapGrid.length; i++) {
if (Math.abs(player.position.x - blockedMapGrid[i][0]) +
Math.abs(player.position.y - blockedMapGrid[i][1]) < 36) {
xVelocity = -xVelocity * 1.2;
yVelocity = -yVelocity * 1.2;
console.log("Blocked by " + blockedMapGrid[i][0])
};
}
player.position.x = player.position.x + xVelocity;
player.position.y = player.position.y + yVelocity;
yVelocity *= 0.80;
xVelocity *= 0.80;
camera.position.x = player.position.x;
camera.position.y = player.position.y;
requestAnimationFrame(animate);
renderer.render(scene, camera);
};
This part of your detector is wrong:
Math.abs(player.position.x - blockedMapGrid[i][0]) +
Math.abs(player.position.y - blockedMapGrid[i][1]) < 36
Basically, here, you approximate distance from player to the point on grid by using added absolute values instead of root of sum of squares. The truth is, you don't need such a complex grid (repeating lines) and distance.
It looks like you are doing Axis-Aligned Bounding Box (AABB) detection. There is plenty of resources on internet how to optimize it.
But general approach would be like this. Your grid array should consist of boxes with (x,y,w,h) measures. Could be thin, long, square, anything.
Let's also assume your player has a bounding box (player.x, player.y, player.w, player.h), then
for (var i = 0; i < grid.length; i++) {
if (player.x < grid[i].x + grid[i].w &&
player.x + player.w > grid[i].x &&
player.y < grid[i].y + grid[i].h &&
player.y + player.h > grid[i].y) {
//collision detected! move player to previous known position
break;
}
}
You can vary, what you do when collision is detected, but finding if two boxes overlap using 4 conditions is the key here.
Update
Another problem arising from the code in the question is "bouncing" or "getting stuck" after collision is detected.
As a rule of thumb, you should never use velocity = -velocity after collision without also making sure the character gets back into the "clear", i.e. player's bounding box is not overlapping with any obstacles. Otherwise you will be stuck in infinite loop collision? -> vel = -vel, pos += vel*t -> collision -> ... with velocity bouncing from negative to positive and back without ever allowing player to get out of the wall.
The easiest way to fix it is to calculate new position of the player in temporary variables first, check if new position is not colliding, and only then make it permanent and call render(), otherwise simply ignore it and render without moving the player.
Another way is to remember last known "good" position and only give back control of the character, when it is returned to this previous position, possibly after animation or a bunch of uncontrollable moves.
There are more elaborate ways, mostly involving some kind of physics emulation to let the character bounce of multiple obstacles, assuming control inputs do not overpower inertia - think of a car on a slippery road or a boat hitting multiple trees. But either way, after you detect collision and before calling "render()" you have to place the character to a physically possible position, or it will be famously "stuck in textures".
Good day, I am trying to create a simple 2D solar system model in javascript, but am having some trouble understanding how to go about calculating where planets will be for the next frame, aswell as a few other bits which I'll go into detail with soon.
After watching this very nice video and a whole bunch of his others, I made a quick MS paint image to try and simplify my situation.
With the second scene, you can see that the new position is calulated using the velocity, gravitational pull, and the angle between these two 'directions'?
I cannot get my head around how to figure this all out.
Below is a JS fiddle of my code. You'll notice I'm trying my best to use real NASA given data to keep it accurate.
You'll want to look specifically at lines 138 which is where all the calculations for its next move are made.
https://jsfiddle.net/c8eru7mk/9/
attraction: function(p2) {
// Distance to other body
var dx = p2.position.x - this.position.x;
var dy = p2.position.y - this.position.y;
var d = Math.sqrt(dx ** 2 + dy ** 2); // Possibly correct
// Force of attracrtion
this.f = G * (this.mass * p2.mass) / (d ** 2); // Possibly Correct
// Direction of force, If you read it hard enough you should be able to hear my screams of pain
// Not sure if this is correct, most likely not.
var theta = Math.atan2(dy, dx);
var fx = Math.cos(theta) * this.f;
var fy = Math.sin(theta) * this.f;
this.velocity.x += fx / this.mass;
this.velocity.y += fy / this.mass;
this.position.x += this.velocity.x;
this.position.y += this.velocity.y;
}
The problems I'm currently facing are
If I am to use NASA values, the distance between planets is so big, they won't fit on the screen, and I can't simply scale the distances down by multiplying them by 0.0002 or whatever, as that'll mess with the gravitational constant, and the simulation will be completely off.
I have no idea how to caluclate the next position and my brain has imploded several times this past week trying to attempt it several times.
I have no idea on how to check if my configuration data of planets is wrong, or if the simulation is wrong, so I'm pretty much just guessing.
This is also my first time actually coding anything more complex than a button in javascript too, so feedback on code layout and whatnot is welcome!
Many thanks
Using NASA values is not a problem when using separate coordinates for drawing. Using an appropriate linear transfomration from real coordinates to screen coordinatees for displaying does not influence the physical values and computations.
For simulating the motion of a planet with iterative updates one can assume that the gravitational force and the velocity are constant for a small portion of time dt. This factor dt is missing in your conversions from accelration to velocity and from velocity to distance. Choosing an appropriate value for dt may need some experiments. If the value is too big the approximation will be too far off from reality. If the value is too small you may not see any movement or rounding errors may influence the result.
For the beginning let us assume that the sun is always at (0,0). Also for a start let us ignore the forces between the planets. Then here are the necessary formulas for a first not too bad approximation:
scalar acceleration of a planet at position (x,y) by the gravitational force of the sun (with mass M): a = G*M/(d*d) where d=sqrt(x*x+y*y). Note that this is indepent of the planet's mass.
acceleration vector: ax = -a*x/d, ay = -a*y/d (the vector (-x,-y) is pointing towards the sun and must be brought the length a)
change of the planet's velocity (vx,vy): vx += ax*dt, vy += ay*dt
change of the planet's position: x += vx*dt, y += vy*dt
First of all, I'm trying to make a "simple" 3D game using Three.js and in the future some network framework to make it multiplayer, since I plan on doing the network part in the future I searched a little and discovered that most "action" game use a "tick" based game loop to make it possible to sync the clients and the server, and then interpolate between the ticks to make it smooth.
I already have some "working" code of the tick (handle input, update, draw) function, what I want to know is if my implementation is right, and how this "deterministic" loop should work, supposing that my implementation is working, when I increase the "tick rate" the game gets faster (the update function is running more times), is this right?
this.loops = 0;
this.tick_rate = 20;
this.skip_ticks = 1000 / this.tick_rate;
this.max_frame_skip = 10;
this.next_game_tick = performance.now();
This first part of the code is inside the constructor of the Game class
Game.prototype.run = function () {
this.handle_input();
this.loops = 0;
while (performance.now() > this.next_game_tick && this.loops < this.max_frame_skip){
this.up_stats.update();
this.update();
this.next_game_tick += this.skip_ticks;
this.loops++;
}
this.draw();
//monitor performance
this.stats.update();
//next update
requestAnimationFrame(this.run.bind(this));
};
Full code at: https://github.com/derezzedex/first_three_js/blob/master/js/game/main.js
This looks pretty reasonable to me and I've used similar patterns in the past.
Structuring synchronized simulations is a huge topic, but what you have is a good starting point and might be enough depending on the complexity of your game.
edit: A bit more detail...
Yes it works the same, except that this.dt is always the same. i.e. 1000 / your desired FPS for the game loop.
If you want to do the smoothing/interpolation in between frames... you'll have to record the previous state of your object as well.. and you probably won't want to use the Euler rotations, since eulers don't interpolate well. because an angle of 360 degrees, flips back to 0, so the interpolation logic gets weird...
But instead.. you can record the state before and after the update...
and interpolate the .quaternion instead.. which for small changes in rotation works fine just linear interpolating .. If the changes are too big, you can use quaternion.slerp() which can handle interpolating over big distances.
So you've got lastTickTime, and currentTime, and nextTickTime ....
each frame.. you're doing something like:
To interpolate you do something like:
var alpha= (currentTime-lastTickTime) / (nextTickTime-lastTickTime);//nextTickTime-lastTickTime = your framerate delta so for 60fps = 1000/60 = 16.666666666666668
var recip = 1.0 - alpha;
object.position.x = (object.lastPosition.x * recip)+(object.nextPosition.x*alpha)
object.position.y = (object.lastPosition.y * recip)+(object.nextPosition.y*alpha)
object.position.z = (object.lastPosition.z * recip)+(object.nextPosition.z*alpha)
object.scale.x = (object.lastScale.x * recip)+(object.nextScale.x*alpha)
object.scale.y = (object.lastScale.y * recip)+(object.nextScale.y*alpha)
object.scale.z = (object.lastScale.z * recip)+(object.nextScale.z*alpha)
object.quaternion.x = (object.lastQuaternion.x * recip)+(object.nextQuaternion.x*alpha)
object.quaternion.y = (object.lastQuaternion.y * recip)+(object.nextQuaternion.y*alpha)
object.quaternion.z = (object.lastQuaternion.z * recip)+(object.nextQuaternion.z*alpha)
object.quaternion.w = (object.lastQuaternion.w * recip)+(object.nextQuaternion.w*alpha)
In a proper three app, you probably shouldn't store the lastPosition and nextPosition directly on the object, and instead put it in the object.userData, but whatever.. it will probably still work..
EDIT5: Finally got Angus Johnson's Clipper library implemented in Javascript and selected Sourceforge for host.
LIVE DEMO: http://jsclipper.sourceforge.net/6.1.1.1/main_demo.html
Download source:
https://sourceforge.net/projects/jsclipper/
Wikipage with step-by-step tutorial:
https://sourceforge.net/p/jsclipper/wiki/Home%206/
Presentation of Demo Program including tens of sample polygons:
https://sourceforge.net/p/jsclipper/wiki/Main_Demo%206/
I hope this helps anyone who needs polyline and polygon clipping library with offsetting features.
EDIT4: The one possibility is to convert pascal to javascript using
http://p2js.gelicon.biz/en/ . Not succeeded yet. p2js.exe clipper.pas gives Fatal error "Can't find unit system used by clipper".
EDIT: I found script# (Github), which seems to be able to convert C# to Javascript. Clipper lib is available in C#, so would it be possible to make C#->JS conversion using Script# and how?
EDIT3: Got not converted with script#, but there is also Emscripten, but 4000 cpp lines converted to 300 000 Javascript lines, so not an option. Manual conversion seems to be a king.
EDIT2: I made an example, which shows the problem. Use arrow left and right to apply offset. In certain distance it works ok, but then something goes wrong. The yellow stroked polygon is so called raw offset polygon, and AFAIK Clipper lib provides a way to take care of removing unneeded parts of raw offset polygon.
There is a Clipper library by Angus Johnson for offsetting polygons.
I'd need this functionality in Javascript for offsetting SVG polygons.
Has someone made a Javascript port of it?
If not, I'd appreciate some guidelines eg. the following:
- how enormous task it would be?
- which one to choose for source (Delphi, C#, C++)?
- is everything in lib needed for offsetting?
The Clipper library produces the following results which are just the desired functionality:
Some links:
- Files in Sourceforge
- Clipper Documentation
- One Stackoverflow answer
- Offsetting algorithm
I have succeded in porting clipper to JS, and after a while, after thorough testing going to release it. Seems that all the functionality could have been ported.
One caveat, 128bit support is reduced to 106bit.
One of the pros is to reach large space of browsers and possibility to use svg, vml, html5 canvas as graphics interface.
Any ideas, Which host would be easiest to publish, with demo possibility?
EDIT:
Finally got Angus Johnson's Clipper library implemented in Javascript and selected Sourceforge for host.
LIVE DEMO:
http://jsclipper.sourceforge.net/6.1.1.1/main_demo.html
Downloads:
https://sourceforge.net/projects/jsclipper/
Wikipage with step-by-step tutorial:
https://sourceforge.net/p/jsclipper/wiki/Home%206/
Presentation of Demo Program including tens of sample polygons:
https://sourceforge.net/p/jsclipper/wiki/Main_Demo%206/
I hope this helps anyone who needs polyline and polygon clipping library with offsetting features.
There are no simple solutions when it comes to polygon inflating. If you have a concave polygon, sooner or later it will break into several smaller polygons if you decrease the offset enough. So I would suggest using an existing, proven, algorithm (Clipper should be a good one).
On your question about porting C# to JS, I would say it's certainly possible, but the question is how much time it would take and whether the auto-porting tools will be of any use. Judging from this discussion, I doubt it:
I took a quick stab at using ScriptSharp to translate the C# code to
Javascript, but there are too many incompatible structures to use that
and I couldn't get it to output a javascript file. Trying to implement
the Vatti clipping algorithm in Javascript seems to be the next step.
...
And yes, it won't help you using all sorts of automatics conversion
tools.The clipper has data structures like Int64 or Int128 which are
non existent in JS or AS .I just removed them altogether .Int32 should
be enough for most cases unless you work on smth related to geography
or huge maps .
The ActionScript port one of the users there mentions is no longer available, unfortunately.
function offsetPoints(pts, offset) {
let newPoints = [];
for (let j = 0; j < pts.length; j++) {
let i = (j - 1);
if (i < 0) i += pts.length;
let k = (j + 1) % pts.length;
let v1 = [pts[j].x - pts[i].x, pts[j].y - pts[i].y];
let mag1 = Math.sqrt(v1[0] * v1[0] + v1[1] * v1[1])
v1 = [v1[0] / mag1, v1[1] / mag1]
v1 = [v1[0] * offset, v1[1] * offset]
let n1 = [-v1[1], v1[0]];
let x1 = pts[i].x + n1[0];
let y1 = pts[i].y + n1[1];
let x2 = pts[j].x + n1[0];
let y2 = pts[j].y + n1[1];
let v2 = [pts[k].x - pts[j].x, pts[k].y - pts[j].y];
let mag2 = Math.sqrt(v2[0] * v2[0] + v2[1] * v2[1])
v2 = [v2[0] / mag2, v2[1] / mag2]
v2 = [v2[0] * offset, v2[1] * offset]
let n2 = [-v2[1], v2[0]];
let x3 = pts[j].x + n2[0];
let y3 = pts[j].y + n2[1];
let x4 = pts[k].x + n2[0];
let y4 = pts[k].y + n2[1];
let den = ((y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1));
let ua = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / den;
let x = x1 + ua * (x2 - x1);
let y = y1 + ua * (y2 - y1);
newPoints.push({ x, y });
}
return newPoints;
}
I am simulating a page turn effect in html5 canvas.
On each page I am drawing lines to simulate lined paper.
These lines are drawn as the page is turned and in order to give natural perspective I am drawing them using quadratic curves based of several factors (page turn progress, closeness to the center of the page etc.. etc...)
The effect is very natural and looks great but I am looking for ways to optimize this.
Currently I am drawing every line twice, once for the actual line and once for a tiny highlight 1px below this line. I am doing this like so:
// render lines (shadows)
self.context.lineWidth = 0.35;
var midpage = (self.PAGE_HEIGHT)/2;
self.context.strokeStyle = 'rgba(0,0,0,1)';
self.context.beginPath();
for(i=3; i < 21; i++){
var lineX = (self.PAGE_HEIGHT/22)*i;
var curveX = (midpage - lineX) / (self.PAGE_HEIGHT);
self.context.moveTo(foldX, lineX);
self.context.quadraticCurveTo(foldX, lineX + ((-verticalOutdent*4) * curveX), foldX - foldWidth - Math.abs(offset.x), lineX + ((-verticalOutdent*2) * curveX));
}
self.context.stroke();
// render lines (highlights)
self.context.strokeStyle = 'rgba(255,255,255,0.5)';
self.context.beginPath();
for(i=3; i < 21; i++){
var lineX = (self.PAGE_HEIGHT/22)*i;
var curveX = (midpage - lineX) / (self.PAGE_HEIGHT);
self.context.moveTo(foldX, lineX+2);
self.context.quadraticCurveTo(foldX, lineX + ((-verticalOutdent*4) * curveX) + 1, foldX - foldWidth - Math.abs(offset.x), lineX + ((-verticalOutdent*2) * curveX) + 1);
}
self.context.stroke();
As you can see I am opening a path, looping through each line, then drawing the path. Then I repeat the whole process for the 'highlight' lines.
Is there any way to combine both of these operations into a single loop without drawing each line individually within the loop which would actually be far more expensive?
This is a micro-optimization, I am well aware of this. However this project is a personal exercise for me in order to learn html5 canvas performance best practices/optimizations.
Thanks in advance for any suggestions/comments
Paths can be stroked as many times as you like, they're not cleared when you call .stroke(), so:
create your path (as above)
.stroke() it
translate the context
change the colours
.stroke() it again
EDIT tried this myself - it didn't work - the second copy of the path didn't notice the translation of the coordinate space :(
It apparently would work if the path was created using new Path() as documented in the (draft) specification instead of the "current default path" but that doesn't appear to be supported in Chrome yet.