Paddle reflect ball depending on contact point - c#

When hitting paddle ball is bouncing weird.
private Vector3 direction;
private void Start()
{
direction = transform.up;
}
private void FixedUpdate()
{
transform.Translate(direction * (Time.deltaTime * BallManager.Instance.initialBallSpeed));
}
private void OnCollisionEnter(Collision col)
{
if (col.gameObject.CompareTag("Paddle"))
{
var cp = col.contacts[0];
direction = Vector2.Reflect(direction, PaddleScript.Instance.transform.position.normalized);
}
}
I want to make to bounce with always same force up and to side depending on hit point.

First of all when using Physics you shouldn't go through Transform at all.
You wouldn't even need this component at all.. why not simply
eliminate all gravity (Gravity Scale -> 0)
eliminate all drag (Linear Drag -> 0)
put a proper PhysicsMaterial2D on the ball and/or paddles colliders and set bounciness to 1 and friction to 0
=> It should already behave as desired by default.
Otherwise I would change your code to
[SerializeField] private Rigidbody2D rigidbody;
private Vector3 direction;
private void Start()
{
if(!rigidbody) rigidbody= GetComponent<Rigidbody2D>();
direction = transform.up;
}
private void FixedUpdate()
{
rigidbody.MovePosition(rigidbody.position + direction * Time.deltaTime * BallManager.Instance.initialBallSpeed);
}
private void OnCollisionEnter(Collision col)
{
if (col.gameObject.CompareTag("Paddle"))
{
var cp = col.contacts[0];
// and then you want to reflect on the normal of the contact..
// a normalized position doesn't make any sense in this context
direction = Vector2.Reflect(direction, cp.normal);
}
or if you wanted to add the variance depending on where the ball hits the paddle I would put an object at each tip of the paddle and then you can inverse lerp
public static class Vector2Extensions
{
public static float InverseLerp(thjs Vector2 value, Vector2 min, Vector2 max)
{
var direction = max - min;
var AV = value - min;
return Mathf.Clamp01(Vector2.Dot(AV, direction) / Vector2.Dot(direction, direction));
}
}
to check whether your hit contact is closer to left (0) or right (1) tip or exactly in the center (0.5) and according to this factor add some min/max angle offset to the vector. e.g. using plain reflect as before when exactly in the center and then add some rotation according to the offset factor from above e.g. using
direction = Vector2.Reflect(direction, cp.normal);
// this will return a dynamic factor of whether the contact is closer to left (0), right (1) or in the middle (0.5)
var inverseLerp = cp.point.InverseLerp(leftTip, rightTip);
// this will map the inverseLerp factor which goes between `0` and `1`
// to the added angle offset which goes from -30° to 30°
var angle = Mathf.Lerp(-30, 30, inverseLerp);
direction = (Vector2)(Quaternion.Euler(0, 0, angle) * direction);
If you want you could even remove the Reflect entirely and then not add a rotation to the direction but rather hard replace it with a new direction based on the same lerp as before

Related

Drone for target pursuit in unity

I've already done the player drone control, now I want to make a bot. The problem is that I don't understand a little how to determine the slope, turn and climb/decrease in height.
I would not like to change the current control method, since it works fine. Also, the bot always knows about the player's position.
public class DroneFootballAI : MonoBehaviour
{
public float minMaxPitch;
public float minMaxRoll;
public float yawPower;
public Vector2 cyclic;
public float pedals;
public float throttle;
public float lerpSpeed;
public Rigidbody rb;
public Transform playerTransform;
public CheckNode checkNode;
public FootballController footballController;
private float _finalPitch;
private float _finalRoll;
private float _yaw;
private float _finalYaw;
private float _isMove;
private List<DroneEngine> _engines;
private void Awake()
{
rb = GetComponent<Rigidbody>();
_engines = GetComponentsInChildren<DroneEngine>().ToList();
checkNode = GetComponent<CheckNode>();
}
private void FixedUpdate()
{
if (footballController.isGameStart)
{
GetInput();
DroneMove();
}
}
private void GetInput()
{
_isMove = 0;
cyclic.x = Input.GetAxis("Horizontal");
cyclic.y = Input.GetAxis("Vertical");
pedals = Input.GetAxis("Pedal");
throttle = Input.GetAxis("Throttle");
_isMove = cyclic.x + cyclic.y + pedals + throttle;
}
private void DroneMove()
{
foreach (var engine in _engines)
{
engine.UpdateEngine(rb, throttle);
}
float pitch = cyclic.y * minMaxPitch;
float roll = -cyclic.x * minMaxRoll;
_yaw += pedals * yawPower;
_finalPitch = Mathf.Lerp(_finalPitch, pitch, Time.deltaTime * lerpSpeed);
_finalRoll = Mathf.Lerp(_finalRoll, roll, Time.deltaTime * lerpSpeed);
_finalYaw = Mathf.Lerp(_finalYaw, _yaw, Time.deltaTime * lerpSpeed);
Quaternion rot = Quaternion.Euler(_finalPitch, _finalYaw, _finalRoll);
rb.MoveRotation(rot);
}
}
To determine the slope, turn, and climb/descend for the bot, you can use the position of the player as a reference. For example, you can calculate the slope by finding the difference in height between the bot and the player, and use that to adjust the pitch (tilt up or down) of the bot. You can also calculate the difference in horizontal position between the bot and the player, and use that to adjust the roll (tilt left or right) of the bot.
To turn towards the player, you can calculate the angle between the bot's current direction and the direction towards the player, and use that to adjust the yaw (rotation around the vertical axis) of the bot.
To climb or descend towards the player, you can calculate the difference in height between the bot and the player and adjust the throttle (engine power) accordingly. For example, if the player is higher than the bot, you can increase the throttle to make the bot climb, and if the player is lower than the bot, you can decrease the throttle to make the bot descend.
You can use a combination of these techniques to create a simple AI for the bot that follows the player and tries to stay at a certain distance from the player. You can also use more advanced techniques, such as pathfinding or behavior trees, to create more sophisticated AI behaviors.
Below is an example to apply the above suggestions:
private void DroneMove()
{
// Calculate the vector pointing from the bot's position to the player's position
Vector3 directionToPlayer = playerTransform.position - transform.position;
// Calculate the slope of this vector by taking the difference in y-coordinates and dividing it by the difference in x-coordinates
float slope = (playerTransform.position.y - transform.position.y) / (playerTransform.position.x - transform.position.x);
// Calculate the pitch and roll values based on the slope
float pitch = slope * minMaxPitch;
float roll = -slope * minMaxRoll;
// Update the yaw value as before
_yaw += pedals * yawPower;
// Lerp the final pitch, roll, and yaw values as before
_finalPitch = Mathf.Lerp(_finalPitch, pitch, Time.deltaTime * lerpSpeed);
_finalRoll = Mathf.Lerp(_finalRoll, roll, Time.deltaTime * lerpSpeed);
_finalYaw = Mathf.Lerp(_finalYaw, _yaw, Time.deltaTime * lerpSpeed);
// Calculate the quaternion based on the final pitch, roll, and yaw values
Quaternion rot = Quaternion.Euler(_finalPitch, _finalYaw, _finalRoll);
// Set the bot's rotation to the calculated quaternion
rb.MoveRotation(rot);
// Calculate the throttle value based on the distance to the player
throttle = Mathf.Clamp(1 - (directionToPlayer.magnitude / checkNode.viewDistance), 0, 1);
// Update the engines with the calculated throttle value
foreach (var engine in _engines)
{
engine.UpdateEngine(rb, throttle);
}
}
Note that i didn't test this code since this is just for guidance.

Camera approaching and distancing loop in Unity3D

i can't understand how can i make it so that my camera will approach an object and after reaching a certain distance from it,it will go back to the initial position, and so on.
Here is my code:
public class Camera : MonoBehaviour
{
public GameObject cameraLook;
float speed = 10f;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
Vector3 distance = (transform.position - cameraLook.transform.position);
Debug.Log(distance);
transform.LookAt(cameraLook.transform.position);
if (distance.x > 30)
{
go();
}
if (distance.x < 0)
{
goBack();
}
}
void go()
{
transform.position += transform.forward * speed * Time.deltaTime;
}
void goBack()
{
transform.position-= transform.forward * speed * Time.deltaTime;
}
}
Update is called once every frame.
What currently happens in your code is
Frame 1
You reach a distance of x > 30
you call go once
Frame 2
Now your distance might be < 30 but still > 0 due to the movement last frame
You do nothing at all anymore
or the same with
Frame 1
You reach a distance of < 0
you call go once
Frame 2
Now your distance might be > 0 but still < 30 due to the movement last frame
You do nothing at all anymore
Then also it is very "dangerous" / unreliable to simply check the global X axis difference between your objects. This assumes that your camera is definitely moving along the X axis.
Instead I would rather use something like e.g.
public class Camera : MonoBehaviour
{
public GameObject cameraLook;
[Toolbox("The minimum distance the camera should stay away from the object when moving closer")]
[Min(0f)] public float minDistance = 0f;
[Toolbox("The maximum distance the camera should move away from the object")]
[Min(0f)] public float maxDistance = 30f;
[Toolbox("How fast shall the camera travel. If your target objects moves have in mind that this needs to be bigger then the movement speed of your target object ;)")]
[Min(0f)] public float moveUnitsPerSecond = 10f;
[Toolbox("If true starts moving towards otherwise starts moving away")]
public bool startMovingTowards = true;
private bool isMovingTowards;
private void Start()
{
isMovingTowards = startMovingTowards;
}
void Update()
{
// first look at the target object
transform.LookAt(cameraLook.transform.position);
// get the desired final distance based on the move direction flag
var targetDistance = isMovingTowards ? minDistance : maxDistance;
// get the desired final position based on the desired distance
// simply moving the distance backwards away from the object
var targetPosition = cameraLook.transform.position - transform.forward * targetDistance;
// Move smoothly with the given moveUnitsPerSecond to the desired final position
transform.position = Vector3.MoveTowards(transform.position, targetPosition, moveUnitsPerSecond * Time.deltaTime);
// is the desired final position reached (within a precision of 0.00001)
if(transform.position == targetPosition)
{
// then simply invert the move direction
isMovingTowards = !isMovingTowards;
}
}
}

