I have a multiplayer Javascript game where the player is a circle, and is able shoot/"eject" circle bullets in the direction that the player is rotated. My code is working perfectly, except it shoots from the middle of the player. I would like it so that the circles are shot from the top right position of the player, where the gun is located. The issue is that when the players rotation changes, you cannot simply add (1, -1) to the position of the player.
Here is my code:
GameServer.prototype.ejectMass = function(client) {
for (var i = 0; i < client.cells.length; i++) {
var cell = client.cells[i]; // the player
var angle = this.toRad(client.rotation); // rotation of the player
var d = new Vec2(Math.sin(angle), Math.cos(-angle)).scale(-180);
var sq = (~~d.sqDist(d));
d.x = sq > 1 ? d.x / sq : 1;
d.y = sq > 1 ? d.y / sq : 0;
// cell.position is the players position
var pos = new Vec2(
cell.position.x + d.x * cell._size,
cell.position.y + d.y * cell._size
);
var ejected = 0;
// Create cell and add it to node list
ejected = new Entity.EjectedMass(this, null, pos, this.config.ejectSize * cell.ejectSize); // the bullet being shot
ejected.setBoostDefault(-this.config.ejectVelocity * cell.ejectSpeed, angle);
ejected.ejectedOwner = cell; // set the person who shot the bullet
this.addNode(ejected); // add the bullet into the game
if (typeof ejected !== 'undefined') {
setTimeout(this.removeNode.bind(this, ejected), 1000); // remove the ejected bullet after 1 second
}
}
};
And here is an illustration of the current way it is working:
Assuming that the player (circle) is at its own local origin then the position of the gun is relative to the player's origin. Assuming the coordinate system is that of the canvas with forward along the x axis from left to right, and clockwise 90deg (left of player) is the Y axis going down.
Image: C is local circle origin (0,0) with Forward along the red arrow from C, Gx and Gy are the local coordinates of the gun from the circle center C. Top left shows the canvas coordinate (world) system origin. In code below, The player position is relative to that world origin. The final gunPos is also give relative to the world coordinates. B vec is the bullets bullet.delta vector
const bulletSpeed = 10;
var gunPos = {x : 10, Y : 10} // ten units forward ten units left of circle center
var player = {rotation : ?, x : ?, y : ?} // unknown player position and rotation
// get the unit vector of the rotated x axis. Along player forward
var xAx = Math.cos(player.rotation);
var xAy = Math.sin(player.rotation);
// transform the gunpos to absolute position (world coordinates) of rotated player
var rotatedGunPos = {};
rotatedGunPos.x = gunPos.x * xAx - gunPos.y * xAy + player.x;
rotatedGunPos.y = gunPos.x * xAy + gunPos.y * xAx + player.y;
// and fire the bullet from
var bullet = {}
bullet.x = rotatedGunPos.x;
bullet.y = rotatedGunPos.y;
// bullet vector is
bullet.deltaX = xAx * BULLET_SPEED;
bullet.deltaY = xAy * BULLET_SPEED;
You didn't provide enough details about your layout such as what are orientations of X- and Y-axis? Where is 0 angle? Is angle clockwise or counterclockwise? Still the basic idea is the same. Let's assume that X-axis is to the right and Y-axis is down as it looks like from your attached image and adding (1, -1) to get top-right corner. Also assume that angle = 0 for X-axis and angle is clockwise i.e. angle = Pi/2 is aligned with positive direction of Y-axis = down. When the gun is pointed Up i.e. angle = -Pi/2 your starting point is (1, -1) which is at distance sqrt(2) and additionally rotated to Pi/4 corresponding to gun orientation. This is all you need to know.
var angle = this.toRad(client.rotation); // rotation of the player
var gunStartAngle = angle + Math.PI/4;
var sqrt2 = Math.sqrt(2);
// cell.position is the players position
var pos = new Vec2(
cell.position.x + cell._size * sqrt2 * Math.cos(gunStartAngle),
cell.position.y + cell._size * sqrt2 * Math.sin(gunStartAngle)
);
Obviously if your layout is different, you should fix the details of the math but the idea remains the same.
Related
I made a little Engine / Game. Its about Physics and Collision everything seems to be fine the only thing i cannot figure out how to achieve this:
How to make the Sphere bounce correctly off the Corners?
I got only Collision Detection for all 4 Sides for each Block but that makes the Game so hard because when the Sphere would hit a Corner it would gain velocity on the X-Axis also.
Try this by letting the Sphere fall down on the Edge of a Block, it
will slide to the side and keep its fall-direction.
The Game is on CodePen
Just in Case you want to make your own Level
Check CodePen :)
The solution once you have found the point where the circle contacts the corner
Defining the problem. Set ? to your values
const corner = {x : ?, y : ?};
const ball = {
x : ?, // ball center
y : ?,
dx : ?, // deltas (the speed and direction the ball is moving on contact)
dy : ?,
}
And image to help visulize
The the steps are
// get line from ball center to corner
const v1x = ball.x - corner.x; // green line to corner
const v1y = ball.x - corner.x;
// normalize the line and rotate 90deg to get the tangent
const len = (v1x ** 2 + v1y ** 2) ** 0.5;
const tx = -v1y / len; // green line as tangent
const ty = v1x / len;
// Get the dot product of the balls deltas and the tangent
// and double it (the dot product represents the distance the balls
// previous distance was away from the line v1, we double it so we get
// the distance along the tangent to the other side of the line V1)
const dot = (ball.dx * tx + ball.dy * ty) * 2; // length of orange line
// reverse the delta and move dot distance parallel to the tangent
// to find the new ball delta.
ball.dx = -ball.dx + tx * dot; // outgoing delta (red)
ball.dy = -ball.dy + ty * dot;
In easelJS, what is the best way to rotate an object around another? What I'm trying to accomplish is a method to rotate the crosshair around the circle pictured below, just like a planet orbits the sun:
I've been able to rotate objects around their own center point, but am having a difficult time devising a way to rotate one object around the center point of a second object. Any ideas?
Might make sense to wrap content in a Container. Translate the coordinates so the center point is where you want it, and then rotate the container.
To build on what Lanny is suggesting, there may be cases where you don't want to rotate the entire container. An alternative would be to use trigonometric functions and an incrementing angle to calculate the x/y position of the crosshair. You can find the x/y by using an angle (converted to radians) and Math.cos(angleInRadians) for x and Math.sin(angleInRadians) for y, the multiply by the radius of the orbit.
See this working example for reference.
Here's a complete snippet.
var stage = new createjs.Stage("stage");
var angle = 0;
var circle = new createjs.Shape();
circle.graphics.beginFill("#FF0000").drawEllipse(-25, -25, 50, 50).endFill();
circle.x = 100;
circle.y = 100;
var crosshair = new createjs.Shape();
crosshair.graphics.setStrokeStyle(2).beginStroke("#FF0000").moveTo(5, 0).lineTo(5, 10).moveTo(0, 5).lineTo(10, 5).endStroke();
stage.addChild(circle);
stage.addChild(crosshair);
createjs.Ticker.addEventListener("tick", function(){
angle++;
if(angle > 360)
angle = 1;
var rads = angle * Math.PI / 180;
var x = 100 * Math.cos(rads);
var y = 100 * Math.sin(rads);
crosshair.x = x + 100;
crosshair.y = y + 100;
stage.update();
});
Put another point respect to origin point with the same direction
var one_meter = 1 / map_resolution;
// get one meter distance from pointed points
var extra_x = one_meter * Math.cos(temp_rotation);
var extra_y = one_meter * Math.sin(-temp_rotation);
var new_x = mapXY.x + extra_x;
var new_y = mapXY.y + extra_y;
var home_point = new createjs.Shape().set({ x: new_x, y: new_y });
home_point.graphics.beginFill("Blue").drawCircle(0, 0, 10);
stage.addChild(home_point);
stage.update();
I have a ball and a stick (for a billiard game).
First the ball is placed in a position of a table. On clicking the ball the stick appears, in such a way that we can determine the angle by which stick is placed by clicking the ball (on clicking we determine the angle of mouse with respect to centre of ball and place the stick at that angle touching the ball).
So now the stick is also in the table. Now I am dragging the stick along that angle only, if dragged in another angle than the initial angle it returns false.
On drag end I am calculating the distance moved by the stick and the stick returns to the initial position touching the ball. Then I am trying to move the ball with respect to the angle of the stick and the distance moved by the stick.
The ball moves here but not with respect to any of these. That has become my issue I have updated the fiddle here:
strikerGroup.on('dragend', function () {
var strikerLastPos = strikerGroup.getAbsolutePosition();
strikerGroup.setPosition(initStrikerGrpX, initStrikerGrpY);
striker.speedX = striker.speedY = 2;
var strikerGrpDistMoved = Math.sqrt(((strikerLastPos.x - strikerGroup.getAbsolutePosition().x) * (strikerLastPos.x - strikerGroup.getAbsolutePosition().x)) + ((strikerLastPos.y - strikerGroup.getAbsolutePosition().y) * (strikerLastPos.y - strikerGroup.getAbsolutePosition().y)));
var newX = striker.getX() + (Math.cos(theta) * strikerGrpDistMoved);
var newY = striker.getY() - (Math.sin(theta) * strikerGrpDistMoved);
var strikerMove = new Kinetic.Tween({
node: striker,
duration: 5,
x: newX,
y: newY,
easing: Kinetic.Easings.EaseInOut
});
console.log(striker.getX());
strikerMove.play();
layer.batchDraw();
// strikerGroup striked the striker!!!!
});
You calculate the angle of the billiard stick to the ball like this:
var dx = ballX - stickX;
var dy = ballY - stickY;
var angle = Math.atan2(dy,dx);
Then you can move the ball along that angle like this:
var newBallX = ballX + desiredRollDistance * Math.cos(angle);
var newBallY = ballY + desiredRollDistance * Math.sin(angle);
Your desired roll distance would be based on how far the stick was drawn back away from the ball.
The further the stick was drawn back == the further the ball would travel.
You can calculate the distance from the stick to the ball like this:
var dx = ballX - stickX;
var dy = ballY - stickY;
var lengthFromStickToBall = Math.sqrt(dx*dx+dy*dy);
Here is a Demo: http://jsfiddle.net/m1erickson/B6K9z/
Suppose my div has left:200px and top:400px, after I apply a rotate transform of suppose 90 deg the above top and left positions no more point to the old positions. Now how can we calculate the new top and left for the transformed div which are equivalent to the left and top positions of the non-transformed div after rotation.
Edited answer
Besides the starting position of the corner point (top-left in your example), and the rotation angle, we also need to know the position of the reference point of the rotation. This is the point around which we rotate the div (CSS calls it transform-origin). If you don't specify it, then normally, the centre of mass of the element is used.
I don't know of any JavaScript method that simply calculates it for you, but I can show you its Math, and a simple JS implementation.
Math
P: original position of the corner point, with (Px, Py) coordinates
O: reference point of the rotation, with (Ox, Oy) coordinates
Calculate the original position of P, relative to O.
x = Px - Ox
y = Py - Oy
Calculate the rotated position of P, relative to O.
x' = x * cos(angle) - y * sin(angle)
y' = x * sin(angle) + y * cos(angle)
Convert this position back to the original coordinate system.
Px' = x' + Ox
Py' = y' + Oy
If you're not aware of the formulas in step #2, you can find an explanation here.
JavaScript implementation
function rotatedPosition(pLeft, pTop, oLeft, oTop, angle){
// 1
var x = pLeft - oLeft;
var y = pTop - oTop;
// 2
var xRot = x * Math.cos(angle) - y * Math.sin(angle);
var yRot = x * Math.sin(angle) + y * Math.cos(angle);
// 3
var pLeftRot = xRot + oLeft;
var pTopRot = yRot + oTop
return {left: pLeftRot, top: pTopRot};
}
rotatedPosition requires you to define the original position of the point and the reference point, plus the angle.
In case you need a method which takes only a single argument, the div element itself, and computes the rest for you, then you can do something like:
function divTopLeftRotatedPosition(div){
var pLeft = // ...
var pTop = // ...
var width = // ...
var height = // ...
var angle = // ...
return rotatedPosition(pLeft, pTop, pLeft + width / 2, pTop + height / 2, angle);
}
I'm sorry to say that Math really isn't my strong suit. Normally I can get by, but this has got me totally stumped.
I'm trying to code up a quiz results screen in HTML/CSS/Javascript.
On my interface, I have a semicircle (the right hemisphere of a target).
I have a range of 'scores' (integers out of 100 - so 50, 80, 90 etc.).
I need to plot these points on the semicircle to be n% away from the centre, where n is the value of each score - the higher the score, the closer to the centre of the target the point will appear.
I know how wide my semicircle is, and have already handled the conversion of the % values so that the higher ones appear closer to the centre while the lower ones appear further out.
What I can't wrap my head around is plotting these points on a line that travels out from the centre point (x = 0, y = target height/2) of the target at a random angle (so the points don't overlap).
Any suggestions are gratefully received!
Do you have an example of what you want this to look like? It sounds like you want to divide up the circle into N slices where N is the number of points you need to display, then plot the points along each of those radii. So you might have something like:
Edit: code was rotating about the origin, not the circle specified
var scores = [];
//...
//assume scores is an array of distances from the center of the circle
var points = [];
var interval = 2 * Math.PI / N;
var angle;
for (var i = 0; i < N; i++) {
angle = interval * i;
//assume (cx, cy) are the coordinates of the center of your circle
points.push({
x: scores[i] * Math.cos(angle) + cx,
y: scores[i] * Math.sin(angle) + cy
});
}
Then you can plot points however you see fit.
After much headscratching, I managed to arrive at this solution (with the help of a colleague who's much, much better at this kind of thing than me):
(arr_result is an array containing IDs and scores - scores are percentages of 100)
for (var i = 0; i < arr_result.length; i++){
var angle = angleArray[i]; // this is an array of angles (randomised) - points around the edge of the semicircle
var radius = 150; // width of the semicircle
var deadZone = 25 // to make matters complicated, the circle has a 'dead zone' in the centre which we want to discount
var maxScore = 100
var score = parseInt(arr_result[i]['score'], 10)
var alpha = angle * Math.PI
var distance = (maxScore-score)/maxScore*(radius-deadZone) + deadZone
var x = distance * Math.sin(alpha)
var y = radius + distance * Math.cos(alpha)
$('#marker_' + arr_result[i]['id'], templateCode).css({ // target a specific marker and move it using jQuery
'left' : pointX,
'top': pointY
});
}
I've omitted the code for generating the array of angles and randomising that array - that's only needed for presentational purposes so the markers don't overlap.
I also do some weird things with the co-ordinates before I move the markers (again, this has been omitted) as I want the point to be at the bottom-centre of the marker rather than the top-left.