NavmeshAgent player not parallel to slope of hill when moving over hill - c#

NavmeshAgent player not parallel to slope of hill when moving over hill. On plane surface its going smoothly.
See Video
Below Image properties of navMesh and player
https://ibb.co/fijmoV
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
public class SampleAgentScript : MonoBehaviour {
public Transform target ;
NavMeshAgent agent;
// private static bool start1=false , start2=false, start3;
// Use this for initialization
void Start()
{
agent = GetComponent<NavMeshAgent>();
}
void Update()
{
//if white button click moves to targer-1
agent.SetDestination(target.position);
}
}

I am not sure if NavmeshAgent is supposed to do that for you. This looks like something you're supposed to do manually.
You can correct the rotation of the character to match the slope by performing a raycast downwards and obtaining the normal of the hit point. After obtaining the normal of the hit point, you can then calculate the new rotation with that normal hit point. There are many ways to do that calculation but using Quaternion.FromToRotation and lerping the rotation with Quaternion.Lerp seems to work best.
Finally, make sure to the raycast is only done to Objects you considered as a "Hill" or "Ground". You can do this with the bitwise operation on the layer the "Hill" or "Ground" objects are placed on. The example below assumes that the Objects you consider as "Hill" or "Ground" are on a layer called "Hill".
//Reference of the moving GameObject that will be corrected
public GameObject movingObject;
//Offset postion from where the raycast is cast from
public Vector3 originOffset;
public float maxRayDist = 100f;
//The speed to apply the corrected slope angle
public float slopeRotChangeSpeed = 10f;
void Update()
{
//Get the object's position
Transform objTrans = movingObject.transform;
Vector3 origin = objTrans.position;
//Only register raycast consided as Hill(Can be any layer name)
int hillLayerIndex = LayerMask.NameToLayer("Hill");
//Calculate layermask to Raycast to.
int layerMask = (1 << hillLayerIndex);
RaycastHit slopeHit;
//Perform raycast from the object's position downwards
if (Physics.Raycast(origin + originOffset, Vector3.down, out slopeHit, maxRayDist, layerMask))
{
//Drawline to show the hit point
Debug.DrawLine(origin + originOffset, slopeHit.point, Color.red);
//Get slope angle from the raycast hit normal then calcuate new pos of the object
Quaternion newRot = Quaternion.FromToRotation(objTrans.up, slopeHit.normal)
* objTrans.rotation;
//Apply the rotation
objTrans.rotation = Quaternion.Lerp(objTrans.rotation, newRot,
Time.deltaTime * slopeRotChangeSpeed);
}
}

Related

How to make an object that walks forward until it hits a wall?

I want a character that simply walks forward until it hits a wall, then it does a 180 degree spin and repeats the action.
Making him walk forward is easy, how do I program him hitting the wall?
My current code:
public class Enemy : MonoBehaviour
{
public float speed = 5;
public Vector3 userDirection = Vector3.forward;
// Update is called once per frame
void Update()
{
transform.position += userDirection * speed * Time.deltaTime;
}
}
You can use raycast to detect walls and avoid them.
Raycast in Unity is a Physics function that projects a Ray into the scene, returning a boolean value if a target was successfully hit
The following code is a simple demenstration of raycast. Distance determine how far you ray is casted and layermask determine which layers ray should detect. Therefore, you must put your walls in a Layer and sit this variable equal to that:
public float distance;
public LayerMask wallLayerMask;
public Vector3 userDirection = Vector3.forward;
void Update()
{
if (Physics.Raycast(transform.position, userDirection, distance, wallLayerMask))
{
// rotate, possibly:
// userDirection *= -1
}
transform.position += userDirection * speed * Time.deltaTime;
}
UPDATE
As stated in comments, you can also use colliders. To use colliders, you need to add another empty gameObject to your enemy, add a SphereCollider to it. Sphere Collider's radius determines how far you want to detect walls. Then add the following code to the second object:
// a reference to your enemy controller class
public Enemy enemy;
private void OnCollisionEnter(Collider other)
{
enemy.Rotate();
}
The OnCollisionEnter is called whenever an object, which has a collider, collides with our object. You need to assign a layer to this new object (wallCheck) and another to your walls. Then from Edit -> Project Settings -> Physics uncheck the collision of wallCheck with any layer other than your walls layer.

Moving Game Object with UI Buttons

