How to work out what direction an object is moving - c#

SO I currently have an object which moves in the Y axis (up and down). How would I be able to program it so the program knows whether the object is moving up or down?
I understand I'd need an if statement like the following:
if (object is moving up)
{
//set direction to 1
}
else
{
//object must be going down, set direction to 2
}
I just don't understand what syntax I'd need to use. This would be easy if I was holding down the key and it was moving up or down however that's not the case. The object is a bouncing ball and therefore when you set a power the ball jumps, and bounces so it is constantly changing.
Thanks for your help, let me know if this wasn't described well and you need more info.

You need to save the current coordinates so that you can check them after the update method is called. Then you can check the differences between the previous and current location:
Declare it in Game1() constructor:
Point previous = new Point();
previous.X = myObject.InitialX;
previous.Y = myObject.InitialY;
In Update:
int deltaX = object.X - previous.X;
int deltaY = object.Y - previous.Y;
if (deltaX < 0)
{
object is moving upwards
}
else
{
object is moving downwards
}
if (deltaY < 0)
{
object is moving to the left
}
else
{
object is moving to the right
}
//update the previous state
previous.X = myObject.X;
previous.Y = myObject.Y;

When you update the position of the ball you have one position and you calculate the position for the next frame -> You can calculate the distance vector.
With the sign of the y-value of this distance vector you can decide if it's moving upwards, downwards or isn't moving (=0).

Related

Unity calculate how much object moved on a given axis

I am working on a VR project in Unity (2020.3.40f), and need to add the option to move an object on its axis based on the controller's (the user's hand) movement.
Currently I store the controller's position when it grabs the object, and continuously calculate the distance the controller has moved from the initial position.
But it is inaccurate, because the controller might have moved in a direction that shouldn't affect the object's position.
For example:
I have this blue lever that the user has to pull. I want to know how much the controller has moved along the green axis, so I can move the lever accordingly.
If the user moves their hand upwards, it shouldn't affect the lever (but in my current implementation, I use Vector3.Distance so the lever moves anyway).
My code:
private void OnTriggerEnter(Collider other)
{
controller = other.GetComponentInParent<IController>();
if (controller == null || controller.IsOccupied)
{
return;
}
controller.IsOccupied = true;
controllerStartPosition = controller.GetPosition();
}
private void Update()
{
if (controller == null) return;
Vector3 currentControllerPosition = controller.GetPosition();
float distance = Vector3.Distance(currentControllerPosition, controllerStartPosition);
transform.Translate(0, 0, distance * sensitivity); // The object always moves along its forward axis.
}
I assume that I need to project the controller's position on the object's forward axis and calculate the distance of that, but I have very basic knowledge in vectors maths so I am not sure about that.
So my question is, What are the calculations that I should do to get the correct distance?
As mentioned what you want to do is Vector3.Project your given hand movement onto the desired target axis direction and only move about this delta.
Something like
private void Update()
{
Vetcor3 currentControllerPosition = controller.GetPosition();
// the total vector in world space your hand has moved since start
Vector3 delta = currentControllerPosition - controllerStartPosition;
// the delta projected onto this objects forward vector in world space
// you can of course adjust the vector but from your usage this seems the desired one
Vector3 projectedDelta = Vector3.Project(delta, transform.forward);
// finally moving only about that projected vector in world space
transform.position += projectedDelta * sensitivity;
}
what you are currently doing is;
you are calculating the distance in every axis which the movment on every axis will change the outcome. What you need is when calculating the distance only pass in the parameters in the desired axis for example:
float distance = currentControllerPosition.x - controllerStartPosition.x;
this will give you the diffrence between the x axis of these points.
for example if it was at 5 and it moved to 8 this will return you 3 regardless the movement on the other axis.

How can I check if an object is facing a certain direction in Unity C#

