Run in opposite direction? - c#

I have this code that figures out the shortest distance between my white balls and my green balls.
When it's used by my green balls, they chase the white ones and turn them into green ones.
Is there a way to do the opposite of what my green balls do so my white ones run away? The green balls code is below:
private Vector3 FindClosestHuman()
{
GameObject[] Humans = GameObject.FindGameObjectsWithTag("Human");
Transform bestTarget = null;
float closestDistanceSqr = Mathf.Infinity;
Vector3 currentPosition = this.transform.position;
foreach (GameObject human in Humans)
{
Vector3 directionToTarget = human.transform.position - currentPosition;
float dSqrToTarget = directionToTarget.sqrMagnitude;
if (dSqrToTarget < closestDistanceSqr)
{
closestDistanceSqr = dSqrToTarget;
bestTarget = human.transform;
}
}
return bestTarget.position;
}
void Update ()
{
infectionAgent.SetDestination(FindClosestHuman());
}
I've tried using transform.translate and Vector3 in the Update method but I just can't seem to get it to work...

There is no opposite of SetDestination for Unity's NavMeshAgent.
That is: how you decide which direction to take at a fork when running away from something? Randomly? The one that gives the most distance? What if you can run in circles, is that better than running into a dead end, even if you stay closer to your away-from target? Does running towards your away-from target in order to reach a branch that lets you reach a farther-away destination allowed?
You need to figure out what "run away" means and write your own algorithm for that sort of navigation.

Related

How to detect when a cube is directly above another cube?

