Twitching of vehicle - c#

So I edited my question because I have forgotten to show you a camera script. Maybe the problem is somewhere there. I just believe that even after editing the old question there will absolutely no people who read edited article. So I've created a new question.
So I have such a problem. I try to make a tram moving. To do it I downloaded a script from Asset Store "Bezier Path Creator": https://assetstore.unity.com/packages/tools/utilities/b-zier-path-creator-136082. It works nice. Before the need of creating third person camera. After writing a short script I saw that vehicle is twitching after getting some speed. I sat a couple of days trying to solve this problem and I saw one thing. If to remove * Time.deltaTime there are no twitching anymore but there is dependence on FPS (what was predictable). Also if to click Pause button and look frame by frame there are no twitching. What can I do about it?
There are some pieces of code I have problems with:
switch (acceleration) {
case -1:
acc = -12F;
break;
case -2:
acc = -15F;
break;
case -3:
acc = -18F;
break;
case 0:
acc = 0.000f;
break;
case 1:
acc = 9f;
break;
case 2:
acc = 12f;
break;
case 3:
acc = 17.1f;
break;
}
if (Input.GetKeyDown ("e")) {
acceleration++;
}
if (Input.GetKeyDown ("q")) {
acceleration--;
}
if (acceleration < -3) {
acceleration = -3;
}
if (acceleration > 3) {
acceleration = 3;
}
if (speed > 40.0f) {
speed = 40.0f;
saveSpeed = speed;
speed = saveSpeed;
} else if (speed >= 0.0f && speed <= 0.03f && acceleration == 0) {
speed = 0.0f;
} else if (speed <= 0.0f && speed >= -0.03f && acceleration == 0) {
speed = 0.0f;
} else if (speed <= 40.0f) {
speed += ((Time.time - startTime)/1000)*(-acc);
}
// Taking a piece of Asset Store code
transform.rotation = pathCreator.path.GetRotationAtDistance(distanceTravelled);
transform.position = pathCreator.path.GetPointAtDistance(distanceTravelled); // I guess the problem is somewhere there
So there are GetRotationAtDistance and GetPointAtDistance methods from Asset Store script:
public Vector3 GetPointAtDistance(float dst, EndOfPathInstruction endOfPathInstruction = EndOfPathInstruction.Loop)
{
float t = dst / length;
return GetPoint(t, endOfPathInstruction);
}
public Vector3 GetPoint(float t, EndOfPathInstruction endOfPathInstruction = EndOfPathInstruction.Loop)
{
var data = CalculatePercentOnPathData(t, endOfPathInstruction);
return Vector3.Lerp(vertices[data.previousIndex], vertices[data.nextIndex], data.percentBetweenIndices);
}
public Quaternion GetRotationAtDistance(float dst, EndOfPathInstruction endOfPathInstruction = EndOfPathInstruction.Loop)
{
float t = dst / length;
return GetRotation(t, endOfPathInstruction);
}
public Quaternion GetRotation(float t, EndOfPathInstruction endOfPathInstruction = EndOfPathInstruction.Loop)
{
var data = CalculatePercentOnPathData(t, endOfPathInstruction);
Vector3 direction = Vector3.Lerp(tangents[data.previousIndex], tangents[data.nextIndex], data.percentBetweenIndices);
Vector3 normal = Vector3.Lerp(normals[data.previousIndex], normals[data.nextIndex], data.percentBetweenIndices);
return Quaternion.LookRotation(direction, normal);
}
Script of camera:
using UnityEngine;
using System.Collections;
public class CameraRotateAround : MonoBehaviour {
public GameObject target;
public Vector3 offset;
public float sensitivity = 3; // чувствительность мышки
public float limit = 80; // ограничение вращения по Y
public float zoom = 0.25f; // чувствительность при увеличении, колесиком мышки
public float zoomMax = 10; // макс. увеличение
public float zoomMin = 3; // мин. увеличение
private float X, Y;
void Start ()
{
limit = Mathf.Abs(limit);
if(limit > 90) limit = 90;
offset = new Vector3(offset.x, offset.y, -Mathf.Abs(zoomMax)/2);
transform.position = target.transform.position + offset;
offset.z -= zoom + 3;
}
void Update ()
{
if(Input.GetAxis("Mouse ScrollWheel") > 0) offset.z += zoom;
else if(Input.GetAxis("Mouse ScrollWheel") < 0) offset.z -= zoom;
offset.z = Mathf.Clamp(offset.z, -Mathf.Abs(zoomMax), -Mathf.Abs(zoomMin));
X = transform.localEulerAngles.y + Input.GetAxis("Mouse X") * sensitivity;
Y += Input.GetAxis("Mouse Y") * sensitivity;
Y = Mathf.Clamp (Y, -limit, 0);
transform.localEulerAngles = new Vector3(-Y, X, 0);
transform.localPosition = transform.localRotation * offset + target.transform.position;
}
}
Hope somebody'll help me this time because I really don't know what can I do to fix this bag.