Still pretty new to Unity and C#. I can usually find my way around when it comes to setting/changing the position of an object, but when it comes to rotation it's still mind boggling to me, with quaternion, eulerangles etc..
I'm trying to check if an object (a head) is facing a certain direction, if it's not facing that direction it needs to set it to that direction depending which way it goes (up, down, left or right). This is for a snake game (like the old top down nokia game) I'm currently developing.
I tried to look it up, but none of the answers are specific to what I'm trying to do.
As of right now this is what I have for player input (a snippet of code):
private int GetAxisRaw(Axis axis)
{
if (axis == Axis.Horizontal)
{
bool left = Input.GetKeyDown(KeyCode.LeftArrow) || Input.GetKeyDown(KeyCode.A);
bool right = Input.GetKeyDown(KeyCode.RightArrow) || Input.GetKeyDown(KeyCode.D);
if (left)
{
snakeHeadRotation.transform.Rotate(0, 180, 0); // Keeps rotating when left is pressed
return -1;
}
if (right)
{
snakeHeadRotation.transform.Rotate(0, -180, 0);
return 1;
}
return 0;
}
As you can see the snakeHeadRotation.transform.Rotate(0, 180, 0); is getting called each time the player presses the left key for instance, thus turning the head of the snake even when the snake is still traveling to the left.
My logic would be to store the rotation value somewhere (type Quaternion/bool maybe) and check to see if that rotation value matches up with the current rotation value set to left (which is snakeHeadRotation.transform.Rotate(0, 180, 0);) If that's the case, don't do anything. If it isn't, set it to snakeHeadRotation.transform.Rotate(0, 180, 0); for instance.
the code would look something like (writing it out):
private int GetAxisRaw(Axis axis)
{
if (axis == Axis.Horizontal)
{
bool left = Input.GetKeyDown(KeyCode.LeftArrow) || Input.GetKeyDown(KeyCode.A);
bool right = Input.GetKeyDown(KeyCode.RightArrow) || Input.GetKeyDown(KeyCode.D);
if (left && rotation is not equal to snakeHeadRotation.transform.Rotate(0, 180, 0))
{
snakeHeadRotation.transform.Rotate(0, 180, 0);
return -1;
}
if (right)
{
snakeHeadRotation.transform.Rotate(0, -180, 0);
return 1;
}
return 0;
}
As said, I'm very unfamiliar with setting or storing rotation values. How can I achieve this? Is this even the right approach or is there a more logical one? I can't figure this one out myself..
I hope I explained it right, I'm usually terrible at explaining things.
Using Transform.Rotate(x) will rotate your head by x degrees relatively to your current head direction.
What you want I suppose is to set the absolute rotation of your head depending on the arrow key you pressed. Thus, even if you press multiple times the same arrow key, the head will stay in the correct direction.
To do that you can create 4 rotations for each head direction and so use them as needed.
For example, if your head is facing forward by default:
// create your directions
private static Quaternion forwardDirection = Quaternion.Identity;
private static Quaternion rightDirection = Quaternion.FromToRotation(Vector3.forward, Vector3.right);
private static Quaternion leftDirection = Quaternion.FromToRotation(Vector3.forward, Vector3.left);
private static Quaternion backDirection = Quaternion.FromToRotation(Vector3.forward, Vector3.back);
...
private int GetAxisRaw(Axis axis)
{
...
if (left)
{
// set the absolute direction of your head to the left
snakeHeadRotation.transform.rotation = leftDirection;
return -1;
}
if (right)
{
// set the absolute direction of your head to the right
snakeHeadRotation.transform.rotation = rightDirection;
return 1;
}
...
}
I would just create 4 quaternions for each direction and set the rotation according to the key pressed (so like hear.rotation = leftQ). This is the simplest way.
I am away from my PC currently but I will try to find another solution when I can
You could compare the current rotation to the known rotations for the 4 directions.
But don't go that way, it's bad practice for several reasons:
what if later you want to add animations, e.g. the head turns in .5 seconds, not instantly?
comparing 4 floats only to check one of 4 possible directions is inefficient.
Create a enum instead:
public enum Direction
{
Up,
Down,
Left,
Right
}
Add a member of this type to your behavior, to keep track of where your snake head is going.
Something like:
Direction direction;
...
private int GetAxisRaw(Axis axis)
{
switch (direction)
{
case Direction.Up:
if (left && !right)
direction = Direction.Left;
... turn snake left ...
else if (right && !left)
direction = Direction.Right;
... turn snake right ...
// ignore inputs for up or down if going up
break;
... repeat for the other directions and possible inputs ...
}
...
}

Projectiles passing through other entities

I wrote a collision detection system for a game I am working on, and I am experiencing a weird glitch where, occasionally, projectiles will go through the player or walls scattered throughout the level. Because the projectiles can be fired at any angle, I decomposed the bounding box of each projectile into multiple, smaller bounding boxes that I then rotate around the center of the texture according to the rotation of the projectile in space. For some reason, occasionally a Spear projectile will go through the player or a wall even through others do not.
I use the following methods to determine the rotation of the texture and to translate the bounding boxes:
public double RotateToFaceTarget()
{
double rotation = Math.Atan2((double)_direction.Y, (double)_direction.X);
return rotation;
}
public List<BoundingBox> TranslateBoundingBox(List<BoundingBox> box, double rotation)
{
List<BoundingBox> newBounds = new List<BoundingBox>();
foreach (BoundingBox b in box)
{
Vector2 boundsOrigin = new Vector2(b.Pos.X + b.Size.X / 2, b.Pos.Y + b.Size.Y / 2);
Vector2 texOrigin = new Vector2(_pos.X + _texture.Width / 2, _pos.Y + _texture.Height / 2);
Vector2 newPosBasedOnOrigin = Vector2.Transform(boundsOrigin - texOrigin, Matrix.CreateRotationZ((float)rotation)) + boundsOrigin;
newBounds.Add(new BoundingBox(newPosBasedOnOrigin, b.Size));
}
return newBounds;
}
_direction is calculated by subtracting the position of the projectile from the target location and normalizing. I use this method to determine if the projectile is colliding with another entity:
public bool ProjectileCollision(Entity e, Projectile entity2)
{
if (entity2.CanCollide)
{
foreach(GameObject.BoundingBox b in entity2.BoundingBox)
{
foreach(GameObject.BoundingBox b2 in e.BoundingBox)
{
if (b2.Intersect(b) && (entity2.IgnoredEntities.Contains(e.Type) == false))
{
entity2.IsActive = false;
e.Health -= entity2.Damage;
return true;
}
return false;
}
}
return false;
}
return false;
}
And this is my Bounding Box Intersection method:
public bool Intersect(BoundingBox intersected)
{
if ((_pos.Y < intersected.Pos.Y + intersected.Size.Y) && (_pos.Y + _size.Y > intersected.Pos.Y) && (_pos.X + _size.X > intersected.Pos.X) && (_pos.X < intersected.Pos.X + intersected.Size.X))
{ return true; }
return false;
}
EDIT: On further testing, it seems that the projectile will always detect a hit if the player hits based on the top left corner ( which makes sense now that I look at my intersect code). Is there another way to re-write my Intersect method to use something more accurate than the top left corner?
EDIT2: I drew the hitboxes for certain objects, and this is one instance of when I catch the spear going through the player:
http://imgur.com/a/fAxZw
the player is the larger pink square. The hitboxes are not being translated correctly, but it shouldn't just stop working, for some and not others, right?
It could happen, because of high velocity and small object, projectile could fly through object. Beside of checking if objects are intersecting, you have to check if object will intersect, to check if object is in line of fire. You could achieve this by raycasting.
On those cases like your i had function that check if object is near other. Simple checking if object is inside of some radius of other object. If yes then i was checking if object is flying toward other object, and checking distance between them. When distance is really close then collision happened.

Using Switch for Simple Unity3D Patrol Movement

I'm using a switch statement to create two movement types on an enemy: Forward, and Backwards. The enemy has three patrol points. When he begins, I want him moving from the first patrol point and adding 1 to the current point when he hits his second patrol point (empty 3D gameobject), and so on. Then I'll have him reverse direction when he hits the final point.
switch (moveType)
{
case MoveType.Forward:
if (transform.position == patrolPoints[currentPoint].position)
{
currentPoint ++;
}
break;
case MoveType.Backwards:
if (transform.position == patrolPoints[patrolPointsLength].position)
{
currentPoint --;
}
break;
}
The problem is, I can't figure out a way to "trigger" the two MoveTypes. How do I code this so that when the enemy hits his final patrol point, he switches to MoveType.Backwards? I'm sure I'm making this way harder than it needs to be. Thanks for the help!
This is how I would do it if I really wanted to use the switch statement:
float someSmallValue = 0.5f; // Adjust this as per your needs
if (Vector3.Distance(transform.position, patrolPoints[currentPoint].position) < someSmallValue)
{
switch (moveType)
{
case MoveType.Forward:
currentPoint++;
if (currentPoint > patrolPoints.Length - 1)
{
currentPoint -= 1;
moveType = MoveType.Backwards;
}
break;
case MoveType.Backwards:
currentPoint--;
if (currentPoint < 0)
{
currentPoint = 1;
moveType = MoveType.Forward;
}
break;
}
}
I think renaming currentPoint to targetPoint would make the variable name more clear for this chunk of code.
EDIT: I forgot to decrement currentPoint inside the Backwards case block. Updated my answer.
The solution I have here adds some things like a pause time. It also ensures that fast moving entities won't overshoot the destination and not turn around or something.
// Movement speed normalized. So 1 is instantaneous travel and zero is no movement.
public float movementSpeed = 0.025;
// Let's add a 'pause' time where the unit waits a while at the turning point.
public float waitTime = 1;
// Lets say the list has 5 points.
public List<Vector3> patrolPoints ...
// We keep track of our starting and ending points. Here we start at zero and move to position 1.
public Vector2 currentLeg = new Vector2(0,1);
// We use a coroutine
IEnumerator Patrol()
{
// This keeps track of where we are. 0 is at the starting point and 1 is at the destination.
float progress = 0;
while(true)
{
Vector3 startingPoint = patrolPoints[currentLeg.x];
Vector3 destination = patrolPoints[currentLeg.y];
// Please note this won't compile. It's for brevity. You must lerp x,y,z indiviualy in C#.
transform.position = Mathf.Lerp(startingPoint, destination, progress);
progress+= movementSpeed;
// If we are at our destination
if(progress >=1 )
{
// Reset our progress so wa can start over.
progress = 0;
// Our current point is now what our destination was before.
currentLeg.x = currentLeg.y;
switch(moveType)
{
case MoveType.Forward:
{
// If this condition is true then it means we are at the final point (4 in this case). So we invert the movement direction and set the current point to 3.
if(currentLeg.y == patrolPoints.Count()-1)
{
currentLeg.y -= 1;
moveType = MoveType.Backwards;
// We wait 1 seconds and then do everything again.
yield return new WaitForSeconds(waitTime);
}
else
currentLeg.y++;
}
break;
case MoveType.Backward:
{
// If this condition is true then it means we are at the starting point (0). So we invert the movement direction and set the current point to 1.
if(currentLeg.y == 0)
{
currentLeg.y += 1;
moveType = MoveType.Forward;
// We wait 1 seconds and then do everything again.
yield return new WaitForSeconds(waitTime);
}
else
currentLeg.y--;
}
break;
}
}
}
}
For a quick fix Jayson's answer will be better. But it might fail if the unit moves fast or the unit will stop too early if it moves too slow.
EDIT: The wait time was in the wrong place.
So you want to use a switch to patrol because only 1 enum can be selected at a time?
Why not use a public Transform target; variable and have your AI always follow whats in that variable? Then just make an empty game object and stick it into that variable and he will follow it wherever it goes. He wont be limited to "Forward" and "Backward", and you wont have to recode or be confused, when you want to recycle the code for an AI moving "Left" to "Right"
This will speed up execution, clean up your code, get rid of tons of lines of code as well, and you can still use your switch statement too, because this actually will open your code up to more dynamic abilities such as:
public enum ActionType
{
Patrol,
Veer,
Stop
}
Lets say hes patrolling and halfway to the target he spots an enemy, ActionType curAction; Can be set to ActionType.Veer , now he is veering off the patrol line to attack the enemy, and after you can set it back to ActionType.Patrol and he continues to the target.
So for patrolling you can set up a public List<Vector3> wayPoints; and just add a bunch of Vector3's in there. When he reaches the target, he can stop for a sec or two, then have the empty game object target jump to the next vector3 in the list.

Rotating a vector

I want to do a simple vector rotation.
The goal is to head my first-person camera which is currently pointing to to target t with direction d to a new target t1 with a new direction d1.
The transition between d and d1 should be a smooth movement.
With
public void FlyLookTo(Vector3 target) {
_flyTargetDirection = target - _cameraPosition;
_flyTargetDirection.Normalize();
_rotation = new Matrix();
_rotationAxis = Vector3.Cross(Direction, _flyTargetDirection);
// This bool tells the Update()-method to trigger the changeDirection() method.
_isLooking = true;
}
I am initiating the direction change with its new parameter and with
// this method gets executed by the Update()-method if the isLooking flag is up.
private void _changeDirection() {
dist = Vector3.Distance(Direction, _flyTargetDirection);
// check whether we have reached the desired direction
if (dist >= 0.00001f) {
_rotationAxis = Vector3.Cross(Direction, _flyTargetDirection);
_rotation = Matrix.CreateFromAxisAngle(_rotationAxis, MathHelper.ToRadians(_flyViewingSpeed - Math.ToRadians(rotationSpeed)));
// update the cameras direction.
Direction = Vector3.TransformNormal(Direction, _rotation);
} else {
_onDirectionReached();
_isLooking = false;
}
}
I am performing the actual movement.
My Problem: The actual movement works fine but the speed of the movement slows down the more the current direction gets closer to the desired direction which makes it a very unpleasant movement if executes several times in a row.
How can I make the camera move from direction d to direction d1 with an equal speed over its movement ?
your code looks pretty solid. does _flyViewingSpeed or rotationSpeed change at all?
Another approach would be to use Vector3.Lerp() which will do exactly what you're trying to do. note however, you need to use the initial start and goal directions - not the current directions - or you'll get varying speed changes.
Also, instead of using distance (which is usually used for points), i would use Vector3.Dot() which is kind of like distance for directions. it should also be faster than Distance().
hope this helps.

Categories