Trajectory prediction for the ball getting hit by another ball - unity3d

I'm creating a prediction path for the ball getting hit by another ball.
Below is my code
MainCueball.cs
void Update()
{
Vector3 reduceTransPos = new Vector3(transform.position.x, transform.position.y - 2f, transform.position.z);
Ray ray = new Ray(transform.position, -transform.forward);
if (Physics.SphereCast(ray, cueBallRadius, out hit))
{
if (hit.collider != null)
{
lineRender.enabled = true;
lineRender.SetPosition(0, transform.position);
lineRender.SetPosition(1, -transform.forward + new Vector3(hit.point.x, hit.point.y + 1f, hit.point.z));
if (hit.collider.gameObject.CompareTag("OtherCueBall"))
{
Vector3 newDirection = (hit.transform.position - hit.point).normalized;
hit.collider.gameObject.GetComponent<OtherCueBallTrajectory>().DrawPredictionLine(newDirection);
}
}
}
}
OtherBallTrajectory.cs
private LineRenderer lineRender;
private void Awake()
{
lineRender = GetComponent<LineRenderer>();
}
public void DrawPredictionLine(Vector3 targetDestination)
{
lineRender.enabled = true;
lineRender.SetPosition(0, transform.position);
lineRender.SetPosition(1, targetDestination);
}
As you can see above the second prediction path is not accurate it's pointing on the left side when it should be pointing in the center

I have fixed this by using Vector3 newDirection =
Vector3.Reflect(hit.transform.position, hit.normal);

Related

Unity - Vector3.Lerp / MoveTowards doesn't work

The problem I can't solve is: cloned object, slow progression of parent object to X axis. But no matter what I did I could do it based on time, this is the latest version of the code.
public GameObject Ball;
private void OnTriggerExit(Collider other)
{
if (other.CompareTag("Gate"))
{
GameObject clone2 = Instantiate(Ball, transform.position, transform.rotation);
transform.position = Vector3.Lerp(transform.position, new Vector3(transform.position.x * 2,
transform.position.y,
transform.position.z), 25);
clone2.transform.parent = gameObject.transform;
}
if (other.CompareTag("Push"))
{
Destroy(gameObject);
}
}
#BugFinder already mentioned it in the comments. But the likely issue is that OnTriggerExit only gets called once and moves only one frame. Not only that but you are moving it over 25 seconds. Which is a long time for a Lerp function.
Below is a solution you can try:
float speed = 5;
private void OnTriggerExit(Collider other)
{
if (other.CompareTag("Gate"))
{
GameObject clone2 = Instantiate(Ball, transform.position, transform.rotation);
Vector3 targetLocation = new Vector3(transform.position.x * 2, transform.position.y, transform.position.z);
StartCoroutine(MoveTowardsLocation(clone2.transform, targetLocation, speed));
}
else if (other.CompareTag("Push"))
{
Destroy(gameObject);
}
}
private IEnumerator MoveTowardsLocation(Transform movedObj, Vector3 targetLocation, float speed)
{
Vector3 direction = (movedObj - movedObj.position).normalized;
while(Vector3.Distance(movedObj.position, targetLocation) > 0.01f)
{
movedObj.position = Vector3.Lerp(movedObj.position, movedObj.position + direction * speed * Time.deltatime, 1f);
yield return null;
}
movedObj.position = targetLocation;
}
In your code Lerp is called only once on trigger exit and position is updated only in one frame which is not resulting to visible change. To fix it you need start a coroutine on trigger exit and change transform.position in coroutine.

Shoot raycast on reflect direction