I am using the Unity Engine with C#.
I have a 1x1 cube which moves forward on a grid of 49, 1x1 cubes (screenshot below) - when I press the start button on the controller.
The movement code for the cube is below.
void MovePlayerCube()
{
transform.Translate(direction * moveSpeed * Time.deltaTime);
}
When this cube passes over a cube with an arrow on it, the cube will change direction to where the arrow is pointing (staying on the same Y axis).
I need to detect the exact point at which the cube is directly over the cube with the arrow on it, and run the 'change direction' code at that point.
I'm currently using Vector3.Distance to check if the X and Z coordinates of the 2 cubes are close enough together (if they are less than 0.03f in distance), I can't check if they are equal due to floating point imprecision.
However this is really ineffective as half the time this code doesn't register for probably the same reason, and if I increase the 0.03f to a point where it never misses it becomes really noticeable that the cube isn't aligned with the grid anymore.
There has to be a proper solution to this and hopefully I've clarified the situation enough?
Any advice is appreciated.
You are moving your cube via
transform.Translate(direction * moveSpeed * Time.deltaTime);
which will never be exact an might overshoot your positions.
=> I would rather implement a coroutine for moving the cube exactly one field at a time, ensuring that after each iteration it fully aligns with the grid and run your checks once in that moment.
It doesn't even have to match exactly then, you only need to check if you are somewhere hitting a cube below you.
So something like e.g.
private Vector3Int direction = Vector3Int.left;
private IEnumerator MoveRoutine()
{
// depends on your needs if this runs just forever or certain steps
// or has some exit condition
while(true)
{
// calculate the next position
// optional round it to int => 1x1 grid ensured on arrival
// again depends a bit on your needs
var nextPosition = Vector3Int.RoundToInt(transform.position) + direction;
// move until reaching the target position
// Vector3 == Vector3 uses a precision of 1e-5
while(transform.position != nextPosition)
{
transform.position = Vector3.MoveTowards(transform.position, nextPosition, moveSpeed * Time.deltaTime);
yield return null;
}
// set target position in one frame just to be sure
transform.position = nextPosition;
// run your check here ONCE and adjust direction
}
}
start this routine only ONCE via
StartCoroutine(MoveRoutine());
or if you have certain exit conditions at least only run one routine at a time.
A Corouine is basically just a temporary Update routine with a little bit different writing => of course you could implement the same in Update as well if you prefer that
private Vector3Int direction = Vector3Int.left;
private Vector3 nextPosition;
private void Start()
{
nextPosition = transform.position;
}
private void Update()
{
if(transform.position != nextPosition)
{
transform.position = Vector3.MoveTowards(transform.position, nextPosition, moveSpeed * Time.deltaTime);
}
else
{
transform.position = nextPosition;
// run your check here ONCE and adjust direction
// then set next position
nextPosition = Vector3Int.RoundToInt(transform.position) + direction;
}
}
Then regarding the check you can have a simple raycast since you only run it in a specific moment:
if(Physics.Raycast(transform.position, Vector3.down, out var hit))
{
direction = Vector3Int.RountToInt(hit.transform.forward);
}
assuming of course your targets have colliders attached, your moved cube position (pivot) is above those colliders (assumed it from your image) and your targets forward actually points int the desired new diretcion
I would do it this way. First I would split the ability of certain objects to be "moving with certain speed" and "moving in a certain direction", this can be done with C# interfaces. Why? Because then your "arrow" cube could affect not only your current moving cube, but anything that implements the interfaces (maybe in the future you'll have some enemy cube, and it will also be affected by the arrow modifier).
IMovingSpeed.cs
public interface IMovementSpeed
{
float MovementSpeed{ get; set; }
}
IMovementDirection3D.cs
public interface IMovementDirection3D
{
Vector3 MovementDirection { get; set; }
}
Then you implement the logic of your cube that moves automatically in a certain direction. Put this component on your player cube.
public class MovingStraight: MonoBehaviour, IMovementSpeed, IMovementDirection3D
{
private float _movementSpeed;
Vector3 MovementSpeed
{
get { return _movementSpeed; }
set { _movementSpeed = value; }
}
private Vector3 _movementDirection;
Vector3 MovementDirection
{
get { return _movementDirection; }
set { _movementDirection= value; }
}
void Update()
{
// use MovementSpeed and MovementDirection to advance the object position
}
}
Now to implement how the arrow cube modifies other objects, I would attach a collision (trigger) component for both moving cube and the arrow cube.
In the component of the arrow cube, you can implement an action when something enters this trigger zone, in our case we check if this object "has direction that we can change", and if so, we change the direction, and make sure that the object will be aligned, by forcing the arrow cube's position and the other object's position to be the same on the grid.
public class DirectionModifier: MonoBehaviour
{
private Vector3 _newDirection;
private void OnTriggerEnter(Collider collider)
{
IMovementDirection3D objectWithDirection = collider as IMovementDirection3D ;
if (objectWithDirection !=null)
{
objectWithDirection.MovementDirection = _newDirection;
// to make sure the object will continue moving exactly
// from where the arrow cube is
collider.transform.position.x = transform.position.x;
collider.transform.position.y = transform.position.y;
}
}
}
If you made your trigger zones too large, however, then the moving cube will "jump" abruptly when it enters the arrow cube's trigger zone. You can fix it by either starting a coroutine as other answers suggested, or you could make the trigger zones pretty small, so that the jump is not noticeable (just make sure not to make them too small, or they may not intersect each other)
You could then similarly create many other modifying blocks, that would change speed or something
I think that it is enough for you to check if the X and Z coordinates are equal, since the movement occurs only along them
Example
if(_player.transfom.position.x == _gameSquare.transfom.position.x && _player.transfom.position.z == _gameSquare.transfom.position.z)
{
Debag.Log("Rotate!")
}

I have no syntax errors in Unity, How can I find errors so I learn why my crosshair stays in the center of the screen?