How to realistically reflect a 3d sphere in Unity with C#

I've been trying to realistically reflect a 3d sphere on the walls of a box for a while now in Unity. For some reason, the reflection is generally correct, but when the ball hits a wall in certain directions, the reflection is incorrect.
To illustrate what happens to the ball upon hitting a wall: T = top wall, R = right wall, L = left wall, and B = bottom wall. Let r = the ball comes/goes to the right, l = for the left, and s = the ball stops/slows down significantly. The instructions below take this format: Xyz, where X = the wall the ball is about to hit, y = the ball's initial direction, z = the reflection. The game has a top-down perspective, and the instructions are based on the wall's perspective. I'm also new to C#, so the code is potentially eye burning.
Instructions: Tll, Trl; Bll, Brl; Rls or after hitting another wall Rlr, Rrl; Lls or after hitting another wall Llr, Lrl
Generally, when the ball stops, it jumps in the air. I wonder if this is because the angle reflects along the wrong axis, but why would this only sometimes happen? Also, when only one key is held, the ball bounces back and forth until it leaves the arena. I know about discrete and continuous hit detection, and the setting is on discrete, but the walls generally contain the ball well enough, with this case being the exception.
What I tried:
Figuring out how to use Vector3.Reflect. I do not understand what variables this function should contain and how to apply this to my code. I did look at the Unity Documentation page for help, but it did not answer my question.
Changing the negative signs, as the angle has to be a reflection on the y-axis, and this does change how the reflections work, but does not solve the problem. The current way the negatives are ordered are the most ideal I found.
Giving the ball a physics material for bounciness.
Adding a small number to the denominator in the arctan equation to help prevent a division by zero. This did not help at all.
Creating different equations (basically changing the negatives) for varying combinations of positive and negative accelerations. Since there is a certain positive or negative acceleration associated with each button press (see Movement script), and there seems to be an issue with said signs, I wondered if associating each acceleration with its own set of equations could solve the problem. It did not work.
Checked if the walls were at different angles.
Deleting the variables xA and yA, or placing them in different spots.
Experimented with finding the speed of the object, but had no idea how to implement it.
The code for the Movement script for the player named Controller:
public class Movement : MonoBehaviour
{
public static float xAcceleration = 0.0f;
public static float yAcceleration = 0.0f;
// Update is called once per frame
void Update()
{
if (Input.GetKey(KeyCode.W)) //If the key W is pressed:
{
Vector3 position = this.transform.position; //Variable position is set to transform the players placement in the game.
if (yAcceleration >= -5 && yAcceleration <= 5) //If the y vector of the acceleration is >= -5 and <= 5:
{
yAcceleration = yAcceleration + 0.01f; //The y vector of the acceleration increases by 0.01 as long as the key W is pressed.
}
position.z = position.z + (0.1f * yAcceleration); //The position of the object on the z-axis (pretend it is the y-axis in the game world) is transformed by its original position plus its speed times its yAcceleration.
this.transform.position = position; //The gameObject is now transformed to a position equal to the variable position by the z-axis.
}
else //If the key W is let go of:
{
Vector3 position = this.transform.position;
position.z = position.z + (0.1f * yAcceleration);
this.transform.position = position; //The position of the gameObject continues to update, but its acceleration does not change. Basically, it continues to move forward.
}
//The rest of the code is very similar to the above, but I included it just in case there was something wrong.
if (Input.GetKey(KeyCode.S))
{
Vector3 position = this.transform.position;
if (yAcceleration >= -5 && yAcceleration <= 5)
{
yAcceleration = (yAcceleration) - 0.01f;
}
position.z = position.z + (0.1f * yAcceleration);
this.transform.position = position;
}
else
{
Vector3 position = this.transform.position;
position.z = position.z + (0.1f * yAcceleration);
this.transform.position = position;
}
if (Input.GetKey(KeyCode.A))
{
Vector3 position = this.transform.position;
if (xAcceleration >= -5 && xAcceleration <= 5)
{
xAcceleration = (xAcceleration) - 0.01f;
}
position.x = position.x + (0.1f * xAcceleration);
this.transform.position = position;
}
else
{
Vector3 position = this.transform.position;
position.x = position.x + (0.1f * xAcceleration);
this.transform.position = position;
}
if (Input.GetKey(KeyCode.D))
{
Vector3 position = this.transform.position;
if (xAcceleration >= -5 && xAcceleration <= 5)
{
xAcceleration = (xAcceleration) + 0.01f;
}
position.x = position.x + (0.1f * xAcceleration);
this.transform.position = position;
}
else
{
Vector3 position = this.transform.position;
position.x = position.x + (0.1f * xAcceleration);
this.transform.position = position;
}
}
}
This is the code for the collider and reflection:
public class Collider : MonoBehaviour
{
public float xA;
public float yA;
void OnCollisionEnter(Collision collision) //If a gameObject enters the collision of another object, this immediately happens once.
{
if (gameObject.tag == "Boundary") //If the gameObject has a tag named Boundary:
{
yA = -Movement.yAcceleration; //yA stores the value of yAcceleration after being called from script Movement as a negative. Its a reflection.
Movement.xAcceleration = (Movement.xAcceleration * -Mathf.Atan(yA / Movement.xAcceleration)); //xAcceleration is changed based on this equation: A * artan(A_y / A_x). The 0.000001 was here, adding to A_x to help prevent a 0 as the denominator.
xA = Movement.xAcceleration; //This is declared now...
Movement.yAcceleration = (Movement.yAcceleration * Mathf.Atan(Movement.yAcceleration / xA)); //This uses xA because Movement.xAcceleration is changed, and the yAcceleration calculation is based on the xAcceleration prior the collision.
}
}
void OnCollisionStay(Collision collision)
{
if (gameObject.tag == "Boundary")
{
yA = Movement.yAcceleration; //The same thing happens as before.
Movement.xAcceleration = (Movement.xAcceleration * -Mathf.Atan(yA / Movement.xAcceleration));
xA = Movement.xAcceleration;
Movement.yAcceleration = (Movement.yAcceleration * Mathf.Atan(Movement.yAcceleration / xA));
Movement.xAcceleration = -Movement.xAcceleration / 2; //On collision, the ball is reflected across the x-axis at half its speed.
Movement.yAcceleration = Movement.yAcceleration / 2; //yAcceleration is half its original value.
}
}
}
The picture below is the game setup. I apologize that it is a link; I do not have enough Reputation to merit a loaded image on this page. Also, if anything is unclear, please message me.
https://i.stack.imgur.com/VREV4.png
I would really appreciate the help. Thanks!
One very important note here: As soon as there is any Rigidbody involved you do not want to set any values through the .transform - This breaks the physics and collision detection!
Your Movement should rather alter the behavior of the Rigidbody e.g. by simply changing its Rigibody.velocity
I would then also place the collision check directly into the balls's component and check whether you hit a wall ("Boundary")
Then another note: Your code is currently frame-rate dependent. It means that if your target device runs with only 30 frames per second you will add 0.3 per second to the acceleration. If you run however on a more powerful device that manages to run with 200 frames per second then you add 2 per second.
You should rather define the de/increase per second and multiply it by Time.deltaTime
All together maybe something like this
public class Movement : MonoBehaviour
{
// Adjust these settings via the Inspector
[SerializeField] private float _maxMoveSpeed = 5f;
[SerializeField] private float _speedIncreasePerSecond = 1f;
// Already reference this via the Inspector
[SerializeField] private Rigidbody _rigidbody;
private void Awake()
{
if(!_rigidbody) _rigidbody = GetComponent<Rigidbody>();
}
// Get User Input in Update
private void Update()
{
var velocity = _rigidbody.velocity;
velocity.y = 0;
if (Input.GetKey(KeyCode.W) && velocity.z < _maxMoveSpeed)
{
velocity.z += _speedIncreasePerSecond * Time.deltaTime;
}
if (Input.GetKey(KeyCode.S) && velocity.z > -_maxMoveSpeed)
{
velocity.z -= _speedIncreasePerSecond * Time.deltaTime;
}
if (Input.GetKey(KeyCode.A) && velocity.x > -_maxMoveSpeed)
{
velocity.x -= _speedIncreasePerSecond * Time.deltaTime;
}
if (Input.GetKey(KeyCode.D) && velocity.x < _maxMoveSpeed)
{
velocity.x += _speedIncreasePerSecond * Time.deltaTime;
}
// clamp to the max speed in case you move diagonal
if(velocity.magnitude > _maxMoveSpeed)
{
velocity = velocity.normalized * _maxMoveSpeed;
}
_rigidbody.velocity = velocity;
}
}
And then finally simply add a PhysicsMaterial with desired settings to the walls and ball.
I used Friction = 0f and Bounciness = 0.7f for ball and walls. For slow movements you also might want/have to adjust the Bounce Threshold in the Project's Physics Settings otherwise there will be no bouncing if the velocity is smaller then 2 by default.
This depends a bit on your definition of "realistic". I disabled gravity so the ball also has no rotation and angular friction:

