I'm wanting my arrow to point the way I'm targeting in my game.
At the moment, my prefab instantiates in the center of the screen point to the left. When I move my arrow, the point still keeps to the left. How can I get it so that the arrow at least points to the center of my screen, or ideally, has a way of moving, depending on the direction the player is aiming.
For example, if my player is aiming to the top left of the screen, the arrow point would face that way and fire off in that direction.
Here is the code i have so far:
public Object _projectilePrefab;
private bool _pressed = false;
public GameObject _flystickPosition;
private GameObject _currentProjectile;
private Vector3 _firePoint;
private float _startTime;
public float sensitivityX = 15F;
public float sensitivityY = 15F;
int x = 0;
int y = 0;
// Use this for initialization
void Start()
{
_currentProjectile = (GameObject)Instantiate(_projectilePrefab, _flystickPosition.transform.position, _flystickPosition.transform.rotation);
_firePoint = Camera.mainCamera.transform.position - new Vector3(1, 1, 0);
_startTime = (Time.time * 10.0f);
//_serialData = GetComponent<ReadSerial>();
//_udpData = GetComponent<HV_ReadUDPData>();
}
// Update is called once per frame
void Update()
{
_firePoint = Camera.mainCamera.transform.position - new Vector3(1, 1, 0);
float _moveBananaY = transform.localEulerAngles.y + Input.GetAxisRaw("LeftRight") * sensitivityX;
float _moveBananaX = transform.localEulerAngles.x + Input.GetAxisRaw("UpDown") * sensitivityY;
transform.localEulerAngles = new Vector3(_moveBananaX, _moveBananaY, 0);
if (Input.GetKeyDown(KeyCode.LeftArrow))
{
y--;
}
if (Input.GetKeyDown(KeyCode.RightArrow))
{
y++;
}
if (Input.GetKeyDown(KeyCode.UpArrow))
{
x--;
}
if (Input.GetKeyDown(KeyCode.DownArrow))
{
x++;
}
float xPercent = 0;
float yPercent = 0;
xPercent = x / 25.0f;
yPercent = y / 25.0f;
_flystickPosition.transform.parent.localEulerAngles = new Vector3(xPercent * 90, yPercent * 90, 0.0f);
float smooth = Time.time - _startTime;
_currentProjectile.transform.position = Vector3.Lerp(lerpFrom, _flystickPosition.transform.position, smooth);
if (Input.GetKeyDown(KeyCode.Space))
{
_currentProjectile.GetComponent<Rigidbody>().velocity = _flystickPosition.transform.parent.forward.normalized * 20;
_currentProjectile = null;
_currentProjectile = (GameObject)Instantiate(_projectilePrefab, _firePoint, transform.rotation);
lerpFrom = _currentProjectile.transform.position;
_startTime = Time.time;
}
}
All of this code is found within my FireProjectile class. It is attached to my camera.
Well if I understand you correctly, your game is 2D. Its not important though. I would offer two solutions for your issue:
You can get your pointing direction by using simple vector math. Vector3(to - from). So your arrow pointing direction is : Target.transform.position - Player.transform.position. Now when it is pointing the right direction you can move it like:
Arrow.transform.Translate(Vector3.forward * Time.deltaTime);
Also if your arrow must point to target, but stay in different position than player, just raycast from Player to Target, get raycast point and theres your arrow "to" Vector3.
If arrow always mimic user rotation, you can simply apply rotation to arrow Arrow.transform.rotation = Player.transform.rotation. If rotations don't match (i.e. your object is rotated), just add empty gameobject to mimic rotations and put arrow as child of it. now you can rotate it as you will.
Related
I am working on a project in unity and I have a small circle that shows how much power will be applied to a ball and an arrow that shows the direction.
The circle and arrow are meant to scale up to a max distance; the arrow scales but it is too big (takes up half the screen) and doesn't rotate properly; the circle does not scale at all. I have tried to change the local scale of the arrow and messed around with the various values but I am not sure what to really do. The arrow tends to only face the correct direction when the cursor is in the top left and the arrow is in the bottom right.
The two points, point A and B are two empty objects; point B is attached to the ball and pointA follows the mouse. When the ball is clicked on and the cursor is dragged away pointB moves in the opposite direction; I am trying to get the arrow to face pointB at all times or point at pointB from the opposite side of the ball.
Everything except for the arrow and circle rotating and scaling works. I'm fairly new to code and don't understand Mathf.Log. The arrow rotate and scale code is commented out as I am currently trying to get the circle to work.
If you can point me in the right direction or help with just one of these issues I'd greatly appreciate it.
public class PlayerBallHit : MonoBehaviour
{
private GameObject mousePointA;
private GameObject mousePointB;
private GameObject arrow;
private GameObject circle;
// calc distance
private float currDistance;
public float maxDistance = 3f;
private float spaceLimit;
private float shootPower;
public float shootPowervar;
public Vector2 shootDirection;
void Start()
{
}
void Awake()
{
mousePointA = GameObject.FindGameObjectWithTag("PointA");
mousePointB = GameObject.FindGameObjectWithTag("PointB");
arrow = GameObject.FindGameObjectWithTag("Stick");
circle = GameObject.FindGameObjectWithTag("Circle");
}
private void OnMouseDrag()
{
currDistance = Vector2.Distance(mousePointA.transform.position, transform.position);
if (currDistance <= 3f)
{
spaceLimit = currDistance;
}
else
{
spaceLimit = maxDistance;
}
// Direction of Hit and Circle
StrDirMarkers();
// calc Power & Direction
shootPower = Mathf.Abs(spaceLimit) * shootPowervar;
Vector3 dimxy = mousePointA.transform.position - transform.position;
float difference = dimxy.magnitude;
mousePointB.transform.position = (Vector3)transform.position + ((dimxy / difference) * currDistance * -1);
mousePointB.transform.position = new UnityEngine.Vector3(mousePointB.transform.position.x, mousePointB.transform.position.y);
shootDirection = (Vector2)Vector3.Normalize(mousePointA.transform.position - transform.position);
}
void OnMouseUp()
{
//arrow.GetComponent<SpriteRenderer>().enabled =false;
circle.GetComponent<SpriteRenderer>().enabled = false;
Vector2 push = shootDirection * shootPower *-1;
GetComponent<Rigidbody2D>().AddForce(push, ForceMode2D.Impulse);
}
private void StrDirMarkers()
{
//arrow.GetComponent<SpriteRenderer>().enabled = true;
circle.GetComponent<SpriteRenderer>().enabled = true;
// calc position
/*
if (currDistance <= maxDistance)
{
arrow.transform.position = new Vector2((2f * transform.position.x) - mousePointA.transform.position.x, (2f * transform.position.y) - mousePointA.transform.position.y);
}
else
{
Vector2 dimxy = mousePointA.transform.position - transform.position;
float difference = dimxy.magnitude;
arrow.transform.position = (Vector2)transform.position + ((dimxy / difference) * maxDistance * -1);
arrow.transform.position = new UnityEngine.Vector2(arrow.transform.position.x, arrow.transform.position.y);
}
*/
circle.transform.position = transform.position + new Vector3(0, 0, 0.04f);
Vector3 dir = mousePointA.transform.position - transform.position;
float rot;
if(Vector3.Angle(dir, transform.forward)> 90)
{
rot = Vector3.Angle(dir, transform.right);
}else
{
rot = Vector3.Angle(dir, transform.right) * -1;
}
//arrow.transform.eulerAngles = new Vector3(0, 0, rot);
// scale arrow
float scaleX = Mathf.Log(1 + spaceLimit / 10000f, 2f) * 0.05f;
float scaleY = Mathf.Log(1 + spaceLimit / 10000f, 2f) * 0.05f;
//arrow.transform.localScale = new Vector3(1 + scaleX, 1 + scaleY, 0.001f);
circle.transform.localScale = new Vector3(1 + scaleX, 1 + scaleY, 0.001f);
}
}
try to use this code for scaling and rotate the arrow
Vector3 dir = mousePointA.transform.position - transform.position;
float rot;
if (mousePointA.transform.position.y >= transform.position.y)
{
rot = Vector3.Angle(dir, transform.position) * -1;
}
else
{
rot = Vector3.Angle(dir, transform.position);
}
arrow.transform.eulerAngles = new Vector3(0, 0, rot);
// scale arrow
float scaleValue = Vector3.Distance(mousePointA.transform.position,
transform.position);
arrow.transform.localScale = new Vector3(1 + scaleValue,
arrow.transform.localScale.y, 1);
circle.transform.localScale = new Vector3(1 + scaleValue * 0.05f, 1 + scaleValue *
0.05f, 0.001f);
I'm trying to recreate a solar system and i'm using newton's law. The actual force works great but when i try to "predict" the path of a planet it just doesn't work: to predict it i use a lineRenderer whose points get placed on the position at the Tth instant ( T being time ). When i start the game they are close but as time passes the planet actually goes in "orbit" while the line shoots up.
I can't understand why this happens seen that i calculate the planet's position in the same way as the line's one. I also tried instantiating a ball but same result.
public void UpdateSpeed(Vector3 acceleration,float time)
{
velocity += acceleration * time;
}
public void UpdatePosition(float time)
{
transform.position +=velocity *time;
}
public void UpdateLine(float time)
{
position += velocity * time;
Debug.Log(position);
Instantiate(ball, position, Quaternion.identity);
line.positionCount++;
line.SetPosition(line.positionCount-1, position);
}
and here the function that computes acceleration
public Vector3 CalculateAcceleration(GameObject subj, float time)
{
Vector3 Acceleration = Vector3.zero;
foreach (var pl in planets)
{
if (pl != subj)
{
float sqrDistance= Mathf.Pow(Vector3.Distance(subj.transform.position, pl.transform.position), 2);
Vector3 direction = (pl.transform.position - subj.transform.position).normalized;
Acceleration += direction * (pl.GetComponent<Rigidbody>().mass * GravitationalConstant)/sqrDistance;
}
}
return Acceleration;
}
is the rigidbody attached to the planet making a difference for the position?
If you are having trouble with the orbit, calculate the OrbitFirst then call it to move your object with different time, just modify delta time below
LineRenderer lr;
//Build the orbit path/line
void CalculateOrbitEllipse()
{
Vector3[] points = new Vector3[segments + 1];
for (int i = 0; i < segments; i++)
{
Vector2 position2D = orbitPath.Evaluate((float)i / (float)segments);
points[i] = new Vector3(position2D.x, 0f, position2D.y);
}
points[segments] = points[0];
lr.positionCount = segments + 1;
lr.SetPositions(points);
}
//make the planet move,
//you can invoke this using your time variable, Time.deltaTime see below
IEnumerator AnimationOrbit()
{
if (orbitPeriod < 0.1f)
{
orbitPeriod = 0.1f;
}
float orbitSpeed = 0.1f / orbitPeriod;
while (orbitActive)
{
orbitProgress += Time.deltaTime * orbitSpeed;
orbitProgress %= 1f;
SetOrbitingObjectPosition();
lr = GetComponent<LineRenderer>();
CalculateOrbitEllipse();
yield return null;
}
}
void SetOrbitingObjectPosition()
{
Vector2 orbitPos = orbitPath.Evaluate(orbitProgress);
orbitingObject.localPosition = new Vector3(orbitPos.x, 0, orbitPos.y);
}
so as often happens i'm an idiot. I was calculating in the acceleration function distances with the actual bodies positions instead of the simulated position so i always had a false value that was increasing instead of varying
I don't usually post here but it has taken me hours now trying to figure this out and I've searched the net already but couldn't find the answer. I'm wishing someone might be able to assist me here. I'm a newbie and this is the first time I'm trying to combine two types of Third Person Camera Control that follows the Players movement in Unity C#:
Camera Snaps to a pre-defined Position and Rotation as the Player Moves (No Mouse Look)
Mouse Look activates when a Mouse Button is held down and so the Camera rotates according to Mouse movement whether the Player's Position is changing or not
It almost works except that I cannot seem to reset the Mouse Look to its first ever pre-defined setting. After the Player releases the Mouse Button, the code for #1 kicks in and so the Camera seem to go back to its default view. But doing further Mouse Looks, I noticed the Camera always returns to the last position & rotation it was deactivated. I need it to go back to the original pre-defined position & rotation even before the Player activates his first Mouse Look so its not too disorienting for the Player.
I tried several codes but couldn't get it to work so I removed non working lines and just posted those that I think are applicable ones. Please refer to my code below. Would appreciate it if someone can help me. Thanks in advance!
Edit: Updated the code to have two methods of controlling the Camera and added suggested code to reset the currentX & Y values. Comment/uncomment each of the method call to test. But I still have the problem of not being able to smooth out the zooming of the mouse look.
Final Edit: I have again updated the code below, cleaned it up, and included the suggested changes. Code should now be fully working and has no jitters. Thanks for the assistance! :-)
Last Final Edit: Added "Field of View Zooming" by Mouse Wheel and so have completed the code.
using UnityEngine;
using System.Collections;
public class PlayerViewController : MonoBehaviour {
// General Vars
public Transform targetFollow;
private bool lookAround = false;
// For SmoothDampFollow
public Vector3 followDefaultDistance = new Vector3 (0f, 12.0f, -20f);
public float followDistanceDamp = 0.2f;
public Vector3 followVelocity = Vector3.one;
// For Camera Orbit
public float orbitDistance = 20.0f;
public float orbitDamp = 5.0f;
private const float angleMinY = 7.0f;
private const float angleMaxY = 50.0f;
private float currentX = 7.0f;
private float currentY = 50.0f;
// For Zooming Field Of View
public float FOVmin = 50.0f;
public float FOVmax = 100.0f;
public float mouseWheelSpeed = 5.0f;
void Update () {
if (Input.GetMouseButtonDown (1)) {
currentX = transform.eulerAngles.y;
currentY = transform.eulerAngles.x;
}
if (Input.GetMouseButton (1)) {
lookAround = true;
} else {
lookAround = false;
}
ZoomFOV ();
}
void FixedUpdate () {
if (lookAround) {
CameraOrbit ();
} else {
SmoothDampFollow ();
}
}
void ZoomFOV () {
if (Input.GetAxis ("Mouse ScrollWheel") > 0) {
GetComponent<Camera> ().fieldOfView = GetComponent<Camera> ().fieldOfView - mouseWheelSpeed;
if (GetComponent<Camera> ().fieldOfView <= FOVmin) { GetComponent<Camera> ().fieldOfView = FOVmin; }
} else if (Input.GetAxis ("Mouse ScrollWheel") < 0) {
GetComponent<Camera> ().fieldOfView = GetComponent<Camera> ().fieldOfView + mouseWheelSpeed;
if (GetComponent<Camera> ().fieldOfView >= FOVmax) { GetComponent<Camera> ().fieldOfView = FOVmax; }
}
}
void SmoothDampFollow () {
if (!targetFollow) {
return;
} else {
Vector3 wantedPosition = targetFollow.position + (targetFollow.rotation * followDefaultDistance);
transform.position = Vector3.SmoothDamp (transform.position, wantedPosition, ref followVelocity, followDistanceDamp);
transform.LookAt (targetFollow, targetFollow.up);
}
}
void CameraOrbit () {
if (!targetFollow) {
return;
} else {
currentX += Input.GetAxis ("Mouse X");
currentY += Input.GetAxis ("Mouse Y");
currentY = Mathf.Clamp (currentY, angleMinY, angleMaxY);
Vector3 dir = new Vector3 (0, 0, -orbitDistance);
Quaternion rotation = Quaternion.Euler (currentY, currentX, 0);
Vector3 wantedPosition = targetFollow.position + rotation * dir;
transform.position = Vector3.Lerp (transform.position, wantedPosition, Time.deltaTime * orbitDamp);
transform.LookAt (targetFollow.position);
}
}
}
Updated:
Try this
void LateUpdate()
{
if (lookAround)
{
currentX += Input.GetAxisRaw("Mouse X");
currentY += Input.GetAxisRaw("Mouse Y");
currentY = Mathf.Clamp(currentY, angleMinY, angleMaxY);
Vector3 dir = new Vector3(0, 0, -distance);
Quaternion rotation = Quaternion.Euler(currentY, currentX, 0);
Vector3 wantedPosition = target.position + rotation * dir;
transform.position = Vector3.Lerp(transform.position, wantedPosition, Time.deltaTime * damping);
camTransform.LookAt(target.position);
}
...
And
void Update()
{
// Here
if (Input.GetMouseButtonDown(1))
{
currentX = transform.eulerAngles.y;
currentY = transform.eulerAngles.x;
}
if (Input.GetMouseButton(1))
{
...
What i've done is i reset the currentX and currentY to their current eulerangle-values on mouse-press in Update(). And i've added a position Lerp to the wanted lookAround position in LateUpdate()
EDIT:
Though I still haven't fixed the smooth zooming of the mouse look when right mouse button is initially held down
Try this change
void CameraOrbit()
{
currentX += Input.GetAxisRaw("Mouse X");
currentY += Input.GetAxisRaw("Mouse Y");
currentY = Mathf.Clamp(currentY, angleMinY, angleMaxY);
Vector3 dir = new Vector3(0, 0, -distance);
Quaternion rotation = Quaternion.Euler(currentY, currentX, 0);
// -->
Vector3 wantedPosition = target.position + rotation * dir;
transform.position = Vector3.Lerp(transform.position, wantedPosition, Time.deltaTime * damping);
// <--
camTransform.LookAt(target.position);
}
I have an issue whereby I am unable to uplift camera height to head of third-person controller, always it is in the middle of third-person controller. Could anyone help me on this regard.
This is image description
Here is the code attatched to my thirdpersoncontroller:
public class ThirdPersonCam : NetworkBehaviour
{
public Transform cameraTransform;
private Transform _target;
// The distance in the x-z plane to the target
public float distance = 2.0f;
// the height we want the camera to be above the target
public float height = 0.0f;
public float angularSmoothLag = 0.0000001f;
public float angularMaxSpeed = 100000000.0f;
public float heightSmoothLag = 0.3f;
public float snapSmoothLag = 0.2f;
public float snapMaxSpeed = 720.0f;
public float clampHeadPositionScreenSpace = 0.75f;
public float lockCameraTimeout = 0.2f;
private Vector3 headOffset = Vector3.zero;
private Vector3 centerOffset = Vector3.zero;
private float heightVelocity = 0.0f;
private float angleVelocity = 0.0f;
private bool snap = false;
private ThirdPersonShooter controller;
private float targetHeight = 100000.0f;
void Awake()
{
if (!cameraTransform && Camera.main)
cameraTransform = Camera.main.transform;
if (!cameraTransform)
{
Debug.Log("Please assign a camera to the ThirdPersonCamera script.");
enabled = false;
}
_target = transform;
if (_target)
{
controller = _target.GetComponent<ThirdPersonShooter>();
}
if (controller)
{
CharacterController characterController = _target.GetComponent<CharacterController>();
centerOffset = characterController.bounds.center - _target.position;
headOffset = centerOffset;
headOffset.y = characterController.bounds.max.y - _target.position.y;
}
else
Debug.Log("Please assign a target to the camera that has a ThirdPersonController script attached.");
Cut(_target, centerOffset);
}
void DebugDrawStuff()
{
Debug.DrawLine(_target.position, _target.position + headOffset);
}
public float AngleDistance(float a, float b)
{
a = Mathf.Repeat(a, 360);
b = Mathf.Repeat(b, 360);
return Mathf.Abs(b - a);
}
void Apply(Transform dummyTarget, Vector3 dummyCenter)
{
// Early out if we don't have a target
if (!controller)
return;
Vector3 targetCenter = _target.position + centerOffset;
Vector3 targetHead = _target.position + headOffset;
// DebugDrawStuff();
// Calculate the current & target rotation angles
float originalTargetAngle = _target.eulerAngles.y;
float currentAngle = cameraTransform.eulerAngles.y;
// Adjust real target angle when camera is locked
float targetAngle = originalTargetAngle;
// When pressing Fire2 (alt) the camera will snap to the target direction real quick.
//// It will stop snapping when it reaches the target
//if (Input.GetButton("LockOn"))
// snap = true;
if (snap)
{
// We are close to the target, so we can stop snapping now!
if (AngleDistance(currentAngle, originalTargetAngle) < 3.0f)
snap = false;
currentAngle = Mathf.SmoothDampAngle(currentAngle, targetAngle, ref angleVelocity, snapSmoothLag, snapMaxSpeed);
}
// Normal camera motion
else
{
if (controller.GetLockCameraTimer() < lockCameraTimeout)
{
targetAngle = currentAngle;
}
// Lock the camera when moving backwards!
// * It is really confusing to do 180 degree spins when turning around.
if (AngleDistance(currentAngle, targetAngle) > 160 && controller.IsMovingBackwards())
targetAngle += 180;
currentAngle = Mathf.SmoothDampAngle(currentAngle, targetAngle, ref angleVelocity, angularSmoothLag, angularMaxSpeed);
}
// When jumping don't move camera upwards but only down!
if (controller.IsJumping())
{
// We'd be moving the camera upwards, do that only if it's really high
float newTargetHeight = targetCenter.y + height;
if (newTargetHeight < targetHeight || newTargetHeight - targetHeight > 5)
targetHeight = targetCenter.y + height;
}
// When walking always update the target height
else
{
targetHeight = targetCenter.y + height;
}
// Damp the height
float currentHeight = cameraTransform.position.y;
currentHeight = Mathf.SmoothDamp(currentHeight, targetHeight, ref heightVelocity, heightSmoothLag);
// Convert the angle into a rotation, by which we then reposition the camera
Quaternion currentRotation = Quaternion.Euler(0, currentAngle, 0);
// Set the position of the camera on the x-z plane to:
// distance meters behind the target
cameraTransform.position = targetCenter;
cameraTransform.position += currentRotation * Vector3.back * distance;
Vector3 tempCameraTransformPos = cameraTransform.position;
tempCameraTransformPos.y = currentHeight;
cameraTransform.position = tempCameraTransformPos;
// Always look at the target
SetUpRotation(targetCenter, targetHead);
}
void LateUpdate()
{
Apply(transform, Vector3.zero);
}
void Cut(Transform dummyTarget, Vector3 dummyCenter)
{
float oldHeightSmooth = heightSmoothLag;
float oldSnapMaxSpeed = snapMaxSpeed;
float oldSnapSmooth = snapSmoothLag;
snapMaxSpeed = 10000;
snapSmoothLag = 0.001f;
heightSmoothLag = 0.001f;
snap = true;
Apply(transform, Vector3.zero);
heightSmoothLag = oldHeightSmooth;
snapMaxSpeed = oldSnapMaxSpeed;
snapSmoothLag = oldSnapSmooth;
}
void SetUpRotation(Vector3 centerPos, Vector3 headPos)
{
// Now it's getting hairy. The devil is in the details here, the big issue is jumping of course.
// * When jumping up and down we don't want to center the guy in screen space.
// This is important to give a feel for how high you jump and avoiding large camera movements.
//
// * At the same time we dont want him to ever go out of screen and we want all rotations to be totally smooth.
//
// So here is what we will do:
//
// 1. We first find the rotation around the y axis. Thus he is always centered on the y-axis
// 2. When grounded we make him be centered
// 3. When jumping we keep the camera rotation but rotate the camera to get him back into view if his head is above some threshold
// 4. When landing we smoothly interpolate towards centering him on screen
Vector3 cameraPos = cameraTransform.position;
Vector3 offsetToCenter = centerPos - cameraPos;
// Generate base rotation only around y-axis
Quaternion yRotation = Quaternion.LookRotation(new Vector3(offsetToCenter.x, 0, offsetToCenter.z));
Vector3 relativeOffset = Vector3.forward * distance + Vector3.down * height;
cameraTransform.rotation = yRotation * Quaternion.LookRotation(relativeOffset);
// Calculate the projected center position and top position in world space
Ray centerRay = cameraTransform.GetComponent<Camera>().ViewportPointToRay(new Vector3(.5f, 0.5f, 1f));
Ray topRay = cameraTransform.GetComponent<Camera>().ViewportPointToRay(new Vector3(.5f, clampHeadPositionScreenSpace, 1f));
Vector3 centerRayPos = centerRay.GetPoint(distance);
Vector3 topRayPos = topRay.GetPoint(distance);
float centerToTopAngle = Vector3.Angle(centerRay.direction, topRay.direction);
float heightToAngle = centerToTopAngle / (centerRayPos.y - topRayPos.y);
float extraLookAngle = heightToAngle * (centerRayPos.y - centerPos.y);
if (extraLookAngle < centerToTopAngle)
{
extraLookAngle = 0;
}
else
{
extraLookAngle = extraLookAngle - centerToTopAngle;
cameraTransform.rotation *= Quaternion.Euler(-extraLookAngle, 0, 0);
}
}
public Vector3 GetCenterOffset()
{
return centerOffset;
}
}
I am working on a simple patrol script, and everything works but the characters are randomly rolling through the floor when they turn around. Here is a short clip of them...
Here is my script...
public class Patrol : MonoBehaviour
{
public float speed = 5;
public float directionChangeInterval = 1;
public float maxHeadingChange = 30;
public bool useRootMotion;
CharacterController controller;
float heading;
Vector3 targetRotation;
Vector3 forward
{
get { return transform.TransformDirection(Vector3.forward); }
}
void Awake()
{
controller = GetComponent<CharacterController>();
// Set random initial rotation
heading = Random.Range(0, 360);
transform.eulerAngles = new Vector3(0, heading, 0);
StartCoroutine(NewHeadingRoutine());
}
void Update()
{
transform.eulerAngles = Vector3.Slerp(transform.eulerAngles, targetRotation, Time.deltaTime * directionChangeInterval);
if (useRootMotion)
{
return;
}
else
{
controller.SimpleMove(forward * speed);
}
}
void OnControllerColliderHit(ControllerColliderHit hit)
{
if (hit.gameObject.tag == "Player")
{
// Bounce off the obstacle and change direction
var newDirection = Vector3.Reflect(forward, hit.normal);
transform.rotation = Quaternion.FromToRotation(Vector3.forward, newDirection);
heading = transform.eulerAngles.y;
NewHeading();
}
if (hit.gameObject.tag == "Boundary")
{
// Bounce off the obstacle and change direction
var newDirection = Vector3.Reflect(forward, hit.normal);
transform.rotation = Quaternion.FromToRotation(Vector3.forward, newDirection);
heading = transform.eulerAngles.y;
NewHeading();
}
}
/// Finds a new direction to move towards.
void NewHeading()
{
var floor = transform.eulerAngles.y - maxHeadingChange;
var ceil = transform.eulerAngles.y + maxHeadingChange;
heading = Random.Range(floor, ceil);
targetRotation = new Vector3(0, heading, 0);
}
/// Repeatedly calculates a new direction to move towards.
IEnumerator NewHeadingRoutine()
{
while (true)
{
NewHeading();
yield return new WaitForSeconds(directionChangeInterval);
}
}
}
I have tried adding a rigidbody to the characters and constraining rotation, but that doesnt work. Oddly enough, the character control isnt rotating at all. In the scene view I can see the character collider staying as it should, but the character flips through the mesh on its own.
It looks like it's because they are walking into a corner and being bounced between the two walls constantly which causes them to behave strangely. I would add a method of checking for a series of very quick collisions to detect that they are in a corner or stuck and then adapt accordingly, perhaps with a method to rotate 180 degrees and keep walking or the like.
You can do it like this:
float fTime = 0.1f
float fTimer = 0;
int iCollisionCounter;
if(collision){
if(fTimer > 0) iCollisionCounter++;
if(iCollisionCounter >= 5) //Character is stuck
fTimer = fTime;
}
Void Update(){
fTimer -= time.deltaTime;
}
That means that if there are multiple collisions within 0.1 seconds of each other you can handle it.
Hope this helps!