I am making a weapon in my 2D game that will shoot at a player-market target. Is there any way to have my bullet sprite follow the raycast2D that the weapon casts?
Since you have a ray (aka vector), on every frame of your animation, you can calculate the position of your sprite like so
spritePosition2d = rayStartPosition2d + rayNormal2d * bulletSpeed * timeElapsedFromShooting
where timeElapsedFromShoot in seconds.
Feel free to give more details in the comments so I can update the answer to your requirements.
Let me know if this helps.
Related
I'm currently attempting to create a first-person space flight camera.
First, allow me to define what I mean by that.
Notice that I am currently using Row-Major matrices in my math library (meaning, the basis vectors in my 4x4 matrices are laid out in rows, and the affine translation part is in the fourth row). Hopefully this helps clarify the order in which I multiply my matrices.
What I have so Far
So far, I have successfully implemented a simple first-person camera view. The code for this is as follows:
fn fps_camera(&mut self) -> beagle_math::Mat4 {
let pitch_matrix = beagle_math::Mat4::rotate_x(self.pitch_in_radians);
let yaw_matrix = beagle_math::Mat4::rotate_y(self.yaw_in_radians);
let view_matrix = yaw_matrix.get_transposed().mul(&pitch_matrix.get_transposed());
let translate_matrix = beagle_math::Mat4::translate(&self.position.mul(-1.0));
translate_matrix.mul(&view_matrix)
}
This works as expected. I am able to walk around and look around with the mouse.
What I am Attempting to do
However, an obvious limitation of this implementation is that since pitch and yaw is always defined relative to a global "up" direction, the moment I pitch more than 90 degrees, getting the world to essentially being upside-down, my yaw movement is inverted.
What I would like to attempt to implement is what could be seen more as a first-person "space flight" camera. That is, no matter what your current orientation is, pitching up and down with the mouse will always translate into up and down in the game, relative to your current orientation. And yawing left and right with your mouse will always translate into a left and right direction, relative to your current orientation.
Unfortunately, this problem has got me stuck for days now. Bear with me, as I am new to the field of linear algebra and matrix transformations. So I must be misunderstanding or overlooking something fundamental. What I've implemented so far might thus look... stupid and naive :) Probably because it is.
What I've Tried so far
The way that I always end up coming back to thinking about this problem is to basically redefine the world's orientation every frame. That is, in a frame, you translate, pitch, and yaw the world coordinate space using your view matrix. You then somehow redefine this orientation as being the new default or zero-rotation. By doing this, you can then, in your next frame apply new pitch and yaw rotations based on this new default orientation, which (by my thinking, anyways), would mean that mouse movement will always translate directly to up, down, left, and right, no matter how you are oriented, because you are basically always redefining the world coordinate space in terms relative to what your previous orientation was, as opposed to the simple first-person camera, which always starts from the same initial coordinate space.
The latest code I have which attempts to implement my idea is as follows:
fn space_camera(&mut self) -> beagle_math::Mat4 {
let previous_pitch_matrix = beagle_math::Mat4::rotate_x(self.previous_pitch);
let previous_yaw_matrix = beagle_math::Mat4::rotate_y(self.previous_yaw);
let previous_view_matrix = previous_yaw_matrix.get_transposed().mul(&previous_pitch_matrix.get_transposed());
let pitch_matrix = beagle_math::Mat4::rotate_x(self.pitch_in_radians);
let yaw_matrix = beagle_math::Mat4::rotate_y(self.yaw_in_radians);
let view_matrix = yaw_matrix.get_transposed().mul(&pitch_matrix.get_transposed());
let translate_matrix = beagle_math::Mat4::translate(&self.position.mul(-1.0));
// SAVES
self.previous_pitch += self.pitch_in_radians;
self.previous_yaw += self.yaw_in_radians;
// RESETS
self.pitch_in_radians = 0.0;
self.yaw_in_radians = 0.0;
translate_matrix.mul(&(previous_view_matrix.mul(&view_matrix)))
}
This, however, does nothing to solve the issue. It actually gives the exact same result and problem as the fps camera.
My thinking behind this code is basically: Always keep track of an accumulated pitch and yaw (in the code that is the previous_pitch and previous_yaw) based on deltas each frame. The deltas are pitch_in_radians and pitch_in_yaw, as they are always reset each frame.
I then start off by constructing a view matrix that would represent how the world was orientated previously, that is the previous_view_matrix. I then construct a new view matrix based on the deltas of this frame, that is the view_matrix.
I then attempt to do a view matrix that does this:
Translate the world in the opposite direction of what represents the camera's current position. Nothing is different here from the FPS camera.
Orient that world according to what my orientation has been so far (using the previous_view_matrix. What I would want this to represent is the default starting point for the deltas of my current frame's movement.
Apply the deltas of the current frame using the current view matrix, represented by view_matrix
My hope was that in step 3, the previous orientation would be seen as a starting point for a new rotation. That if the world was upside-down in the previous orientation, the view_matrix would apply a yaw in terms of the camera's "up", which would then avoid the problem of inverted controls.
I must surely be either attacking the problem from the wrong angle, or misunderstanding essential parts of matrix multiplication with rotations.
Can anyone help pin-point where I'm going wrong?
[EDIT] - Rolling even when you only pitch and yaw the camera
For anyone just stumbling upon this, I fixed it by a combination of the marked answer and Locke's answer (ultimately, in the example given in my question, I also messed up the matrix multiplication order).
Additionally, when you get your camera right, you may stumble upon the odd side-effect that holding the camera stationary, and just pitching and yawing it about (such as moving your mouse around in a circle), will result in your world slowly rolling as well.
This is not a mistake, this is how rotations work in 3D. Kevin added a comment in his answer that explains it, and additionally, I also found this GameDev Stack Exchange answer explaining it in further detail.
The problem is that two numbers, pitch and yaw, provide insufficient degrees of freedom to represent consistent free rotation behavior in space without any “horizon”. Two numbers can represent a look-direction vector but they cannot represent the third component of camera orientation, called roll (rotation about the “depth” axis of the screen). As a consequence, no matter how you implement the controls, you will find that in some orientations the camera rolls strangely, because the effect of trying to do the math with this information is that every frame the roll is picked/reconstructed based on the pitch and yaw.
The minimal solution to this is to add a roll component to your camera state. However, this approach (“Euler angles”) is both tricky to compute with and has numerical stability issues (“gimbal lock”).
Instead, you should represent your camera/player orientation as a quaternion, a mathematical structure that is good for representing arbitrary rotations. Quaternions are used somewhat like rotation matrices, but have fewer components; you'll multiply quaternions by quaternions to apply player input, and convert quaternions to matrices to render with.
It is very common for general purpose game engines to use quaternions for describing objects' rotations. I haven't personally written quaternion camera code (yet!) but I'm sure the internet contains many examples and longer explanations you can work from.
It looks like a lot of the difficulty you are having is due to trying to normalize the transformation to apply the new translation. It seems like this is probably a large part of what is tripping you up. I would suggest changing how you store your position and rotation. Instead, try letting your view matrix define your position.
/// Apply rotation based on the change in mouse position
pub fn on_mouse_move(&mut self, dx: f32, dy: f32) {
// I think this is correct, but it might need tweaking
let rotation_matrix = Mat4::rotate_xy(-y, x);
self.apply_movement(&rotation_matrix, &Vec3::zero())
}
/// Append axis-aligned movement relative to the camera and rotation
pub fn apply_movement(&mut self, rotation: &Mat4<f32>, translation: &Vec3<f32>) {
// Create transformation matrix for translation
let translation = Mat4::translate(translation);
// Append translation and rotation to existing view matrix
self.view_matrix = self.view_matrix * translation * rotation;
}
/// You can get the position from the last column [x, y, z, w] of your view matrix.
pub fn translation(&self) -> Vec3<f32> {
self.view_matrix.column(3).into()
}
I made a couple assumptions about the library:
Mat4 implements Mul<Self> so you do not need to call x.mul(y) explicitly and can instead use x * y. Same goes for Sub.
There exists a Mat4::rotate_xy function. If there isn't one, it would be equivalent to Mat4::rotate_xyz(delta_pitch, delta_yaw, 0.0) or Mat4::rotate_x(delta_pitch) * Mat4::rotate_y(delta_yaw).
I'm somewhat eyeballing the equations so hopefully this is correct. The main idea is to take the delta from the previous inputs and create matrices from that which can then be added on top of the previous view_matrix. If you attempt to take the difference after creating transformation matrices it will only be more work for you (and your processor).
As a side note I see you are using self.position.mul(-1.0). This tells me that your projection matrix is probably backwards. You likely want to adjust your projection matrix by scaling it by a factor of -1 in the z axis.
I am building a Star Fox like game. The player needs to control a ship in order to move trough gaps in the walls. Here are my problems:
I need to somehow detect collision with wall (if any)
How do I make the wall (Rect) slowly get bigger until it reaches a point?
Full code
If the solution can be done with classes, that would be great!
Here's the documentation for the pygame.Rect class: https://www.pygame.org/docs/ref/rect.html#pygame.Rect.inflate
To detect collision, the pygame.Rect class has methods to detect collision between Rects. There are a few there, so you could use collidelist() to check if the player's ship's Rect collides with any of the wall Rects.
The class also has two methods inflate() and inflate_ip which can be used to increase the size of any Rects.
This is an excerpt from Fundamentals of Computer Graphics by Peter Shirley. On Page 114 (in the 3rd edition it reads:
We'd like to be able to change the viewpoint in 3D and look in any
direction. There are a multitude of conventions for specifying viewer
position and orientation. We will use the following one:
the eye position e
the gaze direction g
the view-up vector t
The eye position is a location that the eye "sees from". If you think
of graphics as a photographic process, it is the center of the lens.
The gaze direction is any vector in the direction that the viewer is
looking. The view-up vector is any vector in the plane that both
bisects the viewer's head into right and left halves and points "to
the sky" for a person standing on the ground. These vectors provide us
with enough information to set up a coordinate system with origin e
and uvw basis.....
The bold sentence is the one confusing me the most. Unfortunately the book provides only very basic and crude diagrams and doesn't provide any examples.
Does this sentence mean that all view-up vectors are simply (0, 1, 0)?
I tried it on some examples but it didn't quite match up with the given solutions (though it came close sometimes).
Short answer: the view-up vector is not derived from other components: instead, it is a user input, chosen so as to ensure the camera is right-side up. Or, to put it another way, the view-up vector is how you tell your camera system what direction "up" is, for purposes of orienting the camera.
The reason you need a view-up vector is that the position and gaze direction of the camera is not enough to completely determine its pose: you can still spin the camera around the position/gaze axis. The view-up vector is needed to finish locking down the camera; and because people usually prefer to look at things right-side up, the view-up vector is conventionally a fixed direction, determined by how the scene is oriented in your coordinate space.
In theory, the view-up vector could be in any direction, but in practice "up" is usually a coordinate direction. Which coordinate is "up" is a matter of convention: in your case, it appears the Y-axis is "up", but some systems prefer the Z-axis.
That said, I will reiterate: you can choose pretty much any direction you want. If you want your first-person POV to "lean" (e.g., to look around a corner, or to indicate intoxication), you can tweak your view-up vector to accomplish this. Also, consider camera control in a game like Super Mario Galaxy...
I took a graphics class last year, and I'm pretty rusty. I referenced some old notes, so here goes.
I think that bolded line is just trying to explain what the view-up vector (VUP) would mean in one case for sake of introduction, not what it necessarily is in all cases. The wording is a bit odd; here's a rewording: "Consider a person standing on the ground. VUP in that case would be the vector that bisects the viewer's head and points to the sky."
To determine a standard upward vector, do the following:
Normalize g.
g_norm x (0,1,0) gives you view-right, or the vector to the right of your camera view
view-right x g gives you VUP.
You can then apply a rotation if you wish to do so.
Does this sentence mean that all view-up vectors are simply (0, 1, 0)?
No (0,1,0) is the world up vector. We're looking for the camera's up vector.
Others have written in depth explanations. I will provide the code below, which is largely self documenting. Example is in DirectX C++.
DirectX::XMVECTOR Camera::getDirection() const noexcept
{
// camDirection = camPosition - camTarget
const dx::XMVECTOR forwardVector{0.0f, 0.0f, 1.0f, 0.0f};
const auto lookVector = dx::XMVector3Transform( forwardVector,
dx::XMMatrixRotationRollPitchYaw( m_pitch, m_yaw, 0.0f ) );
const auto camPosition = dx::XMLoadFloat3( &m_position );
const auto camTarget = dx::XMVectorAdd( camPosition,
lookVector );
return dx::XMVector3Normalize( dx::XMVectorSubtract( camPosition,
camTarget ) );
}
DirectX::XMVECTOR Camera::getRight() const noexcept
{
const dx::XMVECTOR upVector{0.0f, 1.0f, 0.0f, 0.0f};
return dx::XMVector3Cross( upVector,
getDirection() );
}
DirectX::XMVECTOR Camera::getUp() const noexcept
{
return dx::XMVector3Cross( getDirection(),
getRight() );
}
I'm starting to develop a poc with the main features of a turn-based RPG similar to Breath of Fire 4, a mixture of 3D environment with characters and items such as billboards.
I'm using an orthographic camera with an angle of 30 degrees on the X axis, I did my sprite to act as a billboard with the pivot in the center, the problem occurs when the sprite is nearing a 3D object such as a wall.
Check out the image:
I had tried the solution leaving the rotation matrix of the billboard "upright", worked well, but of course, depending on the height and angle of the camera toward the billboard it gets kinda flattened, I also changed the pivot to the bottom of the sprite but this problem appears with objects in front of the sprite too. I was thinking that the solution would be to create a fragment shader that relies on the depth texture of some previous pass, I tried to think in how to do it with shaders but I could not figure it out. Could you help me with some article or anything that puts me in the right direction? Thank you.
See what I am trying to achieve on this video.
You had got the right approach. Use the upright matrix, and scale up Z of billboards preparing flattened Z by your camera. The Z scaling should be about 1.1547. It is (1 / cos30), which makes billboards look like original size from the camera with the angle of 30 degrees. It seems a tricky way but developers of BoF4 on the video might use the same solution too.
I am currently working on a tennis game in 3D with unity. When the user hits the ball, say at x=0,y=5,z=0 with velocity at certain position, say z=10, what's the ball x and y coordinate after reaching z=10?
To obtain the ball's velocity, you have to use the ball's RigidBody.
If your script is attached as a component of your ball, you would access it through rigidbody.velocity.magnitude.
Although Heilo's approach would work perfectly, i stress the idea of getting a direct velocity measure, e.g. Distance/Time, time being the frame during an Update in the Update() method.
This is actually very simple to do...
Just get the distance it's traveled between two update functions like so:
var previousPosition : Vector3;
function Update() {
//Get the difference/distance between the previous position and the current position
var velocity = Vector3.Distance(previousPosition, transform.position);
}
Because im doing this over a single frame, there is no need to compute extra math to get the velocity. However, if you wish to do it over second, you'll need to get the FPS for the game and do some math with that.. So i suggest that unless you want to use this direct route, use what Heilo suggests.