I'm creating a pool game. And I want to shoot Raycast on the reflect direction so I can draw LineRenderer on it.
Main Cue Ball Script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DrawCueBallTrajectory : MonoBehaviour
{
private RaycastHit hit;
private LineRenderer lineRender;
private float cueBallRadius;
private void Awake()
{
lineRender = GetComponent<LineRenderer>();
cueBallRadius = GetComponent<SphereCollider>().radius;
}
void Update()
{
Vector3 reduceTransPos = new Vector3(transform.position.x, transform.position.y - 2f, transform.position.z);
Ray ray = new Ray(transform.position, -transform.forward);
if (Physics.SphereCast(ray, cueBallRadius, out hit))
{
if (hit.collider != null)
{
lineRender.enabled = true;
lineRender.SetPosition(0, transform.position);
lineRender.SetPosition(1, -transform.forward + new Vector3(hit.point.x, hit.point.y + 1f, hit.point.z));
if (hit.collider.gameObject.CompareTag("OtherCueBall"))
{
Vector3 newDirection = Vector3.Reflect(hit.transform.position, hit.normal);
hit.collider.gameObject.GetComponent<OtherCueBallTrajectory>().DrawPredictionLine(newDirection);
}
}
}
}
}
Other Ball Script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class OtherCueBallTrajectory : MonoBehaviour
{
private RaycastHit hit;
private LineRenderer lineRender;
private float cueBallRadius;
private void Awake()
{
lineRender = GetComponent<LineRenderer>();
cueBallRadius = GetComponent<SphereCollider>().radius;
}
public void DrawPredictionLine(Vector3 targetDestination)
{
Ray ray = new Ray(transform.position); // what to put here
}
}
You must emit another sphere cast along the point of impact. Also adjust the lineRender points count according to the number of fractures. I made these changes to the script main cue ball and do not see the need to refer the second sphere cast to the other ball.
if (Physics.SphereCast(ray, cueBallRadius, out hit))
{
if (hit.collider != null)
{
lineRender.enabled = true;
lineRender.positionCount = 2; // here I set lineRender position count
lineRender.SetPosition(0, transform.position);
lineRender.SetPosition(1, -transform.forward + new Vector3(hit.point.x, hit.point.y + 1f, hit.point.z));
if (hit.collider.gameObject.CompareTag("OtherCueBall"))
{
Vector3 newDirection = Vector3.Reflect(hit.transform.position, hit.normal);
lineRender.positionCount = 3; // we need 1 more break point
// second sphere cast begin from hit.point along reflectDirection
if (Physics.SphereCast(hit.point, cueBallRadius, newDirection, out var secondHit))
{
lineRender.SetPosition(2, secondHit.point); // stop when hit wall or anything
}
else
{
lineRender.SetPosition(2, hit.point + newDirection * 10f); // point along direction for maximum of 10f length for e.g..
}
}
}
}

Inverse Kinematics (IK) Foot Placement

