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;
}
}
Related
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RotateAroundTarget : MonoBehaviour
{
public Transform target;
public Vector3 axisToRotate;
public float verticalSpeed = 20;
public float radius = 10;
public bool moveToRadius = false;
private float targetHeight;
private float oldRadius;
private bool moveToRadiusCoroutineOnce = true;
// Start is called before the first frame update
void Start()
{
// Initialize with a height to start moving toward
targetHeight = GetNewTargetHeight();
// If we start at a given distance away from the target, then this
// will become our radius when we RotateAround it
oldRadius = radius;
if (moveToRadius == false)
{
transform.position = new Vector3(0, 0, radius);
}
}
// Update is called once per frame
void Update()
{
if(moveToRadius && moveToRadiusCoroutineOnce)
{
StartCoroutine(MoveTo(new Vector3(0,0,radius), 5f));
moveToRadiusCoroutineOnce = false;
}
if(radius != oldRadius)
{
//transform.position = new Vector3(0, 0, radius);
oldRadius = radius;
moveToRadiusCoroutineOnce = true;
}
transform.RotateAround(target.transform.position,
axisToRotate, 20 * Time.deltaTime);
// Just like your rotation rate, we scale this based on game time
float maxHeightChange = verticalSpeed * Time.deltaTime;
float targetHeightDelta = targetHeight - transform.position.z;
if (maxHeightChange >= Mathf.Abs(targetHeightDelta))
{
// If the current height is close enough to the target, we can move it
// there and then generate a new height to go towards
transform.position = new Vector3(
transform.position.x,
targetHeight,
transform.position.z);
targetHeight = GetNewTargetHeight();
}
else
{
// Otherwise, limit the magnitude of the height change by the speed and
// apply it to the current height
float allowedHeightChange = Mathf.Clamp(
targetHeightDelta,
-maxHeightChange,
maxHeightChange);
transform.position = new Vector3(
transform.position.x,
transform.position.y + allowedHeightChange,
transform.position.z);
}
}
private float GetNewTargetHeight()
{
return Random.Range(0, 5f);
}
private IEnumerator MoveTo(Vector3 endPos, float moveDurationInSeconds)
{
var fromPosition = transform.position;
var toPosition = endPos;
// looks strange but as long as you yield somewhere inside
// the loop it simply means repeat the sequence forever
// just like the Update method
while (true)
{
var passedTime = 0f;
while (passedTime < moveDurationInSeconds)
{
var lerpFactor = passedTime / moveDurationInSeconds;
// and now add ease-in and ease-out
var smoothedLerpFactor = Mathf.SmoothStep(0, 1, lerpFactor);
transform.position = Vector3.Lerp(fromPosition, toPosition, smoothedLerpFactor);
passedTime += Mathf.Min(moveDurationInSeconds - passedTime, Time.deltaTime);
// reads like: "pause" here, render this frame and continue
// from here in the next frame
yield return null;
}
break;
}
}
}
I added the variable axisToRotate and using it in the line :
transform.RotateAround(target.transform.position,
axisToRotate, 20 * Time.deltaTime);
before the line was :
transform.RotateAround(target.transform.position,
new Vector3(0,1,0), 20 * Time.deltaTime);
and if i'm setting in the inspector the axisToRotate to 0,1,0 it's working fine. the transform is changing the height randomaly and rotating in horizontal around the target.
but now I want to be able also to change the rotation to vertical around the target with the random height.
if I'm changing the axisToRotate to 0,0,1 and not using the height part it will rotate around the target vertical fine but if I'm using the height part it will move the transform far from the target and will not rotate around the target at all.
I tried to change the variable axisToRotate in the inspector to 0,0,1 but then it's moving the transfrom away from the target.
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 am trying to build a trajectory arc that would predict the trajectory path of the object, the drag and shoot seem fine but the arc is not working. initially I used an arrow for showing the direction of the movement of the object but later I tried to do the same using an array which would store 2 points and would keep updating after every iteration and it would result in an arc since I used the equations of motion to predict the positions after each frame.
***
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class movement: MonoBehaviour
{
public float velocity;
float time;
float x;
float y;
float tt;
float g;
Vector2 force;
public float power = 2.0f;
Vector3 startpoint;
Vector3 endpoint;
Camera cam;
public Vector2 maxpower;
public Vector2 minpower;
public Rigidbody2D rb;
Vector3 currentposition;
Vector3 sp;
LineRenderer lr;
int resolution = 10;
Vector3 newpoint;
// Start is called before the first frame update
void Start()
{
time = 0f;
g = Mathf.Abs(Physics2D.gravity.y);
cam = Camera.main;
lr = GetComponent<LineRenderer>();
}
// Update is called once per frame
void Update()
{
time += Time.deltaTime;
x = gameObject.transform.position.x + velocity * time;
tt = time * time;
y = gameObject.transform.position.y + (g * tt) / 2f;
if(Input.GetMouseButtonDown(0))
{
startpoint = cam.ScreenToWorldPoint(Input.mousePosition);
startpoint.z = 5;
}
if(Input.GetMouseButton(0))
{
sp = new Vector3(gameObject.transform.position.x, gameObject.transform.position.y, 5);
currentposition = cam.ScreenToWorldPoint(Input.mousePosition);
currentposition.z = 5;
LineRenderer(sp);
}
if (Input.GetMouseButtonUp(0))
{
endpoint = cam.ScreenToWorldPoint(Input.mousePosition);
endpoint.z = 5;
force = new Vector2(Mathf.Clamp(startpoint.x - endpoint.x, minpower.x, maxpower.x), Mathf.Clamp(startpoint.y - endpoint.y, minpower.y, maxpower.y));
rb.AddForce(force * power, ForceMode2D.Impulse);
x = x + velocity * time;
y = y + (g * tt) / 2f;
EndLine();
}
}
public void LineRenderer(Vector3 p)
{
lr.positionCount = resolution;
Vector3 arc = p;
for(int i=0;i<resolution;i++)
{
newpoint = calculate(arc, i / (float)resolution);
lr.SetPosition(i, newpoint);
arc = newpoint;
}
}
public Vector3 calculate(Vector3 point, float t)
{
point.x += velocity * t;
point.y += 0.5f * g * t * t;
return point;
}
public void EndLine()
{
lr.positionCount = 0;
}
}
***
This the code, any help is appreciated.
A bug that I see is your delta-time:
You call Calculate(arc, i/resolution), with i and resolution both being integers.
The parameter might be float time, but C# will first calculate i/resolution as an integer division, then convert the result to float. The result will be zero as long as i is less than resolution.
Change it to: i/(float)resolution to force a floating-point division.
I have the following Unity Script
void Update()
{
Ray mouseRay = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit = new RaycastHit();
if(Physics.Raycast(mouseRay, out hit))
{
if (Input.GetMouseButtonDown(0))
{
Camera.main.GetComponent<CameraController>().ZoomIn(hit.transform.position);
}
}
}
Then in the camera I Have
public void ZoomIn(Vector3 target)
{
zoomSavePosition = camera.transform.position;
StartCoroutine(Zoom(zoomSavePosition, target, zoomDuration));
}
// ZoomRoutine
IEnumerator Zoom(Vector3 from, Vector3 to, float duration)
{
float time = 0;
Quaternion rotation = camera.transform.rotation;
Quaternion targetRotation = Quaternion.LookRotation(to - camera.transform.position);
while (time < zoomDuration)
{
camera.transform.position = Vector3.Lerp(from, to, time / zoomDuration);
camera.transform.rotation = Quaternion.Slerp(rotation, targetRotation, time / zoomDuration);
time += TimeManager.instance.deltaTime;
yield return null;
}
camera.transform.position = to;
camera.transform.rotation = targetRotation;
}
This works great, but it zoom inside the object. I would like to, no matter from where and to what, I always zoom and end up at the same distance of the target.
I was thinking to use MoveTowards but inside the coroutine this will make the movement stop, but the rotation will keep going (and it's a big ugly)
So give a point A and a point B how do I calculate a distance in the middle that is set to always the same distance from B ?
For zooming to a specific distance from the target object you need to calculate the distance from the target object in direction of the camera and then subtract it to the target position.
var heading = to - from;
var dist = heading.magnitude;
var direction = heading / dist;
Vector3 newTargetPosition = to - (direction * distance);
Ref: https://docs.unity3d.com/Manual/DirectionDistanceFromOneObjectToAnother.html
With this adjustment you can stop zooming to any provided distance.
Here is how adjusted code would look like:
public void ZoomIn(Vector3 target)
{
float targetDistance = 2f;
zoomSavePosition = camera.transform.position;
StartCoroutine(Zoom(zoomSavePosition, target, zoomDuration, targetDistance));
}
// ZoomRoutine
IEnumerator Zoom(Vector3 from, Vector3 to, float duration, float distance)
{
float time = 0;
Quaternion rotation = camera.transform.rotation;
Quaternion targetRotation = Quaternion.LookRotation(to - camera.transform.position);
var heading = to - from;
var dist = heading.magnitude;
var direction = heading / dist;
Vector3 newTargetPosition = to - (direction * distance);
while (time < zoomDuration)
{
camera.transform.position = Vector3.Lerp(from, newTargetPosition, time / zoomDuration);
camera.transform.rotation = Quaternion.Slerp(rotation, targetRotation, time / zoomDuration);
time += Time.deltaTime;
yield return null;
}
camera.transform.position = newTargetPosition;
camera.transform.rotation = targetRotation;
}
You can set value of targetDistance where you want to stop zooming.
I currently have the movement of my enemy working where they start to move towards the enemy but I am looking to change that to only moving towards the player if the player gets within a certain distance of the enemy so I will need to create some code to work out where the enemy is and if they are within say 175 pixels, the enemy will start to move. I am not sure how to implement this into the code I already have.
This is my enemy class code for movement: it uses trig to calculate the shortest distance to the enemy then pushes the enemy object towards the player. Once a collision has occurred, the player is removed.
class Enemy : Obj
{
float spd = 1;
float detectionDistance = 175;
public Enemy(Vector2 pos)
: base(pos)
{
position = pos;
spriteName = "BlackBall";
speed = spd;
}
public override void Update()
{
rotation = point_direction(position.X, position.Y, Player.player.position.X, Player.player.position.Y);
speed = spd;
base.Update();
}
public override void pushTo(float pix, float dir)
{
float newX = (float)Math.Cos(MathHelper.ToRadians(dir));
float newY = (float)Math.Sin(MathHelper.ToRadians(dir));
newX *= pix;
newY *= pix;
if (!Collision(new Vector2(newX, newY), new Player(Vector2.Zero)))
{
base.pushTo(pix, dir);
}
}
//Uses Trig to calculate the shortest distance to the player then moves towards that position
private float point_direction(float x, float y, float x2, float y2)
{
float diffx = x - x2;
float diffy = y - y2;
float adj = diffx;
float opp = diffy;
float tan = opp / adj;
float res = MathHelper.ToDegrees((float)Math.Atan2(opp, adj));
res = (res - 180) % 360;
if (res < 0) { res += 360; }
return res;
}
public override void Update()
{
rotation = point_direction(position.X, position.Y, Player.player.position.X, Player.player.position.Y);
distance = sqrt(xdiff^2 + ydiff^2) <-- This line is pseudo-code
if(distance<detectionDistance)
{
speed = spd;
}
else
{
speed = 0
}
base.Update();
}
Use the distance formula and check if the player position is within 175 units.
public override void Update()
{
rotation = point_direction(position.X, position.Y, Player.player.position.X, Player.player.position.Y);
speed = Math.Sqrt(Math.Pow(Math.Abs(Player.player.position.X - position.X), 2.0) + Math.Pow(Math.Abs(Player.player.position.Y - position.Y), 2.0))) <= 175 ? spd : 0;
base.Update();
}