I have a camera in my scene I am controlling with a third party device. If I spin the device, pitch it forward etc the same action is replicated with my camera. However, I'm stuck working out how to always make sure my camera is facing forward and moving forward in spite of its Y rotation.
For example, my camera starts at point 0,0,0 with the same rotation value. If I rotate my camera 90 degrees (either side) and move my camera forward, my view now moves side ways. How can I get it so that where ever my camera is facing, that is my forward direction?
Here is my code so far:
private void MoveForward()
{
Debug.Log("yaw: " + yaw + " pitch: " + pitch + " roll: " + roll);
if( pitch > maxPitch.x && pitch < maxPitch.y && reversePitch == false)
{
camera.rigidbody.AddForce(0, 0,-pitch);
// camera.transform.position += new Vector3(0,0, -pitch);
}
else if ( pitch > maxPitch.x && pitch < maxPitch.y && reversePitch == true)
{
camera.rigidbody.AddForce(0, 0, pitch);
// camera.transform.position += new Vector3(0,0, pitch);
}
else
{
camera.rigidbody.velocity = Vector3.zero;
// camera.transform.position = new Vector3(0,0,0);
}
}
private void MoveSideways()
{
if( roll > maxRoll.x && roll < maxRoll.y && reverseRoll == false)
{
camera.rigidbody.AddForce( roll, 0, 0);
}
else if ( roll < maxRoll.x && roll > maxRollNegative && reverseRoll == false)
{
camera.rigidbody.AddForce( roll, 0 ,0);
}
else if( roll > maxRoll.x && roll < maxRoll.y && reverseRoll == true)
{
camera.rigidbody.AddForce( -roll, 0, 0);
}
else if ( roll < maxRoll.x && roll > maxRollNegative && reverseRoll == true)
{
camera.rigidbody.AddForce( -roll, 0 ,0);
}
else
{
camera.rigidbody.velocity = Vector3.zero;
}
}
private void RotateAround()
{
if(reverseYaw == true)
{
target = Quaternion.Euler(0, yaw,0);
}
else
{
target = Quaternion.Euler(0, -yaw,0);
}
transform.rotation = Quaternion.Slerp(camera.transform.rotation, target, Time.time *rotationSpeed);
}
This occurs because Rigidbody.AddForce takes a world space vector, but you are giving it a local space vector. Use Transform.TransformDirection to convert your vector from local space to world space.
Vector3 force = transform.TransformDirection(0, 0, pitch);
camera.rigidbody.AddForce(force);
Related
My problem is for a wall jump.
I basically want my wall jump to have a bit of force away from to wall to make it look and feel better than just jumping up straight.
The problem is, when I add a force on the x axis, it does not register when I am also holding the arrow towards the wall. If I am not holding down the walking button, then press jump, then the x force works.
I have a feeling that the horizontal axis force is competing with the opposite force that I want to add to the player when he hits the jump button.
Walking code:
private void ApplyMovement()
{
if (isGrounded)
{
myRigidbody.velocity = new Vector2(movementSpeed * movementInputDirection, myRigidbody.velocity.y);
}
else if (!isGrounded && !isWallSliding && movementInputDirection != 0)
{
Vector2 forceToAdd = new Vector2(movementForceInAir * movementInputDirection, 0);
myRigidbody.AddForce(forceToAdd);
if (Mathf.Abs(myRigidbody.velocity.x) > movementSpeed)
{
myRigidbody.velocity = new Vector2(movementSpeed * movementInputDirection, myRigidbody.velocity.y);
}
}
else if (!isGrounded && !isWallSliding && movementInputDirection == 0)
{
myRigidbody.velocity = new Vector2(myRigidbody.velocity.x * airDragMultiplier, myRigidbody.velocity.y);
}
if (isWallSliding)
{
if (myRigidbody.velocity.y < -wallSlideSpeed)
{
myRigidbody.velocity = new Vector2(myRigidbody.velocity.x, -wallSlideSpeed);
}
}
}
Jump code:
private void Jump()
{
if (canJump && !isWallSliding)
{
myRigidbody.velocity = new Vector2(myRigidbody.velocity.x, jumpForce);
numberOfJumpsLeft--;
}
else if ((isWallSliding || isTouchingWall) && !isGrounded && canJump)
{
isWallSliding = false;
numberOfJumpsLeft--;
Vector2 forceToAdd = new Vector2(wallPushForce * -facingDirection, wallJumpForce *
wallJumpDirection.y);
myRigidbody.AddForce(forceToAdd, ForceMode2D.Impulse);
}
}
I'm trying to get the velocity of my character to be zero when it collides with another sprite. It does this, however the distance between the textures increases when it collides if I choose a higher value for speed, the higher the value the further the distance between the textures when collision occurs.
At a speed of 1f the collision seems to work fine (both textures touching) but 1f is too slow for my game and I can't figure out how to increase it without increasing the distance of collision.
I believe this is because I use: rectangle.Right + Velocity.X in my Collision region but I'm not sure how to alter this to fix my problem. Help would be appreciated.
//In Sprite class
#region Collision
protected bool IsTouchingLeft(Sprite sprite)
{
return rectangle.Right + Velocity.X > sprite.rectangle.Left &&
rectangle.Left < sprite.rectangle.Left &&
rectangle.Bottom > sprite.rectangle.Top &&
rectangle.Top < sprite.rectangle.Bottom;
}
protected bool IsTouchingRight(Sprite sprite)
{
return rectangle.Left + Velocity.X < sprite.rectangle.Right &&
rectangle.Right > sprite.rectangle.Right &&
rectangle.Bottom > sprite.rectangle.Top &&
rectangle.Top < sprite.rectangle.Bottom;
}
protected bool IsTouchingTop(Sprite sprite)
{
return rectangle.Bottom + Velocity.Y > sprite.rectangle.Top &
rectangle.Top < sprite.rectangle.Top &&
rectangle.Right > sprite.rectangle.Left &&
rectangle.Left < sprite.rectangle.Right;
}
protected bool IsTouchingBottom(Sprite sprite)
{
return rectangle.Top + Velocity.Y < sprite.rectangle.Bottom &
rectangle.Bottom > sprite.rectangle.Bottom &&
rectangle.Right > sprite.rectangle.Left &&
rectangle.Left < sprite.rectangle.Right;
}
#endregion
//In MainChar class
foreach (var sprite in sprites)
{
if (sprite == this)
{
continue;
}
if (Velocity.X > 0 && IsTouchingLeft(sprite) || Velocity.X < 0 && IsTouchingRight(sprite))
{
Velocity.X = 0;
}
if (Velocity.Y > 0 && IsTouchingTop(sprite) || Velocity.Y < 0 && IsTouchingBottom(sprite))
{
Velocity.Y = 0;
}
if (sprite.rectangle.Intersects(rectangle))
{
hasDied = true;
}
}
Handling multiple collisions correctly will require sub-stepping, repeating the collision checks until there are no more collisions or a counter expires. This is how physics engines operate.
I am going to show a different method. It requires not doing the move that caused the collision and adjusting the velocity by half each step. Slow down until you get there a maximum of log2(Velocity) steps(for velocities < 32, 5 steps):
foreach (var sprite in sprites)
{
if (sprite == this)
{
continue;
}
// it is good practice to use () for mixed && and ||
if ((Velocity.X > 0 && IsTouchingLeft(sprite)) || (Velocity.X < 0 && IsTouchingRight(sprite)))
{
Velocity.X *= 0.5f;
// by halving, the velocity will exponentially decay to 0
}
if ((Velocity.Y > 0 && IsTouchingTop(sprite)) || (Velocity.Y < 0 && IsTouchingBottom(sprite)))
{
Velocity.Y *= 0.5f;
}
if (sprite.rectangle.Intersects(rectangle))
{
hasDied = true;
}
// use new velocity to update the position.
}
You may have to fix the death condition, it is unclear when it should be triggered. The code as written will move up to the other object, but never overlap on its own.
The following code is what I believe you intended(move at a speed of 1 until overlap)
foreach (var sprite in sprites)
{
if (sprite == this)
{
continue;
}
// it is good practice to use () for mixed && and ||
if ((Velocity.X > 0 && IsTouchingLeft(sprite)) || (Velocity.X < 0 && IsTouchingRight(sprite)))
{
if (Velocity.X >= 2) // cap it at a minimum above 1
{
Velocity.X *= 0.5f;
// by halving, the velocity will exponentially decay to 0
}
if (sprite.rectangle.Intersects(rectangle))
{
Velocity.X = 0; // probably need to do this for Y here as well.
hasDied = true;
}
}
if ((Velocity.Y > 0 && IsTouchingTop(sprite)) || (Velocity.Y < 0 && IsTouchingBottom(sprite)))
{
if (Velocity.Y >= 2) // cap it at a minimum above 1
{
Velocity.Y *= 0.5f;
// by halving, the velocity will exponentially decay to 0
}
if (sprite.rectangle.Intersects(rectangle))
{
Velocity.Y = 0; // probably need to do this for X here as well.
hasDied = true;
}
}
}
So basically what i am trying to do is create an animation script that does not just take simple direction like Up(0,1),Down(0,-1),Left(-1,0),Right(1,0)(Those numbers being vectors) but to have range of directions for to fill in the gap between those directions and play the more dominant direction Up,Down,Left,Right.
So each quarter section below to be set to an animation.
I have created a few scripts to try and achieve this i came up with one that wasn't perfect but it did work when dragging the enemies around in the scene editor while the game was running. When using their movements scripts for their movement it did not work. The animation script is below and underneath that how i move the enemies.
Below would be how i calculate the direction the enemy was moving:
IEnumerator directionFinder()
{
// while (true)
//{
Vector3 previousPosition = transform.position;
yield return new WaitForSeconds(1/10);
currentPosition = transform.position;
currentPosition.x = currentPosition.x - previousPosition.x;
currentPosition.y = currentPosition.y - previousPosition.y;
currentPosition.z = 0f;
angle = (Mathf.Atan2(currentPosition.y, currentPosition.x) * Mathf.Rad2Deg);
if(angle < 0)
{
angle = Mathf.Abs(angle) + 180;
}
// }
}
Then this is what calculated which animation to use:
void animationDirection()
{
if (angle == 0)
{
anim.SetInteger("Move", currentAnim);
}
if (angle > 45 && angle <135)//W
{
anim.SetInteger("Move", 1);
currentAnim = 1;
}
else if (angle > 135 && angle < 225)//A
{
anim.SetInteger("Move", 2);
currentAnim = 2;
}
else if (angle > 225 && angle < 315)//S
{
anim.SetInteger("Move", 3);
currentAnim = 3;
}
else if((angle < 45 && angle >= 0) || (angle > 315 && angle <= 0))//D
{
anim.SetInteger("Move", 4);
currentAnim = 4;
}
}
This using the code below it would move to the waypoint that was selected via the rest of my code that would just loop through a set of waypoints changing the waypoint when it arrived at one.
transform.position = Vector2.MoveTowards(transform.position, waypoints[waypointNUmber].transform.position, speed * Time.deltaTime);
Below I tried another way but it did not work and created weird buggy animations:
IEnumerator directionalAnimation()
{
while (true)
{
Debug.Log("working");
Vector2 previousPosition = transform.position;
yield return new WaitForSeconds(1/10);
Vector2 currentPosition = transform.position;
Vector2 direction = (previousPosition - currentPosition).normalized;
float moveX = direction.x;
float moveY = direction.y;
if (moveX < 0)
{
moveXNegative = true;
moveX = Mathf.Abs(moveX);
}
if (moveY < 0)
{
moveYNegative = true;
moveY = Mathf.Abs(moveY);
}
if (direction.magnitude != 0)
{
if (moveX > moveY)
{
if (moveXNegative)
{
//left
Walking = true;
anim.SetInteger("Move", 2);
moveYNegative = false;
}
else
{
//Right
Walking = true;
anim.SetInteger("Move", 4);
moveYNegative = false;
}
}
else if (moveY > moveX)
{
if (moveYNegative)
{
//Down
Walking = true;
anim.SetInteger("Move", 3);
}
else
{
//Up
Walking = true;
anim.SetInteger("Move", 1);
}
}
}
else
{
source.Pause();
Walking = false;
}
if (Walking)
{
source.UnPause();
}
}
}
i have problem with blocking movement i already read question on Blocking Movement On Collision
but i dont have any idea for my own problem.
if you can give me the logic i will try to solve my own problem.
i hope you can help me
this my player class update
:EDIT: thanks for Kai Hartmann for reply
my problem is for 2D graphic and i want to know how to stop movement when object1 collision with object2
public void Update(GameTime gameTime)
{
// Ani Test
up_ani.Update(gameTime);
down_ani.Update(gameTime);
left_ani.Update(gameTime);
right_ani.Update(gameTime);
position += velocity;
if (Keyboard.GetState().IsKeyDown(key.MoveUp) && prevState.IsKeyUp(key.MoveDown) && prevState.IsKeyUp(key.MoveRight) && prevState.IsKeyUp(key.MoveLeft))
{
currentFace = FacePosition.Up;
velocity.Y = -3;
}
else if (Keyboard.GetState().IsKeyDown(key.MoveDown) && prevState.IsKeyUp(key.MoveUp) && prevState.IsKeyUp(key.MoveRight) && prevState.IsKeyUp(key.MoveLeft))
{
currentFace = FacePosition.Down;
velocity.Y = 3;
}
else if (Keyboard.GetState().IsKeyDown(key.MoveRight) && prevState.IsKeyUp(key.MoveDown) && prevState.IsKeyUp(key.MoveUp) && prevState.IsKeyUp(key.MoveLeft))
{
currentFace = FacePosition.Right;
velocity.X = 3;
}
else if (Keyboard.GetState().IsKeyDown(key.MoveLeft) && prevState.IsKeyUp(key.MoveDown) && prevState.IsKeyUp(key.MoveRight) && prevState.IsKeyUp(key.MoveUp))
{
currentFace = FacePosition.Left;
velocity.X = -3;
}
else
{
//currentFace = FacePosition.Down;
velocity = Vector2.Zero;
}
prevState = Keyboard.GetState();
}
and this my collision
public void IntersectWithScore(Vector2 vector1, Vector2 vector2, Texture2D object1, Texture2D object2, int doSomethingToScore, bool isIncrease)
{
if (vector1.X + object1.Width < vector2.X || vector1.X > vector2.X + object2.Width ||
vector1.Y + object1.Height < vector2.Y || vector1.Y > vector2.Y + object2.Height)
{
}
else
{
player1.Velocity = Vector2.Zero;
}
}
No need for a collision check Method as far as I'm concerned :) I've written this code really quickly but should work okay?(ish). I've assumed you want to check if something is in the player's way and also assumed that there are more than one objects that could be in your players way so to do this I suggest making a class for the 'Solid' object and giving it a position, width and height. Then make a list of this class to contain the multiple 'solid' objects.
// collidable object's Class name = SolidBlock
SolidBlock newSolidBlock;
List<SolidBlock> solidBlocksList = new List<SolidBlock>();
// This will create 10 'solid' objects and adds them all to a list
// This would preferably go in the Load() function so you can set the positions of
// the blocks after creating them (after the loop)
int i = 0;
while (i < 10)
{
newSolidBlock = new SolidBlock(Vector2, position) // Assuming width and height
// are set within class
solidBlocksList.Add(newSolidBlock);
i++;
}
// It doesn't matter how you create them, the important lines here are the creation of a
// newSolidBlock and then the line adding it to the list of blocks.
solidBlocksList[0].position = new Vector (20, 50); // < for example this is how you would set
// a block's position.
Then in your players update function you refer to this list of objects.
// Input a list of the objects you want your player to collide with
// this is a list in case you have multiple object2s you want to check
// for collision.
// An Object2 should have a position, height, and width, yes?
public void Update(GameTime gameTime, List<Object2> object2List)
{
// Ani Test
up_ani.Update(gameTime);
down_ani.Update(gameTime);
left_ani.Update(gameTime);
right_ani.Update(gameTime);
position += velocity;
if (Keyboard.GetState().IsKeyDown(key.MoveUp) && prevState.IsKeyUp(key.MoveDown) && prevState.IsKeyUp(key.MoveRight) && prevState.IsKeyUp(key.MoveLeft))
{
foreach (Object2 o in object2List)
{
if (position.Y <= o.position.Y + o.height)
{
velocity.Y = 0;
}
else
{
velocity.Y = -3;
}
}
currentFace = FacePosition.Up;
}
else if (Keyboard.GetState().IsKeyDown(key.MoveDown) && prevState.IsKeyUp(key.MoveUp) && prevState.IsKeyUp(key.MoveRight) && prevState.IsKeyUp(key.MoveLeft))
{
foreach (Object2 o in object2List)
{
if (position.Y + playerWidth >= o.position.Y)
{
velocity.Y = 0;
}
else
{
velocity.Y = 3;
}
}
currentFace = FacePosition.Down;
}
else if (Keyboard.GetState().IsKeyDown(key.MoveRight) && prevState.IsKeyUp(key.MoveDown) && prevState.IsKeyUp(key.MoveUp) && prevState.IsKeyUp(key.MoveLeft))
{
foreach (Object2 o in object2List)
{
if (position.X + playerWidth >= o.position.X)
{
velocity.X = 0;
}
else
{
velocity.X = 3;
}
}
currentFace = FacePosition.Right;
}
else if (Keyboard.GetState().IsKeyDown(key.MoveLeft) && prevState.IsKeyUp(key.MoveDown) && prevState.IsKeyUp(key.MoveRight) && prevState.IsKeyUp(key.MoveUp))
{
foreach (Object2 o in object2List)
{
if (position.X <= o.position.X + o.width)
{
velocity.X = 0;
}
else
{
velocity.X = -3;
}
}
currentFace = FacePosition.Left;
}
else
{
velocity = Vector2.Zero;
}
prevState = Keyboard.GetState();
}
Sorry if this isn't much help, especially the first block of code, that was very much an example of how you could create multiple object2s.
When my AABB physics engine resolves an intersection, it does so by finding the axis where the penetration is smaller, then "push out" the entity on that axis.
Considering the "jumping moving left" example:
If velocityX is bigger than velocityY, AABB pushes the entity out on the Y axis, effectively stopping the jump (result: the player stops in mid-air).
If velocityX is smaller than velocitY (not shown in diagram), the program works as intended, because AABB pushes the entity out on the X axis.
How can I solve this problem?
Source code:
public void Update()
{
Position += Velocity;
Velocity += World.Gravity;
List<SSSPBody> toCheck = World.SpatialHash.GetNearbyItems(this);
for (int i = 0; i < toCheck.Count; i++)
{
SSSPBody body = toCheck[i];
body.Test.Color = Color.White;
if (body != this && body.Static)
{
float left = (body.CornerMin.X - CornerMax.X);
float right = (body.CornerMax.X - CornerMin.X);
float top = (body.CornerMin.Y - CornerMax.Y);
float bottom = (body.CornerMax.Y - CornerMin.Y);
if (SSSPUtils.AABBIsOverlapping(this, body))
{
body.Test.Color = Color.Yellow;
Vector2 overlapVector = SSSPUtils.AABBGetOverlapVector(left, right, top, bottom);
Position += overlapVector;
}
if (SSSPUtils.AABBIsCollidingTop(this, body))
{
if ((Position.X >= body.CornerMin.X && Position.X <= body.CornerMax.X) &&
(Position.Y + Height/2f == body.Position.Y - body.Height/2f))
{
body.Test.Color = Color.Red;
Velocity = new Vector2(Velocity.X, 0);
}
}
}
}
}
public static bool AABBIsOverlapping(SSSPBody mBody1, SSSPBody mBody2)
{
if(mBody1.CornerMax.X <= mBody2.CornerMin.X || mBody1.CornerMin.X >= mBody2.CornerMax.X)
return false;
if (mBody1.CornerMax.Y <= mBody2.CornerMin.Y || mBody1.CornerMin.Y >= mBody2.CornerMax.Y)
return false;
return true;
}
public static bool AABBIsColliding(SSSPBody mBody1, SSSPBody mBody2)
{
if (mBody1.CornerMax.X < mBody2.CornerMin.X || mBody1.CornerMin.X > mBody2.CornerMax.X)
return false;
if (mBody1.CornerMax.Y < mBody2.CornerMin.Y || mBody1.CornerMin.Y > mBody2.CornerMax.Y)
return false;
return true;
}
public static bool AABBIsCollidingTop(SSSPBody mBody1, SSSPBody mBody2)
{
if (mBody1.CornerMax.X < mBody2.CornerMin.X || mBody1.CornerMin.X > mBody2.CornerMax.X)
return false;
if (mBody1.CornerMax.Y < mBody2.CornerMin.Y || mBody1.CornerMin.Y > mBody2.CornerMax.Y)
return false;
if(mBody1.CornerMax.Y == mBody2.CornerMin.Y)
return true;
return false;
}
public static Vector2 AABBGetOverlapVector(float mLeft, float mRight, float mTop, float mBottom)
{
Vector2 result = new Vector2(0, 0);
if ((mLeft > 0 || mRight < 0) || (mTop > 0 || mBottom < 0))
return result;
if (Math.Abs(mLeft) < mRight)
result.X = mLeft;
else
result.X = mRight;
if (Math.Abs(mTop) < mBottom)
result.Y = mTop;
else
result.Y = mBottom;
if (Math.Abs(result.X) < Math.Abs(result.Y))
result.Y = 0;
else
result.X = 0;
return result;
}
It's hard to read code of other people, but I think this is one possible (purely brainstormed) workaround, although of course I'm not able to test it:
Before the collision detection happens, save the players velocity to some temporary variable.
After you've done your collision response, check if the players X or Y position has been corrected
If X position has been changed, manually reset (as a kind of "safety reset") the players Y velocity to the one that he had before the response.
By the way, what happens with your current code when your players hits the roof while jumping?
I had that same problem. Even the Microsoft platformer starterskit seems to have that bug.
The solution I found so far is to either use multisampling (argh) or to use the movedirection of the object: only move the distance between those 2 objects and no further when a collision is detected (and do that for each axis).
One possible solution I found is sorting the objects before resolving based on the velocity of the player.