I once had a similar problem and This link Helped me out a ton.
The problem you are experiencing is due to The camera being 3rd person and not parented to the object it is following.
The problem is that the target object when moved, moves before the camera, thus causing the stutter, as the camera moves just after the object. Try putting the camera script in LateUpdate, but if that does not work, then I suggest checking the link I have linked above.
The link talks about interpolation, which, without increasing your physics timestep, moves the camera to a position which the provided asset calculates the camera will be at, thus removing the stutter and achieving smooth motion. Check the link for the full info

Related

UNITY C#..my enemys are piling up with vector2.move towards

hi there thanks for taking the time to look at my question
im currenty making a 2D side scroller/platformer and im currently facing a issue where my enemies are piling up on top of each other. through googling i see alot of Astar pathfinding solving this however i hesitant to jump into ai right now as i glanced over its internal scripts and they are outside my current knowledge and im still attempting to get a understanding of just using scripting to achieve what i want.
i currently have my enemies correctly chasing my player with a vector2.movetowards however eventually they all pile on each other.
through google i have found this is a typical outcome of movetowards and i found this solution
https://answers.unity.com/questions/1608266/stop-enemies-from-grouping-up.html
and i implemented its code
void avoidOthers()
{
var hits = Physics2D.OverlapCircleAll(enemy.transform.position, avoidenceRadius);//create a circle around each enemy and check to see if they are not colliding
foreach (var hit in hits)
{
if(hit.GetComponent<DartFish>() != null && hit.transform != transform)//check to see if it is a enemy
{
Debug.Log("enemy transform" + enemy.transform.position);
Debug.Log("hit transform" + hit.transform.position);
Vector2 difference = enemy.transform.position - hit.transform.position;//check how close we are to another enemy
difference = difference.normalized / Mathf.Abs(difference.magnitude);//calculate how long the vector is
sum += difference;//add together differences so we can calculate a average of the group
count++;
}
}
if(count > 0)// if we found enemys interfearing with each other
{
sum /= count;//average of the movement
sum = sum.normalized * avoidenceSpeed;//calculate movement speed
Debug.Log("sum = " + sum);
enemy.transform.position = Vector2.MoveTowards(enemy.transform.position, enemy.transform.position + (Vector3)sum, avoidenceSpeed * Time.deltaTime);
}
}
however i needed to add "enemy" to each transform.position or they would instantly snap from my spawn location to the player.
after adding enemy they stopped snapping over a large distance except now they started piling again
all of my debug logs look like they are printing out the correct values
i was also reading through transform.translate as looking at my "sum" values this should be how much they are moving away from each other
so i changed the line
enemy.transform.position = Vector2.MoveTowards(enemy.transform.position, enemy.transform.position + (Vector3)sum, avoidenceSpeed * Time.deltaTime);
to
transform.Translate((Vector3)sum * avoidenceSpeed * Time.deltaTime, Space.World);
now it seems to only effect one fish and the others clump together still
from my understanding physics.overlapcircle all should be functioning as a list?
heres my entire code albeit long(still relatively new to coding)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DartFish : MonoBehaviour
{
public Transform target;
public float movementSpeed;
private float initialMovementSpeed;
public float dashSpeed;
private float spawnSpeed = 1.5f;
public float chaseRadius;
private float initialChaseRadius;
public float dashRadius;
private Vector2 targetDirection;
private float directionX;
private float previousDirectionX;
private float directionY;
private float spriteAngle;
private float previousAngle;
private float currentSpriteAngle;
private float difference;
private SpriteRenderer mySpriteRenderer;
private Rigidbody2D myRigidbody;
private bool directionIsAboutToChange;
private bool directionHasChanged;
private Vector3 angleCompensation;
// avoid others variables
public float avoidenceSpeed;
public float avoidenceRadius;
public Transform enemy;
private Vector2 sum = Vector2.zero;
private float count;
// Start is called before the first frame update
void Start()
{
enemy = GameObject.FindWithTag("dartfish").transform;
target = GameObject.FindWithTag("Player").transform;
mySpriteRenderer = GetComponent<SpriteRenderer>();
myRigidbody = GetComponent<Rigidbody2D>();
initialMovementSpeed = movementSpeed;
initialChaseRadius = chaseRadius;
}
// Update is called once per frame
void Update()
{
targetDirection = target.position - transform.position;// where is the player?
directionX = targetDirection.x;//extract only x for "check direction"
directionY = targetDirection.y;
if(directionX < -initialChaseRadius + .5f)// is the player further then the chase radius?
{
myRigidbody.velocity = new Vector2(-1.5f, 0);
}
else
{
myRigidbody.velocity = Vector2.zero;
}
if(directionX > 0)//calculate tangent angle to the player in radians and then convert it to degrees
{
spriteAngle = Mathf.Atan2(targetDirection.y, targetDirection.x) * Mathf.Rad2Deg;
}
else if(directionX < 0)//if player is to the left flip opp/adj and add 100° to zero angle
{
spriteAngle = Mathf.Atan2(targetDirection.x, targetDirection.y) * Mathf.Rad2Deg +100f;
}
currentSpriteAngle = spriteAngle;
difference = spriteAngle - previousAngle;
difference = Mathf.Abs(difference);
if(directionX < 1 && directionX > -1)//are we about to change directions?
{
//Debug.Log("direction is about to change");
directionIsAboutToChange = true;
}
if(directionIsAboutToChange == true)//did we pass through trigger zone?
{
if(directionX > 1 || directionX < -1)
//Debug.Log("direction has changed");
directionHasChanged = true;
}
lookAtMe();
chaseMe();
avoidOthers();
/*
//debug logs with spam control
if(previousAngle != spriteAngle)
{
Debug.Log("angle = " + spriteAngle);
}
if(difference != 0)
{
Debug.Log("difference = " + difference);
}
if(previousDirectionX != directionX)
{
Debug.Log("directionX = " + directionX);
}
if(previousAngle != currentSpriteAngle)
{
Debug.Log("previous angle = " + previousAngle);
}
*/
previousAngle = currentSpriteAngle;
previousDirectionX = directionX;
}
void lookAtMe()
{
if(directionHasChanged == true)//angle compensation for sprite flip
{
if(directionY > 0)//is the player above or below us
{
angleCompensation = transform.localEulerAngles;//get current rotation
//flip it
if(directionX > 0)
{
transform.localEulerAngles = -angleCompensation;
}
if(directionX < 0)
{
transform.localEulerAngles = angleCompensation;
}
}
if(directionY < 0)
{
if(directionX > 0)
{
transform.localEulerAngles = angleCompensation;
}
if(directionX < 0)
{
transform.localEulerAngles = -angleCompensation;
}
}
directionHasChanged = false;
directionIsAboutToChange = false;
}
if(directionX < -1)
{
mySpriteRenderer.flipX = true;
//Debug.Log("entered void lookAtMe.directionX <");
if(spriteAngle > previousAngle && spriteAngle < 50f )
{
transform.Rotate(0f, 0f, -difference);
}
else if(spriteAngle < previousAngle && spriteAngle > -50f)
{
transform.Rotate(0f, 0f, difference);
}
}
else if(directionX > 1)
{
mySpriteRenderer.flipX = false;
//Debug.Log("entered void lookAtMe.directionX >");
if(spriteAngle > previousAngle && spriteAngle < 50f)
{
transform.Rotate(0f, 0f, difference);
}
else if(spriteAngle < previousAngle && spriteAngle > -50f)
{
transform.Rotate(0f, 0f, -difference);
}
}
}
void avoidOthers()
{
var hits = Physics2D.OverlapCircleAll(enemy.transform.position, avoidenceRadius);//create a circle around each enemy and check to see if they are not colliding
foreach (var hit in hits)
{
if(hit.GetComponent<DartFish>() != null && hit.transform != transform)//check to see if it is a enemy
{
Debug.Log("enemy transform" + enemy.transform.position);
Debug.Log("hit transform" + hit.transform.position);
Vector2 difference = enemy.transform.position - hit.transform.position;//check how close we are to another enemy
difference = difference.normalized / Mathf.Abs(difference.magnitude);//calculate how long the vector is
sum += difference;//add together differences so we can calculate a average of the group
count++;
}
}
if(count > 0)// if we found enemys interfearing with each other
{
sum /= count;//average of the movement
sum = sum.normalized * avoidenceSpeed;//calculate movement speed
Debug.Log("sum = " + sum);
transform.Translate((Vector3)sum * avoidenceSpeed * Time.deltaTime, Space.World);
}
}
void chaseMe()
{
if(Vector3.Distance(target.position, transform.position) <= chaseRadius && Vector3.Distance(target.position, transform.position) > dashRadius)
{
transform.position = Vector3.MoveTowards(transform.position, target.position, movementSpeed * Time.deltaTime);
}
else if(Vector3.Distance(target.position, transform.position) <= dashRadius)
{
transform.position = Vector3.MoveTowards(transform.position, target.position, dashSpeed * Time.deltaTime);
}
}
}
please help me stop my enemys from clumping up without the use of AI
thanks again for any assistance and your time its much appreciated