I need to make a sprite move horizontally for a predetermined distance, flip horizontally, and return

I'm trying to make a penguin sprite slide back and forth along platforms I have made. I have achieved this however I am really struggling to make the sprite flip horizontally once it moves back. I believe I may need to change my method of making the penguins move in order to incorporate the flip. I saw lots of threads about rotating around the Y axis but I don't know how to incorporate it. As it stands the penguins just goes back and forth always facing the positive x direction. The game is in 2D on Unity and is written in C#. Thanks for any advice. :)
using UnityEngine;
using System.Collections;
public class Enemy1 : MonoBehaviour {
private SpriteRenderer SpriteRenderer;
public float min = 2f;
public float max = 3f;
public int x = 0;
public bool facingRight = true;
// Use this for initialization
void Start()
{
min = transform.position.x;
max = transform.position.x + 27;
}
// Update is called once per frame
void Update()
{
transform.position = new Vector3(Mathf.PingPong(Time.time * 2, max - min) + min, transform.position.y, transform.position.z);
}
}
You can track the direction of your sprite and use localScale.x or flipX:
float _prevX = 0f;
...
void Update()
{
float newX = Mathf.PingPong (Time.time * 2, max - min);
transform.position = new Vector3(newX + min, transform.position.y, transform.position.z);
Vector3 scale = transform.localScale;
scale.x = newX < _prevX ? -1 : 1;
transform.localScale = scale;
_prevX = newX;
}
Another way: track when your sprite near "edge" points and "flip" only then, otherwise the first way is more general if you rotate your sprite earlier for some reasons.
Its been a while since I've messed with sprites but i think setting transform.scale.z = -1 will flip the sprite so it faces the other direction.

