I'm creating a game with raycasted 3D graphics like Wolfenstein 3D but using line segments instead of a grid of blocks for walls. Everything is fine when drawing the floors until rotating the player view.
the floor should be aligned against the walls
Here is the view in 2D, with each pixel on the floor on the screen rendered as a blue point:
In the top image is when the player's rotation is Math.PI. In the bottom image it is rotated slightly.
A significant feature of this is the beginning of the cone of points is aligned along the y axis. It should look like a frustrum.
Here is the code I am using to find the x and y coordinates of each point where a texture is drawn on the floor. This code is run for each x value on the screen.
The variable "projPlane" is the projection plane, which is the size of the screen.
projDistance is the distance from the player to the projection plane so that it fits within the field of view, or (projPlane.width/2)/Math.tan(VectorMath.toRadians(fov/2))
pHeight is the players height.
The variable "x" is the x value of the row being rendered on the screen.
//FLOOR TEXTURE
var floorSize = Math.floor((projPlane.height-wallSize)/2); //draw the floor from under the wall
var floorTextureIndex = 1;
//for texture y
if(floorSize > 0){ // values need to be positive
//find the point on the floor
var textureWidth = textures[floorTextureIndex].textureImage.width;
var textureHeight = textures[floorTextureIndex].textureImage.height;
//console.log(coordX);
for (var ty = 0; ty < floorSize; ty++){
//angle is tan
var yAngle = projPlane.distance / (ty + wallSize/2); //ty + wallSize/2 is the point on the projection plane
var yDistance = yAngle * pHeight; //pHeight is player height
var worldY = player.y + Math.sin(player.vector)*yDistance;
var coordY = Math.floor(worldY % (textureHeight));
var xAngle = Math.atan((projPlane.width/2 - x)/projPlane.distance);
/*if(x < projPlane.width/2){//tangent of the angle in the projectionPlane
xAngle = (x) / projPlane.distance;
}
else{
xAngle = (x-projPlane.width) / projPlane.distance;
}*/
var xDistance = yDistance/Math.cos(xAngle);
var worldX = player.x + Math.cos(player.vector - xAngle)*xDistance;
//console.log(xDistance);
var coordX = Math.floor(worldX % (textureWidth));//disable until I can get y
floorPoints.push(new Point(worldX,worldY));
var tempTexture = textures[floorTextureIndex];
if(tempTexture.textureData[coordX] != undefined){
// a different function drawns these to the screen in descending order
floorTextureColors.push(tempTexture.textureData[coordX][coordY]);
}
};
}
It doesn't seem to be an issue with the y value since the y coordinates of the floor texture seem to appear where they should.(EDIT: it actually was to do with the y value. Adding the xAngle to the player.vector when finding the y position returns a correct y position but there is still a "curved" distortion. I hope one of you can propose a more concrete solution.)
What I do to find the X coordinate is form a triangle with the distance from the player to the floor point as the opposite side the angle that the point makes with the player. The hypotenuse should be the magnitude of the distance to the point's x coordinate.
Then I multiply the cosine of the angle by the magnitude to get the x value.
It works whenever the character isn't pointing west and east. What is causing all the first points to have the same y value? I think that's the biggest clue on the distortion occurring here.
Related
I'm trying to draw the perimeter of a circle depending on the angle inputed by the user. The angle determines the perimeter completion : 360° being the full circle, 180 half of the circle, and so on.
My problem is : given the radius, the angle and the center coordinates of the circle, how can I dynamically compute the path of the perimeter ?
I know it's probably basic math but everything I tried so far didn't work.
Here is my fiddle : https://jsfiddle.net/Hal_9100/L311qq88/
My problem is finding the right formula for the x and y coordinates of the path :
var x = i * (radius * Math.cos(angle)) + centerX;
var y = i * (radius * Math.sin(angle)) + centerY;
Am I going all wrong here ?
Here is an example of what I'm trying to do : please note that only the black perimeter should be drawn : I just used the dotted red lines to give a visual example of how the perimeter should be drawn depending on the value given by the user.
Yes, the problem is your maths. Here is the correct way to calculate the x,y coordinate pairs (note that the iteration is from zero to the required angle, not from zero to the radius):
for (var i = 0; i <= angle; i++) {
var x = (radius * Math.cos((i-90)*Math.PI/180)) + centerX;
var y = (radius * Math.sin((i-90)*Math.PI/180)) + centerY;
Your fiddle works fine if you substitute these three lines.
I need to position a dot on a coordinate system based on its bearing and radius.
Using the following code i can position a dot at the correct distance from the center, but this only works horizontally.
What i need is both top & left position of the dot based on bearing & radius
setTimeout(function() {
var km2show = 100;
var testDistance = 50;
var width = $('#radar').width(); // Width & height of radar
var center = width/2; // Radar center
var px2km = center/km2show; // Pixels per km
var radius = radius2coor((px2km*testDistance),center);
var bearing = 45;
// Set height of radar based on the width
$('#radar').height(width);
// Set dot in center of radar
$('#centerDot').css({top: center, left: center});
$('#container').append("<div style='position:absolute;top:0px;left:"+radius+"px;color:white;'>*</div>");
},100);
function radius2coor(radius,center) {
var res = radius-center;
return res;
}
Please see
jsFiddle
So how would i go about getting bot top and left position of the dot ?
The end result should position the dot at the red marker having a bearing of 45 degrees:
The main issue you were encountering was that the angle wasn't in radians so first thing we want is to convert the 45 degrees to pi/4.
Also, when going from regular angular coordinates to x,y coordinates you multiply the radius by sine of the angle to find y coordinate and you multiply the radius by the cosine of the angle to get the x coordinate. Just think about the unit circle and it will make sense.
var bearing = parseInt(prompt("enter angle in degrees", "0"));
if(!isNaN(bearing)){
setTimeout(function() {
var km2show = 100;
var testDistance = 50;
var width = $('#radar').width(); // Width & height of radar
var center = width/2; // Radar center
var px2km = center/km2show; // Pixels per k
//not sure what this is doing so I set a random radius
//(called it distanceFromCenter). If you need this to be
//the distance between two cartesian points then you can
//just implement the distance formula.
//var radius = radius2coor((px2km*testDistance),center);
var radius = 100;
var radianBearing = (bearing/180)*Math.PI
// Set height of radar based on the width
$('#radar').height(width);
// Set dot in center of radar
$('#centerDot').css({top: center, left: center});
//the main issue you were encountering was that the angle wasn't in radians so I converted it.
positionDot(radius, radianBearing);
},100);
}
function positionDot(distanceFromCenter, bearing, width)
{
//when going from regular angular coordinates to x,y coordinates you multiply the radius by sine of the angle to find y coordinate and you multiply the radius by the cosine of the angle to get the x coordinate.
$('#container').append("<div style='position:absolute;top:"+(-distanceFromCenter*Math.sin(bearing)).toString()+"px;left:"+distanceFromCenter*Math.cos(bearing)+"px;color:white;'>*</div>");
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<div id='radar' style='width:100%;max-width:400px;height:400px;border:1px black solid;border-radius:400px;background-color:#3c3c3c;position:relative;'>
<div id='centerDot' style='position:absolute;color:white;'>
<div style='position:relative;' id='container'></div>
<b>*</b>
</div>
</div>
To get both coordinates, you need coordinates of center, radius and bearing.
Note that trigonometric functions usually work with argument in radians, not degrees (don't know about javascript math library)
P.X = Center.X + Radius * Math.Cos(bearing)
P.Y = Center.Y + Radius * Math.Sin(bearing)
You can get it with:
x = center + radius * cos(bearing)
y = center + radius * sin(bearing)
and as MBo said, you have to convert bearing into radians
rad = deg * PI / 180
https://upload.wikimedia.org/wikipedia/sr/8/85/Trig-funkcije1.gif
I am currently developing a game, which requires a map consisting of various tile images. I managed to make them display correctly (see second image) but I am now unsure of how to calculate the clicked tile from the mouse position.
Are there any existing libraries for this purpose?
Please also note, that the tile images aren't drawn perfectly "corner-facing-camera", they are slightly rotated clockwise.
Isometric Transformations
Define a projection
Isometric display is the same as standard display, the only thing that has changed is the direction of the x and y axis. Normally the x axis is defined as (1,0) one unit across and zero down and the y axis is (0,1) zero units across and one down. For isometric (strictly speaking your image is a dimetric projection) you will have something like x axis (0.5,1) and y axis (-1,0.5)
The Matrix
From this you can create a rendering matrix with 6 values Two each for both axes and two for the origin, which I will ignore for now (the origin) and just use the 4 for the axis and assume that the origin is always at 0,0
var dimetricMatrix = [0.5,1.0,-1,0.5]; // x and y axis
Matrix transformation
From that you can get a point on the display that matches a given isometric coordinate. Lets say the blocks are 200 by 200 pixels and that you address each block by the block x and y. Thus the block in the bottom of your image is at x = 2 and y = 1 (the first top block is x = 0, y = 0)
Using the matrix we can get the pixel location of the block
var blockW = 200;
var blockH = 200;
var locX = 2;
var locY = 1;
function getLoc(x,y){
var xx,yy; // intermediate results
var m = dimetricMatrix; // short cut to make code readable
x *= blockW; // scale up
y *= blockH;
// now move along the projection x axis
xx = x * m[0];
yy = x * m[1];
// then add the distance along the y axis
xx += y * m[2];
yy += y * m[3];
return {x : xx, y : yy};
}
Befoer I move on you can see that I have scaled the x and y by the block size. We can simplify the above code and include the scale 200,200 in the matrix
var xAxis = [0.5, 1.0];
var yAxis = [-1, 0.5];
var blockW = 200;
var blockH = 200;
// now create the matrix and scale the x and y axis
var dimetricMatrix = [
xAxis[0] * blockW,
xAxis[1] * blockW,
yAxis[0] * blockH,
yAxis[1] * blockH,
]; // x and y axis
The matrix holds the scale in the x and y axis so that the two numbers for x axis tell us the direction and length of a transformed unit.
Simplify function
And redo the getLoc function for speed and efficiency
function transformPoint(point,matrix,result){
if(result === undefined){
result = {};
}
// now move along the projection x axis
result.x = point.x * matrix[0] + point.y * matrix[2];
result.y = point.x * matrix[1] + point.y * matrix[3];
return result;
}
So pass a point and get a transformed point back. The result argument allows you to pass an existing point and that saves having to allocate a new point if you are doing it often.
var point = {x : 2, y : 1};
var screen = transformPoint(point,dimetricMatrix);
// result is the screen location of the block
// next time
screen = transformPoint(point,dimetricMatrix,screen); // pass the screen obj
// to avoid those too
// GC hits that kill
// game frame rates
Inverting the Matrix
All that is handy but you need the reverse of what we just did. Luckily the way matrices work allows us to reverse the process by inverting the matrix.
function invertMatrix(matrix){
var m = matrix; // shortcut to make code readable
var rm = [0,0,0,0]; // resulting matrix
// get the cross product of the x and y axis. It is the area of the rectangle made by the
// two axis
var cross = m[0] * m[3] - m[1] * m[2]; // I call it the cross but most will call
// it the determinate (I think that cross
// product is more suited to geometry while
// determinate is for maths geeks)
rm[0] = m[3] / cross; // invert both axis and unscale (if cross is 1 then nothing)
rm[1] = -m[1] / cross;
rm[2] = -m[2] / cross;
rm[3] = m[0] / cross;
return rm;
}
Now we can invert our matrix
var dimetricMatrixInv = invertMatrix(dimetricMatrix); // get the invers
And now that we have the inverse matrix we can use the transform function to convert from a screen location to a block location
var screen = {x : 100, y : 200};
var blockLoc = transformPoint(screen, dimetricMatrixInv );
// result is the location of the block
The Matrix for rendering
For a bit of magic the transformation matrix dimetricMatrix can also be used by the 2D canvas, but you need to add the origin.
var m = dimetricMatrix;
ctx.setTransform(m[0], m[1], m[2], m[3], 0, 0); // assume origin at 0,0
Now you can draw a box around the block with
ctx.strokeRect(2,1,1,1); // 3rd by 2nd block 1 by 1 block wide.
The origin
I have left out the origin in all the above, I will leave that up to you to find as there is a trillion pages online about matrices as all 2D and 3D rendering use them and getting a good deep knowledge of them is important if you wish to get into computer visualization.
For a 2d game, I have these concepts:
stage: container in which items and cameras are placed.
item: a visible entity, located on a certain point in the world, anchored from center.
camera: an invisible entity, used to generate relative images of world, located on a certain point in the world, anchored from center.
In the illustrations, you can see how they are related, and what the end result should be.
Here is the code I have: (dumbed down to make it easier to read)
Note1: This is not happening on canvas, so I will not use canvas translation or rotation (and even then, I don't think it would make the problem any easier).
Note2: Item and camera positions are center coordinates.
var sin = Math.sin(rotationRad);
var cos = Math.cos(rotationRad);
var difX = item.x - camera.x;
var difY = item.y - camera.y;
var offsetX = camera.width / 2;
var offsetY = camera.height / 2;
var view.x = (cos * difX) - (sin * difY) + _Ax + _Bx;
var view.y = (sin * difX) + (cos * difY) + _Ay + _By;
This is supposed to calculate an items new position by:
calculating new position of item by rotating it around camera center
(_A*) adjusting item position by offsetting camera position
(_B*) adjusting item position by offsetting camera size
I tried several different solutions to use for _A* and _B* here, but none of them work.
What is the correct way to do this, and if possible, what is the explanation?
You first subtract new origin position from object position. You then rotate it by the inverse of the rotation. New origin can be camera position of top left corner of viewport. Of course if you know viewport center its top left corner is computed by subtracting half of its dimensions.
Like this:
var topLeft = Camera.Position - Camera.Size / 2;
var newPosition = Object.Position - topLeft;
newPosition = Rotate(newPosition, -camera.Angle);
Rotation is very simple:
rotatedX = x * cos(angle) - y * sin(angle)
rotatedY = y * cos(angle) + x * sin(angle)
im working on a 2d canvas game. i have a player circle and some circles (random in size and position). i made the random circles move around an random x.y point. this means i have to radii. one is the radius from the rotationpoint to the middlepoint of the "bubble" and the other ist die radius of the bubble itself.
what i need is the collision between playercircle und the bubbles. i know how to create circle to circle collisondetction with pythagorean theorem and it works quite well. However there is a problem:
right now the collision works for the random x and y point + the radius (from rotationpoint) but not for the bubble itself.
what i tryed is to store the x and y of the rotationpoint + the radius to the middlepoint of the bubble into a variable to use them in collision. it works quite fine. if i console.log these x and y points they give me the changing x and ys from the middlepoint of the bubble.
my problem now is that if if substract these point from the playercircle x and y i didnt work with the right collision. so obviously im missing somethig and right now i am at a dead end.
i made a fiddle to show you, the function for the collision is on line 170, variablenames BubbleX and BubbleY. The .counter to animate the around the neg. or positiv:
http://jsfiddle.net/CLrPx/1/ (you need to use the console to the if there is a collision or not)
function collideBubbles(c1, c2) {
// moving/rotation xPos and yPos
var bubbleX = c2.xPos + Math.cos(c2.counter / 100) * c2.radius; // actual x and y pos. from bubble!
var bubbleY = c2.yPos + Math.cos(c2.counter / 100) * c2.radius;
//console.log('bubbleX: ' + bubbleX);
//console.log('bubbleY: ' + bubbleY);
var dx = c1.xPos - bubbleX; // change with pos from actual bubble!
var dy = c1.yPos - bubbleY; // change with pos from actual bubble!
var distance = c1.radius + c2.bubbleRadius
// Pytagorean Theorem
return (dx * dx + dy * dy <= distance * distance);
}