Moving the player on the y-axis

I am working on a Pong-Clone for Android right now and have watched a tutorial about it. It works perfectly with the keyboard, but how do I get a similar control on the mobile phone. My current code looks like this:
private void Start()
{
ball = GameObject.FindGameObjectWithTag("Ball").GetComponent<Ball>();
col = GetComponent<BoxCollider2D>();
if (side == Side.Left)
forwardDirection = Vector2.right;
else if (side == Side.Right)
forwardDirection = Vector2.left;
}
private void Update()
{
if (!overridePosition)
MovePaddle();
}
private void MovePaddle()
{
float targetYPosition = GetNewYPosition();
ClampPosition(ref targetYPosition);
transform.position = new Vector3(transform.position.x, targetYPosition, transform.position.z);
}
private void ClampPosition(ref float yPosition)
{
float minY = Camera.main.ScreenToWorldPoint(new Vector3(0, 0)).y;
float maxY = Camera.main.ScreenToWorldPoint(new Vector3(0, Screen.height)).y;
yPosition = Mathf.Clamp(yPosition, minY, maxY);
}
private float GetNewYPosition()
{
float result = transform.position.y;
if (isAI)
{
if (BallIncoming())
{
if (firstIncoming)
{
firstIncoming = false;
randomYOffset = GetRandomOffset();
}
result = Mathf.MoveTowards(transform.position.y, ball.transform.position.y + randomYOffset, moveSpeed * Time.deltaTime);
}
else
{
firstIncoming = true;
}
}
else
{
float movement = Input.GetAxisRaw("Vertical " + side.ToString()) * moveSpeed * Time.deltaTime;
result = transform.position.y + movement;
}
return result;
}
I want the player to move on the y Axis, but I have no idea how I can do it with touch controls (I am new to games). I would really appreciate any help, so I can continue programming :)
I'm going to assume what you want isove the padel by touch and drag up or down.
I would keep your code for debugging and add a switch to only use touch if touch is supported and your code otherwise.
// Adjust via the Inspector how much the user has to drag in order to move the padel
[SerializeField] private float touchDragSensitivity = 1;
private float lastTouchPosY;
// Make sure to store the camera
// Using Camera.main is quite expensive!
[SerializeField] private Camera _mainCamera;
private void Awake()
{
if(!_mainCamera) _mainCamera = GetComponent<Camera>();
...
}
private float GetNewPosition()
{
...
else
{
var movement = 0f;
if(!Input.touchSupported)
{
// For non-touch device keep your current code
movement = Input.GetAxisRaw("Vertical " + side.ToString()) * moveSpeed * Time.deltaTime;
}
else
{
if(Input.touchCount > 0)
{
var touch = Input.GetTouch(0);
var touchPositionY = _mainCamera.ScreenToWorldPoint(touch.position).y;
if(touch.phase = TouchPhase.Moved)
{
var delta = touchPositionY - lastTouchPosY;
movement = delta * touchDragSensitivity;
}
lastTouchPosY = touchPositionY;
}
}
result = transform.position.y + movement;
}
}
Or alternatively if you rather wanted a similar experience as for the button input you could also only check whether the touch is on the bottom or top half of the screen and move the padel constantly like with your original code before:
private void Update()
{
...
else
{
var movement = 0f;
if(!Input.touchSupported)
{
// For non-touch device keep your current code
movement = Input.GetAxisRaw("Vertical " + side.ToString()) * moveSpeed * Time.deltaTime;
}
else
{
if(Input.touchCount > 0)
{
var touch = Input.GetTouch(0);
var touchPositionY = touch.position.y;
movement = (touchPositionY >= Screen.height / 2f ? 1 : -1) * moveSpeed * Time.deltaTime;
}
}
result = transform.position.y + movement;
}
}
Update
As to your question
is there some way to split the screen so two players can play on the same screen
Yes: You can add a check on which side a touch is using touch.position.x >= screen.width / 2f => right side.
You could e.g. filter for valid touches like
if(Input.touchCount > 0)
{
if(side == Side.Right)
{
var validTouches = Input.touches.Where(t => t.position >= Screen.width / 2f).ToArray();
if(validTouches.Length > 0)
{
var touchPositionY = validTouches[0].position.y;
movement = (touchPositionY >= Screen.height / 2f ? 1 : -1) * moveSpeed * Time.deltaTime;
}
}
else
{
var validTouches = Input.touches.Where(t => t.position < Screen.width / 2f).ToArray();
if(validTouches.Length > 0)
{
var touchPositionY = validTouches[0].position.y;
movement = (touchPositionY >= Screen.height / 2f ? 1 : -1) * moveSpeed * Time.deltaTime;
}
}
}
If you want to press and hold make the object move you can make two buttons one for +y and the other for -y. then use Input.GetTouch to register the players input.