For Unity Game: shouldn't the cross-hair move accordingly as the player rig does to point at enemies? how can I find error with no syntax error messages?
I tried visiting Unity forums I found support about raycasting followed directions add some code to the crosshair.cs script I receive no syntax errors.
public class CrossHair: MonoBehaviour{
[SerializeField] private GameObject standardCross;
[SerializeField] private GameObject redCross;
float moveForce = 1.0f;
float rotateTorque = 1.0f;
float hoverHeight = 4.0f;
float hoverForce = 5.0f;
float hoverDamp = 0.5f;
Rigidbody rb;
private RaycastHit raycastHit;
void Start()
{
standardCross.gameObject.SetActive(true);
rb = GetComponent<Rigidbody>();
// Fairly high drag makes the object easier to control.
rb.drag = 0.5f;
rb.angularDrag = 0.5f;
}
void Update()
{
// Push/turn the object based on arrow key input.
rb.AddForce(Input.GetAxis("Vertical") * moveForce * transform.forward);
rb.AddTorque(Input.GetAxis("Horizontal") * rotateTorque * Vector3.up);
RaycastHit hit;
Ray downRay = new Ray(transform.position, -Vector3.up)
if (Physics.Raycast(downRay, out hit))
{
//The "error" in height is the difference between the desired height
// and the height measured by the raycast distance.
float hoverError = hoverHeight - hit.distance;
// Only apply a lifting force if the object is too low (ie, let
// gravity pull it downward if it is too high).
if (hoverError > 0)
{
// Subtract the damping from the lifting force and apply it to
// the rigidbody.
float upwardSpeed = rb.velocity.y;
float lift = hoverError * hoverForce - upwardSpeed * hoverDamp;
rb.AddForce(lift * Vector3.up);
}
}
Ray targettingRay = new Ray(transform.position, transform.forward)
if (Physics.Raycast(targettingRay, out raycastHit, 100))
{
if (raycastHit.transform.tag == "Enemies")
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
redCross.gameObject.SetActive(true);
standardCross.gameObject.SetActive(false);
}
}
else
{
redCross.gameObject.SetActive(false);
standardCross.gameObject.SetActive(true);
}
}
}
I except for the crosshair in my game to follow the player rig camera as the code explains. Any guidance is appreciated.
Syntax errors are those kind of errors which prevent your game/program from running at all. They are like grammar mistakes that need to be fixed before you can run your code.
A syntax error is your pc telling you:
I can't run this because I don't understand this. or I can't run this because this is not allowed.
One example would be int x = "Hello World". This in not allowed because I can not assign an string value to an integer (like that).
But your code not having any syntax errors does not mean it will do what you intended it for, it just means your code will run.
A good and easy way of debugging your code in Unity is to add Debug.Log("My Log Message"); statements to your code where you think it would be beneficial. These are going to be logged to your console output in Unity while you are in play mode. You can for example do something like this to constantly get your cross's position and rotation logged:
Debug.Log("Cross Position: " + standardCross.transform.position);
Debug.Log("Cross Rotation: " + standardCross.transform.rotation);
Just be sure to remove them once you are done with them because having Debug.Logs in your code takes as significant hit to your performance.
Another, more sophisticated way of debugging your code is through the usage of breakpoints in Visual Studio or whatever IDE/Editor you are using.
It basically comes down to declaring points where your program should pause execution for you to look at values in your program itself.
It's quite handy but goes above and beyond what I could tell you via this text, so please have a look at this Unity-specific use case: Debugging Unity games with Visual Studio
Now to your code:
First: There is a Vector3.down so you don't have to use -Vector3.up.
Second: Do you need a GameObject as crosshair? Why not just add a UI-Crosshair instead?
That way it always stays in the middle of your screen wherever you turn your camera.
Just add a new Image to your UI via GameObject -> UI -> Image, give it some kind of crosshair look in the inspector and lock it to the middle of the screen by left-clicking on the little crosshair in the top left of the Inspector while you have your Image selected and then Shift + Alt + Left click the middle option.
If you really want to use a separate gameObject as crosshair then maybe attatch it to your player object as a child. That way it will move and rotate with your player automatically and you do not have to do this via script.
Hope this helps!

Detect initial orientation for Gyroscope Camera, but heading only

So I'm using a script I found on here (How to enable gyroscope camera at current device orientation) BUT I only want this initial device orientation to affect the heading.
Quaternion offset;
void Awake()
{
Input.gyro.enabled = true;
}
void Start()
{
//Subtract Quaternion
offset = transform.rotation * Quaternion.Inverse(GyroToUnity(Input.gyro.attitude));
}
void Update()
{
GyroModifyCamera();
}
void GyroModifyCamera()
{
//Apply offset
transform.rotation = offset * GyroToUnity(Input.gyro.attitude);
}
private static Quaternion GyroToUnity(Quaternion q)
{
return new Quaternion(q.x, q.y, -q.z, -q.w);
}
For the backstory, I have a few interior renderings, with a gyro control to look around the space. I don't want to affect the entire world orientation, I just want them to be facing in the "correct" direction, in terms of the best view of the space. (If not, it just depends on how the device is oriented and its common to jump into a space looking at a random, uninteresting corner of the room.)
With the script above, it resets the camera on all three axes (and therefore the entire world orientation), so if the user is holding the tablet at an unusual angle, then things get really weird.
Here's a quick image to assist the explanation:
I always want the initial view to be aligned with the green arrow, not something random like the red arrow. But just the heading, it's OK to be looking at the floor or ceiling as long as the heading is right in that sweet spot of the room.
Thanks!