I have a problem with my humanoid character's Foot IK.
I'm building an Isometric Top-Down game with NavMesh to move the Player on click, and I wanted to add some life to the characters so I decided to add IK Foot Placement by following this tutorial.
It worked well, but I wanted to go further by making it more sensitive to colliders so the player's foot gets placed on the ground's surface perfectly.
This script below works well when the ground is rotated.
I tried to fix it a lot but it still doesn't work.
[ Preview Images ]
Here's my script:
using UnityEngine;
using UnityEngine.AI;
public class IKFootPlacement : MonoBehaviour
{
public bool ikActive = false;
public Animator anim;
public NavMeshSurface navmesh;
public LayerMask layerMask;
public Transform LeftFoot_Transform;
public Transform RightFoot_Transform;
public float LeftFoot_DistanceToGround;
public float RightFoot_DistanceToGround;
public float footGap = 0.0f;
private Vector3 L_TargetPosition;
private Vector3 R_TargetPosition;
private Ray L_Ray;
private Ray R_Ray;
void Start()
{
anim = GetComponent<Animator>();
}
void OnAnimatorIK(int layerIndex)
{
if (anim)
{
// Vector3 LeftFoot_position = anim.GetIKPosition(AvatarIKGoal.LeftFoot);
// Vector3 RightFoot_Position = anim.GetIKPosition(AvatarIKGoal.RightFoot);
float IKLeftWeight = anim.GetFloat("IKLeftFootWeight");
anim.SetIKPositionWeight(AvatarIKGoal.LeftFoot, IKLeftWeight);
anim.SetIKRotationWeight(AvatarIKGoal.LeftFoot, IKLeftWeight);
float IKRightWeight = anim.GetFloat("IKRightFootWeight");
anim.SetIKPositionWeight(AvatarIKGoal.RightFoot, IKRightWeight);
anim.SetIKRotationWeight(AvatarIKGoal.RightFoot, IKRightWeight);
//~ Left Foot
if (LeftFoot_Transform)
{
RaycastHit L_Hit;
L_Ray = new Ray(LeftFoot_Transform.position + Vector3.up, Vector3.down);
if (Physics.Raycast(L_Ray, out L_Hit, 2f, layerMask))
{
L_TargetPosition = L_Hit.point;
LeftFoot_DistanceToGround = Vector3.Distance(LeftFoot_Transform.position, L_TargetPosition);
if (LeftFoot_DistanceToGround > 0.15)
L_TargetPosition.y -= LeftFoot_DistanceToGround;
else
L_TargetPosition.y += LeftFoot_DistanceToGround;
L_TargetPosition.y += footGap;
anim.SetIKPosition(AvatarIKGoal.LeftFoot, L_TargetPosition);
anim.SetIKRotation(AvatarIKGoal.LeftFoot, Quaternion.LookRotation(transform.forward, L_Hit.normal));
}
}
//~ Right Foot
if (RightFoot_Transform)
{
RaycastHit R_Hit;
R_Ray = new Ray(RightFoot_Transform.position + Vector3.up, Vector3.down);
if (Physics.Raycast(R_Ray, out R_Hit, 2f, layerMask))
{
R_TargetPosition = R_Hit.point;
RightFoot_DistanceToGround = Vector3.Distance(RightFoot_Transform.position, R_TargetPosition);
if (RightFoot_DistanceToGround > 0.15)
R_TargetPosition.y -= RightFoot_DistanceToGround;
else
R_TargetPosition.y += RightFoot_DistanceToGround;
R_TargetPosition.y += footGap;
anim.SetIKPosition(AvatarIKGoal.RightFoot, R_TargetPosition);
anim.SetIKRotation(AvatarIKGoal.RightFoot, Quaternion.LookRotation(transform.forward, R_Hit.normal));
}
}
}
}
void OnDrawGizmos()
{
Gizmos.color = Color.magenta;
Gizmos.DrawWireSphere(L_TargetPosition, 0.05f);
Gizmos.DrawWireSphere(R_TargetPosition, 0.05f);
Gizmos.color = Color.red;
Gizmos.DrawRay(L_Ray);
Gizmos.DrawRay(R_Ray);
}
}

Making a volleyball game, overlap not working