How to change the path of a shot? (2D)

I want my shots to follow a specific pattern (I also need the arc and gap between the shots to be adjustable). Right now I've got my shooting script down but the shots go in a straight line which is not what I want (don't want a straight line now but I'll need it later when designing other weapons).
Here's a screenshot with example of said saidpatters:
I don't know much about quaternions and angles so all I tried is modifying the angles after x time and the velocity after x time but none worked (it might be the solution but I have 0 clue how to use angles in unity so I couldn't get it to work).
Another thing please provide an explanation along with your answer because I want to learn why something works the way it does so I don't have to ask again later.
Here's my code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
public class Player_Shooting : MonoBehaviour
{
[SerializeField]
private Transform shootingPoint;
[SerializeField]
private GameObject shot; //this is what I'm shooting, shot also has a script but all it does is apply velocity upwards and do damage to enemy if it hits
private bool shootAgain = true;
private int dexterity = Player_Stats.GetDexterity();
private int numberofshots = 2; //amount of shots
private int shotGap = 5; //how many degrees between the shots
void Update()
{
Vector3 mousepos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
Vector2 direction = new Vector2(mousepos.x - transform.position.x, mousepos.y - transform.position.y);
transform.up = direction;
if (Input.GetButton("Fire1") && shootAgain == true)
{
shootAgain = false;
StartCoroutine(RateOfFire(dexterity));
}
}
private void Shoot()
{
Vector3 temp = transform.rotation.eulerAngles;
Quaternion angle = Quaternion.Euler(temp.x, temp.y, temp.z);
for (int i = 0; i < numberofshots; i++)
{
int multiplier = i + 1;
if (numberofshots % 2 == 1)
{
Instantiate(shot, shootingPoint.position, angle);
if (i % 2 == 0)
{
temp.z -= shotGap * multiplier;
angle = Quaternion.Euler(temp.x, temp.y, temp.z);
}
else
{
temp.z += shotGap * multiplier;
angle = Quaternion.Euler(temp.x, temp.y, temp.z);
}
}
else if (numberofshots % 2 == 0)
{
if (i % 2 == 0)
{
temp.z -= shotGap * multiplier;
angle = Quaternion.Euler(temp.x, temp.y, temp.z);
}
else
{
temp.z += shotGap * multiplier;
angle = Quaternion.Euler(temp.x, temp.y, temp.z);
}
Instantiate(shot, shootingPoint.position, angle);
}
}
}
IEnumerator RateOfFire(int dex)
{
Shoot();
float time = dex / 75;
time *= 6.5f;
time += 1.5f;
yield return new WaitForSeconds(1 / time);
shootAgain = true;
}
}
This is what i came up with after a few hours.
it can be improved upon for your needs but it works and with less code.
i used a separate script on another gameObject to Instantiate the projectiles. The bullet script is attached to a sprite with a trail
it should be easy to manipulate the firing sequence from there.
comments explain what most things do.
i added a bool function to fire in opposing angles.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class bullet : MonoBehaviour{
public float turnLength = 0.5f; // how long it turns for 0.0+
public float turnSpeed = 5f; // how fast the projectile turns 0.0+
public float anglePauseTime = 0.2f; // Optional wave form variable. coupled with high turnrate + curve speed = higher frequency sine wave.
public float shotAngle = -12f; // the angle the shot is taken as an offset (usually nagative value) 0- or turnspeed*2.25 for straight shots
public float projectileSpeed = 50; // obvious
public bool opositeAngles = false;
// Start is called before the first frame update
void Start(){
if(opositeAngles){
transform.Rotate(0, 0, -shotAngle);
}
else{
transform.Rotate(0, 0, shotAngle);
}
StartCoroutine(WaveForm(turnLength, turnSpeed, anglePauseTime, opositeAngles));
}
// Update is called once per frame
void Update(){
transform.position += transform.right * Time.deltaTime * projectileSpeed;
}
IEnumerator WaveForm(float seconds, float aglSpeed, float pause, bool reverse){
// multiplier correlates to waitForSeconds(seconds)
// faster update time = smoother curves for fast projectiles
// less cycles = shorter Corutine time.
//10, 0.1 100cycles/second (shallow waves, jagged on higher frequency waves, doesnt last long)
//10, 0.05 200cycles/second (probably best)
//100, 0.02 500cycles/second (smooth curves all around. requires smaller adjustment numbers)
// i had to up it for the waveform to last longer.
float newSeconds = seconds * 10;
for (int i = 0; i < newSeconds; i++) {
for (int j = 0; j < newSeconds; j++) {
yield return new WaitForSeconds(0.05f); // controls update time in fractions of a second.
if(reverse){
transform.Rotate(0, 0, -aglSpeed, Space.Self);
}
else {
transform.Rotate(0, 0, aglSpeed, Space.Self);
}
}
yield return new WaitForSeconds(pause);
aglSpeed = -aglSpeed;
}
}
}
Example image