Detect if there is any object between two objects with BoxCastAll

I ran into an issue with box-casts while developing a 3D game for mobile.
I want to check the path between my player and his target, to avoid him passing through environmental objects (he doesn't have a rigidbody attached and movement is only possible between specific points).
This is the code that I used to check:
private bool CheckPath (Vector3 position, Vector3 target)
{
Vector3 center = Vector3.Lerp(transform.position, target, 0.5f);
Vector3 halfExtents = new Vector3(1, 1, (transform.position - target).magnitude) / 2;
Quaternion rotation = Quaternion.LookRotation((transform.position - target).normalized);
RaycastHit[] rhit = Physics.BoxCastAll(center, halfExtents, (transform.position - target).normalized, rotation);
bool result = rhit.All(r => r.collider.tag != "Environment");
DebugUtilities.BoxCastDebug.DrawBox(center, halfExtents, rotation, result ? Color.green : Color.red, 3);
return result;
}
This code works for the most situations:
But fails, for example for this situation:
To visualize the box-cast I used the script from this Unity Answers link.
I am not sure where the problem lies, although the most likely cause would be a flaw in the debugging script mentioned above, which made me believe that my box-cast call was correct.
I am grateful for every solution, although a simple one would be more appreciated.
Some further information (if needed):
The objects that I want to make impassable are marked with a Environment tag.
I was not the one that wrote the debugging script, it seemed to work at first, so I was fine with it.
Three problems in your code
1.The position variable from the unction parameter is not used. You are instead using transform.position which means that the starting point may be wrong.
Replace all your transform.position with position.
2.You are performing the raycast backwards. It should not be transform.position - target. That should be target - transform.position.
3.Your Physics.BoxCastAll will not work properly since there is no ending to the raycast. Objects behind the starting will be detected by the raycast. Now, if you fix problem #2, the problem will reverse. Now, all the objects in behind the target will also be detected since you did not provide the raycast distance. You can provide the distance with Vector3.Distance(position, target) in the last parameter of the Physics.BoxCastAll function.
Fixed function:
private bool CheckPath(Vector3 position, Vector3 target)
{
Vector3 halfExtents = Vector3.one;
Quaternion rotation = Quaternion.LookRotation(target - position);
Vector3 direction = target - position;
float distance = Vector3.Distance(position, target);
RaycastHit[] rhit = Physics.BoxCastAll(position, halfExtents, direction, rotation, distance);
bool result = rhit.All(r => r.collider.tag != "Environment");
Vector3 center = Vector3.Lerp(position, target, 0.5f);
halfExtents = new Vector3(1, 1, (target - position).magnitude) / 2;
DebugUtilities.DrawBox(center, halfExtents, rotation, result ? Color.green : Color.red);
// Debug.DrawRay(position, direction, result ? Color.green : Color.red);
return result;
}
It's worth setting up 3 objects (Cubes) in order to easily test this function. The first one is from Object (Player). The middle one should be the obstacle with the "Environment" tag. The last one should be the target Object where player is moving to.
Then you can use the script below to test it. Run it and move the obstacle away between the player and the target and it should work as expected.
public GameObject playerObject;
public GameObject targetObject;
void Update()
{
Debug.Log("Path cleared: " + CheckPath(playerObject.transform.position, targetObject.transform.position));
}
You will get the similar result below:

Calculating/Predicting a way

I'm just starting with physics, so I'm not always sure about what I'm doing. It's a 2D project but I'm using 3D physical objects like SphereCollider etc..
What I have:
Objects floating in space and affecting each other through gravity:
protected virtual IEnumerator OnTriggerStay(Collider other) {
yield return new WaitForFixedUpdate();
if(other.attachedRigidbody) {
Vector3 offsetVector = this.transform.position - other.transform.position;
float distance = offsetVector.magnitude;
float gravityForce = (other.rigidbody.mass * mass) / Mathf.Pow(distance, 2);
// Clamp gravity.
if(gravityForce > 1.0F) {
gravityForce = 1.0F;
}
other.attachedRigidbody.constantForce.force = offsetVector.normalized * gravityForce;
}
}
There are controllable objects on which the player can click and drag a line away from the object in order to give it a force (shoot) in the opposite direction.
What I want to achieve:
The player should see a rough prediction of the way while aiming. That means that the way-prediction needs to take in account the current velocity, the force which would be applied when the player release the mouse button and the gravity of the surrounding objects.
What I have tried so far:
For testing purposes I just save the computed/predicted positions in an array and draw those positions in OnDrawGizmos().
I wrote a method which returns the gravity influence for a certain position called computeGravityForPosition(Vector3 position).
And thats how I try to calculate the positions:
private void drawWayPrediction() {
Vector3 pos = this.transform.position;
// The offsetVector for the shooting action.
Vector3 forceVector = pos - Camera.main.ScreenToWorldPoint(Input.mousePosition);
forceVector.z = 0.0F;
// The predicted momentum scaled up to increase the strength.
Vector3 force = (forceVector.normalized * forceVector.magnitude);
// 1. I guess that this is wrong, but don't know how to do it properly.
momentum = this.rigidbody.velocity + force;
for(int i = 0; i < predictionPoints.Length; i++) {
float t = i * Time.fixedDeltaTime;
momentum += computeGravityForPosition(pos);
pos += momentum * t * t;
predictionPoints[i] = pos;
}
}
At the beginning, when the objects just slowly approaching each other it looks okay. After the first shot, the prediction is completely wrong. I guess it is because of 1. in the code. Just adding the force to the velocity is probably horrible wrong.
Thank you very much for your time.
EDIT:
I removed seemingly unnessecary parts.
I still think that the main problem lays in 1. in the code. I just don't know how to mix up the current movement of the object (from which I only have the current velocity as far as I know the physics engine of unity) with the new created force:
Vector3 forceVector = pos - Camera.main.ScreenToWorldPoint(Input.mousePosition);
Vector3 force = (forceVector.normalized * forceVector.magnitude);
So if you are using a new version of unity probably above 2018, you can use the nice method
Physics.Simulate(dt); // delta time, dt, is the amount of time to simulate.
https://docs.unity3d.com/ScriptReference/Physics.Simulate.html
https://docs.unity3d.com/2018.3/Documentation/ScriptReference/PhysicsScene.Simulate.html
By using this function you can manually advance the simulation.
This method should be applied to a different physics scene.
Therefore I suggest that when you click you will simulate a few physics steps (the more you will simulate the more accurate indication the player will get),
with every step you store the position of the object and when you are done simulating draw a line between all the points.
In my opinion, it should run quite fast if done correctly.
The code should look something like this:
public PhysicsScene physicsScene;
GameObject actualBall;
GameObject simulatedBall;
OnClick() {
simulatedBall.SetPosition(actualBall.transform.position);
if (!physicsScene.IsValid())
return; // do nothing if the physics Scene is not valid.
for (int i=0; i < 10; i++) {
physicsScene.Simulate(Time.fixedDeltaTime);
// store the position.
myPoints.append(simulatedBall.rb.position);
}
// draw a line from the stored points.
}
In addition there is this video that I hope will help, good luck
https://www.youtube.com/watch?v=GLu1T5Y2SSc
I hope I answered your question and if not tell me :)
Disclaimer : Unfortunately I suck at math so can't provide any code for the calculations.
Now that the legal stuff is out of the way :)
In my opinion you are looking at this all wrong. What you need is to calculate the curve (path of the objects trajectory) and then simply plot the curve in OnDrawGizmos with a line renderer.
You don't need to simulate the behaviour of the object. Not only is this a LOT faster but it's also simpler in terms of TimeScale shenanigans. By changing the TimeScale you are also affecting the TimeScale of your trajectory simulation which will most likely look and feel weird.
By doing a basic trajectory calculation you will not have this issue.
PS: This link might help.

Categories