I have attempted to make an agar.io clone using javaScript along with p5js. Here's the link on github for the full source of this project. Now, I've got the basic gist of the game, but now I'm trying to add an eject/split (In the game, by pressing W an ejection happens). But the ejection/w, is not working correctly— Basically, it's firing the w in the wrong direction, usually close to the blob. Here's the part for the ejection(W)'s position relative to the blob's position (the position where the W is fired, [should fire towards the direction of the mouse]):
this.update = function() {
let newvel = createVector(mouseX-width/2, mouseY-height/2);
newvel.setMag(speed);
this.vel.lerp(newvel, 0.2);
this.pos.add(this.vel);
}
let pos = createVector(mouseX - (this.pos.x + this.vel.x), mouseY - (this.pos.y + this.vel.y));
Don't worry about the few extra variables/uncalled functions (lerp, createVector, setMag, & add are all p5JS functions), the code is too long so I only put the part with the update function and the "setting the W's position" part of the code. "this.vel" is basically the velocity, so I included it in the W's new position, because as the blob is moving, the W might be eaten instantly.
But it still fires W the wrong direction.... Really need to fix this ...
I tried everything like
pos = createVector(mouseX - width/2, mouseY - height/2);
But nevertheless, it still fires the W the wrong direction. Unfortunately, I'm not really sure how to approach this the logical way :( NOTE:
I have also used the translate function to centre the blobs from initial position (0, 0)to translate(width / 2, height / 2)
The way you wrote it, it won't hurt to initialize the ejected blob at mouseX and mouseY with a translation of n-displacement, along with the constrain function limiting the blob's position on the current viewport of the map, subtracted by the distance between the blob and the cursor (by using the dist function). If you would like the animation process to be smoother, and slightly more accurate curvature-wise you can use the lerp function on top of it. Figuring out how to order these steps is simple. First get the regular plane coordinate working by translating before rendering, then integrating the curves using lerp to avoid omitting accuracy (after the calculations and constrains have been made).
Related
Every element from my scene is made of a chain of 3 Object3Ds. The order parent-to-child is cellPivot -> modifier -> setup
setup's purpose is to permanently align a loaded object by resizing / giving some padding that must always be there. It is not supposed to be changed once set
modifier's purpose is to actually perform the real transformation on the object
cellPivot's purpose is to allow me to drag modifier into a cell grid
An example why all this is needed: let's say I have a vertical door in an orthographic perspective that I wanna fit in a 1x1 space, so I give some padding on the x-axis to align the door in the center, similar to the picture below where the orange block is the door
Since I want to move this in any cell in the map, I use cellPivot's position to decide where. I can't use right away modifier since sometimes I wanna rotate the model inside the cell, which requires to modify both position and rotation (since my models are not built around (0, 0, 0), but along +X and +Z)
I have succesfully managed to rotate these doors by rotating modifier around the center of the model (which acts as a pivot). Here's the functions that does the rotation:
three.Object3D.prototype.pivot = function(pivot, f) {
pivot = lib.VecToVector3(three, pivot); // just a conversion between libs
this.position.sub(pivot);
f(this);
this.position.add(pivot);
return this;
};
three.Object3D.prototype.pivotRotate = function(pivot, axis, theta, rotational = false, abs = false) {
if(abs)
theta -= this.rotation.y; /// not good, handles only y
this.pivot(pivot, () => this.position.applyAxisAngle(axis, theta));
if(rotational)
this.rotateOnAxis(axis, theta);
return this;
};
The line that rotates the door and works:
this.o3d.userData.modifier.pivotRotate(this.o3d.userData.center, new three.Vector3(0, 1, 0), this.rot, true);
I'm now trying to do something similar with the player too. I record what keys are pressed, I calculate the normal of the vector of desired direction (if I press W and D I'll get (1, 1), if I press just W I'll get (0, 1)), after which I use the following line to detect the angle at which the user wanna move:
Math.atan2(-normal[1], normal[0]);
I have already tested that the angle is correct. On top of that, the codebase before "rotating around a pivot" used the same code and it worked fine
Everytime there's actually a direction the user wanna go, I'll run the following line:
this.o3d.userData.modifier.pivotRotate(this.o3d.userData.center, new three.Vector3(0, 1, 0), Math.atan2(-normal[1], normal[0]), true, true);
If the user just keeps a key pressed, then abs will make sure that no visible rotation is made (since theta will be 0)
Here's the problem: everytime I press A, be it in combination with W or S or not, the character will rotate like insane. I put after the line from above the following code to see what's happening:
com.log(new three.Euler().setFromQuaternion(this.o3d.userData.setup.getWorldQuaternion(new three.Quaternion())));
I'm getting this:
As you can see, x and z are reaching -pi, and y bouces back and forth. This does not happen for any other combination that does not contain key A
Update after 2 days:
I have rewrote my function like this:
I got these in console while trying to move in the problematic positions:
As it can be seen in the first log, my target is at rotation 0 and is going for -2.35..., but rotAfterRot is showing weird results..: -pi and -.78...
This is the result of running this.rotateOnAxis(axis, theta). I have changed this exact line with this.rotation.y += theta. Now everything is working as it should be: no weird -pi and rotAfterRot.y is actually theta
My guess is that rotateOnAxis is also counting other features of the object, like position, but still can't figure how it spits that -pi
TLDR:
I need to change the perspective over an object in a 2d cavas.
Let's say I make a photo of my desk while I sit next to it. I want to change that image, to look as if I would see it from above. Basically the view would change like in the image below (yes, my paint skills are awesome :)) ).
I am using canvas and easeljs (createjs).
I am totally aware that this is not a 3rd object, I don't have a stage and a camera. I am also aware that easeljs doesn't support this as a feature.
What I am actually trying is to emulate this (I am also not interested in quality of the result at this point).
What I tried and it worked (I noticed I am not the first to try to do this and didn't actually found a real answer so this is my current approach):
I take the original image. Divide it in a high number of images (the higher the better) as in the image below.
Then I scale each of the "mini-images" on x axis with a different factor (that I'm computing depending on the angle the picture was made). Part of relevant code below (excuse the hardcodings and creepy code - is from a proof of concept I made in like 10 minutes):
for (var i = 0; i < 400; i++) {
crops[i] = new createjs.Bitmap(baseImage);
crops[i].sourceRect = new createjs.Rectangle(0, i, 700, i + 1);
crops[i].regX = 350;
crops[i].y = i - 1;
crops[i].x = 100;
crops[i].scaleX = (400 - i / 2) * angleFactor;
stage.addChild(crops[i]);
}
Then you crop again only the relevant part.
Ok... so this works but the performance is... terrible - you basically generate 400 images - in this case - then you put them in a canvas. Yes, I know it can be optimized a bit but is still pretty bad. So I was wondering if you have any other (preferably better) approaches.
I also tried combining a skewing transformation with a scale and a rotation but I could not achieve the right result (but I actually still think there may still be something here... I just can't put my finger on it and my "real" math is a bit rusty).
Using thingiview.js, Three.js and the trackballControls, I've put together a system in which I can upload an STL file, and then render it on the canvas. trackballControls are pretty great with some adjustment, but I'm having an issue:
I would like to zoom in at the point of the mouse cursor as opposed to the center of the grid/plane.
I've done a simple captureEvent to get the on screen coordinates of the mouse and track it, but I'm having issue on figuring out where to tap into the control scheme to do it.
I checked out the _zoomStart / _zoomEnd stuff (which confuses me a little as it goes off of "y", I assumed it would be "z"). But when trying to add a _zoomStart.x, it basically ignores it.
Now I may not be a guru, but I'm comfortable banging around usually.
I'd also like to make sure that when I pan, the zoom and rotate still bases off the center of the object, as opposed to the center of the grid/plane.
Have been searching for days through posts and examples, but not really finding any answers.
I'm sure I'm not looking in the right place/heading in the right direction. A helpful nudge (or better yet a swift kick) in the right direction would be truly appreciated.
EDIT
this.zoomCamera = function () {
var factor = 1.0 + ( _zoomEnd.y - _zoomStart.y ) * _this.zoomSpeed;
if ( factor !== 1.0 && factor > 0.0 ) {
_eye.multiplyScalar( factor );
if ( _this.staticMoving ) {
_zoomStart.copy( _zoomEnd );
} else {
_zoomStart.y += ( _zoomEnd.y - _zoomStart.y ) * this.dynamicDampingFactor;
}
}
};
I assume the above is where I would go in for the zoom alter. What I don't understand is it being set to _zoomStart.y. But taking it as it is, how would I implement x?
I mean, if _zoomStart and _zoomEnd are a Vector2, where in the above code does it define x?
confuzzled
Zooming in Trackballcontrols is not actually zooming (that would be setting the camera fov). Two objects are just getting moved around in the controls..., the other would be the camera (this.object), the other the point it's looking at (this.target). I have not played much with trackballcontrols, but I would hazard a guess it won't touch the target at all (so all movement and zooming will revolve around that).
You could try changing the target at onclick, something like:
mycontrols.target = new THREE.Vector3(newx, newy, newz);
You might need to update/reset some other Trackballcontrols internal variables, but it might also work just like that.
For getting the 3D x/y/z coordinates from the 2D x/y mouse coordinates, I suggest searching around for ray casting or object picking, should find plenty of examples.
The trick is that _zoomStart and _zoomEnd is created for touch zooming, and when you zoom using the mouse wheel you have to pass only one variable expressing: "how much to zoom". The programmer didn't create a new variable for it but used the _zoom###.y component.
So _zoomStart and _zoomEnd doesn't provide information about how the zooming will be executed, these variables contain the "instruction" only. Then the software converts it to the "zoompan" vector expressing the required movement of the camera in 3D.
I have a test application where I am creating collison detection between to rectangles using Raphael.js.
I am able to get collison detection working properly, but I have to drag it around slooowly.... the issue arises when I move the mouse too quickly. It seems that it is not refreshing quick enough to detect the draggable rect.
The purple square is the only one that drags.
JS Fiddle
I guess my question is how can I improve detection/fix my issue?
Thanks in advance.
Since move is getting called on each pixel move, you don't have time to do much in the way of calculations to keep it smooth. First, I replaced you function for determining overlap with a more standard one:
var rect_collision = function (x1, y1, size1, x2, y2, size2) {
var a = {top: y1, bottom: y1+size1, left: x1, right: x1+size1};
var b = {top: y2, bottom: y2+size2, left: x2, right: x2+size2};
// this is the general way to figure out if two rects are overlapping
return !(a.left >= b.right || a.right <= b.left ||
a.top >= b.bottom || a.bottom <= b.top);
};
This just checks to see if one rectangle is completely to the left, right, top, or bottom of the other one. If it isn't, then they must be overlapping. Since this just gives a true or false value, I still had to figure out which side the collision occurred on.
To figure that out, I broke the collisions into two components, an x collision and a y collision by pretending that first only dx changed then only dy changed. Once I knew which direction was causing the overlap I could then use the change in direction to determine which side the overlap occurred on. For example, if x caused the collision and the previous dx was greater than the current dx then the collision was on the right side.
// check the x and y directions separately
var x_collide = rect_collision(r2_x, r2_y, rectSize, x, r1_y, rectSize);
// see if we are currently overlapping
if (!x_collide) {
// not colliding, update our x position normally
this.attr({x:x});
this.pdx = dx;
}
else {
// we are, stick the moving rect to the correct side of the stationary one
// based on the drag direction that got us stuck
this.attr({x: this.pdx > dx ? r2_x + rectSize + 1 : r2_x - rectSize - 1});
}
I then added a little bit of extra logic to match the functionality that you had which prevent the user from dragging the rectangle directly through the stationary one. Basically I ended up just seeing if the move would place the moving rectangle directly on the opposite side of the stationary one, and if so, prevent it.
I also cleaned up your checks for the border to get rid of all of the Math.min and Math.max calls since you didn't really need those. That's more of a preference thing though since I doubt there were causing much of the performance issues.
You can see the results at http://jsfiddle.net/X7H9G/3/. I'm not sure if this is the best solution, but it seems to do the job.
The question title may be vague. Basically, imagine a racing game built in canvas. The track takes up 10,000 x 10,000 pixels of screen space. However the browser window is 500 x 500 pixels. The car should stay centered in the browser and the 'viewable' area of the 10,000 x 10,000 canvas will change. Otherwise the car would just drive off the edge at disappear.
Does this technique have a name?
What are the basic principles to make this happen?
If the car should stay at the same position (relative to the canvas' position), then you should not move the car. Instead, move the background picture/track/map to the other side.
Causing your eyes to think the car moves right can be done by either moving the car to the right, or by moving the map to the left. The second option seems to be what you want, since the car won't move whereas the viewable area (i.e. the map) will.
This is a quick demo from scratch: http://jsfiddle.net/vXsqM/.
It comes down to altering the map's position the other way round:
$("body").on("keydown", function(e) {
if(e.which === 37) pos.x += speed; // left key, so move map to the right
if(e.which === 38) pos.y += speed;
if(e.which === 39) pos.x -= speed;
if(e.which === 40) pos.y -= speed;
// make sure you can't move the map too far.
// clamp does: if x < -250 return -250
// if x > 0 return 0
// else it's allowed, so just return x
pos.x = clamp(pos.x, -250, 0);
pos.y = clamp(pos.y, -250, 0);
draw();
});
You can then draw the map with the position saved:
ctx.drawImage(img, pos.x, pos.y);
If you're looking for a way to actually move the car when the map cannot be moved any further (because you're driving the car close to a side of the map), then you'd have to extend the clamping and also keep track of when the car should be moved and how far: http://jsfiddle.net/vXsqM/1/.
// for x coordinate:
function clamp2(x, y, a, b) { // x = car x, y = map x, a = min map x, b = max map x
return y > b ? -y : y < a ? a - y : x;
}
The position clamping then becomes a little more complex:
// calculate how much car should be moved
posCar.x = clamp2(posCar.x, posMap.x, -250, 0);
posCar.y = clamp2(posCar.y, posMap.y, -250, 0);
// also don't allow the car to be moved off the map
posCar.x = clamp(posCar.x, -100, 100);
posCar.y = clamp(posCar.y, -100, 100);
// calculate where the map should be drawn
posMapReal.x = clamp(posMap.x, -250, 0);
posMapReal.y = clamp(posMap.y, -250, 0);
// keep track of where the map virtually is, to calculate car position
posMap.x = clamp(posMap.x, -250 - 100, 0 + 100);
posMap.y = clamp(posMap.y, -250 - 100, 0 + 100);
// the 100 is because the car (circle in demo) has a radius of 25 and can
// be moved max 100 pixels to the left and right (it then hits the side)
Two things:
Canvas transformation methods
First, the canvas transformation methods (along with context.save() and context.restore() are your friends and will greatly simplify the math needed to view a portion of a large 'world`. If you use this approach, you can get the desired behavior just by specifying the portion of the world that is visible and the world-coordinates of everything you want to draw.
This is not the only way of doing things* but the transformation methods are meant for exactly this kind of problem. You can use them or you can reinvent them by manually keeping track of where your background should be drawn, etc., etc.
Here's an example of how to use them, adapted from a project of mine:
function(outer, inner, ctx, drawFunction) {
//Save state so we can return to a clean transform matrix.
ctx.save();
//Clip so that we cannot draw outside of rectangle defined by `outer`
ctx.beginPath();
ctx.moveTo(outer.left, outer.top);
ctx.lineTo(outer.right, outer.top);
ctx.lineTo(outer.right, outer.bottom);
ctx.lineTo(outer.left, outer.bottom);
ctx.closePath();
//draw a border before clipping so we can see our viewport
ctx.stroke();
ctx.clip();
//transform the canvas so that the rectangle defined by `inner` fills the
//rectangle defined by `outer`.
var ratioWidth = (outer.right - outer.left) / (inner.right - inner.left);
var ratioHeight = (outer.bottom - outer.top) / (inner.bottom - inner.top);
ctx.translate(outer.left, outer.top);
ctx.scale(ratioWidth, ratioHeight);
ctx.translate(-inner.left, -inner.top);
//here I assume that your drawing code is a function that takes the context
//and draws your world into it. For performance reasons, you should
//probably pass `inner` as an argument too; if your draw function knows what
//portion of the world it is drawing, it can ignore things outside of that
//region.
drawFunction(ctx);
//go back to the previous canvas state.
ctx.restore();
};
If you are clever, you can use this to create multiple viewports, picture-in-pictures, etc. of different sizes and zoom in and out on stuff.
Performance
Second, as I commented in the code, you should make sure your drawing code knows what portion of your larger 'world' will be visible so that you don't do a lot of work trying to draw things that will not be visible.
The canvas transformation methods are meant for solving exactly this kind of problem. Use 'em!
*You will likely have problems if your world is so large that its coordinates cannot fit in an appropriate integer. You'll hit that problem roughly when your world exceeds billion (10^9) or a long trillion (10^18) pixels in any dimension, depending on whether the integers are 32- or 64-bit. If your world isn't measured in pixels but in 'world units', you'll run into problems when your world's total size and smallest feature scale lead to floating point inaccuracies. In that case, you will need to do extra work to keep track of things... but you'll probably still want to use the canvas transformation methods!
My very first game was a racing game where I moved the background instead of the car and although I want to think now that I had my reasons to make it so... I just didn't know better.
There are a few techniques that you need to know to achieve this well.
Tiled background. You need to make your track out of smaller pieces that tiled. To To draw 10,000 x 10,000 pixels is 100MPix image usually such image will have 32bit depth (4 bytes) this will end up being 400MB in memory. Compressions like PNG, JPEG won't help you since these are made to store and transfer images. They cant be rendered to a canvas without decompressing.
Move the car along your track. There is nothing worst then moving the BG under the car. If you need to add more features to your game like AI cars... now they will have to move along the map and to implement car collisions you need to make some not hard but strange spacial transformations.
Add camera entity. The camera needs to have position and viewport size (this is the size of your canvas). The camera will make or break your game. This is the entity that will give you the sense of speed in the game... You can have a camera shake for collisions, if you have drifts if your game the camera can slide pass the desired position and center back to the car, etc. Of course the most important thing will be tracking the car. Some simple suggestions I can give you are to not put the car in dead center of the camera. put the car a little behind so you can see a bit more what's in front of your. The faster the car moves the more you should offset the camera. Also you can't just compute the position of the camera instead compute desired position and slowly per frame move the current camera position to the desired position.
Now when you have camera and a large tiled map, when you draw the tiles you have to subtrack the camera position. You can also compute which tiles are not visible and skip them. This technique will allow you do extend your game with even larger maps or you can stream your map where you don't have all the tiles loaded and load in advance on background (AJAX) what will be visible soon.