Combine camera pan to with touch controls in Unity

I've been asked to look into creating a simple iterative application with Unity. This application has 2 major functions regarding the camera.
LERP-ing the camera to focus on a target object.
Once moved relinquish control to the user and allow the user to rotate and zoom around the object.
I'm new to this but I've managed to create two scripts that achieve these goals in isolation. Now I'm struggling to fit them together.
I'll start with the relevant code for user interaction.
First, I use TouchKit to set the delta values on each frame this is in Start.
// set the delta on each frame for horizontal and vertical rotation
var oneTouch = new TKPanRecognizer();
oneTouch.gestureRecognizedEvent += (r) =>
{
HorizontalDelta += r.deltaTranslation.x * rotateSpeed * Time.deltaTime;
VerticalDelta -= r.deltaTranslation.y * rotateSpeed * Time.deltaTime;
};
// do the same for pinch
var pinch = new TKPinchRecognizer();
pinch.gestureRecognizedEvent += (r) =>
{
rotateDistance -= r.deltaScale * 200.0f * Time.deltaTime;
};
TouchKit.addGestureRecognizer(oneTouch);
TouchKit.addGestureRecognizer(pinch);
And on Update:
VerticalDelta = Mathf.Clamp(VerticalDelta, verticalPivotMin, verticalPivotMax);
var direction = GetDirection(HorizontalDelta, VerticalDelta);
var currentTarget = targetsSwitched ? target2 : target;
transform.position = currentTarget.position - direction * rotateDistance;
transform.LookAt(currentTarget.position);
// ...
private Vector3 GetDirection(float x, float y)
{
Quaternion q = Quaternion.Euler(y, x, 0);
return q * Vector3.forward;
}
This works beautifully and does exactly what I want. The problem comes when I try to integrate this code with my camera moving script. This shows where I want to add the Update code
void Update ()
{
if (currentlyMoving)
{
FocusTarget(currentTarget);
}
else
{
// accept user input if not moving
if (Input.GetKeyDown(KeyCode.Space))
{
SetMoveToTarget(mainTargetObject);
}
if (Input.GetKeyDown(KeyCode.Q))
{
SetMoveToTarget(subTargetObject1);
}
if (Input.GetKeyDown(KeyCode.E))
{
SetMoveToTarget(subTargetObject2);
}
}
}
These are the functions that actually move the camera:
void SetMoveToTarget(GameObject target)
{
if (currentlyMoving == false)
{
currentlyMoving = true;
fromRotation = currentTarget.transform.rotation;
currentTarget = target;
toRotation = currentTarget.transform.rotation;
timeStartedLerping = Time.time;
}
}
void FocusTarget(GameObject target)
{
float timeSinceStarted = Time.time - timeStartedLerping;
float percentageComplete = timeSinceStarted / (lerpSpeed);
transform.position = Vector3.MoveTowards(transform.position, target.transform.position, moveSpeed * Time.deltaTime);
transform.rotation = Quaternion.Lerp(fromRotation, toRotation, Mathf.Pow(percentageComplete, (float)1.2));
if (Vector3.Distance(transform.position, target.transform.position) < 0.1 && percentageComplete > 0.99)
{
transform.position = target.transform.position;
transform.rotation = target.transform.rotation;
currentlyMoving = false;
}
}
I think what I need to do (and I may be wrong on this) is set rotateDistance to be the difference between the currentTarget in the second script and currentTarget in the first script.
Thank you in advance, it's quite a tricky one for me.
In the end I had to change how I was dealing with moving the camera in the first place. The problem was that moving to a set game object worked and was easy to set-up but it's much easier to integrate with the look script if you actually calculate when the next position of the camera should be and move to that.
I'm going to paste the working product here for posterity, it's missing some stuff from the end game but the camera is working.
using UnityEngine;
using UnityEngine.UI;
public class NewCamera : MonoBehaviour {
// targets
public GameObject target;
public GameObject target2;
// settings
public float RotateSpeed = 50.0f;
public float RotateDistance = 3;
public float CameraMoveSpeed = 20f;
public float VerticalPivotMin = 5;
public float VerticalPivotMax = 65;
public float MinZoomIn = 1.7f;
public float MaxZoomIn = 4f;
// private
private GameObject lookTarget;
private bool currentlyMoving = false;
private Vector3 targetVector;
private float timeStartedLerping;
private float HorizontalDelta;
private float VerticalDelta;
void Start ()
{
lookTarget = target;
var oneTouch = new TKPanRecognizer();
oneTouch.gestureRecognizedEvent += (r) =>
{
if (currentlyMoving == false)
{
HorizontalDelta += r.deltaTranslation.x * RotateSpeed * Time.deltaTime;
VerticalDelta -= r.deltaTranslation.y * RotateSpeed * Time.deltaTime;
VerticalDelta = Mathf.Clamp(VerticalDelta, VerticalPivotMin, VerticalPivotMax);
}
};
var pinch = new TKPinchRecognizer();
pinch.gestureRecognizedEvent += (r) =>
{
if (currentlyMoving == false)
{
RotateDistance -= r.deltaScale * 200.0f * Time.deltaTime;
RotateDistance = Mathf.Clamp(RotateDistance, MinZoomIn, MaxZoomIn);
}
};
TouchKit.addGestureRecognizer(oneTouch);
TouchKit.addGestureRecognizer(pinch);
}
void Update ()
{
if (currentlyMoving)
{
FocusTarget();
}
else
{
var direction = GetDirection(HorizontalDelta, VerticalDelta);
transform.position = lookTarget.transform.position - direction * RotateDistance;
transform.LookAt(lookTarget.transform.position);
}
}
public void OnButtonClick()
{
var currentTarget = target.GetInstanceID() == lookTarget.GetInstanceID() ? target : target2;
var nextTarget = currentTarget.GetInstanceID() == target.GetInstanceID() ? target2 : target;
var cameraPosition = transform.position;
var targetPosition = currentTarget.transform.position;
SetMoveToTarget(nextTarget, cameraPosition - targetPosition);
}
void SetMoveToTarget(GameObject moveTo, Vector3 offset)
{
currentlyMoving = true;
targetVector = moveTo.transform.position + offset;
lookTarget = moveTo;
}
void FocusTarget()
{
transform.position = Vector3.Lerp(transform.position, targetVector, CameraMoveSpeed * Time.deltaTime);
if (Vector3.Distance(transform.position, targetVector) < 0.01)
{
transform.position = targetVector;
currentlyMoving = false;
}
}
private Vector3 GetDirection(float x, float y)
{
Quaternion q = Quaternion.Euler(y, x, 0);
return q * Vector3.forward;
}
}
If you want to use it for any reason, just add this script to a camera and set the two targets. I have to make a more robust version where you can add new targets easily but this will do for now.
Happy hacking!