I am trying to set up control of Player game object by UI buttons.
2d top down view scene.
I want to transfer Player object smoothly on fixed distance (0.8 for Axix X for left-right direction and 2.4 for up-down)when I release left/right/up/down ui buttons.
Here i found code for smooth movement but there Player is moving all the time while ui button is pressed.
Can you help me to make it move on mouse up (pointer up) and to move for public x= 0.8f for left/right, and public y = 2.4f for up/down
And at the same time i want to use different speed (peblic) for moves on x and y Axis
Its ok if it should be totally other script using smth like transform.translate
Kindly guide to for any possible solution for this. Thanks
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityStandardAssets.CrossPlatformInput;
public class PlayerControl : MonoBehaviour
{
float movX;
float movY;
Rigidbody2D rb;
void Start()
{
rb = GetComponent<Rigidbody2D>();
}
void Update()
{
movX = CrossPlatformInputManager.GetAxisRaw("Horizontal");
movY = CrossPlatformInputManager.GetAxisRaw("Vertical");
rb.velocity = new Vector2(movX * 1, movY * 1);
}
}
This script can be moved by the WASD keys.
This should move your gameObject by the requested amount over x amount of time(speed).
Currently it can be only moved when it'S reached its destionation but you can easily modify this :), by stopping the coroutine
using System.Collections;
using UnityEngine;
public class PlayerControl : MonoBehaviour
{
// We gonna move by WASD keys
[Header("Speed & Movement settings")]
[SerializeField]
float Speed = 2.0f;
[SerializeField]
float movSpeedX = 0.8f;
[SerializeField]
float movSpeedY = 2.4f;
bool ReachedTarget = true;
void Update()
{
if (ReachedTarget)
{
Vector2 dest = Vector2.zero;
Vector2 currentPos = transform.position;
if (Input.GetKeyUp(KeyCode.W))
{
dest = currentPos + (Vector2.up * movSpeedY);
StartCoroutine(moveTo(dest, Speed));
}
else if (Input.GetKeyUp(KeyCode.S))
{
dest = currentPos + (Vector2.down * movSpeedY);
StartCoroutine(moveTo(dest, Speed));
}
else if (Input.GetKeyUp(KeyCode.D))
{
dest = currentPos + (Vector2.right * movSpeedX);
StartCoroutine(moveTo(dest, Speed));
}
else if (Input.GetKeyUp(KeyCode.A))
{
dest = currentPos + (Vector2.left * movSpeedX);
StartCoroutine(moveTo(dest, Speed));
}
}
}
// Time to take is in seconds
IEnumerator moveTo(Vector2 TargetPosition, float TimetoTake)
{
Vector2 originalPosition = transform.position;
float Time_taken = 0f;
ReachedTarget = false;
while (Time_taken < 1)
{
Time_taken += Time.deltaTime / TimetoTake;
// Interpolating between the original and target position this basically provides your "speed"
transform.position = Vector2.Lerp(originalPosition, TargetPosition, Time_taken);
yield return null;
}
Time_taken = 0;
transform.position = TargetPosition;
ReachedTarget = true;
}
}
I can't find any documentation of CrossPlatformInputManager, and I know nothing about it. But if you need to get the event of "release a key" instead of "press the key", try this: Input.GetKeyUp.
Description
Returns true during the frame the user releases the key identified by
name.
You need to call this function from the Update function, since the
state gets reset each frame. It will not return true until the user
has pressed the key and released it again.
For the list of key identifiers see Conventional Game Input. When
dealing with input it is recommended to use Input.GetAxis and
Input.GetButton instead since it allows end-users to configure the
keys.
using UnityEngine;
using System.Collections;
public class ExampleClass : MonoBehaviour
{
void Update()
{
if (Input.GetKeyUp("space"))
{
print("Space key was released");
}
}
}
If you want to stop the rigid body, you need to reset its velocity to
zero. Or you can use
Rigidbody2D.MovePosition
to move it a certain distance.
Parameters
position The new position for the Rigidbody object.
Description
Moves the rigidbody to position.
Moves the rigidbody to the specified position by calculating the
appropriate linear velocity required to move the rigidbody to that
position during the next physics update. During the move, neither
gravity or linear drag will affect the body. This causes the object to
rapidly move from the existing position, through the world, to the
specified position.
Because this feature allows a rigidbody to be moved rapidly to the
specified position through the world, any colliders attached to the
rigidbody will react as expected i.e. they will produce collisions
and/or triggers. This also means that if the colliders produce a
collision then it will affect the rigidbody movement and potentially
stop it from reaching the specified position during the next physics
update. If the rigidbody is kinematic then any collisions won't affect
the rigidbody itself and will only affect any other dynamic colliders.
2D rigidbodies have a fixed limit on how fast they can move therefore
attempting to move large distances over short time-scales can result
in the rigidbody not reaching the specified position during the next
physics update. It is recommended that you use this for relatively
small distance movements only.
It is important to understand that the actual position change will
only occur during the next physics update therefore calling this
method repeatedly without waiting for the next physics update will
result in the last call being used. For this reason, it is recommended
that it is called during the FixedUpdate callback.
Note: MovePosition is intended for use with kinematic rigidbodies.
// Move sprite bottom left to upper right. It does not stop moving.
// The Rigidbody2D gives the position for the cube.
using UnityEngine;
using System.Collections;
public class Example : MonoBehaviour
{
public Texture2D tex;
private Vector2 velocity;
private Rigidbody2D rb2D;
private Sprite mySprite;
private SpriteRenderer sr;
void Awake()
{
sr = gameObject.AddComponent<SpriteRenderer>();
rb2D = gameObject.AddComponent<Rigidbody2D>();
}
void Start()
{
mySprite = Sprite.Create(tex, new Rect(0.0f, 0.0f, tex.width, tex.height), new Vector2(0.5f, 0.5f), 100.0f);
velocity = new Vector2(1.75f, 1.1f);
sr.color = new Color(0.9f, 0.9f, 0.0f, 1.0f);
transform.position = new Vector3(-2.0f, -2.0f, 0.0f);
sr.sprite = mySprite;
}
void FixedUpdate()
{
rb2D.MovePosition(rb2D.position + velocity * Time.fixedDeltaTime);
}
}
Both documents have an example.
Or you don't want to user keyboard but UI buttons, try this: IPointerDownHandler and IPointerUpHandler
Description
Interface to implement if you wish to receive OnPointerDown callbacks.
Detects ongoing mouse clicks until release of the mouse button. Use
IPointerUpHandler to handle the release of the mouse button.
//Attach this script to the GameObject you would like to have mouse clicks detected on
//This script outputs a message to the Console when a click is currently detected or when it is released on the GameObject with this script attached
using UnityEngine;
using UnityEngine.EventSystems;
public class Example : MonoBehaviour, IPointerDownHandler, IPointerUpHandler
{
//Detect current clicks on the GameObject (the one with the script attached)
public void OnPointerDown(PointerEventData pointerEventData)
{
//Output the name of the GameObject that is being clicked
Debug.Log(name + "Game Object Click in Progress");
}
//Detect if clicks are no longer registering
public void OnPointerUp(PointerEventData pointerEventData)
{
Debug.Log(name + "No longer being clicked");
}
}

