I have two force fields I & II (Image here - https://i.stack.imgur.com/yXAhQ.png). They both have an attached script with a bool 'repel', which is toggled on MouseDown(). These force fields need to either attract or repel a spaceship directly in its path. The actual movement of the ship is done by raycasting right & down from the ship. These raycasts check the 'repel' bool of the force fields and the ship is then either moved away or towards the force fields. All gameObjects are Kinematic.
I need to be able to move the ship back and forth between the force fields by toggling their 'repel' bools. This was working fine by using transform.Translate to move the ship. However, collisions were buggy, so I decided to use Rigidbody2D.MovePosition instead.
Now, the ship can move towards ForceField I and when it detects ForceField II, it changes its course along a vertical line, which is what I want. BUT, it can no longer move towards or away from ForceField I when it detects it along the X axis ray. So, the ship can now only move up and down. How do I keep the ship moving between the force fields?
Here's the code attached to the ship -
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class shipRays_test : MonoBehaviour {
public float rayDistance = 100;
public Vector2 Xspeed;
public Vector2 Yspeed;
private polarityScript polarityright;
private polarityScript polaritydown;
private Rigidbody2D rb;
void Start()
{
rb = GetComponent<Rigidbody2D>();
}
void FixedUpdate ()
{
Debug.DrawRay (transform.position, Vector2.right * rayDistance, Color.red);
RaycastHit2D rightHit = Physics2D.Raycast (transform.position, Vector2.right, rayDistance);
Debug.DrawRay (transform.position, Vector2.down * rayDistance, Color.green);
RaycastHit2D downHit = Physics2D.Raycast (transform.position, Vector2.down, rayDistance);
if (rightHit.collider != null)
{
if (rightHit.collider.CompareTag ("forceField"))
{
polarityright = rightHit.collider.gameObject.GetComponent<polarityScript > ();
if (polarityright.repel)
{
rb.MovePosition (rb.position - Xspeed * Time.fixedDeltaTime);
}
else if (!polarityright.repel)
{
rb.MovePosition (rb.position + Xspeed * Time.fixedDeltaTime);
}
}
}
if (downHit.collider != null)
{
if (downHit.collider.CompareTag ("forceField"))
{
polaritydown= downHit.collider.gameObject.GetComponent<polarityScript >();
if (polaritydown.repel)
{
rb.MovePosition (rb.position + Yspeed * Time.fixedDeltaTime);
}
else if (!polaritydown.repel)
{
rb.MovePosition (rb.position - Yspeed * Time.fixedDeltaTime);
}
}
}
}
}
Apparently Rigidbody.MovePosition() stores the value passed and interpolates over frames, each invocation overwriting the previous value, during which time Rigidbody.position does not change. This isn't noted in the documentation at all (or at least not well), but there's the rub.
To fix this you need to create a variable to hold the desired new position yourself, adding to it as your logic flow and only at the end calling MovePosition().
So you'd have to set things up like this:
Vector3 newPos = rb.position;
if (rightHit.collider != null)
{
//...
{
newPos -= Xspeed * Time.fixedDeltaTime;
}
//...
}
if (downHit.collider != null)
{
//...
{
newPos += Yspeed * Time.fixedDeltaTime;
}
//...
}
rb.MovePosition(newPos);
Related
I just can’t figure out how to make random points for patrolling, I went through 100 manuals, but I can’t do it.
No need to write about NavMeshAgent. Not used.
using System.Collections;
using UnityEngine;
public sealed class Manikin : MonoBehaviour {
private Vector3 StartManikin = Vector3.zero,
NewPosition = Vector3.zero;
private readonly float Speed = 2.0f;
void Start() {
StartManikin = transform.position;
}
void Update() {
if (Vector3.zero == NewPosition) {
NewPosition = StartManikin + Random.onUnitSphere * 2;
}
if (Vector3.Distance(transform.position, NewPosition) < 0.1f) {
StartManikin = transform.position;
NewPosition = Vector3.zero;
} else {
transform.LookAt(new Vector3(NewPosition.x, StartManikin.y, NewPosition.z));
transform.position = Vector3.MoveTowards(transform.position, NewPosition, Speed * Time.deltaTime);
}
}
}
The problem is that there may be obstacles in the form of a fence, a tree, houses, cars, etc.
I need that when the enemy appears, random points are generated in his radius so that he does not leave further.
Help me out, I can’t figure out what needs to be done to make everything work ...
It turns out that I just did not see that the Pivot of the model is not clear where, it flies in another dimension, the problem is solved, it is necessary to reset all transformations and the world point of the model's coordinates.
I didn’t see it right away, all the options turned out to be working ...
And also, it was necessary to fix the problem with the take-off along the y-axis, so that this does not happen, you need to look at the bottom point of the object from which we will spawn the coordinate for it.
if (Vector3.zero == NewPosition) {
NewPosition = StartManikin + (Random.onUnitSphere * 5);
NewPosition.y = transform.GetComponent<CapsuleCollider>().bounds.min.y;
}
if (Vector3.Distance(transform.position, NewPosition) < 0.1f) {
StartManikin = transform.position;
NewPosition = Vector3.zero;
} else {
transform.LookAt(new Vector3(NewPosition.x, transform.position.y, NewPosition.z));
transform.position = Vector3.MoveTowards(transform.position, NewPosition, Speed * Time.deltaTime);
}
So I am making game and I need good PlayerMovement Script but there is little problem. You see I made so that my player is following this path (I didn't make it I downloaded it from marketplace) and he can go only forward. Then I put another Vector3 (horizontalMove) value and hooked it to transform.position and didn't get what I really wanted. When I hit play everything works and I can go Left to Right but my position does not stay the same. The moment I let go of my keys (a, d) my player returns to track, resets in middle. I need it to stay in the position I left him. And too, just adding rb.position in my transform.position just send me flying in the air. I need help.
`public class PathFollower : MonoBehaviour
{
public PathCreator pathCreator;
public EndOfPathInstruction endOfPathInstruction;
public float speed = 5;
float distanceTravelled;
float horizontalInput;
[SerializeField] float horizontalMultiplier = 2;
public Rigidbody rb;
void Start() {
if (pathCreator != null)
{
// Subscribed to the pathUpdated event so that we're notified if the path changes during the game
pathCreator.pathUpdated += OnPathChanged;
}
}
void Update()
{
if (pathCreator != null)
{
horizontalInput = Input.GetAxis("Horizontal");
Vector3 horizontalMove = transform.right * horizontalInput * speed * Time.fixedDeltaTime * horizontalMultiplier;
distanceTravelled += speed * Time.deltaTime;
Debug.Log();
transform.position = pathCreator.path.GetPointAtDistance(distanceTravelled, endOfPathInstruction) + new Vector3 (0,1,0) + horizontalMove;
transform.rotation = pathCreator.path.GetRotationAtDistance(distanceTravelled, endOfPathInstruction);
}
}
// If the path changes during the game, update the distance travelled so that the follower's position on the new path
// is as close as possible to its position on the old path
void OnPathChanged() {
distanceTravelled = pathCreator.path.GetClosestDistanceAlongPath(transform.position);
}
}
}`
You need a global variable that holds the value of horizontalMove, so it isn't reset in the update loop.
Vector3 horizontalMove;
void Update()
{
if (pathCreator != null)
{
horizontalInput = Input.GetAxis("Horizontal");
Vector3 horizontalMove += transform.right * horizontalInput * speed * Time.deltaTime * horizontalMultiplier;
distanceTravelled += speed * Time.deltaTime;
Debug.Log();
transform.position = pathCreator.path.GetPointAtDistance(distanceTravelled, endOfPathInstruction) + new Vector3 (0,1,0) + horizontalMove;
transform.rotation = pathCreator.path.GetRotationAtDistance(distanceTravelled, endOfPathInstruction);
}
}
I also took the liberty to change fixedDeltaTime to deltaTime, since that is the appropriate variable for an Update context.
I use this code to move and rotate my object, but it moves through the walls. Yes, I have colliders on object and walls, but my object doesn't collide with these walls.
using UnityEngine;
using System.Collections;
public class player_Control : MonoBehaviour {
public float upspeed;
public float downspeed;
public float rotationSpeed;
Transform myTrans;
Vector3 myPos;
Vector3 myRot;
float angle;
void Start() {
myPos = transform.position;
myRot = transform.rotation.eulerAngles;
}
void FixedUpdate() {
angle = transform.eulerAngles.magnitude * Mathf.Deg2Rad;
if (Input.GetKey(KeyCode.RightArrow)) { // ROTATE RIGHT
myRot.z -= rotationSpeed;
}
if (Input.GetKey(KeyCode.LeftArrow)) { // ROTATE LEFT
myRot.z += rotationSpeed;
}
if (Input.GetKey(KeyCode.UpArrow)) { // UP
myPos.y += (Mathf.Cos(-angle) * upspeed) * Time.deltaTime;
myPos.x += (Mathf.Sin(-angle) * upspeed) * Time.deltaTime;
}
if (Input.GetKey(KeyCode.DownArrow)) { // DOWN
myPos.y += (Mathf.Cos(-angle) * -downspeed) * Time.deltaTime;
myPos.x += (Mathf.Sin(-angle) * -downspeed) * Time.deltaTime;
}
transform.position = myPos;
transform.rotation = Quaternion.Euler(myRot);
}
}
Your issue here is that you are bypassing the Unity's physics engine and altering the players position and rotation directly.
Unity's physics checks are built around usage of the Rigidbody component and has several specific functions for changing an objects position and rotation so that it collides correctly with other objects within the scene.
For your usage above I would look at the following two functions for changing position and rotation of your character:
https://docs.unity3d.com/ScriptReference/Rigidbody.MovePosition.html
https://docs.unity3d.com/ScriptReference/Rigidbody.MoveRotation.html
I have start a project and I attached a rigidbody to my player for apply some force on it.So when I run my project then in FixedUpdate() function I apply a force on player to moving it in forward direction.So when I pressed leftarrow or rightarrow it perform 'tilt' means rotate it's wings.
But now I want to move my player up or down smoothly by pressing Uparrow or Downarrow,and when I press both uparrow and downarrow then it must be affect on player.
Here is my code.Please help me. :(
void FixedUpdate ()
{
rb.AddForce(transform.forward *1000f);
float movedir = Input.GetAxis ("Horizontal");
Vector3 movement = new Vector3 (movedir, 0.0f, 0.0f);
rb.velocity = movement * movingspeed;
rb.rotation = Quaternion.Euler (0.0f, 0.0f, rb.velocity.x * -3f);//perform tilt
if (Input.GetKeyDown (KeyCode.UpArrow))
{
//code for smoothly move up from current position to = current position + 2f
}
if (Input.GetKeyDown (KeyCode.DownArrow))
{
//code for smoothly move down from current position to = current position - 2f
}
}
Your question is not really clear. Move smoothly could mean many things.
In general you should use RigidBody.MovePosition and Rigidbody.MoveRotation to set a Rigidbody's transforms instead of rb.rotation and rb.position in order to get "smooth" movements:
Use Rigidbody.MovePosition to move a Rigidbody, complying with the Rigidbody's interpolation setting.
If Rigidbody interpolation is enabled on the Rigidbody, calling Rigidbody.MovePosition results in a smooth transition between the two positions in any intermediate frames rendered. This should be used if you want to continuously move a rigidbody in each FixedUpdate.
Set Rigidbody.position instead, if you want to teleport a rigidbody from one position to another, with no intermediate positions being rendered.
So as you can see depending on your settings using Rigidbody.MovePosition might already result in a "smooth" movement.
rb.MovePosition(transform.position + Vector3.up * 2.0f);
Also you use Input.GetKeyDown so it works like a trigger ... it is not called continously like Input.GetKey
If you want to move continously while the key stays pressed use e.g.
// set e.g. in the inspector
public float verticalMoveSpeed;
// ...
if (Input.GetKey(KeyCode.UpArrow))
{
rb.MovePosition(transform.position + Vector3.up * verticalMoveSpeed * Time.deltaTime);
}
if (Input.GetKey(KeyCode.DownArrow))
{
rb.MovePosition(transform.position - Vector3.up * verticalMoveSpeed * Time.deltaTime);
}
If you want to trigger the movement only with GetKeyDown instead you could also do something like e.g.
// set e.g. in the inspector
public float verticalMoveSpeed;
// ...
if (Input.GetKeyDown(KeyCode.UpArrow))
{
StartCoroutine(MoveVertical(2.0f, verticalMoveSpeed));
}
else if (Input.GetKeyDown(KeyCode.DownArrow))
{
StartCoroutine(MoveVertical(-2.0f, verticalMoveSpeed));
}
// ...
private IEnumerator MoveVertical(float distance, float speed)
{
var originalY = transform.position.y;
var targetY = originalY + distance;
var currentY = originalY;
do
{
rb.MovePosition(new Vector 3(transform.position.x, currentY, transform.positiom.z);
// Update currentY to the next Y position
currentY = Mathf.Clamp(currentY + speed * Time.deltaTime, originalY, targetY);
yield return null;
}
while(currentY < originalY);
// make sure you didn't move to much on Y
rb.MovePosition(new Vector3(transform.position.x, targetY, transform.position,z));
}
Than there are two options to prevent concurrent routines:
use a flag. This also prevents the routine from beeing interrupted/called twice/called concurrent
privtae bool isMovingVertical;
// ...
if (Input.GetKeyDown(KeyCode.UpArrow) && !isMovingVertical )
{
StartCoroutine(MoveVertical(2.0f, verticalMoveSpeed));
}
else if (Input.GetKeyDown(KeyCode.DownArrow) && !isMovingVertical )
{
StartCoroutine(MoveVertical(-2.0f, verticalMoveSpeed));
}
// ...
private IEnumerator MoveVertical(float distance, float speed)
{
isMovingVertical = true;
// ...
isMovingVertical = false;
}
use StopAllCoroutines to interrupt a running routine (attention this might lead to "infinite" moves in one direction - at least without you preventing it with additional checks)
if (Input.GetKeyDown(KeyCode.UpArrow))
{
StopAllCoroutines();
StartCoroutine(MoveVertical(2.0f, verticalMoveSpeed));
}
if (Input.GetKeyDown(KeyCode.DownArrow))
{
StopAllCoroutines();
StartCoroutine(MoveVertical(-2.0f, verticalMoveSpeed));
}
For the most part this script works, however every now and then an enemy will fail at pathfinding and go through a building or wall. Is there a way i can stop this?
using UnityEngine;
using System.Collections;
namespace Daniel {
public class EnemyAI : Living {
// Detection private int range = 10; private float speed = 10f; private bool isThereAnyThing = false;
// Waypoints/Targets
public GameObject[] targets;
private float rotationSpeed = 900f;
private RaycastHit hit;
GameObject target;
[SerializeField]
private int randomTarget = 0;
[SerializeField]
float timeToNextCheck = 3;
public float effectTimer = 2f;
public GameObject deathEffect;
public LayerMask detectThis;
void Start()
{
randomTarget = Random.Range(0, 8);
target = targets[randomTarget];
}
void FixedUpdate()
{
timeToNextCheck = timeToNextCheck - Time.deltaTime;
ScanForNewWaypoint();
LookAtTarget();
Move();
CheckForObsticales();
}
void LookAtTarget()
{
//Look At Somthly Towards the Target if there is nothing in front.
if (!isThereAnyThing)
{
Vector3 relativePos = target.transform.position - transform.position;
Quaternion rotation = Quaternion.LookRotation(relativePos);
transform.rotation = Quaternion.Slerp(transform.rotation, rotation, Time.deltaTime);
}
}
void Move()
{
// Enemy translate in forward direction.
transform.Translate(Vector3.forward * Time.deltaTime * speed);
}
public void CheckForObsticales()
{
//Checking for any Obstacle in front.
// Two rays left and right to the object to detect the obstacle.
Transform leftRay = transform;
Transform rightRay = transform;
//Use Phyics.RayCast to detect the obstacle
if (Physics.Raycast(leftRay.position + (transform.right * 7f), transform.forward, out hit, range, detectThis) || Physics.Raycast(rightRay.position - (transform.right * 7f), transform.forward, out hit, range))
{
if (hit.collider.gameObject.CompareTag("Obstacles"))
{
isThereAnyThing = true;
transform.Rotate(Vector3.up * Time.deltaTime * rotationSpeed);
}
}
// Now Two More RayCast At The End of Object to detect that object has already pass the obsatacle.
// Just making this boolean variable false it means there is nothing in front of object.
if (Physics.Raycast(transform.position - (transform.forward * 4), transform.right, out hit, 10, detectThis) ||
Physics.Raycast(transform.position - (transform.forward * 4), -transform.right, out hit, 10, detectThis))
{
if (hit.collider.gameObject.CompareTag("Obstacles"))
{
isThereAnyThing = false;
}
}
}
public void ScanForNewWaypoint()
{
CheckForObsticales();
if (timeToNextCheck <= 0)
{
timeToNextCheck = Random.Range(6, 3);
randomTarget = Random.Range(0, 8);
target = targets[randomTarget];
}
}
public override void TakeHit(float dmg, Vector3 hitPoint, Vector3 hitDirection)
{
if (dmg >= health)
{
Destroy(Instantiate(deathEffect, hitPoint, Quaternion.FromToRotation(Vector3.forward, hitDirection)) as GameObject, effectTimer);
Debug.Log("Exploded");
}
base.TakeHit(dmg, hitPoint, hitDirection);
}
}
}
Okay now this is one of the most common problems people face when playing with Physics Engine and believe me there are lot of factors that adds up to the result/consequences you are facing. Most common solutions devised by people is to use Update which is Executed Every Frame for Physics calculation which is Entirely Wrong. All the physics related calculation needs to be performed inside the Fixed Update otherwise it will be too expensive to execute physics based simulations in the Update which means every single frame. In your case where your Enemy is going through walls and Physics Collisions are missing the best solution you can take is to tweak some Physics Related Properties in Unity these are located in Edit>>>Project Settings>>>Time change the Time Step and Maximum Allowed Time Step in the inspector to set your collisions right. There are some other things to keep in mind too while dealing with physics like scale, mass and colliders etc. Here is a Link concerning the detailed mechanics of the physics settings in unity. Make sure you read and understand the contents of the link before working and tweaking physics settings.