154 lines
4.8 KiB
C#
154 lines
4.8 KiB
C#
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
|
|
public class BallPathTest : MonoBehaviour
|
|
{
|
|
public AudioSource audioToSync;
|
|
public List<float> allHits = new List<float>();
|
|
public LineRenderer line;
|
|
public Transform mover;
|
|
public float horizontalSpeed = 1f;
|
|
public float gravity = -9f;
|
|
public float gravityWhenUpwardMult = 1f;
|
|
public float maxFallSpeed = 10;
|
|
public AnimationCurve bouncingEfficiencyGraph = new AnimationCurve();
|
|
// public float bouncingEfficiency =0.4f;
|
|
public float changeDirCloseThresh = 0.1f;
|
|
public Vector2 velocity;
|
|
public bool simulate = false;
|
|
|
|
public GameObject bouncerPrefab;
|
|
public Transform bouncerParent;
|
|
|
|
void OnValidate()
|
|
{
|
|
if (simulate)
|
|
{
|
|
Simulate();
|
|
simulate = false;
|
|
}
|
|
}
|
|
float timer = 0;
|
|
public List<float> hitsCache = new List<float>();
|
|
List<Vector3> pointsHistory = new List<Vector3>();
|
|
List<BounceData> hitPointsHistory = new List<BounceData>();
|
|
|
|
|
|
void Start()
|
|
{
|
|
Reset(Time.time);
|
|
}
|
|
|
|
void Reset(float time){
|
|
velocity = new Vector2(horizontalSpeed, 0);
|
|
mover.transform.position = Vector2.zero;
|
|
hitsCache = new List<float>();
|
|
hitsCache.AddRange(allHits);
|
|
pointsHistory= new List<Vector3>();
|
|
hitPointsHistory= new List<BounceData>();
|
|
prevTime=time;
|
|
timer =0;
|
|
}
|
|
|
|
// Update is called once per frame
|
|
void Update()
|
|
{
|
|
ManualUpdate(Time.time);
|
|
}
|
|
|
|
float prevTime;
|
|
bool ManualUpdate(float time){
|
|
float dt = time - prevTime;
|
|
prevTime = time;
|
|
timer += dt;
|
|
|
|
audioToSync.time = time;
|
|
|
|
if(hitsCache.Count <= 1){return true;}
|
|
|
|
velocity += new Vector2(0, gravity*dt * (velocity.y > 0 ? gravityWhenUpwardMult : 1));
|
|
|
|
if (timer > hitsCache[0])
|
|
{
|
|
float timeToNextHit = hitsCache[1]-hitsCache[0];//(hitsCache.Count >1)?1:0
|
|
Debug.Log($"hit: {timeToNextHit}, velocity : {velocity}");
|
|
BounceData bounceData = new BounceData();
|
|
bounceData.point = mover.position;
|
|
bounceData.prevVelocity = velocity;
|
|
//Bounce
|
|
hitsCache.RemoveAt(0);
|
|
bool changeDir= timeToNextHit < changeDirCloseThresh;
|
|
float bouncingEfficiency = bouncingEfficiencyGraph.Evaluate(Mathf.Abs(velocity.y)/maxFallSpeed);
|
|
velocity = new Vector2(velocity.x * (changeDir ? -1 : 1), velocity.y * -bouncingEfficiency * (changeDir ? 0:1));
|
|
bounceData.newVelocity = velocity;
|
|
|
|
hitPointsHistory.Add(bounceData);
|
|
}
|
|
|
|
velocity = new Vector2(velocity.x, velocity.y < -maxFallSpeed ? -maxFallSpeed : velocity.y);
|
|
|
|
mover.transform.position += (Vector3)velocity * dt;
|
|
|
|
pointsHistory.Add(mover.transform.position);
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
void Simulate(){
|
|
float fps = 60;
|
|
float totalLength = allHits[allHits.Count-1];
|
|
float curFrame =0;
|
|
Reset(0);
|
|
|
|
//Purge existing bouncing plats
|
|
bouncerParent.PurgeChildrenEdit();
|
|
|
|
int totalFrames = Mathf.CeilToInt(totalLength * fps);
|
|
while(curFrame < totalFrames){
|
|
if(ManualUpdate(curFrame/fps)){
|
|
break;
|
|
}
|
|
curFrame++;
|
|
}
|
|
|
|
line.positionCount = pointsHistory.Count;
|
|
line.SetPositions(pointsHistory.ToArray());
|
|
|
|
foreach(BounceData data in hitPointsHistory){
|
|
GameObject newPlat = Instantiate(bouncerPrefab, bouncerParent);
|
|
newPlat.transform.position = data.point;
|
|
newPlat.transform.rotation = Quaternion.AngleAxis(CalculateWallAngle(data.prevVelocity,data.newVelocity), Vector3.forward);
|
|
}
|
|
}
|
|
|
|
public static float CalculateWallAngle(Vector2 previousVelocity, Vector2 newVelocity)
|
|
{
|
|
// Normalize velocities to ignore the speed and just focus on direction
|
|
Vector2 previousDir = previousVelocity.normalized;
|
|
Vector2 newDir = newVelocity.normalized;
|
|
|
|
// The reflection vector is the difference between the two normalized velocities
|
|
Vector2 reflectionVector = newDir - previousDir;
|
|
|
|
// Calculate the wall normal using reflection law (midpoint of incoming and outgoing angles)
|
|
Vector2 wallNormal = Vector2.Perpendicular(reflectionVector).normalized;
|
|
|
|
// Calculate the angle of the wall from the wall's normal
|
|
float wallAngle = Mathf.Atan2(wallNormal.y, wallNormal.x) * Mathf.Rad2Deg;
|
|
|
|
// Make sure the angle is between 0 and 360 degrees
|
|
if (wallAngle < 0) wallAngle += 360;
|
|
|
|
return wallAngle;
|
|
}
|
|
}
|
|
|
|
|
|
[System.Serializable]
|
|
public struct BounceData{
|
|
public Vector3 point;
|
|
public Vector2 prevVelocity;
|
|
public Vector2 newVelocity;
|
|
} |