I'm making a 2D game on Unity and I created an overlap to see if the ball is on the hit area. However, whenever I push the hit button, the ball is hit even if it is away of the player. I am sending the code for the Player, which contains what is needed so far, to see what may have gone wrong. Supposedly with the foreach hitCollider in hitColliders it should be fixed but it is not working I and just canĀ“t wrap my mind around it.
public int speed;
public bool estoyFloor, subiendo, bajando;
public bool canRight, canLeft;
public bool canMove;
public float posYinit;
public Vector3 posInitPlayer;
bool m_Started;
public LayerMask m_LayerMask;
public GameObject volleyBall;
// Use this for initialization
void Start () {
m_Started = true;
}
void FixedUpdate()
{
Collider2D[] hitColliders = Physics2D.OverlapBoxAll(gameObject.transform.position, transform.localScale / 2, 0);
foreach (Collider2D hitCollider in hitColliders)
{
if (Input.GetButtonDown("Fire1"))
{
volleyBall.GetComponent<Rigidbody2D>().velocity = Vector3.zero;
volleyBall.GetComponent<Rigidbody2D>().AddForce(new Vector3(300, 500));
}
}
}
void OnDrawGizmos()
{
Gizmos.color = Color.red;
if (m_Started)
Gizmos.DrawWireCube(transform.position, transform.localScale / 2);
}
// Update is called once per frame
void Update () {
if (Input.GetButtonDown("Fire1"))
canMove = true;
if (Input.GetAxisRaw("Horizontal") > 0 && canRight)
transform.position += Vector3.right * speed * Time.deltaTime;
if (Input.GetAxisRaw("Horizontal") < 0 && canLeft)
transform.position += Vector3.left * speed * Time.deltaTime;
if (Input.GetButtonDown("Jump") && estoyFloor)
{
subiendo = true;
posYinit = transform.position.y;
}
if (transform.position.y > posYinit + 2.5f)
{
subiendo = false;
bajando = true;
}
if (!estoyFloor && !subiendo)
bajando = true;
Debug.DrawLine(transform.position, transform.position + new Vector3(0, -1, 0), Color.red);
Debug.DrawLine(transform.position, transform.position + new Vector3(0.8f, 0, 0), Color.red);
Debug.DrawLine(transform.position, transform.position + new Vector3(1.5f, 0, 0), Color.red);
RaycastHit2D[] hitDown = Physics2D.LinecastAll(transform.position, transform.position + new Vector3(0, -1, 0));
RaycastHit2D[] hitsRight = Physics2D.LinecastAll(transform.position, transform.position + new Vector3(0.8f, 0, 0));
RaycastHit2D[] hitsLeft = Physics2D.LinecastAll(transform.position, transform.position + new Vector3(1.5f, 0, 0));
estoyFloor = false;
foreach (RaycastHit2D hit in hitDown)
{
if (hit.collider.name == "Floor")
{
bajando = false;
estoyFloor = true;
}
}
canRight = true;
foreach (RaycastHit2D hit in hitsRight)
{
if (hit.collider.tag == "Wall")
canRight = false;
}
canLeft = true;
foreach (RaycastHit2D hit in hitsLeft)
{
if (hit.collider.tag == "Wall")
canLeft = false;
}
if (subiendo)
transform.position += Vector3.up * speed * Time.deltaTime;
if (bajando)
transform.position += Vector3.down * speed * Time.deltaTime;
}
Your overlap box is probably colliding with the player it is on. Because you do not check if the collider belonged to the volleyball in your loop, you are hitting the ball no matter what. Try to get the transform from the collider and check its tag to see if it is a volleyball before adding force to the ball and see if that works. You could also use a layermask on your Overlap box check as well to ensure only volleyballs get detected.

I can not change the state of animation in unity

public class TranslatreDora : MonoBehaviour {
public Vector3 WalkSpeed;
public Animator walkDora_;
public float RunSpeedDora=1.6f;
private Rigidbody DoraRigidBody;
public Vector3 JumpVector;
private bool Jumping_;
public Transform DoraFoot_;
public Vector3 DoraFootRay_;
void Start ()
{
walkDora_.GetComponent<Animator>();
walkDora_.SetFloat("DoraSpeed", 1);
DoraRigidBody = GetComponent<Rigidbody>();
}
if (Input.GetKeyDown(KeyCode.J) || (Input.GetKey(KeyCode.W) && Input.GetKeyDown(KeyCode.J)))
{
Jumping_ = true;
JumpVector = new Vector3(0, 300, 0);
walkDora_.SetBool("Jumping", true);
walkDora_.SetFloat("JumpFloat", 1);
Debug.Log("Jump 1");
DoraRigidBody.AddForce(JumpVector);
Debug.Log("Jump 1 ing");
RayCastTest();
}
Debug.Log("Finished");
}
void RayCastTest()
{
Debug.Log("Entered");
//RayCasting
Vector3 DoraRayTest = new Vector3(DoraFoot_.position.x, DoraFoot_.position.y, DoraFoot_.position.z);
Ray RayDoraDown = new Ray(DoraRayTest, Vector3.down);
Debug.DrawRay(DoraRayTest, Vector3.down, Color.green);
Debug.Log("Entered 2");
RaycastHit RHit;
Debug.Log("Entered 3");
***//HERE This if-statement does not even get executed***
if (Physics.Raycast(DoraRayTest, Vector3.down, 1f))
{
Debug.Log("Not still False");
walkDora_.SetBool("Jumping", false);
Debug.Log("False");
}
Debug.Log("Entered Exit");
}
}
That If statement don't even execute so my Animation gets stuck.
I want the ray cast to only scan the ray if I hit jump and then when the character is near the Ground That If Statement should be executed.