Getting a Model to Clamp to the Camera in 3D - c#

I'm trying to get a 3D model that I've implemented into Monogame (Xna 4.0) code to constantly be in the bottom right of the screen, it's a gun - basic pistol model, there's nothing fancy - doesn't even have textures! But I can't figure it out, I've tried some maths to get it to stay there, but the problem is that the X and Z offset I set is hard-coded so when the player rotates the camera, while the pistol rotates to face the new LookAt, the actual model doesn't move position so it goes out of sight very quickly.
It's been a long road for a pretty pointless 3D game for an A-Level (Highschool equivalent I suppose) course in computer science which I'm making in Monogame Xna to rake in those complexity marks, but I've got to the last stretch, I need to get my fbx model to sit like the player is holding it in the bottom right of the screen. Right now, if it sits in the centre of the screen, there's not much notice - however, if I increase my X or Z offset to position it where I want, when you begin to rotate your camera it falls behind the camera or goes in a massive circle around the camera which breaks immersion (despite how silly it sounds it really bugs me).
After the simple way didn't work, I tried attaching it to the BoundingBox that the current camera is using: Still left a gap, tried a BoundingSphere to see if I could get it to move around the sphere's edge and follow the player that way: No luck. Yet.
tl;dr I've tried attaching gun to BoundingBox, BoundingSphere and regular Vector Positioning to no success.
if (pausedGame == false)
{
camera.UpdateCamera(gameTime);
if (carryingWeapon)
{
weapons[weaponToCarry].SetRotationMatrix(Matrix.CreateRotationX(camera.GetCameraRotation().X), Matrix.CreateRotationY(camera.GetCameraRotation().Y));
weapons[weaponToCarry].SetPosition(camera.weaponSlot.Center + camera.GetLookAtOffSet());
//Debug.WriteLine(weapons[weaponToCarry].Position);
//Debug.WriteLine(camera.Position);
}
CheckCollision();
AIMovement();
UpdateVisitedNodes(updatedNodes);
}
that was my update for it, simple setting position and rotation of model. The 'weaponslot.Centre' is due to the fact that right now I left on using BoundingSphere so that piece of code is still in there.
else if (_state == ItemState.Dynamic)
{
foreach (var mesh in model.Meshes)
{
foreach (BasicEffect effect in mesh.Effects)
{
effect.EnableDefaultLighting();
effect.PreferPerPixelLighting = true;
effect.World = Matrix.CreateScale(SCALE, SCALE, SCALE) * rotationMatrix *
Matrix.CreateTranslation(position);
effect.View = view;
effect.Projection = projection;
}
mesh.Draw();
}
}
this is my draw for the actual item, it's nothing fancy.
Like you can imagine, I want it to be locked in the bottom right of the frustum and moves it's position according to where the player is looking themselves. Right now the positioning of the item is really messed up - any conceptual help is welcome! Thanks.

