Good day. I'm working on a fps game, and I have a problem with camera, or with movement to be pricese... as it doesn't take camera position into accound when performs movement...
I tried to fix it in several different ways, but I guess I just don't understand the math needed :(
Anyway, here's the code:
static public void UpdateCharacterPositionAndCamera(float time)
{
//mouse look update
float rotationSpeed = 3f;
if (Input.mouseState != Input.mouseStatePrevious)
{
float xDifference = Input.mouseState.X - Display.gd.Viewport.Width / 2;
float yDifference = Input.mouseState.Y - Display.gd.Viewport.Height / 2;
yRotation -= rotationSpeed * xDifference * time;
xRotation -= rotationSpeed * yDifference * time;
Mouse.SetPosition(Display.gd.Viewport.Width / 2, Display.gd.Viewport.Height / 2);
}
//camera
Vector3 cameraPosition = playerPos;
Vector3 cameraReference = new Vector3(0f, 0f, -1f);
Matrix rotationMatrix = Matrix.CreateRotationY(MathHelper.ToRadians(yRotation)) * Matrix.CreateRotationX(MathHelper.ToRadians(xRotation));
Vector3 transformedReference = Vector3.Transform(cameraReference, rotationMatrix);
Vector3 cameraLookat = cameraPosition + transformedReference;
viewMatrix = Matrix.CreateLookAt(cameraPosition, cameraLookat, new Vector3(0.0f, 1.0f, 0.0f));
projectionMatrix = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, Display.gd.Viewport.AspectRatio, 0.1f, 2500.0f);
//movement
float moveSpeed = 5.0f;
Vector3 moveVector = new Vector3(0, 0, 0);
if (Input.keyState.IsKeyDown(Keys.Up) || Input.keyState.IsKeyDown(Keys.W))
moveVector += new Vector3(0, 0, -1);
if (Input.keyState.IsKeyDown(Keys.Down) || Input.keyState.IsKeyDown(Keys.S))
moveVector += new Vector3(0, 0, 1);
if (Input.keyState.IsKeyDown(Keys.Right) || Input.keyState.IsKeyDown(Keys.D))
moveVector += new Vector3(1, 0, 0);
if (Input.keyState.IsKeyDown(Keys.Left) || Input.keyState.IsKeyDown(Keys.A))
moveVector += new Vector3(-1, 0, 0);
if (Input.keyState.IsKeyDown(Keys.Q))
moveVector += new Vector3(0, 1, 0);
if (Input.keyState.IsKeyDown(Keys.Z))
moveVector += new Vector3(0, -1, 0);
playerPos += moveVector * moveSpeed;
}
Can you tell me how to change the move vector so it takes camera direction into account...?
Multiply the move vector by the camera rotation matrix, or its inverse (I think it's the inverse). It's the same as the view matrix, but without the translation. If you want to fix the camera onto a plane, you need to zero out the respective component afterwards and perhaps normalize the new vector.
I'm not familiar with the XNA API, so I cannot tell you exactly which methods to use. You might be able to extract the rotation matrix from the view matrix, or you might need to look at the difference (lookAt-positios) from the origin.
aere's how I usually do it:
//movement
Matrix camWorld = Matrix.Invert(view);
Vector3 moveVector = Vector3.Zero;
...
if(...keys.Up... or ...keys.W...)
moveVector += camWorld.Forward;
if(... keys.right or ... keys.D...)
moveVector += camWorld.Right;
...
moveVector.Normalize();
PlayerPos += moveVector * moveSpeed;
Related
basically i am trying to move a character around so when i press W it goes forward but when i let go it doesn't stop(only once in a while). I am on Update void and not fixed Update.
private void Update()
{
if (controller.isGrounded)
{
if(Input.GetKey(KeyCode.W))
{
anim.SetInteger("condition", 1);
moveDir = new Vector3(0, 0, 1);
moveDir *= speed;
moveDir = transform.TransformDirection(moveDir);
}
if(Input.GetKeyUp (KeyCode.W))
{
anim.SetInteger("condition", 0);
moveDir = new Vector3(0, 0, 0);
}
}
rot += Input.GetAxis("Horizontal") * rotSpeed * Time.deltaTime;
transform.eulerAngles = new Vector3(0, rot, 0);
moveDir.y -= gravity * Time.deltaTime;
controller.Move (moveDir * Time.deltaTime);
}
}
Try looking at your animator and checking that Apply Root Motion is checked off.
Animator in Inspector
What root motion does is that it moves your character through the animation instead of through code. Here is a detailed explanation on root motion. What's root motion and how it works
I think I was able to recreate your issue and I was getting the same results. After I unchecked Apply Root Motion, my character stopped once I let go of "W".
i'm currently in progress of a new game where my character has to move. While my character walks forward and i'm rotating it on the z axis, it just rotates instead of walking down the new z axis.
void Update()
{
{
if (Input.GetKeyDown(KeyCode.W))
{
anim.SetInteger("Condition", 1);
moveDir = new Vector3(0, 0, 1);
moveDir *= speed;
moveDir = transform.TransformDirection(moveDir);
}
if (Input.GetKeyUp(KeyCode.W))
{
anim.SetInteger("Condition", 0);
moveDir = new Vector3(0, 0, 0);
}
moveDir.y -= gravity * Time.deltaTime;
controller.Move(moveDir * Time.deltaTime);
}
rot += Input.GetAxisRaw("Horizontal") * rotSpeed * Time.deltaTime;
transform.eulerAngles = new Vector3(0, rot, 0);
}
}
I want to be able to walk forward while changing the z axis instead of walking then stop and then walk again.
You can't do that using a CharacterController because your call to controller.move() overwrites the rotation ... you need to write a custom move function.
I want make an helicopter ai for game,
but i can't figure out how can i do flying movement.
I need 2 types of movement(Flying around and Flying to)
Helicopter will spawn at corner of map and fly to some player - FlyingTo
after Heli will arrive to a player it will start Flying around
and shoot missile.
I tried use transform.Translate, transform.rotation, rigidbody.force and many other things to movement, but didn't find anything working.
Few of my tries:
Vector3 flyto = (nearest.transform.position + new Vector3(0, 100, 0));
Vector3 Kouzlo1 = new Vector3(base.transform.position.x, 0, base.transform.position.z);
Vector3 Kouzlo2 = new Vector3(nearest.transform.position.x, 0, nearest.transform.position.z);
/*Vector3 targetDir = flyto - transform.position;
float step = 10 * Time.deltaTime;
Vector3 newDir = Vector3.RotateTowards(transform.forward, targetDir, step, 0.0f);
transform.rotation = Quaternion.LookRotation(newDir);*/
if (Vector3.Distance(Kouzlo1, Kouzlo2) < 100)
{
var rotate = Quaternion.LookRotation(flyto - transform.position);
transform.rotation = Quaternion.Slerp(transform.rotation, rotate,
Time.deltaTime * 10);
transform.Translate(Vector3.forward * 10 * Time.deltaTime);
/*transform.position = new Vector3(transform.position.x -
Vector3.Distance(Kouzlo1, Kouzlo2), nearest.transform.position.y + 100,
transform.position.z - Vector3.Distance(Kouzlo1, Kouzlo2));
Vector3 vvv = transform.rotation * new Vector3(0f, 1f, 0f);
transform.RotateAround(flyto, vvv, 180 * Time.deltaTime);*/
//transform.RotateAround(flyto, Vector3.forward, 10 * Time.deltaTime);
}else {
//transform.rotation = Quaternion.Slerp(transform.rotation,
Quaternion.LookRotation(flyto -transform.position), 10 * Time.deltaTime);
//transform.position += transform.forward * 10 * Time.deltaTime;
transform.LookAt(nearest.transform.position);
rigidbody.AddForce(Vector3.forward * 10);
}
You need to understand the method Lerp / Slerp first.
Lerp(a, b, t);
When t=0 it returns a, t=1 it returns b, otherwise it returns a value between a and b.
So you may use
Quaternion.Slerp(a, b, t += Time.deltaTime * 10);
And there is another similar method RotateTowards (or MoveTowards in Vector3 / Mathf class) can do same work
Quaternion.RotateTowards(a, b, Time.deltaTime);
I am writing a kind of RTS CameraController in Unity using C#. I already managed to implement the basic functions, but now I am facing a tricky problem. I managed to rotate my camera and afterwards move into the right direction, BUT my scrolling boundaries are not working anymore after rotating (of course because I am just checking for x on the horizontal scrolling, but after rotation I would need to check for y instead). With boundaries I mean the restriction to only scroll until a certain position is reached (x < 0).
I am a bit stuck on how to solve my problem easily. One way would seems to be using conditions and check the rotation before checking for the boundaries, but this doesn't seem like a good aproach for me, just fixing the problem, but not really taking care of the cause. Now my question: Is there an easier or better way to achieve what I am doing? Did I miss something completely?
This is how I want the camera to move in all 4 rotations. The rectangles are my the world, the arrows define my bounds. The camera should always overshoot in backwards direction and stop before the bounds in forward direction.:
Here is my update method (vertDist and horDist are just defining from when on the mousescroll should start):
void LateUpdate() {
//LEFT
if((Input.mousePosition.x < horDist || Input.GetAxis("Horizontal") < 0)
&& transform.position.x > 0)
transform.Translate(-speed, 0, 0);
//RIGHT
if((Input.mousePosition.x > Screen.width - horDist || Input.GetAxis("Horizontal") > 0)
&& transform.position.x < world.worldX)
transform.Translate(speed, 0, 0);
//UP
if((Input.mousePosition.y > Screen.height - vertDist || Input.GetAxis("Vertical") > 0)
&& transform.position.z < world.worldZ - 30) {
Vector3 temp = transform.eulerAngles;
transform.eulerAngles = new Vector3(0, transform.eulerAngles.y, transform.eulerAngles.z);
transform.Translate(0, 0, speed);
transform.eulerAngles = temp;
}
//DOWN
if((Input.mousePosition.y < vertDist || Input.GetAxis("Vertical") < 0)
&& transform.position.z > -10) {
Vector3 temp = transform.eulerAngles;
transform.eulerAngles = new Vector3(0, transform.eulerAngles.y, transform.eulerAngles.z);
transform.Translate(0, 0, -speed);
transform.eulerAngles = temp;
}
//ZOOM
//camera.fieldOfView -= Input.GetAxis("Mouse ScrollWheel");
//ROTATE
if(Input.GetButtonDown("Rotate")){
Vector3 targetPosition = transform.position;
targetPosition += transform.forward * 27;
//transform.Rotate(new Vector3(0, Mathf.Sign(Input.GetAxis("Rotate")) * 90, 0), Space.World);
transform.RotateAround(targetPosition, Vector3.up, Mathf.Sign(Input.GetAxis("Rotate")) * 90);
}
}
Use
transform.Translate(0, 0, -speed, Space.Self); // For all the Translate, use Space.Self
It will only move based on it's local position instead of world position. But you will get what you wanted.
Hope it helps.
Finally I got a solution! I decided to define a Rect, depending on the current rotation and afterwards check if the camera-position is inside the Rect. Thus I had to mess a bit around with the positions, because I don't know why Rect.Contains() only takes the x- and y- coordinate of a position. My camera just moves along the x- and z-axis, so I did this weird coordinate swapping there:
void LateUpdate() {
Rect bounds = DefineBounds();
Vector3 pos = transform.position;
//LEFT
if((/*Input.mousePosition.x < horDist ||*/ Input.GetAxis("Horizontal") < 0)) {
pos += transform.TransformDirection(-speed * Time.deltaTime, 0, 0);
//reset pos if out of bounds
if(!bounds.Contains(new Vector2(pos.x, pos.z)))
pos -= transform.TransformDirection(-speed * Time.deltaTime, 0, 0);
}
//RIGHT
if((/*Input.mousePosition.x > Screen.width - horDist ||*/ Input.GetAxis("Horizontal") > 0)) {
pos += transform.TransformDirection(speed * Time.deltaTime, 0, 0);
//reset pos if out of bounds
if(!bounds.Contains(new Vector2(pos.x, pos.z)))
pos -= transform.TransformDirection(speed * Time.deltaTime, 0, 0);
}
//UP
if((/*Input.mousePosition.y > Screen.height - vertDist ||*/ Input.GetAxis("Vertical") > 0)) {
//Set camera x-angle to 0
Vector3 temp = transform.eulerAngles;
transform.eulerAngles = new Vector3(0, transform.eulerAngles.y, transform.eulerAngles.z);
pos += transform.TransformDirection(0, 0, speed * Time.deltaTime);
//reset pos if out of bounds
if(!bounds.Contains(new Vector2(pos.x, pos.z)))
pos -= transform.TransformDirection(0, 0, speed * Time.deltaTime);
//Reset camera x-angle
transform.eulerAngles = temp;
}
//DOWN
if((/*Input.mousePosition.y < vertDist ||*/ Input.GetAxis("Vertical") < 0)) {
//Set camera x-angle to 0
Vector3 temp = transform.eulerAngles;
transform.eulerAngles = new Vector3(0, transform.eulerAngles.y, transform.eulerAngles.z);
pos += transform.TransformDirection(0, 0, -speed * Time.deltaTime);
//reset pos if out of bounds
if(!bounds.Contains(new Vector2(pos.x, pos.z)))
pos -= transform.TransformDirection(0, 0, -speed * Time.deltaTime);
//Reset camera x-angle
transform.eulerAngles = temp;
}
transform.position = pos;
//ZOOM
//camera.fieldOfView -= Input.GetAxis("Mouse ScrollWheel");
transform.position = new Vector3(pos.x, pos.y - Input.GetAxis("Mouse ScrollWheel"), pos.z);
//ROTATE
if(Input.GetButtonDown("Rotate")){
Vector3 targetPosition = transform.position;
Vector2 posXZ;
targetPosition += transform.forward * 27;
transform.RotateAround(targetPosition, Vector3.up, Mathf.Sign(Input.GetAxis("Rotate")) * 90);
bounds = DefineBounds();
pos = transform.position;
posXZ = new Vector2(pos.x, pos.z);
while(!bounds.Contains(posXZ)) {
posXZ = Vector2.MoveTowards(posXZ, bounds.center, 1);
pos.x = posXZ.x;
pos.z = posXZ.y;
}
transform.position = pos;
}
}
I've followed a tutorial on how to make a first person camera rotate and move in a 3D world in XNA on the Microsoft website. But when I rotate the camera along it's Y axis, it doesn't move the direction it is rotated/facing, instead it moves as if it was facing the direction it was originally faced.
Here's my code:
static Vector3 avatarPosition = new Vector3(0, 0, 0);
static Vector3 cameraPosition = avatarPosition;
Vector3 cameraReference = new Vector3(10, 0, 0);
// Create a vector pointing the direction the camera is facing.
Matrix world = Matrix.CreateWorld(new Vector3(0, -1, 0), Vector3.Forward, Vector3.Up);
Matrix rotationMatrix = Matrix.CreateRotationY(MathHelper.ToRadians(0));
int Rot = 0;
Vector3 worldVector = new Vector3(5,-2, 0);
Matrix view, proj;
Vector3 cameraLookat;
Update()
{
world = Matrix.CreateWorld(worldVector, Vector3.Forward, Vector3.Up);
if (IsKeyDown(Keys.W))
avatarPosition += new Vector3(0.2f, 0f, 0);
if (IsKeyDown(Keys.S))
avatarPosition += new Vector3(-0.2f, 0f, 0);
if (IsKeyDown(Keys.A))
avatarPosition += new Vector3(0f, 0f, -0.2f);
if (IsKeyDown(Keys.D))
avatarPosition += new Vector3(0f, 0f, 0.2f);
if (IsKeyDown(Keys.Left))
Rot += 1;
if (IsKeyDown(Keys.Right))
Rot += -1;
rotationMatrix = Matrix.CreateRotationY(MathHelper.ToRadians(Rot));
// Create a vector pointing the direction the camera is facing.
Vector3 transformedReference = Vector3.Transform(cameraReference, rotationMatrix);
// Calculate the position the camera is looking at.
cameraLookat = transformedReference + cameraPosition;
// Set up the view matrix and projection matrix.
view = Matrix.CreateLookAt(cameraPosition, cameraLookat, new Vector3(0.0f, 1.0f, 0.0f));
proj = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(45), graphics.GraphicsDevice.Viewport.AspectRatio,
0.1f, 1000);
cameraPosition = avatarPosition;
}
Can someone please tell me why the camera does not travel the way it is rotated? Or can someone please just give me the damn code to make one?
I appears the problem you are experiencing is coming from this area of the code:
if (IsKeyDown(Keys.W))
avatarPosition += new Vector3(0.2f, 0f, 0);
if (IsKeyDown(Keys.S))
avatarPosition += new Vector3(-0.2f, 0f, 0);
if (IsKeyDown(Keys.A))
avatarPosition += new Vector3(0f, 0f, -0.2f);
if (IsKeyDown(Keys.D))
avatarPosition += new Vector3(0f, 0f, 0.2f);
Instead of translating the avatarPosition directly along the x or z axis, it should be using the direction you're pointing which appears to be your transformedReference variable.
For instance, to move forward the way the camera is facing:
if (IsKeyDown(Keys.W))
avatarPosition += transformedReference;
However, since the transformedReference variable appears to be normalized, it may not move the avatar the distance you'd hoped. In that case simply multiply it by some constant MOVEMENT_SPEED for something like such:
if (IsKeyDown(Keys.W))
avatarPosition += transformedReference * MOVEMENT_SPEED;