How do I jump a fix distance in Unity 3d?

I have a Controller which moves my object diagonally. The left arrow should move the player forward and to the left 45 degrees, and the right arrow the same to the right. I would like to move the player relatively to its current position. Right now it moves relatively to the point(0,0,0).
My code:
public class JollyJumper : MonoBehaviour {
protected CharacterController control;
public float fTime = 1.5f; // Hop time
public float fRange = 5.0f; // Max dist from origin
public float fHopHeight = 2.0f; // Height of hop
private Vector3 v3Dest;
private Vector3 v3Last;
private float fTimer = 0.0f;
private bool moving = false;
private int number= 0;
private Vector2 direction;
public virtual void Start () {
control = GetComponent<CharacterController>();
if(!control){
Debug.LogError("No Character Controller");
enabled=false;
}
}
void Update () {
if (fTimer >= fTime&& moving) {
var playerObject = GameObject.Find("Player");
v3Last = playerObject.transform.position;
Debug.Log(v3Last);
v3Dest = direction *fRange;
//v3Dest = newVector* fRange + v3Last;
v3Dest.z = v3Dest.y;
v3Dest.y = 0.0f;
fTimer = 0.0f;
moving = false;
}
if(Input.GetKeyDown(KeyCode.LeftArrow)){
moving = true;
direction = new Vector2(1.0f, 1.0f);
number++;
}
if(Input.GetKeyDown(KeyCode.RightArrow)){
moving = true;
direction = new Vector2(-1.0f, 1.0f);
number++;
}
if(moving){
Vector3 v3T = Vector3.Lerp (v3Last, v3Dest, fTimer / fTime);
v3T.y = Mathf.Sin (fTimer/fTime * Mathf.PI) * fHopHeight;
control.transform.position = v3T;
fTimer += Time.deltaTime;
}
}
}
How can resolve this? Any ideas? Thanks a lot!
The short answer is: you hard-coded two locations you want to jump to: points (1, 1) and (-1, 1). You should create new Vector each time you start jumping. Replace each
direction = new Vector2(1.0f, 1.0f);
with this line:
v3Dest = transform.position + new Vector3(1.0f, 0, 1) * fRange;
and it should work.
While I'm on it, there are some other things I want to point:
There is a lot of floating point error after each jump. Notice that in your code v3T will never be equal to v3Dest (you never actually reach your destination), because you switch the moving flag earlier. You should explicitly set your position to v3Dest when the jump is over.
You are checking jump timers etc. every frame. A more elegent solution is to start a coroutine.
You use a sinusoid as your jump curve, which looks ok, but using a parabola would be conceptually more correct.
Right now it is possible to start next jump mid-air (I'm not sure whether it is intended or not)
Here is some code you may use that avoids those problems:
using System.Collections;
using UnityEngine;
public class Jumper : MonoBehaviour
{
#region Set in editor;
public float jumpDuration = 0.5f;
public float jumpDistance = 3;
#endregion Set in editor;
private bool jumping = false;
private float jumpStartVelocityY;
private void Start()
{
// For a given distance and jump duration
// there is only one possible movement curve.
// We are executing Y axis movement separately,
// so we need to know a starting velocity.
jumpStartVelocityY = -jumpDuration * Physics.gravity.y / 2;
}
private void Update()
{
if (jumping)
{
return;
}
else if (Input.GetKeyDown(KeyCode.LeftArrow))
{
// Warning: this will actually move jumpDistance forward
// and jumpDistance to the side.
// If you want to move jumpDistance diagonally, use:
// Vector3 forwardAndLeft = (transform.forward - transform.right).normalized * jumpDistance;
Vector3 forwardAndLeft = (transform.forward - transform.right) * jumpDistance;
StartCoroutine(Jump(forwardAndLeft));
}
else if (Input.GetKeyDown(KeyCode.RightArrow))
{
Vector3 forwardAndRight = (transform.forward + transform.right) * jumpDistance;
StartCoroutine(Jump(forwardAndRight));
}
}
private IEnumerator Jump(Vector3 direction)
{
jumping = true;
Vector3 startPoint = transform.position;
Vector3 targetPoint = startPoint + direction;
float time = 0;
float jumpProgress = 0;
float velocityY = jumpStartVelocityY;
float height = startPoint.y;
while (jumping)
{
jumpProgress = time / jumpDuration;
if (jumpProgress > 1)
{
jumping = false;
jumpProgress = 1;
}
Vector3 currentPos = Vector3.Lerp(startPoint, targetPoint, jumpProgress);
currentPos.y = height;
transform.position = currentPos;
//Wait until next frame.
yield return null;
height += velocityY * Time.deltaTime;
velocityY += Time.deltaTime * Physics.gravity.y;
time += Time.deltaTime;
}
transform.position = targetPoint;
yield break;
}
}

Categories