How can a make the camera follow a rolling ball from behind?

I am using unity 2018.3.5f1 so a few solutions to this problem don't work.
Starting from the Roll-A-Ball demo project, I want to extend the camera script to follow the player, but also keeping track of the direction of movement. Ex: If the ball starts moving to the side, I need the camera to rotate around the player's axis to position behind him, and then start following him.
I have tried making the camera a child to the ball i'm trying to control and i've tried making a script but it won't follow the ball correctly it keeps rotating the camera as well as the ball (i'm using this bit of code and trying to modify it as it's the only one that works)
here is the code I have at the moment:
using UnityEngine;
using System.Collections;
public class CompleteCameraController : MonoBehaviour
{
public GameObject player; //Public variable to store a reference to the player game object
private Vector3 offset; //Private variable to store the offset distance between the player and camera
// Use this for initialization
void Start()
{
//Calculate and store the offset value by getting the distance between the player's position and camera's position.
offset = transform.position - player.transform.position;
}
// LateUpdate is called after Update each frame
void LateUpdate()
{
// Set the position of the camera's transform to be the same as the player's, but offset by the calculated offset distance.
transform.position = player.transform.position + offset;
}
}
I understand why it is rotating but no reasearch is helping me find out how to lock the camera so it is looking behind the ball at all times
Have you tried adding some restraints to your camera? If it is only supposed to turn to follow the ball, you can freeze the position of 2 axes (x and z), and only let the camera rotate around the y axis of the ball.
You can play around with the restraints on the Inspector, or use GetComponent<Rigidbody>().constraints = RigidbodyConstraints.FreezeRotationX; in the camera script.
(EDIT) Sorry, this is how you would do it if the gameObject has a rigidbody, but the idea is the same if it doesn't have one.
When you're setting the position or rotation of the camera, think about which components of the position/rotation you actually want to change (x,y,z).
Use offset = new Vector3 (offset.x, offset.y, offset.z)and replace anything that shouldn't change with a constant.
Also, when you made the Camera the child of the ball, you could have set it so that every frame, the Camera updates in such a way that it won't roll with the ball. You could do this by putting code in the update method of the camera that sets the component x, y, or z equal to some constant. Whatever axis the ground plane is on is probably the correct choice.
If you want the same camera angle that the camera should follow while following the ball (similar to Temple Run), then try the below code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
public class CompleteCameraController : MonoBehaviour
{
public Transform Player;
public float distanceFromObject = 3f;
void Update()
{
Vector3 lookOnObject = Player.position - transform.position;
lookOnObject = Player.position - transform.position;
transform.forward = lookOnObject.normalized;
Vector3 playerLastPosition;
playerLastPosition = Player.position - lookOnObject.normalized * distanceFromObject;
playerLastPosition.y = Player.position.y + distanceFromObject / 2;
transform.position = playerLastPosition;
}
When the ball moves left or right, the camera will follow the same angle as the ball moves towards.

Unity sprite flying off map when rotating

I am currently programming a game where the player must tilt their phone to rotate the world so they can navigate through a maze. This is proving difficult as when I rotate the phone the sprite flies off screen.
I am assuming this is to do with the forces between the character's and the world's colliders which ends up launching the character.
However, I want the character to have gravity follow the players relative down direction so that when the character rotates it falls the direction of its feet.
Therefore, how should I rotate the direction of gravity to be relative to the sprite's direction and prevent the player from flying off when rotating?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GyroController : MonoBehaviour {
private Gyroscope Gyro ;
private float rotation;
private Vector3 startEulerAngles;
private Vector3 startGyroAttitudeToEuler;
public GameObject Player = null;
private void Awake () {
if (SystemInfo.supportsGyroscope) {
Gyro = Input.gyro;
Gyro.enabled = false;
startEulerAngles = transform.eulerAngles;
startGyroAttitudeToEuler = Input.gyro.attitude.eulerAngles;
Debug.Log("Gyro Enabled");
}
else {
Debug.Log("No Gyro Detected");
}
}
private void Update()
{
Vector3 deltaEulerAngles = Input.gyro.attitude.eulerAngles - startGyroAttitudeToEuler;
deltaEulerAngles.x = 0.0f;
deltaEulerAngles.y = 0.0f;
Player.transform.eulerAngles = startEulerAngles - deltaEulerAngles;
}
}
So far I have only tried one method of changing the direction of gravity which involves moving the center of gravity which only made the character fall through other colliders. Hence, my gravity code is not included in this snippet.
I am not asking for the code to be made for me, just a direction for me to look in as i've hit a dead end using rigidbody2d
Thankyou.

