I have many particles moving around and hit detection to see when they touch. If two particles touch they should bounce off in the opposite direction.
particle.moveSelf = function() {
var radians = this.angle * Math.PI / 180;
var moveX = this.velocity * Math.sin(radians);
var moveY = this.velocity * Math.cos(radians);
for (var i = 0; i < particles.length; i++) {
var distance = this.position.getDistance(new Point(particles[i].position.x, particles[i].position.y));
//if distance < radius 1 + radius 2, bounce this circle away
if (distance < (this.radius + particles[i].radius) && this.id !== particles[i].id) {
this.velocity = -this.velocity;
}
}
this.position.x += moveX;
this.position.y += moveY;
};
When I run this code, the circles get stuck in each other moving back and forth by 1*velocity every frame.
There are lots of questions on how to work out the velocity or angle of the bounce but my problem is just that it gets stuck in an infinite oscillation.
I done this before so here are some insights:
the safest way is add list of collisions per each particle
in first pass you just detect the collisions
and clear/fill the collision lists with indexes of all particles that collide to each other
for (i=0;i<particles;i++) particle[i].collide.clear();
for (i=0;i<particles;i++)
for (j=i+1;j<particles;j++)
if (colide(particle[i],particle[j]))
{
particle[i].collide.add(j);
particle[j].collide.add(i);
}
you can also do this with one list inside for (i=0;i<...) loop instead
but that is not that precise
update position and speed of collided items only
now comes the tricky part
I add new direction vector to each particle and set it to zero
then add in each collision a reflection (unit or speed or force impulse) vector to it
for (i=0;i<particles;i++)
for (j=0;j<particle[i].collides;j++)
{
// here compute reflected direction ,speed whatever of the bounce to vector dir
particle[ i].reflect+=dir;
particle[particle[i].collide[j]].reflect+=dir;
}
when it is done then just update the positions/speed ...
but you need add the stuff needed for update for example
if you used unit vectors then normalize reflect, and set it size to new speed
the same goes for displacement so the particles do not overlap
you can also compute the combined kinetic energy vector of all bounced particles and compute the new speeds form it via mass energy distribution
for (i=0;i<particles;i++)
{
particle[i].speed=compute_speed_from(particle[i].reflect);
}
[Notes]
the best is to store driving reflection force
it is easy to implement physics simulation of bounce properly on it
and also for handling springs and other stuff
if you have permanent bonds then you can have them precomputed once in some permanent collide lists
to improve performance
bullet 2 can be used without collide list but then it is not so precise for multiple bounces at once
Related
I have a Mesh created with a BufferGeometry.
I also have the coordinates of where my mouse intersects the Mesh, using the Raycaster.
I am trying to detect faces within(and touching) a radius from the intersection point.
Once I detect the "tangent" faces, I then want to color the faces. Because I am working with a BufferGeometry, I am manipulating the buffer attributes on my geometry.
Here is my code:
let vertexA;
let vertexB;
let vertexC;
let intersection;
const radius = 3;
const color = new THREE.Color('red');
const positionsAttr = mesh.geometry.attributes.position;
const colorAttr = mesh.geometry.attributes.color;
// on every mouseMove event, do below:
vertexA = new THREE.Vector3();
vertexB = new THREE.Vector3();
vertexC = new THREE.Vector3();
intersection = raycaster.intersectObject(mesh).point;
// function to detect tangent edge
function isEdgeTouched(v1, v2, point, radius) {
const line = new THREE.Line3();
const closestPoint = new THREE.Vector3();
line.set(v1, v2);
line.closestPointToPoint(point, true, closestPoint);
return point.distanceTo(closestPoint) < radius;
}
// function to color a face
function colorFace(faceIndex) {
colorAttr.setXYZ(faceIndex * 3 + 0, color.r, color.g, color.b);
colorAttr.setXYZ(faceIndex * 3 + 0, color.r, color.g, color.b);
colorAttr.setXYZ(faceIndex * 3 + 0, color.r, color.g, color.b);
colorAttr.needsUpdate = true;
}
// iterate over each face, color it if tangent
for (let i=0; i < (positionsAttr.count) /3); i++) {
vertexA.fromBufferAttribute(positionsAttr, i * 3 + 0);
vertexB.fromBufferAttribute(positionsAttr, i * 3 + 1);
vertexC.fromBufferAttribute(positionsAttr, i * 3 + 2);
if (isEdgeTouched(vertexA, vertexB, point, radius)
|| isEdgeTouched(vertexA, vertexB, point, radius)
|| isEdgeTouched(vertexA, vertexB, point, radius)) {
colorFace(i);
}
While this code works, it seems to be very poor in performance especially when I am working with a geometry with many many faces. When I checked the performance monitor on Chrome DevTools, I notices that both the isEdgeTouched and colorFace functions take up too much time on each iteration for a face.
Is there a way to improve this algorithm, or is there a better algorithm to use to detect adjacent faces?
Edit
I got some help from the THREE.js slack channel, and modified the algorithm to use Three's Sphere. I am now no longer doing "edge" detection, but instead checking whether a face is within the Sphere
Updated code below:
const sphere = new THREE.Sphere(intersection, radius);
// now checking if each vertex of a face is within sphere
// if all are, then color the face at index i
for (let i=0; i < (positionsAttr.count) /3); i++) {
vertexA.fromBufferAttribute(positionsAttr, i * 3 + 0);
vertexB.fromBufferAttribute(positionsAttr, i * 3 + 1);
vertexC.fromBufferAttribute(positionsAttr, i * 3 + 2);
if (sphere.containsPoint(vertexA)
&& sphere.containsPoint(vertexA)
&& sphere.containsPoint(vertexA)) {
colorFace(i);
}
When I tested this in my app, I noticed that the performance has definitely improved from the previous version. However, I am still wondering if I could improve this further.
This seem to be a classic Nearest Neighbors problem.
You can narrow the search by finding the nearest triangles to a given point very fast by building a Bounding Volume Hierarchy (BVH) for the mesh, such as the AABB-tree.
BVH:
https://en.m.wikipedia.org/wiki/Bounding_volume_hierarchy
AABB-Tree:
https://www.azurefromthetrenches.com/introductory-guide-to-aabb-tree-collision-detection/
Then you can query against the BVH a range query using a sphere or a box of a given radius. That amounts to traverse the BVH using a sphere/box "query" which is used to discard quickly and very early the Bounding Volume Nodes that does not clip the sphere/box "query". At the end the real distance or intersection test is made only with triangles whose BV intersect the sphere/box "query", typically a very small fraction of the triangles.
The complexity of the query against the BVH is O(log n) in contrast with your approach which is O(n).
So I want a snappy movement for my player. Right now my code looks like
move() {
var vel = this.body.GetLinearVelocity()
if(!this.pressingDown && !this.pressingUp){
vel.y = 0;
}
if(!this.pressingRight && !this.pressingRight){
vel.x = 0;
}
if(this.pressingDown){
vel.y = this.speed;
}
if(this.pressingUp){
vel.y = -this.speed;
}
if(this.pressingRight){
vel.x = this.speed;
}
if(this.pressingLeft){
vel.x = -this.speed
}
this.body.SetLinearVelocity(vel)
and this works but when I'm moving diagnolly the player is moving faster than the max speed. How do I fix this?
Determine the directional unit vector and then multiply it by this.speed. That way the magnitude of the velocity is always this.speed. Otherwise, as you discovered, your speed may be sqrt(this.speed * this.speed * 2) instead of just this.speed.
A way to determine this directional unit vector would be to recognize the angle you want to move at based on the keys pressed and then getting the sine and cosine values for that angle. So when this.pressingRight, the angle is 0. When this.pressingUp, the angle is 90 degrees (or Pi/2 radians). Or when this.pressingUp && this.pressingRight, the angle is 45 degrees (Pi/4 radians). Just complete the if-statement for all serviceable combinations. Perhaps put that in its own function called something like getAngleInRadiansForKeyPresses.
The implementation (in pseudo-javascript-code) might then look something like:
move() {
var angle = getAngleInRadiansForKeyPresses();
var vel = new b2Vec2(Math.cos(angle) * this.speed, Math.sin(angle) * this.speed);
this.body.SetLinearVelocity(vel);
}
I'm trying to make a game with top-down perspective where the player sprite is fixed in the center of screen and the background is moving (scrolling) in the direction opposite to the player's direction, so it results in effect of the player's movement.
I started with these examples: Invaders (for background scrolling) and Asteroids Movement (for sprite movement). The former uses TileSprite and its tilePosition property to make vertical scrolling of the background. This works good for linear scrolling in fixed direction (vertical or horizontal). But in my case I need implement scrolling (i.e. movement) in any direction. Furthermore I need such physics features as acceleration and drag, like in the Asteroids example. So I would like to apply Arcade Physics to TileSprite. But it seems that Arcade Physics doesn't work with TileSprite. Or, to be more precise, it doesn't work as I expected.
I've tried to enable Arcade Physics for TileSprite as it used with Sprites. Here is the code:
function preload() {
game.load.baseURL = 'http://examples.phaser.io/assets/';
game.load.crossOrigin = 'anonymous';
game.load.image('ship', 'games/invaders/player.png');
game.load.image('starfield', 'games/invaders/starfield.png');
}
var player;
var cursors;
var starfield;
var playerAngle = 0; // angle of the player's movement, in degrees
var playerSpeed = 5; // the player's speed, px per frame
function create() {
game.physics.startSystem(Phaser.Physics.ARCADE);
// The scrolling starfield background
starfield = game.add.tileSprite(0, 0, 800, 600, 'starfield');
game.physics.enable(starfield, Phaser.Physics.ARCADE);
starfield.body.velocity.set(200);
// The player sprite
player = game.add.sprite(400, 300, 'ship');
player.anchor.setTo(0.5, 0.5);
// Gameplay controls
cursors = game.input.keyboard.createCursorKeys();
}
function update() {
// Scroll the background
var delta = 0;
if (cursors.left.isDown)
delta = -1;
else if (cursors.right.isDown)
delta = 1;
if (delta)
player.angle = playerAngle = (playerAngle + delta + 360) % 360;
var a = (playerAngle + 90) / 360 * 2 * Math.PI; // angle of the background movement/scrolling, in radians
// move the background by playerSpeed along the a angle
game.physics.arcade.velocityFromRotation(a, 200, starfield.body.velocity);
}
Try at Phaser sandbox
Unfortunately this code doesn't work as I expected.
As far as I understood and as written in the docs, Arcade Physics is only intended for work with Sprite, not TileSprite nor any other game object. If so, this means that I have to manually reproduce all the Physics stuff such as acceleration and drag myself with respect to TileSprite. I've written simple TileSprite movement with constant velocity (no acceleration, drag and other stuff). Here is the code:
function preload() {
game.load.baseURL = 'http://examples.phaser.io/assets/';
game.load.crossOrigin = 'anonymous';
game.load.image('ship', 'games/invaders/player.png');
game.load.image('starfield', 'games/invaders/starfield.png');
}
var player;
var cursors;
var starfield;
var playerAngle = 0; // angle of the player's movement, in degrees
var playerSpeed = 5; // the player's speed, px per frame
function create() {
game.physics.startSystem(Phaser.Physics.ARCADE);
// The scrolling starfield background
starfield = game.add.tileSprite(0, 0, 800, 600, 'starfield');
// The player sprite
player = game.add.sprite(400, 300, 'ship');
player.anchor.setTo(0.5, 0.5);
// Gameplay controls
cursors = game.input.keyboard.createCursorKeys();
}
function update() {
// Scroll the background
var delta = 0;
if (cursors.left.isDown)
delta = -1;
else if (cursors.right.isDown)
delta = 1;
if (delta)
player.angle = playerAngle = (playerAngle + delta + 360) % 360;
var a = (playerAngle + 90) / 360 * 2 * Math.PI; // angle of the background movement/scrolling, in radians
// move the background by playerSpeed along the a angle
starfield.tilePosition.x += playerSpeed * Math.cos(a);
starfield.tilePosition.y += playerSpeed * Math.sin(a);
}
Try at Phaser sandbox
Sure it works, however even such simple thing as linear motion require some math knowledge. Other things will require much more knowledge of math and physics. I would not like to implement such things manually.
So is there any way to use Phaser Physics in conjunction with tilePosition?
My suggestion is to rethink the whole thing. Main idea is to not make the player sprite fixed, but to make it "dynamic" with physics enabled. To have your desired effect, just make the camera follow the player sprite (with no deadzone). That way, the player remains in the center, and manage the tilesprite classically.
I'd like to throw a ball (with an image) into a 2d scene and check it for a collision when it reached some distance. But I can't make it "fly" correctly. It seems like this has been asked like a million times, but with the more I find, the more confused I get..
Now I followed this answer but it seems, like the ball behaves very different than I expect. In fact, its moving to the top left of my canvas and becoming too little way too fast - ofcouse I could adjust this by setting vz to 0.01 or similar, but then I dont't see a ball at all...
This is my object (simplyfied) / Link to full source who is interested. Important parts are update() and render()
var ball = function(x,y) {
this.x = x;
this.y = y;
this.z = 0;
this.r = 0;
this.src = 'img/ball.png';
this.gravity = -0.097;
this.scaleX = 1;
this.scaleY = 1;
this.vx = 0;
this.vy = 3.0;
this.vz = 5.0;
this.isLoaded = false;
// update is called inside window.requestAnimationFrame game loop
this.update = function() {
if(this.isLoaded) {
// ball should fly 'into' the scene
this.x += this.vx;
this.y += this.vy;
this.z += this.vz;
// do more stuff like removing it when hit the ground or check for collision
//this.r += ?
this.vz += this.gravity;
}
};
// render is called inside window.requestAnimationFrame game loop after this.update()
this.render = function() {
if(this.isLoaded) {
var x = this.x / this.z;
var y = this.y / this.z;
this.scaleX = this.scaleX / this.z;
this.scaleY = this.scaleY / this.z;
var width = this.img.width * this.scaleX;
var height = this.img.height * this.scaleY;
canvasContext.drawImage(this.img, x, y, width, height);
}
};
// load image
var self = this;
this.img = new Image();
this.img.onLoad = function() {
self.isLoaded = true;
// update offset to spawn the ball in the middle of the click
self.x = this.width/2;
self.y = this.height/2;
// set radius for collision detection because the ball is round
self.r = this.x;
};
this.img.src = this.src;
}
I'm also wondering, which parametes for velocity should be apropriate when rendering the canvas with ~ 60fps using requestAnimationFrame, to have a "natural" flying animation
I'd appreciate it very much, if anyone could point me to the right direction (also with pseudocode explaining the logic ofcourse).
Thanks
I think the best way is to simulate the situation first within metric system.
speed = 30; // 30 meters per second or 108 km/hour -- quite fast ...
angle = 30 * pi/180; // 30 degree angle, moved to radians.
speed_x = speed * cos(angle);
speed_y = speed * sin(angle); // now you have initial direction vector
x_coord = 0;
y_coord = 0; // assuming quadrant 1 of traditional cartesian coordinate system
time_step = 1.0/60.0; // every frame...
// at most 100 meters and while not below ground
while (y_coord > 0 && x_coord < 100) {
x_coord += speed_x * time_step;
y_coord += speed_y * time_step;
speed_y -= 9.81 * time_step; // in one second the speed has changed 9.81m/s
// Final stage: ball shape, mass and viscosity of air causes a counter force
// that is proportional to the speed of the object. This is a funny part:
// just multiply each speed component separately by a factor (< 1.0)
// (You can calculate the actual factor by noticing that there is a limit for speed
// speed == (speed - 9.81 * time_step)*0.99, called _terminal velocity_
// if you know or guesstimate that, you don't need to remember _rho_,
// projected Area or any other terms for the counter force.
speed_x *= 0.99; speed_y *=0.99;
}
Now you'll have a time / position series, which start at 0,0 (you can calculate this with Excel or OpenOffice Calc)
speed_x speed_y position_x position_y time
25,9807687475 14,9999885096 0 0 0
25,72096106 14,6881236245 0,4286826843 0,2448020604 1 / 60
25,4637514494 14,3793773883 0,8530785418 0,4844583502 2 / 60
25,2091139349 14,0737186144 1,2732304407 0,7190203271
...
5,9296028059 -9,0687933774 33,0844238036 0,0565651137 147 / 60
5,8703067779 -9,1399704437 33,1822622499 -0,0957677271 148 / 60
From that sheet one can first estimate the distance of ball hitting ground and time.
They are 33,08 meters and 2.45 seconds (or 148 frames). By continuing the simulation in excel, one also notices that the terminal velocity will be ~58 km/h, which is not much.
Deciding that terminal velocity of 60 m/s or 216 km/h is suitable, a correct decay factor would be 0,9972824054451614.
Now the only remaining task is to decide how long (in meters) the screen will be and multiply the pos_x, pos_y with correct scaling factor. If screen of 1024 pixels would be 32 meters, then each pixel would correspond to 3.125 centimeters. Depending on the application, one may wish to "improve" the reality and make the ball much larger.
EDIT: Another thing is how to project this on 3D. I suggest you make the path generated by the former algorithm (or excel) as a visible object (consisting of line segments), which you will able to rotate & translate.
The origin of the bad behaviour you're seeing is the projection that you use, centered on (0,0), and more generally too simple to look nice.
You need a more complete projection with center, scale, ...
i use that one for adding a little 3d :
projectOnScreen : function(wx,wy,wz) {
var screenX = ... real X size of your canvas here ... ;
var screenY = ... real Y size of your canvas here ... ;
var scale = ... the scale you use between world / screen coordinates ...;
var ZOffset=3000; // the bigger, the less z has effet
var k =ZOffset; // coeficient to have projected point = point for z=0
var zScale =2.0; // the bigger, the more a change in Z will have effect
var worldCenterX=screenX/(2*scale);
var worldCenterY=screenY/(2*scale);
var sizeAt = ig.system.scale*k/(ZOffset+zScale*wz);
return {
x: screenX/2 + sizeAt * (wx-worldCenterX) ,
y: screenY/2 + sizeAt * (wy-worldCenterY) ,
sizeAt : sizeAt
}
}
Obviously you can optimize depending on your game. For instance if resolution and scale don't change you can compute some parameters once, out of that function.
sizeAt is the zoom factor (canvas.scale) you will have to apply to your images.
Edit : for your update/render code, as pointed out in the post of Aki Suihkonen, you need to use a 'dt', the time in between two updates. so if you change later the frame per second (fps) OR if you have a temporary slowdown in the game, you can change the dt and everything still behaves the same.
Equation becomes x+=vx*dt / ... / vx+=gravity*dt;
you should have the speed, and gravity computed relative to screen height, to have same behaviour whatever the screen size.
i would also use a negative z to start with. to have a bigger ball first.
Also i would separate concerns :
- handle loading of the image separatly. Your game should start after all necessary assets are loaded. Some free and tiny frameworks can do a lot for you. just one example : crafty.js, but there are a lot of good ones.
- adjustment relative to the click position and the image size should be done in the render, and x,y are just the mouse coordinates.
var currWidth = this.width *scaleAt, currHeight= this.height*scaleAt;
canvasContext.drawImage(this.img, x-currWidth/2, y-currHeight/2, currWidth, currHeight);
Or you can have the canvas to do the scale. bonus is that you can easily rotate this way :
ctx.save();
ctx.translate(x,y);
ctx.scale(scaleAt, scaleAt); // or scaleAt * worldToScreenScale if you have
// a scaling factor
// ctx.rotate(someAngle); // if you want...
ctx.drawImage(this.img, x-this.width/2, x-this.height/2);
ctx.restore();
I've tried a setInterval loop with css and animate. Both ways of movement consists of tiny movement from oldpos1 -> newpos1 with no random curve movement, easing however occured with jQuery animate but only between randomly generated 1-3 pixels, which is not what I want
.
Does the problem lies in setInterval's clock, where only linear time units flow?
Where should I start, to make below images exist in jQuery?
What I would like to do:
Dodge behaviour:
A, B - particle
AB1 - common dodge area, only certain amount
2 Movement:
Av, Bv - random circular movement
Aacc, Bacc - where the tiny random acceleration occurs (on image marked as more condenced dashed lines)
I would not rely on jQuery's animate for this as your case is rather special ... instead, use the "game loop pattern": Have a game object which keeps a collection of particles, which are moved (and collided ...) and then drawn in regular intervals.
Here's a basic structure:
function Particle(x, y) {
this.x = x;
this.y = y;
this.speed = 0; // in pixels per second
this.direction = 0; // in radians per second
}
Particle.prototype.move = function(d_time) {
this.x += Math.cos(this.direction) * this.speed;
this.y += Math.sin(this.direction) * this.speed;
}
Particle.prototype.draw = function() {
// either set the position of a DOM object belonging to this particle
// or draw to a canvas
}
function Game() {
this.particles = Array();
this.MS_PER_FRAME = 20; // in milliseconds
this.D_TIME = 1000.0 / this.MS_PER_FRAME;
}
Game.prototype.tick = function() {
$.each(this.particles, function(_, particle) {
particle.move(this.D_TIME);
particle.draw();
})
}
Game.prototype.go = function() {
setInterval(this.tick, this.MS_PER_FRAME)
})
Then you can manipulate speed and direction of particles as you like, maybe by introducing additional members d_speed (acceleration) and d_direction or so.