C# How to make a smooth jump in unity3d without moving the X, towards the nearest object

I would like to make a smooth jump towards the nearest cube. I already have a script to detect the closest cube. I want that the X-axis is locked, so only the Y-axis and the Z-axis change when jumping. I would like to use a Jump animation when jumping. I already tried to use Vector3MoveTowards, but that didn't really work well, maybe I didn't use it properly.
Detect nearest cube where the player should jump to (C#)
void Update()
{
FindClosestCube ();
GameObject closestCube = FindClosestCube ();
Debug.Log (closestCube);
}
GameObject FindClosestCube() {
GameObject[] gos;
gos = GameObject.FindGameObjectsWithTag("cube");
GameObject closest = null;
float distance = Mathf.Infinity;
float position = transform.position.z;
foreach (GameObject go in gos) {
float diff = go.transform.position.z - position;
float curDistance = diff;
if (curDistance < distance) {
closest = go;
distance = curDistance;
}
}
return closest;
}
The tricky part is that at some cubes you have to jump up (y+1), with some cubes you jump towards the same Y (y+0) and with some cubes you jump down (y-1).
How do I do this?
Image of how it looks like:
EDIT: I have this code right now:
----------------C#-----------------
Rigidbody rb;
public int clicks = 0;
Vector3 target;
public Animation jumpAnimation;
bool jump = false;
float cubeDiffY;
bool movePlayer;
public float smoothTime = 0.3f;
public float yVelocity = 0.0f;
void Start()
{
rb = GetComponent<Rigidbody> ();
}
void Update ()
{
FindClosestCube ();
GameObject closestCube = FindClosestCube ();
Debug.Log ("Closestcube = " + closestCube);
target = closestCube.transform.position + new Vector3 (0f, 0.7f, 0f);
cubeDiffY = target.y - transform.position.y;
movePlayer = true;
Debug.Log("Cube Difference Y-axis = " + Mathf.Round(cubeDiffY));
if (Input.GetMouseButtonDown (0))
{
clicks += 1;
jump = true;
jumpAnimation = gameObject.GetComponent<Animation>();
//jumpAnimation.Play ();
}
if (jump == true)
{
Jump ();
}
}
void Jump()
{
float newPosition = Mathf.SmoothDamp (transform.position.y, target.y, ref yVelocity, smoothTime);
transform.position = new Vector3 (0, newPosition, transform.position.z);
}
I calculated the difference in Y-axis between the cube where the player is standing on and the closestCube. But the Jump() doesn't work. How do I fix that?
Okay I set up a quick version of your game and got what you wanted to work, it is not exactly a quick solution, because what your doing doesn't have built in functionality for other than using animations.
Here is the character script that has all the code you need and commented thoroughly so it should explain itself.
using UnityEngine;
public class Character : MonoBehaviour
{
//the collider for the player
private new BoxCollider collider;
//the jump box collider on a empty game object that is a child to the player object
public BoxCollider JumpBox;
//the offset of the cube so it doesn't stop inside of it
public Vector3 cubeOffset;
//how high the jump will be
public float JumpHeight;
//how fast the jump will be
public float JumpSpeed;
//holds the change in position the jump will produce
private Vector3 jumpDelta;
//holds the destination cube the jump is attempting to hit
private Cube destinationCube;
//true if a jumping animation is currently playing
private bool jumping = false;
//used to swap the jump direction from up to down
private bool jumpDirection = true;
//used to hold the position of the jump so it knows when to stop
private float jumpPosition = 0;
// Use this for initialization
void Start()
{
collider = GetComponent<BoxCollider>();
}
// Update is called once per frame
void Update()
{
if(jumping)
{
//move straight towards the cube
transform.position = transform.position + (JumpSpeed * jumpDelta);
//move up and down to simulate a jump
//check the current move direction
if (jumpDirection)
{
//add to the jump position twice product of the JumpHeight the JumpSpeed so that it will
//rise and fall the same amount of time it takes to move to the destination
jumpPosition += JumpHeight * JumpSpeed * 2;
//if it has passed the jump height reverse the jump direction
if (jumpPosition >= JumpHeight)
jumpDirection = !jumpDirection;
transform.position += transform.up * JumpHeight * JumpSpeed * 2;
}
//the jump direction is going down
else
{
jumpPosition -= JumpHeight * JumpSpeed * 2;
transform.position -= transform.up * JumpHeight * JumpSpeed * 2;
}
//check if the character collider intersects witht he cubes collider
//if it has then stop jumping and set the final position as the destination position
if (collider.bounds.Intersects(destinationCube.BoxCollider.bounds))
{
jumping = false;
transform.position = destinationCube.transform.position + cubeOffset;
}
}
//detect a jump
if (Input.GetKeyDown(KeyCode.Space))
{
//detect all hits on the jump box
Collider[] hits = Physics.OverlapBox(JumpBox.center, JumpBox.size * 0.5f);
//get the closest collider with the right tag
Collider result = GetClosestColliderWithTag(hits, "Cube");
//if we have a result then begin the jumping animation
if(result != null)
{
//gets the destination cubes cube component(the custom class you have on your cubes)
destinationCube = result.gameObject.GetComponent<Cube>();
//calculate the jump delta
jumpDelta = (result.transform.position + cubeOffset) - transform.position;
//remove the left and right components so the jumping doesnt move to the left or right of the player
Vector3 component = Vector3.Project(jumpDelta, -transform.right);
jumpDelta -= component;
component = Vector3.Project(jumpDelta, transform.right);
jumpDelta -= component;
//setup the jump animation control fields to the initial values
jumpPosition = 0;
jumpDirection = true;
jumping = true;
}
}
}
private Collider GetClosestColliderWithTag(Collider[] colliders, string tag)
{
//just gets the closest collider
float distance = float.MaxValue;
int result = -1;
for (int i = 0; i < colliders.Length; i++)
{
if (colliders[i].tag == tag)
{
float distanceTemp = Vector3.Distance(transform.position, colliders[i].transform.position);
if (distanceTemp < distance)
{
distance = distanceTemp;
result = i;
}
}
}
if (result != -1)
return colliders[result];
else return null;
}
}
And here is my cube script which has some things you will need to add
using UnityEngine;
public class Cube : MonoBehaviour {
//these arent important just fields I used to set up a quick version of your game
public GameObject StartPoint;
public GameObject EndPoint;
public float Speed;
private Vector3 directionVector;
private bool direction;
//YOU WILL NEED THIS!!
[HideInInspector]
public BoxCollider BoxCollider;
// Use this for initialization
void Start() {
//not important
directionVector = EndPoint.transform.position - StartPoint.transform.position;
directionVector.Normalize();
//DONT FORGET TO SET YOUR BOX COLLIDER
BoxCollider = GetComponent<BoxCollider>();
}
// Update is called once per frame
void Update()
{
float distance = 0;
if (direction)
{
distance = Vector3.Distance(EndPoint.transform.position, transform.position);
transform.position += directionVector * Speed;
if (distance < Vector3.Distance(EndPoint.transform.position, transform.position))
direction = !direction;
}
else
{
distance = Vector3.Distance(StartPoint.transform.position, transform.position);
transform.position -= directionVector * Speed;
if (distance < Vector3.Distance(StartPoint.transform.position, transform.position))
direction = !direction;
}
}
}
Previous Answer
I would say you need to calculate the perceived position of the object in the future.
Vector3 futurePos = cubePos + (cubeMoveDirection * cubeMoveSpeed);
Once you have the future position, even if it is not exact, you should aim your animation towards that position. To do this I would have the animation change a speed vector instead of an actual transforms position that way we can rotate this speed vector in any direction you want while keeping the orientation of the block. Otherwise you have to rotate the entire block to point towards the direction you want. If this is what you want then put your block under a empty gameobject, rotate the empty gameobject to point to where you want and do the speed calculations only.
Next your animation should have a net move vector which should be pre-calculated and scaled down or up to meet the distance to the future position. It will look something like this(note this is not tested)
//class fields
Vector3 AnimatedSpeed;
Vector3 AnimationDelta;
//basic calculation
//get the direction vector from the players current position to the future
block position
Vector3 dirVector = futurePos - transform.position;
//find the rotation from the current orientation to the direction vector
Quaternion rotation = Quaternion.FromToRotation(transform.forward, dirVector);
//calculate the distance from you to the cube and scale it with the magnitude of the AnimationDelta
float result = Vector3.Distance(transform.position, futurePos);
result = result / animationDelta.magnitude;
//finally rotate the forward vector by the rotation and multiply it by the
//animation speed and the result to get the step by step movement as
//the animation plays. NOTE: The animation should be based on forward direction
transform.position += (AnimationSpeed * rotation) * result * Time.deltaTime;
Hopefully this does it, like I said I haven't tested it at all so you may have to do some tweaking based on your particular case as this is essentially psuedo-code.
Good luck! I'm off to bed I'll check back when I wake up.

Categories