Prevent object from passing through collider

I am building a 3D maze that is moved by the player. I'm taking a "labyrinth" approach and having a small ball be maneuvered through the maze when the player moves the maze. The problem occurs when the player moves a wall that the ball is currently resting on in the direction of the ball. The ball passes through the wall and stops on the next available wall.
The maze uses a mesh collider and rigidbody and the ball is a sphere collider.
I have drastically increased the physics frame rate to no avail. Considering the complexity of the maze and the potentially large number of maze combinations I really don't want to attach simple colliders to every wall. Any tips, tricks, suggestions, comments, etc. would be appreciated.
Ball Script for continious rotation:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ballGravity : MonoBehaviour {
public Rigidbody m_Rigidbody;
public Vector3 m_EulerAngleVelocity;
// Use this for initialization
void Start () {
m_EulerAngleVelocity = new Vector3 (0, 100, 0);
m_Rigidbody = GetComponent<Rigidbody>();
}
// Update is called once per frame
void FixedUpdate () {
Quaternion deltaRotation = Quaternion.Euler (m_EulerAngleVelocity * Time.deltaTime);
m_Rigidbody.MoveRotation (m_Rigidbody.rotation * deltaRotation);
}
}
Maze Rotation Script:
using UnityEngine;
using System.Collections;
public class rotObj : MonoBehaviour
{
private float baseAngle = 0.0f;
float rotSpeed = 10;
void OnMouseDown(){
Vector3 pos = Camera.main.WorldToScreenPoint(transform.position);
pos = Input.mousePosition - pos;
baseAngle = Mathf.Atan2(pos.y, pos.x) * Mathf.Rad2Deg;
baseAngle -= Mathf.Atan2(transform.right.y, transform.right.x) *Mathf.Rad2Deg;
}
void OnMouseDrag(){
//float rotY = Input.GetAxis("Vertical")*rotSpeed*Mathf.Deg2Rad;
//gm.transform.Rotate(Vector3.right, rotY);
Vector3 pos = Camera.main.WorldToScreenPoint(transform.position);
pos = Input.mousePosition - pos;
float ang = Mathf.Atan2(pos.y, pos.x) *Mathf.Rad2Deg - baseAngle;
transform.rotation = Quaternion.AngleAxis(ang, Vector3.forward);
}
}
To get consistent physics state its best to move colliders within FixedUpdate - if you move colliders in a OnMouseXX stage of the pipeline (Consider consulting https://docs.unity3d.com/Manual/ExecutionOrder.html), you risk that the change might be missed next time FixedUpdate is called. I.e. your ball moves through the state of your scene based on how it was before you rotated the maze, and with some unfortunate timings its possible that collisions get missed.
If you already tried with a tighter timestep, consider queueuing the OnMouseDrag change, like store the resulting rotation in a variable, and apply it in FixedUpdate, so it is applied just in case for next physics computation.
you may also consider moving your ball at the same time as you rotate your maze, quickly moving a mesh collider against a much smaller rigidbody is a sure source of trouble. Can you move a camera instead ?

Categories

Resources