What I would do is parenting the gun to the camera (it's done that way in engines like unity as well).
You would need to do that by yourself in mono (I think) - the basic principle is to first transform the gun relative to the cam and multiply with the cameras World-Matrix afterwards - this also means to have the guns position relative to the camera
gun.worldmatrix = Matrix.CreateTranslation(gun.position) * camera.world;
You now just move and rotate the camera and the gun keeps in relative position to the camera. Remember: The guns position/(rotation) has to be relative to the camera in that case.
The important thing is just the order in which you multiply the matrices (haven't tried it, hopefully I got it right above)

Related

Unity Camera Maintain perspective while rotating

I'm trying to write a camera script which will maintain the perspective point which I'm looking at while rotating.
public void RegisterRotationControls()
{
var horizontal = CrossPlatformInputManager.GetAxis(InputAxisName.Horizontal.ToString());
offsetX = Quaternion.AngleAxis(horizontal * turnSpeed, Vector3.up) * offsetX;
transform.position = player.position + offsetX;
transform.LookAt(player.position + PlayerOffset);
}
Currently this script is only working properly when the camera is looking at the player, however the camera has freedom to move position yet the rotation should maintain relativity to the center.
The top row dipicts the current behavior, while the bottom depicts what should be occuring
Manually calculating the rotation to move a camera in a circle while retaining its original (relative) facing is somewhat cumbersome, and requires you to do a lot more math than you need to.
A popular solution is to parent the camera under an empty GameObject, and centering that GameObject on the point/object you want the camera to rotate around. (This basically internalizes a lot of the calculations, letting the engine do the heavy lifting.) Then, you can locally translate the camera as needed and then rotate the container GameObject to move the camera in a circle.

(Unity) Issue with rotation when transform.up is aligning to a direction

I am testing out some ideas in Unity where a player can walk around a circle while staying on it (so the circle has its own gravity) and also being oriented properly. This game is currently being done in 2D, so all objects are sprites.
I do hope I can explain myself properly. Please ask if you need any further clarification...
It appeared that I succeeded with my idea until I noticed something odd.
So as expected, the player moves around the circle without falling off (custom gravity worked just fine) and its Z rotation is affected as it aligns itself with a direction:
// Align code:
// We reverse the direction so the object is standing up the right way.
private void Update()
{
transform.up = -(planet.position - transform.position);
}
It works... mostly. However, when the player object's rotation Z naturally reaches 180, it appears to flip horizontally (like a mirror effect) and then it returns to normal as rotation Z leaves 180. The visual flip happens because for some reason the object's Y becomes 180 at the same time too. At no other point does X or Y change in regards to rotation. Only Z. So the moment Z hits 180, Y is affected and the moment we leave Z 180, Y returns to 0.
I'm happy to provide a quick video of it happening in-game if anybody needs some visual understanding of what's going on.
The visibility of this bug tends to rely on how fast you're moving around the circle. If you're moving fast enough, you can probably skip over 180 and not see it happen at all, however if you move slow enough there's no denying it's there. It's also problematic for the fact that I simply make the camera a child of the player so when the player flips, so does the camera causing the entire scene to flip which can look extremely glitchy for a player to see.
I really have no idea how to tackle this issue as I have no clue why it would do such a thing. At every other rotation value it behaves just fine. It's only at Z = 180 (so the object is exactly upside down) does it decide to rotate in the wrong ways.
EDIT: Changed tag to Unity3D
It's probably because of converting quaternions (which is what's actually used internally for rotations) to euler angles for display. Can you try to use Quaternion.Slerp to rotate?

Different method for collision detection using faces of a rectangle

I have been working on and off of a certain game I'm making (trying to get it finished!) and a problem that has been unsolved for a while in it is the collision detection between a ball and a square.
Basically what I want to happen eventually is depending on the angle/way the rectangle is facing, I want the ball to bounce of it accordingly (I know I could just inverse the ball direction before/as it hit the square).
At the moment though, my current problem is trying to inverse the correct X and Y components depending on the side/face that the ball collides with the square, e.g. if the ball hits the right side of the square, then I need to inverse the ball's X component.
This doesn't seem to work and I was wondering if I could somehow label each side of the rectangle, in terms of for the top of it label that 'face 1' or something, then for the right side of it 'face 2' or 'side 2', etc...
I have provided some code below (this is the code I'm using now):
//(collision with right side of square)
if (theBall.GetRectangle.Left <= thePaddle.GetRectangle.Right)
{
theBall.pVelocity.X = -theBall.pVelocity.X;
}
//(collision with bottom of square)
if (theBall.GetRectangle.Top <= thePaddle.GetRectangle.Bottom)
{
theBall.pVelocity.Y = -theBall.pVelocity.Y;
}
I have written the code for the other 2 sides of the rectangle but they are just the opposite of the two above, i.e. for collision with top of rectangle = opposite of bottom, etc.
EDIT: the object I am checking against if the ball has collided with DOES NOT move, I mean it only rotates... so I don't know if this is important (it probably is, therefore I apologise for missing this info out at the start).
EDIT # 23:36: ok, I have tried something.... and it hasn't worked... :(
public Vector2 DistBetweenBallAndBlock(Paddle thePaddle, Ball theBall)
{
Vector2 centreOfBall = new Vector2(theBall.Texture.Width / 2, theBall.Texture.Height / 2);
float distX = thePaddle.Position.X - centreOfBall.X;
float distY = thePaddle.Position.Y - centreOfBall.Y;
if (distX < 0)
{
distX = -distX;
}
if (distY < 0)
{
distY = -distY;
}
return new Vector2(distX, distY);
}
I have then tried to just print the result just to get an idea of what's going on and what sort of values are being output:
Vector2 a = ball.DistBetweenBallAndBlock(paddle1, ball);
angleOfPaddle = Math.Atan2(a.Y, a.X);
I then just print this value to screen, however I am getting the same result of 0.63...
To detect a collision between Rectangles you could use Rectangle.Intersect method, instead of checking the objects' sides.
And to detect which side of the rectangle is hit, you can compute the Vector2 between the ball center and the rectangle center. Getting its angle with Math.Atan2 you can easily know which face of the rectangle has been hit.
Looked up some Vector stuff, based on my comment.
Collision Style
The optimal way of colliding with a circular object is to collide using a vector between it and the nearest point of the object you're checking against. If the distance is less than or equal to the radius of the circle, there is a collision. The advantages of this method are that you don't have to keep track of a rectangle, you get circular collision, and you get the angle of the collision from the vector.
How You Do It
I'll assume you have some strategy to keep from considering every object every frame, and keep to the basic problem. Your paddle has 4 vertices, one for each corner. Because your ball is essentially a vertex with a picture drawn over it, you can easily check the distance between the ball and each corner of your paddle. Two will be nearest. From there, it's just a matter of finding out if that edge collides. I found a solution to that here, which includes a nice formula.
Does that help?

What kind of projection and camera to use for 3D/2D top-down view?

I am probably going to smack myself in the face once the answer becomes clear, but I'm afraid I can't figure out what to do with my camera in order to effectively mix scrolling 2D and 3D objects.
Currently I'm using a displacement camera, which I implemented when this was still just a 2D project. The camera displaces the draw position of all 2D objects based on where its position is in the world. In case that's not clear, here's the code:
public void DrawSprite(Sprite sprite)
{
Vector2 drawtopLeftPosition = ApplyTransformation(sprite.Position);
//TODO: add culling logic here
sprite.Draw(spriteBatch, drawtopLeftPosition);
}
private Vector2 ApplyTransformation(Vector2 spritetopLeftPosition)
{
return (spritetopLeftPosition - topLeftPosition);
}
Simple enough. This worked effectively until I tried to push 3D into the equation. I have several spheres which I want to display alongside the 2D game objects. I have already figured out how to Z-order everything properly, but I cannot get the spheres to project correctly. They appear and disappear depending on where the camera is and often fly around erratically with even the slightest camera movement. Here are my camera matrices:
viewMatrix = Matrix.CreateLookAt(new Vector3(Position, 1f), new Vector3(Position , 0), Vector3.Up);
projectionMatrix = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, graphics.Viewport.AspectRatio, 1f, 1000f);
Note that the Vector2 Position is the absolute center of the camera viewport, not the top left or anything like that. I have also tried using OrthographicOffCenter and Orthographic projections. The viewMatrix updates each frame with the same CreateLookAt function based on the current camera position. Here is the Camera method which draws a 3D object:
public void DrawModel(Sprite3D sprite, GraphicsDevice device)
{
device.BlendState = BlendState.Opaque;
device.DepthStencilState = DepthStencilState.Default;
device.SamplerStates[0] = SamplerState.LinearWrap;
sprite.DrawBasic(this, device);
}
My main point of confusion is whether I should displace the 3D objects as well as the 2D. I have tried doing this but have had more success actually glimpsing the 3D objects on screen if I do not.
Here is the Sprite3D section for drawing:
public Matrix GenerateWorld()
{
return Matrix.CreateScale(Scale) * Matrix.CreateTranslation(new Vector3(Position, -ZPosition)) * Matrix.CreateFromYawPitchRoll(Rotation3D.X, Rotation3D.Y, Rotation3D.Z);
}
private Matrix GenerateDEBUGWorld()
{
return Matrix.CreateScale(Scale) * Matrix.CreateTranslation(Vector3.Zero);
}
public void DrawBasic(Camera camera, GraphicsDevice device)
{
Matrix[] transforms = new Matrix[Model.Bones.Count];
Model.CopyAbsoluteBoneTransformsTo(transforms);
foreach (ModelMesh mesh in Model.Meshes)
{
foreach (BasicEffect effect in mesh.Effects)
{
effect.World = (transforms[mesh.ParentBone.Index] * GenerateDEBUGWorld());
effect.View = camera.ViewMatrix;
effect.Projection = camera.ProjectionMatrix;
}
mesh.Draw();
}
}
GenerateDEBUGWorld simply places the sphere at 0,0 so that I always know where it should be.
A few things I've noticed:
If I set the Z position of the camera to a larger number (say, 10), spheres move less erratically but still wrongly
Using OrthographicOffCenter projection displays tiny little spheres at the center of the screen which do not change in position or scale, even if I multiply the Sprite3D's Scale variable by 1000.
So, the main question: should I even use a displacement camera, or is there a better way to mix 2D and 3D effectively (billboarding, perhaps? but I don't want to add unnecessary complexity to this project)? And, if I do use displacement, should I displace the 3D objects as well as the 2D? Finally, what sort of projection should be used?
Update: If I move the camera back to z position of 100 and make the spheres scale up by 100%, they behave somewhat normally-- when the camera moves left they move right, up they move down, etc, as you'd expect for a perspective projection. However they move far too much in relation to the rest of the 2D objects. Is this a matter of scale? I feel like it's very difficult to reconcile the way sprites are shown and the way 3D objects are shown onscreen...
It sounds like what you're looking for is Unproject - this will take a 2D point and "cast it" into 3D space. If all of your objects are in 3D space, your typical Ortho/LookAt camera should work as you'd expect...I'm also going completely from memory here, since I can't be arsed to look it up. ;)
Just a thought, but your Z in your view matrix is being constrained between 1 and 0. Update the Z component of those Vector3's to be the actual Z of the camera and see what that does for you.
<2 cents/>

making my player sprite land on top of my platform sprite

in my XNA game(im fairly new to XNA by the way) i would like to have my player sprite land on top of a platform. i have a player sprite class that inherits from my regular sprite class, and the regular sprite class for
basic non playable sprite stuff such as boxes, background stuff, and platforms. However, i am unsure how to implement a way to make my player sprite land on a platform.
My player Sprite can jump and move around, but i dont know where and how to check to see if it is on top of my platform sprite.
My player sprites jump method is here
private void Jump()
{
if (mCurrentState != State.Jumping)
{
mCurrentState = State.Jumping;
mStartingPosition = Position;
mDirection.Y = MOVE_UP;
mSpeed = new Vector2(jumpSpeed, jumpSpeed);
}
}
mStartingPosition is player sprites starting position of the jump, and Position is the player sprites current position. I would think that my code for checking to see whether my player sprite is on top of my platform sprite. I am unsure how to reference my platform sprite inside of the playersprite class and inside of the jump method.
i think it should be something like this
//platformSprite.CollisonBox would be the rectangle around the platform, but im not
//sure how to check to see if player.Position is touching any point
//on platformSprite.CollisionBox
if(player.Position == platformSprite.CollisionBox)
{
player.mDirection = 0;
}
Again im pretty new to programming and XNA, and some of this logic i dont quite understand so any help on any of it would be greatly appreciated:D
Thanks
If player.Position is a Point and CollisionBox is a Rectangle, you could use
if (platformSprite.CollisionBox.Contains (player.Position))
I was playing around with something like this just a few days ago.
What you're calling a CollisionBox I called a BoundingBox. A BoundingBox is a Rectangle which represents the area occupied by the sprite.
You'll probably find it helpful to define a BoundingBox for your sprites instead of just using their position.
You can easily test for the collision of Rectangles using the following code:
if (player.BoundingBox.Intersects(platform.BoundingBox)
{
// handle collision here...
}
For this to work, make sure that the X and Y coordinates of your BoundingBox are correctly reflecting your sprite's position.
You may want to look at the Platformer Starter Kit. It includes this as well as detecting when the player is touching an enemy, collecting gems, etc.
The answer about BoundingBox is right, but IMHO it is easier to use Rectangle if you are making a 2D game rather than BoundingBox, wich is designed for a 3D one.
Anyway both objects have intersection / collision test methods that takes Vector2 or Point as parameter.
Now you have to have a box for your player, and a box for your platform. If one collide with the other, you have to check from where (the player can hit the platform from up, down, left, right, or maybe up & left on the left up corner).
If the player is on top of the platform, then just stop his fall, probably by setting his Y-speed component to 0.
Maybe you will need the player to go through the platform when jumping (player hit from bottom) but not when falling (player hit from top).

Categories