I want to create a 1st person 3D game with Three.JS. The controls are W A S D. I have the keys assigned to move forward (W), move backward (S), turn left (A) and turn right (D).
In this code I explain the logic behind the controls. The deltas' porpuse is to inform what direction the camera is looking, so that when it moves forward or backward it moves in that direction. That is what I'm having problems with.
//variables
var pi = Math.PI; //just because I use PI a lot
//controls
var moveForward = false;
var turnLeft = false;
var moveBackward = false;
var turnRight = false;
//direção
var deltaZ = pi;
var deltaX = 0;
var increaseDeltaX = true;
var increaseDeltaZ = false;
/*code adding the camera, the scene and eventListener for the keys. When the keys mentioned are clicked the corresponded var turns true*/
function animate() {
//clicking W
if (moveForward) {
camera.position.z += deltaZ/pi;
camera.position.x -= deltaX/pi;
//I used delta/pi because if delta=pi camera.position.z will increase 1
}
//clicking S
if (moveBackward) {
camera.position.z -= deltaZ/pi;
camera.position.x += deltaX/pi;
}
//clicking D
if (turnRight) {
camera.rotation.y += pi/300; //clicking 300 times will spin 180 degrees
//deltaZ starts in pi and decreasing, it varies between pi and -pi
if (increaseDeltaZ) {
deltaZ += pi / 150; //150= 300/2; pi= 90 degrees = 180/2
} else {
deltaZ -= pi / 150
}
if (deltaZ >= pi) {
increaseDeltaZ = false;
} else if (deltaZ <= -pi) {
increaseDeltaZ = true;
}
//deltaX starts in 0 and increasing, it varies between pi and -pi
if (increaseDeltaX) {
deltaX += pi / 150;
} else {
deltaX -= pi / 150;
}
if (deltaX >= pi) {
increaseDeltaX = false;
} else if (deltaX <= -pi) {
increaseDeltaX = true;
}
}
This code works well. When I turn right and then move forward, the camera moves in the right direction. The problem is when I turn the camera left. I tried to just reverse the calculations but the values just get messed up and the deltas go over pi and the directions become incorrect.
What do I put in the if (left)?
Related
I want to focus the camera on THREE.Geometry, one vertex at a time and
Transition the camera to the next vertex of the same Geometry
How should i accomplish 1 & 2?
I created a fiddle.
http://jsfiddle.net/5oajajpd/
the function move camera goes through each vertex and sets the camera position. To transition this with animation you can set the x,y, and z properties through the jquery animate function, or your animation lib of choice.
The move camera function is triggered by an interval. In this sphere example it will spiral around and around the sphere forever.
var i = 0;
function moveCamera() {
var point = mesh.geometry.vertices[i];
var coeff = 1 + altitude / rad;
camera.position.x = point.x * coeff;
camera.position.y = point.y * coeff;
camera.position.z = point.z * coeff;
camera.lookAt(mesh.position);
i++;
if (i > mesh.geometry.vertices.length) {
i = 0;
}
}
Im trying to rotate my player to face where I last clicked. I've acutally manged to do this, but now I want to see the player rotate at a set speed instead of the sprite just changing rotation instantly.
Ive tried several methods I've found online, but none of them work for me. Here's what I have
void Update()
{
if (Input.GetMouseButtonDown (0))
{
Vector3 diff = Camera.main.ScreenToWorldPoint(Input.mousePosition) - transform.position;
diff.Normalize();
float rot_z = Mathf.Atan2(diff.y, diff.x) * Mathf.Rad2Deg;
transform.rotation= Quaternion.Euler(0f, 0f, rot_z - 90);
Instantiate(ProjectilePrefab, transform.position, transform.rotation);
}
}
The code above works fine, but it shows no movement. I have tried to do this but the position is wrong and the rotation is instant as well:
Vector3 diff = Camera.main.ScreenToWorldPoint(Input.mousePosition) - transform.position;
var newRotation = Quaternion.LookRotation(diff);
newRotation.y = 0.0f;
newRotation.x = 0.0f;
transform.rotation = Quaternion.Slerp(transform.rotation, newRotation, Time.deltaTime * 30);
ANy ideas?
Two problems here. First, the Slerp function is only called once after the user pressed the mouse button. You have to move it outside the if part or use a coroutine. Second, Slerp expects a float from 0 to 1 to indicate the progress and not time or deltaTime. The official examples covering the lerp functions are just bad, because using the time value would work only during the first second the game is running.
You need something like this
if (Input.GetMouseButtonDown (0)) {
// your other stuff here
float starTime = Time.time;
}
float t = Time.time - startTime;
transform.rotation = Quaternion.Slerp(transform.rotation, newRotation, t);
The rotation would finish after one second. If you want it faster or slower just multiply t with a factor.
I found the answer. This is how I made it work
if (Input.GetMouseButtonDown (0)) {
target = Camera.main.ScreenToWorldPoint (Input.mousePosition);
rotateToTarget = true;
print ("NEW TARGET: " + target);
}
if (rotateToTarget == true && target != null) {
print ("Rotating towards target");
targetRotation = Quaternion.LookRotation (transform.position - target.normalized, Vector3.forward);
targetRotation.x = 0.0f;//Set to zero because we only care about z axis
targetRotation.y = 0.0f;
player.transform.rotation = Quaternion.Slerp (transform.rotation, targetRotation, Time.deltaTime * rotationSpeed);
if (Mathf.Abs (player.transform.rotation.eulerAngles.z - targetRotation.eulerAngles.z) < 1) {
rotateToTarget = false;
travelToTarget = true;
player.transform.rotation = targetRotation;
print ("ROTATION IS DONE!");
}
}
So I have a heightmap system which works well enough, however since the THREE.js has updated to r60 which removed the Face4 object, I am having issues.
My code is something like this:
this.buildGeometry = function(){
var geo, len, i, f, y;
geo = new THREE.PlaneGeometry(3000, 3000, 128, 128);
geo.dynamic = true;
geo.applyMatrix(new THREE.Matrix4().makeRotationX(-Math.PI / 2));
this.getHeightData('heightmap.png', function (data) {
len = geo.faces.length;
for(i=0;i<len;i++){
f = geo.faces[i];
if( f ){
y = (data[i].r + data[i].g + data[i].b) / 2;
geo.vertices[f.a].y = y;
geo.vertices[f.b].y = y;
geo.vertices[f.c].y = y;
geo.vertices[f.d].y = y;
}
}
geo.computeFaceNormals();
geo.computeCentroids();
mesh = new THREE.Mesh(geo, new THREE.MeshBasicMaterial({color:0xff0000}) );
scene.add(mesh);
});
};
This works well since a pixel represents each face. How is this done now that the faces are all triangulated?
Similarly I use image maps for model positioning as well. Each pixel matches to the respective Face4 and a desired mesh is placed at its centroid. How can this be accomplished now?
I really miss being able to update the library and do not want to be stuck in r59 anymore =[
This approach works fine on the recent versions (tested on r66).
Notice that the genFn returns the height y given current col and row, maxCol and maxRow (for testing purposes, you can of course replace it with a proper array lookup or from a grayscale image... 64x64 determines the mesh resolution and 1x1 the real world dimensions.
var genFn = function(x, y, X, Y) {
var dx = x/X;
var dy = y/Y;
return (Math.sin(dx*15) + Math.cos(dy * 5) ) * 0.05 + 0.025;
};
var geo = new THREE.PlaneGeometry(1, 1, 64, 64);
geo.applyMatrix(new THREE.Matrix4().makeRotationX(-Math.PI / 2));
var iz, ix,
gridZ1 = geo.widthSegments +1,
gridX1 = geo.heightSegments+1;
for (iz = 0; iz < gridZ1; ++iz) {
for (ix = 0; ix < gridX1; ++ix) {
geo.vertices[ ix + gridX1*iz ].y = genFn(ix, iz, gridX1, gridZ1);
}
}
geo.computeFaceNormals();
geo.computeVertexNormals();
geo.computeCentroids();
var mesh = new THREE.Mesh(
geo,
mtl
);
scene.add(mesh);
Is the intent of the TrackballControl to have a "border" outside the trackball that induces roll? I personally dislike it. It is a bit discontinuous, and does't really have a lot of purpose (imho).
If not, the function getMouseProjectionOnBall can be changed similar to the following. This does two things (not necessarily "correctly"):
Normalize the radius to fill both axis
Map z values outside of the ball (ie where z was previously 0)
I find this a lot more natural, personally.
Thoughts?
this.getMouseProjectionOnBall = function(clientX, clientY) {
var xnormalized = (clientX - _this.screen.width * 0.5 - _this.screen.offsetLeft) / (_this.screen.width / 2.0);
var ynormalized = (_this.screen.height * 0.5 + _this.screen.offsetTop - clientY) / (_this.screen.height / 2.0);
var mouseOnBall = new THREE.Vector3(
xnormalized,
ynormalized,
0.0
);
var length = mouseOnBall.length();
var ballRadius = 1.0; // As a fraction of the screen
if (length > ballRadius * 0.70710678118654752440) {
var temp = ballRadius / 1.41421356237309504880;
mouseOnBall.z = temp * temp / length;
// Remove old method.
// This Left z = 0, which meant rotation axis
// becomes z, which is a roll
//mouseOnBall.normalize();
} else {
mouseOnBall.z = Math.sqrt(1.0 - length * length);
}
_eye.copy(_this.object.position).sub(_this.target);
var projection = _this.object.up.clone().setLength(mouseOnBall.y);
projection.add(_this.object.up.clone().cross(_eye).setLength(mouseOnBall.x));
projection.add(_eye.setLength(mouseOnBall.z));
return projection;
};
I am trying to implement the collision detection between rotated rectangle and circle by following this http://www.migapro.com/circle-and-rotated-rectangle-collision-detection/
I have added the code in jsfiddle here http://jsfiddle.net/Z6KSX/2/.
What am i missing here ?
function check_coll ( circle_x,circle_y, rect_x, rect_y, rect_width, rect_height, rect_angle)
{
// Rotate circle's center point back
var rect_centerX = rect_x /2 ;
var rect_centerY = rect_y /2 ;
var cx = (Math.cos(rect_angle) * (circle_x - rect_centerX)) - (Math.sin(rect_angle) * (circle_y - rect_centerY)) + rect_centerX;
var cy = (Math.sin(rect_angle) * (circle_x - rect_centerX)) + (Math.cos(rect_angle) * (circle_y - rect_centerY)) + rect_centerY;
// Closest point
var x, y;
// Find the unrotated closest x point from center of unrotated circle
if (cx < rect_x) {
x = rect_x;
}
else if (cx > rect_x + rect_width){
x = rect_x + rect_width;
}
else{
x = cx;
}
// Find the unrotated closest y point from center of unrotated circle
if (cy < rect_y){
y = rect_y;
}
else if (cy > rect_y + rect_height) {
y = rect_y + rect_height;
}
else {
y = cy;
}
// Determine collision
var collision = false;
var c_radius = 5;
var distance = findDistance(cx, cy, x, y);
if (distance < c_radius) {
collision = true; // Collision
}
else {
collision = false;
}
return collision;
}
function findDistance (x1, y1, x2, y2) {
var a = Math.abs(x1 - x2);
var b = Math.abs(y1 - y2);
var c = Math.sqrt((a * a) + (b * b));
return c;
}
Hehe, I find this amusing as I somewhat recently solved this for myself after spending a large amount of time going down the wrong path.
Eventually I figured out a way:
1.) Simply rotate the point of the center of the circle by the Negative amount the rectangle has been rotated by. Now the point is 'aligned' with the rectangle (in the rectangles relative coordinate space).
2.) Solve for circle vs. AABB. The way I solved it gave me a point on the rectangle that is closest to the circle's center.
3.) Rotate the resulting point from by the Positive amount the rectangle has been rotated by. Continue solving as usual (checking if the distance between that point and the circle center is within the circle's radius)
From a very quick glance at your code, it seems like maybe you are doing the same thing, but missing the last step? I suggest drawing out your point on the rectangle from step 2 to see exactly where it is to help debug.
I was able to figure this out . The issue in the code was, I was using the wrong radius and had missed the center of rect_x and rect_y
var rect_centerX = rect_x + (rect_width / 2);
var rect_centerY = rect_y + (rect_height /2);
When dealing with rotation on the canvas we will need to add the translate values to the corresponding x and y values used in createrect.
I also use this code for my project and it's working. The only thing you need to do is use -angle instead of the angle.
Here is my code link
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
const rectX = 100;
const rectY = 100;
const rectWidth = 200;
const rectHeight = 100;
const circleRadius = 2;
const rectMidPointX = rectX + rectWidth / 2;
const rectMidPointY = rectY + rectHeight / 2;
const angle = Math.PI / 4;
let circleX;
let circleY;
canvas.addEventListener('mousemove', (e) => {
circleX = e.clientX;
circleY = e.clientY;
ctx.save();
ctx.beginPath();
ctx.fillStyle = '#fff';
ctx.arc(circleX, circleY, circleRadius, 0, 2 * Math.PI);
ctx.fill();
ctx.stroke();
ctx.restore();
calculateIntersection();
})
ctx.save();
//ctx.fillRect(100, 100, 100, 100);
ctx.strokeStyle = 'black';
ctx.translate(rectMidPointX, rectMidPointY);
ctx.rotate(angle);
ctx.translate(-rectMidPointX, -rectMidPointY);
ctx.strokeRect(rectX, rectY, rectWidth, rectHeight);
ctx.restore();
// Determine collision
let collision = false;
const findDistance = (fromX, fromY, toX, toY) => {
const a = Math.abs(fromX - toX);
const b = Math.abs(fromY - toY);
return Math.sqrt((a * a) + (b * b));
};
function calculateIntersection() {
// Rotate circle's center point back
const unrotatedCircleX = Math.cos(-angle) * (circleX - rectMidPointX) -
Math.sin(-angle) * (circleY - rectMidPointY) + rectMidPointX;
const unrotatedCircleY = Math.sin(-angle) * (circleX - rectMidPointX) +
Math.cos(-angle) * (circleY - rectMidPointY) + rectMidPointY;
// Closest point in the rectangle to the center of circle rotated backwards(unrotated)
let closestX, closestY;
// Find the unrotated closest x point from center of unrotated circle
if (unrotatedCircleX < rectX)
closestX = rectX;
else if (unrotatedCircleX > rectX + rectWidth)
closestX = rectX + rectWidth;
else
closestX = unrotatedCircleX;
// Find the unrotated closest y point from center of unrotated circle
if (unrotatedCircleY < rectY)
closestY = rectY;
else if (unrotatedCircleY > rectY + rectHeight)
closestY = rectY + rectHeight;
else
closestY = unrotatedCircleY;
const distance = findDistance(unrotatedCircleX, unrotatedCircleY, closestX, closestY);
if (distance < circleRadius)
collision = true; // Collision
else
collision = false;
console.log('collision', collision);
}
<canvas id="canvas" width="400px" height="400px" />