Need help in drawing the projected path in unity game . I want my game to draw line with bounce features
Thanks in advance
Need help in drawing the projected path in unity game . I want my game to draw line with bounce features
Well this isn’t exactly trivial if you want to do it right.
Wrong way would be to just draw the line, and you can use LineRenderer for that (obviously after you find the points of collision), however the real issue is that you have to guarantee that the ball will actually precisely go over this line instead of doing something else.
Therefore, to do this properly, you actually have to write a collision-aware “physics” surrogate which you use both for the line tracing and for the ball propulsion. (Edit: Or you can do what Kurt suggests in post #6 below, which should be a superior solution.)
To detect collisions along the path you can use Physics2D.Raycast and to propel the ball along the line you move the ball one movement step per frame (scaled by Time.deltaTime).
However, once you figure out everything there is to this, it turns out to be a complete time-slicing “physicsy” algorithm and you can’t btw use Raycast, you want to use CircleCast instead.
Of course, you can try and make a good enough hybrid, maybe the sloppy way is good enough. The algorithm is essentially this (in semi-pseudocode)
List<Vector2> findAllCollisionPoints(Vector2 position, Vector2 direction, float length, int collisionLayer) {
var list = new List<Vector2>();
var remainingLength = length;
while(remainingLength > 0f) {
if(!tryGetCollision(position, direction, collisionLayer, remainingLength, out point, out normal))
break;
list.Add(point);
var distance = Vector2.Distance(point, position);
remainingLength -= distance;
position = point;
direction = Vector2.Reflect(direction, normal);
}
// create the last point hanging in mid-air here if remainingLength is still > 0f
if(remainingLength > 0f)
list.Add(position + remainingLength * direction);
return list;
}
Something like that. This can then be reused for the actual movement of the ball.
As I said, it’s not trivial, I’d say 3.3 out of 10 on my personal difficulty scale.
Edit:
Personally I’d make it not allocate a new list/array each frame, this is just a rough example to get the point across.
Edit2:
For reflecting you can use Vector2.Reflect, which does direction - (2f * dot(direction, normal)) * normal
Edit3:
Typo in code
HI thanks for the reply. It will be great if i get complete code.As i am new to unity.
The rest you need to fill in on your own. It’s pretty much specific to how you develop your project, and I believe nobody will do it for you. Some intermediate coding knowledge is necessary for doing pretty much anything, so if it’s not about this, you’ll soon hit another obstacle anyway (no pun intended). Better try to learn what’s going on here, C#- and mathematics-wise, it’s not that hard and will open up a lot of possibilities for you.
Edit:
As I said, tryGetCollision
is some custom implementation of Physics2D.CircleCast and you really need to learn more about this for yourself. There is no one-size-fits-all solution.
Just for the record, a non-allocating version of the above method can be implemented like this
void findAllCollisionPointsNonAlloc(List<Vector2> list, Vector2 position, Vector2 direction, float length, int collisionLayer) {
// remove this line: var list = new List<Vector2>();
// ... everything in between stays the same
// remove this line: return list;
}
And then in the calling site you’re responsible for creating the list and clearing it before use, which is much more optimal for the frame-by-frame performance
using System;
using System.Collections.Generic;
using UnityEngine;
public class MyBehaviour : MonoBehaviour {
[SerializeField] GameObject _ball;
[SerializeField] [Range(0f, 360f)] float _angle = 160f;
[SerializeField] [Min(0f)] float _length = 5f;
[SerializeField] int _collisionLayer;
List<Vector2> _list;
void Awake() {
_list = new(); // optionally set list's maximum capacity here
}
void Update() {
_list.Clear();
findAllCollisionPointsNonAlloc(_list, _ball.transform.position, toDirection(_angle), _length, _collisionLayer);
// use _list points here
}
static Vector2 toDirection(float degrees) {
var radians = degrees * Mathf.Deg2Rad;
return new(MathF.Cos(radians), MathF.Sin(radians));
}
}
Clear does not deallocate the list or change its size in memory, it simply truncates the accessible portion of it, letting the subsequent calls to simply overwrite the previous entries.
Obviously this isn’t a complete solution, it’s just an example. Direction should be fed back somehow, so that it doesn’t compute from the same angle in each frame and so on. There are a lot of minute details that need to be taken care of.
The OP should note too that a raycast is not going to be adequate when checking what a circle will hit unless it’s hitting only simple edges. For instance, a “ball” hitting the top stack of “balls” would actually contact in a different location than the raycast.
All the physics queries provide non-allocating calls but for balls you can use:
Physics2D.CircleCast or Collider2D.Cast.
These return the hit-point AND the centroid of the shape (circle) when it hits.
Hope that helps too.
Hey Orion, I managed to make this work with Physics2D itself… pseudocode to follow:
// stop auto physics:
Physics2D.autoSimulation = false;
// fire off your one single aim ball
// loop on this for as long as you want your aimline to grow:
{
Physics2D.Simulate(Time.fixedDeltaTime);
// record the ball position out of the Rigidbody2D
}
// when you're done recording points:
// destroy your aim ball and...
Physics2D.autoSimulation = true;
// use the list of points to construct your LineRenderer aimline!
// when you release, physics is ready to do the job automatically
I also set all the balls to the same Layer and set that Layer to ignore itself. Season to taste.
Hey, that’s definitely better / more true to the actual physics. I’ve worked with Simulate before but for some reason didn’t think of this application. Now I need to experiment with this; my solution has profound issues with kinematic/dynamic collisions (i.e. player-moved objects) because once the ball overlaps with such an object, it’s a world of pain to find a reasonable strategy to get it free again. Not impossible to do properly, but needlessly difficult.
Note that “autoSimulation” was made obsolete a long time ago in 2D physics so you should use Physics2D.simulation (same in 3D physics now).
Also, you can create a scene dedicated to simulation by using the CreateSceneParameters, specifying that you want an isolated local 2D physics world in it.
You can get the PhysicsScene2D from that Unity scene using GetPhysicsScene2D and perform actions on it like simulation.
Here’s an example of usage in a little scene I set-up when I implemented both the 2D/3D local physics scenes: https://youtu.be/mV0Vkkx5z2k?si=wjKU1FWdlbeeBZKN
Hope that helps.